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.
Files changed (211) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.rdoc +39 -0
  4. data/examples/attr_accessible.rb +24 -0
  5. data/examples/attr_protected.rb +24 -0
  6. data/examples/cache_key.rb +26 -0
  7. data/examples/custom_types.rb +26 -0
  8. data/examples/identity_map.rb +30 -0
  9. data/examples/identity_map/automatic.rb +2 -0
  10. data/examples/keys.rb +42 -0
  11. data/examples/modifiers/set.rb +27 -0
  12. data/examples/plugins.rb +40 -0
  13. data/examples/querying.rb +39 -0
  14. data/examples/sample_app.rb +43 -0
  15. data/examples/scopes.rb +56 -0
  16. data/examples/validating/embedded_docs.rb +31 -0
  17. data/lib/mark_mapper.rb +125 -0
  18. data/lib/mark_mapper/config.rb +90 -0
  19. data/lib/mark_mapper/connection.rb +60 -0
  20. data/lib/mark_mapper/criteria_hash.rb +194 -0
  21. data/lib/mark_mapper/document.rb +46 -0
  22. data/lib/mark_mapper/embedded_document.rb +32 -0
  23. data/lib/mark_mapper/exceptions.rb +33 -0
  24. data/lib/mark_mapper/extensions/array.rb +27 -0
  25. data/lib/mark_mapper/extensions/boolean.rb +45 -0
  26. data/lib/mark_mapper/extensions/date.rb +29 -0
  27. data/lib/mark_mapper/extensions/duplicable.rb +86 -0
  28. data/lib/mark_mapper/extensions/float.rb +18 -0
  29. data/lib/mark_mapper/extensions/hash.rb +26 -0
  30. data/lib/mark_mapper/extensions/integer.rb +27 -0
  31. data/lib/mark_mapper/extensions/kernel.rb +11 -0
  32. data/lib/mark_mapper/extensions/nil_class.rb +18 -0
  33. data/lib/mark_mapper/extensions/object.rb +30 -0
  34. data/lib/mark_mapper/extensions/object_id.rb +18 -0
  35. data/lib/mark_mapper/extensions/set.rb +20 -0
  36. data/lib/mark_mapper/extensions/string.rb +31 -0
  37. data/lib/mark_mapper/extensions/symbol.rb +87 -0
  38. data/lib/mark_mapper/extensions/time.rb +29 -0
  39. data/lib/mark_mapper/locale/en.yml +5 -0
  40. data/lib/mark_mapper/middleware/identity_map.rb +41 -0
  41. data/lib/mark_mapper/normalizers/criteria_hash_key.rb +17 -0
  42. data/lib/mark_mapper/normalizers/criteria_hash_value.rb +66 -0
  43. data/lib/mark_mapper/normalizers/fields_value.rb +26 -0
  44. data/lib/mark_mapper/normalizers/hash_key.rb +19 -0
  45. data/lib/mark_mapper/normalizers/integer.rb +19 -0
  46. data/lib/mark_mapper/normalizers/options_hash_value.rb +83 -0
  47. data/lib/mark_mapper/normalizers/sort_value.rb +55 -0
  48. data/lib/mark_mapper/options_hash.rb +103 -0
  49. data/lib/mark_mapper/pagination.rb +6 -0
  50. data/lib/mark_mapper/pagination/collection.rb +32 -0
  51. data/lib/mark_mapper/pagination/paginator.rb +46 -0
  52. data/lib/mark_mapper/plugins.rb +22 -0
  53. data/lib/mark_mapper/plugins/accessible.rb +61 -0
  54. data/lib/mark_mapper/plugins/active_model.rb +18 -0
  55. data/lib/mark_mapper/plugins/associations.rb +96 -0
  56. data/lib/mark_mapper/plugins/associations/base.rb +98 -0
  57. data/lib/mark_mapper/plugins/associations/belongs_to_association.rb +63 -0
  58. data/lib/mark_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +35 -0
  59. data/lib/mark_mapper/plugins/associations/belongs_to_proxy.rb +52 -0
  60. data/lib/mark_mapper/plugins/associations/collection.rb +29 -0
  61. data/lib/mark_mapper/plugins/associations/embedded_collection.rb +44 -0
  62. data/lib/mark_mapper/plugins/associations/in_array_proxy.rb +133 -0
  63. data/lib/mark_mapper/plugins/associations/many_association.rb +63 -0
  64. data/lib/mark_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  65. data/lib/mark_mapper/plugins/associations/many_documents_proxy.rb +142 -0
  66. data/lib/mark_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  67. data/lib/mark_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  68. data/lib/mark_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  69. data/lib/mark_mapper/plugins/associations/one_as_proxy.rb +22 -0
  70. data/lib/mark_mapper/plugins/associations/one_association.rb +48 -0
  71. data/lib/mark_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +30 -0
  72. data/lib/mark_mapper/plugins/associations/one_embedded_proxy.rb +44 -0
  73. data/lib/mark_mapper/plugins/associations/one_proxy.rb +95 -0
  74. data/lib/mark_mapper/plugins/associations/proxy.rb +138 -0
  75. data/lib/mark_mapper/plugins/associations/single_association.rb +46 -0
  76. data/lib/mark_mapper/plugins/caching.rb +21 -0
  77. data/lib/mark_mapper/plugins/callbacks.rb +42 -0
  78. data/lib/mark_mapper/plugins/clone.rb +24 -0
  79. data/lib/mark_mapper/plugins/counter_cache.rb +97 -0
  80. data/lib/mark_mapper/plugins/dirty.rb +61 -0
  81. data/lib/mark_mapper/plugins/document.rb +41 -0
  82. data/lib/mark_mapper/plugins/dumpable.rb +22 -0
  83. data/lib/mark_mapper/plugins/dynamic_querying.rb +45 -0
  84. data/lib/mark_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  85. data/lib/mark_mapper/plugins/embedded_callbacks.rb +81 -0
  86. data/lib/mark_mapper/plugins/embedded_document.rb +53 -0
  87. data/lib/mark_mapper/plugins/equality.rb +23 -0
  88. data/lib/mark_mapper/plugins/identity_map.rb +144 -0
  89. data/lib/mark_mapper/plugins/indexable.rb +86 -0
  90. data/lib/mark_mapper/plugins/inspect.rb +16 -0
  91. data/lib/mark_mapper/plugins/keys.rb +470 -0
  92. data/lib/mark_mapper/plugins/keys/key.rb +134 -0
  93. data/lib/mark_mapper/plugins/keys/static.rb +45 -0
  94. data/lib/mark_mapper/plugins/logger.rb +18 -0
  95. data/lib/mark_mapper/plugins/modifiers.rb +140 -0
  96. data/lib/mark_mapper/plugins/pagination.rb +16 -0
  97. data/lib/mark_mapper/plugins/partial_updates.rb +77 -0
  98. data/lib/mark_mapper/plugins/persistence.rb +79 -0
  99. data/lib/mark_mapper/plugins/protected.rb +45 -0
  100. data/lib/mark_mapper/plugins/querying.rb +173 -0
  101. data/lib/mark_mapper/plugins/querying/decorated_markmapper_query.rb +75 -0
  102. data/lib/mark_mapper/plugins/rails.rb +79 -0
  103. data/lib/mark_mapper/plugins/rails/active_record_association_adapter.rb +33 -0
  104. data/lib/mark_mapper/plugins/sci.rb +82 -0
  105. data/lib/mark_mapper/plugins/scopes.rb +28 -0
  106. data/lib/mark_mapper/plugins/serialization.rb +109 -0
  107. data/lib/mark_mapper/plugins/timestamps.rb +29 -0
  108. data/lib/mark_mapper/plugins/touch.rb +18 -0
  109. data/lib/mark_mapper/plugins/userstamps.rb +18 -0
  110. data/lib/mark_mapper/plugins/validations.rb +96 -0
  111. data/lib/mark_mapper/query.rb +278 -0
  112. data/lib/mark_mapper/railtie.rb +52 -0
  113. data/lib/mark_mapper/railtie/database.rake +65 -0
  114. data/lib/mark_mapper/translation.rb +10 -0
  115. data/lib/mark_mapper/version.rb +4 -0
  116. data/lib/rails/generators/mark_mapper/config/config_generator.rb +37 -0
  117. data/lib/rails/generators/mark_mapper/config/templates/marklogic.yml +19 -0
  118. data/lib/rails/generators/mark_mapper/model/model_generator.rb +40 -0
  119. data/lib/rails/generators/mark_mapper/model/templates/model.rb +17 -0
  120. data/spec/config/mark_mapper.yml +6 -0
  121. data/spec/examples_spec.rb +25 -0
  122. data/spec/functional/accessible_spec.rb +198 -0
  123. data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +64 -0
  124. data/spec/functional/associations/belongs_to_proxy_spec.rb +255 -0
  125. data/spec/functional/associations/in_array_proxy_spec.rb +349 -0
  126. data/spec/functional/associations/many_documents_as_proxy_spec.rb +230 -0
  127. data/spec/functional/associations/many_documents_proxy_spec.rb +968 -0
  128. data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +238 -0
  129. data/spec/functional/associations/many_embedded_proxy_spec.rb +288 -0
  130. data/spec/functional/associations/many_polymorphic_proxy_spec.rb +302 -0
  131. data/spec/functional/associations/one_as_proxy_spec.rb +489 -0
  132. data/spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb +207 -0
  133. data/spec/functional/associations/one_embedded_proxy_spec.rb +100 -0
  134. data/spec/functional/associations/one_proxy_spec.rb +406 -0
  135. data/spec/functional/associations_spec.rb +48 -0
  136. data/spec/functional/caching_spec.rb +75 -0
  137. data/spec/functional/callbacks_spec.rb +330 -0
  138. data/spec/functional/counter_cache_spec.rb +235 -0
  139. data/spec/functional/dirty_spec.rb +316 -0
  140. data/spec/functional/document_spec.rb +310 -0
  141. data/spec/functional/dumpable_spec.rb +24 -0
  142. data/spec/functional/dynamic_querying_spec.rb +75 -0
  143. data/spec/functional/embedded_document_spec.rb +316 -0
  144. data/spec/functional/equality_spec.rb +20 -0
  145. data/spec/functional/extensions_spec.rb +16 -0
  146. data/spec/functional/identity_map_spec.rb +483 -0
  147. data/spec/functional/keys_spec.rb +339 -0
  148. data/spec/functional/logger_spec.rb +20 -0
  149. data/spec/functional/modifiers_spec.rb +446 -0
  150. data/spec/functional/options_hash_spec.rb +41 -0
  151. data/spec/functional/pagination_spec.rb +89 -0
  152. data/spec/functional/partial_updates_spec.rb +530 -0
  153. data/spec/functional/protected_spec.rb +199 -0
  154. data/spec/functional/querying_spec.rb +984 -0
  155. data/spec/functional/rails_spec.rb +55 -0
  156. data/spec/functional/sci_spec.rb +374 -0
  157. data/spec/functional/scopes_spec.rb +204 -0
  158. data/spec/functional/static_keys_spec.rb +153 -0
  159. data/spec/functional/timestamps_spec.rb +97 -0
  160. data/spec/functional/touch_spec.rb +125 -0
  161. data/spec/functional/userstamps_spec.rb +46 -0
  162. data/spec/functional/validations_spec.rb +416 -0
  163. data/spec/quality_spec.rb +51 -0
  164. data/spec/spec_helper.rb +150 -0
  165. data/spec/support/matchers.rb +15 -0
  166. data/spec/support/models.rb +256 -0
  167. data/spec/symbol_operator_spec.rb +70 -0
  168. data/spec/symbol_spec.rb +9 -0
  169. data/spec/unit/associations/base_spec.rb +146 -0
  170. data/spec/unit/associations/belongs_to_association_spec.rb +30 -0
  171. data/spec/unit/associations/many_association_spec.rb +64 -0
  172. data/spec/unit/associations/one_association_spec.rb +48 -0
  173. data/spec/unit/associations/proxy_spec.rb +103 -0
  174. data/spec/unit/clone_spec.rb +79 -0
  175. data/spec/unit/config_generator_spec.rb +24 -0
  176. data/spec/unit/criteria_hash_spec.rb +218 -0
  177. data/spec/unit/document_spec.rb +251 -0
  178. data/spec/unit/dynamic_finder_spec.rb +125 -0
  179. data/spec/unit/embedded_document_spec.rb +676 -0
  180. data/spec/unit/equality_spec.rb +38 -0
  181. data/spec/unit/exceptions_spec.rb +12 -0
  182. data/spec/unit/extensions_spec.rb +368 -0
  183. data/spec/unit/identity_map_middleware_spec.rb +134 -0
  184. data/spec/unit/inspect_spec.rb +47 -0
  185. data/spec/unit/key_spec.rb +276 -0
  186. data/spec/unit/keys_spec.rb +155 -0
  187. data/spec/unit/mark_mapper_spec.rb +37 -0
  188. data/spec/unit/model_generator_spec.rb +45 -0
  189. data/spec/unit/normalizers/criteria_hash_key_spec.rb +37 -0
  190. data/spec/unit/normalizers/criteria_hash_value_spec.rb +200 -0
  191. data/spec/unit/normalizers/fields_value_spec.rb +45 -0
  192. data/spec/unit/normalizers/hash_key_spec.rb +15 -0
  193. data/spec/unit/normalizers/integer_spec.rb +24 -0
  194. data/spec/unit/normalizers/options_hash_value_spec.rb +99 -0
  195. data/spec/unit/normalizers/sort_value_spec.rb +98 -0
  196. data/spec/unit/options_hash_spec.rb +64 -0
  197. data/spec/unit/pagination/collection_spec.rb +30 -0
  198. data/spec/unit/pagination/paginator_spec.rb +118 -0
  199. data/spec/unit/pagination_spec.rb +11 -0
  200. data/spec/unit/plugins_spec.rb +89 -0
  201. data/spec/unit/query_spec.rb +837 -0
  202. data/spec/unit/rails_compatibility_spec.rb +40 -0
  203. data/spec/unit/rails_reflect_on_association_spec.rb +118 -0
  204. data/spec/unit/rails_spec.rb +188 -0
  205. data/spec/unit/serialization_spec.rb +169 -0
  206. data/spec/unit/serializers/json_serializer_spec.rb +218 -0
  207. data/spec/unit/serializers/xml_serializer_spec.rb +198 -0
  208. data/spec/unit/time_zones_spec.rb +44 -0
  209. data/spec/unit/translation_spec.rb +27 -0
  210. data/spec/unit/validations_spec.rb +588 -0
  211. metadata +307 -0
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe MarkMapper::OptionsHash do
4
+ subject { described_class.new }
5
+
6
+ describe "#[]=" do
7
+ it "changes order to sort" do
8
+ subject[:order] = "foo asc"
9
+ subject[:sort].should == [["foo", 1]]
10
+ subject[:order].should be_nil
11
+ end
12
+
13
+ it "changes sort(id) to sort(_id)" do
14
+ subject[:sort] = "id asc"
15
+ subject[:sort].should == [["_id", 1]]
16
+ end
17
+
18
+ it "changes select to fields" do
19
+ subject[:select] = [:foo]
20
+ subject[:fields].should == [:foo]
21
+ subject[:select].should be_nil
22
+ end
23
+
24
+ it "changes offset to skip" do
25
+ subject[:offset] = 10
26
+ subject[:skip].should == 10
27
+ subject[:offset].should be_nil
28
+ end
29
+
30
+ it "changes id to _id" do
31
+ subject[:id] = :foo
32
+ subject[:_id].should == :foo
33
+ subject[:id].should be_nil
34
+ end
35
+
36
+ it "does not change the sort field" do
37
+ subject[:order] = :order.asc
38
+ subject[:sort].should == [["order", 1]]
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Pagination" do
4
+ before do
5
+ @document = Doc do
6
+ key :first_name, String
7
+ key :last_name, String
8
+ key :age, Integer
9
+
10
+ def self.per_page; 1 end
11
+ end
12
+
13
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
14
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
15
+ @doc3 = @document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
16
+ end
17
+
18
+ it "should return the total pages" do
19
+ result = @document.paginate(:per_page => 2, :page => 1)
20
+ result.total_pages.should == 2
21
+ end
22
+
23
+ it "should return the total pages when defaulting to the document class per_page" do
24
+ result = @document.paginate(:page => 1)
25
+ result.total_pages.should == 3
26
+ end
27
+
28
+ it "should return the total of records" do
29
+ result = @document.paginate(:per_page => 2, :page => 1)
30
+ result.total_entries.should == 3
31
+ end
32
+
33
+ it "should return the items" do
34
+ result = @document.paginate(:per_page => 2, :page => 1, :order => 'first_name')
35
+ result.size.should == 2
36
+ result.should == [@doc1, @doc3]
37
+ end
38
+
39
+ it "should accept conditions" do
40
+ result = @document.paginate({
41
+ :last_name => 'Nunemaker',
42
+ :order => "age DESC",
43
+ :per_page => 2,
44
+ :page => 1,
45
+ })
46
+ result.should == [@doc1, @doc3]
47
+ result.first.age.should == 27
48
+
49
+ result = @document.paginate({
50
+ :conditions => {:last_name => 'Nunemaker'},
51
+ :order => "age DESC",
52
+ :per_page => 2,
53
+ :page => 1} )
54
+ result.should == [@doc1, @doc3]
55
+ result.first.age.should == 27
56
+ end
57
+
58
+ it "should withstand rigor" do
59
+ result = @document.paginate({
60
+ :per_page => 1,
61
+ :page => 1,
62
+ :order => 'age desc',
63
+ :last_name => 'Nunemaker'
64
+ })
65
+ result.should == [@doc1]
66
+ result.total_entries.should == 2
67
+ result.total_pages.should == 2
68
+
69
+ result = @document.paginate({
70
+ :per_page => 1,
71
+ :page => 2,
72
+ :order => 'age desc',
73
+ :last_name => 'Nunemaker'
74
+ })
75
+ result.should == [@doc3]
76
+ result.total_entries.should == 2
77
+ result.total_pages.should == 2
78
+
79
+ result = @document.paginate({
80
+ :per_page => 2,
81
+ :page => 1,
82
+ :order => 'age desc',
83
+ :last_name => 'Nunemaker'
84
+ })
85
+ result.should == [@doc1, @doc3]
86
+ result.total_entries.should == 2
87
+ result.total_pages.should == 1
88
+ end
89
+ end
@@ -0,0 +1,530 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Partial Updates" do
4
+ before do
5
+ @klass = Doc("PartialUpdates") do
6
+ key :string_field, String
7
+ key :array_field, Array
8
+ key :hash_field, Hash
9
+ key :integer_field, Integer
10
+
11
+ timestamps!
12
+ end
13
+
14
+ @dealiased_keys_class = Doc("DealiasedKeys") do
15
+ key :foo, :abbr => "f"
16
+ end
17
+
18
+ @pet_klass = EDoc('Pet') do
19
+ key :name, String
20
+ key :flag, Boolean
21
+ end
22
+
23
+ @person_class = Doc('Person') do
24
+ key :name, String
25
+ end
26
+ @person_class.many :pets, :class => @pet_klass
27
+
28
+ @author_class = Doc("Author")
29
+
30
+ @post_class = Doc("Post") do
31
+ key :title, String
32
+ end
33
+ @post_class.one :author, :as => :authorable, :class => @author_class, :dependent => :nullify
34
+
35
+ @comments_class = Doc("Comment") do
36
+ key :text, String
37
+ key :owner_id, ObjectId
38
+ end
39
+
40
+ @klass.partial_updates = true
41
+ @dealiased_keys_class.partial_updates = true
42
+ @person_class.partial_updates = true
43
+ @post_class.partial_updates = true
44
+ @author_class.partial_updates = true
45
+ @comments_class.partial_updates = true
46
+
47
+ @obj = @klass.new
48
+ end
49
+
50
+ after do
51
+ @klass.destroy_all
52
+ @dealiased_keys_class.destroy_all
53
+ @person_class.destroy_all
54
+ @author_class.destroy_all
55
+ @post_class.destroy_all
56
+ @comments_class.destroy_all
57
+ end
58
+
59
+ it "should be able to turn on and off partial updates for the klass" do
60
+ @klass.partial_updates = false
61
+ @klass.partial_updates.should be_falsey
62
+
63
+ @klass.partial_updates = true
64
+ @klass.partial_updates.should be_truthy
65
+ end
66
+
67
+ it "should have partial updates off by default" do
68
+ Doc {}.partial_updates.should be_falsey
69
+ end
70
+
71
+ it "should update fields" do
72
+ @obj.string_field = "foo"
73
+ @obj.string_field.should == "foo"
74
+ @obj.save!
75
+
76
+ @obj.string_field = "bar"
77
+ @obj.string_field.should == "bar"
78
+ @obj.save!
79
+ end
80
+
81
+ describe "with partial updates on" do
82
+ it "should only update fields that have changed on save" do
83
+ @obj.string_field = "foo"
84
+ @obj.save!
85
+
86
+ mock_collection = double 'collection'
87
+ allow(@obj).to receive(:collection).and_return mock_collection
88
+
89
+ expect(@obj.collection).to receive(:update).with({:_id => @obj.id}, {
90
+ '$set' => {
91
+ "string_field" => "bar",
92
+ "updated_at" => kind_of(Time)
93
+ }
94
+ })
95
+
96
+ @obj.string_field = "bar"
97
+ @obj.save!
98
+ end
99
+
100
+ it "should properly nullify associations" do
101
+ @post = @post_class.create
102
+ @author = @author_class.new
103
+ @post.author = @author
104
+ @author.reload
105
+ @author.authorable_id.should == @post.id
106
+
107
+ @post.author = @author_class.new
108
+
109
+ @author.reload
110
+ @author.authorable_id.should be_nil
111
+ end
112
+ end
113
+
114
+ describe "when partial updates are off" do
115
+ before do
116
+ @klass.partial_updates = false
117
+ @obj = @klass.new
118
+
119
+ @post_class.partial_updates = false
120
+ @author_class.partial_updates = false
121
+ end
122
+
123
+ it "should update all attributes" do
124
+ @obj.string_field = "foo"
125
+ @obj.save!
126
+
127
+ mock_collection = double 'collection'
128
+ allow(@obj).to receive(:collection).and_return mock_collection
129
+
130
+ expect(@obj.collection).to receive(:save).with({
131
+ "_id" => @obj.id,
132
+ "string_field" => "bar",
133
+ "array_field" => [],
134
+ "hash_field" => {},
135
+ "created_at" => kind_of(Time),
136
+ "updated_at" => kind_of(Time),
137
+ })
138
+
139
+ @obj.string_field = "bar"
140
+ @obj.save!
141
+ end
142
+
143
+ it "should raise if fields_for_partial_update is called" do
144
+ lambda {
145
+ @obj.fields_for_partial_update
146
+ }.should raise_error(MarkMapper::Plugins::PartialUpdates::PartialUpdatesDisabledError)
147
+ end
148
+
149
+ it "should properly nullify associations" do
150
+ @post = @post_class.create
151
+ @author = @author_class.new
152
+ @post.author = @author
153
+ @author.reload
154
+ @author.authorable_id.should == @post.id
155
+
156
+ @post.author = @author_class.new
157
+
158
+ @author.reload
159
+ @author.authorable_id.should be_nil
160
+ end
161
+ end
162
+
163
+ describe "detecting attribute changes" do
164
+ it "should be able to find the fields_for_partial_update" do
165
+ @obj.string_field = "foo"
166
+
167
+ fields_for_partial_updates = @obj.fields_for_partial_update
168
+ fields_for_partial_updates.keys.should =~ [:set_fields, :unset_fields]
169
+ fields_for_partial_updates[:set_fields].should =~ ["_id", "string_field"]
170
+ fields_for_partial_updates[:unset_fields] == []
171
+ end
172
+
173
+ it "should not find any if no fields have changed" do
174
+ @obj.save!
175
+ @obj.fields_for_partial_update.should == {
176
+ :set_fields => [],
177
+ :unset_fields => []
178
+ }
179
+ end
180
+
181
+ it "should be cleared after save" do
182
+ @obj.string_field = "foo"
183
+ @obj.fields_for_partial_update[:set_fields].should =~ ["_id", "string_field"]
184
+
185
+ @obj.save!
186
+
187
+ @obj.fields_for_partial_update.should == {
188
+ :set_fields => [],
189
+ :unset_fields => []
190
+ }
191
+ end
192
+
193
+ it "should detect in place updates with a string" do
194
+ @obj.string_field = "foo"
195
+ @obj.save!
196
+
197
+ @obj.string_field.gsub!(/foo/, "bar")
198
+ @obj.string_field.should == "bar"
199
+ @obj.fields_for_partial_update[:set_fields].should == ["string_field"]
200
+ end
201
+
202
+ it "should detect in place updates with an array" do
203
+ @obj.array_field = [1]
204
+ @obj.save!
205
+
206
+ @obj.array_field << 2
207
+ @obj.array_field.should == [1,2]
208
+ @obj.fields_for_partial_update[:set_fields].should == ["array_field"]
209
+ end
210
+
211
+ it "should detect non-key based values" do
212
+ @obj.attributes = { :non_keyed_field => "foo" }
213
+ @obj.fields_for_partial_update[:set_fields].should =~ ["_id", "non_keyed_field"]
214
+ end
215
+
216
+ it "should allow fields that have numbers to be changed" do
217
+ @obj.integer_field = 1
218
+ @obj.save!
219
+
220
+ @obj.integer_field = 2
221
+ @obj.fields_for_partial_update[:set_fields].should == ["integer_field"]
222
+ end
223
+
224
+ it "should update fields with dealiased keys" do
225
+ @obj = @dealiased_keys_class.new(:foo => "one")
226
+ @obj.fields_for_partial_update[:set_fields].should =~ ["_id", "f"]
227
+ end
228
+
229
+ it "should update fields that have been deleted" do
230
+ @obj.attributes = { :foo => "bar" }
231
+ @obj.save!
232
+
233
+ attrs = @obj.attributes.dup
234
+ attrs.delete("foo")
235
+
236
+ allow(@obj).to receive(:attributes).and_return(attrs)
237
+
238
+ @obj.fields_for_partial_update.should == {
239
+ :set_fields => [],
240
+ :unset_fields => ["foo"]
241
+ }
242
+ end
243
+
244
+ it "should have an empty list of fields_for_partial_update[:set_fields] after reload" do
245
+ @obj.integer_field = 10
246
+ @obj.save!
247
+
248
+ @obj.reload
249
+ @obj.fields_for_partial_update.should == {
250
+ :set_fields => [],
251
+ :unset_fields => []
252
+ }
253
+ end
254
+
255
+ it "should return [] when re-found with find()" do
256
+ @obj.save!
257
+ obj_refound = @klass.find(@obj.id)
258
+ obj_refound.fields_for_partial_update.should == {
259
+ :set_fields => [],
260
+ :unset_fields => []
261
+ }
262
+ end
263
+
264
+ it "should return a field when re-found with find() and changed" do
265
+ @obj.save!
266
+ obj_refound = @klass.find(@obj.id)
267
+ obj_refound.string_field = "foo"
268
+ obj_refound.fields_for_partial_update[:set_fields].should == ["string_field"]
269
+ end
270
+
271
+ it "should return a field when it is a new object and it's been initialized with new" do
272
+ @obj = @klass.new({
273
+ :string_field => "foo"
274
+ })
275
+ @obj.fields_for_partial_update[:set_fields].should =~ ["_id", "string_field"]
276
+ end
277
+
278
+ it "should be able to detect any change in an array (deep copy)" do |variable|
279
+ @obj = @klass.create!({ :array_field => [["array", "of"], ["arrays"]] })
280
+ @obj.array_field.last.unshift "many"
281
+ @obj.save!
282
+ @obj.reload
283
+ @obj.array_field.last.should == ["many", "arrays"]
284
+ end
285
+
286
+ it "should be able to detect any change in an array (super deep copy)" do |variable|
287
+ @obj = @klass.create!({ :array_field => [["array", "of"], ["arrays"]] })
288
+ @obj.array_field.last << "foo"
289
+ @obj.fields_for_partial_update[:set_fields].should == ["array_field"]
290
+ end
291
+
292
+ it "should be able to detect a deep change in a hash" do
293
+ @obj = @klass.new({
294
+ :hash_field => {
295
+ :a => {
296
+ :really => {
297
+ :deep => :hash
298
+ }
299
+ }
300
+ }
301
+ })
302
+ @obj.save!
303
+
304
+ @obj.fields_for_partial_update[:set_fields].should == []
305
+
306
+ @obj.hash_field[:a][:really] = {
307
+ :really => {
308
+ :really => {
309
+ :really => {
310
+ :deep => :hash
311
+ }
312
+ }
313
+ }
314
+ }
315
+
316
+ @obj.fields_for_partial_update[:set_fields].should == ["hash_field"]
317
+ end
318
+ end
319
+
320
+ it "should set timestamps" do
321
+ @obj.string_field = "foo"
322
+ @obj.save!
323
+
324
+ @obj.created_at.should_not be_nil
325
+ @obj.updated_at.should_not be_nil
326
+
327
+ @obj.reload
328
+ @obj.created_at.should_not be_nil
329
+ @obj.updated_at.should_not be_nil
330
+ end
331
+
332
+ it "should be able to reload set values" do
333
+ @obj.string_field = "foo"
334
+ @obj.integer_field = 1
335
+ @obj.save!
336
+
337
+ @obj.reload
338
+ @obj.string_field.should == "foo"
339
+ @obj.integer_field.should == 1
340
+ end
341
+
342
+ it "should be able to reload documents created from create" do
343
+ @obj = @klass.create({
344
+ :string_field => "foo",
345
+ :integer_field => 1
346
+ })
347
+
348
+ @obj.reload
349
+ @obj.string_field.should == "foo"
350
+ @obj.integer_field.should == 1
351
+ end
352
+
353
+ describe "with embedded documents" do
354
+ before do
355
+ @person = @person_class.new
356
+ @person.pets.build
357
+ @person.save!
358
+ @person.reload
359
+ @pet = @person.pets.first
360
+ end
361
+
362
+ it "should have the child as an update key when the child changes" do
363
+ @pet.name = "monkey"
364
+ @person.fields_for_partial_update[:set_fields].should == ["pets"]
365
+ end
366
+ end
367
+
368
+ describe "with a has many" do
369
+ before do
370
+ @post_class.partial_updates = true
371
+ @comments_class.partial_updates = true
372
+
373
+ @post_class.many :comments, :class => @comments_class, :foreign_key => :owner_id
374
+ end
375
+
376
+ after do
377
+ @post_class.destroy_all
378
+ @comments_class.destroy_all
379
+ end
380
+
381
+ it "should save the children assigned through a hash in new (when assigned through new)" do
382
+ @post = @post_class.new({
383
+ :title => "foobar",
384
+ "comments" => [
385
+ { "text" => "one" }
386
+ ],
387
+ })
388
+
389
+ @post.save!
390
+ @post.reload
391
+
392
+ @post.title.should == "foobar"
393
+ @post.comments.length == 1
394
+ @post.comments.first.text.should == 'one'
395
+ end
396
+
397
+ it "should save the children assigned through a hash in new (when assigned through new) even when the children are refernced before save" do
398
+ @post = @post_class.new({
399
+ :title => "foobar",
400
+ "comments" => [
401
+ { "text" => "one" }
402
+ ],
403
+ })
404
+
405
+ # this line is important - it causes the proxy to load
406
+ @post.comments.length == 1
407
+
408
+ @post.comments.first.should be_persisted
409
+ @post.comments.first.fields_for_partial_update[:set_fields].should == []
410
+
411
+ @post.save!
412
+ @post.reload
413
+
414
+ @post.title.should == "foobar"
415
+ @post.comments.length.should == 1
416
+ @post.comments.first.text.should == 'one'
417
+ end
418
+
419
+ it "should update the children after a save if fields have changed" do
420
+ @post = @post_class.new({
421
+ :title => "foobar",
422
+ "comments" => [
423
+ { "text" => "one" }
424
+ ],
425
+ })
426
+
427
+ @post.comments.length == 1
428
+
429
+ @post.save!
430
+ @post.reload
431
+
432
+ comment = @post.comments.first
433
+ comment.text = "two"
434
+ comment.save!
435
+ comment.reload
436
+ comment.text.should == "two"
437
+ end
438
+
439
+ it "should save the built document when saving parent" do
440
+ post = @post_class.create(:title => "foobar")
441
+ comment = post.comments.build(:text => 'Foo')
442
+
443
+ post.save!
444
+
445
+ post.should_not be_new
446
+ comment.should_not be_new
447
+ end
448
+
449
+ it "should clear objects between test runs" do
450
+ @post_class.count.should == 0
451
+ @comments_class.count.should == 0
452
+ end
453
+
454
+ it "should only save one object with create" do
455
+ @post_class.count.should == 0
456
+ @comments_class.count.should == 0
457
+
458
+ @post_class.create(:title => "foobar")
459
+ @comments_class.create()
460
+
461
+ @post_class.count.should == 1
462
+ @comments_class.count.should == 1
463
+ end
464
+
465
+ it "should save an association with create with the correct attributes" do
466
+ post = @post_class.create(:title => "foobar")
467
+ @comments_class.create("text" => 'foo')
468
+
469
+ @comments_class.count.should == 1
470
+ @comments_class.first.text.should == "foo"
471
+ end
472
+
473
+ it "should be able to find objects through the association proxy" do
474
+ post = @post_class.create!(:title => "foobar")
475
+ comment = @comments_class.create!("text" => 'foo')
476
+
477
+ @comments_class.count.should == 1
478
+ @comments_class.first.text.should == "foo"
479
+
480
+ post.comments << comment
481
+
482
+ post.comments.count.should == 1
483
+ post.comments.first.text.should == "foo"
484
+ post.comments.all("text" => "foo").length.should == 1
485
+ end
486
+
487
+ it "should work with destroy all and conditions" do
488
+ @post_class.partial_updates = true
489
+ @comments_class.partial_updates = true
490
+
491
+ post = @post_class.create(:title => "foobar")
492
+ post.comments << @comments_class.create(:text => '1')
493
+ post.comments << @comments_class.create(:text => '2')
494
+ post.comments << @comments_class.create(:text => '3')
495
+
496
+ post.comments.count.should == 3
497
+ post.comments.destroy_all(:text => '1')
498
+ post.comments.count.should == 2
499
+
500
+ post.comments.destroy_all
501
+ post.comments.count.should == 0
502
+ end
503
+
504
+ it "should work with dealiased keys" do
505
+ @obj = @dealiased_keys_class.new(:foo => "foo")
506
+ @obj.save!
507
+ @obj.reload
508
+ @obj.foo.should == "foo"
509
+
510
+ @obj.foo = "bar"
511
+ @obj.save!
512
+ @obj.reload
513
+ @obj.foo.should == "bar"
514
+ end
515
+
516
+ it "should be able to nullify one associations through re-assignment" do
517
+ @post = @post_class.create
518
+ @author = @author_class.new
519
+ @post.author = @author
520
+
521
+ @author.reload
522
+ @author.authorable_id.should == @post.id
523
+
524
+ @post.author = @author_class.new
525
+
526
+ @author.reload
527
+ @author.authorable_id.should be_nil
528
+ end
529
+ end
530
+ end