cooler 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 +18 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +38 -0
- data/LICENSE +19 -0
- data/README.md +3 -0
- data/Rakefile +9 -0
- data/cooler.gemspec +25 -0
- data/lib/cooler/adapter.rb +52 -0
- data/lib/cooler/errors.rb +5 -0
- data/lib/cooler/model.rb +187 -0
- data/lib/cooler/version.rb +3 -0
- data/lib/cooler.rb +9 -0
- data/spec/cooler/model_spec.rb +234 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/test_models.rb +36 -0
- metadata +133 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cooler (0.0.1)
|
5
|
+
activesupport (~> 3.2.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ZenTest (4.8.3)
|
11
|
+
activesupport (3.2.9)
|
12
|
+
i18n (~> 0.6)
|
13
|
+
multi_json (~> 1.0)
|
14
|
+
autotest (4.4.6)
|
15
|
+
ZenTest (>= 4.4.1)
|
16
|
+
diff-lcs (1.1.3)
|
17
|
+
i18n (0.6.1)
|
18
|
+
multi_json (1.4.0)
|
19
|
+
rake (10.0.2)
|
20
|
+
rr (1.0.4)
|
21
|
+
rspec (2.12.0)
|
22
|
+
rspec-core (~> 2.12.0)
|
23
|
+
rspec-expectations (~> 2.12.0)
|
24
|
+
rspec-mocks (~> 2.12.0)
|
25
|
+
rspec-core (2.12.1)
|
26
|
+
rspec-expectations (2.12.0)
|
27
|
+
diff-lcs (~> 1.1.3)
|
28
|
+
rspec-mocks (2.12.0)
|
29
|
+
|
30
|
+
PLATFORMS
|
31
|
+
ruby
|
32
|
+
|
33
|
+
DEPENDENCIES
|
34
|
+
autotest (~> 4.4.0)
|
35
|
+
cooler!
|
36
|
+
rake
|
37
|
+
rr (~> 1.0.0)
|
38
|
+
rspec (~> 2.12.0)
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright © 2012 MoHound
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the “Software”), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
+
so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
data/cooler.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cooler/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "cooler"
|
8
|
+
gem.version = Cooler::VERSION
|
9
|
+
gem.authors = ["Ivan Valdes"]
|
10
|
+
gem.email = ["ivan@mohound.com"]
|
11
|
+
gem.description = %q{Mini ORM, agnostic to key value store databases}
|
12
|
+
gem.summary = %q{Mini ORM, agnostic to key value store databases}
|
13
|
+
gem.homepage = "http://github.com/mohound/coolio"
|
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
|
+
|
21
|
+
gem.add_dependency('activesupport', '~> 3.2.0')
|
22
|
+
gem.add_development_dependency('rspec', '~> 2.12.0')
|
23
|
+
gem.add_development_dependency('rr', '~> 1.0.0')
|
24
|
+
gem.add_development_dependency('autotest', '~> 4.4.0')
|
25
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Cooler
|
2
|
+
# Defines functions to get and set keys from the database.
|
3
|
+
class Adapter
|
4
|
+
class << self
|
5
|
+
# Gets the get block.
|
6
|
+
attr_reader :get
|
7
|
+
|
8
|
+
# Gets the set block.
|
9
|
+
attr_reader :set
|
10
|
+
end
|
11
|
+
|
12
|
+
# Sets the block that handles getting a key.
|
13
|
+
#
|
14
|
+
# block - The block that receives as argument the String with the
|
15
|
+
# queried key. It should return a Hash.
|
16
|
+
#
|
17
|
+
# Examples
|
18
|
+
#
|
19
|
+
# Cooler::Adapter.get = ->(key) { JSON.parse(redis.get(key)) }
|
20
|
+
# Cooler::Adapter.get = Proc.new do |key|
|
21
|
+
# begin
|
22
|
+
# couchbase[key]
|
23
|
+
# rescue Couchbase::NotFound
|
24
|
+
# puts 'uh oh'
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# Returns nothing.
|
29
|
+
def self.get=(block)
|
30
|
+
raise 'Argument must be a block' unless Proc === block
|
31
|
+
@get = block
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sets the block that handles storing a key.
|
35
|
+
#
|
36
|
+
# block - The block that receives as argument the String with the
|
37
|
+
# queried key, and serialized object.
|
38
|
+
#
|
39
|
+
# Examples
|
40
|
+
#
|
41
|
+
# Cooler::Adapter.set = ->(key, value) { couchbase[key] = value }
|
42
|
+
# Cooler::Adapter.get = lambda do |key, value|
|
43
|
+
# redis.set(key, value.to_json)
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# Returns nothing.
|
47
|
+
def self.set=(block)
|
48
|
+
raise 'Argument must be a block' unless Proc === block
|
49
|
+
@set = block
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/cooler/model.rb
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
module Cooler
|
2
|
+
# Defines the base methods to define a model class.
|
3
|
+
module Model
|
4
|
+
# Called after including in some class. For more information, read:
|
5
|
+
# http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-included
|
6
|
+
#
|
7
|
+
# Returns nothing.
|
8
|
+
def self.included(base)
|
9
|
+
base.send :extend, ClassMethods
|
10
|
+
base.instance_variable_set :@default_values, {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Sets the key for an instance.
|
14
|
+
attr_writer :_key
|
15
|
+
|
16
|
+
# Defines class methods for a Model.
|
17
|
+
module ClassMethods
|
18
|
+
# Called after the class is inherited. It will copy its default
|
19
|
+
# values, so subclass can use them too. For more informaiton, read:
|
20
|
+
# http://www.ruby-doc.org/core-1.9.3/Class.html#method-i-inherited
|
21
|
+
#
|
22
|
+
# Returns nothing.
|
23
|
+
def inherited(subclass)
|
24
|
+
subclass.instance_variable_set :@default_values, default_values.dup
|
25
|
+
subclass.instance_variable_set :@key_block, @key_block.dup
|
26
|
+
end
|
27
|
+
|
28
|
+
# Gets the default values.
|
29
|
+
attr_reader :default_values
|
30
|
+
|
31
|
+
# Define the key for documents of this instance.
|
32
|
+
#
|
33
|
+
# block - A block that contains how to build the key for an instance.
|
34
|
+
# It receives the instance as an argument. This block should
|
35
|
+
# return a String.
|
36
|
+
#
|
37
|
+
# Examples
|
38
|
+
#
|
39
|
+
# key { |click| ['click', click.app_key, click.tracking].join('_') }
|
40
|
+
# key do |install|
|
41
|
+
# ['install', install.app_key, install.finger_print].join('_')
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# Returns nothing if passing a block, the block if not passing it.
|
45
|
+
def key(&block)
|
46
|
+
if block_given?
|
47
|
+
@key_block = block
|
48
|
+
end
|
49
|
+
@key_block || Proc.new { }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Defines a default value for an attribute.
|
53
|
+
#
|
54
|
+
# attr - The String or Symbol attribute name, to add the default value.
|
55
|
+
# value - The Object to use as default value (optional).
|
56
|
+
# block - A block to be run as default value (optional).
|
57
|
+
#
|
58
|
+
# Examples
|
59
|
+
#
|
60
|
+
# default :clicks, []
|
61
|
+
# default :seconds, -> { Time.now.seconds }
|
62
|
+
# default :app_key, lambda { |install| install.app.key }
|
63
|
+
#
|
64
|
+
# Returns nothing.
|
65
|
+
# Raises an exception if attribute does not exist.
|
66
|
+
def default(attr, value, &block)
|
67
|
+
unless instance_methods.include?("#{attr}=".to_sym) &&
|
68
|
+
instance_methods.include?(attr.to_sym)
|
69
|
+
raise "Unknown attribute #{attr}"
|
70
|
+
end
|
71
|
+
@default_values[attr.to_sym] = value || block
|
72
|
+
end
|
73
|
+
|
74
|
+
# Gets an Object from Couchbase, and construct an instance by its
|
75
|
+
# key. In order to use Hash version, the class should define a key.
|
76
|
+
#
|
77
|
+
# key - The String that contains the key to query the object. Or the
|
78
|
+
# Hash with the value defined in the key to search. See examples
|
79
|
+
# for extra reference.
|
80
|
+
#
|
81
|
+
# Examples
|
82
|
+
#
|
83
|
+
# class Install
|
84
|
+
# key { |i| "install_#{i.app_key}" }
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# install = Install.get(app_key: '123')
|
88
|
+
# # => Gets object with key: 'install_123'
|
89
|
+
#
|
90
|
+
# install = Install.get('install_123')
|
91
|
+
# # => Gets object with key: 'install_123'
|
92
|
+
#
|
93
|
+
# Return a Model instance or nothing if not found.
|
94
|
+
def get(key)
|
95
|
+
if Hash === key
|
96
|
+
raise 'Key not defined' unless @key_block
|
97
|
+
key = @key_block.(OpenStruct.new(key))
|
98
|
+
end
|
99
|
+
result = Cooler::Adapter.get.(key) and new(key, result)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Gets an Object from Couchbase, and construct an instance by its
|
103
|
+
# key. Raises an error if not found.
|
104
|
+
#
|
105
|
+
# key - The String that contains the key to query the object.
|
106
|
+
#
|
107
|
+
# Return a Model instance.
|
108
|
+
# Raises Cooler::NotFound if not found. Compatible with
|
109
|
+
# Sinatra::NotFound;
|
110
|
+
# http://www.sinatrarb.com/intro.html#Not%20Found
|
111
|
+
def get!(key)
|
112
|
+
get(key) || raise(Cooler::NotFound)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Initializes a new instance, and saves it at the same time.
|
116
|
+
#
|
117
|
+
# attrs - May contain at the first position, the String key for the
|
118
|
+
# document owner of the instance. Then it a Hash with its
|
119
|
+
# attributes (default: {}).
|
120
|
+
#
|
121
|
+
# Returns the Object new instance.
|
122
|
+
def create(*attrs)
|
123
|
+
instance = new(*attrs)
|
124
|
+
instance.save
|
125
|
+
instance
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Initialize a Base.
|
130
|
+
#
|
131
|
+
# attrs - May contain at the first position, the String key for the
|
132
|
+
# document owner of the instance. Then it a Hash with its
|
133
|
+
# attributes (default: {}).
|
134
|
+
def initialize(*attrs)
|
135
|
+
@_key = attrs.shift if String === attrs.first
|
136
|
+
self.attributes = attrs.shift || {}
|
137
|
+
set_default_values
|
138
|
+
end
|
139
|
+
|
140
|
+
# The key for the document.
|
141
|
+
#
|
142
|
+
# Returns the String key for this document.
|
143
|
+
def _key
|
144
|
+
@_key || self.class.key.(self)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Assigns attributes from the passed Hash. Only setteable attributes
|
148
|
+
# will be assigned.
|
149
|
+
#
|
150
|
+
# attributes - The Hash with the attributes to assign.
|
151
|
+
#
|
152
|
+
# Returns nothing.
|
153
|
+
def attributes=(attributes)
|
154
|
+
return unless Hash === attributes
|
155
|
+
attributes = attributes.symbolize_keys
|
156
|
+
attributes.each { |k, v| send("#{k}=", v) if respond_to?("#{k}=") }
|
157
|
+
end
|
158
|
+
|
159
|
+
# Provides basic serialization of the instance, it includes only
|
160
|
+
# its Struct attributes, and not those defined from attr_accessor.
|
161
|
+
#
|
162
|
+
# Returns the serialized Hash.
|
163
|
+
def serializable_hash
|
164
|
+
Hash[*each_pair.map { |k, v| [k, v] }.flatten]
|
165
|
+
end
|
166
|
+
|
167
|
+
# Saves persisted attributes to the database.
|
168
|
+
#
|
169
|
+
# Returns true if saved, false if not.
|
170
|
+
def save
|
171
|
+
Cooler::Adapter.set.(_key, serializable_hash)
|
172
|
+
!Cooler::Adapter.get.(_key).nil?
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
def set_default_values
|
177
|
+
self.class.default_values.each do |key, value|
|
178
|
+
if send(key).nil?
|
179
|
+
if Proc === value
|
180
|
+
value = value.arity.zero? ? value.() : value.(self)
|
181
|
+
end
|
182
|
+
send("#{key}=", value)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
data/lib/cooler.rb
ADDED
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cooler::Model do
|
4
|
+
describe 'initialize' do
|
5
|
+
it 'should return an empty instance if not passing arguments' do
|
6
|
+
instance = TestModel.new
|
7
|
+
instance.foo.should be_nil
|
8
|
+
instance.bar.should be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should populate attributes if passing a Hash' do
|
12
|
+
instance = TestModel.new(foo: 'bar', nil: true)
|
13
|
+
instance.foo.should == 'bar'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should set its key' do
|
17
|
+
instance = TestModel.new('keykey', foo: 'bar')
|
18
|
+
instance._key.should == 'keykey'
|
19
|
+
instance.foo.should == 'bar'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should not modify passed attributes' do
|
23
|
+
attrs = { 'foo' => 'bar' }
|
24
|
+
instance = TestModel.new(attrs)
|
25
|
+
attrs.should have_key('foo')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'key' do
|
30
|
+
it 'should set its key to a new format' do
|
31
|
+
TestModelNewKey.key { |i| "foo_bar_#{i.foo}" }
|
32
|
+
t = TestModelNewKey.new(foo: 'baaar')
|
33
|
+
t._key.should == 'foo_bar_baaar'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '_key' do
|
38
|
+
it 'should be the passed key' do
|
39
|
+
instance = TestModel.new('keykey')
|
40
|
+
instance._key.should == 'keykey'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should be the constructed key if not passing one' do
|
44
|
+
instance = TestModel.new
|
45
|
+
instance._key.should == 'test_'
|
46
|
+
instance.foo = 'bacon'
|
47
|
+
instance._key.should == 'test_bacon'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should return nil if no key block registered' do
|
51
|
+
expect { TestModelNoKey.new._key.should be_nil }.to_not raise_error
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should be able to se its key' do
|
55
|
+
instance = TestModel.new
|
56
|
+
instance._key = 'CHUNKY'
|
57
|
+
instance._key.should == 'CHUNKY'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'get' do
|
62
|
+
it "should return the instance if Adapter's get block returns something" do
|
63
|
+
mock(Cooler::Adapter).get do
|
64
|
+
->(key) { key.should == 'test_bacon' && { foo: 'bar' } }
|
65
|
+
end
|
66
|
+
instance = TestModel.get('test_bacon')
|
67
|
+
instance.should be_an_instance_of(TestModel)
|
68
|
+
instance._key.should == 'test_bacon'
|
69
|
+
instance.foo.should == 'bar'
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should be nil if no results' do
|
73
|
+
mock(Cooler::Adapter).get { ->(k) { nil } }
|
74
|
+
TestModel.get('test_bacon').should be_nil
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should raise an error if no key defined and passing a Hash' do
|
78
|
+
expect { TestModelNoKey.get(foo: 'bar') }.to raise_error
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should get the instance if passing a Hash' do
|
82
|
+
mock(Cooler::Adapter).get do
|
83
|
+
->(key) { key.should == 'test_chunky' && { foo: 'bar' } }
|
84
|
+
end
|
85
|
+
instance = TestModel.get(foo: 'chunky')
|
86
|
+
instance.should be_an_instance_of(TestModel)
|
87
|
+
instance._key.should == 'test_chunky'
|
88
|
+
instance.foo.should == 'bar'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'get!' do
|
93
|
+
it 'should return the instance if found by Adapter' do
|
94
|
+
mock(Cooler::Adapter).get do
|
95
|
+
->(k) { k.should == 'test_bacon' && { foo: 'bar' } }
|
96
|
+
end
|
97
|
+
instance = TestModel.get!('test_bacon')
|
98
|
+
instance.should be_an_instance_of(TestModel)
|
99
|
+
instance._key.should == 'test_bacon'
|
100
|
+
instance.foo.should == 'bar'
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should raise an exception if no results' do
|
104
|
+
mock(Cooler::Adapter).get { ->(k) { nil } }
|
105
|
+
expect { TestModel.get!('test_bacon') }.to raise_error(Cooler::NotFound)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'attributes=' do
|
110
|
+
before(:each) do
|
111
|
+
@instance = TestModelUsingAttrAccessors.new
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should not do anything if not passing a Hash' do
|
115
|
+
expect { @instance.attributes = [] }.to_not raise_error
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should assign class properties' do
|
119
|
+
@instance.attributes = {'foo' => 'bar', chunky: 'BACON'}
|
120
|
+
@instance.foo.should == 'bar'
|
121
|
+
@instance.chunky.should == 'BACON'
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should assign attributes' do
|
125
|
+
@instance.attributes = {'bar' => 'foo', bacon: 'CHUNKY'}
|
126
|
+
@instance.bar.should == 'foo'
|
127
|
+
@instance.bacon.should == 'CHUNKY'
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should not modify passed attributes' do
|
131
|
+
@instance.attributes = attrs = { 'foo' => 'bar' }
|
132
|
+
attrs.should have_key('foo')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe 'serializable_hash' do
|
137
|
+
it 'should only include class properties' do
|
138
|
+
instance = TestModelUsingAttrAccessors.new(foo: 'bar', bacon: 'CHUNKY')
|
139
|
+
instance.serializable_hash.keys.should_not include(:bacon)
|
140
|
+
instance.serializable_hash.keys.should include(:foo)
|
141
|
+
instance.serializable_hash[:foo].should == 'bar'
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'using attribute accessors' do
|
146
|
+
it 'should assign the passed attribute when creating a new instance' do
|
147
|
+
instance = TestModelUsingAttrAccessors.new(foo: 'chunky', bar: 'bacon')
|
148
|
+
instance.foo.should == 'chunky'
|
149
|
+
instance.bar.should == 'bacon'
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe 'default' do
|
154
|
+
it 'should raise an exception if no such attribute' do
|
155
|
+
expect { TestModelWithDefaultValues.default :chunky }.
|
156
|
+
to raise_error
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should set the default value using an Object' do
|
160
|
+
TestModelWithDefaultValues.default :one, []
|
161
|
+
TestModelWithDefaultValues.default :four, []
|
162
|
+
instance = TestModelWithDefaultValues.new
|
163
|
+
instance.one.should == []
|
164
|
+
instance.four.should == []
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should set the default value using a block' do
|
168
|
+
TestModelWithDefaultValues.default :two, -> { 3*3 }
|
169
|
+
TestModelWithDefaultValues.default :four, lambda { 4*4 }
|
170
|
+
TestModelWithDefaultValues.default :five, Proc.new { 5*5 }
|
171
|
+
instance = TestModelWithDefaultValues.new
|
172
|
+
instance.two.should == 9
|
173
|
+
instance.four.should == 16
|
174
|
+
instance.five.should == 25
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'should set the default value using a block passing the instance' do
|
178
|
+
TestModelWithDefaultValues.default :three, ->(i) { i.one }
|
179
|
+
TestModelWithDefaultValues.default :six, lambda { |i| i.two }
|
180
|
+
TestModelWithDefaultValues.default :seven, Proc.new { |i| i.four }
|
181
|
+
instance = TestModelWithDefaultValues.new(one: '1', two: '2', four: '4')
|
182
|
+
instance.three.should == '1'
|
183
|
+
instance.six.should == '2'
|
184
|
+
instance.seven.should == '4'
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'should not set the default value if it already has another value' do
|
188
|
+
TestModelWithDefaultValues.default :eight, 8
|
189
|
+
TestModelWithDefaultValues.new(eight: '10').eight.should == '10'
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe 'save' do
|
194
|
+
it "should call Adapter's set to save its attributes and fail" do
|
195
|
+
instance = TestModelForSave.new(foo: 'bar', bar: 'BAR')
|
196
|
+
mock(Cooler::Adapter).set do
|
197
|
+
->(key, value) do
|
198
|
+
key.should == 'Chunky_BAR'
|
199
|
+
value.should == { foo: 'bar' }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
mock(Cooler::Adapter).get { ->(k) { nil } }
|
203
|
+
instance.save.should be_false
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should call Adapter to save its attributes and succed' do
|
207
|
+
instance = TestModelForSave.new(foo: 'bar', bar: 'BAR')
|
208
|
+
mock(Cooler::Adapter).set do
|
209
|
+
->(key, value) do
|
210
|
+
key.should == 'Chunky_BAR'
|
211
|
+
value.should == { foo: 'bar' }
|
212
|
+
end
|
213
|
+
end
|
214
|
+
mock(Cooler::Adapter).get { ->(k) { k.should == 'Chunky_BAR' } }
|
215
|
+
instance.save.should be_true
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe 'create' do
|
220
|
+
it 'should call Adapter to save its attributes' do
|
221
|
+
mock(Cooler::Adapter).set do
|
222
|
+
->(key, value) do
|
223
|
+
key.should == 'Chunky_BAR'
|
224
|
+
value.should == { foo: 'bar' }
|
225
|
+
end
|
226
|
+
end
|
227
|
+
mock(Cooler::Adapter).get do
|
228
|
+
->(k) { k.should == 'Chunky_BAR' && { foo: 'bar' } }
|
229
|
+
end
|
230
|
+
instance = TestModelForSave.create(foo: 'bar', bar: 'BAR')
|
231
|
+
instance.should be_an_instance_of(TestModelForSave)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
dir = File.dirname(File.expand_path(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(dir, '..', 'lib'))
|
3
|
+
$LOAD_PATH.unshift(dir)
|
4
|
+
|
5
|
+
require 'cooler'
|
6
|
+
|
7
|
+
Dir[File.join(dir, 'support', '**', '*.rb')].each { |f| require f }
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.mock_with :rr
|
11
|
+
config.order = 'random'
|
12
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class TestModel < Struct.new(:foo, :bar)
|
2
|
+
include Cooler::Model
|
3
|
+
key { |i| "test_#{i.foo}" }
|
4
|
+
end
|
5
|
+
|
6
|
+
class TestModelNewKey < Struct.new(:foo, :bar)
|
7
|
+
include Cooler::Model
|
8
|
+
end
|
9
|
+
|
10
|
+
class TestModelDuplicateAttrs < Struct.new(:foo, :bar)
|
11
|
+
include Cooler::Model
|
12
|
+
end
|
13
|
+
|
14
|
+
class TestModelNoAttrs
|
15
|
+
include Cooler::Model
|
16
|
+
end
|
17
|
+
|
18
|
+
class TestModelNoKey
|
19
|
+
include Cooler::Model
|
20
|
+
end
|
21
|
+
|
22
|
+
class TestModelUsingAttrAccessors < Struct.new(:foo, :chunky)
|
23
|
+
include Cooler::Model
|
24
|
+
attr_accessor :bar, :bacon
|
25
|
+
end
|
26
|
+
|
27
|
+
class TestModelWithDefaultValues < Struct.new(:one, :two, :three)
|
28
|
+
include Cooler::Model
|
29
|
+
attr_accessor :four, :five, :six, :seven, :eight, :nine
|
30
|
+
end
|
31
|
+
|
32
|
+
class TestModelForSave < Struct.new(:foo)
|
33
|
+
include Cooler::Model
|
34
|
+
attr_accessor :bar
|
35
|
+
key { |i| "Chunky_#{i.bar}" }
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cooler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ivan Valdes
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.2.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.2.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.12.0
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 2.12.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rr
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.0.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.0.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: autotest
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 4.4.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 4.4.0
|
78
|
+
description: Mini ORM, agnostic to key value store databases
|
79
|
+
email:
|
80
|
+
- ivan@mohound.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- Gemfile
|
87
|
+
- Gemfile.lock
|
88
|
+
- LICENSE
|
89
|
+
- README.md
|
90
|
+
- Rakefile
|
91
|
+
- cooler.gemspec
|
92
|
+
- lib/cooler.rb
|
93
|
+
- lib/cooler/adapter.rb
|
94
|
+
- lib/cooler/errors.rb
|
95
|
+
- lib/cooler/model.rb
|
96
|
+
- lib/cooler/version.rb
|
97
|
+
- spec/cooler/model_spec.rb
|
98
|
+
- spec/spec_helper.rb
|
99
|
+
- spec/support/test_models.rb
|
100
|
+
homepage: http://github.com/mohound/coolio
|
101
|
+
licenses: []
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
segments:
|
113
|
+
- 0
|
114
|
+
hash: 2911025146145919076
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ! '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
segments:
|
122
|
+
- 0
|
123
|
+
hash: 2911025146145919076
|
124
|
+
requirements: []
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 1.8.23
|
127
|
+
signing_key:
|
128
|
+
specification_version: 3
|
129
|
+
summary: Mini ORM, agnostic to key value store databases
|
130
|
+
test_files:
|
131
|
+
- spec/cooler/model_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
- spec/support/test_models.rb
|