toystore 0.8.3 → 0.9.0
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 +1 -2
- data/Changelog.md +9 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +71 -0
- data/Guardfile +15 -0
- data/README.md +28 -0
- data/examples/attributes_abbreviation.rb +1 -2
- data/examples/attributes_virtual.rb +1 -2
- data/examples/identity_map.rb +7 -12
- data/examples/memcached.rb +1 -1
- data/examples/memory.rb +1 -1
- data/examples/mongo.rb +1 -1
- data/examples/redis.rb +1 -1
- data/examples/riak.rb +1 -1
- data/lib/toy.rb +40 -39
- data/lib/toy/attribute.rb +1 -6
- data/lib/toy/attributes.rb +61 -90
- data/lib/toy/caching.rb +11 -13
- data/lib/toy/callbacks.rb +12 -31
- data/lib/toy/cloneable.rb +20 -0
- data/lib/toy/collection.rb +8 -7
- data/lib/toy/dirty.rb +17 -36
- data/lib/toy/dirty_store.rb +32 -0
- data/lib/toy/equality.rb +2 -0
- data/lib/toy/extensions/boolean.rb +22 -18
- data/lib/toy/identity_map.rb +39 -62
- data/lib/toy/list.rb +23 -22
- data/lib/toy/logger.rb +2 -17
- data/lib/toy/mass_assignment_security.rb +3 -5
- data/lib/toy/middleware/identity_map.rb +23 -4
- data/lib/toy/object.rb +16 -0
- data/lib/toy/persistence.rb +72 -62
- data/lib/toy/proxies/list.rb +19 -18
- data/lib/toy/proxies/proxy.rb +7 -6
- data/lib/toy/querying.rb +2 -4
- data/lib/toy/reference.rb +28 -26
- data/lib/toy/reloadable.rb +17 -0
- data/lib/toy/serialization.rb +25 -25
- data/lib/toy/store.rb +3 -11
- data/lib/toy/validations.rb +9 -28
- data/lib/toy/version.rb +1 -1
- data/perf/reads.rb +7 -9
- data/perf/writes.rb +6 -8
- data/spec/helper.rb +3 -1
- data/spec/support/constants.rb +1 -4
- data/spec/support/identity_map_matcher.rb +5 -5
- data/spec/support/objects.rb +38 -0
- data/spec/toy/attribute_spec.rb +1 -1
- data/spec/toy/attributes_spec.rb +1 -153
- data/spec/toy/callbacks_spec.rb +1 -45
- data/spec/toy/cloneable_spec.rb +47 -0
- data/spec/toy/dirty_spec.rb +12 -44
- data/spec/toy/dirty_store_spec.rb +47 -0
- data/spec/toy/equality_spec.rb +5 -19
- data/spec/toy/extensions/boolean_spec.rb +2 -0
- data/spec/toy/identity/uuid_key_factory_spec.rb +2 -2
- data/spec/toy/identity_map_spec.rb +45 -37
- data/spec/toy/identity_spec.rb +1 -1
- data/spec/toy/inspect_spec.rb +1 -1
- data/spec/toy/lists_spec.rb +20 -5
- data/spec/toy/logger_spec.rb +1 -29
- data/spec/toy/mass_assignment_security_spec.rb +16 -5
- data/spec/toy/middleware/identity_map_spec.rb +68 -2
- data/spec/toy/persistence_spec.rb +88 -30
- data/spec/toy/reference_spec.rb +0 -1
- data/spec/toy/references_spec.rb +20 -0
- data/spec/toy/reloadable_spec.rb +81 -0
- data/spec/toy/serialization_spec.rb +1 -110
- data/spec/toy/validations_spec.rb +0 -21
- data/spec/toy_spec.rb +4 -5
- data/test/lint_test.rb +1 -1
- metadata +21 -26
- data/.autotest +0 -11
- data/LOGGING.rdoc +0 -12
- data/README.rdoc +0 -27
- data/examples/models.rb +0 -51
- data/lib/toy/dolly.rb +0 -30
- data/lib/toy/embedded_list.rb +0 -45
- data/lib/toy/embedded_lists.rb +0 -68
- data/lib/toy/index.rb +0 -74
- data/lib/toy/indices.rb +0 -56
- data/lib/toy/proxies/embedded_list.rb +0 -79
- data/spec/toy/dolly_spec.rb +0 -76
- data/spec/toy/embedded_list_spec.rb +0 -607
- data/spec/toy/embedded_lists_spec.rb +0 -172
- data/spec/toy/index_spec.rb +0 -230
- data/spec/toy/indices_spec.rb +0 -141
- data/specs.watchr +0 -52
data/spec/toy/identity_spec.rb
CHANGED
data/spec/toy/inspect_spec.rb
CHANGED
data/spec/toy/lists_spec.rb
CHANGED
@@ -7,11 +7,6 @@ describe Toy::Lists do
|
|
7
7
|
User.lists.should == {}
|
8
8
|
end
|
9
9
|
|
10
|
-
it "does not share with embedded lists" do
|
11
|
-
Game.embedded_list(:moves)
|
12
|
-
Game.lists.should == {}
|
13
|
-
end
|
14
|
-
|
15
10
|
describe ".list?" do
|
16
11
|
before do
|
17
12
|
User.list(:games)
|
@@ -92,4 +87,24 @@ describe Toy::Lists do
|
|
92
87
|
end
|
93
88
|
end
|
94
89
|
end
|
90
|
+
|
91
|
+
describe "#clone" do
|
92
|
+
before do
|
93
|
+
User.list(:games)
|
94
|
+
|
95
|
+
@game = Game.create
|
96
|
+
@user = User.create(:games => [@game])
|
97
|
+
end
|
98
|
+
|
99
|
+
let(:game) { @game }
|
100
|
+
let(:user) { @user }
|
101
|
+
|
102
|
+
it "clones list id attributes" do
|
103
|
+
user.clone.game_ids.should_not equal(user.game_ids)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "clones the list" do
|
107
|
+
user.clone.games.should_not equal(user.games)
|
108
|
+
end
|
109
|
+
end
|
95
110
|
end
|
data/spec/toy/logger_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
describe Toy::Logger do
|
4
|
-
|
4
|
+
uses_objects('User')
|
5
5
|
|
6
6
|
before do
|
7
7
|
@logger = Toy.logger
|
@@ -18,32 +18,4 @@ describe Toy::Logger do
|
|
18
18
|
it "should use Toy.logger for instance" do
|
19
19
|
User.new.logger.should == Toy.logger
|
20
20
|
end
|
21
|
-
|
22
|
-
describe ".log_operation" do
|
23
|
-
let(:adapter) { Adapter[:memory].new({}) }
|
24
|
-
|
25
|
-
it "logs operation" do
|
26
|
-
Toy.logger = stub(:debug? => true)
|
27
|
-
User.logger.should_receive(:debug).with('TOYSTORE GET User :memory "foo"')
|
28
|
-
User.logger.should_receive(:debug).with(' "bar"')
|
29
|
-
User.log_operation(:get, User, adapter, 'foo', 'bar')
|
30
|
-
end
|
31
|
-
|
32
|
-
it "ignores operations that should not be logged" do
|
33
|
-
Toy.logger = stub(:debug? => true)
|
34
|
-
User.logger.should_receive(:debug).with('TOYSTORE IMG User :memory "foo"')
|
35
|
-
User.log_operation(:img, User, adapter, 'foo', 'bar')
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
describe "#log_operation" do
|
40
|
-
let(:adapter) { Adapter[:memory].new({}) }
|
41
|
-
|
42
|
-
it "logs operation" do
|
43
|
-
Toy.logger = stub(:debug? => true)
|
44
|
-
User.logger.should_receive(:debug).with('TOYSTORE GET User :memory "foo"')
|
45
|
-
User.logger.should_receive(:debug).with(' "bar"')
|
46
|
-
User.log_operation(:get, User, adapter, 'foo', 'bar')
|
47
|
-
end
|
48
|
-
end
|
49
21
|
end
|
@@ -31,6 +31,12 @@ describe Toy::MassAssignmentSecurity do
|
|
31
31
|
user.name.should == 'John'
|
32
32
|
end
|
33
33
|
|
34
|
+
it "should not ignore inaccessible attribute on #initialize_from_database" do
|
35
|
+
user = User.allocate.initialize_from_database(:name => 'John', :admin => true)
|
36
|
+
user.admin.should be_true
|
37
|
+
user.name.should == 'John'
|
38
|
+
end
|
39
|
+
|
34
40
|
it "should ignore inaccessible attribute on #attributes=" do
|
35
41
|
user = User.new
|
36
42
|
user.attributes = {:name => 'John', :admin => true}
|
@@ -50,11 +56,10 @@ describe Toy::MassAssignmentSecurity do
|
|
50
56
|
user.admin = true
|
51
57
|
user.save!
|
52
58
|
|
53
|
-
User.
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
59
|
+
from_db = User.get(user.id)
|
60
|
+
from_db.should_not equal(user)
|
61
|
+
from_db.admin.should be_true
|
62
|
+
from_db.name.should == 'John'
|
58
63
|
end
|
59
64
|
|
60
65
|
it "should ignore inaccessible attribute on #update_attributes" do
|
@@ -103,6 +108,12 @@ describe Toy::MassAssignmentSecurity do
|
|
103
108
|
user.name.should == 'John'
|
104
109
|
end
|
105
110
|
|
111
|
+
it "should not ignore protected attribute on #initialize_from_database" do
|
112
|
+
user = User.allocate.initialize_from_database(:name => 'John', :admin => true)
|
113
|
+
user.admin.should be_true
|
114
|
+
user.name.should == 'John'
|
115
|
+
end
|
116
|
+
|
106
117
|
it "should not ignore protected attributes on #initialize from the database" do
|
107
118
|
user = User.new(:name => 'John')
|
108
119
|
user.admin = true
|
@@ -7,25 +7,91 @@ describe Toy::Middleware::IdentityMap do
|
|
7
7
|
def app
|
8
8
|
@app ||= Rack::Builder.new do
|
9
9
|
use Toy::Middleware::IdentityMap
|
10
|
+
|
10
11
|
map "/" do
|
11
12
|
run lambda {|env| [200, {}, []] }
|
12
13
|
end
|
14
|
+
|
13
15
|
map "/fail" do
|
14
16
|
run lambda {|env| raise "FAIL!" }
|
15
17
|
end
|
16
18
|
end.to_app
|
17
19
|
end
|
18
20
|
|
21
|
+
before do
|
22
|
+
@enabled = Toy::IdentityMap.enabled
|
23
|
+
Toy::IdentityMap.enabled = false
|
24
|
+
end
|
25
|
+
|
26
|
+
after do
|
27
|
+
Toy::IdentityMap.enabled = @enabled
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should delegate" do
|
31
|
+
called = false
|
32
|
+
mw = Toy::Middleware::IdentityMap.new lambda { |env|
|
33
|
+
called = true
|
34
|
+
[200, {}, nil]
|
35
|
+
}
|
36
|
+
mw.call({})
|
37
|
+
called.should be_true
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should enable identity map during delegation" do
|
41
|
+
mw = Toy::Middleware::IdentityMap.new lambda { |env|
|
42
|
+
Toy::IdentityMap.should be_enabled
|
43
|
+
[200, {}, nil]
|
44
|
+
}
|
45
|
+
mw.call({})
|
46
|
+
end
|
47
|
+
|
48
|
+
class Enum < Struct.new(:iter)
|
49
|
+
def each(&b)
|
50
|
+
iter.call(&b)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should enable IM for body each" do
|
55
|
+
mw = Toy::Middleware::IdentityMap.new lambda { |env|
|
56
|
+
[200, {}, Enum.new(lambda { |&b|
|
57
|
+
Toy::IdentityMap.should be_enabled
|
58
|
+
b.call "hello"
|
59
|
+
})]
|
60
|
+
}
|
61
|
+
body = mw.call({}).last
|
62
|
+
body.each { |x| x.should eql('hello') }
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should disable IM after body close" do
|
66
|
+
mw = Toy::Middleware::IdentityMap.new lambda { |env| [200, {}, []] }
|
67
|
+
body = mw.call({}).last
|
68
|
+
Toy::IdentityMap.should be_enabled
|
69
|
+
body.close
|
70
|
+
Toy::IdentityMap.should_not be_enabled
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should clear IM after body close" do
|
74
|
+
mw = Toy::Middleware::IdentityMap.new lambda { |env| [200, {}, []] }
|
75
|
+
body = mw.call({}).last
|
76
|
+
|
77
|
+
Toy::IdentityMap.repository['hello'] = 'world'
|
78
|
+
Toy::IdentityMap.repository.should_not be_empty
|
79
|
+
|
80
|
+
body.close
|
81
|
+
|
82
|
+
Toy::IdentityMap.repository.should be_empty
|
83
|
+
end
|
84
|
+
|
19
85
|
context "with a successful request" do
|
20
86
|
it "clear the identity map" do
|
21
|
-
Toy.
|
87
|
+
Toy::IdentityMap.should_receive(:clear).twice
|
22
88
|
get '/'
|
23
89
|
end
|
24
90
|
end
|
25
91
|
|
26
92
|
context "when the request raises an error" do
|
27
93
|
it "clear the identity map" do
|
28
|
-
Toy.
|
94
|
+
Toy::IdentityMap.should_receive(:clear).once
|
29
95
|
get '/fail' rescue nil
|
30
96
|
end
|
31
97
|
end
|
@@ -7,43 +7,38 @@ describe Toy::Persistence do
|
|
7
7
|
Class.new { include Toy::Store }
|
8
8
|
end
|
9
9
|
|
10
|
-
describe ".
|
10
|
+
describe ".adapter" do
|
11
11
|
it "sets if arguments and reads if not" do
|
12
|
-
User.
|
13
|
-
User.
|
12
|
+
User.adapter(:memory, {})
|
13
|
+
User.adapter.should == Adapter[:memory].new({})
|
14
14
|
end
|
15
15
|
|
16
16
|
it "defaults options to empty hash" do
|
17
17
|
Adapter[:memory].should_receive(:new).with({}, {})
|
18
|
-
User.
|
18
|
+
User.adapter(:memory, {})
|
19
19
|
end
|
20
20
|
|
21
21
|
it "works with options" do
|
22
22
|
Adapter[:memory].should_receive(:new).with({}, :something => true)
|
23
|
-
User.
|
23
|
+
User.adapter(:memory, {}, :something => true)
|
24
24
|
end
|
25
25
|
|
26
26
|
it "raises argument error if name provided but not client" do
|
27
27
|
lambda do
|
28
|
-
klass.
|
28
|
+
klass.adapter(:memory)
|
29
29
|
end.should raise_error(ArgumentError, 'Client is required')
|
30
30
|
end
|
31
31
|
|
32
|
-
it "
|
33
|
-
|
34
|
-
|
35
|
-
end.should raise_error(StandardError, 'No store has been set')
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
describe ".has_store?" do
|
40
|
-
it "returns true if store set" do
|
41
|
-
klass.store(:memory, {})
|
42
|
-
klass.has_store?.should be_true
|
32
|
+
it "defaults to memory adapter if no name or client provided and has not been set" do
|
33
|
+
klass.adapter.should_not be_nil
|
34
|
+
klass.adapter.client.should eql({})
|
43
35
|
end
|
44
36
|
|
45
|
-
it "
|
46
|
-
|
37
|
+
it "allows changing adapter even after use of default" do
|
38
|
+
hash = {}
|
39
|
+
klass.adapter
|
40
|
+
klass.adapter :memory, hash
|
41
|
+
klass.adapter.client.should equal(hash)
|
47
42
|
end
|
48
43
|
end
|
49
44
|
|
@@ -56,7 +51,7 @@ describe Toy::Persistence do
|
|
56
51
|
let(:doc) { @doc }
|
57
52
|
|
58
53
|
it "creates key in database with attributes" do
|
59
|
-
User.
|
54
|
+
User.adapter.read(doc.id).should == {
|
60
55
|
'name' => 'John',
|
61
56
|
'age' => 50,
|
62
57
|
}
|
@@ -113,9 +108,58 @@ describe Toy::Persistence do
|
|
113
108
|
end
|
114
109
|
end
|
115
110
|
|
116
|
-
describe "#
|
111
|
+
describe "#adapter" do
|
117
112
|
it "delegates to class" do
|
118
|
-
User.new.
|
113
|
+
User.new.adapter.should equal(User.adapter)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "declaring an attribute with an abbreviation" do
|
118
|
+
before do
|
119
|
+
User.attribute(:twitter_access_token, String, :abbr => 'tat')
|
120
|
+
end
|
121
|
+
|
122
|
+
it "persists to adapter using abbreviation" do
|
123
|
+
user = User.create(:twitter_access_token => '1234')
|
124
|
+
raw = user.adapter.read(user.id)
|
125
|
+
raw['tat'].should == '1234'
|
126
|
+
raw.should_not have_key('twitter_access_token')
|
127
|
+
end
|
128
|
+
|
129
|
+
it "loads from store correctly" do
|
130
|
+
user = User.create(:twitter_access_token => '1234')
|
131
|
+
user = User.get(user.id)
|
132
|
+
user.twitter_access_token.should == '1234'
|
133
|
+
user.tat.should == '1234'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "#initialize_from_database" do
|
138
|
+
before do
|
139
|
+
User.attribute(:age, Integer, :default => 20)
|
140
|
+
@user = User.allocate
|
141
|
+
end
|
142
|
+
|
143
|
+
it "sets new record to false" do
|
144
|
+
@user.initialize_from_database
|
145
|
+
@user.should_not be_new_record
|
146
|
+
end
|
147
|
+
|
148
|
+
it "sets attributes" do
|
149
|
+
@user.initialize_from_database('age' => 21)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "sets defaults" do
|
153
|
+
@user.initialize_from_database
|
154
|
+
@user.age.should == 20
|
155
|
+
end
|
156
|
+
|
157
|
+
it "does not fail with nil" do
|
158
|
+
@user.initialize_from_database(nil).should == @user
|
159
|
+
end
|
160
|
+
|
161
|
+
it "returns self" do
|
162
|
+
@user.initialize_from_database.should == @user
|
119
163
|
end
|
120
164
|
end
|
121
165
|
|
@@ -163,7 +207,7 @@ describe Toy::Persistence do
|
|
163
207
|
end
|
164
208
|
|
165
209
|
it "does not persist virtual attributes" do
|
166
|
-
@doc.
|
210
|
+
@doc.adapter.read(@doc.id).should_not include('accepted_terms')
|
167
211
|
end
|
168
212
|
end
|
169
213
|
|
@@ -171,23 +215,23 @@ describe Toy::Persistence do
|
|
171
215
|
before do
|
172
216
|
@doc = User.create(:name => 'John', :age => 28)
|
173
217
|
@key = @doc.id
|
174
|
-
@value = User.
|
218
|
+
@value = User.adapter.read(@doc.id)
|
175
219
|
@doc.name = 'Bill'
|
176
220
|
@doc.accepted_terms = false
|
177
221
|
@doc.save
|
178
222
|
end
|
179
223
|
let(:doc) { @doc }
|
180
224
|
|
181
|
-
it "
|
225
|
+
it "does not change primary key" do
|
182
226
|
doc.id.should == @key
|
183
227
|
end
|
184
228
|
|
185
|
-
it "updates value in
|
186
|
-
User.
|
229
|
+
it "updates value in adapter" do
|
230
|
+
User.adapter.read(doc.id).should_not == @value
|
187
231
|
end
|
188
232
|
|
189
233
|
it "does not persist virtual attributes" do
|
190
|
-
@doc.
|
234
|
+
@doc.adapter.read(@doc.id).should_not include('accepted_terms')
|
191
235
|
end
|
192
236
|
|
193
237
|
it "updates the attributes in the instance" do
|
@@ -211,7 +255,7 @@ describe Toy::Persistence do
|
|
211
255
|
end
|
212
256
|
|
213
257
|
describe "#delete" do
|
214
|
-
it "should remove the instance
|
258
|
+
it "should remove the instance" do
|
215
259
|
doc = User.create
|
216
260
|
doc.delete
|
217
261
|
User.key?(doc.id).should be_false
|
@@ -219,7 +263,7 @@ describe Toy::Persistence do
|
|
219
263
|
end
|
220
264
|
|
221
265
|
describe "#destroy" do
|
222
|
-
it "should remove the instance
|
266
|
+
it "should remove the instance" do
|
223
267
|
doc = User.create
|
224
268
|
doc.destroy
|
225
269
|
User.key?(doc.id).should be_false
|
@@ -238,4 +282,18 @@ describe Toy::Persistence do
|
|
238
282
|
doc.should be_destroyed
|
239
283
|
end
|
240
284
|
end
|
285
|
+
|
286
|
+
describe "#clone" do
|
287
|
+
it "returns instance that is a new_record" do
|
288
|
+
User.new.clone.should be_new_record
|
289
|
+
User.create.clone.should be_new_record
|
290
|
+
end
|
291
|
+
|
292
|
+
it "is never destroyed" do
|
293
|
+
user = User.create
|
294
|
+
user.clone.should_not be_destroyed
|
295
|
+
user.destroy
|
296
|
+
user.clone.should_not be_destroyed
|
297
|
+
end
|
298
|
+
end
|
241
299
|
end
|
data/spec/toy/reference_spec.rb
CHANGED
@@ -316,7 +316,6 @@ describe Toy::Reference do
|
|
316
316
|
it "delegates #equal?" do
|
317
317
|
@game.user.should equal(@user)
|
318
318
|
@user.should equal(@game.user)
|
319
|
-
@game.user.should equal(User.get(@user.id)) # identity map
|
320
319
|
@game.user.should_not equal(User.create)
|
321
320
|
@game.user.should_not equal(@game)
|
322
321
|
end
|
data/spec/toy/references_spec.rb
CHANGED
@@ -83,4 +83,24 @@ describe Toy::References do
|
|
83
83
|
reference.options.should == {:some_option => true}
|
84
84
|
end
|
85
85
|
end
|
86
|
+
|
87
|
+
describe "#eql?" do
|
88
|
+
it "returns true if reference and target is same class and id" do
|
89
|
+
Game.reference(:user)
|
90
|
+
user = User.create
|
91
|
+
game = Game.create(:user => user)
|
92
|
+
user.should eql(game.user)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#equal?" do
|
97
|
+
it "returns true if same object through proxy" do
|
98
|
+
Game.reference(:user)
|
99
|
+
user = User.create
|
100
|
+
game = Game.create(:user => user)
|
101
|
+
|
102
|
+
user.should equal(game.user)
|
103
|
+
game.user.should equal(user)
|
104
|
+
end
|
105
|
+
end
|
86
106
|
end
|