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 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