Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
DannyBen committed May 25, 2016
0 parents commit 7d75c1f
Show file tree
Hide file tree
Showing 14 changed files with 384 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
@@ -0,0 +1,8 @@
/cache
/coverage
/dev
/doc
/gems
/Gemfile.lock
/.yardoc
*.gem
2 changes: 2 additions & 0 deletions .rspec
@@ -0,0 +1,2 @@
--color
--format documentation
19 changes: 19 additions & 0 deletions .travis.yml
@@ -0,0 +1,19 @@
language: ruby

rvm:
- "2.0.0"
- "2.2.1"

notifications:
email: false

script: bundle exec rspec

# notifications:
# email:
# recipients:
# - one@example.com
# - other@example.com
# on_success: [always|never|change] # default: change
# on_failure: [always|never|change] # default: always

4 changes: 4 additions & 0 deletions Gemfile
@@ -0,0 +1,4 @@
source "https://rubygems.org"

gemspec

21 changes: 21 additions & 0 deletions LICENSE
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) Danny Ben Shitrit

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
117 changes: 117 additions & 0 deletions README.md
@@ -0,0 +1,117 @@
Lightly - Ruby File Cache
==================================================

[![Gem](https://img.shields.io/gem/v/lightly.svg?style=flat-square)](https://rubygems.org/gems/lightly)
[![Travis](https://img.shields.io/travis/DannyBen/lightly.svg?style=flat-square)](https://travis-ci.org/DannyBen/lightly)
[![Code Climate](https://img.shields.io/codeclimate/github/DannyBen/lightly.svg?style=flat-square)](https://codeclimate.com/github/DannyBen/lightly)
[![Gemnasium](https://img.shields.io/gemnasium/DannyBen/lightly.svg?style=flat-square)](https://gemnasium.com/DannyBen/lightly)

---

Lightly is a file cache for performing heavy tasks, lightly.

---

Install
--------------------------------------------------

```
$ gem install lightly
```

Or with bundler:

```ruby
gem 'lightly'
```

Usage
--------------------------------------------------

```ruby
require 'lightly'

lightly = Lightly.new

content = lightly.with 'key' do
# Heavy operation here
end
```

This will look for a cached object with the given key and return it
if it exists and not older than 1 hour. Otherwise, it will perform the
operation inside the block, and save it to the cache object.

By default, the cached objects are stored in the `./cache` directory, and
expire after 60 minutes. The cache directory will be created as needed.

In addition, the provided key is hashed to its MD5 representation.

You can change these settings on initialization:

```ruby
lightly = Lightly.new dir: 'tmp/my_cache', life: 7200, hash: false
```

Or later:

```ruby
lightly = Lightly.new
lightly.dir = 'tmp/my_cache'
lightly.life = 7200 # seconds
lightly.hash = false
```

To check if a key is cached, use the `cached?` method:

```ruby
lightly = Lightly.new
lightly.cached? 'example'
# => false

content = lightly.with 'example' do
open('http://example.com').read
end
lightly.cached? 'example'
# => true
```

You can enable/disable the cache at any time:

```ruby
lightly = Lightly.new
lightly.disable
lightly.enabled?
# => false

content = lightly.with 'example' do
open('http://example.com').read
end
lightly.cached? 'example'
# => false

lightly.enable
content = lightly.with 'example' do
open('http://example.com').read
end
lightly.cached? 'example'
# => true
```

The `key` method is an alias to `with`, if you prefer a different wording:

```ruby
cache = Lightly.new

content = cache.key 'example' do
# Heavy operation here
end
```

---

For a similar gem that provides caching specifically for HTTP downloads,
see the [WebCache gem][1]


[1]: https://github.com/DannyBen/webcache
9 changes: 9 additions & 0 deletions Runfile
@@ -0,0 +1,9 @@
require "runfile-tasks"
require_relative 'lib/lightly'

name "Lightly Developer Toolbelt"
summary "Runfile tasks for building the Lightly gem"
version Lightly::VERSION

RunfileTasks::RubyGems.all 'lightly'
RunfileTasks::Testing.rspec
2 changes: 2 additions & 0 deletions lib/lightly.rb
@@ -0,0 +1,2 @@
require 'lightly/version'
require 'lightly/lightly'
69 changes: 69 additions & 0 deletions lib/lightly/lightly.rb
@@ -0,0 +1,69 @@
require 'digest/md5'
require 'fileutils'

class Lightly
attr_accessor :dir, :life, :hash

def initialize(opts={})
@dir = opts[:dir] || 'cache'
@life = opts[:life] || 3600
@hash = opts.key?(:hash) ? opts[:hash] : true
@enabled = true
end

def with(key, &block)
return load key if cached?(key) && enabled?

content = block.call
save key, content if enabled?
content
end
alias_method :key, :with

def flush
return false if dir == '/' || dir.empty?
FileUtils.rm_rf dir
end

def enabled?
@enabled
end

def enable
@enabled = true
end

def disable
@enabled = false
end

def cached?(key)
path = get_path key
File.exist? path and !expired? path
end

private

def save(key, content)
FileUtils.mkdir_p dir
path = get_path key
File.open path, 'wb' do |f|
f.write Marshal.dump content
end
end

def load(key)
Marshal.load File.binread(get_path key)
end

def get_path(key)
key = Digest::MD5.hexdigest(key) if hash
File.join dir, key
end

def expired?(path)
expired = life > 0 && File.exist?(path) && Time.new - File.mtime(path) >= life
FileUtils.rm path if expired
expired
end
end
3 changes: 3 additions & 0 deletions lib/lightly/version.rb
@@ -0,0 +1,3 @@
class Lightly
VERSION = "0.1.1"
end
23 changes: 23 additions & 0 deletions lightly.gemspec
@@ -0,0 +1,23 @@
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'lightly/version'

Gem::Specification.new do |s|
s.name = 'lightly'
s.version = Lightly::VERSION
s.date = Date.today.to_s
s.summary = "File cache for performing heavy tasks, lightly."
s.description = "Easy to use file cache"
s.authors = ["Danny Ben Shitrit"]
s.email = 'db@dannyben.com'
s.files = Dir['README.md', 'lib/**/*.*']
s.homepage = 'https://github.com/DannyBen/lightly'
s.license = 'MIT'
s.required_ruby_version = ">= 2.0.0"

s.add_development_dependency 'runfile', '~> 0.7'
s.add_development_dependency 'runfile-tasks', '~> 0.4'
s.add_development_dependency 'rspec', '~> 3.4'
s.add_development_dependency 'simplecov', '~> 0.11'
s.add_development_dependency 'byebug', '~> 9.0'
end
7 changes: 7 additions & 0 deletions spec/README.md
@@ -0,0 +1,7 @@
Testing
==================================================

Run tests with:

$ bundle exec run spec

94 changes: 94 additions & 0 deletions spec/lightly/lightly_spec.rb
@@ -0,0 +1,94 @@
require 'spec_helper'

describe Lightly do
let(:lightly) { Lightly.new }

before do
lightly.flush
end

describe '#new' do
it "sets default properties" do
expect(lightly).to be_enabled
expect(lightly.life).to eq 3600
expect(lightly.dir).to eq 'cache'
expect(lightly.hash).to be true
end

it "accepts initialization properties" do
lightly = Lightly.new dir: 'store', life: 120, hash: false
expect(lightly.life).to eq 120
expect(lightly.dir).to eq 'store'
expect(lightly.hash).to be false
end
end

describe '#with' do
it "skips caching if disabled" do
lightly.disable
lightly.with('key') { 'content' }
expect(Dir['cache/*']).to be_empty
end

it "creates a cache folder" do
expect(Dir).not_to exist 'cache'
lightly.with('key') { 'content' }
expect(Dir).to exist 'cache'
end

it "saves a file" do
lightly.with('key') { 'content' }
expect(Dir['cache/*']).not_to be_empty
end

it "loads from cache" do
lightly.with('key') { 'content' }
expect(lightly).to be_cached 'key'
expect(lightly).to receive(:load)
lightly.with('key') { 'new, irrelevant content' }
end

it "returns content from cache" do
lightly.with('key') { 'content' }
expect(lightly).to be_cached 'key'
content = lightly.with('key') { 'new, irrelevant content' }
expect(content).to eq 'content'
end
end

describe '#key' do
it "behaves like #with" do
expect(lightly.method(:key)).to eq lightly.method(:with)
end
end

describe '#cached?' do
context 'with a cached key' do
it "returns true" do
lightly.with('key') { 'content' }
expect(lightly).to be_cached 'key'
end
end

context 'with an uncached key' do
it "returns false" do
expect(lightly).not_to be_cached 'key'
end
end
end

describe '#enable' do
it "enables cache handling" do
lightly.enable
expect(lightly).to be_enabled
end
end

describe '#disable' do
it "disables cache handling" do
lightly.disable
expect(lightly).not_to be_enabled
end
end

end

0 comments on commit 7d75c1f

Please sign in to comment.