toystore 0.5

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 (113) hide show
  1. data/.autotest +11 -0
  2. data/.bundle/config +2 -0
  3. data/.gitignore +6 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +49 -0
  6. data/LICENSE +9 -0
  7. data/LOGGING.rdoc +16 -0
  8. data/README.rdoc +13 -0
  9. data/Rakefile +7 -0
  10. data/examples/memcached.rb +20 -0
  11. data/examples/memory.rb +20 -0
  12. data/examples/models.rb +51 -0
  13. data/examples/redis.rb +20 -0
  14. data/lib/toy.rb +81 -0
  15. data/lib/toy/attribute.rb +73 -0
  16. data/lib/toy/attributes.rb +137 -0
  17. data/lib/toy/caching.rb +20 -0
  18. data/lib/toy/callbacks.rb +48 -0
  19. data/lib/toy/collection.rb +55 -0
  20. data/lib/toy/connection.rb +28 -0
  21. data/lib/toy/dirty.rb +47 -0
  22. data/lib/toy/dolly.rb +30 -0
  23. data/lib/toy/embedded_list.rb +45 -0
  24. data/lib/toy/embedded_lists.rb +68 -0
  25. data/lib/toy/equality.rb +19 -0
  26. data/lib/toy/exceptions.rb +29 -0
  27. data/lib/toy/extensions/array.rb +22 -0
  28. data/lib/toy/extensions/boolean.rb +43 -0
  29. data/lib/toy/extensions/date.rb +24 -0
  30. data/lib/toy/extensions/float.rb +13 -0
  31. data/lib/toy/extensions/hash.rb +17 -0
  32. data/lib/toy/extensions/integer.rb +22 -0
  33. data/lib/toy/extensions/nil_class.rb +17 -0
  34. data/lib/toy/extensions/object.rb +26 -0
  35. data/lib/toy/extensions/set.rb +23 -0
  36. data/lib/toy/extensions/string.rb +17 -0
  37. data/lib/toy/extensions/time.rb +29 -0
  38. data/lib/toy/identity.rb +26 -0
  39. data/lib/toy/identity/abstract_key_factory.rb +10 -0
  40. data/lib/toy/identity/uuid_key_factory.rb +9 -0
  41. data/lib/toy/identity_map.rb +109 -0
  42. data/lib/toy/index.rb +74 -0
  43. data/lib/toy/indices.rb +56 -0
  44. data/lib/toy/inspect.rb +12 -0
  45. data/lib/toy/list.rb +46 -0
  46. data/lib/toy/lists.rb +37 -0
  47. data/lib/toy/logger.rb +26 -0
  48. data/lib/toy/mass_assignment_security.rb +16 -0
  49. data/lib/toy/persistence.rb +138 -0
  50. data/lib/toy/plugins.rb +23 -0
  51. data/lib/toy/proxies/embedded_list.rb +74 -0
  52. data/lib/toy/proxies/list.rb +97 -0
  53. data/lib/toy/proxies/proxy.rb +59 -0
  54. data/lib/toy/querying.rb +57 -0
  55. data/lib/toy/reference.rb +134 -0
  56. data/lib/toy/references.rb +19 -0
  57. data/lib/toy/serialization.rb +81 -0
  58. data/lib/toy/store.rb +36 -0
  59. data/lib/toy/timestamps.rb +22 -0
  60. data/lib/toy/validations.rb +45 -0
  61. data/lib/toy/version.rb +3 -0
  62. data/lib/toystore.rb +1 -0
  63. data/spec/helper.rb +35 -0
  64. data/spec/spec.opts +3 -0
  65. data/spec/support/constants.rb +41 -0
  66. data/spec/support/identity_map_matcher.rb +20 -0
  67. data/spec/support/name_and_number_key_factory.rb +5 -0
  68. data/spec/toy/attribute_spec.rb +176 -0
  69. data/spec/toy/attributes_spec.rb +394 -0
  70. data/spec/toy/caching_spec.rb +62 -0
  71. data/spec/toy/callbacks_spec.rb +97 -0
  72. data/spec/toy/connection_spec.rb +47 -0
  73. data/spec/toy/dirty_spec.rb +99 -0
  74. data/spec/toy/dolly_spec.rb +76 -0
  75. data/spec/toy/embedded_list_spec.rb +607 -0
  76. data/spec/toy/embedded_lists_spec.rb +172 -0
  77. data/spec/toy/equality_spec.rb +46 -0
  78. data/spec/toy/exceptions_spec.rb +18 -0
  79. data/spec/toy/extensions/array_spec.rb +25 -0
  80. data/spec/toy/extensions/boolean_spec.rb +41 -0
  81. data/spec/toy/extensions/date_spec.rb +48 -0
  82. data/spec/toy/extensions/float_spec.rb +14 -0
  83. data/spec/toy/extensions/hash_spec.rb +21 -0
  84. data/spec/toy/extensions/integer_spec.rb +29 -0
  85. data/spec/toy/extensions/nil_class_spec.rb +14 -0
  86. data/spec/toy/extensions/set_spec.rb +27 -0
  87. data/spec/toy/extensions/string_spec.rb +28 -0
  88. data/spec/toy/extensions/time_spec.rb +94 -0
  89. data/spec/toy/identity/abstract_key_factory_spec.rb +7 -0
  90. data/spec/toy/identity/uuid_key_factory_spec.rb +7 -0
  91. data/spec/toy/identity_map_spec.rb +150 -0
  92. data/spec/toy/identity_spec.rb +52 -0
  93. data/spec/toy/index_spec.rb +230 -0
  94. data/spec/toy/indices_spec.rb +141 -0
  95. data/spec/toy/inspect_spec.rb +15 -0
  96. data/spec/toy/list_spec.rb +576 -0
  97. data/spec/toy/lists_spec.rb +95 -0
  98. data/spec/toy/logger_spec.rb +33 -0
  99. data/spec/toy/mass_assignment_security_spec.rb +116 -0
  100. data/spec/toy/persistence_spec.rb +312 -0
  101. data/spec/toy/plugins_spec.rb +39 -0
  102. data/spec/toy/querying_spec.rb +162 -0
  103. data/spec/toy/reference_spec.rb +400 -0
  104. data/spec/toy/references_spec.rb +86 -0
  105. data/spec/toy/serialization_spec.rb +354 -0
  106. data/spec/toy/store_spec.rb +41 -0
  107. data/spec/toy/timestamps_spec.rb +63 -0
  108. data/spec/toy/validations_spec.rb +171 -0
  109. data/spec/toy_spec.rb +26 -0
  110. data/specs.watchr +52 -0
  111. data/test/lint_test.rb +40 -0
  112. data/toystore.gemspec +24 -0
  113. metadata +290 -0
@@ -0,0 +1,95 @@
1
+ require 'helper'
2
+
3
+ describe Toy::Lists do
4
+ uses_constants('User', 'Game', 'Move')
5
+
6
+ it "defaults lists to empty hash" do
7
+ User.lists.should == {}
8
+ end
9
+
10
+ it "does not share with embedded lists" do
11
+ Game.embedded_list(:moves)
12
+ Game.lists.should == {}
13
+ end
14
+
15
+ describe ".list?" do
16
+ before do
17
+ User.list(:games)
18
+ end
19
+
20
+ it "returns true if attribute (symbol)" do
21
+ User.list?(:games).should be_true
22
+ end
23
+
24
+ it "returns true if attribute (string)" do
25
+ User.list?('games').should be_true
26
+ end
27
+
28
+ it "returns false if not attribute" do
29
+ User.list?(:foobar).should be_false
30
+ end
31
+ end
32
+
33
+ describe "declaring a list" do
34
+ describe "using conventions" do
35
+ before do
36
+ @list = User.list(:games)
37
+ end
38
+
39
+ it "knows about its lists" do
40
+ User.lists[:games].should == Toy::List.new(User, :games)
41
+ end
42
+
43
+ it "returns list" do
44
+ @list.should == Toy::List.new(User, :games)
45
+ end
46
+ end
47
+
48
+ describe "with type" do
49
+ before do
50
+ @list = User.list(:active_games, Game)
51
+ end
52
+ let(:list) { @list }
53
+
54
+ it "sets type" do
55
+ list.type.should be(Game)
56
+ end
57
+
58
+ it "sets options to hash" do
59
+ list.options.should be_instance_of(Hash)
60
+ end
61
+ end
62
+
63
+ describe "with options" do
64
+ before do
65
+ @list = User.list(:games, :dependent => true)
66
+ end
67
+ let(:list) { @list }
68
+
69
+ it "sets type" do
70
+ list.type.should be(Game)
71
+ end
72
+
73
+ it "sets options" do
74
+ list.options.should have_key(:dependent)
75
+ list.options[:dependent].should be_true
76
+ end
77
+ end
78
+
79
+ describe "with type and options" do
80
+ before do
81
+ @list = User.list(:active_games, Game, :dependent => true)
82
+ end
83
+ let(:list) { @list }
84
+
85
+ it "sets type" do
86
+ list.type.should be(Game)
87
+ end
88
+
89
+ it "sets options" do
90
+ list.options.should have_key(:dependent)
91
+ list.options[:dependent].should be_true
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,33 @@
1
+ require 'helper'
2
+
3
+ describe Toy::Logger do
4
+ uses_constants('User')
5
+
6
+ it "should use Toy.logger for class" do
7
+ User.logger.should == Toy.logger
8
+ end
9
+
10
+ it "should use Toy.logger for instance" do
11
+ User.new.logger.should == Toy.logger
12
+ end
13
+
14
+ describe ".log_operation" do
15
+ let(:adapter) { Adapter[:memory].new({}) }
16
+
17
+ it "logs operation" do
18
+ User.logger.should_receive(:debug).with('ToyStore GET :memory "foo"')
19
+ User.logger.should_receive(:debug).with(' "bar"')
20
+ User.log_operation('GET', adapter, 'foo', 'bar')
21
+ end
22
+ end
23
+
24
+ describe "#log_operation" do
25
+ let(:adapter) { Adapter[:memory].new({}) }
26
+
27
+ it "logs operation" do
28
+ User.logger.should_receive(:debug).with('ToyStore GET :memory "foo"')
29
+ User.logger.should_receive(:debug).with(' "bar"')
30
+ User.log_operation('GET', adapter, 'foo', 'bar')
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,116 @@
1
+ require 'helper'
2
+
3
+ describe Toy::MassAssignmentSecurity do
4
+ uses_constants('User', 'Game')
5
+
6
+ describe 'A class with accessible attributes' do
7
+ before do
8
+ User.attribute :name, String
9
+ User.attribute :admin, Boolean, :default => false
10
+
11
+ User.attr_accessible :name
12
+ @user = User.create(:name => 'Steve Sloan')
13
+ end
14
+
15
+ it 'should have accessible attributes class method' do
16
+ User.accessible_attributes.should include(:name)
17
+ end
18
+
19
+ it "should default accessible attributes to empty" do
20
+ Game.accessible_attributes.should be_empty
21
+ end
22
+
23
+ it "should assign inaccessible attribute through accessor" do
24
+ @user.admin = true
25
+ @user.admin.should be_true
26
+ end
27
+
28
+ it "should ignore inaccessible attribute on #initialize" do
29
+ user = User.new(:name => 'John', :admin => true)
30
+ user.admin.should be_false
31
+ user.name.should == 'John'
32
+ end
33
+
34
+ it "should not ignore inaccessible attributes on #initialize from the database" do
35
+ user = User.new(:name => 'John')
36
+ user.admin = true
37
+ user.save!
38
+
39
+ user = User.get(user.id)
40
+ user.admin.should be_true
41
+ user.name.should == 'John'
42
+ end
43
+
44
+ it "should ignore inaccessible attribute on #update_attributes" do
45
+ @user.update_attributes(:name => 'Ren Hoek', :admin => true)
46
+ @user.name.should == 'Ren Hoek'
47
+ @user.admin.should be_false
48
+ end
49
+
50
+ it "should be indifferent to whether the accessible keys are strings or symbols" do
51
+ @user.update_attributes("name" => 'Stimpson J. Cat', "admin" => true)
52
+ @user.name.should == 'Stimpson J. Cat'
53
+ @user.admin.should be_false
54
+ end
55
+
56
+ it "should accept nil as constructor's argument without raising exception" do
57
+ lambda { User.new(nil) }.should_not raise_error
58
+ end
59
+ end
60
+
61
+ describe 'A class with protected attributes' do
62
+ before do
63
+ User.attribute :name, String
64
+ User.attribute :admin, Boolean, :default => false
65
+
66
+ User.attr_protected :admin
67
+
68
+ @user = User.create(:name => 'Steve Sloan')
69
+ end
70
+
71
+ it "should have protected attributes class method" do
72
+ User.protected_attributes.should include(:admin)
73
+ end
74
+
75
+ it "should default protected attributes to empty" do
76
+ Game.protected_attributes.should be_empty
77
+ end
78
+
79
+ it "should assign protected attribute through accessor" do
80
+ @user.admin = true
81
+ @user.admin.should be_true
82
+ end
83
+
84
+ it "should ignore protected attribute on #initialize" do
85
+ user = User.new(:name => 'John', :admin => true)
86
+ user.admin.should be_false
87
+ user.name.should == 'John'
88
+ end
89
+
90
+ it "should not ignore protected attributes on #initialize from the database" do
91
+ user = User.new(:name => 'John')
92
+ user.admin = true
93
+ user.save!
94
+
95
+ user = User.get(user.id)
96
+ user.admin.should be_true
97
+ user.name.should == 'John'
98
+ end
99
+
100
+ it "should ignore protected attribute on #update_attributes" do
101
+ @user.update_attributes(:name => 'Ren Hoek', :admin => true)
102
+ @user.name.should == 'Ren Hoek'
103
+ @user.admin.should be_false
104
+ end
105
+
106
+ it "should be indifferent to whether the protected keys are strings or symbols" do
107
+ @user.update_attributes("name" => 'Stimpson J. Cat', "admin" => true)
108
+ @user.name.should == 'Stimpson J. Cat'
109
+ @user.admin.should be_false
110
+ end
111
+
112
+ it "should accept nil as constructor's argument without raising exception" do
113
+ lambda { User.new(nil) }.should_not raise_error
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,312 @@
1
+ require 'helper'
2
+
3
+ describe Toy::Persistence do
4
+ uses_constants('User')
5
+
6
+ let(:klass) do
7
+ Class.new { include Toy::Store }
8
+ end
9
+
10
+ describe ".store" do
11
+ it "sets if arguments and reads if not" do
12
+ User.store(:memory, {})
13
+ User.store.should == Adapter[:memory].new({})
14
+ end
15
+
16
+ it "works with options" do
17
+ Adapter[:memory].should_receive(:new).with({}, :something => true)
18
+ User.store(:memory, {}, :something => true)
19
+ end
20
+
21
+ it "raises argument error if name provided but not client" do
22
+ lambda do
23
+ klass.store(:memory)
24
+ end.should raise_error(ArgumentError, 'Client is required')
25
+ end
26
+
27
+ it "raises argument error if no name or client provided and has not been set" do
28
+ lambda do
29
+ klass.store
30
+ end.should raise_error(StandardError, 'No store has been set')
31
+ end
32
+ end
33
+
34
+ describe ".cache" do
35
+ it "sets if arguments and reads if not" do
36
+ klass.cache(:memory, {})
37
+ klass.cache.should == Adapter[:memory].new({})
38
+ end
39
+
40
+ it "raises argument error if name provided but not client" do
41
+ lambda do
42
+ klass.cache(:memory)
43
+ end.should raise_error(ArgumentError, 'Client is required')
44
+ end
45
+
46
+ it "raises argument error if no name or client provided and has not been set" do
47
+ lambda do
48
+ klass.cache
49
+ end.should raise_error(StandardError, 'No cache has been set')
50
+ end
51
+ end
52
+
53
+ describe ".has_store?" do
54
+ it "returns true if cache set" do
55
+ klass.store(:memory, {})
56
+ klass.has_store?.should be_true
57
+ end
58
+
59
+ it "returns false if cache not set" do
60
+ klass.has_store?.should be_false
61
+ end
62
+ end
63
+
64
+ describe ".has_cache?" do
65
+ it "returns true if cache set" do
66
+ klass.cache(:memory, {})
67
+ klass.has_cache?.should be_true
68
+ end
69
+
70
+ it "returns false if cache not set" do
71
+ klass.has_cache?.should be_false
72
+ end
73
+ end
74
+
75
+ describe ".store_key" do
76
+ it "returns namespace and id" do
77
+ doc = User.new
78
+
79
+ User.store_key(doc.id).should == "User:#{doc.id}"
80
+ User.namespace('Crazy')
81
+ User.store_key(doc.id).should == "Crazy:#{doc.id}"
82
+ end
83
+ end
84
+
85
+ describe ".namespace" do
86
+ it "defaults to class name" do
87
+ User.namespace.should == 'User'
88
+ end
89
+
90
+ it "sets if argument and reads if not" do
91
+ User.namespace('CrazyUser')
92
+ User.namespace.should == 'CrazyUser'
93
+ end
94
+ end
95
+
96
+ describe ".create" do
97
+ before do
98
+ User.attribute :name, String
99
+ User.attribute :age, Integer
100
+ @doc = User.create(:name => 'John', :age => 50)
101
+ end
102
+ let(:doc) { @doc }
103
+
104
+ it "creates key in database with attributes" do
105
+ User.store.read(doc.store_key).should == {
106
+ 'name' => 'John',
107
+ 'id' => doc.id,
108
+ 'age' => 50,
109
+ }
110
+ end
111
+
112
+ it "returns instance of model" do
113
+ doc.should be_instance_of(User)
114
+ end
115
+ end
116
+
117
+ describe ".delete(*ids)" do
118
+ it "should delete a single record" do
119
+ doc = User.create
120
+ User.delete(doc.id)
121
+ User.key?(doc.id).should be_false
122
+ end
123
+
124
+ it "should delete multiple records" do
125
+ doc1 = User.create
126
+ doc2 = User.create
127
+
128
+ User.delete(doc1.id, doc2.id)
129
+
130
+ User.key?(doc1.id).should be_false
131
+ User.key?(doc2.id).should be_false
132
+ end
133
+
134
+ it "should not complain when records do not exist" do
135
+ doc = User.create
136
+ User.delete("taco:bell:tacos")
137
+ end
138
+ end
139
+
140
+ describe ".destroy(*ids)" do
141
+ it "should destroy a single record" do
142
+ doc = User.create
143
+ User.destroy(doc.id)
144
+ User.key?(doc.id).should be_false
145
+ end
146
+
147
+ it "should destroy multiple records" do
148
+ doc1 = User.create
149
+ doc2 = User.create
150
+
151
+ User.destroy(doc1.id, doc2.id)
152
+
153
+ User.key?(doc1.id).should be_false
154
+ User.key?(doc2.id).should be_false
155
+ end
156
+
157
+ it "should not complain when records do not exist" do
158
+ doc = User.create
159
+ User.destroy("taco:bell:tacos")
160
+ end
161
+ end
162
+
163
+ describe "#store" do
164
+ it "delegates to class" do
165
+ User.new.store.should equal(User.store)
166
+ end
167
+ end
168
+
169
+ describe "#store_key" do
170
+ it "returns pluralized human name and id" do
171
+ doc = User.new
172
+ doc.store_key.should == "User:#{doc.id}"
173
+ end
174
+ end
175
+
176
+ describe "#new_record?" do
177
+ it "returns true if new" do
178
+ User.new.should be_new_record
179
+ end
180
+
181
+ it "returns false if not new" do
182
+ User.create.should_not be_new_record
183
+ end
184
+ end
185
+
186
+ describe "#persisted?" do
187
+ it "returns true if persisted" do
188
+ User.create.should be_persisted
189
+ end
190
+
191
+ it "returns false if not persisted" do
192
+ User.new.should_not be_persisted
193
+ end
194
+
195
+ it "returns false if deleted" do
196
+ doc = User.create
197
+ doc.delete
198
+ doc.should_not be_persisted
199
+ end
200
+ end
201
+
202
+ describe "#save" do
203
+ before do
204
+ User.attribute :name, String
205
+ User.attribute :age, Integer
206
+ User.attribute :accepted_terms, Boolean, :virtual => true
207
+ end
208
+
209
+ context "with new record" do
210
+ before do
211
+ @doc = User.new(:name => 'John', :age => 28, :accepted_terms => true)
212
+ @doc.save
213
+ end
214
+
215
+ it "saves to key" do
216
+ User.key?(@doc.id)
217
+ end
218
+
219
+ it "does not persist virtual attributes" do
220
+ @doc.store.read(@doc.store_key).should_not include('accepted_terms')
221
+ end
222
+ end
223
+
224
+ context "with existing record" do
225
+ before do
226
+ @doc = User.create(:name => 'John', :age => 28)
227
+ @key = @doc.store_key
228
+ @value = User.store.read(@doc.store_key)
229
+ @doc.name = 'Bill'
230
+ @doc.accepted_terms = false
231
+ @doc.save
232
+ end
233
+ let(:doc) { @doc }
234
+
235
+ it "stores in same key" do
236
+ doc.store_key.should == @key
237
+ end
238
+
239
+ it "updates value in store" do
240
+ User.store.read(doc.store_key).should_not == @value
241
+ end
242
+
243
+ it "does not persist virtual attributes" do
244
+ @doc.store.read(@doc.store_key).should_not include('accepted_terms')
245
+ end
246
+
247
+ it "updates the attributes in the instance" do
248
+ doc.name.should == 'Bill'
249
+ end
250
+ end
251
+ end
252
+
253
+ describe "#update_attributes" do
254
+ before do
255
+ User.attribute :name, String
256
+ end
257
+
258
+ it "should change attribute and save" do
259
+ user = User.create(:name => 'John')
260
+ User.get(user.id).name.should == 'John'
261
+
262
+ user.update_attributes(:name => 'Geoffrey')
263
+ User.get(user.id).name.should == 'Geoffrey'
264
+ end
265
+ end
266
+
267
+ describe "#delete" do
268
+ it "should remove the instance from the store" do
269
+ doc = User.create
270
+ doc.delete
271
+ User.key?(doc.id).should be_false
272
+ end
273
+ end
274
+
275
+ describe "#destroy" do
276
+ it "should remove the instance from the store" do
277
+ doc = User.create
278
+ doc.destroy
279
+ User.key?(doc.id).should be_false
280
+ end
281
+ end
282
+
283
+ describe "#destroyed?" do
284
+ it "should be false if not deleted" do
285
+ doc = User.create
286
+ doc.should_not be_destroyed
287
+ end
288
+
289
+ it "should be true if deleted" do
290
+ doc = User.create
291
+ doc.delete
292
+ doc.should be_destroyed
293
+ end
294
+ end
295
+
296
+ describe "with cache store" do
297
+ before do
298
+ @cache = User.cache(:memory, {})
299
+ @memory = User.store(:memory, {})
300
+ @user = User.create
301
+ end
302
+
303
+ let(:cache) { @cache }
304
+ let(:memory) { @memory }
305
+ let(:user) { @user }
306
+
307
+ it "writes to cache and store" do
308
+ cache[user.store_key].should == user.persisted_attributes
309
+ memory[user.store_key].should == user.persisted_attributes
310
+ end
311
+ end
312
+ end