looksy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in looksy.gemspec
4
+ gemspec
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,8 @@
1
+ require "looksy/version"
2
+ require "looksy/dynamic_find_match"
3
+ require "looksy/lookup"
4
+ require "looksy/null_cache"
5
+ require "looksy/cacheable"
6
+
7
+ module Looksy
8
+ end
@@ -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
@@ -0,0 +1,7 @@
1
+ module Looksy
2
+ class NullCache
3
+ def fetch(key, options = {}, &block)
4
+ block.call
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module Looksy
2
+ VERSION = "0.0.1"
3
+ 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
@@ -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