toystore 0.5

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