toystore 0.8.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/.gitignore +1 -2
  2. data/Changelog.md +9 -0
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +71 -0
  5. data/Guardfile +15 -0
  6. data/README.md +28 -0
  7. data/examples/attributes_abbreviation.rb +1 -2
  8. data/examples/attributes_virtual.rb +1 -2
  9. data/examples/identity_map.rb +7 -12
  10. data/examples/memcached.rb +1 -1
  11. data/examples/memory.rb +1 -1
  12. data/examples/mongo.rb +1 -1
  13. data/examples/redis.rb +1 -1
  14. data/examples/riak.rb +1 -1
  15. data/lib/toy.rb +40 -39
  16. data/lib/toy/attribute.rb +1 -6
  17. data/lib/toy/attributes.rb +61 -90
  18. data/lib/toy/caching.rb +11 -13
  19. data/lib/toy/callbacks.rb +12 -31
  20. data/lib/toy/cloneable.rb +20 -0
  21. data/lib/toy/collection.rb +8 -7
  22. data/lib/toy/dirty.rb +17 -36
  23. data/lib/toy/dirty_store.rb +32 -0
  24. data/lib/toy/equality.rb +2 -0
  25. data/lib/toy/extensions/boolean.rb +22 -18
  26. data/lib/toy/identity_map.rb +39 -62
  27. data/lib/toy/list.rb +23 -22
  28. data/lib/toy/logger.rb +2 -17
  29. data/lib/toy/mass_assignment_security.rb +3 -5
  30. data/lib/toy/middleware/identity_map.rb +23 -4
  31. data/lib/toy/object.rb +16 -0
  32. data/lib/toy/persistence.rb +72 -62
  33. data/lib/toy/proxies/list.rb +19 -18
  34. data/lib/toy/proxies/proxy.rb +7 -6
  35. data/lib/toy/querying.rb +2 -4
  36. data/lib/toy/reference.rb +28 -26
  37. data/lib/toy/reloadable.rb +17 -0
  38. data/lib/toy/serialization.rb +25 -25
  39. data/lib/toy/store.rb +3 -11
  40. data/lib/toy/validations.rb +9 -28
  41. data/lib/toy/version.rb +1 -1
  42. data/perf/reads.rb +7 -9
  43. data/perf/writes.rb +6 -8
  44. data/spec/helper.rb +3 -1
  45. data/spec/support/constants.rb +1 -4
  46. data/spec/support/identity_map_matcher.rb +5 -5
  47. data/spec/support/objects.rb +38 -0
  48. data/spec/toy/attribute_spec.rb +1 -1
  49. data/spec/toy/attributes_spec.rb +1 -153
  50. data/spec/toy/callbacks_spec.rb +1 -45
  51. data/spec/toy/cloneable_spec.rb +47 -0
  52. data/spec/toy/dirty_spec.rb +12 -44
  53. data/spec/toy/dirty_store_spec.rb +47 -0
  54. data/spec/toy/equality_spec.rb +5 -19
  55. data/spec/toy/extensions/boolean_spec.rb +2 -0
  56. data/spec/toy/identity/uuid_key_factory_spec.rb +2 -2
  57. data/spec/toy/identity_map_spec.rb +45 -37
  58. data/spec/toy/identity_spec.rb +1 -1
  59. data/spec/toy/inspect_spec.rb +1 -1
  60. data/spec/toy/lists_spec.rb +20 -5
  61. data/spec/toy/logger_spec.rb +1 -29
  62. data/spec/toy/mass_assignment_security_spec.rb +16 -5
  63. data/spec/toy/middleware/identity_map_spec.rb +68 -2
  64. data/spec/toy/persistence_spec.rb +88 -30
  65. data/spec/toy/reference_spec.rb +0 -1
  66. data/spec/toy/references_spec.rb +20 -0
  67. data/spec/toy/reloadable_spec.rb +81 -0
  68. data/spec/toy/serialization_spec.rb +1 -110
  69. data/spec/toy/validations_spec.rb +0 -21
  70. data/spec/toy_spec.rb +4 -5
  71. data/test/lint_test.rb +1 -1
  72. metadata +21 -26
  73. data/.autotest +0 -11
  74. data/LOGGING.rdoc +0 -12
  75. data/README.rdoc +0 -27
  76. data/examples/models.rb +0 -51
  77. data/lib/toy/dolly.rb +0 -30
  78. data/lib/toy/embedded_list.rb +0 -45
  79. data/lib/toy/embedded_lists.rb +0 -68
  80. data/lib/toy/index.rb +0 -74
  81. data/lib/toy/indices.rb +0 -56
  82. data/lib/toy/proxies/embedded_list.rb +0 -79
  83. data/spec/toy/dolly_spec.rb +0 -76
  84. data/spec/toy/embedded_list_spec.rb +0 -607
  85. data/spec/toy/embedded_lists_spec.rb +0 -172
  86. data/spec/toy/index_spec.rb +0 -230
  87. data/spec/toy/indices_spec.rb +0 -141
  88. data/specs.watchr +0 -52
@@ -1,7 +1,7 @@
1
1
  require 'helper'
2
2
 
3
3
  describe Toy::Identity do
4
- uses_constants('User', 'Piece')
4
+ uses_objects('User', 'Piece')
5
5
 
6
6
  describe "setting the key" do
7
7
  it "should set key factory to UUIDKeyFactory" do
@@ -1,7 +1,7 @@
1
1
  require 'helper'
2
2
 
3
3
  describe Toy::Attributes do
4
- uses_constants('User')
4
+ uses_objects('User')
5
5
 
6
6
  before do
7
7
  User.attribute(:name, String)
@@ -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
@@ -1,7 +1,7 @@
1
1
  require 'helper'
2
2
 
3
3
  describe Toy::Logger do
4
- uses_constants('User')
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.without_identity_map do
54
- user = User.get(user.id)
55
- user.admin.should be_true
56
- user.name.should == 'John'
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.identity_map.should_receive(:clear).twice
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.identity_map.should_receive(:clear).twice
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 ".store" do
10
+ describe ".adapter" do
11
11
  it "sets if arguments and reads if not" do
12
- User.store(:memory, {})
13
- User.store.should == Adapter[:memory].new({})
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.store(:memory, {})
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.store(:memory, {}, :something => true)
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.store(:memory)
28
+ klass.adapter(:memory)
29
29
  end.should raise_error(ArgumentError, 'Client is required')
30
30
  end
31
31
 
32
- it "raises argument error if no name or client provided and has not been set" do
33
- lambda do
34
- klass.store
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 "returns false if store not set" do
46
- klass.has_store?.should be_false
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.store.read(doc.id).should == {
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 "#store" do
111
+ describe "#adapter" do
117
112
  it "delegates to class" do
118
- User.new.store.should equal(User.store)
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.store.read(@doc.id).should_not include('accepted_terms')
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.store.read(@doc.id)
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 "stores in same key" do
225
+ it "does not change primary key" do
182
226
  doc.id.should == @key
183
227
  end
184
228
 
185
- it "updates value in store" do
186
- User.store.read(doc.id).should_not == @value
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.store.read(@doc.id).should_not include('accepted_terms')
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 from the store" do
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 from the store" do
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
@@ -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
@@ -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