ghost_dm-core 1.3.0.beta

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 (254) hide show
  1. data/.autotest +29 -0
  2. data/.document +5 -0
  3. data/.gitignore +35 -0
  4. data/.yardopts +1 -0
  5. data/Gemfile +65 -0
  6. data/LICENSE +20 -0
  7. data/README.md +269 -0
  8. data/Rakefile +4 -0
  9. data/dm-core.gemspec +24 -0
  10. data/lib/dm-core.rb +292 -0
  11. data/lib/dm-core/adapters.rb +222 -0
  12. data/lib/dm-core/adapters/abstract_adapter.rb +237 -0
  13. data/lib/dm-core/adapters/in_memory_adapter.rb +113 -0
  14. data/lib/dm-core/associations/many_to_many.rb +499 -0
  15. data/lib/dm-core/associations/many_to_one.rb +290 -0
  16. data/lib/dm-core/associations/one_to_many.rb +348 -0
  17. data/lib/dm-core/associations/one_to_one.rb +86 -0
  18. data/lib/dm-core/associations/relationship.rb +663 -0
  19. data/lib/dm-core/backwards.rb +13 -0
  20. data/lib/dm-core/collection.rb +1515 -0
  21. data/lib/dm-core/core_ext/kernel.rb +23 -0
  22. data/lib/dm-core/core_ext/pathname.rb +6 -0
  23. data/lib/dm-core/core_ext/symbol.rb +10 -0
  24. data/lib/dm-core/identity_map.rb +7 -0
  25. data/lib/dm-core/model.rb +874 -0
  26. data/lib/dm-core/model/hook.rb +103 -0
  27. data/lib/dm-core/model/is.rb +32 -0
  28. data/lib/dm-core/model/property.rb +249 -0
  29. data/lib/dm-core/model/relationship.rb +378 -0
  30. data/lib/dm-core/model/scope.rb +89 -0
  31. data/lib/dm-core/property.rb +866 -0
  32. data/lib/dm-core/property/binary.rb +21 -0
  33. data/lib/dm-core/property/boolean.rb +20 -0
  34. data/lib/dm-core/property/class.rb +17 -0
  35. data/lib/dm-core/property/date.rb +10 -0
  36. data/lib/dm-core/property/date_time.rb +10 -0
  37. data/lib/dm-core/property/decimal.rb +36 -0
  38. data/lib/dm-core/property/discriminator.rb +44 -0
  39. data/lib/dm-core/property/float.rb +16 -0
  40. data/lib/dm-core/property/integer.rb +22 -0
  41. data/lib/dm-core/property/invalid_value_error.rb +22 -0
  42. data/lib/dm-core/property/lookup.rb +27 -0
  43. data/lib/dm-core/property/numeric.rb +38 -0
  44. data/lib/dm-core/property/object.rb +34 -0
  45. data/lib/dm-core/property/serial.rb +14 -0
  46. data/lib/dm-core/property/string.rb +38 -0
  47. data/lib/dm-core/property/text.rb +9 -0
  48. data/lib/dm-core/property/time.rb +10 -0
  49. data/lib/dm-core/property_set.rb +177 -0
  50. data/lib/dm-core/query.rb +1366 -0
  51. data/lib/dm-core/query/conditions/comparison.rb +911 -0
  52. data/lib/dm-core/query/conditions/operation.rb +721 -0
  53. data/lib/dm-core/query/direction.rb +36 -0
  54. data/lib/dm-core/query/operator.rb +35 -0
  55. data/lib/dm-core/query/path.rb +114 -0
  56. data/lib/dm-core/query/sort.rb +39 -0
  57. data/lib/dm-core/relationship_set.rb +72 -0
  58. data/lib/dm-core/repository.rb +226 -0
  59. data/lib/dm-core/resource.rb +1214 -0
  60. data/lib/dm-core/resource/persistence_state.rb +75 -0
  61. data/lib/dm-core/resource/persistence_state/clean.rb +40 -0
  62. data/lib/dm-core/resource/persistence_state/deleted.rb +30 -0
  63. data/lib/dm-core/resource/persistence_state/dirty.rb +96 -0
  64. data/lib/dm-core/resource/persistence_state/immutable.rb +34 -0
  65. data/lib/dm-core/resource/persistence_state/persisted.rb +29 -0
  66. data/lib/dm-core/resource/persistence_state/transient.rb +80 -0
  67. data/lib/dm-core/spec/lib/adapter_helpers.rb +64 -0
  68. data/lib/dm-core/spec/lib/collection_helpers.rb +21 -0
  69. data/lib/dm-core/spec/lib/counter_adapter.rb +38 -0
  70. data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
  71. data/lib/dm-core/spec/lib/spec_helper.rb +74 -0
  72. data/lib/dm-core/spec/setup.rb +174 -0
  73. data/lib/dm-core/spec/shared/adapter_spec.rb +341 -0
  74. data/lib/dm-core/spec/shared/public/property_spec.rb +229 -0
  75. data/lib/dm-core/spec/shared/resource_spec.rb +1232 -0
  76. data/lib/dm-core/spec/shared/sel_spec.rb +111 -0
  77. data/lib/dm-core/spec/shared/semipublic/property_spec.rb +176 -0
  78. data/lib/dm-core/spec/shared/semipublic/query/conditions/abstract_comparison_spec.rb +261 -0
  79. data/lib/dm-core/support/assertions.rb +8 -0
  80. data/lib/dm-core/support/chainable.rb +18 -0
  81. data/lib/dm-core/support/deprecate.rb +12 -0
  82. data/lib/dm-core/support/descendant_set.rb +89 -0
  83. data/lib/dm-core/support/equalizer.rb +48 -0
  84. data/lib/dm-core/support/ext/array.rb +22 -0
  85. data/lib/dm-core/support/ext/blank.rb +25 -0
  86. data/lib/dm-core/support/ext/hash.rb +67 -0
  87. data/lib/dm-core/support/ext/module.rb +47 -0
  88. data/lib/dm-core/support/ext/object.rb +57 -0
  89. data/lib/dm-core/support/ext/string.rb +24 -0
  90. data/lib/dm-core/support/ext/try_dup.rb +12 -0
  91. data/lib/dm-core/support/hook.rb +405 -0
  92. data/lib/dm-core/support/inflections.rb +60 -0
  93. data/lib/dm-core/support/inflector/inflections.rb +211 -0
  94. data/lib/dm-core/support/inflector/methods.rb +151 -0
  95. data/lib/dm-core/support/lazy_array.rb +451 -0
  96. data/lib/dm-core/support/local_object_space.rb +13 -0
  97. data/lib/dm-core/support/logger.rb +201 -0
  98. data/lib/dm-core/support/mash.rb +176 -0
  99. data/lib/dm-core/support/naming_conventions.rb +90 -0
  100. data/lib/dm-core/support/ordered_set.rb +380 -0
  101. data/lib/dm-core/support/subject.rb +33 -0
  102. data/lib/dm-core/support/subject_set.rb +250 -0
  103. data/lib/dm-core/version.rb +3 -0
  104. data/script/performance.rb +275 -0
  105. data/script/profile.rb +218 -0
  106. data/spec/lib/rspec_immediate_feedback_formatter.rb +54 -0
  107. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +68 -0
  108. data/spec/public/associations/many_to_many_spec.rb +197 -0
  109. data/spec/public/associations/many_to_one_spec.rb +83 -0
  110. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +40 -0
  111. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  112. data/spec/public/associations/one_to_many_spec.rb +81 -0
  113. data/spec/public/associations/one_to_one_spec.rb +176 -0
  114. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +46 -0
  115. data/spec/public/collection_spec.rb +69 -0
  116. data/spec/public/finalize_spec.rb +76 -0
  117. data/spec/public/model/hook_spec.rb +246 -0
  118. data/spec/public/model/property_spec.rb +88 -0
  119. data/spec/public/model/relationship_spec.rb +1040 -0
  120. data/spec/public/model_spec.rb +462 -0
  121. data/spec/public/property/binary_spec.rb +41 -0
  122. data/spec/public/property/boolean_spec.rb +22 -0
  123. data/spec/public/property/class_spec.rb +28 -0
  124. data/spec/public/property/date_spec.rb +22 -0
  125. data/spec/public/property/date_time_spec.rb +22 -0
  126. data/spec/public/property/decimal_spec.rb +23 -0
  127. data/spec/public/property/discriminator_spec.rb +135 -0
  128. data/spec/public/property/float_spec.rb +22 -0
  129. data/spec/public/property/integer_spec.rb +22 -0
  130. data/spec/public/property/object_spec.rb +107 -0
  131. data/spec/public/property/serial_spec.rb +22 -0
  132. data/spec/public/property/string_spec.rb +22 -0
  133. data/spec/public/property/text_spec.rb +63 -0
  134. data/spec/public/property/time_spec.rb +22 -0
  135. data/spec/public/property_spec.rb +341 -0
  136. data/spec/public/resource_spec.rb +288 -0
  137. data/spec/public/sel_spec.rb +53 -0
  138. data/spec/public/setup_spec.rb +145 -0
  139. data/spec/public/shared/association_collection_shared_spec.rb +309 -0
  140. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  141. data/spec/public/shared/collection_shared_spec.rb +1667 -0
  142. data/spec/public/shared/finder_shared_spec.rb +1629 -0
  143. data/spec/rcov.opts +6 -0
  144. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  145. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +13 -0
  146. data/spec/semipublic/associations/many_to_many_spec.rb +94 -0
  147. data/spec/semipublic/associations/many_to_one_spec.rb +63 -0
  148. data/spec/semipublic/associations/one_to_many_spec.rb +55 -0
  149. data/spec/semipublic/associations/one_to_one_spec.rb +53 -0
  150. data/spec/semipublic/associations/relationship_spec.rb +200 -0
  151. data/spec/semipublic/associations_spec.rb +177 -0
  152. data/spec/semipublic/collection_spec.rb +110 -0
  153. data/spec/semipublic/model_spec.rb +96 -0
  154. data/spec/semipublic/property/binary_spec.rb +13 -0
  155. data/spec/semipublic/property/boolean_spec.rb +47 -0
  156. data/spec/semipublic/property/class_spec.rb +33 -0
  157. data/spec/semipublic/property/date_spec.rb +43 -0
  158. data/spec/semipublic/property/date_time_spec.rb +46 -0
  159. data/spec/semipublic/property/decimal_spec.rb +83 -0
  160. data/spec/semipublic/property/discriminator_spec.rb +19 -0
  161. data/spec/semipublic/property/float_spec.rb +82 -0
  162. data/spec/semipublic/property/integer_spec.rb +82 -0
  163. data/spec/semipublic/property/lookup_spec.rb +29 -0
  164. data/spec/semipublic/property/serial_spec.rb +13 -0
  165. data/spec/semipublic/property/string_spec.rb +13 -0
  166. data/spec/semipublic/property/text_spec.rb +31 -0
  167. data/spec/semipublic/property/time_spec.rb +50 -0
  168. data/spec/semipublic/property_spec.rb +114 -0
  169. data/spec/semipublic/query/conditions/comparison_spec.rb +1501 -0
  170. data/spec/semipublic/query/conditions/operation_spec.rb +1294 -0
  171. data/spec/semipublic/query/path_spec.rb +471 -0
  172. data/spec/semipublic/query_spec.rb +3682 -0
  173. data/spec/semipublic/resource/state/clean_spec.rb +88 -0
  174. data/spec/semipublic/resource/state/deleted_spec.rb +78 -0
  175. data/spec/semipublic/resource/state/dirty_spec.rb +162 -0
  176. data/spec/semipublic/resource/state/immutable_spec.rb +105 -0
  177. data/spec/semipublic/resource/state/transient_spec.rb +162 -0
  178. data/spec/semipublic/resource/state_spec.rb +230 -0
  179. data/spec/semipublic/resource_spec.rb +23 -0
  180. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  181. data/spec/semipublic/shared/resource_shared_spec.rb +199 -0
  182. data/spec/semipublic/shared/resource_state_shared_spec.rb +79 -0
  183. data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
  184. data/spec/spec.opts +5 -0
  185. data/spec/spec_helper.rb +38 -0
  186. data/spec/support/core_ext/hash.rb +10 -0
  187. data/spec/support/core_ext/inheritable_attributes.rb +46 -0
  188. data/spec/support/properties/huge_integer.rb +17 -0
  189. data/spec/unit/array_spec.rb +23 -0
  190. data/spec/unit/blank_spec.rb +73 -0
  191. data/spec/unit/data_mapper/ordered_set/append_spec.rb +26 -0
  192. data/spec/unit/data_mapper/ordered_set/clear_spec.rb +24 -0
  193. data/spec/unit/data_mapper/ordered_set/delete_spec.rb +28 -0
  194. data/spec/unit/data_mapper/ordered_set/each_spec.rb +19 -0
  195. data/spec/unit/data_mapper/ordered_set/empty_spec.rb +20 -0
  196. data/spec/unit/data_mapper/ordered_set/entries_spec.rb +22 -0
  197. data/spec/unit/data_mapper/ordered_set/eql_spec.rb +51 -0
  198. data/spec/unit/data_mapper/ordered_set/equal_value_spec.rb +84 -0
  199. data/spec/unit/data_mapper/ordered_set/hash_spec.rb +12 -0
  200. data/spec/unit/data_mapper/ordered_set/include_spec.rb +23 -0
  201. data/spec/unit/data_mapper/ordered_set/index_spec.rb +28 -0
  202. data/spec/unit/data_mapper/ordered_set/initialize_spec.rb +32 -0
  203. data/spec/unit/data_mapper/ordered_set/merge_spec.rb +36 -0
  204. data/spec/unit/data_mapper/ordered_set/shared/append_spec.rb +24 -0
  205. data/spec/unit/data_mapper/ordered_set/shared/clear_spec.rb +9 -0
  206. data/spec/unit/data_mapper/ordered_set/shared/delete_spec.rb +25 -0
  207. data/spec/unit/data_mapper/ordered_set/shared/each_spec.rb +17 -0
  208. data/spec/unit/data_mapper/ordered_set/shared/empty_spec.rb +9 -0
  209. data/spec/unit/data_mapper/ordered_set/shared/entries_spec.rb +9 -0
  210. data/spec/unit/data_mapper/ordered_set/shared/include_spec.rb +9 -0
  211. data/spec/unit/data_mapper/ordered_set/shared/index_spec.rb +13 -0
  212. data/spec/unit/data_mapper/ordered_set/shared/initialize_spec.rb +28 -0
  213. data/spec/unit/data_mapper/ordered_set/shared/merge_spec.rb +28 -0
  214. data/spec/unit/data_mapper/ordered_set/shared/size_spec.rb +13 -0
  215. data/spec/unit/data_mapper/ordered_set/shared/to_ary_spec.rb +11 -0
  216. data/spec/unit/data_mapper/ordered_set/size_spec.rb +27 -0
  217. data/spec/unit/data_mapper/ordered_set/to_ary_spec.rb +23 -0
  218. data/spec/unit/data_mapper/subject_set/append_spec.rb +47 -0
  219. data/spec/unit/data_mapper/subject_set/clear_spec.rb +34 -0
  220. data/spec/unit/data_mapper/subject_set/delete_spec.rb +40 -0
  221. data/spec/unit/data_mapper/subject_set/each_spec.rb +30 -0
  222. data/spec/unit/data_mapper/subject_set/empty_spec.rb +31 -0
  223. data/spec/unit/data_mapper/subject_set/entries_spec.rb +31 -0
  224. data/spec/unit/data_mapper/subject_set/get_spec.rb +34 -0
  225. data/spec/unit/data_mapper/subject_set/include_spec.rb +32 -0
  226. data/spec/unit/data_mapper/subject_set/named_spec.rb +33 -0
  227. data/spec/unit/data_mapper/subject_set/shared/append_spec.rb +18 -0
  228. data/spec/unit/data_mapper/subject_set/shared/clear_spec.rb +9 -0
  229. data/spec/unit/data_mapper/subject_set/shared/delete_spec.rb +9 -0
  230. data/spec/unit/data_mapper/subject_set/shared/each_spec.rb +9 -0
  231. data/spec/unit/data_mapper/subject_set/shared/empty_spec.rb +9 -0
  232. data/spec/unit/data_mapper/subject_set/shared/entries_spec.rb +9 -0
  233. data/spec/unit/data_mapper/subject_set/shared/get_spec.rb +9 -0
  234. data/spec/unit/data_mapper/subject_set/shared/include_spec.rb +9 -0
  235. data/spec/unit/data_mapper/subject_set/shared/named_spec.rb +9 -0
  236. data/spec/unit/data_mapper/subject_set/shared/size_spec.rb +13 -0
  237. data/spec/unit/data_mapper/subject_set/shared/to_ary_spec.rb +9 -0
  238. data/spec/unit/data_mapper/subject_set/shared/values_at_spec.rb +44 -0
  239. data/spec/unit/data_mapper/subject_set/size_spec.rb +42 -0
  240. data/spec/unit/data_mapper/subject_set/to_ary_spec.rb +34 -0
  241. data/spec/unit/data_mapper/subject_set/values_at_spec.rb +57 -0
  242. data/spec/unit/hash_spec.rb +28 -0
  243. data/spec/unit/hook_spec.rb +1235 -0
  244. data/spec/unit/inflections_spec.rb +16 -0
  245. data/spec/unit/lazy_array_spec.rb +1949 -0
  246. data/spec/unit/mash_spec.rb +312 -0
  247. data/spec/unit/module_spec.rb +71 -0
  248. data/spec/unit/object_spec.rb +38 -0
  249. data/spec/unit/try_dup_spec.rb +46 -0
  250. data/tasks/ci.rake +1 -0
  251. data/tasks/spec.rake +38 -0
  252. data/tasks/yard.rake +9 -0
  253. data/tasks/yardstick.rake +19 -0
  254. metadata +365 -0
@@ -0,0 +1,3682 @@
1
+ require 'spec_helper'
2
+
3
+ require 'ostruct'
4
+
5
+ # TODO: make some of specs for Query.new shared. the assertions and
6
+ # normalizations should happen for Query#update, Query#relative and
7
+ # Query#merge and should probably be in shared specs
8
+
9
+ # class methods
10
+ describe DataMapper::Query do
11
+ before :all do
12
+ class ::Password < DataMapper::Property::String
13
+ length 40
14
+ end
15
+
16
+ class ::User
17
+ include DataMapper::Resource
18
+
19
+ property :name, String, :key => true
20
+ property :password, Password
21
+ property :balance, Decimal, :precision => 5, :scale => 2
22
+
23
+ belongs_to :referrer, self, :required => false
24
+ has n, :referrals, self, :inverse => :referrer
25
+ end
26
+
27
+ @repository = DataMapper::Repository.new(:default)
28
+ @model = User
29
+
30
+ @fields = [ :name ].freeze
31
+ @links = [ :referrer ].freeze
32
+ @conditions = { :name => 'Dan Kubb' }
33
+ @offset = 0
34
+ @limit = 1
35
+ @order = [ :name ].freeze
36
+ @unique = false
37
+ @add_reversed = false
38
+ @reload = false
39
+
40
+ @options = {
41
+ :fields => @fields,
42
+ :links => @links,
43
+ :conditions => @conditions,
44
+ :offset => @offset,
45
+ :limit => @limit,
46
+ :order => @order,
47
+ :unique => @unique,
48
+ :add_reversed => @add_reversed,
49
+ :reload => @reload,
50
+ }
51
+ end
52
+
53
+ it { DataMapper::Query.should respond_to(:new) }
54
+
55
+ describe '.new' do
56
+ describe 'with a repository' do
57
+ describe 'that is valid' do
58
+ before :all do
59
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
60
+ end
61
+
62
+ it { @return.should be_kind_of(DataMapper::Query) }
63
+
64
+ it 'should set the repository' do
65
+ @return.repository.should == @repository
66
+ end
67
+ end
68
+
69
+ describe 'that is invalid' do
70
+ it 'should raise an exception' do
71
+ lambda {
72
+ DataMapper::Query.new('invalid', @model, @options)
73
+ }.should raise_error(ArgumentError, '+repository+ should be DataMapper::Repository, but was String')
74
+ end
75
+ end
76
+ end
77
+
78
+ describe 'with a model' do
79
+ describe 'that is valid' do
80
+ before :all do
81
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
82
+ end
83
+
84
+ it { @return.should be_kind_of(DataMapper::Query) }
85
+
86
+ it 'should set the model' do
87
+ @return.model.should == @model
88
+ end
89
+ end
90
+
91
+ describe 'that is invalid' do
92
+ it 'should raise an exception' do
93
+ lambda {
94
+ DataMapper::Query.new(@repository, 'invalid', @options)
95
+ }.should raise_error(ArgumentError, '+model+ should be DataMapper::Model, but was String')
96
+ end
97
+ end
98
+ end
99
+
100
+ describe 'with a fields option' do
101
+ describe 'that is an Array containing a Symbol' do
102
+ before :all do
103
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
104
+ end
105
+
106
+ it { @return.should be_kind_of(DataMapper::Query) }
107
+
108
+ it 'should set the fields' do
109
+ @return.fields.should == @model.properties.values_at(*@fields)
110
+ end
111
+ end
112
+
113
+ describe 'that is an Array containing a String' do
114
+ before :all do
115
+ @options[:fields] = [ 'name' ]
116
+
117
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
118
+ end
119
+
120
+ it { @return.should be_kind_of(DataMapper::Query) }
121
+
122
+ it 'should set the fields' do
123
+ @return.fields.should == @model.properties.values_at('name')
124
+ end
125
+ end
126
+
127
+ describe 'that is an Array containing a Property' do
128
+ before :all do
129
+ @options[:fields] = @model.properties.values_at(:name)
130
+
131
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
132
+ end
133
+
134
+ it { @return.should be_kind_of(DataMapper::Query) }
135
+
136
+ it 'should set the fields' do
137
+ @return.fields.should == @model.properties.values_at(:name)
138
+ end
139
+ end
140
+
141
+ describe 'that is an Array containing a Property from an ancestor' do
142
+ before :all do
143
+ class ::Contact < User; end
144
+
145
+ @options[:fields] = User.properties.values_at(:name)
146
+
147
+ @return = DataMapper::Query.new(@repository, Contact, @options.freeze)
148
+ end
149
+
150
+ it { @return.should be_kind_of(DataMapper::Query) }
151
+
152
+ it 'should set the fields' do
153
+ @return.fields.should == User.properties.values_at(:name)
154
+ end
155
+ end
156
+
157
+ describe 'that is missing' do
158
+ before :all do
159
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:fields).freeze)
160
+ end
161
+
162
+ it { @return.should be_kind_of(DataMapper::Query) }
163
+
164
+ it 'should set fields to the model default properties' do
165
+ @return.fields.should == @model.properties.defaults
166
+ end
167
+ end
168
+
169
+ describe 'that is invalid' do
170
+ it 'should raise an exception' do
171
+ lambda {
172
+ DataMapper::Query.new(@repository, @model, @options.update(:fields => :name))
173
+ }.should raise_error(StandardError)
174
+ end
175
+ end
176
+
177
+ describe 'that is an Array containing an unknown Symbol' do
178
+ it 'should raise an exception' do
179
+ lambda {
180
+ DataMapper::Query.new(@repository, @model, @options.update(:fields => [ :unknown ]))
181
+ }.should raise_error(ArgumentError, "+options[:fields]+ entry :unknown does not map to a property in #{@model}")
182
+ end
183
+ end
184
+
185
+ describe 'that is an Array containing an unknown String' do
186
+ it 'should raise an exception' do
187
+ lambda {
188
+ DataMapper::Query.new(@repository, @model, @options.update(:fields => [ 'unknown' ]))
189
+ }.should raise_error(ArgumentError, "+options[:fields]+ entry \"unknown\" does not map to a property in #{@model}")
190
+ end
191
+ end
192
+ end
193
+
194
+ describe 'with a links option' do
195
+ describe 'that is an Array containing a Symbol' do
196
+ before :all do
197
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
198
+ end
199
+
200
+ it { @return.should be_kind_of(DataMapper::Query) }
201
+
202
+ it 'should set the links' do
203
+ @return.links.should == @model.relationships.values_at(*@links)
204
+ end
205
+ end
206
+
207
+ describe 'that is an Array containing a String' do
208
+ before :all do
209
+ @options[:links] = [ 'referrer' ]
210
+
211
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
212
+ end
213
+
214
+ it { @return.should be_kind_of(DataMapper::Query) }
215
+
216
+ it 'should set the links' do
217
+ @return.links.should == @model.relationships.values_at('referrer')
218
+ end
219
+ end
220
+
221
+ describe 'that is an Array containing a Relationship' do
222
+ before :all do
223
+ @options[:links] = @model.relationships.values_at(:referrer)
224
+
225
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
226
+ end
227
+
228
+ it { @return.should be_kind_of(DataMapper::Query) }
229
+
230
+ it 'should set the links' do
231
+ @return.links.should == @model.relationships.values_at(:referrer)
232
+ end
233
+ end
234
+
235
+ describe 'that is missing' do
236
+ before :all do
237
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:links).freeze)
238
+ end
239
+
240
+ it { @return.should be_kind_of(DataMapper::Query) }
241
+
242
+ it 'should set links to an empty Array' do
243
+ @return.links.should == []
244
+ end
245
+ end
246
+
247
+ describe 'that is invalid' do
248
+ it 'should raise an exception' do
249
+ lambda {
250
+ DataMapper::Query.new(@repository, @model, @options.update(:links => :referral))
251
+ }.should raise_error(StandardError)
252
+ end
253
+ end
254
+
255
+ describe 'that is an empty Array' do
256
+ it 'should raise an exception' do
257
+ lambda {
258
+ DataMapper::Query.new(@repository, @model, @options.update(:links => []))
259
+ }.should raise_error(ArgumentError, '+options[:links]+ should not be empty')
260
+ end
261
+ end
262
+
263
+ describe 'that is an Array containing an unknown Symbol' do
264
+ it 'should raise an exception' do
265
+ lambda {
266
+ DataMapper::Query.new(@repository, @model, @options.update(:links => [ :unknown ]))
267
+ }.should raise_error(ArgumentError, "+options[:links]+ entry :unknown does not map to a relationship in #{@model}")
268
+ end
269
+ end
270
+
271
+ describe 'that is an Array containing an unknown String' do
272
+ it 'should raise an exception' do
273
+ lambda {
274
+ DataMapper::Query.new(@repository, @model, @options.update(:links => [ 'unknown' ]))
275
+ }.should raise_error(ArgumentError, "+options[:links]+ entry \"unknown\" does not map to a relationship in #{@model}")
276
+ end
277
+ end
278
+ end
279
+
280
+ describe 'with a conditions option' do
281
+ describe 'that is a valid Hash' do
282
+ describe 'with the Property key' do
283
+ before :all do
284
+ @options[:conditions] = { @model.properties[:name] => 'Dan Kubb' }
285
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
286
+ end
287
+
288
+ it { @return.should be_kind_of(DataMapper::Query) }
289
+
290
+ it 'should set the conditions' do
291
+ @return.conditions.should ==
292
+ DataMapper::Query::Conditions::Operation.new(
293
+ :and,
294
+ DataMapper::Query::Conditions::Comparison.new(
295
+ :eql,
296
+ @model.properties[:name],
297
+ 'Dan Kubb'
298
+ )
299
+ )
300
+ end
301
+
302
+ it 'should be valid' do
303
+ @return.should be_valid
304
+ end
305
+ end
306
+
307
+ describe 'with the Symbol key mapping to a Property' do
308
+ before :all do
309
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
310
+ end
311
+
312
+ it { @return.should be_kind_of(DataMapper::Query) }
313
+
314
+ it 'should set the conditions' do
315
+ @return.conditions.should ==
316
+ DataMapper::Query::Conditions::Operation.new(
317
+ :and,
318
+ DataMapper::Query::Conditions::Comparison.new(
319
+ :eql,
320
+ @model.properties[:name],
321
+ 'Dan Kubb'
322
+ )
323
+ )
324
+ end
325
+
326
+ it 'should be valid' do
327
+ @return.should be_valid
328
+ end
329
+ end
330
+
331
+ describe 'with the String key mapping to a Property' do
332
+ before :all do
333
+ @options[:conditions] = { 'name' => 'Dan Kubb' }
334
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
335
+ end
336
+
337
+ it { @return.should be_kind_of(DataMapper::Query) }
338
+
339
+ it 'should set the conditions' do
340
+ @return.conditions.should ==
341
+ DataMapper::Query::Conditions::Operation.new(
342
+ :and,
343
+ DataMapper::Query::Conditions::Comparison.new(
344
+ :eql,
345
+ @model.properties[:name],
346
+ 'Dan Kubb'
347
+ )
348
+ )
349
+ end
350
+
351
+ it 'should be valid' do
352
+ @return.should be_valid
353
+ end
354
+ end
355
+
356
+ supported_by :all do
357
+ describe 'with the Symbol key mapping to a Relationship' do
358
+ before :all do
359
+ @user = @model.create(:name => 'Dan Kubb')
360
+
361
+ @options[:conditions] = { :referrer => @user }
362
+
363
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
364
+ end
365
+
366
+ it { @return.should be_kind_of(DataMapper::Query) }
367
+
368
+ it 'should set the conditions' do
369
+ @return.conditions.should ==
370
+ DataMapper::Query::Conditions::Operation.new(
371
+ :and,
372
+ DataMapper::Query::Conditions::Comparison.new(
373
+ :eql,
374
+ @model.relationships[:referrer],
375
+ @user
376
+ )
377
+ )
378
+ end
379
+
380
+ it 'should be valid' do
381
+ @return.should be_valid
382
+ end
383
+ end
384
+
385
+ describe 'with the String key mapping to a Relationship' do
386
+ before :all do
387
+ @user = @model.create(:name => 'Dan Kubb')
388
+
389
+ @options[:conditions] = { 'referrer' => @user }
390
+
391
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
392
+ end
393
+
394
+ it { @return.should be_kind_of(DataMapper::Query) }
395
+
396
+ it 'should set the conditions' do
397
+ @return.conditions.should ==
398
+ DataMapper::Query::Conditions::Operation.new(
399
+ :and,
400
+ DataMapper::Query::Conditions::Comparison.new(
401
+ :eql,
402
+ @model.relationships['referrer'],
403
+ @user
404
+ )
405
+ )
406
+ end
407
+
408
+ it 'should be valid' do
409
+ @return.should be_valid
410
+ end
411
+ end
412
+
413
+ describe 'with the Symbol key mapping to a Relationship and a nil value' do
414
+ before :all do
415
+ @options[:conditions] = { :referrer => nil }
416
+
417
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
418
+ end
419
+
420
+ it { @return.should be_kind_of(DataMapper::Query) }
421
+
422
+ it 'should set the conditions' do
423
+ @return.conditions.should ==
424
+ DataMapper::Query::Conditions::Operation.new(
425
+ :and,
426
+ DataMapper::Query::Conditions::Comparison.new(
427
+ :eql,
428
+ @model.relationships[:referrer],
429
+ nil
430
+ )
431
+ )
432
+ end
433
+
434
+ it 'should be valid' do
435
+ @return.should be_valid
436
+ end
437
+ end
438
+
439
+ describe 'with the Symbol key mapping to a Relationship and an empty Array' do
440
+ before :all do
441
+ @options[:conditions] = { :referrer => [] }
442
+
443
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
444
+ end
445
+
446
+ it { @return.should be_kind_of(DataMapper::Query) }
447
+
448
+ it 'should set the conditions' do
449
+ @return.conditions.should ==
450
+ DataMapper::Query::Conditions::Operation.new(
451
+ :and,
452
+ DataMapper::Query::Conditions::Comparison.new(
453
+ :in,
454
+ @model.relationships[:referrer],
455
+ []
456
+ )
457
+ )
458
+ end
459
+
460
+ it 'should be invalid' do
461
+ @return.should_not be_valid
462
+ end
463
+ end
464
+ end
465
+
466
+ describe 'with the Query::Operator key' do
467
+ before :all do
468
+ @options[:conditions] = { :name.gte => 'Dan Kubb' }
469
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
470
+ end
471
+
472
+ it { @return.should be_kind_of(DataMapper::Query) }
473
+
474
+ it 'should set the conditions' do
475
+ @return.conditions.should ==
476
+ DataMapper::Query::Conditions::Operation.new(
477
+ :and,
478
+ DataMapper::Query::Conditions::Comparison.new(
479
+ :gte,
480
+ @model.properties[:name],
481
+ 'Dan Kubb'
482
+ )
483
+ )
484
+ end
485
+
486
+ it 'should be valid' do
487
+ @return.should be_valid
488
+ end
489
+ end
490
+
491
+ describe 'with the Query::Path key' do
492
+ before :all do
493
+ @options[:conditions] = { @model.referrer.name => 'Dan Kubb' }
494
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
495
+ end
496
+
497
+ it { @return.should be_kind_of(DataMapper::Query) }
498
+
499
+ it 'should not set the conditions' do
500
+ pending do
501
+ @return.conditions.should be_nil
502
+ end
503
+ end
504
+
505
+ it 'should set the links' do
506
+ @return.links.should == [ @model.relationships[:referrals], @model.relationships[:referrer] ]
507
+ end
508
+
509
+ it 'should be valid' do
510
+ @return.should be_valid
511
+ end
512
+ end
513
+
514
+ describe 'with the String key mapping to a Query::Path' do
515
+ before :all do
516
+ @options[:conditions] = { 'referrer.name' => 'Dan Kubb' }
517
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
518
+ end
519
+
520
+ it { @return.should be_kind_of(DataMapper::Query) }
521
+
522
+ it 'should not set the conditions' do
523
+ pending do
524
+ @return.conditions.should be_nil
525
+ end
526
+ end
527
+
528
+ it 'should set the links' do
529
+ @return.links.should == [ @model.relationships[:referrals], @model.relationships[:referrer] ]
530
+ end
531
+
532
+ it 'should be valid' do
533
+ @return.should be_valid
534
+ end
535
+ end
536
+
537
+ describe 'with an Array with 1 entry' do
538
+ before :all do
539
+ @options[:conditions] = { :name => [ 'Dan Kubb' ] }
540
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
541
+ end
542
+
543
+ it { @return.should be_kind_of(DataMapper::Query) }
544
+
545
+ it 'should set the conditions' do
546
+ pending do
547
+ @return.conditions.should ==
548
+ DataMapper::Query::Conditions::Operation.new(
549
+ :and,
550
+ DataMapper::Query::Conditions::Comparison.new(
551
+ :eql,
552
+ @model.properties[:name],
553
+ 'Dan Kubb'
554
+ )
555
+ )
556
+ end
557
+ end
558
+
559
+ it 'should be valid' do
560
+ @return.should be_valid
561
+ end
562
+ end
563
+
564
+ describe 'with an Array with no entries' do
565
+ before :all do
566
+ @options[:conditions] = { :name => [] }
567
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
568
+ end
569
+
570
+ it { @return.should be_kind_of(DataMapper::Query) }
571
+
572
+ it 'should set the conditions' do
573
+ pending do
574
+ @return.conditions.should ==
575
+ DataMapper::Query::Conditions::Operation.new(
576
+ :and,
577
+ DataMapper::Query::Conditions::Comparison.new(
578
+ :eql,
579
+ @model.properties[:name],
580
+ 'Dan Kubb'
581
+ )
582
+ )
583
+ end
584
+ end
585
+
586
+ it 'should not be valid' do
587
+ @return.should_not be_valid
588
+ end
589
+ end
590
+
591
+ describe 'with an Array with duplicate entries' do
592
+ before :all do
593
+ @options[:conditions] = { :name => [ 'John Doe', 'Dan Kubb', 'John Doe' ] }
594
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
595
+ end
596
+
597
+ it { @return.should be_kind_of(DataMapper::Query) }
598
+
599
+ it 'should set the conditions' do
600
+ @return.conditions.should ==
601
+ DataMapper::Query::Conditions::Operation.new(
602
+ :and,
603
+ DataMapper::Query::Conditions::Comparison.new(
604
+ :in,
605
+ @model.properties[:name],
606
+ [ 'John Doe', 'Dan Kubb' ]
607
+ )
608
+ )
609
+ end
610
+
611
+ it 'should be valid' do
612
+ @return.should be_valid
613
+ end
614
+ end
615
+
616
+ describe 'with a Property subclass' do
617
+ before :all do
618
+ @options[:conditions] = { :password => 'password' }
619
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
620
+ end
621
+
622
+ it { @return.should be_kind_of(DataMapper::Query) }
623
+
624
+ it 'should set the conditions' do
625
+ @return.conditions.should ==
626
+ DataMapper::Query::Conditions::Operation.new(
627
+ :and,
628
+ DataMapper::Query::Conditions::Comparison.new(
629
+ :eql,
630
+ @model.properties[:password],
631
+ 'password'
632
+ )
633
+ )
634
+ end
635
+
636
+ it 'should be valid' do
637
+ @return.should be_valid
638
+ end
639
+ end
640
+
641
+ describe 'with a Symbol for a String property' do
642
+ before :all do
643
+ @options[:conditions] = { :name => 'Dan Kubb'.to_sym }
644
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
645
+ end
646
+
647
+ it { @return.should be_kind_of(DataMapper::Query) }
648
+
649
+ it 'should set the conditions' do
650
+ @return.conditions.should ==
651
+ DataMapper::Query::Conditions::Operation.new(
652
+ :and,
653
+ DataMapper::Query::Conditions::Comparison.new(
654
+ :eql,
655
+ @model.properties[:name],
656
+ 'Dan Kubb' # typecast value
657
+ )
658
+ )
659
+ end
660
+
661
+ it 'should be valid' do
662
+ @return.should be_valid
663
+ end
664
+ end
665
+
666
+ describe 'with a Float for a Decimal property' do
667
+ before :all do
668
+ @options[:conditions] = { :balance => 50.5 }
669
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
670
+ end
671
+
672
+ it { @return.should be_kind_of(DataMapper::Query) }
673
+
674
+ it 'should set the conditions' do
675
+ @return.conditions.should ==
676
+ DataMapper::Query::Conditions::Operation.new(
677
+ :and,
678
+ DataMapper::Query::Conditions::Comparison.new(
679
+ :eql,
680
+ @model.properties[:balance],
681
+ BigDecimal('50.5') # typecast value
682
+ )
683
+ )
684
+ end
685
+
686
+ it 'should be valid' do
687
+ @return.should be_valid
688
+ end
689
+ end
690
+ end
691
+
692
+ describe 'that is a valid Array' do
693
+ before :all do
694
+ @options[:conditions] = [ 'name = ?', 'Dan Kubb' ]
695
+
696
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
697
+ end
698
+
699
+ it { @return.should be_kind_of(DataMapper::Query) }
700
+
701
+ it 'should set the conditions' do
702
+ @return.conditions.should == DataMapper::Query::Conditions::Operation.new(:and, [ 'name = ?', [ 'Dan Kubb' ] ])
703
+ end
704
+
705
+ it 'should be valid' do
706
+ @return.should be_valid
707
+ end
708
+ end
709
+
710
+ describe 'that is missing' do
711
+ before :all do
712
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:conditions).freeze)
713
+ end
714
+
715
+ it { @return.should be_kind_of(DataMapper::Query) }
716
+
717
+ it 'should set conditions to nil by default' do
718
+ @return.conditions.should be_nil
719
+ end
720
+
721
+ it 'should be valid' do
722
+ @return.should be_valid
723
+ end
724
+ end
725
+
726
+ describe 'that is an empty Array' do
727
+ it 'should raise an exception' do
728
+ lambda {
729
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => []))
730
+ }.should raise_error(ArgumentError, '+options[:conditions]+ should not be empty')
731
+ end
732
+ end
733
+
734
+ describe 'that is an Array with a blank statement' do
735
+ it 'should raise an exception' do
736
+ lambda {
737
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => [ ' ' ]))
738
+ }.should raise_error(ArgumentError, '+options[:conditions]+ should have a statement for the first entry')
739
+ end
740
+ end
741
+
742
+ describe 'that is a Hash with a Symbol key that is not for a Property in the model' do
743
+ it 'should raise an exception' do
744
+ lambda {
745
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => { :unknown => 1 }))
746
+ }.should raise_error(ArgumentError, "condition :unknown does not map to a property or relationship in #{@model}")
747
+ end
748
+ end
749
+
750
+ describe 'that is a Hash with a String key that is not for a Property in the model' do
751
+ it 'should raise an exception' do
752
+ lambda {
753
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => { 'unknown' => 1 }))
754
+ }.should raise_error(ArgumentError, "condition \"unknown\" does not map to a property or relationship in #{@model}")
755
+ end
756
+ end
757
+
758
+ describe 'that is a Hash with a String key that is a Path and not for a Relationship in the model' do
759
+ it 'should raise an exception' do
760
+ lambda {
761
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => { 'unknown.id' => 1 }))
762
+ }.should raise_error(ArgumentError, "condition \"unknown.id\" does not map to a property or relationship in #{@model}")
763
+ end
764
+ end
765
+ end
766
+
767
+ describe 'with an offset option' do
768
+ describe 'that is valid' do
769
+ before :all do
770
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
771
+ end
772
+
773
+ it { @return.should be_kind_of(DataMapper::Query) }
774
+
775
+ it 'should set the offset' do
776
+ @return.offset.should == @offset
777
+ end
778
+ end
779
+
780
+ describe 'that is missing' do
781
+ before :all do
782
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:offset).freeze)
783
+ end
784
+
785
+ it { @return.should be_kind_of(DataMapper::Query) }
786
+
787
+ it 'should set offset to 0' do
788
+ @return.offset.should == 0
789
+ end
790
+ end
791
+
792
+ describe 'that is invalid' do
793
+ it 'should raise an exception' do
794
+ lambda {
795
+ DataMapper::Query.new(@repository, @model, @options.update(:offset => '0'))
796
+ }.should raise_error(StandardError)
797
+ end
798
+ end
799
+
800
+ describe 'that is less than 0' do
801
+ it 'should raise an exception' do
802
+ lambda {
803
+ DataMapper::Query.new(@repository, @model, @options.update(:offset => -1))
804
+ }.should raise_error(ArgumentError, '+options[:offset]+ must be greater than or equal to 0, but was -1')
805
+ end
806
+ end
807
+
808
+ describe 'that is greater than 0 and a nil limit' do
809
+ it 'should raise an exception' do
810
+ lambda {
811
+ DataMapper::Query.new(@repository, @model, @options.except(:limit).update(:offset => 1))
812
+ }.should raise_error(ArgumentError, '+options[:offset]+ cannot be greater than 0 if limit is not specified')
813
+ end
814
+ end
815
+ end
816
+
817
+ describe 'with a limit option' do
818
+ describe 'that is valid' do
819
+ before :all do
820
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
821
+ end
822
+
823
+ it { @return.should be_kind_of(DataMapper::Query) }
824
+
825
+ it 'should set the limit' do
826
+ @return.limit.should == @limit
827
+ end
828
+ end
829
+
830
+ describe 'that is missing' do
831
+ before :all do
832
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:limit).freeze)
833
+ end
834
+
835
+ it { @return.should be_kind_of(DataMapper::Query) }
836
+
837
+ it 'should set limit to nil' do
838
+ @return.limit.should be_nil
839
+ end
840
+ end
841
+
842
+ describe 'that is invalid' do
843
+ it 'should raise an exception' do
844
+ lambda {
845
+ DataMapper::Query.new(@repository, @model, @options.update(:limit => '1'))
846
+ }.should raise_error(StandardError)
847
+ end
848
+ end
849
+
850
+ describe 'that is less than 0' do
851
+ it 'should raise an exception' do
852
+ lambda {
853
+ DataMapper::Query.new(@repository, @model, @options.update(:limit => -1))
854
+ }.should raise_error(ArgumentError, '+options[:limit]+ must be greater than or equal to 0, but was -1')
855
+ end
856
+ end
857
+ end
858
+
859
+ describe 'with an order option' do
860
+ describe 'that is a single Symbol' do
861
+ before :all do
862
+ @options[:order] = :name
863
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
864
+ end
865
+
866
+ it { @return.should be_kind_of(DataMapper::Query) }
867
+
868
+ it 'should set the order' do
869
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name]) ]
870
+ end
871
+ end
872
+
873
+ describe 'that is a single String' do
874
+ before :all do
875
+ @options[:order] = 'name'
876
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
877
+ end
878
+
879
+ it { @return.should be_kind_of(DataMapper::Query) }
880
+
881
+ it 'should set the order' do
882
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name]) ]
883
+ end
884
+ end
885
+
886
+ describe 'that is a single Property' do
887
+ before :all do
888
+ @options[:order] = @model.properties.values_at(:name)
889
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
890
+ end
891
+
892
+ it { @return.should be_kind_of(DataMapper::Query) }
893
+
894
+ it 'should set the order' do
895
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name]) ]
896
+ end
897
+ end
898
+
899
+ describe 'that contains a Query::Direction with a property that is not part of the model' do
900
+ before :all do
901
+ @property = DataMapper::Property::String.new(@model, :unknown)
902
+ @direction = DataMapper::Query::Direction.new(@property, :desc)
903
+ @return = DataMapper::Query.new(@repository, @model, @options.update(:order => [ @direction ]))
904
+ end
905
+
906
+ it 'should set the order, since it may map to a joined model' do
907
+ @return.order.should == [ @direction ]
908
+ end
909
+ end
910
+
911
+ describe 'that contains a Property that is not part of the model' do
912
+ before :all do
913
+ @property = DataMapper::Property::String.new(@model, :unknown)
914
+ @return = DataMapper::Query.new(@repository, @model, @options.update(:order => [ @property ]))
915
+ end
916
+
917
+ it 'should set the order, since it may map to a joined model' do
918
+ @return.order.should == [ DataMapper::Query::Direction.new(@property) ]
919
+ end
920
+ end
921
+
922
+ describe 'that contains a Query::Path to a property on a linked model' do
923
+ before :all do
924
+ @property = @model.referrer.name
925
+ @return = DataMapper::Query.new(@repository, @model, @options.update(:order => [ @property ]))
926
+ end
927
+
928
+ it 'should set the order' do
929
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name]) ]
930
+ end
931
+ end
932
+
933
+ describe 'that is an Array containing a Symbol' do
934
+ before :all do
935
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
936
+ end
937
+
938
+ it { @return.should be_kind_of(DataMapper::Query) }
939
+
940
+ it 'should set the order' do
941
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name]) ]
942
+ end
943
+ end
944
+
945
+ describe 'that is an Array containing a String' do
946
+ before :all do
947
+ @options[:order] = [ 'name' ]
948
+
949
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
950
+ end
951
+
952
+ it { @return.should be_kind_of(DataMapper::Query) }
953
+
954
+ it 'should set the order' do
955
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name]) ]
956
+ end
957
+ end
958
+
959
+ describe 'that is an Array containing a Property' do
960
+ before :all do
961
+ @options[:order] = @model.properties.values_at(:name)
962
+
963
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
964
+ end
965
+
966
+ it { @return.should be_kind_of(DataMapper::Query) }
967
+
968
+ it 'should set the order' do
969
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name]) ]
970
+ end
971
+ end
972
+
973
+ describe 'that is an Array containing a Property from an ancestor' do
974
+ before :all do
975
+ class ::Contact < User; end
976
+
977
+ @options[:order] = User.properties.values_at(:name)
978
+
979
+ @return = DataMapper::Query.new(@repository, Contact, @options.freeze)
980
+ end
981
+
982
+ it { @return.should be_kind_of(DataMapper::Query) }
983
+
984
+ it 'should set the order' do
985
+ @return.order.should == [ DataMapper::Query::Direction.new(User.properties[:name]) ]
986
+ end
987
+ end
988
+
989
+ describe 'that is an Array containing an Operator' do
990
+ before :all do
991
+ @options[:order] = [ :name.asc ]
992
+
993
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
994
+ end
995
+
996
+ it { @return.should be_kind_of(DataMapper::Query) }
997
+
998
+ it 'should set the order' do
999
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name], :asc) ]
1000
+ end
1001
+ end
1002
+
1003
+ describe 'that is an Array containing an Query::Direction' do
1004
+ before :all do
1005
+ @options[:order] = [ DataMapper::Query::Direction.new(@model.properties[:name], :asc) ]
1006
+
1007
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
1008
+ end
1009
+
1010
+ it { @return.should be_kind_of(DataMapper::Query) }
1011
+
1012
+ it 'should set the order' do
1013
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name], :asc) ]
1014
+ end
1015
+ end
1016
+
1017
+ describe 'that is an Array containing an Query::Direction with a Property from an ancestor' do
1018
+ before :all do
1019
+ class ::Contact < User; end
1020
+
1021
+ @options[:order] = [ DataMapper::Query::Direction.new(User.properties[:name], :asc) ]
1022
+
1023
+ @return = DataMapper::Query.new(@repository, Contact, @options.freeze)
1024
+ end
1025
+
1026
+ it { @return.should be_kind_of(DataMapper::Query) }
1027
+
1028
+ it 'should set the order' do
1029
+ @return.order.should == [ DataMapper::Query::Direction.new(User.properties[:name], :asc) ]
1030
+ end
1031
+ end
1032
+
1033
+ describe 'that is missing' do
1034
+ before :all do
1035
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:order).freeze)
1036
+ end
1037
+
1038
+ it { @return.should be_kind_of(DataMapper::Query) }
1039
+
1040
+ it 'should set order to the model default order' do
1041
+ @return.order.should == @model.default_order(@repository.name)
1042
+ end
1043
+ end
1044
+
1045
+ describe 'that is invalid' do
1046
+ it 'should raise an exception' do
1047
+ lambda {
1048
+ DataMapper::Query.new(@repository, @model, @options.update(:order => 'unknown'))
1049
+ }.should raise_error(ArgumentError, "+options[:order]+ entry \"unknown\" does not map to a property in #{@model}")
1050
+ end
1051
+ end
1052
+
1053
+ describe 'that is an Array containing an unknown String' do
1054
+ it 'should raise an exception' do
1055
+ lambda {
1056
+ DataMapper::Query.new(@repository, @model, @options.update(:order => [ 'unknown' ]))
1057
+ }.should raise_error(ArgumentError, "+options[:order]+ entry \"unknown\" does not map to a property in #{@model}")
1058
+ end
1059
+ end
1060
+
1061
+ describe 'that contains a Symbol that is not for a Property in the model' do
1062
+ it 'should raise an exception' do
1063
+ lambda {
1064
+ DataMapper::Query.new(@repository, @model, @options.update(:order => [ :unknown ]))
1065
+ }.should raise_error(ArgumentError, "+options[:order]+ entry :unknown does not map to a property in #{@model}")
1066
+ end
1067
+ end
1068
+
1069
+ describe 'that contains a String that is not for a Property in the model' do
1070
+ it 'should raise an exception' do
1071
+ lambda {
1072
+ DataMapper::Query.new(@repository, @model, @options.update(:order => [ 'unknown' ]))
1073
+ }.should raise_error(ArgumentError, "+options[:order]+ entry \"unknown\" does not map to a property in #{@model}")
1074
+ end
1075
+ end
1076
+ end
1077
+
1078
+ describe 'with a unique option' do
1079
+ describe 'that is valid' do
1080
+ before :all do
1081
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
1082
+ end
1083
+
1084
+ it { @return.should be_kind_of(DataMapper::Query) }
1085
+
1086
+ it 'should set the unique? flag' do
1087
+ @return.unique?.should == @unique
1088
+ end
1089
+ end
1090
+
1091
+ describe 'that is missing' do
1092
+ before :all do
1093
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:unique, :links).freeze)
1094
+ end
1095
+
1096
+ it { @return.should be_kind_of(DataMapper::Query) }
1097
+
1098
+ it 'should set the query to not be unique' do
1099
+ @return.should_not be_unique
1100
+ end
1101
+ end
1102
+
1103
+ describe 'that is invalid' do
1104
+ it 'should raise an exception' do
1105
+ lambda {
1106
+ DataMapper::Query.new(@repository, @model, @options.update(:unique => nil))
1107
+ }.should raise_error(ArgumentError, '+options[:unique]+ should be true or false, but was nil')
1108
+ end
1109
+ end
1110
+ end
1111
+
1112
+ describe 'with an add_reversed option' do
1113
+ describe 'that is valid' do
1114
+ before :all do
1115
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
1116
+ end
1117
+
1118
+ it { @return.should be_kind_of(DataMapper::Query) }
1119
+
1120
+ it 'should set the add_reversed? flag' do
1121
+ @return.add_reversed?.should == @add_reversed
1122
+ end
1123
+ end
1124
+
1125
+ describe 'that is missing' do
1126
+ before :all do
1127
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:add_reversed).freeze)
1128
+ end
1129
+
1130
+ it { @return.should be_kind_of(DataMapper::Query) }
1131
+
1132
+ it 'should set the query to not add in reverse order' do
1133
+ # TODO: think about renaming the flag to not sound 'clumsy'
1134
+ @return.should_not be_add_reversed
1135
+ end
1136
+ end
1137
+
1138
+ describe 'that is invalid' do
1139
+ it 'should raise an exception' do
1140
+ lambda {
1141
+ DataMapper::Query.new(@repository, @model, @options.update(:add_reversed => nil))
1142
+ }.should raise_error(ArgumentError, '+options[:add_reversed]+ should be true or false, but was nil')
1143
+ end
1144
+ end
1145
+ end
1146
+
1147
+ describe 'with a reload option' do
1148
+ describe 'that is valid' do
1149
+ before :all do
1150
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
1151
+ end
1152
+
1153
+ it { @return.should be_kind_of(DataMapper::Query) }
1154
+
1155
+ it 'should set the reload? flag' do
1156
+ @return.reload?.should == @reload
1157
+ end
1158
+ end
1159
+
1160
+ describe 'that is missing' do
1161
+ before :all do
1162
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:reload).freeze)
1163
+ end
1164
+
1165
+ it { @return.should be_kind_of(DataMapper::Query) }
1166
+
1167
+ it 'should set the query to not reload' do
1168
+ @return.should_not be_reload
1169
+ end
1170
+ end
1171
+
1172
+ describe 'that is invalid' do
1173
+ it 'should raise an exception' do
1174
+ lambda {
1175
+ DataMapper::Query.new(@repository, @model, @options.update(:reload => nil))
1176
+ }.should raise_error(ArgumentError, '+options[:reload]+ should be true or false, but was nil')
1177
+ end
1178
+ end
1179
+ end
1180
+
1181
+ describe 'with options' do
1182
+ describe 'that are unknown' do
1183
+ before :all do
1184
+ @options.update(@options.delete(:conditions))
1185
+
1186
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
1187
+ end
1188
+
1189
+ it { @return.should be_kind_of(DataMapper::Query) }
1190
+
1191
+ it 'should set the conditions' do
1192
+ @return.conditions.should ==
1193
+ DataMapper::Query::Conditions::Operation.new(
1194
+ :and,
1195
+ DataMapper::Query::Conditions::Comparison.new(
1196
+ :eql,
1197
+ @model.properties[:name],
1198
+ @conditions[:name]
1199
+ )
1200
+ )
1201
+ end
1202
+ end
1203
+
1204
+ describe 'that are invalid' do
1205
+ it 'should raise an exception' do
1206
+ lambda {
1207
+ DataMapper::Query.new(@repository, @model, 'invalid')
1208
+ }.should raise_error(StandardError)
1209
+ end
1210
+ end
1211
+ end
1212
+
1213
+ describe 'with no options' do
1214
+ before :all do
1215
+ @return = DataMapper::Query.new(@repository, @model)
1216
+ end
1217
+
1218
+ it { @return.should be_kind_of(DataMapper::Query) }
1219
+
1220
+ it 'should set options to an empty Hash' do
1221
+ @return.options.should == {}
1222
+ end
1223
+ end
1224
+ end
1225
+ end
1226
+
1227
+ # instance methods
1228
+ describe DataMapper::Query do
1229
+ before :all do
1230
+ class ::User
1231
+ include DataMapper::Resource
1232
+
1233
+ property :name, String, :key => true
1234
+ property :citizenship, String
1235
+
1236
+ belongs_to :referrer, self, :required => false
1237
+ has n, :referrals, self, :inverse => :referrer
1238
+ has n, :grandparents, self, :through => :referrals, :via => :referrer
1239
+ end
1240
+
1241
+ class ::Other
1242
+ include DataMapper::Resource
1243
+
1244
+ property :id, Serial
1245
+ end
1246
+
1247
+ # finalize the models
1248
+ DataMapper.finalize
1249
+
1250
+ @repository = DataMapper::Repository.new(:default)
1251
+ @model = User
1252
+ @options = { :limit => 3 }
1253
+ @query = DataMapper::Query.new(@repository, @model, @options)
1254
+ @original = @query
1255
+ end
1256
+
1257
+ before :all do
1258
+ @other_options = {
1259
+ :fields => [ @model.properties[:name] ].freeze,
1260
+ :links => [ @model.relationships[:referrer] ].freeze,
1261
+ :conditions => [ 'name = ?', 'Dan Kubb' ].freeze,
1262
+ :offset => 1,
1263
+ :limit => 2,
1264
+ :order => [ DataMapper::Query::Direction.new(@model.properties[:name], :desc) ].freeze,
1265
+ :unique => true,
1266
+ :add_reversed => true,
1267
+ :reload => true,
1268
+ }
1269
+ end
1270
+
1271
+ subject { @query }
1272
+
1273
+ it { should respond_to(:==) }
1274
+
1275
+ describe '#==' do
1276
+ describe 'when other is equal' do
1277
+ before :all do
1278
+ @return = @query == @query
1279
+ end
1280
+
1281
+ it { @return.should be(true) }
1282
+ end
1283
+
1284
+ describe 'when other is equivalent' do
1285
+ before :all do
1286
+ @return = @query == @query.dup
1287
+ end
1288
+
1289
+ it { @return.should be(true) }
1290
+ end
1291
+
1292
+ DataMapper::Query::OPTIONS.each do |name|
1293
+ describe "when other has an inequalvalent #{name}" do
1294
+ before :all do
1295
+ @return = @query == @query.merge(name => @other_options[name])
1296
+ end
1297
+
1298
+ it { @return.should be(false) }
1299
+ end
1300
+ end
1301
+
1302
+ describe 'when other is a different type of object that can be compared, and is equivalent' do
1303
+ before :all do
1304
+ @other = OpenStruct.new(
1305
+ :repository => @query.repository,
1306
+ :model => @query.model,
1307
+ :sorted_fields => @query.sorted_fields,
1308
+ :links => @query.links,
1309
+ :conditions => @query.conditions,
1310
+ :order => @query.order,
1311
+ :limit => @query.limit,
1312
+ :offset => @query.offset,
1313
+ :reload? => @query.reload?,
1314
+ :unique? => @query.unique?,
1315
+ :add_reversed? => @query.add_reversed?
1316
+ )
1317
+
1318
+ @return = @query == @other
1319
+ end
1320
+
1321
+ it { @return.should be(false) }
1322
+ end
1323
+
1324
+ describe 'when other is a different type of object that can be compared, and is not equivalent' do
1325
+ before :all do
1326
+ @other = OpenStruct.new(
1327
+ :repository => @query.repository,
1328
+ :model => @query.model,
1329
+ :sorted_fields => @query.sorted_fields,
1330
+ :links => @query.links,
1331
+ :conditions => @query.conditions,
1332
+ :order => @query.order,
1333
+ :limit => @query.limit,
1334
+ :offset => @query.offset,
1335
+ :reload? => true,
1336
+ :unique? => @query.unique?,
1337
+ :add_reversed? => @query.add_reversed?
1338
+ )
1339
+
1340
+ @return = @query == @other
1341
+ end
1342
+
1343
+ it { @return.should be(false) }
1344
+ end
1345
+
1346
+ describe 'when other is a different type of object that cannot be compared' do
1347
+ before :all do
1348
+ @return = @query == 'invalid'
1349
+ end
1350
+
1351
+ it { @return.should be(false) }
1352
+ end
1353
+ end
1354
+
1355
+ it { should respond_to(:conditions) }
1356
+
1357
+ describe '#conditions' do
1358
+ before :all do
1359
+ @query.update(:name => 'Dan Kubb')
1360
+
1361
+ @return = @query.conditions
1362
+ end
1363
+
1364
+ it { @return.should be_kind_of(DataMapper::Query::Conditions::AndOperation) }
1365
+
1366
+ it 'should return expected value' do
1367
+ @return.should ==
1368
+ DataMapper::Query::Conditions::Operation.new(
1369
+ :and,
1370
+ DataMapper::Query::Conditions::Comparison.new(
1371
+ :eql,
1372
+ @model.properties[:name],
1373
+ 'Dan Kubb'
1374
+ )
1375
+ )
1376
+ end
1377
+ end
1378
+
1379
+ [ :difference, :- ].each do |method|
1380
+ it { should respond_to(method) }
1381
+
1382
+ describe "##{method}" do
1383
+ supported_by :all do
1384
+ before :all do
1385
+ @key = @model.key(@repository.name)
1386
+
1387
+ @self_relationship = DataMapper::Associations::OneToMany::Relationship.new(
1388
+ :self,
1389
+ @model,
1390
+ @model,
1391
+ {
1392
+ :child_key => @key.map { |p| p.name },
1393
+ :parent_key => @key.map { |p| p.name },
1394
+ :child_repository_name => @repository.name,
1395
+ :parent_repository_name => @repository.name,
1396
+ }
1397
+ )
1398
+
1399
+ 10.times do |n|
1400
+ @model.create(:name => "#{@model} #{n}")
1401
+ end
1402
+ end
1403
+
1404
+ subject { @query.send(method, @other) }
1405
+
1406
+ describe 'with other matching everything' do
1407
+ before do
1408
+ @query = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1409
+ @other = DataMapper::Query.new(@repository, @model)
1410
+
1411
+ @expected = DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb')
1412
+ end
1413
+
1414
+ it { should be_kind_of(DataMapper::Query) }
1415
+
1416
+ it { should_not equal(@query) }
1417
+
1418
+ it { should_not equal(@other) }
1419
+
1420
+ it 'should factor out the operation matching everything' do
1421
+ subject.conditions.should == @expected
1422
+ end
1423
+ end
1424
+
1425
+ describe 'with self matching everything' do
1426
+ before do
1427
+ @query = DataMapper::Query.new(@repository, @model)
1428
+ @other = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1429
+
1430
+ @expected = DataMapper::Query::Conditions::Operation.new(:not,
1431
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb')
1432
+ )
1433
+ end
1434
+
1435
+ it { should be_kind_of(DataMapper::Query) }
1436
+
1437
+ it { should_not equal(@query) }
1438
+
1439
+ it { should_not equal(@other) }
1440
+
1441
+ it 'should factor out the operation matching everything, and negate the other' do
1442
+ subject.conditions.should == @expected
1443
+ end
1444
+ end
1445
+
1446
+ describe 'with self having a limit' do
1447
+ before do
1448
+ @query = DataMapper::Query.new(@repository, @model, :limit => 5)
1449
+ @other = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1450
+
1451
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
1452
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@query.merge(:fields => @key))),
1453
+ DataMapper::Query::Conditions::Operation.new(:not,
1454
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb')
1455
+ )
1456
+ )
1457
+ end
1458
+
1459
+ it { should_not equal(@query) }
1460
+
1461
+ it { should_not equal(@other) }
1462
+
1463
+ it 'should put each query into a subquery and AND them together, and negate the other' do
1464
+ subject.conditions.should == @expected
1465
+ end
1466
+ end
1467
+
1468
+ describe 'with other having a limit' do
1469
+ before do
1470
+ @query = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1471
+ @other = DataMapper::Query.new(@repository, @model, :limit => 5)
1472
+
1473
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
1474
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb'),
1475
+ DataMapper::Query::Conditions::Operation.new(:not,
1476
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@other.merge(:fields => @key)))
1477
+ )
1478
+ )
1479
+ end
1480
+
1481
+ it { should_not equal(@query) }
1482
+
1483
+ it { should_not equal(@other) }
1484
+
1485
+ it 'should put each query into a subquery and AND them together, and negate the other' do
1486
+ subject.conditions.should == @expected
1487
+ end
1488
+ end
1489
+
1490
+ describe 'with self having an offset > 0' do
1491
+ before do
1492
+ @query = DataMapper::Query.new(@repository, @model, :offset => 5, :limit => 5)
1493
+ @other = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1494
+
1495
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
1496
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@query.merge(:fields => @key))),
1497
+ DataMapper::Query::Conditions::Operation.new(:not,
1498
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb')
1499
+ )
1500
+ )
1501
+ end
1502
+
1503
+ it { should_not equal(@query) }
1504
+
1505
+ it { should_not equal(@other) }
1506
+
1507
+ it 'should put each query into a subquery and AND them together, and negate the other' do
1508
+ subject.conditions.should == @expected
1509
+ end
1510
+ end
1511
+
1512
+ describe 'with other having an offset > 0' do
1513
+ before do
1514
+ @query = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1515
+ @other = DataMapper::Query.new(@repository, @model, :offset => 5, :limit => 5)
1516
+
1517
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
1518
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb'),
1519
+ DataMapper::Query::Conditions::Operation.new(:not,
1520
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@other.merge(:fields => @key)))
1521
+ )
1522
+ )
1523
+ end
1524
+
1525
+ it { should_not equal(@query) }
1526
+
1527
+ it { should_not equal(@other) }
1528
+
1529
+ it 'should put each query into a subquery and AND them together, and negate the other' do
1530
+ subject.conditions.should == @expected
1531
+ end
1532
+ end
1533
+
1534
+ describe 'with self having links' do
1535
+ before :all do
1536
+ @do_adapter = defined?(DataMapper::Adapters::DataObjectsAdapter) && @adapter.kind_of?(DataMapper::Adapters::DataObjectsAdapter)
1537
+ end
1538
+
1539
+ before do
1540
+ @query = DataMapper::Query.new(@repository, @model, :links => [ :referrer ])
1541
+ @other = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1542
+
1543
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
1544
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@query.merge(:fields => @key))),
1545
+ DataMapper::Query::Conditions::Operation.new(:not,
1546
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb')
1547
+ )
1548
+ )
1549
+ end
1550
+
1551
+ it { should_not equal(@query) }
1552
+
1553
+ it { should_not equal(@other) }
1554
+
1555
+ it 'should put each query into a subquery and AND them together, and negate the other query' do
1556
+ subject.conditions.should == @expected
1557
+ end
1558
+ end
1559
+
1560
+ describe 'with other having links' do
1561
+ before :all do
1562
+ @do_adapter = defined?(DataMapper::Adapters::DataObjectsAdapter) && @adapter.kind_of?(DataMapper::Adapters::DataObjectsAdapter)
1563
+ end
1564
+
1565
+ before do
1566
+ @query = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1567
+ @other = DataMapper::Query.new(@repository, @model, :links => [ :referrer ])
1568
+
1569
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
1570
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb'),
1571
+ DataMapper::Query::Conditions::Operation.new(:not,
1572
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@other.merge(:fields => @key)))
1573
+ )
1574
+ )
1575
+ end
1576
+
1577
+ it { should_not equal(@query) }
1578
+
1579
+ it { should_not equal(@other) }
1580
+
1581
+ it 'should put each query into a subquery and AND them together, and negate the other query' do
1582
+ subject.conditions.should == @expected
1583
+ end
1584
+ end
1585
+
1586
+ describe 'with different conditions, no links/offset/limit' do
1587
+ before do
1588
+ property = @model.properties[:name]
1589
+
1590
+ @query = DataMapper::Query.new(@repository, @model, property.name => 'Dan Kubb')
1591
+ @other = DataMapper::Query.new(@repository, @model, property.name => 'John Doe')
1592
+
1593
+ @query.conditions.should_not == @other.conditions
1594
+
1595
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
1596
+ DataMapper::Query::Conditions::Comparison.new(:eql, property, 'Dan Kubb'),
1597
+ DataMapper::Query::Conditions::Operation.new(:not,
1598
+ DataMapper::Query::Conditions::Comparison.new(:eql, property, 'John Doe')
1599
+ )
1600
+ )
1601
+ end
1602
+
1603
+ it { should be_kind_of(DataMapper::Query) }
1604
+
1605
+ it { should_not equal(@query) }
1606
+
1607
+ it { should_not equal(@other) }
1608
+
1609
+ it 'should AND the conditions together, and negate the other query' do
1610
+ subject.conditions.should == @expected
1611
+ end
1612
+ end
1613
+
1614
+ describe 'with different fields' do
1615
+ before do
1616
+ @property = @model.properties[:name]
1617
+
1618
+ @query = DataMapper::Query.new(@repository, @model)
1619
+ @other = DataMapper::Query.new(@repository, @model, :fields => [ @property ])
1620
+
1621
+ @query.fields.should_not == @other.fields
1622
+ end
1623
+
1624
+ it { should be_kind_of(DataMapper::Query) }
1625
+
1626
+ it { should_not equal(@query) }
1627
+
1628
+ it { should_not equal(@other) }
1629
+
1630
+ it { subject.conditions.should == DataMapper::Query::Conditions::Operation.new(:and) }
1631
+
1632
+ it 'should use the other fields' do
1633
+ subject.fields.should == [ @property ]
1634
+ end
1635
+ end
1636
+
1637
+ describe 'with different order' do
1638
+ before do
1639
+ @property = @model.properties[:name]
1640
+
1641
+ @query = DataMapper::Query.new(@repository, @model)
1642
+ @other = DataMapper::Query.new(@repository, @model, :order => [ DataMapper::Query::Direction.new(@property, :desc) ])
1643
+
1644
+ @query.order.should_not == @other.order
1645
+ end
1646
+
1647
+ it { should be_kind_of(DataMapper::Query) }
1648
+
1649
+ it { should_not equal(@query) }
1650
+
1651
+ it { should_not equal(@other) }
1652
+
1653
+ it { subject.conditions.should == DataMapper::Query::Conditions::Operation.new(:and) }
1654
+
1655
+ it 'should use the other order' do
1656
+ subject.order.should == [ DataMapper::Query::Direction.new(@property, :desc) ]
1657
+ end
1658
+ end
1659
+
1660
+ describe 'with different unique' do
1661
+ before do
1662
+ @query = DataMapper::Query.new(@repository, @model)
1663
+ @other = DataMapper::Query.new(@repository, @model, :unique => true)
1664
+
1665
+ @query.unique?.should_not == @other.unique?
1666
+ end
1667
+
1668
+ it { should be_kind_of(DataMapper::Query) }
1669
+
1670
+ it { should_not equal(@query) }
1671
+
1672
+ it { should_not equal(@other) }
1673
+
1674
+ it { subject.conditions.should == DataMapper::Query::Conditions::Operation.new(:and) }
1675
+
1676
+ it 'should use the other unique' do
1677
+ subject.unique?.should == true
1678
+ end
1679
+ end
1680
+
1681
+ describe 'with different add_reversed' do
1682
+ before do
1683
+ @query = DataMapper::Query.new(@repository, @model)
1684
+ @other = DataMapper::Query.new(@repository, @model, :add_reversed => true)
1685
+
1686
+ @query.add_reversed?.should_not == @other.add_reversed?
1687
+ end
1688
+
1689
+ it { should be_kind_of(DataMapper::Query) }
1690
+
1691
+ it { should_not equal(@query) }
1692
+
1693
+ it { should_not equal(@other) }
1694
+
1695
+ it { subject.conditions.should == DataMapper::Query::Conditions::Operation.new(:and) }
1696
+
1697
+ it 'should use the other add_reversed' do
1698
+ subject.add_reversed?.should == true
1699
+ end
1700
+ end
1701
+
1702
+ describe 'with different reload' do
1703
+ before do
1704
+ @query = DataMapper::Query.new(@repository, @model)
1705
+ @other = DataMapper::Query.new(@repository, @model, :reload => true)
1706
+
1707
+ @query.reload?.should_not == @other.reload?
1708
+ end
1709
+
1710
+ it { should be_kind_of(DataMapper::Query) }
1711
+
1712
+ it { should_not equal(@query) }
1713
+
1714
+ it { should_not equal(@other) }
1715
+
1716
+ it { subject.conditions.should == DataMapper::Query::Conditions::Operation.new(:and) }
1717
+
1718
+ it 'should use the other reload' do
1719
+ subject.reload?.should == true
1720
+ end
1721
+ end
1722
+
1723
+ describe 'with different models' do
1724
+ before { @other = DataMapper::Query.new(@repository, Other) }
1725
+
1726
+ it { method(:subject).should raise_error(ArgumentError) }
1727
+ end
1728
+ end
1729
+ end
1730
+ end
1731
+
1732
+ it { should respond_to(:dup) }
1733
+
1734
+ describe '#dup' do
1735
+ before :all do
1736
+ @new = @query.dup
1737
+ end
1738
+
1739
+ it 'should return a Query' do
1740
+ @new.should be_kind_of(DataMapper::Query)
1741
+ end
1742
+
1743
+ it 'should not equal query' do
1744
+ @new.should_not equal(@query)
1745
+ end
1746
+
1747
+ it 'should eql query' do
1748
+ @new.should eql(@query)
1749
+ end
1750
+
1751
+ it 'should == query' do
1752
+ @new.should == @query
1753
+ end
1754
+ end
1755
+
1756
+ it { should respond_to(:eql?) }
1757
+
1758
+ describe '#eql?' do
1759
+ describe 'when other is equal' do
1760
+ before :all do
1761
+ @return = @query.eql?(@query)
1762
+ end
1763
+
1764
+ it { @return.should be(true) }
1765
+ end
1766
+
1767
+ describe 'when other is eql' do
1768
+ before :all do
1769
+ @return = @query.eql?(@query.dup)
1770
+ end
1771
+
1772
+ it { @return.should be(true) }
1773
+ end
1774
+
1775
+ DataMapper::Query::OPTIONS.each do |name|
1776
+ describe "when other has an not eql #{name}" do
1777
+ before :all do
1778
+ @return = @query.eql?(@query.merge(name => @other_options[name]))
1779
+ end
1780
+
1781
+ it { @return.should be(false) }
1782
+ end
1783
+ end
1784
+
1785
+ describe 'when other is a different type of object' do
1786
+ before :all do
1787
+ @other = OpenStruct.new(
1788
+ :repository => @query.repository,
1789
+ :model => @query.model,
1790
+ :sorted_fields => @query.sorted_fields,
1791
+ :links => @query.links,
1792
+ :conditions => @query.conditions,
1793
+ :order => @query.order,
1794
+ :limit => @query.limit,
1795
+ :offset => @query.offset,
1796
+ :reload? => @query.reload?,
1797
+ :unique? => @query.unique?,
1798
+ :add_reversed? => @query.add_reversed?
1799
+ )
1800
+
1801
+ @return = @query.eql?(@other)
1802
+ end
1803
+
1804
+ it { @return.should be(false) }
1805
+ end
1806
+ end
1807
+
1808
+ it { should respond_to(:fields) }
1809
+
1810
+ describe '#fields' do
1811
+ before :all do
1812
+ @return = @query.fields
1813
+ end
1814
+
1815
+ it { @return.should be_kind_of(Array) }
1816
+
1817
+ it 'should return expected value' do
1818
+ @return.should == [ @model.properties[:name], @model.properties[:citizenship], @model.properties[:referrer_name] ]
1819
+ end
1820
+ end
1821
+
1822
+ it { should respond_to(:filter_records) }
1823
+
1824
+ describe '#filter_records' do
1825
+ supported_by :all do
1826
+ before :all do
1827
+ @john = { 'name' => 'John Doe', 'referrer_name' => nil }
1828
+ @sam = { 'name' => 'Sam Smoot', 'referrer_name' => nil }
1829
+ @dan = { 'name' => 'Dan Kubb', 'referrer_name' => 'Sam Smoot' }
1830
+
1831
+ @records = [ @john, @sam, @dan ]
1832
+
1833
+ @query.update(:name.not => @sam['name'])
1834
+
1835
+ @return = @query.filter_records(@records)
1836
+ end
1837
+
1838
+ it 'should return Enumerable' do
1839
+ @return.should be_kind_of(Enumerable)
1840
+ end
1841
+
1842
+ it 'should not be the records provided' do
1843
+ @return.should_not equal(@records)
1844
+ end
1845
+
1846
+ it 'should return expected values' do
1847
+ @return.should == [ @dan, @john ]
1848
+ end
1849
+ end
1850
+ end
1851
+
1852
+ it { should respond_to(:inspect) }
1853
+
1854
+ describe '#inspect' do
1855
+ before :all do
1856
+ @return = @query.inspect
1857
+ end
1858
+
1859
+ it 'should return expected value' do
1860
+ @return.should == DataMapper::Ext::String.compress_lines(<<-INSPECT)
1861
+ #<DataMapper::Query
1862
+ @repository=:default
1863
+ @model=User
1864
+ @fields=[#<DataMapper::Property::String @model=User @name=:name>, #<DataMapper::Property::String @model=User @name=:citizenship>, #<DataMapper::Property::String @model=User @name=:referrer_name>]
1865
+ @links=[]
1866
+ @conditions=nil
1867
+ @order=[#<DataMapper::Query::Direction @target=#<DataMapper::Property::String @model=User @name=:name> @operator=:asc>]
1868
+ @limit=3
1869
+ @offset=0
1870
+ @reload=false
1871
+ @unique=false>
1872
+ INSPECT
1873
+ end
1874
+ end
1875
+
1876
+ [ :intersection, :& ].each do |method|
1877
+ it { should respond_to(method) }
1878
+
1879
+ describe "##{method}" do
1880
+ supported_by :all do
1881
+ before :all do
1882
+ @key = @model.key(@repository.name)
1883
+
1884
+ @self_relationship = DataMapper::Associations::OneToMany::Relationship.new(
1885
+ :self,
1886
+ @model,
1887
+ @model,
1888
+ {
1889
+ :child_key => @key.map { |p| p.name },
1890
+ :parent_key => @key.map { |p| p.name },
1891
+ :child_repository_name => @repository.name,
1892
+ :parent_repository_name => @repository.name,
1893
+ }
1894
+ )
1895
+
1896
+ 10.times do |n|
1897
+ @model.create(:name => "#{@model} #{n}")
1898
+ end
1899
+ end
1900
+
1901
+ subject do
1902
+ result = @query.send(method, @other)
1903
+
1904
+ if @another
1905
+ result = result.send(method, @another)
1906
+ end
1907
+
1908
+ result
1909
+ end
1910
+
1911
+ describe 'with equivalent query' do
1912
+ before { @other = @query.dup }
1913
+
1914
+ it { should be_kind_of(DataMapper::Query) }
1915
+
1916
+ it { should_not equal(@query) }
1917
+
1918
+ it { should_not equal(@other) }
1919
+
1920
+ it { should == @query }
1921
+ end
1922
+
1923
+ describe 'with other matching everything' do
1924
+ before do
1925
+ @query = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1926
+ @other = DataMapper::Query.new(@repository, @model)
1927
+ end
1928
+
1929
+ it { should be_kind_of(DataMapper::Query) }
1930
+
1931
+ it { should_not equal(@query) }
1932
+
1933
+ it { should_not equal(@other) }
1934
+
1935
+ it 'should factor out the operation matching everything' do
1936
+ pending 'TODO: compress Query#conditions for proper comparison' do
1937
+ should == DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1938
+ end
1939
+ end
1940
+ end
1941
+
1942
+ describe 'with self matching everything' do
1943
+ before do
1944
+ @query = DataMapper::Query.new(@repository, @model)
1945
+ @other = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1946
+ @another = DataMapper::Query.new(@repository, @model, :citizenship => 'US')
1947
+ end
1948
+
1949
+ it { should be_kind_of(DataMapper::Query) }
1950
+
1951
+ it { should_not equal(@query) }
1952
+
1953
+ it { should_not equal(@other) }
1954
+
1955
+ it { should_not equal(@another) }
1956
+
1957
+ it 'should factor out the operation matching everything' do
1958
+ should == DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb', :citizenship => 'US')
1959
+ end
1960
+ end
1961
+
1962
+ describe 'with self having a limit' do
1963
+ before do
1964
+ @query = DataMapper::Query.new(@repository, @model, :limit => 5)
1965
+ @other = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1966
+
1967
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
1968
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@query.merge(:fields => @key))),
1969
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb')
1970
+ )
1971
+ end
1972
+
1973
+ it { should_not equal(@query) }
1974
+
1975
+ it { should_not equal(@other) }
1976
+
1977
+ it 'should put each query into a subquery and AND them together' do
1978
+ subject.conditions.should == @expected
1979
+ end
1980
+ end
1981
+
1982
+ describe 'with other having a limit' do
1983
+ before do
1984
+ @query = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
1985
+ @other = DataMapper::Query.new(@repository, @model, :limit => 5)
1986
+
1987
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
1988
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb'),
1989
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@other.merge(:fields => @key)))
1990
+ )
1991
+ end
1992
+
1993
+ it { should_not equal(@query) }
1994
+
1995
+ it { should_not equal(@other) }
1996
+
1997
+ it 'should put each query into a subquery and AND them together' do
1998
+ subject.conditions.should == @expected
1999
+ end
2000
+ end
2001
+
2002
+ describe 'with self having an offset > 0' do
2003
+ before do
2004
+ @query = DataMapper::Query.new(@repository, @model, :offset => 5, :limit => 5)
2005
+ @other = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
2006
+
2007
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
2008
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@query.merge(:fields => @key))),
2009
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb')
2010
+ )
2011
+ end
2012
+
2013
+ it { should_not equal(@query) }
2014
+
2015
+ it { should_not equal(@other) }
2016
+
2017
+ it 'should put each query into a subquery and AND them together' do
2018
+ subject.conditions.should == @expected
2019
+ end
2020
+ end
2021
+
2022
+ describe 'with other having an offset > 0' do
2023
+ before do
2024
+ @query = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
2025
+ @other = DataMapper::Query.new(@repository, @model, :offset => 5, :limit => 5)
2026
+
2027
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
2028
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb'),
2029
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@other.merge(:fields => @key)))
2030
+ )
2031
+ end
2032
+
2033
+ it { should_not equal(@query) }
2034
+
2035
+ it { should_not equal(@other) }
2036
+
2037
+ it 'should put each query into a subquery and AND them together' do
2038
+ subject.conditions.should == @expected
2039
+ end
2040
+ end
2041
+
2042
+ describe 'with self having links' do
2043
+ before :all do
2044
+ @do_adapter = defined?(DataMapper::Adapters::DataObjectsAdapter) && @adapter.kind_of?(DataMapper::Adapters::DataObjectsAdapter)
2045
+ end
2046
+
2047
+ before do
2048
+ @query = DataMapper::Query.new(@repository, @model, :links => [ :referrer ])
2049
+ @other = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
2050
+
2051
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
2052
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@query.merge(:fields => @key))),
2053
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb')
2054
+ )
2055
+ end
2056
+
2057
+ it { should_not equal(@query) }
2058
+
2059
+ it { should_not equal(@other) }
2060
+
2061
+ it 'should put each query into a subquery and AND them together' do
2062
+ subject.conditions.should == @expected
2063
+ end
2064
+ end
2065
+
2066
+ describe 'with other having links' do
2067
+ before :all do
2068
+ @do_adapter = defined?(DataMapper::Adapters::DataObjectsAdapter) && @adapter.kind_of?(DataMapper::Adapters::DataObjectsAdapter)
2069
+ end
2070
+
2071
+ before do
2072
+ @query = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
2073
+ @other = DataMapper::Query.new(@repository, @model, :links => [ :referrer ])
2074
+
2075
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
2076
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb'),
2077
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@other.merge(:fields => @key)))
2078
+ )
2079
+ end
2080
+
2081
+ it { should_not equal(@query) }
2082
+
2083
+ it { should_not equal(@other) }
2084
+
2085
+ it 'should put each query into a subquery and AND them together' do
2086
+ subject.conditions.should == @expected
2087
+ end
2088
+ end
2089
+
2090
+ describe 'with different conditions, no links/offset/limit' do
2091
+ before do
2092
+ property = @model.properties[:name]
2093
+
2094
+ @query = DataMapper::Query.new(@repository, @model, property.name => 'Dan Kubb')
2095
+ @other = DataMapper::Query.new(@repository, @model, property.name => 'John Doe')
2096
+
2097
+ @query.conditions.should_not == @other.conditions
2098
+
2099
+ @expected = DataMapper::Query::Conditions::Operation.new(:and,
2100
+ DataMapper::Query::Conditions::Comparison.new(:eql, property, 'Dan Kubb'),
2101
+ DataMapper::Query::Conditions::Comparison.new(:eql, property, 'John Doe')
2102
+ )
2103
+ end
2104
+
2105
+ it { should be_kind_of(DataMapper::Query) }
2106
+
2107
+ it { should_not equal(@query) }
2108
+
2109
+ it { should_not equal(@other) }
2110
+
2111
+ it 'should AND the conditions together' do
2112
+ subject.conditions.should == @expected
2113
+ end
2114
+ end
2115
+
2116
+ describe 'with different fields' do
2117
+ before do
2118
+ @property = @model.properties[:name]
2119
+
2120
+ @query = DataMapper::Query.new(@repository, @model)
2121
+ @other = DataMapper::Query.new(@repository, @model, :fields => [ @property ])
2122
+
2123
+ @query.fields.should_not == @other.fields
2124
+ end
2125
+
2126
+ it { should be_kind_of(DataMapper::Query) }
2127
+
2128
+ it { should_not equal(@query) }
2129
+
2130
+ it { should_not equal(@other) }
2131
+
2132
+ it { subject.conditions.should be_nil }
2133
+
2134
+ it 'should use the other fields' do
2135
+ subject.fields.should == [ @property ]
2136
+ end
2137
+ end
2138
+
2139
+ describe 'with different order' do
2140
+ before do
2141
+ @property = @model.properties[:name]
2142
+
2143
+ @query = DataMapper::Query.new(@repository, @model)
2144
+ @other = DataMapper::Query.new(@repository, @model, :order => [ DataMapper::Query::Direction.new(@property, :desc) ])
2145
+
2146
+ @query.order.should_not == @other.order
2147
+ end
2148
+
2149
+ it { should be_kind_of(DataMapper::Query) }
2150
+
2151
+ it { should_not equal(@query) }
2152
+
2153
+ it { should_not equal(@other) }
2154
+
2155
+ it { subject.conditions.should be_nil }
2156
+
2157
+ it 'should use the other order' do
2158
+ subject.order.should == [ DataMapper::Query::Direction.new(@property, :desc) ]
2159
+ end
2160
+ end
2161
+
2162
+ describe 'with different unique' do
2163
+ before do
2164
+ @query = DataMapper::Query.new(@repository, @model)
2165
+ @other = DataMapper::Query.new(@repository, @model, :unique => true)
2166
+
2167
+ @query.unique?.should_not == @other.unique?
2168
+ end
2169
+
2170
+ it { should be_kind_of(DataMapper::Query) }
2171
+
2172
+ it { should_not equal(@query) }
2173
+
2174
+ it { should_not equal(@other) }
2175
+
2176
+ it { subject.conditions.should be_nil }
2177
+
2178
+ it 'should use the other unique' do
2179
+ subject.unique?.should == true
2180
+ end
2181
+ end
2182
+
2183
+ describe 'with different add_reversed' do
2184
+ before do
2185
+ @query = DataMapper::Query.new(@repository, @model)
2186
+ @other = DataMapper::Query.new(@repository, @model, :add_reversed => true)
2187
+
2188
+ @query.add_reversed?.should_not == @other.add_reversed?
2189
+ end
2190
+
2191
+ it { should be_kind_of(DataMapper::Query) }
2192
+
2193
+ it { should_not equal(@query) }
2194
+
2195
+ it { should_not equal(@other) }
2196
+
2197
+ it { subject.conditions.should be_nil }
2198
+
2199
+ it 'should use the other add_reversed' do
2200
+ subject.add_reversed?.should == true
2201
+ end
2202
+ end
2203
+
2204
+ describe 'with different reload' do
2205
+ before do
2206
+ @query = DataMapper::Query.new(@repository, @model)
2207
+ @other = DataMapper::Query.new(@repository, @model, :reload => true)
2208
+
2209
+ @query.reload?.should_not == @other.reload?
2210
+ end
2211
+
2212
+ it { should be_kind_of(DataMapper::Query) }
2213
+
2214
+ it { should_not equal(@query) }
2215
+
2216
+ it { should_not equal(@other) }
2217
+
2218
+ it 'should use the other reload' do
2219
+ subject.reload?.should == true
2220
+ end
2221
+ end
2222
+
2223
+ describe 'with different models' do
2224
+ before { @other = DataMapper::Query.new(@repository, Other) }
2225
+
2226
+ it { method(:subject).should raise_error(ArgumentError) }
2227
+ end
2228
+ end
2229
+ end
2230
+ end
2231
+
2232
+ it { should respond_to(:limit) }
2233
+
2234
+ describe '#limit' do
2235
+ before :all do
2236
+ @return = @query.limit
2237
+ end
2238
+
2239
+ it { @return.should be_kind_of(Integer) }
2240
+
2241
+ it 'should return expected value' do
2242
+ @return.should == 3
2243
+ end
2244
+ end
2245
+
2246
+ it { should respond_to(:limit_records) }
2247
+
2248
+ describe '#limit_records' do
2249
+ supported_by :all do
2250
+ before :all do
2251
+ @john = { 'name' => 'John Doe', 'referrer_name' => nil }
2252
+ @sam = { 'name' => 'Sam Smoot', 'referrer_name' => nil }
2253
+ @dan = { 'name' => 'Dan Kubb', 'referrer_name' => 'Sam Smoot' }
2254
+
2255
+ @records = [ @john, @sam, @dan ]
2256
+
2257
+ @query.update(:limit => 1, :offset => 1)
2258
+
2259
+ @return = @query.limit_records(@records)
2260
+ end
2261
+
2262
+ it 'should return Enumerable' do
2263
+ @return.should be_kind_of(Enumerable)
2264
+ end
2265
+
2266
+ it 'should not be the records provided' do
2267
+ @return.should_not equal(@records)
2268
+ end
2269
+
2270
+ it 'should return expected values' do
2271
+ @return.should == [ @sam ]
2272
+ end
2273
+ end
2274
+ end
2275
+
2276
+ it { should respond_to(:links) }
2277
+
2278
+ describe '#links' do
2279
+ before :all do
2280
+ @return = @query.links
2281
+ end
2282
+
2283
+ it { @return.should be_kind_of(Array) }
2284
+
2285
+ it { @return.should be_empty }
2286
+ end
2287
+
2288
+ it { should respond_to(:match_records) }
2289
+
2290
+ describe '#match_records' do
2291
+ supported_by :all do
2292
+ before :all do
2293
+ @john = { 'name' => 'John Doe', 'referrer_name' => nil }
2294
+ @sam = { 'name' => 'Sam Smoot', 'referrer_name' => nil }
2295
+ @dan = { 'name' => 'Dan Kubb', 'referrer_name' => 'Sam Smoot' }
2296
+
2297
+ @records = [ @john, @sam, @dan ]
2298
+
2299
+ @query.update(:name.not => @sam['name'])
2300
+
2301
+ @return = @query.match_records(@records)
2302
+ end
2303
+
2304
+ it 'should return Enumerable' do
2305
+ @return.should be_kind_of(Enumerable)
2306
+ end
2307
+
2308
+ it 'should not be the records provided' do
2309
+ @return.should_not equal(@records)
2310
+ end
2311
+
2312
+ it 'should return expected values' do
2313
+ @return.should == [ @john, @dan ]
2314
+ end
2315
+ end
2316
+ end
2317
+
2318
+ it { should respond_to(:merge) }
2319
+
2320
+ describe '#merge' do
2321
+ describe 'with a Hash' do
2322
+ before do
2323
+ @return = @query.merge({ :limit => 202 })
2324
+ end
2325
+
2326
+ it 'does not affect the receiver' do
2327
+ @query.options[:limit].should == 3
2328
+ end
2329
+ end
2330
+
2331
+ describe 'with a Query' do
2332
+ before do
2333
+ @other = DataMapper::Query.new(@repository, @model, @options.update(@other_options))
2334
+ @return = @query.merge(@other)
2335
+ end
2336
+
2337
+ it 'does not affect the receiver' do
2338
+ @query.options[:limit].should == 3
2339
+ end
2340
+ end
2341
+ end
2342
+
2343
+ it { should respond_to(:model) }
2344
+
2345
+ describe '#model' do
2346
+ before :all do
2347
+ @return = @query.model
2348
+ end
2349
+
2350
+ it { @return.should be_kind_of(Class) }
2351
+
2352
+ it 'should return expected value' do
2353
+ @return.should == @model
2354
+ end
2355
+ end
2356
+
2357
+ it { should respond_to(:offset) }
2358
+
2359
+ describe '#offset' do
2360
+ before :all do
2361
+ @return = @query.offset
2362
+ end
2363
+
2364
+ it { @return.should be_kind_of(Integer) }
2365
+
2366
+ it 'should return expected value' do
2367
+ @return.should == 0
2368
+ end
2369
+ end
2370
+
2371
+ it { should respond_to(:order) }
2372
+
2373
+ describe '#order' do
2374
+ before :all do
2375
+ @return = @query.order
2376
+ end
2377
+
2378
+ it { @return.should be_kind_of(Array) }
2379
+
2380
+ it 'should return expected value' do
2381
+ @return.should == [ DataMapper::Query::Direction.new(@model.properties[:name]) ]
2382
+ end
2383
+ end
2384
+
2385
+ it { should respond_to(:raw?) }
2386
+
2387
+ describe '#raw?' do
2388
+ describe 'when the query contains raw conditions' do
2389
+ before :all do
2390
+ @query.update(:conditions => [ 'name = ?', 'Dan Kubb' ])
2391
+ end
2392
+
2393
+ it { should be_raw }
2394
+ end
2395
+
2396
+ describe 'when the query does not contain raw conditions' do
2397
+ it { should_not be_raw }
2398
+ end
2399
+ end
2400
+
2401
+ it { should respond_to(:relative) }
2402
+
2403
+ describe '#relative' do
2404
+ describe 'with a Hash' do
2405
+ describe 'that is empty' do
2406
+ before :all do
2407
+ @return = @query.relative({})
2408
+ end
2409
+
2410
+ it { @return.should be_kind_of(DataMapper::Query) }
2411
+
2412
+ it 'should not return self' do
2413
+ @return.should_not equal(@query)
2414
+ end
2415
+
2416
+ it 'should return a copy' do
2417
+ @return.should be_eql(@query)
2418
+ end
2419
+ end
2420
+
2421
+ describe 'using different options' do
2422
+ before :all do
2423
+ @return = @query.relative(@other_options)
2424
+ end
2425
+
2426
+ it { @return.should be_kind_of(DataMapper::Query) }
2427
+
2428
+ it 'should not return self' do
2429
+ @return.should_not equal(@original)
2430
+ end
2431
+
2432
+ it 'should update the fields' do
2433
+ @return.fields.should == @other_options[:fields]
2434
+ end
2435
+
2436
+ it 'should update the links' do
2437
+ @return.links.should == @other_options[:links]
2438
+ end
2439
+
2440
+ it 'should update the conditions' do
2441
+ @return.conditions.should == DataMapper::Query::Conditions::Operation.new(:and, [ 'name = ?', [ 'Dan Kubb' ] ])
2442
+ end
2443
+
2444
+ it 'should update the offset' do
2445
+ @return.offset.should == @other_options[:offset]
2446
+ end
2447
+
2448
+ it 'should update the limit' do
2449
+ @return.limit.should == @other_options[:limit]
2450
+ end
2451
+
2452
+ it 'should update the order' do
2453
+ @return.order.should == @other_options[:order]
2454
+ end
2455
+
2456
+ it 'should update the unique' do
2457
+ @return.unique?.should == @other_options[:unique]
2458
+ end
2459
+
2460
+ it 'should update the add_reversed' do
2461
+ @return.add_reversed?.should == @other_options[:add_reversed]
2462
+ end
2463
+
2464
+ it 'should update the reload' do
2465
+ @return.reload?.should == @other_options[:reload]
2466
+ end
2467
+ end
2468
+
2469
+ describe 'using extra options' do
2470
+ before :all do
2471
+ @options = { :name => 'Dan Kubb' }
2472
+
2473
+ @return = @query.relative(@options)
2474
+ end
2475
+
2476
+ it { @return.should be_kind_of(DataMapper::Query) }
2477
+
2478
+ it 'should not return self' do
2479
+ @return.should_not equal(@original)
2480
+ end
2481
+
2482
+ it 'should update the conditions' do
2483
+ @return.conditions.should ==
2484
+ DataMapper::Query::Conditions::Operation.new(
2485
+ :and,
2486
+ DataMapper::Query::Conditions::Comparison.new(
2487
+ :eql,
2488
+ @model.properties[:name],
2489
+ @options[:name]
2490
+ )
2491
+ )
2492
+ end
2493
+ end
2494
+
2495
+ describe 'using an offset when query offset is greater than 0' do
2496
+ before :all do
2497
+ @query = @query.update(:offset => 1, :limit => 2)
2498
+
2499
+ @return = @query.relative(:offset => 1)
2500
+ end
2501
+
2502
+ it { @return.should be_kind_of(DataMapper::Query) }
2503
+
2504
+ it 'should not return self' do
2505
+ @return.should_not equal(@original)
2506
+ end
2507
+
2508
+ it 'should update the offset to be relative to the original offset' do
2509
+ @return.offset.should == 2
2510
+ end
2511
+ end
2512
+
2513
+ describe 'using an limit when query limit specified' do
2514
+ before :all do
2515
+ @query = @query.update(:offset => 1, :limit => 2)
2516
+
2517
+ @return = @query.relative(:limit => 1)
2518
+ end
2519
+
2520
+ it { @return.should be_kind_of(DataMapper::Query) }
2521
+
2522
+ it 'should not return self' do
2523
+ @return.should_not equal(@original)
2524
+ end
2525
+
2526
+ it 'should update the limit' do
2527
+ @return.limit.should == 1
2528
+ end
2529
+ end
2530
+ end
2531
+ end
2532
+
2533
+ it { should respond_to(:reload?) }
2534
+
2535
+ describe '#reload?' do
2536
+ describe 'when the query should reload' do
2537
+ before :all do
2538
+ @query.update(:reload => true)
2539
+ end
2540
+
2541
+ it { should be_reload }
2542
+ end
2543
+
2544
+ describe 'when the query should not reload' do
2545
+ it { should_not be_reload }
2546
+ end
2547
+ end
2548
+
2549
+ it { should respond_to(:repository) }
2550
+
2551
+ describe '#repository' do
2552
+ before :all do
2553
+ @return = @query.repository
2554
+ end
2555
+
2556
+ it { @return.should be_kind_of(DataMapper::Repository) }
2557
+
2558
+ it 'should return expected value' do
2559
+ @return.should == @repository
2560
+ end
2561
+ end
2562
+
2563
+ it { should respond_to(:reverse) }
2564
+
2565
+ describe '#reverse' do
2566
+ before :all do
2567
+ @return = @query.reverse
2568
+ end
2569
+
2570
+ it { @return.should be_kind_of(DataMapper::Query) }
2571
+
2572
+ it 'should copy the Query' do
2573
+ @return.should_not equal(@original)
2574
+ end
2575
+
2576
+ # TODO: push this into dup spec
2577
+ it 'should not reference original order' do
2578
+ @return.order.should_not equal(@original.order)
2579
+ end
2580
+
2581
+ it 'should have a reversed order' do
2582
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name], :desc) ]
2583
+ end
2584
+
2585
+ [ :repository, :model, :fields, :links, :conditions, :offset, :limit, :unique?, :add_reversed?, :reload? ].each do |attribute|
2586
+ it "should have an equivalent #{attribute}" do
2587
+ @return.send(attribute).should == @original.send(attribute)
2588
+ end
2589
+ end
2590
+ end
2591
+
2592
+ it { should respond_to(:reverse!) }
2593
+
2594
+ describe '#reverse!' do
2595
+ before :all do
2596
+ @return = @query.reverse!
2597
+ end
2598
+
2599
+ it { @return.should be_kind_of(DataMapper::Query) }
2600
+
2601
+ it { @return.should equal(@original) }
2602
+
2603
+ it 'should have a reversed order' do
2604
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name], :desc) ]
2605
+ end
2606
+ end
2607
+
2608
+ [ :slice, :[] ].each do |method|
2609
+ it { should respond_to(method) }
2610
+
2611
+ describe "##{method}" do
2612
+ describe 'with a positive offset' do
2613
+ before :all do
2614
+ @query = @query.update(:offset => 1, :limit => 2)
2615
+
2616
+ @return = @query.send(method, 1)
2617
+ end
2618
+
2619
+ it { @return.should be_kind_of(DataMapper::Query) }
2620
+
2621
+ it 'should not return self' do
2622
+ @return.should_not equal(@original)
2623
+ end
2624
+
2625
+ it 'should update the offset to be relative to the original offset' do
2626
+ @return.offset.should == 2
2627
+ end
2628
+
2629
+ it 'should update the limit to 1' do
2630
+ @return.limit.should == 1
2631
+ end
2632
+ end
2633
+
2634
+ describe 'with a positive offset and length' do
2635
+ before :all do
2636
+ @query = @query.update(:offset => 1, :limit => 2)
2637
+
2638
+ @return = @query.send(method, 1, 1)
2639
+ end
2640
+
2641
+ it { @return.should be_kind_of(DataMapper::Query) }
2642
+
2643
+ it 'should not return self' do
2644
+ @return.should_not equal(@original)
2645
+ end
2646
+
2647
+ it 'should update the offset to be relative to the original offset' do
2648
+ @return.offset.should == 2
2649
+ end
2650
+
2651
+ it 'should update the limit' do
2652
+ @return.limit.should == 1
2653
+ end
2654
+ end
2655
+
2656
+ describe 'with a positive range' do
2657
+ before :all do
2658
+ @query = @query.update(:offset => 1, :limit => 3)
2659
+
2660
+ @return = @query.send(method, 1..2)
2661
+ end
2662
+
2663
+ it { @return.should be_kind_of(DataMapper::Query) }
2664
+
2665
+ it 'should not return self' do
2666
+ @return.should_not equal(@original)
2667
+ end
2668
+
2669
+ it 'should update the offset to be relative to the original offset' do
2670
+ @return.offset.should == 2
2671
+ end
2672
+
2673
+ it 'should update the limit' do
2674
+ @return.limit.should == 2
2675
+ end
2676
+ end
2677
+
2678
+ describe 'with a negative offset' do
2679
+ before :all do
2680
+ @query = @query.update(:offset => 1, :limit => 2)
2681
+
2682
+ @return = @query.send(method, -1)
2683
+ end
2684
+
2685
+ it { @return.should be_kind_of(DataMapper::Query) }
2686
+
2687
+ it 'should not return self' do
2688
+ @return.should_not equal(@original)
2689
+ end
2690
+
2691
+ it 'should update the offset to be relative to the original offset' do
2692
+ pending "TODO: update Query##{method} handle negative offset" do
2693
+ @return.offset.should == 2
2694
+ end
2695
+ end
2696
+
2697
+ it 'should update the limit to 1' do
2698
+ @return.limit.should == 1
2699
+ end
2700
+ end
2701
+
2702
+ describe 'with a negative offset and length' do
2703
+ before :all do
2704
+ @query = @query.update(:offset => 1, :limit => 2)
2705
+
2706
+ @return = @query.send(method, -1, 1)
2707
+ end
2708
+
2709
+ it { @return.should be_kind_of(DataMapper::Query) }
2710
+
2711
+ it 'should not return self' do
2712
+ @return.should_not equal(@original)
2713
+ end
2714
+
2715
+ it 'should update the offset to be relative to the original offset' do
2716
+ pending "TODO: update Query##{method} handle negative offset and length" do
2717
+ @return.offset.should == 2
2718
+ end
2719
+ end
2720
+
2721
+ it 'should update the limit to 1' do
2722
+ @return.limit.should == 1
2723
+ end
2724
+ end
2725
+
2726
+ describe 'with a negative range' do
2727
+ before :all do
2728
+ @query = @query.update(:offset => 1, :limit => 3)
2729
+
2730
+ rescue_if "TODO: update Query##{method} handle negative range" do
2731
+ @return = @query.send(method, -2..-1)
2732
+ end
2733
+ end
2734
+
2735
+ before do
2736
+ pending_if "TODO: update Query##{method} handle negative range", !defined?(@return)
2737
+ end
2738
+
2739
+ it { @return.should be_kind_of(DataMapper::Query) }
2740
+
2741
+ it 'should not return self' do
2742
+ @return.should_not equal(@original)
2743
+ end
2744
+
2745
+ it 'should update the offset to be relative to the original offset' do
2746
+ @return.offset.should == 2
2747
+ end
2748
+
2749
+ it 'should update the limit to 1' do
2750
+ @return.limit.should == 2
2751
+ end
2752
+ end
2753
+
2754
+ describe 'with an offset not within range' do
2755
+ before :all do
2756
+ @query = @query.update(:offset => 1, :limit => 3)
2757
+ end
2758
+
2759
+ it 'should raise an exception' do
2760
+ lambda {
2761
+ @query.send(method, 12)
2762
+ }.should raise_error(RangeError, 'offset 12 and limit 1 are outside allowed range')
2763
+ end
2764
+ end
2765
+
2766
+ describe 'with an offset and length not within range' do
2767
+ before :all do
2768
+ @query = @query.update(:offset => 1, :limit => 3)
2769
+ end
2770
+
2771
+ it 'should raise an exception' do
2772
+ lambda {
2773
+ @query.send(method, 12, 1)
2774
+ }.should raise_error(RangeError, 'offset 12 and limit 1 are outside allowed range')
2775
+ end
2776
+ end
2777
+
2778
+ describe 'with a range not within range' do
2779
+ before :all do
2780
+ @query = @query.update(:offset => 1, :limit => 3)
2781
+ end
2782
+
2783
+ it 'should raise an exception' do
2784
+ lambda {
2785
+ @query.send(method, 12..12)
2786
+ }.should raise_error(RangeError, 'offset 12 and limit 1 are outside allowed range')
2787
+ end
2788
+ end
2789
+
2790
+ describe 'with invalid arguments' do
2791
+ it 'should raise an exception' do
2792
+ lambda {
2793
+ @query.send(method, 'invalid')
2794
+ }.should raise_error(ArgumentError, 'arguments may be 1 or 2 Integers, or 1 Range object, was: ["invalid"]')
2795
+ end
2796
+ end
2797
+ end
2798
+ end
2799
+
2800
+ it { should respond_to(:slice!) }
2801
+
2802
+ describe '#slice!' do
2803
+ describe 'with a positive offset' do
2804
+ before :all do
2805
+ @query = @query.update(:offset => 1, :limit => 2)
2806
+
2807
+ @return = @query.slice!(1)
2808
+ end
2809
+
2810
+ it { @return.should be_kind_of(DataMapper::Query) }
2811
+
2812
+ it 'should return self' do
2813
+ @return.should equal(@original)
2814
+ end
2815
+
2816
+ it 'should update the offset to be relative to the original offset' do
2817
+ @return.offset.should == 2
2818
+ end
2819
+
2820
+ it 'should update the limit to 1' do
2821
+ @return.limit.should == 1
2822
+ end
2823
+ end
2824
+
2825
+ describe 'with a positive offset and length' do
2826
+ before :all do
2827
+ @query = @query.update(:offset => 1, :limit => 2)
2828
+
2829
+ @return = @query.slice!(1, 1)
2830
+ end
2831
+
2832
+ it { @return.should be_kind_of(DataMapper::Query) }
2833
+
2834
+ it 'should return self' do
2835
+ @return.should equal(@original)
2836
+ end
2837
+
2838
+ it 'should update the offset to be relative to the original offset' do
2839
+ @return.offset.should == 2
2840
+ end
2841
+
2842
+ it 'should update the limit' do
2843
+ @return.limit.should == 1
2844
+ end
2845
+ end
2846
+
2847
+ describe 'with a positive range' do
2848
+ before :all do
2849
+ @query = @query.update(:offset => 1, :limit => 3)
2850
+
2851
+ @return = @query.slice!(1..2)
2852
+ end
2853
+
2854
+ it { @return.should be_kind_of(DataMapper::Query) }
2855
+
2856
+ it 'should return self' do
2857
+ @return.should equal(@original)
2858
+ end
2859
+
2860
+ it 'should update the offset to be relative to the original offset' do
2861
+ @return.offset.should == 2
2862
+ end
2863
+
2864
+ it 'should update the limit' do
2865
+ @return.limit.should == 2
2866
+ end
2867
+ end
2868
+
2869
+ describe 'with a negative offset' do
2870
+ before :all do
2871
+ @query = @query.update(:offset => 1, :limit => 2)
2872
+
2873
+ @return = @query.slice!(-1)
2874
+ end
2875
+
2876
+ it { @return.should be_kind_of(DataMapper::Query) }
2877
+
2878
+ it 'should return self' do
2879
+ @return.should equal(@original)
2880
+ end
2881
+
2882
+ it 'should update the offset to be relative to the original offset' do
2883
+ pending 'TODO: update Query#slice! handle negative offset' do
2884
+ @return.offset.should == 2
2885
+ end
2886
+ end
2887
+
2888
+ it 'should update the limit to 1' do
2889
+ @return.limit.should == 1
2890
+ end
2891
+ end
2892
+
2893
+ describe 'with a negative offset and length' do
2894
+ before :all do
2895
+ @query = @query.update(:offset => 1, :limit => 2)
2896
+
2897
+ @return = @query.slice!(-1, 1)
2898
+ end
2899
+
2900
+ it { @return.should be_kind_of(DataMapper::Query) }
2901
+
2902
+ it 'should return self' do
2903
+ @return.should equal(@original)
2904
+ end
2905
+
2906
+ it 'should update the offset to be relative to the original offset' do
2907
+ pending 'TODO: update Query#slice! handle negative offset and length' do
2908
+ @return.offset.should == 2
2909
+ end
2910
+ end
2911
+
2912
+ it 'should update the limit to 1' do
2913
+ @return.limit.should == 1
2914
+ end
2915
+ end
2916
+
2917
+ describe 'with a negative range' do
2918
+ before :all do
2919
+ @query = @query.update(:offset => 1, :limit => 3)
2920
+
2921
+ rescue_if 'TODO: update Query#slice! handle negative range' do
2922
+ @return = @query.slice!(-2..-1)
2923
+ end
2924
+ end
2925
+
2926
+ before do
2927
+ pending_if 'TODO: update Query#slice! handle negative range', !defined?(@return)
2928
+ end
2929
+
2930
+ it { @return.should be_kind_of(DataMapper::Query) }
2931
+
2932
+ it 'should return self' do
2933
+ @return.should equal(@original)
2934
+ end
2935
+
2936
+ it 'should update the offset to be relative to the original offset' do
2937
+ @return.offset.should == 2
2938
+ end
2939
+
2940
+ it 'should update the limit to 1' do
2941
+ @return.limit.should == 2
2942
+ end
2943
+ end
2944
+
2945
+ describe 'with an offset not within range' do
2946
+ before :all do
2947
+ @query = @query.update(:offset => 1, :limit => 3)
2948
+ end
2949
+
2950
+ it 'should raise an exception' do
2951
+ lambda {
2952
+ @query.slice!(12)
2953
+ }.should raise_error(RangeError, 'offset 12 and limit 1 are outside allowed range')
2954
+ end
2955
+ end
2956
+
2957
+ describe 'with an offset and length not within range' do
2958
+ before :all do
2959
+ @query = @query.update(:offset => 1, :limit => 3)
2960
+ end
2961
+
2962
+ it 'should raise an exception' do
2963
+ lambda {
2964
+ @query.slice!(12, 1)
2965
+ }.should raise_error(RangeError, 'offset 12 and limit 1 are outside allowed range')
2966
+ end
2967
+ end
2968
+
2969
+ describe 'with a range not within range' do
2970
+ before :all do
2971
+ @query = @query.update(:offset => 1, :limit => 3)
2972
+ end
2973
+
2974
+ it 'should raise an exception' do
2975
+ lambda {
2976
+ @query.slice!(12..12)
2977
+ }.should raise_error(RangeError, 'offset 12 and limit 1 are outside allowed range')
2978
+ end
2979
+ end
2980
+
2981
+ describe 'with invalid arguments' do
2982
+ it 'should raise an exception' do
2983
+ lambda {
2984
+ @query.slice!('invalid')
2985
+ }.should raise_error(ArgumentError, 'arguments may be 1 or 2 Integers, or 1 Range object, was: ["invalid"]')
2986
+ end
2987
+ end
2988
+ end
2989
+
2990
+ it { should respond_to(:sort_records) }
2991
+
2992
+ describe '#sort_records' do
2993
+ supported_by :all do
2994
+ before :all do
2995
+ @john = { 'name' => 'John Doe', 'referrer_name' => nil }
2996
+ @sam = { 'name' => 'Sam Smoot', 'referrer_name' => nil }
2997
+ @dan = { 'name' => 'Dan Kubb', 'referrer_name' => 'Sam Smoot' }
2998
+
2999
+ @records = [ @john, @sam, @dan ]
3000
+
3001
+ @query.update(:order => [ :name ])
3002
+
3003
+ @return = @query.sort_records(@records)
3004
+ end
3005
+
3006
+ it 'should return Enumerable' do
3007
+ @return.should be_kind_of(Enumerable)
3008
+ end
3009
+
3010
+ it 'should not be the records provided' do
3011
+ @return.should_not equal(@records)
3012
+ end
3013
+
3014
+ it 'should return expected values' do
3015
+ @return.should == [ @dan, @john, @sam ]
3016
+ end
3017
+ end
3018
+ end
3019
+
3020
+ [ :union, :|, :+ ].each do |method|
3021
+ it { should respond_to(method) }
3022
+
3023
+ describe "##{method}" do
3024
+ supported_by :all do
3025
+ before :all do
3026
+ @key = @model.key(@repository.name)
3027
+
3028
+ @self_relationship = DataMapper::Associations::OneToMany::Relationship.new(
3029
+ :self,
3030
+ @model,
3031
+ @model,
3032
+ {
3033
+ :child_key => @key.map { |p| p.name },
3034
+ :parent_key => @key.map { |p| p.name },
3035
+ :child_repository_name => @repository.name,
3036
+ :parent_repository_name => @repository.name,
3037
+ }
3038
+ )
3039
+
3040
+ 10.times do |n|
3041
+ @model.create(:name => "#{@model} #{n}")
3042
+ end
3043
+ end
3044
+
3045
+ subject { @query.send(method, @other) }
3046
+
3047
+ describe 'with equivalent query' do
3048
+ before { @other = @query.dup }
3049
+
3050
+ it { should be_kind_of(DataMapper::Query) }
3051
+
3052
+ it { should_not equal(@query) }
3053
+
3054
+ it { should_not equal(@other) }
3055
+
3056
+ it { should == @query }
3057
+ end
3058
+
3059
+ describe 'with other matching everything' do
3060
+ before do
3061
+ @query = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
3062
+ @other = DataMapper::Query.new(@repository, @model)
3063
+ end
3064
+
3065
+ it { should be_kind_of(DataMapper::Query) }
3066
+
3067
+ it { should_not equal(@query) }
3068
+
3069
+ it { should_not equal(@other) }
3070
+
3071
+ it 'should match everything' do
3072
+ should == DataMapper::Query.new(@repository, @model)
3073
+ end
3074
+ end
3075
+
3076
+ describe 'with self matching everything' do
3077
+ before do
3078
+ @query = DataMapper::Query.new(@repository, @model)
3079
+ @other = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
3080
+ end
3081
+
3082
+ it { should be_kind_of(DataMapper::Query) }
3083
+
3084
+ it { should_not equal(@query) }
3085
+
3086
+ it { should_not equal(@other) }
3087
+
3088
+ it 'should match everything' do
3089
+ should == DataMapper::Query.new(@repository, @model)
3090
+ end
3091
+ end
3092
+
3093
+ describe 'with self having a limit' do
3094
+ before do
3095
+ @query = DataMapper::Query.new(@repository, @model, :limit => 5)
3096
+ @other = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
3097
+
3098
+ @expected = DataMapper::Query::Conditions::Operation.new(:or,
3099
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@query.merge(:fields => @key))),
3100
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb')
3101
+ )
3102
+ end
3103
+
3104
+ it { should_not equal(@query) }
3105
+
3106
+ it { should_not equal(@other) }
3107
+
3108
+ it 'should put each query into a subquery and OR them together' do
3109
+ subject.conditions.should == @expected
3110
+ end
3111
+ end
3112
+
3113
+ describe 'with other having a limit' do
3114
+ before do
3115
+ @query = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
3116
+ @other = DataMapper::Query.new(@repository, @model, :limit => 5)
3117
+
3118
+ @expected = DataMapper::Query::Conditions::Operation.new(:or,
3119
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb'),
3120
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@other.merge(:fields => @key)))
3121
+ )
3122
+ end
3123
+
3124
+ it { should_not equal(@query) }
3125
+
3126
+ it { should_not equal(@other) }
3127
+
3128
+ it 'should put each query into a subquery and OR them together' do
3129
+ subject.conditions.should == @expected
3130
+ end
3131
+ end
3132
+
3133
+ describe 'with self having an offset > 0' do
3134
+ before do
3135
+ @query = DataMapper::Query.new(@repository, @model, :offset => 5, :limit => 5)
3136
+ @other = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
3137
+
3138
+ @expected = DataMapper::Query::Conditions::Operation.new(:or,
3139
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@query.merge(:fields => @key))),
3140
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb')
3141
+ )
3142
+ end
3143
+
3144
+ it { should_not equal(@query) }
3145
+
3146
+ it { should_not equal(@other) }
3147
+
3148
+ it 'should put each query into a subquery and OR them together' do
3149
+ subject.conditions.should == @expected
3150
+ end
3151
+ end
3152
+
3153
+ describe 'with other having an offset > 0' do
3154
+ before do
3155
+ @query = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
3156
+ @other = DataMapper::Query.new(@repository, @model, :offset => 5, :limit => 5)
3157
+
3158
+ @expected = DataMapper::Query::Conditions::Operation.new(:or,
3159
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb'),
3160
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@other.merge(:fields => @key)))
3161
+ )
3162
+ end
3163
+
3164
+ it { should_not equal(@query) }
3165
+
3166
+ it { should_not equal(@other) }
3167
+
3168
+ it 'should put each query into a subquery and OR them together' do
3169
+ subject.conditions.should == @expected
3170
+ end
3171
+ end
3172
+
3173
+ describe 'with self having links' do
3174
+ before :all do
3175
+ @do_adapter = defined?(DataMapper::Adapters::DataObjectsAdapter) && @adapter.kind_of?(DataMapper::Adapters::DataObjectsAdapter)
3176
+ end
3177
+
3178
+ before do
3179
+ @query = DataMapper::Query.new(@repository, @model, :links => [ :referrer ])
3180
+ @other = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
3181
+
3182
+ @expected = DataMapper::Query::Conditions::Operation.new(:or,
3183
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@query.merge(:fields => @key))),
3184
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb')
3185
+ )
3186
+ end
3187
+
3188
+ it { should_not equal(@query) }
3189
+
3190
+ it { should_not equal(@other) }
3191
+
3192
+ it 'should put each query into a subquery and OR them together' do
3193
+ subject.conditions.should == @expected
3194
+ end
3195
+ end
3196
+
3197
+ describe 'with other having links' do
3198
+ before :all do
3199
+ @do_adapter = defined?(DataMapper::Adapters::DataObjectsAdapter) && @adapter.kind_of?(DataMapper::Adapters::DataObjectsAdapter)
3200
+ end
3201
+
3202
+ before do
3203
+ @query = DataMapper::Query.new(@repository, @model, :name => 'Dan Kubb')
3204
+ @other = DataMapper::Query.new(@repository, @model, :links => [ :referrer ])
3205
+
3206
+ @expected = DataMapper::Query::Conditions::Operation.new(:or,
3207
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:name], 'Dan Kubb'),
3208
+ DataMapper::Query::Conditions::Comparison.new(:in, @self_relationship, @model.all(@other.merge(:fields => @key)))
3209
+ )
3210
+ end
3211
+
3212
+ it { should_not equal(@query) }
3213
+
3214
+ it { should_not equal(@other) }
3215
+
3216
+ it 'should put each query into a subquery and OR them together' do
3217
+ subject.conditions.should == @expected
3218
+ end
3219
+ end
3220
+
3221
+ describe 'with different conditions, no links/offset/limit' do
3222
+ before do
3223
+ property = @model.properties[:name]
3224
+
3225
+ @query = DataMapper::Query.new(@repository, @model, property.name => 'Dan Kubb')
3226
+ @other = DataMapper::Query.new(@repository, @model, property.name => 'John Doe')
3227
+
3228
+ @query.conditions.should_not == @other.conditions
3229
+
3230
+ @expected = DataMapper::Query::Conditions::Operation.new(:or,
3231
+ DataMapper::Query::Conditions::Comparison.new(:eql, property, 'Dan Kubb'),
3232
+ DataMapper::Query::Conditions::Comparison.new(:eql, property, 'John Doe')
3233
+ )
3234
+ end
3235
+
3236
+ it { should be_kind_of(DataMapper::Query) }
3237
+
3238
+ it { should_not equal(@query) }
3239
+
3240
+ it { should_not equal(@other) }
3241
+
3242
+ it 'should OR the conditions together' do
3243
+ subject.conditions.should == @expected
3244
+ end
3245
+ end
3246
+
3247
+ describe 'with different fields' do
3248
+ before do
3249
+ @property = @model.properties[:name]
3250
+
3251
+ @query = DataMapper::Query.new(@repository, @model)
3252
+ @other = DataMapper::Query.new(@repository, @model, :fields => [ @property ])
3253
+
3254
+ @query.fields.should_not == @other.fields
3255
+ end
3256
+
3257
+ it { should be_kind_of(DataMapper::Query) }
3258
+
3259
+ it { should_not equal(@query) }
3260
+
3261
+ it { should_not equal(@other) }
3262
+
3263
+ it { subject.conditions.should be_nil }
3264
+
3265
+ it 'should use the other fields' do
3266
+ subject.fields.should == [ @property ]
3267
+ end
3268
+ end
3269
+
3270
+ describe 'with different order' do
3271
+ before do
3272
+ @property = @model.properties[:name]
3273
+
3274
+ @query = DataMapper::Query.new(@repository, @model)
3275
+ @other = DataMapper::Query.new(@repository, @model, :order => [ DataMapper::Query::Direction.new(@property, :desc) ])
3276
+
3277
+ @query.order.should_not == @other.order
3278
+ end
3279
+
3280
+ it { should be_kind_of(DataMapper::Query) }
3281
+
3282
+ it { should_not equal(@query) }
3283
+
3284
+ it { should_not equal(@other) }
3285
+
3286
+ it { subject.conditions.should be_nil }
3287
+
3288
+ it 'should use the other order' do
3289
+ subject.order.should == [ DataMapper::Query::Direction.new(@property, :desc) ]
3290
+ end
3291
+ end
3292
+
3293
+ describe 'with different unique' do
3294
+ before do
3295
+ @query = DataMapper::Query.new(@repository, @model)
3296
+ @other = DataMapper::Query.new(@repository, @model, :unique => true)
3297
+
3298
+ @query.unique?.should_not == @other.unique?
3299
+ end
3300
+
3301
+ it { should be_kind_of(DataMapper::Query) }
3302
+
3303
+ it { should_not equal(@query) }
3304
+
3305
+ it { should_not equal(@other) }
3306
+
3307
+ it { subject.conditions.should be_nil }
3308
+
3309
+ it 'should use the other unique' do
3310
+ subject.unique?.should == true
3311
+ end
3312
+ end
3313
+
3314
+ describe 'with different add_reversed' do
3315
+ before do
3316
+ @query = DataMapper::Query.new(@repository, @model)
3317
+ @other = DataMapper::Query.new(@repository, @model, :add_reversed => true)
3318
+
3319
+ @query.add_reversed?.should_not == @other.add_reversed?
3320
+ end
3321
+
3322
+ it { should be_kind_of(DataMapper::Query) }
3323
+
3324
+ it { should_not equal(@query) }
3325
+
3326
+ it { should_not equal(@other) }
3327
+
3328
+ it { subject.conditions.should be_nil }
3329
+
3330
+ it 'should use the other add_reversed' do
3331
+ subject.add_reversed?.should == true
3332
+ end
3333
+ end
3334
+
3335
+ describe 'with different reload' do
3336
+ before do
3337
+ @query = DataMapper::Query.new(@repository, @model)
3338
+ @other = DataMapper::Query.new(@repository, @model, :reload => true)
3339
+
3340
+ @query.reload?.should_not == @other.reload?
3341
+ end
3342
+
3343
+ it { should be_kind_of(DataMapper::Query) }
3344
+
3345
+ it { should_not equal(@query) }
3346
+
3347
+ it { should_not equal(@other) }
3348
+
3349
+ it { subject.conditions.should be_nil }
3350
+
3351
+ it 'should use the other reload' do
3352
+ subject.reload?.should == true
3353
+ end
3354
+ end
3355
+
3356
+ describe 'with different models' do
3357
+ before { @other = DataMapper::Query.new(@repository, Other) }
3358
+
3359
+ it { method(:subject).should raise_error(ArgumentError) }
3360
+ end
3361
+ end
3362
+ end
3363
+ end
3364
+
3365
+ it { should respond_to(:unique?) }
3366
+
3367
+ describe '#unique?' do
3368
+ describe 'when the query is unique' do
3369
+ before :all do
3370
+ @query.update(:unique => true)
3371
+ end
3372
+
3373
+ it { should be_unique }
3374
+ end
3375
+
3376
+ describe 'when the query is not unique' do
3377
+ it { should_not be_unique }
3378
+ end
3379
+
3380
+ describe 'when links are provided, but unique is not specified' do
3381
+ before :all do
3382
+ @query.should_not be_unique
3383
+ @query.update(:links => [ :referrer ])
3384
+ end
3385
+
3386
+ it { should be_unique }
3387
+ end
3388
+
3389
+ describe 'when links are provided, but unique is false' do
3390
+ before :all do
3391
+ @query.should_not be_unique
3392
+ @query.update(:links => [ :referrer ], :unique => false)
3393
+ end
3394
+
3395
+ it { should_not be_unique }
3396
+ end
3397
+ end
3398
+
3399
+ it { should respond_to(:update) }
3400
+
3401
+ describe '#update' do
3402
+ describe 'with a Query' do
3403
+ describe 'that is equivalent' do
3404
+ before :all do
3405
+ @other = DataMapper::Query.new(@repository, @model, @options)
3406
+
3407
+ @return = @query.update(@other)
3408
+ end
3409
+
3410
+ it { @return.should be_kind_of(DataMapper::Query) }
3411
+
3412
+ it { @return.should equal(@original) }
3413
+ end
3414
+
3415
+ describe 'that has conditions set' do
3416
+ before :all do
3417
+ @and_operation = DataMapper::Query::Conditions::Operation.new(:and)
3418
+ @or_operation = DataMapper::Query::Conditions::Operation.new(:or)
3419
+
3420
+ @and_operation << DataMapper::Query::Conditions::Comparison.new(:eql, User.properties[:name], 'Dan Kubb')
3421
+ @and_operation << DataMapper::Query::Conditions::Comparison.new(:eql, User.properties[:citizenship],'Canada')
3422
+
3423
+ @or_operation << DataMapper::Query::Conditions::Comparison.new(:eql, User.properties[:name], 'Ted Han')
3424
+ @or_operation << DataMapper::Query::Conditions::Comparison.new(:eql, User.properties[:citizenship], 'USA')
3425
+
3426
+ @query_one = DataMapper::Query.new(@repository, @model, :conditions => @and_operation)
3427
+ @query_two = DataMapper::Query.new(@repository, @model, :conditions => @or_operation)
3428
+
3429
+ @conditions = @query_one.merge(@query_two).conditions
3430
+ end
3431
+
3432
+ it { @conditions.should == DataMapper::Query::Conditions::Operation.new(:and, @and_operation, @or_operation) }
3433
+ end
3434
+
3435
+ describe 'that is for an ancestor model' do
3436
+ before :all do
3437
+ class ::Contact < User; end
3438
+
3439
+ @query = DataMapper::Query.new(@repository, Contact, @options)
3440
+ @original = @query
3441
+
3442
+ @other = DataMapper::Query.new(@repository, User, @options)
3443
+
3444
+ @return = @query.update(@other)
3445
+ end
3446
+
3447
+ it { @return.should be_kind_of(DataMapper::Query) }
3448
+
3449
+ it { @return.should equal(@original) }
3450
+ end
3451
+
3452
+ describe 'using a different repository' do
3453
+ it 'should raise an exception' do
3454
+ lambda {
3455
+ @query.update(DataMapper::Query.new(DataMapper::Repository.new(:other), User))
3456
+ }.should raise_error(ArgumentError, '+other+ DataMapper::Query must be for the default repository, not other')
3457
+ end
3458
+ end
3459
+
3460
+ describe 'using a different model' do
3461
+ before :all do
3462
+ class ::Clone
3463
+ include DataMapper::Resource
3464
+
3465
+ property :name, String, :key => true
3466
+ end
3467
+ end
3468
+
3469
+ it 'should raise an exception' do
3470
+ lambda {
3471
+ @query.update(DataMapper::Query.new(@repository, Clone))
3472
+ }.should raise_error(ArgumentError, '+other+ DataMapper::Query must be for the User model, not Clone')
3473
+ end
3474
+ end
3475
+
3476
+ describe 'using different options' do
3477
+ before :all do
3478
+ @other = DataMapper::Query.new(@repository, @model, @options.update(@other_options))
3479
+
3480
+ @return = @query.update(@other)
3481
+ end
3482
+
3483
+ it { @return.should be_kind_of(DataMapper::Query) }
3484
+
3485
+ it { @return.should equal(@original) }
3486
+
3487
+ it 'should update the fields' do
3488
+ @return.fields.should == @options[:fields]
3489
+ end
3490
+
3491
+ it 'should update the links' do
3492
+ @return.links.should == @options[:links]
3493
+ end
3494
+
3495
+ it 'should update the conditions' do
3496
+ @return.conditions.should == DataMapper::Query::Conditions::Operation.new(:and, [ 'name = ?', [ 'Dan Kubb' ] ])
3497
+ end
3498
+
3499
+ it 'should update the offset' do
3500
+ @return.offset.should == @options[:offset]
3501
+ end
3502
+
3503
+ it 'should update the limit' do
3504
+ @return.limit.should == @options[:limit]
3505
+ end
3506
+
3507
+ it 'should update the order' do
3508
+ @return.order.should == @options[:order]
3509
+ end
3510
+
3511
+ it 'should update the unique' do
3512
+ @return.unique?.should == @options[:unique]
3513
+ end
3514
+
3515
+ it 'should update the add_reversed' do
3516
+ @return.add_reversed?.should == @options[:add_reversed]
3517
+ end
3518
+
3519
+ it 'should update the reload' do
3520
+ @return.reload?.should == @options[:reload]
3521
+ end
3522
+ end
3523
+
3524
+ describe 'using extra options' do
3525
+ before :all do
3526
+ @options.update(:name => 'Dan Kubb')
3527
+ @other = DataMapper::Query.new(@repository, @model, @options)
3528
+
3529
+ @return = @query.update(@other)
3530
+ end
3531
+
3532
+ it { @return.should be_kind_of(DataMapper::Query) }
3533
+
3534
+ it { @return.should equal(@original) }
3535
+
3536
+ it 'should update the conditions' do
3537
+ @return.conditions.should ==
3538
+ DataMapper::Query::Conditions::Operation.new(
3539
+ :and,
3540
+ DataMapper::Query::Conditions::Comparison.new(
3541
+ :eql,
3542
+ @model.properties[:name],
3543
+ @options[:name]
3544
+ )
3545
+ )
3546
+ end
3547
+ end
3548
+ end
3549
+
3550
+ describe 'with a Hash' do
3551
+ describe 'that is empty' do
3552
+ before :all do
3553
+ @copy = @query.dup
3554
+ @return = @query.update({})
3555
+ end
3556
+
3557
+ it { @return.should be_kind_of(DataMapper::Query) }
3558
+
3559
+ it { @return.should equal(@original) }
3560
+
3561
+ it 'should not change the Query' do
3562
+ @return.should == @copy
3563
+ end
3564
+ end
3565
+
3566
+ describe 'using different options' do
3567
+ before :all do
3568
+ @return = @query.update(@other_options)
3569
+ end
3570
+
3571
+ it { @return.should be_kind_of(DataMapper::Query) }
3572
+
3573
+ it { @return.should equal(@original) }
3574
+
3575
+ it 'should update the fields' do
3576
+ @return.fields.should == @other_options[:fields]
3577
+ end
3578
+
3579
+ it 'should update the links' do
3580
+ @return.links.should == @other_options[:links]
3581
+ end
3582
+
3583
+ it 'should update the conditions' do
3584
+ @return.conditions.should == DataMapper::Query::Conditions::Operation.new(:and, [ 'name = ?', [ 'Dan Kubb' ] ])
3585
+ end
3586
+
3587
+ it 'should update the offset' do
3588
+ @return.offset.should == @other_options[:offset]
3589
+ end
3590
+
3591
+ it 'should update the limit' do
3592
+ @return.limit.should == @other_options[:limit]
3593
+ end
3594
+
3595
+ it 'should update the order' do
3596
+ @return.order.should == @other_options[:order]
3597
+ end
3598
+
3599
+ it 'should update the unique' do
3600
+ @return.unique?.should == @other_options[:unique]
3601
+ end
3602
+
3603
+ it 'should update the add_reversed' do
3604
+ @return.add_reversed?.should == @other_options[:add_reversed]
3605
+ end
3606
+
3607
+ it 'should update the reload' do
3608
+ @return.reload?.should == @other_options[:reload]
3609
+ end
3610
+ end
3611
+
3612
+ describe 'using extra options' do
3613
+ before :all do
3614
+ @options = { :name => 'Dan Kubb' }
3615
+
3616
+ @return = @query.update(@options)
3617
+ end
3618
+
3619
+ it { @return.should be_kind_of(DataMapper::Query) }
3620
+
3621
+ it { @return.should equal(@original) }
3622
+
3623
+ it 'should update the conditions' do
3624
+ @return.conditions.should == DataMapper::Query::Conditions::Operation.new(
3625
+ :and,
3626
+ DataMapper::Query::Conditions::Comparison.new(
3627
+ :eql,
3628
+ @model.properties[:name],
3629
+ @options[:name]
3630
+ )
3631
+ )
3632
+ end
3633
+ end
3634
+
3635
+ describe 'using raw conditions' do
3636
+ before :all do
3637
+ @query.update(:conditions => [ 'name IS NOT NULL' ])
3638
+
3639
+ @return = @query.update(:conditions => [ 'name = ?', 'Dan Kubb' ])
3640
+ end
3641
+
3642
+ it { @return.should be_kind_of(DataMapper::Query) }
3643
+
3644
+ it { @return.should equal(@original) }
3645
+
3646
+ it 'should update the conditions' do
3647
+ @return.conditions.should == DataMapper::Query::Conditions::Operation.new(
3648
+ :and,
3649
+ [ 'name IS NOT NULL' ],
3650
+ [ 'name = ?', [ 'Dan Kubb' ] ]
3651
+ )
3652
+ end
3653
+ end
3654
+
3655
+ describe 'with the String key mapping to a Query::Path' do
3656
+ before :all do
3657
+ @query.links.should be_empty
3658
+
3659
+ @options = { 'grandparents.name' => 'Dan Kubb' }
3660
+
3661
+ @return = @query.update(@options)
3662
+ end
3663
+
3664
+ it { @return.should be_kind_of(DataMapper::Query) }
3665
+
3666
+ it 'should not set the conditions' do
3667
+ pending do
3668
+ @return.conditions.should be_nil
3669
+ end
3670
+ end
3671
+
3672
+ it 'should set the links' do
3673
+ @return.links.should == [ @model.relationships[:referrals], @model.relationships[:referrer] ]
3674
+ end
3675
+
3676
+ it 'should be valid' do
3677
+ @return.should be_valid
3678
+ end
3679
+ end
3680
+ end
3681
+ end
3682
+ end