toystore 0.8.3 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|