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,62 @@
1
+ require 'helper'
2
+
3
+ describe Toy::Caching do
4
+ uses_constants('User')
5
+
6
+ context "new" do
7
+ before do
8
+ @user = User.new
9
+ end
10
+
11
+ it "should be class:new" do
12
+ @user.cache_key.should == 'User:new'
13
+ end
14
+
15
+ it "should work with suffix" do
16
+ @user.cache_key(:foo).should == 'User:new:foo'
17
+
18
+ @user.cache_key(:foo, :bar).should == 'User:new:foo:bar'
19
+ end
20
+ end
21
+
22
+ context "not new" do
23
+ context "with updated_at" do
24
+ before do
25
+ User.timestamps
26
+ @now = Time.now.utc
27
+ Timecop.freeze(@now) do
28
+ @user = User.create
29
+ end
30
+ end
31
+
32
+ it "should be class:id-timestamp" do
33
+ @user.cache_key.should == "User:#{@user.id}-#{@now.to_s(:number)}"
34
+ end
35
+
36
+ it "should work with suffix" do
37
+ @user.cache_key(:foo).should == "User:#{@user.id}-#{@now.to_s(:number)}:foo"
38
+
39
+ @user.cache_key(:foo, :bar).should == "User:#{@user.id}-#{@now.to_s(:number)}:foo:bar"
40
+ end
41
+ end
42
+
43
+ context "without updated_at" do
44
+ before do
45
+ @now = Time.now.utc
46
+ Timecop.freeze(@now) do
47
+ @user = User.create
48
+ end
49
+ end
50
+
51
+ it "should be class:id" do
52
+ @user.cache_key.should == "User:#{@user.id}"
53
+ end
54
+
55
+ it "should work with suffix" do
56
+ @user.cache_key(:foo).should == "User:#{@user.id}:foo"
57
+
58
+ @user.cache_key(:foo, :bar, :baz). should == "User:#{@user.id}:foo:bar:baz"
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,97 @@
1
+ require 'helper'
2
+
3
+ module CallbackHelper
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ [ :before_create, :after_create,
8
+ :before_update, :after_update,
9
+ :before_save, :after_save,
10
+ :before_destroy, :after_destroy].each do |callback|
11
+ callback_method = "#{callback}_callback"
12
+ send(callback, callback_method)
13
+ define_method(callback_method) { history << callback.to_sym }
14
+ end
15
+ end
16
+
17
+ def history
18
+ @history ||= []
19
+ end
20
+
21
+ def clear_history
22
+ @history = nil
23
+ end
24
+ end
25
+
26
+ describe Toy::Callbacks do
27
+ uses_constants('Game', 'Move')
28
+
29
+ context "regular" do
30
+ before do
31
+ Game.send(:include, CallbackHelper)
32
+ end
33
+
34
+ it "runs callbacks in correct order for create" do
35
+ doc = Game.create
36
+ doc.history.should == [:before_save, :before_create, :after_create, :after_save]
37
+ end
38
+
39
+ it "runs callbacks in correct order for update" do
40
+ doc = Game.create
41
+ doc.clear_history
42
+ doc.save
43
+ doc.history.should == [:before_save, :before_update, :after_update, :after_save]
44
+ end
45
+
46
+ it "runs callbacks in correct order for destroy" do
47
+ doc = Game.create
48
+ doc.clear_history
49
+ doc.destroy
50
+ doc.history.should == [:before_destroy, :after_destroy]
51
+ end
52
+ end
53
+
54
+ context "embedded" do
55
+ before do
56
+ Game.embedded_list(:moves)
57
+ Move.send(:include, CallbackHelper)
58
+
59
+ @move = Move.new
60
+ @game = Game.create(:moves => [@move])
61
+ end
62
+
63
+ it "runs callbacks for save of parent" do
64
+ @move.history.should == [:before_save, :before_create, :after_create, :after_save]
65
+ end
66
+
67
+ it "runs callbacks for update of parent" do
68
+ @move.clear_history
69
+ @game.save
70
+ @move.history.should == [:before_save, :before_update, :after_update, :after_save]
71
+ end
72
+
73
+ it "runs callbacks for destroy of parent" do
74
+ @move.clear_history
75
+ @game.destroy
76
+ @move.history.should == [:before_destroy, :after_destroy]
77
+ end
78
+
79
+ it "does not attempt to run callback defined on parent that is not defined on embedded" do
80
+ Game.define_callbacks :win
81
+ @move.clear_history
82
+
83
+ lambda do
84
+ @game.run_callbacks(:win)
85
+ @move.history.should be_empty
86
+ end.should_not raise_error
87
+ end
88
+
89
+ it "runs create callback when saving new embbeded doc on existing parent" do
90
+ @game.save
91
+ move = Move.new
92
+ @game.moves << move
93
+ @game.save
94
+ move.history.should == [:before_save, :before_create, :after_create, :after_save]
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,47 @@
1
+ require 'helper'
2
+
3
+ describe Toy::Connection do
4
+ uses_constants('User')
5
+
6
+ before do
7
+ @logger = Toy.logger
8
+ end
9
+
10
+ after do
11
+ Toy.logger = @logger
12
+ end
13
+
14
+ describe ".logger" do
15
+ it "should set the default logger" do
16
+ logger = stub
17
+ Toy.logger = logger
18
+ Toy.logger.should == logger
19
+ end
20
+
21
+ it "should be an instance of Logger if not set" do
22
+ Toy.logger = nil
23
+ Toy.logger.should be_instance_of(Logger)
24
+ end
25
+
26
+ it "should use RAILS_DEFAULT_LOGGER if defined" do
27
+ remove_constants("RAILS_DEFAULT_LOGGER")
28
+ RAILS_DEFAULT_LOGGER = stub
29
+
30
+ Toy.logger = nil
31
+ Toy.logger.should == RAILS_DEFAULT_LOGGER
32
+ end
33
+ end
34
+
35
+ describe ".key_factory" do
36
+ it "should set the default key_factory" do
37
+ key_factory = stub
38
+ Toy.key_factory = key_factory
39
+ Toy.key_factory.should == key_factory
40
+ end
41
+
42
+ it "should default to the UUIDKeyFactory" do
43
+ Toy.key_factory = nil
44
+ Toy.key_factory.should be_instance_of(Toy::Identity::UUIDKeyFactory)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,99 @@
1
+ require 'helper'
2
+
3
+ describe Toy::Dirty do
4
+ uses_constants('User')
5
+
6
+ before do
7
+ User.attribute(:name, String)
8
+ end
9
+
10
+ it "has no changes for new with no attributes" do
11
+ User.new.should_not be_changed
12
+ User.new.changed.should be_empty
13
+ User.new.changes.should be_empty
14
+ end
15
+
16
+ it "has changes for new with attributes" do
17
+ user = User.new(:name => 'Geoffrey')
18
+ user.should be_changed
19
+ user.changed.should include('name')
20
+ user.changes.should == {'name' => [nil, 'Geoffrey']}
21
+ end
22
+
23
+ it "knows attribute changed through writer" do
24
+ user = User.new
25
+ user.name = 'John'
26
+ user.should be_changed
27
+ user.changed.should include('name')
28
+ user.changes['name'].should == [nil, 'John']
29
+ end
30
+
31
+ it "knows when attribute did not change" do
32
+ user = User.create(:name => 'Geoffrey')
33
+ user.name = 'Geoffrey'
34
+ user.should_not be_changed
35
+ end
36
+
37
+ describe "#save" do
38
+ before { @user = User.create(:name => 'Geoffrey') }
39
+ let(:user) { @user }
40
+
41
+ it "clears changes" do
42
+ user.name = 'John'
43
+ user.should be_changed
44
+ user.save
45
+ user.should_not be_changed
46
+ end
47
+
48
+ it "sets previous changes" do
49
+ user.previous_changes.should == {'name' => [nil, 'Geoffrey']}
50
+ end
51
+ end
52
+
53
+ it "does not have changes when loaded from database" do
54
+ user = User.create
55
+ loaded = User.get(user.id)
56
+ loaded.should_not be_changed
57
+ end
58
+
59
+ describe "#reload" do
60
+ before { @user = User.create(:name => 'John') }
61
+ let(:user) { @user }
62
+
63
+ it "clears changes" do
64
+ user.name = 'Steve'
65
+ user.reload
66
+ user.should_not be_changed
67
+ end
68
+
69
+ it "clears previously changed" do
70
+ user.reload
71
+ user.previous_changes.should be_empty
72
+ end
73
+ end
74
+
75
+ it "has attribute changed? method" do
76
+ user = User.new
77
+ user.should_not be_name_changed
78
+ user.name = 'John'
79
+ user.should be_name_changed
80
+ end
81
+
82
+ it "has attribute was method" do
83
+ user = User.create(:name => 'John')
84
+ user.name = 'Steve'
85
+ user.name_was.should == 'John'
86
+ end
87
+
88
+ it "has attribute change method" do
89
+ user = User.create(:name => 'John')
90
+ user.name = 'Steve'
91
+ user.name_change.should == ['John', 'Steve']
92
+ end
93
+
94
+ it "has attribute will change! method" do
95
+ user = User.create
96
+ user.name_will_change!
97
+ user.should be_changed
98
+ end
99
+ end
@@ -0,0 +1,76 @@
1
+ require 'helper'
2
+
3
+ describe Toy::Connection do
4
+ uses_constants('User', 'Game', 'Move')
5
+
6
+ before do
7
+ Game.embedded_list(:moves)
8
+ User.attribute(:name, String)
9
+ User.attribute(:skills, Array)
10
+ User.list(:games)
11
+
12
+ @move = Move.new
13
+ @game = Game.create(:moves => [@move])
14
+ @user = User.create({
15
+ :name => 'John',
16
+ :skills => ['looking awesome', 'growing beards'],
17
+ :games => [@game],
18
+ })
19
+ end
20
+ let(:move) { @move }
21
+ let(:game) { @game }
22
+ let(:user) { @user }
23
+
24
+ describe "#clone" do
25
+ it "returns new instance" do
26
+ user.clone.should be_new_record
27
+ end
28
+
29
+ it "has no changes" do
30
+ user.clone.should_not be_changed
31
+ end
32
+
33
+ it "is never destroyed" do
34
+ user.destroy
35
+ user.clone.should_not be_destroyed
36
+ end
37
+
38
+ it "clones the @attributes hash" do
39
+ user.clone.instance_variable_get("@attributes").should_not equal(user.instance_variable_get("@attributes"))
40
+ end
41
+
42
+ it "copies the attributes" do
43
+ user.clone.tap do |clone|
44
+ clone.name.should == user.name
45
+ clone.skills.should == user.skills
46
+ end
47
+ end
48
+
49
+ it "clones duplicable attributes" do
50
+ user.clone.skills.should_not equal(user.skills)
51
+ end
52
+
53
+ it "regenerates id" do
54
+ user.clone.tap do |clone|
55
+ clone.id.should_not be_nil
56
+ clone.id.should_not == user.id
57
+ end
58
+ end
59
+
60
+ it "clones list id attributes" do
61
+ user.clone.game_ids.should_not equal(user.game_ids)
62
+ end
63
+
64
+ it "clones the list" do
65
+ user.clone.games.should_not equal(user.games)
66
+ end
67
+
68
+ it "clones embedded list objects" do
69
+ game.clone.moves.first.should_not equal(game.moves.first)
70
+ end
71
+
72
+ it "regenerates ids for embedded list objects" do
73
+ game.clone.moves.first.id.should_not == game.moves.first.id
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,607 @@
1
+ require 'helper'
2
+
3
+ describe Toy::List do
4
+ uses_constants('Game', 'Move')
5
+
6
+ before do
7
+ @list = Game.embedded_list(:moves)
8
+ end
9
+
10
+ let(:list) { @list }
11
+
12
+ it "has model" do
13
+ list.model.should == Game
14
+ end
15
+
16
+ it "has name" do
17
+ list.name.should == :moves
18
+ end
19
+
20
+ it "has type" do
21
+ list.type.should == Move
22
+ end
23
+
24
+ it "has instance_variable" do
25
+ list.instance_variable.should == :@_moves
26
+ end
27
+
28
+ it "adds list to model" do
29
+ Game.embedded_lists.keys.should include(:moves)
30
+ end
31
+
32
+ it "adds reader method" do
33
+ Game.new.should respond_to(:moves)
34
+ end
35
+
36
+ it "adds writer method" do
37
+ Game.new.should respond_to(:moves=)
38
+ end
39
+
40
+ describe "#eql?" do
41
+ it "returns true if same class, model, and name" do
42
+ list.should eql(list)
43
+ end
44
+
45
+ it "returns false if not same class" do
46
+ list.should_not eql({})
47
+ end
48
+
49
+ it "returns false if not same model" do
50
+ list.should_not eql(Toy::List.new(Move, :moves))
51
+ end
52
+
53
+ it "returns false if not the same name" do
54
+ list.should_not eql(Toy::List.new(Game, :recent_moves))
55
+ end
56
+ end
57
+
58
+ describe "setting list type" do
59
+ before do
60
+ @list = Game.embedded_list(:recent_moves, Move)
61
+ end
62
+ let(:list) { @list }
63
+
64
+ it "uses type provided instead of inferring from name" do
65
+ list.type.should be(Move)
66
+ end
67
+
68
+ it "works properly when reading and writing" do
69
+ game = Game.create
70
+ move = Move.create
71
+ game.recent_moves = [move]
72
+ game.recent_moves.should == [move]
73
+ end
74
+ end
75
+
76
+ describe "list reader" do
77
+ before do
78
+ Move.attribute(:index, Integer)
79
+ @game = Game.create(:move_attributes => [{:index => 0}, {:index => 1}])
80
+ end
81
+
82
+ it "returns instances" do
83
+ @game.moves.each do |move|
84
+ move.should be_instance_of(Move)
85
+ end
86
+ @game.moves[0].id.should_not be_nil
87
+ @game.moves[1].id.should_not be_nil
88
+ @game.moves[0].index.should == 0
89
+ @game.moves[1].index.should == 1
90
+ end
91
+
92
+ it "sets reference to parent for each instance" do
93
+ @game.moves.each do |move|
94
+ move.parent_reference.should == @game
95
+ end
96
+ end
97
+ end
98
+
99
+ describe "list writer" do
100
+ before do
101
+ @move1 = Move.new
102
+ @move2 = Move.new
103
+ @game = Game.create(:moves => [@move2])
104
+ end
105
+
106
+ it "set attribute" do
107
+ @game.move_attributes.should == [@move2.attributes]
108
+ end
109
+
110
+ it "unmemoizes reader" do
111
+ @game.moves.should == [@move2]
112
+ @game.moves = [@move1]
113
+ @game.moves.should == [@move1]
114
+ end
115
+
116
+ it "sets reference to parent for each instance" do
117
+ @game.moves.each do |move|
118
+ move.parent_reference.should == @game
119
+ end
120
+ end
121
+ end
122
+
123
+ describe "list attribute writer" do
124
+ before do
125
+ Move.attribute(:index, Integer)
126
+ @game = Game.new('move_attributes' => [
127
+ {'index' => '1'}
128
+ ])
129
+ end
130
+
131
+ it "typecasts hash values correctly" do
132
+ @game.move_attributes.should == [
133
+ {'id' => @game.moves[0].id, 'index' => 1}
134
+ ]
135
+ end
136
+
137
+ it "accepts hash of hashes" do
138
+ game = Game.new('move_attributes' => {
139
+ '0' => {'index' => 1},
140
+ '1' => {'index' => 2},
141
+ '2' => {'index' => 3},
142
+ })
143
+ game.move_attributes.should == [
144
+ {'id' => game.moves[0].id, 'index' => 1},
145
+ {'id' => game.moves[1].id, 'index' => 2},
146
+ {'id' => game.moves[2].id, 'index' => 3},
147
+ ]
148
+ end
149
+
150
+ it "sets ids if present" do
151
+ game = Game.new('move_attributes' => [
152
+ {'id' => 'foo', 'index' => '1'}
153
+ ])
154
+ game.move_attributes.should == [
155
+ {'id' => 'foo', 'index' => 1},
156
+ ]
157
+ end
158
+ end
159
+
160
+ describe "list#push" do
161
+ before do
162
+ @move = Move.new
163
+ @game = Game.new
164
+ @game.moves.push(@move)
165
+ end
166
+
167
+ it "raises error if wrong type assigned" do
168
+ lambda {
169
+ @game.moves.push(Game.new)
170
+ }.should raise_error(ArgumentError, "Move expected, but was Game")
171
+ end
172
+
173
+ it "sets reference to parent" do
174
+ # right now pushing a move adds a different instance to the proxy
175
+ # so i'm checking that it adds reference to both
176
+ @game.moves.each do |move|
177
+ move.parent_reference.should == @game
178
+ end
179
+ @move.parent_reference.should == @game
180
+ end
181
+
182
+ it "instances should not be persisted" do
183
+ @game.moves.each do |move|
184
+ move.should_not be_persisted
185
+ end
186
+ end
187
+
188
+ it "marks instances as persisted when parent saved" do
189
+ @game.save
190
+ @game.moves.each do |move|
191
+ move.should be_persisted
192
+ end
193
+ end
194
+
195
+ it "should save list" do
196
+ moves = @game.moves.target
197
+ @game.save
198
+ @game.reload
199
+ @game.moves.should == moves
200
+ end
201
+
202
+ it "should keep existing instances as persisted when adding a new instance" do
203
+ @game.save
204
+ @game.moves.push(Move.new)
205
+ @game.moves.first.should be_persisted
206
+ @game.moves.last.should_not be_persisted
207
+ end
208
+
209
+ it "marks instances as persisted when updated" do
210
+ @game.save
211
+ game = @game.reload
212
+ move = Move.new
213
+ game.moves.push(move)
214
+ move.should_not be_persisted
215
+ game.save
216
+ game.moves.each do |move|
217
+ move.should be_persisted
218
+ end
219
+ # move.should be_persisted
220
+ end
221
+ end
222
+
223
+ describe "list#<<" do
224
+ before do
225
+ @move = Move.new
226
+ @game = Game.new
227
+ @game.moves << @move
228
+ end
229
+
230
+ it "raises error if wrong type assigned" do
231
+ lambda {
232
+ @game.moves << Game.new
233
+ }.should raise_error(ArgumentError, "Move expected, but was Game")
234
+ end
235
+
236
+ it "sets reference to parent" do
237
+ # right now pushing a move adds a different instance to the proxy
238
+ # so i'm checking that it adds reference to both
239
+ @game.moves.each do |move|
240
+ move.parent_reference.should == @game
241
+ end
242
+ @move.parent_reference.should == @game
243
+ end
244
+
245
+ it "instances should not be persisted" do
246
+ @game.moves.each do |move|
247
+ move.should_not be_persisted
248
+ end
249
+ end
250
+
251
+ it "marks instances as persisted when parent saved" do
252
+ @game.save
253
+ @game.moves.each do |move|
254
+ move.should be_persisted
255
+ end
256
+ end
257
+ end
258
+
259
+ describe "list#concat" do
260
+ before do
261
+ @move1 = Move.new
262
+ @move2 = Move.new
263
+ @game = Game.new
264
+ @game.moves.concat(@move1, @move2)
265
+ end
266
+
267
+ it "raises error if wrong type assigned" do
268
+ lambda {
269
+ @game.moves.concat(Game.new, Move.new)
270
+ }.should raise_error(ArgumentError, "Move expected, but was Game")
271
+ end
272
+
273
+ it "sets reference to parent" do
274
+ # right now pushing a move adds a different instance to the proxy
275
+ # so i'm checking that it adds reference to both
276
+ @game.moves.each do |move|
277
+ move.parent_reference.should == @game
278
+ end
279
+ @move1.parent_reference.should == @game
280
+ @move2.parent_reference.should == @game
281
+ end
282
+
283
+ it "instances should not be persisted" do
284
+ @game.moves.each do |move|
285
+ move.should_not be_persisted
286
+ end
287
+ end
288
+
289
+ it "marks instances as persisted when parent saved" do
290
+ @game.save
291
+ @game.moves.each do |move|
292
+ move.should be_persisted
293
+ end
294
+ end
295
+ end
296
+
297
+ describe "list#concat (with array)" do
298
+ before do
299
+ @move1 = Move.new
300
+ @move2 = Move.new
301
+ @game = Game.create
302
+ @game.moves.concat([@move1, @move2])
303
+ end
304
+
305
+ it "raises error if wrong type assigned" do
306
+ lambda {
307
+ @game.moves.concat([Game.new, Move.new])
308
+ }.should raise_error(ArgumentError, "Move expected, but was Game")
309
+ end
310
+
311
+ it "sets reference to parent" do
312
+ # right now pushing a move adds a different instance to the proxy
313
+ # so i'm checking that it adds reference to both
314
+ @game.moves.each do |move|
315
+ move.parent_reference.should == @game
316
+ end
317
+ @move1.parent_reference.should == @game
318
+ @move2.parent_reference.should == @game
319
+ end
320
+
321
+ it "instances should not be persisted" do
322
+ @game.moves.each do |move|
323
+ move.should_not be_persisted
324
+ end
325
+ end
326
+
327
+ it "marks instances as persisted when parent saved" do
328
+ @game.save
329
+ @game.moves.each do |move|
330
+ move.should be_persisted
331
+ end
332
+ end
333
+ end
334
+
335
+ shared_examples_for("embedded_list#create") do
336
+ it "creates instance" do
337
+ @move.should be_persisted
338
+ end
339
+
340
+ it "assigns reference to parent" do
341
+ @move.parent_reference.should == @game
342
+ end
343
+
344
+ it "assigns id" do
345
+ @move.id.should_not be_nil
346
+ end
347
+
348
+ it "adds instance to reader" do
349
+ @game.moves.should == [@move]
350
+ end
351
+
352
+ it "marks instance as persisted" do
353
+ @move.should be_persisted
354
+ end
355
+ end
356
+
357
+ describe "list#create" do
358
+ before do
359
+ @game = Game.create
360
+ @move = @game.moves.create
361
+ end
362
+
363
+ it_should_behave_like "embedded_list#create"
364
+ end
365
+
366
+ describe "list#create (with attributes)" do
367
+ before do
368
+ Move.attribute(:move_index, Integer)
369
+ @game = Game.create
370
+ @move = @game.moves.create(:move_index => 0)
371
+ end
372
+
373
+ it_should_behave_like "embedded_list#create"
374
+
375
+ it "sets attributes on instance" do
376
+ @move.move_index.should == 0
377
+ end
378
+ end
379
+
380
+ describe "list#create (invalid)" do
381
+ before do
382
+ @game = Game.create
383
+ @game.moves.should_not_receive(:push)
384
+ @game.moves.should_not_receive(:reset)
385
+ @game.should_not_receive(:reload)
386
+ @game.should_not_receive(:save)
387
+
388
+ Move.attribute(:move_index, Integer)
389
+ Move.validates_presence_of(:move_index)
390
+
391
+ @move = @game.moves.create
392
+ end
393
+
394
+ it "returns instance" do
395
+ @move.should be_instance_of(Move)
396
+ end
397
+
398
+ it "is not persisted" do
399
+ @move.should_not be_persisted
400
+ end
401
+
402
+ it "assigns reference to parent" do
403
+ @move.parent_reference.should == @game
404
+ end
405
+ end
406
+
407
+ describe "list#create (valid with invalid root that validates embedded)" do
408
+ before do
409
+ Game.attribute(:user_id, String)
410
+ Game.validates_presence_of(:user_id)
411
+
412
+ @game = Game.new
413
+ @move = @game.moves.create
414
+ end
415
+
416
+ it "is not persisted" do
417
+ @move.should_not be_persisted
418
+ end
419
+
420
+ it "is persisted when root is valid and saved" do
421
+ @game.user_id = '1'
422
+ @game.save!
423
+ @move.should be_persisted
424
+ end
425
+ end
426
+
427
+ describe "list#destroy" do
428
+ before do
429
+ Move.attribute(:move_index, Integer)
430
+ @game = Game.create
431
+ @move1 = @game.moves.create(:move_index => 0)
432
+ @move2 = @game.moves.create(:move_index => 1)
433
+ end
434
+
435
+ it "should take multiple ids" do
436
+ @game.moves.destroy(@move1.id, @move2.id)
437
+ @game.moves.should be_empty
438
+ @game.reload
439
+ @game.moves.should be_empty
440
+ end
441
+
442
+ it "should take an array of ids" do
443
+ @game.moves.destroy([@move1.id, @move2.id])
444
+ @game.moves.should be_empty
445
+ @game.reload
446
+ @game.moves.should be_empty
447
+ end
448
+
449
+ it "should take a block to filter on" do
450
+ @game.moves.destroy { |move| move.move_index == 1 }
451
+ @game.moves.should == [@move1]
452
+ @game.reload
453
+ @game.moves.should == [@move1]
454
+ end
455
+ end
456
+
457
+ describe "list#destroy_all" do
458
+ before do
459
+ Move.attribute(:move_index, Integer)
460
+ @game = Game.create
461
+ @move1 = @game.moves.create(:move_index => 0)
462
+ @move2 = @game.moves.create(:move_index => 1)
463
+ end
464
+
465
+ it "should destroy all" do
466
+ @game.moves.destroy_all
467
+ @game.moves.should be_empty
468
+ @game.reload
469
+ @game.moves.should be_empty
470
+ end
471
+ end
472
+
473
+ describe "list#each" do
474
+ before do
475
+ @move1 = Move.new
476
+ @move2 = Move.new
477
+ @game = Game.create(:moves => [@move1, @move2])
478
+ end
479
+
480
+ it "iterates through each instance" do
481
+ moves = []
482
+ @game.moves.each do |move|
483
+ moves << move
484
+ end
485
+ moves.should == [@move1, @move2]
486
+ end
487
+ end
488
+
489
+ describe "enumerating" do
490
+ before do
491
+ Move.attribute(:move_index, Integer)
492
+ @move1 = Move.new(:move_index => 0)
493
+ @move2 = Move.new(:move_index => 1)
494
+ @game = Game.create(:moves => [@move1, @move2])
495
+ end
496
+
497
+ it "works" do
498
+ @game.moves.select { |move| move.move_index > 0 }.should == [@move2]
499
+ @game.moves.reject { |move| move.move_index > 0 }.should == [@move1]
500
+ end
501
+ end
502
+
503
+ describe "list#include?" do
504
+ before do
505
+ @move1 = Move.new
506
+ @move2 = Move.new
507
+ @game = Game.create(:moves => [@move1])
508
+ end
509
+
510
+ it "returns true if instance in association" do
511
+ @game.moves.should include(@move1)
512
+ end
513
+
514
+ it "returns false if instance not in association" do
515
+ @game.moves.should_not include(@move2)
516
+ end
517
+
518
+ it "returns false for nil" do
519
+ @game.moves.should_not include(nil)
520
+ end
521
+ end
522
+
523
+ describe "list with block" do
524
+ before do
525
+ Move.attribute(:old, Boolean)
526
+ Game.embedded_list(:moves) do
527
+ def old
528
+ target.select { |move| move.old? }
529
+ end
530
+ end
531
+
532
+ @move_new = Move.create(:old => false)
533
+ @move_old = Move.create(:old => true)
534
+ @game = Game.create(:moves => [@move_new, @move_old])
535
+ end
536
+
537
+ it "extends block methods onto proxy" do
538
+ @game.moves.respond_to?(:old).should be_true
539
+ @game.moves.old.should == [@move_old]
540
+ end
541
+ end
542
+
543
+ describe "list extension with :extensions option" do
544
+ before do
545
+ old_module = Module.new do
546
+ def old
547
+ target.select { |m| m.old? }
548
+ end
549
+ end
550
+
551
+ recent_proc = Proc.new do
552
+ def recent
553
+ target.select { |m| !m.old? }
554
+ end
555
+ end
556
+
557
+ Move.attribute(:old, Boolean)
558
+ Game.embedded_list(:moves, :extensions => [old_module, recent_proc])
559
+
560
+ @move_new = Move.new(:old => false)
561
+ @move_old = Move.new(:old => true)
562
+ @game = Game.create(:moves => [@move_new, @move_old])
563
+ end
564
+
565
+ it "extends modules" do
566
+ @game.moves.respond_to?(:old).should be_true
567
+ @game.moves.old.should == [@move_old]
568
+ end
569
+
570
+ it "extends procs" do
571
+ @game.moves.respond_to?(:recent).should be_true
572
+ @game.moves.recent.should == [@move_new]
573
+ end
574
+ end
575
+
576
+ describe "list#get" do
577
+ before do
578
+ @game = Game.create
579
+ @move = @game.moves.create
580
+ end
581
+
582
+ it "should not find items that don't exist" do
583
+ @game.moves.get('does-not-exist').should be_nil
584
+ end
585
+
586
+ it "should find items that are in list" do
587
+ @game.moves.get(@move.id).should == @move
588
+ end
589
+ end
590
+
591
+ describe "list#get!" do
592
+ before do
593
+ @game = Game.create
594
+ @move = @game.moves.create
595
+ end
596
+
597
+ it "should not find items that don't exist" do
598
+ lambda {
599
+ @game.moves.get!('does-not-exist')
600
+ }.should raise_error(Toy::NotFound, 'Could not find document with id: "does-not-exist"')
601
+ end
602
+
603
+ it "should find items that are in list" do
604
+ @game.moves.get!(@move.id).should == @move
605
+ end
606
+ end
607
+ end