looksy 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +35 -0
- data/Rakefile +1 -0
- data/lib/looksy.rb +8 -0
- data/lib/looksy/cacheable.rb +57 -0
- data/lib/looksy/dynamic_find_match.rb +17 -0
- data/lib/looksy/lookup.rb +59 -0
- data/lib/looksy/null_cache.rb +7 -0
- data/lib/looksy/version.rb +3 -0
- data/looksy.gemspec +21 -0
- data/spec/lib/cacheable_spec.rb +67 -0
- data/spec/lib/dynamic_find_match_spec.rb +52 -0
- data/spec/lib/lookup_spec.rb +64 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/record.rb +36 -0
- metadata +82 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Artin Boghosian
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Looksy
|
2
|
+
|
3
|
+
Looksy adds a caching layer for your ActiveRecord models
|
4
|
+
that represent look up tables in your database.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'looksy'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install looksy
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
Include Looksy::Cacheable in your ActiveRecord class.
|
23
|
+
Provides fetch_all and fetch_by_id(id) methods as well
|
24
|
+
as intercepts method_missing so you can use dynamic methods
|
25
|
+
such as fetch_by_name('name') or fetch_all_by_status('active').
|
26
|
+
You can also fetch by multiple columns such as:
|
27
|
+
fetch_by_type_and_status('type', 'status').
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
1. Fork it
|
32
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
33
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
34
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
35
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/looksy.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module Looksy
|
2
|
+
module Cacheable
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def cache_key
|
10
|
+
@@cache_key ||= [self.name.downcase, 'all'].join('/')
|
11
|
+
end
|
12
|
+
|
13
|
+
def cache_key=(key)
|
14
|
+
@@cache_key = key
|
15
|
+
end
|
16
|
+
|
17
|
+
def cache_options
|
18
|
+
@@cache_options ||= {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def cache_options=(options = {})
|
22
|
+
@@cache_options = options
|
23
|
+
end
|
24
|
+
|
25
|
+
def fetch_all
|
26
|
+
lookup.all
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch_by_id(id)
|
30
|
+
lookup.find_by_id(id)
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(method, *args, &block)
|
34
|
+
if method.match(/^fetch/)
|
35
|
+
method = method.to_s.gsub(/^fetch/, 'find')
|
36
|
+
lookup.send(method, *args, &block)
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def lookup
|
45
|
+
@lookup ||= begin
|
46
|
+
cache = if defined?(Rails)
|
47
|
+
Rails.cache
|
48
|
+
else
|
49
|
+
Looksy::NullCache.new
|
50
|
+
end
|
51
|
+
|
52
|
+
Looksy::Lookup.new(self, cache)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Looksy
|
2
|
+
class DynamicFindMatch < Struct.new(:finder, :attributes)
|
3
|
+
def self.match(method)
|
4
|
+
finder = :find
|
5
|
+
|
6
|
+
case method.to_s
|
7
|
+
when /^find_(all_)?by_([_a-zA-Z]\w*)$/
|
8
|
+
finder = :select if $1 == 'all_'
|
9
|
+
attributes = $2
|
10
|
+
else
|
11
|
+
return nil
|
12
|
+
end
|
13
|
+
|
14
|
+
new(finder, attributes.split('_and_'))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Looksy
|
2
|
+
class Lookup
|
3
|
+
def initialize(klass, cache)
|
4
|
+
@klass = klass
|
5
|
+
@cache = cache
|
6
|
+
end
|
7
|
+
|
8
|
+
def all
|
9
|
+
@cache.fetch(@klass.cache_key, @klass.cache_options) { @klass.all }
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_by_id(id)
|
13
|
+
all.find { |record| record.id == id.to_i }
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(method, *args, &block)
|
17
|
+
if match = Looksy::DynamicFindMatch.match(method)
|
18
|
+
super unless all_attributes_exist?(match.attributes)
|
19
|
+
|
20
|
+
attributes = extract_attributes(match.attributes) do |attribute|
|
21
|
+
args[match.attributes.index(attribute)]
|
22
|
+
end
|
23
|
+
|
24
|
+
all.send(match.finder) do |record|
|
25
|
+
record_attributes = extract_attributes(match.attributes) do |attribute|
|
26
|
+
record.send(attribute)
|
27
|
+
end
|
28
|
+
|
29
|
+
record_attributes == attributes
|
30
|
+
end
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def all_attributes_exist?(attributes)
|
39
|
+
(attributes - klass_attributes).empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
def klass_attributes
|
43
|
+
@klass_attributes ||= @klass.new.attributes.keys
|
44
|
+
end
|
45
|
+
|
46
|
+
def extract_attributes(attributes, &block)
|
47
|
+
Hash[attributes.map { |a| [a, sanitize_attribute_value(block.call(a))] }]
|
48
|
+
end
|
49
|
+
|
50
|
+
def sanitize_attribute_value(value)
|
51
|
+
case value
|
52
|
+
when Symbol, String
|
53
|
+
value.to_s.downcase
|
54
|
+
else
|
55
|
+
value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/looksy.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'looksy/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "looksy"
|
8
|
+
gem.version = Looksy::VERSION
|
9
|
+
gem.authors = ["Artin Boghosian"]
|
10
|
+
gem.email = ["artinboghosian@gmail.com"]
|
11
|
+
gem.description = %q{Add a caching layer to your ActiveRecord models that represent look up tables}
|
12
|
+
gem.summary = %q{Caching layer for look up tables}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_development_dependency "rspec"
|
21
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "support/record"
|
3
|
+
|
4
|
+
describe Looksy::Cacheable do
|
5
|
+
let(:klass) { Record }
|
6
|
+
|
7
|
+
describe '.cache_key' do
|
8
|
+
context 'when not set' do
|
9
|
+
it 'returns the default cache key' do
|
10
|
+
klass.cache_key.should eql('record/all')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'when set' do
|
15
|
+
it 'returns the correct cache key' do
|
16
|
+
klass.cache_key = 'MyKey'
|
17
|
+
klass.cache_key.should eql('MyKey')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '.cache_options' do
|
23
|
+
context 'when not set' do
|
24
|
+
it 'returns the default cache options' do
|
25
|
+
klass.cache_options.should eql({})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when set' do
|
30
|
+
it 'returns the correct cache options' do
|
31
|
+
klass.cache_options = { expires_in: 30 }
|
32
|
+
klass.cache_options.should have_key(:expires_in)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '.fetch_all' do
|
38
|
+
it 'returns all of the records' do
|
39
|
+
klass.fetch_all.should eql(klass.all)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '.fetch_by_id' do
|
44
|
+
let(:record) { klass.last }
|
45
|
+
|
46
|
+
it 'returns the correct record' do
|
47
|
+
klass.fetch_by_id(record.id).should eql(record)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '.method_missing' do
|
52
|
+
context 'when method starts with fetch' do
|
53
|
+
it 'returns matching records' do
|
54
|
+
records = klass.fetch_by_type('Developer')
|
55
|
+
records.count.should eql(3)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when method does not start with fetch' do
|
60
|
+
before { klass.should_receive(:method_missing) }
|
61
|
+
|
62
|
+
it 'calls method missing on the host' do
|
63
|
+
klass.non_existent_method
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Looksy::DynamicFindMatch do
|
4
|
+
describe '.match' do
|
5
|
+
context 'when method matches' do
|
6
|
+
context 'when method has one attribute' do
|
7
|
+
let(:method) { :find_by_this }
|
8
|
+
let(:match) { Looksy::DynamicFindMatch.match(method) }
|
9
|
+
|
10
|
+
it 'returns the correct attributes' do
|
11
|
+
match.attributes.should eql(['this'])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'when method has multiple attributes' do
|
16
|
+
let(:method) { :find_by_this_and_that }
|
17
|
+
let(:match) { Looksy::DynamicFindMatch.match(method) }
|
18
|
+
|
19
|
+
it 'returns the correct attributes' do
|
20
|
+
match.attributes.should eql(['this', 'that'])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when method finds one record' do
|
25
|
+
let(:method) { :find_by_this }
|
26
|
+
let(:match) { Looksy::DynamicFindMatch.match(method) }
|
27
|
+
|
28
|
+
it 'returns the correct finder' do
|
29
|
+
match.finder.should eql(:find)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when method finds multiple records' do
|
34
|
+
let(:method) { :find_all_by_this }
|
35
|
+
let(:match) { Looksy::DynamicFindMatch.match(method) }
|
36
|
+
|
37
|
+
it 'returns the correct finder' do
|
38
|
+
match.finder.should eql(:select)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when method does not match' do
|
44
|
+
let(:method) { :find_does_not_match }
|
45
|
+
let(:match) { Looksy::DynamicFindMatch.match(method) }
|
46
|
+
|
47
|
+
it 'returns nil' do
|
48
|
+
match.should be_nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "support/record"
|
3
|
+
|
4
|
+
describe Looksy::Lookup do
|
5
|
+
let(:klass) { Record }
|
6
|
+
let(:cache) { Looksy::NullCache.new }
|
7
|
+
let(:lookup) { Looksy::Lookup.new(klass, cache) }
|
8
|
+
|
9
|
+
describe '#all' do
|
10
|
+
it 'retrieves all records' do
|
11
|
+
lookup.all.should eql(klass.all)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#find_by_id' do
|
16
|
+
let(:record) { lookup.find_by_id(2) }
|
17
|
+
|
18
|
+
it 'retrieves record matching id' do
|
19
|
+
record.id.should eql(2)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#method_missing' do
|
24
|
+
context 'when finding multiple records' do
|
25
|
+
context 'when finding by single attribute' do
|
26
|
+
let(:records) { lookup.find_all_by_type('Developer') }
|
27
|
+
|
28
|
+
it 'returns the matching records' do
|
29
|
+
records.each { |record| record.type.should eql('Developer') }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when finding by multiple attributes' do
|
34
|
+
let(:records) { lookup.find_all_by_name_and_type('Artin', 'Developer') }
|
35
|
+
|
36
|
+
it 'returns the matching records' do
|
37
|
+
records.each do |record|
|
38
|
+
record.type.should eql('Developer')
|
39
|
+
record.name.should eql('Artin')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when finding single record' do
|
46
|
+
context 'when finding by single attribute' do
|
47
|
+
let(:record) { lookup.find_by_name('Artin') }
|
48
|
+
|
49
|
+
it 'returns the matching receord' do
|
50
|
+
record.name.should eql('Artin')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'when finding by multiple attributes' do
|
55
|
+
let(:record) { lookup.find_by_name_and_type('George', 'QA') }
|
56
|
+
|
57
|
+
it 'returns the matching record' do
|
58
|
+
record.name.should eql('George')
|
59
|
+
record.type.should eql('QA')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "looksy"
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Record < Struct.new(:id, :name, :type)
|
2
|
+
include Looksy::Cacheable
|
3
|
+
|
4
|
+
def self.all
|
5
|
+
@all ||= [
|
6
|
+
new(1, 'Artin', 'Developer'),
|
7
|
+
new(2, 'Paul', 'Developer'),
|
8
|
+
new(3, 'George', 'QA'),
|
9
|
+
new(4, 'Artin', 'Developer')
|
10
|
+
]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.first
|
14
|
+
all.first
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.second
|
18
|
+
all[1]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.third
|
22
|
+
all[2]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.last
|
26
|
+
all.last
|
27
|
+
end
|
28
|
+
|
29
|
+
def attributes
|
30
|
+
{
|
31
|
+
"id" => id,
|
32
|
+
"name" => name,
|
33
|
+
"type" => type
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: looksy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Artin Boghosian
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-16 00:00:00.000000000 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
requirement: &184782620 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *184782620
|
26
|
+
description: Add a caching layer to your ActiveRecord models that represent look up
|
27
|
+
tables
|
28
|
+
email:
|
29
|
+
- artinboghosian@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- .rspec
|
36
|
+
- Gemfile
|
37
|
+
- LICENSE.txt
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- lib/looksy.rb
|
41
|
+
- lib/looksy/cacheable.rb
|
42
|
+
- lib/looksy/dynamic_find_match.rb
|
43
|
+
- lib/looksy/lookup.rb
|
44
|
+
- lib/looksy/null_cache.rb
|
45
|
+
- lib/looksy/version.rb
|
46
|
+
- looksy.gemspec
|
47
|
+
- spec/lib/cacheable_spec.rb
|
48
|
+
- spec/lib/dynamic_find_match_spec.rb
|
49
|
+
- spec/lib/lookup_spec.rb
|
50
|
+
- spec/spec_helper.rb
|
51
|
+
- spec/support/record.rb
|
52
|
+
has_rdoc: true
|
53
|
+
homepage: ''
|
54
|
+
licenses: []
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options: []
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ! '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
requirements: []
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.6.2
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: Caching layer for look up tables
|
77
|
+
test_files:
|
78
|
+
- spec/lib/cacheable_spec.rb
|
79
|
+
- spec/lib/dynamic_find_match_spec.rb
|
80
|
+
- spec/lib/lookup_spec.rb
|
81
|
+
- spec/spec_helper.rb
|
82
|
+
- spec/support/record.rb
|