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.
- data/.autotest +11 -0
- data/.bundle/config +2 -0
- data/.gitignore +6 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +49 -0
- data/LICENSE +9 -0
- data/LOGGING.rdoc +16 -0
- data/README.rdoc +13 -0
- data/Rakefile +7 -0
- data/examples/memcached.rb +20 -0
- data/examples/memory.rb +20 -0
- data/examples/models.rb +51 -0
- data/examples/redis.rb +20 -0
- data/lib/toy.rb +81 -0
- data/lib/toy/attribute.rb +73 -0
- data/lib/toy/attributes.rb +137 -0
- data/lib/toy/caching.rb +20 -0
- data/lib/toy/callbacks.rb +48 -0
- data/lib/toy/collection.rb +55 -0
- data/lib/toy/connection.rb +28 -0
- data/lib/toy/dirty.rb +47 -0
- data/lib/toy/dolly.rb +30 -0
- data/lib/toy/embedded_list.rb +45 -0
- data/lib/toy/embedded_lists.rb +68 -0
- data/lib/toy/equality.rb +19 -0
- data/lib/toy/exceptions.rb +29 -0
- data/lib/toy/extensions/array.rb +22 -0
- data/lib/toy/extensions/boolean.rb +43 -0
- data/lib/toy/extensions/date.rb +24 -0
- data/lib/toy/extensions/float.rb +13 -0
- data/lib/toy/extensions/hash.rb +17 -0
- data/lib/toy/extensions/integer.rb +22 -0
- data/lib/toy/extensions/nil_class.rb +17 -0
- data/lib/toy/extensions/object.rb +26 -0
- data/lib/toy/extensions/set.rb +23 -0
- data/lib/toy/extensions/string.rb +17 -0
- data/lib/toy/extensions/time.rb +29 -0
- data/lib/toy/identity.rb +26 -0
- data/lib/toy/identity/abstract_key_factory.rb +10 -0
- data/lib/toy/identity/uuid_key_factory.rb +9 -0
- data/lib/toy/identity_map.rb +109 -0
- data/lib/toy/index.rb +74 -0
- data/lib/toy/indices.rb +56 -0
- data/lib/toy/inspect.rb +12 -0
- data/lib/toy/list.rb +46 -0
- data/lib/toy/lists.rb +37 -0
- data/lib/toy/logger.rb +26 -0
- data/lib/toy/mass_assignment_security.rb +16 -0
- data/lib/toy/persistence.rb +138 -0
- data/lib/toy/plugins.rb +23 -0
- data/lib/toy/proxies/embedded_list.rb +74 -0
- data/lib/toy/proxies/list.rb +97 -0
- data/lib/toy/proxies/proxy.rb +59 -0
- data/lib/toy/querying.rb +57 -0
- data/lib/toy/reference.rb +134 -0
- data/lib/toy/references.rb +19 -0
- data/lib/toy/serialization.rb +81 -0
- data/lib/toy/store.rb +36 -0
- data/lib/toy/timestamps.rb +22 -0
- data/lib/toy/validations.rb +45 -0
- data/lib/toy/version.rb +3 -0
- data/lib/toystore.rb +1 -0
- data/spec/helper.rb +35 -0
- data/spec/spec.opts +3 -0
- data/spec/support/constants.rb +41 -0
- data/spec/support/identity_map_matcher.rb +20 -0
- data/spec/support/name_and_number_key_factory.rb +5 -0
- data/spec/toy/attribute_spec.rb +176 -0
- data/spec/toy/attributes_spec.rb +394 -0
- data/spec/toy/caching_spec.rb +62 -0
- data/spec/toy/callbacks_spec.rb +97 -0
- data/spec/toy/connection_spec.rb +47 -0
- data/spec/toy/dirty_spec.rb +99 -0
- data/spec/toy/dolly_spec.rb +76 -0
- data/spec/toy/embedded_list_spec.rb +607 -0
- data/spec/toy/embedded_lists_spec.rb +172 -0
- data/spec/toy/equality_spec.rb +46 -0
- data/spec/toy/exceptions_spec.rb +18 -0
- data/spec/toy/extensions/array_spec.rb +25 -0
- data/spec/toy/extensions/boolean_spec.rb +41 -0
- data/spec/toy/extensions/date_spec.rb +48 -0
- data/spec/toy/extensions/float_spec.rb +14 -0
- data/spec/toy/extensions/hash_spec.rb +21 -0
- data/spec/toy/extensions/integer_spec.rb +29 -0
- data/spec/toy/extensions/nil_class_spec.rb +14 -0
- data/spec/toy/extensions/set_spec.rb +27 -0
- data/spec/toy/extensions/string_spec.rb +28 -0
- data/spec/toy/extensions/time_spec.rb +94 -0
- data/spec/toy/identity/abstract_key_factory_spec.rb +7 -0
- data/spec/toy/identity/uuid_key_factory_spec.rb +7 -0
- data/spec/toy/identity_map_spec.rb +150 -0
- data/spec/toy/identity_spec.rb +52 -0
- data/spec/toy/index_spec.rb +230 -0
- data/spec/toy/indices_spec.rb +141 -0
- data/spec/toy/inspect_spec.rb +15 -0
- data/spec/toy/list_spec.rb +576 -0
- data/spec/toy/lists_spec.rb +95 -0
- data/spec/toy/logger_spec.rb +33 -0
- data/spec/toy/mass_assignment_security_spec.rb +116 -0
- data/spec/toy/persistence_spec.rb +312 -0
- data/spec/toy/plugins_spec.rb +39 -0
- data/spec/toy/querying_spec.rb +162 -0
- data/spec/toy/reference_spec.rb +400 -0
- data/spec/toy/references_spec.rb +86 -0
- data/spec/toy/serialization_spec.rb +354 -0
- data/spec/toy/store_spec.rb +41 -0
- data/spec/toy/timestamps_spec.rb +63 -0
- data/spec/toy/validations_spec.rb +171 -0
- data/spec/toy_spec.rb +26 -0
- data/specs.watchr +52 -0
- data/test/lint_test.rb +40 -0
- data/toystore.gemspec +24 -0
- 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
|