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