mark_mapper 0.0.1
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.rdoc +39 -0
- data/examples/attr_accessible.rb +24 -0
- data/examples/attr_protected.rb +24 -0
- data/examples/cache_key.rb +26 -0
- data/examples/custom_types.rb +26 -0
- data/examples/identity_map.rb +30 -0
- data/examples/identity_map/automatic.rb +2 -0
- data/examples/keys.rb +42 -0
- data/examples/modifiers/set.rb +27 -0
- data/examples/plugins.rb +40 -0
- data/examples/querying.rb +39 -0
- data/examples/sample_app.rb +43 -0
- data/examples/scopes.rb +56 -0
- data/examples/validating/embedded_docs.rb +31 -0
- data/lib/mark_mapper.rb +125 -0
- data/lib/mark_mapper/config.rb +90 -0
- data/lib/mark_mapper/connection.rb +60 -0
- data/lib/mark_mapper/criteria_hash.rb +194 -0
- data/lib/mark_mapper/document.rb +46 -0
- data/lib/mark_mapper/embedded_document.rb +32 -0
- data/lib/mark_mapper/exceptions.rb +33 -0
- data/lib/mark_mapper/extensions/array.rb +27 -0
- data/lib/mark_mapper/extensions/boolean.rb +45 -0
- data/lib/mark_mapper/extensions/date.rb +29 -0
- data/lib/mark_mapper/extensions/duplicable.rb +86 -0
- data/lib/mark_mapper/extensions/float.rb +18 -0
- data/lib/mark_mapper/extensions/hash.rb +26 -0
- data/lib/mark_mapper/extensions/integer.rb +27 -0
- data/lib/mark_mapper/extensions/kernel.rb +11 -0
- data/lib/mark_mapper/extensions/nil_class.rb +18 -0
- data/lib/mark_mapper/extensions/object.rb +30 -0
- data/lib/mark_mapper/extensions/object_id.rb +18 -0
- data/lib/mark_mapper/extensions/set.rb +20 -0
- data/lib/mark_mapper/extensions/string.rb +31 -0
- data/lib/mark_mapper/extensions/symbol.rb +87 -0
- data/lib/mark_mapper/extensions/time.rb +29 -0
- data/lib/mark_mapper/locale/en.yml +5 -0
- data/lib/mark_mapper/middleware/identity_map.rb +41 -0
- data/lib/mark_mapper/normalizers/criteria_hash_key.rb +17 -0
- data/lib/mark_mapper/normalizers/criteria_hash_value.rb +66 -0
- data/lib/mark_mapper/normalizers/fields_value.rb +26 -0
- data/lib/mark_mapper/normalizers/hash_key.rb +19 -0
- data/lib/mark_mapper/normalizers/integer.rb +19 -0
- data/lib/mark_mapper/normalizers/options_hash_value.rb +83 -0
- data/lib/mark_mapper/normalizers/sort_value.rb +55 -0
- data/lib/mark_mapper/options_hash.rb +103 -0
- data/lib/mark_mapper/pagination.rb +6 -0
- data/lib/mark_mapper/pagination/collection.rb +32 -0
- data/lib/mark_mapper/pagination/paginator.rb +46 -0
- data/lib/mark_mapper/plugins.rb +22 -0
- data/lib/mark_mapper/plugins/accessible.rb +61 -0
- data/lib/mark_mapper/plugins/active_model.rb +18 -0
- data/lib/mark_mapper/plugins/associations.rb +96 -0
- data/lib/mark_mapper/plugins/associations/base.rb +98 -0
- data/lib/mark_mapper/plugins/associations/belongs_to_association.rb +63 -0
- data/lib/mark_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +35 -0
- data/lib/mark_mapper/plugins/associations/belongs_to_proxy.rb +52 -0
- data/lib/mark_mapper/plugins/associations/collection.rb +29 -0
- data/lib/mark_mapper/plugins/associations/embedded_collection.rb +44 -0
- data/lib/mark_mapper/plugins/associations/in_array_proxy.rb +133 -0
- data/lib/mark_mapper/plugins/associations/many_association.rb +63 -0
- data/lib/mark_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
- data/lib/mark_mapper/plugins/associations/many_documents_proxy.rb +142 -0
- data/lib/mark_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
- data/lib/mark_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
- data/lib/mark_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
- data/lib/mark_mapper/plugins/associations/one_as_proxy.rb +22 -0
- data/lib/mark_mapper/plugins/associations/one_association.rb +48 -0
- data/lib/mark_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +30 -0
- data/lib/mark_mapper/plugins/associations/one_embedded_proxy.rb +44 -0
- data/lib/mark_mapper/plugins/associations/one_proxy.rb +95 -0
- data/lib/mark_mapper/plugins/associations/proxy.rb +138 -0
- data/lib/mark_mapper/plugins/associations/single_association.rb +46 -0
- data/lib/mark_mapper/plugins/caching.rb +21 -0
- data/lib/mark_mapper/plugins/callbacks.rb +42 -0
- data/lib/mark_mapper/plugins/clone.rb +24 -0
- data/lib/mark_mapper/plugins/counter_cache.rb +97 -0
- data/lib/mark_mapper/plugins/dirty.rb +61 -0
- data/lib/mark_mapper/plugins/document.rb +41 -0
- data/lib/mark_mapper/plugins/dumpable.rb +22 -0
- data/lib/mark_mapper/plugins/dynamic_querying.rb +45 -0
- data/lib/mark_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
- data/lib/mark_mapper/plugins/embedded_callbacks.rb +81 -0
- data/lib/mark_mapper/plugins/embedded_document.rb +53 -0
- data/lib/mark_mapper/plugins/equality.rb +23 -0
- data/lib/mark_mapper/plugins/identity_map.rb +144 -0
- data/lib/mark_mapper/plugins/indexable.rb +86 -0
- data/lib/mark_mapper/plugins/inspect.rb +16 -0
- data/lib/mark_mapper/plugins/keys.rb +470 -0
- data/lib/mark_mapper/plugins/keys/key.rb +134 -0
- data/lib/mark_mapper/plugins/keys/static.rb +45 -0
- data/lib/mark_mapper/plugins/logger.rb +18 -0
- data/lib/mark_mapper/plugins/modifiers.rb +140 -0
- data/lib/mark_mapper/plugins/pagination.rb +16 -0
- data/lib/mark_mapper/plugins/partial_updates.rb +77 -0
- data/lib/mark_mapper/plugins/persistence.rb +79 -0
- data/lib/mark_mapper/plugins/protected.rb +45 -0
- data/lib/mark_mapper/plugins/querying.rb +173 -0
- data/lib/mark_mapper/plugins/querying/decorated_markmapper_query.rb +75 -0
- data/lib/mark_mapper/plugins/rails.rb +79 -0
- data/lib/mark_mapper/plugins/rails/active_record_association_adapter.rb +33 -0
- data/lib/mark_mapper/plugins/sci.rb +82 -0
- data/lib/mark_mapper/plugins/scopes.rb +28 -0
- data/lib/mark_mapper/plugins/serialization.rb +109 -0
- data/lib/mark_mapper/plugins/timestamps.rb +29 -0
- data/lib/mark_mapper/plugins/touch.rb +18 -0
- data/lib/mark_mapper/plugins/userstamps.rb +18 -0
- data/lib/mark_mapper/plugins/validations.rb +96 -0
- data/lib/mark_mapper/query.rb +278 -0
- data/lib/mark_mapper/railtie.rb +52 -0
- data/lib/mark_mapper/railtie/database.rake +65 -0
- data/lib/mark_mapper/translation.rb +10 -0
- data/lib/mark_mapper/version.rb +4 -0
- data/lib/rails/generators/mark_mapper/config/config_generator.rb +37 -0
- data/lib/rails/generators/mark_mapper/config/templates/marklogic.yml +19 -0
- data/lib/rails/generators/mark_mapper/model/model_generator.rb +40 -0
- data/lib/rails/generators/mark_mapper/model/templates/model.rb +17 -0
- data/spec/config/mark_mapper.yml +6 -0
- data/spec/examples_spec.rb +25 -0
- data/spec/functional/accessible_spec.rb +198 -0
- data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +64 -0
- data/spec/functional/associations/belongs_to_proxy_spec.rb +255 -0
- data/spec/functional/associations/in_array_proxy_spec.rb +349 -0
- data/spec/functional/associations/many_documents_as_proxy_spec.rb +230 -0
- data/spec/functional/associations/many_documents_proxy_spec.rb +968 -0
- data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +238 -0
- data/spec/functional/associations/many_embedded_proxy_spec.rb +288 -0
- data/spec/functional/associations/many_polymorphic_proxy_spec.rb +302 -0
- data/spec/functional/associations/one_as_proxy_spec.rb +489 -0
- data/spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb +207 -0
- data/spec/functional/associations/one_embedded_proxy_spec.rb +100 -0
- data/spec/functional/associations/one_proxy_spec.rb +406 -0
- data/spec/functional/associations_spec.rb +48 -0
- data/spec/functional/caching_spec.rb +75 -0
- data/spec/functional/callbacks_spec.rb +330 -0
- data/spec/functional/counter_cache_spec.rb +235 -0
- data/spec/functional/dirty_spec.rb +316 -0
- data/spec/functional/document_spec.rb +310 -0
- data/spec/functional/dumpable_spec.rb +24 -0
- data/spec/functional/dynamic_querying_spec.rb +75 -0
- data/spec/functional/embedded_document_spec.rb +316 -0
- data/spec/functional/equality_spec.rb +20 -0
- data/spec/functional/extensions_spec.rb +16 -0
- data/spec/functional/identity_map_spec.rb +483 -0
- data/spec/functional/keys_spec.rb +339 -0
- data/spec/functional/logger_spec.rb +20 -0
- data/spec/functional/modifiers_spec.rb +446 -0
- data/spec/functional/options_hash_spec.rb +41 -0
- data/spec/functional/pagination_spec.rb +89 -0
- data/spec/functional/partial_updates_spec.rb +530 -0
- data/spec/functional/protected_spec.rb +199 -0
- data/spec/functional/querying_spec.rb +984 -0
- data/spec/functional/rails_spec.rb +55 -0
- data/spec/functional/sci_spec.rb +374 -0
- data/spec/functional/scopes_spec.rb +204 -0
- data/spec/functional/static_keys_spec.rb +153 -0
- data/spec/functional/timestamps_spec.rb +97 -0
- data/spec/functional/touch_spec.rb +125 -0
- data/spec/functional/userstamps_spec.rb +46 -0
- data/spec/functional/validations_spec.rb +416 -0
- data/spec/quality_spec.rb +51 -0
- data/spec/spec_helper.rb +150 -0
- data/spec/support/matchers.rb +15 -0
- data/spec/support/models.rb +256 -0
- data/spec/symbol_operator_spec.rb +70 -0
- data/spec/symbol_spec.rb +9 -0
- data/spec/unit/associations/base_spec.rb +146 -0
- data/spec/unit/associations/belongs_to_association_spec.rb +30 -0
- data/spec/unit/associations/many_association_spec.rb +64 -0
- data/spec/unit/associations/one_association_spec.rb +48 -0
- data/spec/unit/associations/proxy_spec.rb +103 -0
- data/spec/unit/clone_spec.rb +79 -0
- data/spec/unit/config_generator_spec.rb +24 -0
- data/spec/unit/criteria_hash_spec.rb +218 -0
- data/spec/unit/document_spec.rb +251 -0
- data/spec/unit/dynamic_finder_spec.rb +125 -0
- data/spec/unit/embedded_document_spec.rb +676 -0
- data/spec/unit/equality_spec.rb +38 -0
- data/spec/unit/exceptions_spec.rb +12 -0
- data/spec/unit/extensions_spec.rb +368 -0
- data/spec/unit/identity_map_middleware_spec.rb +134 -0
- data/spec/unit/inspect_spec.rb +47 -0
- data/spec/unit/key_spec.rb +276 -0
- data/spec/unit/keys_spec.rb +155 -0
- data/spec/unit/mark_mapper_spec.rb +37 -0
- data/spec/unit/model_generator_spec.rb +45 -0
- data/spec/unit/normalizers/criteria_hash_key_spec.rb +37 -0
- data/spec/unit/normalizers/criteria_hash_value_spec.rb +200 -0
- data/spec/unit/normalizers/fields_value_spec.rb +45 -0
- data/spec/unit/normalizers/hash_key_spec.rb +15 -0
- data/spec/unit/normalizers/integer_spec.rb +24 -0
- data/spec/unit/normalizers/options_hash_value_spec.rb +99 -0
- data/spec/unit/normalizers/sort_value_spec.rb +98 -0
- data/spec/unit/options_hash_spec.rb +64 -0
- data/spec/unit/pagination/collection_spec.rb +30 -0
- data/spec/unit/pagination/paginator_spec.rb +118 -0
- data/spec/unit/pagination_spec.rb +11 -0
- data/spec/unit/plugins_spec.rb +89 -0
- data/spec/unit/query_spec.rb +837 -0
- data/spec/unit/rails_compatibility_spec.rb +40 -0
- data/spec/unit/rails_reflect_on_association_spec.rb +118 -0
- data/spec/unit/rails_spec.rb +188 -0
- data/spec/unit/serialization_spec.rb +169 -0
- data/spec/unit/serializers/json_serializer_spec.rb +218 -0
- data/spec/unit/serializers/xml_serializer_spec.rb +198 -0
- data/spec/unit/time_zones_spec.rb +44 -0
- data/spec/unit/translation_spec.rb +27 -0
- data/spec/unit/validations_spec.rb +588 -0
- metadata +307 -0
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MarkMapper::Plugins::Keys::Static do
|
4
|
+
before do
|
5
|
+
@klass = Class.new do
|
6
|
+
include MarkMapper::Document
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it "is off by default" do
|
11
|
+
@klass.static_keys.should == false
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can be turned on" do
|
15
|
+
@klass.static_keys = true
|
16
|
+
@klass.static_keys.should == true
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "a class with static keys" do
|
20
|
+
before do
|
21
|
+
@klass = Class.new do
|
22
|
+
include MarkMapper::Document
|
23
|
+
|
24
|
+
self.static_keys = true
|
25
|
+
|
26
|
+
attr_accessor :an_accessor
|
27
|
+
|
28
|
+
key "valid_key", String
|
29
|
+
end
|
30
|
+
|
31
|
+
@obj = @klass.new
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should be able to assign to a valid key" do
|
35
|
+
@obj.valid_key = "foo"
|
36
|
+
@obj.valid_key.should == "foo"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should raise a MissingKeyError if trying to assign to a field that does not exist" do
|
40
|
+
lambda {
|
41
|
+
@obj['foo'] = "foo"
|
42
|
+
}.should raise_error(MarkMapper::Plugins::Keys::Static::MissingKeyError)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should raise a MissingKeyError if trying to read from a field that does not exist" do
|
46
|
+
lambda {
|
47
|
+
@obj['foo']
|
48
|
+
}.should raise_error(MarkMapper::Plugins::Keys::Static::MissingKeyError)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should raise a MissingKeyError if trying to set attributes= to a field that does not exist" do
|
52
|
+
lambda {
|
53
|
+
@obj.attributes = {:foo => "foo"}
|
54
|
+
}.should raise_error(MarkMapper::Plugins::Keys::Static::MissingKeyError)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should be able to call a method" do
|
58
|
+
@obj.an_accessor = :bar
|
59
|
+
@obj.an_accessor.should == :bar
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should raise a NoMethodError if trying to call the non-existant key method" do
|
63
|
+
lambda {
|
64
|
+
@obj.foo
|
65
|
+
}.should raise_error(NoMethodError)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should raise for read_key" do
|
69
|
+
lambda {
|
70
|
+
@obj.read_key('foo')
|
71
|
+
}.should raise_error(MarkMapper::Plugins::Keys::Static::MissingKeyError)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should raise for attribute" do
|
75
|
+
lambda {
|
76
|
+
@obj.attribute('foo')
|
77
|
+
}.should raise_error(MarkMapper::Plugins::Keys::Static::MissingKeyError)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should not raise if trying to assign to a field that exists (but with a symbol instead of a string)" do
|
81
|
+
lambda {
|
82
|
+
@obj.write_key(:valid_key, "foo")
|
83
|
+
}.should_not raise_error
|
84
|
+
|
85
|
+
@obj.valid_key.should == "foo"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should not raise a MissingKeyError if trying to read from a field that exists (but with a symbol instead of a string)" do
|
89
|
+
lambda {
|
90
|
+
@obj[:valid_key]
|
91
|
+
}.should_not raise_error
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should not blow up when loading if there is a key defined in the db that has not been defined (but it should not load it)" do
|
95
|
+
@klass.collection.insert({ :foo => "bar", :valid_key => "something" })
|
96
|
+
@obj = @klass.first
|
97
|
+
@obj.valid_key.should == "something"
|
98
|
+
|
99
|
+
lambda {
|
100
|
+
@obj.foo
|
101
|
+
}.should raise_error(NoMethodError)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "a class without static keys" do
|
106
|
+
before do
|
107
|
+
@klass = Class.new do
|
108
|
+
include MarkMapper::Document
|
109
|
+
key :valid_key, String
|
110
|
+
end
|
111
|
+
|
112
|
+
@obj = @klass.new
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should be able to assign to a valid key" do
|
116
|
+
@obj.valid_key = "foo"
|
117
|
+
@obj.valid_key.should == "foo"
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should be able to read and assign a dynamic key" do
|
121
|
+
@obj['foo'].should be_nil
|
122
|
+
@obj['foo'] = "bar"
|
123
|
+
@obj['foo'].should == "bar"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "with mixed static + non static models" do
|
128
|
+
before do
|
129
|
+
@static_model = Class.new do
|
130
|
+
include MarkMapper::Document
|
131
|
+
self.static_keys = true
|
132
|
+
end
|
133
|
+
|
134
|
+
@non_static_model = Class.new do
|
135
|
+
include MarkMapper::Document
|
136
|
+
self.static_keys = false
|
137
|
+
end
|
138
|
+
|
139
|
+
@static_key_object = @static_model.new
|
140
|
+
@non_static_key_object = @non_static_model.new
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should allow non-static key assignment to one, only static assignment to the other" do
|
144
|
+
@non_static_key_object['foo'] = 'bar'
|
145
|
+
@non_static_key_object.foo.should == 'bar'
|
146
|
+
@non_static_key_object['foo'].should == 'bar'
|
147
|
+
|
148
|
+
lambda {
|
149
|
+
@static_key_object['foo'] = 'bar'
|
150
|
+
}.should raise_error
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Timestamps" do
|
4
|
+
context "included" do
|
5
|
+
before do
|
6
|
+
@klass = Doc do
|
7
|
+
key :first_name, String
|
8
|
+
end
|
9
|
+
@klass.timestamps!
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should set record_timestamps to true" do
|
13
|
+
@klass.record_timestamps.should be(true)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "timestamping" do
|
18
|
+
before do
|
19
|
+
@klass = Doc do
|
20
|
+
key :first_name, String
|
21
|
+
key :last_name, String
|
22
|
+
key :age, Integer
|
23
|
+
key :date, Date
|
24
|
+
end
|
25
|
+
@klass.timestamps!
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when #record_timestamps is set to true" do
|
29
|
+
it "should set created_at and updated_at on create" do
|
30
|
+
doc = @klass.new(:first_name => 'John', :age => 27)
|
31
|
+
doc.created_at.should be(nil)
|
32
|
+
doc.updated_at.should be(nil)
|
33
|
+
doc.save
|
34
|
+
doc.created_at.should_not be(nil)
|
35
|
+
doc.updated_at.should_not be(nil)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should not overwrite created_at if it already exists" do
|
39
|
+
original_created_at = 1.month.ago
|
40
|
+
doc = @klass.new(:first_name => 'John', :age => 27, :created_at => original_created_at)
|
41
|
+
doc.created_at.to_i.should == original_created_at.to_i
|
42
|
+
doc.updated_at.should be_nil
|
43
|
+
doc.save
|
44
|
+
doc.created_at.to_i.should == original_created_at.to_i
|
45
|
+
doc.updated_at.should_not be_nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should set updated_at on field update but leave created_at alone" do
|
49
|
+
doc = @klass.create(:first_name => 'John', :age => 27)
|
50
|
+
old_created_at = doc.created_at
|
51
|
+
old_updated_at = doc.updated_at
|
52
|
+
doc.first_name = 'Johnny'
|
53
|
+
|
54
|
+
Timecop.freeze(Time.now + 5.seconds) do
|
55
|
+
doc.save
|
56
|
+
end
|
57
|
+
|
58
|
+
doc.created_at.should == old_created_at
|
59
|
+
doc.updated_at.should_not == old_updated_at
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should set updated_at on document update but leave created_at alone" do
|
63
|
+
doc = @klass.create(:first_name => 'John', :age => 27)
|
64
|
+
old_created_at = doc.created_at.to_i
|
65
|
+
|
66
|
+
new_updated_at = Time.at(Time.now.to_i + 5.seconds)
|
67
|
+
Timecop.freeze(new_updated_at) do
|
68
|
+
@klass.update(doc._id, { :first_name => 'Johnny' })
|
69
|
+
end
|
70
|
+
|
71
|
+
doc = doc.reload
|
72
|
+
doc.created_at.to_i.should be_within(1).of(old_created_at.to_i)
|
73
|
+
doc.updated_at.to_i.should be_within(1).of(new_updated_at.to_i)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "when #record_timestamps is set to false" do
|
78
|
+
before do
|
79
|
+
@klass.record_timestamps = false
|
80
|
+
end
|
81
|
+
|
82
|
+
after do
|
83
|
+
@klass.record_timestamps = true
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should not set created_at on document create" do
|
87
|
+
doc = @klass.create(:first_name => "John")
|
88
|
+
doc.created_at.should be_nil
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should not set updated_at on document create" do
|
92
|
+
doc = @klass.create(:first_name => "John")
|
93
|
+
doc.updated_at.should be_nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Touch" do
|
4
|
+
context "touch" do
|
5
|
+
context "document" do
|
6
|
+
before do
|
7
|
+
@document = Doc { timestamps! }
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should update the updated_at timestamp" do
|
11
|
+
doc = @document.create
|
12
|
+
old_updated_at = doc.updated_at
|
13
|
+
|
14
|
+
Timecop.freeze(Time.now + 1.day) do
|
15
|
+
doc.touch
|
16
|
+
end
|
17
|
+
|
18
|
+
doc.reload
|
19
|
+
doc.updated_at.should_not == old_updated_at
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "embedded document" do
|
24
|
+
it "should update the updated_at timestamp" do
|
25
|
+
Doc = Doc("Document") { timestamps!}
|
26
|
+
Emdoc = EDoc("EmbeddedDocument") { timestamps! }
|
27
|
+
Doc.has_many :emdocs, :class => Emdoc
|
28
|
+
|
29
|
+
doc = Doc.create
|
30
|
+
emdoc = Emdoc.new
|
31
|
+
doc.emdocs << emdoc
|
32
|
+
doc.save
|
33
|
+
|
34
|
+
old_updated_at = emdoc.updated_at
|
35
|
+
document_old_updated_at = doc.updated_at
|
36
|
+
|
37
|
+
Timecop.freeze(Time.now + 1.day) do
|
38
|
+
emdoc.touch
|
39
|
+
end
|
40
|
+
|
41
|
+
doc.reload
|
42
|
+
emdoc.updated_at.should_not == old_updated_at
|
43
|
+
doc.updated_at.should_not == document_old_updated_at
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "association" do
|
48
|
+
before do
|
49
|
+
@post_class = Doc("Post") do
|
50
|
+
key :touched_at, DateTime
|
51
|
+
timestamps!
|
52
|
+
end
|
53
|
+
@comment_class = Doc("Comment") do
|
54
|
+
key :post_id, ObjectId
|
55
|
+
key :text, String
|
56
|
+
timestamps!
|
57
|
+
end
|
58
|
+
|
59
|
+
@post_class.many :comments, :class => @comment_class
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should not be true by default' do
|
63
|
+
@comment_class.belongs_to :post, :class => @post_class
|
64
|
+
@comment_class.associations[:post].touch?.should_not be_truthy
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'touch the parent when true' do
|
68
|
+
before do
|
69
|
+
@comment_class.belongs_to :post, :class => @post_class, :touch => true
|
70
|
+
@post = @post_class.create(:title => 'Hello, world!')
|
71
|
+
@comment = @post.comments.build
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should when the child is created" do
|
75
|
+
orig_updated_at = @post.updated_at
|
76
|
+
Timecop.freeze(Time.now + 1.day) do
|
77
|
+
@comment.save
|
78
|
+
end
|
79
|
+
|
80
|
+
@post.reload.updated_at.should_not == orig_updated_at
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should when the child is updated" do
|
84
|
+
@comment.save
|
85
|
+
old_updated_at = @post.updated_at
|
86
|
+
Timecop.freeze(Time.now + 2.day) do
|
87
|
+
@comment.update_attributes(:text => "Something")
|
88
|
+
end
|
89
|
+
@post.reload.updated_at.should_not == old_updated_at
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should when the child is touched" do
|
93
|
+
@comment.save
|
94
|
+
old_updated_at = @post.updated_at
|
95
|
+
Timecop.freeze(Time.now + 3.day) do
|
96
|
+
@comment.touch
|
97
|
+
end
|
98
|
+
@post.reload.updated_at.should_not == old_updated_at
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "when set to a symbol that is a key of parent" do
|
103
|
+
it "should set that key on touch events" do
|
104
|
+
@comment_class.belongs_to :post, :class => @post_class, :touch => :touched_at
|
105
|
+
post = @post_class.create(:title => 'Hello, world!')
|
106
|
+
post.touched_at.should be_nil
|
107
|
+
|
108
|
+
comment = post.comments.build
|
109
|
+
comment.save
|
110
|
+
post.reload.touched_at.should_not be_nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should not touch the parent when false' do
|
115
|
+
post = @post_class.create(:title => 'Hello, world!')
|
116
|
+
comment = post.comments.build
|
117
|
+
Timecop.freeze(Time.now + 1.day) do
|
118
|
+
comment.save
|
119
|
+
end
|
120
|
+
|
121
|
+
post.reload.updated_at.should == post.created_at
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Userstamps
|
4
|
+
describe "Userstamps" do
|
5
|
+
class AltUser
|
6
|
+
include MarkMapper::Document
|
7
|
+
end
|
8
|
+
|
9
|
+
context "userstamping" do
|
10
|
+
before do
|
11
|
+
@document = Doc do
|
12
|
+
userstamps!
|
13
|
+
end
|
14
|
+
@document_alt_user = Doc do
|
15
|
+
userstamps! :class_name => 'Userstamps::AltUser'
|
16
|
+
end
|
17
|
+
@document_alt_user_class = Doc do
|
18
|
+
userstamps! :class => Userstamps::AltUser
|
19
|
+
end
|
20
|
+
@docs = [@document, @document_alt_user, @document_alt_user_class]
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should add creator_id key" do
|
24
|
+
@docs.each{ |d| d.keys.should include('creator_id') }
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should add updater_id key" do
|
28
|
+
@docs.each{ |d| d.keys.should include('updater_id') }
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should add belongs_to creator" do
|
32
|
+
@docs.each{ |d| d.associations.keys.should include(:creator) }
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should add belongs_to updater" do
|
36
|
+
@docs.each{ |d| d.associations.keys.should include(:updater) }
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should properly set class names" do
|
40
|
+
@document.associations[:creator].class_name.should == 'User'
|
41
|
+
@document_alt_user.associations[:creator].class_name.should == 'Userstamps::AltUser'
|
42
|
+
@document_alt_user_class.associations[:creator].class_name.should == 'Userstamps::AltUser'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,416 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Validations" do
|
4
|
+
context "Saving a new document that is invalid" do
|
5
|
+
before do
|
6
|
+
@document = Doc do
|
7
|
+
key :name, String, :required => true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should not insert document" do
|
12
|
+
doc = @document.new
|
13
|
+
doc.save
|
14
|
+
@document.count.should == 0
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should populate document's errors" do
|
18
|
+
doc = @document.new
|
19
|
+
doc.errors.size.should == 0
|
20
|
+
doc.save
|
21
|
+
doc.errors.full_messages.should == ["Name can't be blank"]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "Saving a document that is invalid (destructive)" do
|
26
|
+
before do
|
27
|
+
@document = Doc do
|
28
|
+
key :name, String, :required => true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should raise error" do
|
33
|
+
doc = @document.new
|
34
|
+
lambda { doc.save! }.should raise_error(MarkMapper::DocumentNotValid)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should set document on exception" do
|
38
|
+
doc = @document.new
|
39
|
+
begin
|
40
|
+
doc.save!
|
41
|
+
rescue MarkMapper::DocumentNotValid => e
|
42
|
+
e.document.should == doc
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "Creating a document that is invalid (destructive)" do
|
48
|
+
before do
|
49
|
+
@document = Doc do
|
50
|
+
key :name, String, :required => true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should raise error" do
|
55
|
+
lambda { @document.create! }.should raise_error(MarkMapper::DocumentNotValid)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should create a new document" do
|
59
|
+
instance = @document.create!(:name => "James")
|
60
|
+
instance.new_record?.should be_falsey
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "Saving an existing document that is invalid" do
|
65
|
+
before do
|
66
|
+
@document = Doc do
|
67
|
+
key :name, String, :required => true
|
68
|
+
end
|
69
|
+
|
70
|
+
@doc = @document.create(:name => 'John Nunemaker')
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should not update document" do
|
74
|
+
@doc.name = nil
|
75
|
+
@doc.save
|
76
|
+
@doc.reload.name.should == 'John Nunemaker'
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should populate document's errors" do
|
80
|
+
@doc.name = nil
|
81
|
+
@doc.save
|
82
|
+
@doc.errors.full_messages.should == ["Name can't be blank"]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "Adding validation errors" do
|
87
|
+
before do
|
88
|
+
@document = Doc do
|
89
|
+
key :action, String
|
90
|
+
def action_present
|
91
|
+
errors.add(:action, 'is invalid') if action.blank?
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should work with validate :on => :create callback" do
|
97
|
+
@document.validate :action_present, :on => :create
|
98
|
+
|
99
|
+
doc = @document.create(:action => nil)
|
100
|
+
doc.should have_error_on(:action)
|
101
|
+
|
102
|
+
doc.action = 'kick'
|
103
|
+
doc.save
|
104
|
+
doc.should_not have_error_on(:action)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should work with validate :on => :update callback" do
|
108
|
+
@document.validate :action_present, :on => :update
|
109
|
+
|
110
|
+
doc = @document.new
|
111
|
+
doc.action = nil
|
112
|
+
doc.should_not have_error_on(:action)
|
113
|
+
doc.save
|
114
|
+
|
115
|
+
doc.action = nil
|
116
|
+
doc.should have_error_on(:action)
|
117
|
+
|
118
|
+
doc.action = 'kick'
|
119
|
+
doc.should_not have_error_on(:action)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "validating uniqueness of" do
|
124
|
+
before do
|
125
|
+
@document = Doc do
|
126
|
+
key :name, String
|
127
|
+
validates_uniqueness_of :name
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should not fail if object is new" do
|
132
|
+
doc = @document.new
|
133
|
+
doc.should_not have_error_on(:name)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should not fail when new object is out of scope" do
|
137
|
+
document = Doc do
|
138
|
+
key :name
|
139
|
+
key :adult
|
140
|
+
validates_uniqueness_of :name, :scope => :adult
|
141
|
+
end
|
142
|
+
doc = document.new("name" => "joe", :adult => true)
|
143
|
+
doc.save.should be_truthy
|
144
|
+
|
145
|
+
doc2 = document.new("name" => "joe", :adult => false)
|
146
|
+
doc2.should be_valid
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should work with i18n taken message" do
|
150
|
+
@document.create(:name => 'joe')
|
151
|
+
doc = @document.create(:name => 'joe')
|
152
|
+
doc.should have_error_on(:name)
|
153
|
+
doc.errors[:name].should == ['has already been taken']
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should allow to update an object" do
|
157
|
+
doc = @document.new("name" => "joe")
|
158
|
+
doc.save.should be_truthy
|
159
|
+
|
160
|
+
allow(@document).to \
|
161
|
+
receive(:first).
|
162
|
+
with(:name => 'joe').
|
163
|
+
and_return(doc)
|
164
|
+
|
165
|
+
doc.name = "joe"
|
166
|
+
doc.valid?.should be_truthy
|
167
|
+
doc.should_not have_error_on(:name)
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should fail if object name is not unique" do
|
171
|
+
doc = @document.new("name" => "joe")
|
172
|
+
doc.save.should be_truthy
|
173
|
+
|
174
|
+
allow(@document).to \
|
175
|
+
receive(:first).
|
176
|
+
with(:name => 'joe').
|
177
|
+
and_return(doc)
|
178
|
+
|
179
|
+
doc2 = @document.new("name" => "joe")
|
180
|
+
doc2.should have_error_on(:name)
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should allow multiple blank entries if :allow_blank => true" do
|
184
|
+
document = Doc do
|
185
|
+
key :name
|
186
|
+
validates_uniqueness_of :name, :allow_blank => :true
|
187
|
+
end
|
188
|
+
|
189
|
+
doc = document.new("name" => "")
|
190
|
+
doc.save.should be_truthy
|
191
|
+
|
192
|
+
allow(@document).to \
|
193
|
+
receive(:first).
|
194
|
+
with(:name => '').
|
195
|
+
and_return(doc)
|
196
|
+
|
197
|
+
doc2 = document.new("name" => "")
|
198
|
+
doc2.should_not have_error_on(:name)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should allow multiple nil entries if :allow_nil => true" do
|
202
|
+
document = Doc do
|
203
|
+
key :name
|
204
|
+
validates_uniqueness_of :name, :allow_nil => :true
|
205
|
+
end
|
206
|
+
|
207
|
+
doc = document.new('name' => nil)
|
208
|
+
doc.save.should be_truthy
|
209
|
+
|
210
|
+
doc2 = document.new('name' => nil)
|
211
|
+
doc2.should_not have_error_on(:name)
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should allow entries that differ only in case by default" do
|
215
|
+
document = Doc do
|
216
|
+
key :name
|
217
|
+
validates_uniqueness_of :name
|
218
|
+
end
|
219
|
+
|
220
|
+
doc = document.new("name" => "BLAMMO")
|
221
|
+
doc.save.should be_truthy
|
222
|
+
|
223
|
+
doc2 = document.new("name" => "Blammo")
|
224
|
+
doc2.should_not have_error_on(:name)
|
225
|
+
end
|
226
|
+
|
227
|
+
context "with :case_sensitive => false" do
|
228
|
+
before do
|
229
|
+
@document = Doc do
|
230
|
+
key :name
|
231
|
+
validates_uniqueness_of :name, :case_sensitive => false
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
it "should fail on entries that differ only in case" do
|
236
|
+
doc = @document.new("name" => "BLAMMO")
|
237
|
+
doc.save.should be_truthy
|
238
|
+
|
239
|
+
doc2 = @document.new("name" => "blammo")
|
240
|
+
doc2.should have_error_on(:name)
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should not raise an error if value is nil" do
|
244
|
+
doc = @document.new("name" => nil)
|
245
|
+
lambda { doc.valid? }.should_not raise_error
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should not raise an error if special Regexp characters used" do
|
249
|
+
doc = @document.new("name" => '?')
|
250
|
+
lambda { doc.valid? }.should_not raise_error
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should check for uniqueness using entire string" do
|
254
|
+
doc = @document.new("name" => "John Doe")
|
255
|
+
doc.save.should be_truthy
|
256
|
+
|
257
|
+
doc2 = @document.new("name" => "John")
|
258
|
+
doc2.valid?.should be_truthy
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context "scoped by a single attribute" do
|
263
|
+
before do
|
264
|
+
@document = Doc do
|
265
|
+
key :name, String
|
266
|
+
key :scope, String
|
267
|
+
validates_uniqueness_of :name, :scope => :scope
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
it "should fail if the same name exists in the scope" do
|
272
|
+
doc = @document.new("name" => "joe", "scope" => "one")
|
273
|
+
doc.save.should be_truthy
|
274
|
+
|
275
|
+
allow(@document).to \
|
276
|
+
receive(:first).
|
277
|
+
with(:name => 'joe', :scope => "one").
|
278
|
+
and_return(doc)
|
279
|
+
|
280
|
+
doc2 = @document.new("name" => "joe", "scope" => "one")
|
281
|
+
doc2.should have_error_on(:name)
|
282
|
+
end
|
283
|
+
|
284
|
+
it "should pass if the same name exists in a different scope" do
|
285
|
+
doc = @document.new("name" => "joe", "scope" => "one")
|
286
|
+
doc.save.should be_truthy
|
287
|
+
|
288
|
+
allow(@document).to \
|
289
|
+
receive(:first).
|
290
|
+
with(:name => 'joe', :scope => 'two').
|
291
|
+
and_return(nil)
|
292
|
+
|
293
|
+
doc2 = @document.new("name" => "joe", "scope" => "two")
|
294
|
+
doc2.should_not have_error_on(:name)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
context "scoped by a multiple attributes" do
|
299
|
+
before do
|
300
|
+
@document = Doc do
|
301
|
+
key :name, String
|
302
|
+
key :first_scope, String
|
303
|
+
key :second_scope, String
|
304
|
+
validates_uniqueness_of :name, :scope => [:first_scope, :second_scope]
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
it "should fail if the same name exists in the scope" do
|
309
|
+
doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
|
310
|
+
doc.save.should be_truthy
|
311
|
+
|
312
|
+
allow(@document).to \
|
313
|
+
receive(:first).
|
314
|
+
with(:name => 'joe', :first_scope => 'one', :second_scope => 'two').
|
315
|
+
and_return(doc)
|
316
|
+
|
317
|
+
doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
|
318
|
+
doc2.should have_error_on(:name)
|
319
|
+
end
|
320
|
+
|
321
|
+
it "should pass if the same name exists in a different scope" do
|
322
|
+
doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
|
323
|
+
doc.save.should be_truthy
|
324
|
+
|
325
|
+
allow(@document).to \
|
326
|
+
receive(:first).
|
327
|
+
with(:name => 'joe', :first_scope => 'one', :second_scope => 'one').
|
328
|
+
and_return(nil)
|
329
|
+
|
330
|
+
doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "one")
|
331
|
+
doc2.should_not have_error_on(:name)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
context "validating associated docs" do
|
337
|
+
before do
|
338
|
+
@child_class = EDoc do
|
339
|
+
key :name, :required => true
|
340
|
+
end
|
341
|
+
|
342
|
+
@root_class = Doc { }
|
343
|
+
@root_class.many :children, :class => @child_class
|
344
|
+
@root_class.validates_associated :children, :message => 'are invalid'
|
345
|
+
end
|
346
|
+
|
347
|
+
it "should pass if there are no associated docs" do
|
348
|
+
doc = @root_class.new
|
349
|
+
doc.save.should be_truthy
|
350
|
+
end
|
351
|
+
|
352
|
+
it "should pass if the associated doc is valid" do
|
353
|
+
doc = @root_class.new
|
354
|
+
doc.children.build(:name => 'Joe')
|
355
|
+
doc.save.should be_truthy
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should fail if the associated doc is invalid" do
|
359
|
+
doc = @root_class.new
|
360
|
+
doc.children.build
|
361
|
+
doc.should have_error_on(:children)
|
362
|
+
doc.errors[:children].should == ['are invalid']
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
context "validating associated docs with custom context" do
|
367
|
+
before do
|
368
|
+
@child_class = EDoc do
|
369
|
+
key :name
|
370
|
+
|
371
|
+
validates_length_of :name, :minimum => 5, :on => :custom_context
|
372
|
+
end
|
373
|
+
|
374
|
+
@root_class = Doc { }
|
375
|
+
@root_class.many :children, :class => @child_class
|
376
|
+
@root_class.validates_associated :children, :context => :custom_context
|
377
|
+
end
|
378
|
+
|
379
|
+
it "should pass if there are no associated docs" do
|
380
|
+
doc = @root_class.new
|
381
|
+
doc.valid?(:custom_context).should be_truthy
|
382
|
+
end
|
383
|
+
|
384
|
+
it "should pass if the associated doc is valid" do
|
385
|
+
doc = @root_class.new
|
386
|
+
doc.children.build(:name => 'George')
|
387
|
+
doc.valid?(:custom_context).should be_truthy
|
388
|
+
end
|
389
|
+
|
390
|
+
it "should fail if the associated doc is invalid" do
|
391
|
+
doc = @root_class.new
|
392
|
+
doc.children.build(:name => 'Bob')
|
393
|
+
doc.valid?(:custom_context).should_not be_truthy
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
# context "validates uniqueness of with :unique shortcut" do
|
398
|
+
# it "should work" do
|
399
|
+
# @document = Doc do
|
400
|
+
# key :name, String, :unique => true
|
401
|
+
# end
|
402
|
+
#
|
403
|
+
# doc = @document.create(:name => 'John')
|
404
|
+
# doc.should_not have_error_on(:name)
|
405
|
+
#
|
406
|
+
# allow(@document).to \
|
407
|
+
# receive(:first).
|
408
|
+
# with(:name => 'John').
|
409
|
+
# and_return(doc)
|
410
|
+
#
|
411
|
+
# second_john = @document.create(:name => 'John')
|
412
|
+
# second_john.should have_error_on(:name)
|
413
|
+
# second_john.errors[:name].should == ['has already been taken']
|
414
|
+
# end
|
415
|
+
# end
|
416
|
+
end
|