ghost_dm-core 1.3.0.beta

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