ghost_dm-core 1.3.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. data/.autotest +29 -0
  2. data/.document +5 -0
  3. data/.gitignore +35 -0
  4. data/.yardopts +1 -0
  5. data/Gemfile +65 -0
  6. data/LICENSE +20 -0
  7. data/README.md +269 -0
  8. data/Rakefile +4 -0
  9. data/dm-core.gemspec +24 -0
  10. data/lib/dm-core.rb +292 -0
  11. data/lib/dm-core/adapters.rb +222 -0
  12. data/lib/dm-core/adapters/abstract_adapter.rb +237 -0
  13. data/lib/dm-core/adapters/in_memory_adapter.rb +113 -0
  14. data/lib/dm-core/associations/many_to_many.rb +499 -0
  15. data/lib/dm-core/associations/many_to_one.rb +290 -0
  16. data/lib/dm-core/associations/one_to_many.rb +348 -0
  17. data/lib/dm-core/associations/one_to_one.rb +86 -0
  18. data/lib/dm-core/associations/relationship.rb +663 -0
  19. data/lib/dm-core/backwards.rb +13 -0
  20. data/lib/dm-core/collection.rb +1515 -0
  21. data/lib/dm-core/core_ext/kernel.rb +23 -0
  22. data/lib/dm-core/core_ext/pathname.rb +6 -0
  23. data/lib/dm-core/core_ext/symbol.rb +10 -0
  24. data/lib/dm-core/identity_map.rb +7 -0
  25. data/lib/dm-core/model.rb +874 -0
  26. data/lib/dm-core/model/hook.rb +103 -0
  27. data/lib/dm-core/model/is.rb +32 -0
  28. data/lib/dm-core/model/property.rb +249 -0
  29. data/lib/dm-core/model/relationship.rb +378 -0
  30. data/lib/dm-core/model/scope.rb +89 -0
  31. data/lib/dm-core/property.rb +866 -0
  32. data/lib/dm-core/property/binary.rb +21 -0
  33. data/lib/dm-core/property/boolean.rb +20 -0
  34. data/lib/dm-core/property/class.rb +17 -0
  35. data/lib/dm-core/property/date.rb +10 -0
  36. data/lib/dm-core/property/date_time.rb +10 -0
  37. data/lib/dm-core/property/decimal.rb +36 -0
  38. data/lib/dm-core/property/discriminator.rb +44 -0
  39. data/lib/dm-core/property/float.rb +16 -0
  40. data/lib/dm-core/property/integer.rb +22 -0
  41. data/lib/dm-core/property/invalid_value_error.rb +22 -0
  42. data/lib/dm-core/property/lookup.rb +27 -0
  43. data/lib/dm-core/property/numeric.rb +38 -0
  44. data/lib/dm-core/property/object.rb +34 -0
  45. data/lib/dm-core/property/serial.rb +14 -0
  46. data/lib/dm-core/property/string.rb +38 -0
  47. data/lib/dm-core/property/text.rb +9 -0
  48. data/lib/dm-core/property/time.rb +10 -0
  49. data/lib/dm-core/property_set.rb +177 -0
  50. data/lib/dm-core/query.rb +1366 -0
  51. data/lib/dm-core/query/conditions/comparison.rb +911 -0
  52. data/lib/dm-core/query/conditions/operation.rb +721 -0
  53. data/lib/dm-core/query/direction.rb +36 -0
  54. data/lib/dm-core/query/operator.rb +35 -0
  55. data/lib/dm-core/query/path.rb +114 -0
  56. data/lib/dm-core/query/sort.rb +39 -0
  57. data/lib/dm-core/relationship_set.rb +72 -0
  58. data/lib/dm-core/repository.rb +226 -0
  59. data/lib/dm-core/resource.rb +1214 -0
  60. data/lib/dm-core/resource/persistence_state.rb +75 -0
  61. data/lib/dm-core/resource/persistence_state/clean.rb +40 -0
  62. data/lib/dm-core/resource/persistence_state/deleted.rb +30 -0
  63. data/lib/dm-core/resource/persistence_state/dirty.rb +96 -0
  64. data/lib/dm-core/resource/persistence_state/immutable.rb +34 -0
  65. data/lib/dm-core/resource/persistence_state/persisted.rb +29 -0
  66. data/lib/dm-core/resource/persistence_state/transient.rb +80 -0
  67. data/lib/dm-core/spec/lib/adapter_helpers.rb +64 -0
  68. data/lib/dm-core/spec/lib/collection_helpers.rb +21 -0
  69. data/lib/dm-core/spec/lib/counter_adapter.rb +38 -0
  70. data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
  71. data/lib/dm-core/spec/lib/spec_helper.rb +74 -0
  72. data/lib/dm-core/spec/setup.rb +174 -0
  73. data/lib/dm-core/spec/shared/adapter_spec.rb +341 -0
  74. data/lib/dm-core/spec/shared/public/property_spec.rb +229 -0
  75. data/lib/dm-core/spec/shared/resource_spec.rb +1232 -0
  76. data/lib/dm-core/spec/shared/sel_spec.rb +111 -0
  77. data/lib/dm-core/spec/shared/semipublic/property_spec.rb +176 -0
  78. data/lib/dm-core/spec/shared/semipublic/query/conditions/abstract_comparison_spec.rb +261 -0
  79. data/lib/dm-core/support/assertions.rb +8 -0
  80. data/lib/dm-core/support/chainable.rb +18 -0
  81. data/lib/dm-core/support/deprecate.rb +12 -0
  82. data/lib/dm-core/support/descendant_set.rb +89 -0
  83. data/lib/dm-core/support/equalizer.rb +48 -0
  84. data/lib/dm-core/support/ext/array.rb +22 -0
  85. data/lib/dm-core/support/ext/blank.rb +25 -0
  86. data/lib/dm-core/support/ext/hash.rb +67 -0
  87. data/lib/dm-core/support/ext/module.rb +47 -0
  88. data/lib/dm-core/support/ext/object.rb +57 -0
  89. data/lib/dm-core/support/ext/string.rb +24 -0
  90. data/lib/dm-core/support/ext/try_dup.rb +12 -0
  91. data/lib/dm-core/support/hook.rb +405 -0
  92. data/lib/dm-core/support/inflections.rb +60 -0
  93. data/lib/dm-core/support/inflector/inflections.rb +211 -0
  94. data/lib/dm-core/support/inflector/methods.rb +151 -0
  95. data/lib/dm-core/support/lazy_array.rb +451 -0
  96. data/lib/dm-core/support/local_object_space.rb +13 -0
  97. data/lib/dm-core/support/logger.rb +201 -0
  98. data/lib/dm-core/support/mash.rb +176 -0
  99. data/lib/dm-core/support/naming_conventions.rb +90 -0
  100. data/lib/dm-core/support/ordered_set.rb +380 -0
  101. data/lib/dm-core/support/subject.rb +33 -0
  102. data/lib/dm-core/support/subject_set.rb +250 -0
  103. data/lib/dm-core/version.rb +3 -0
  104. data/script/performance.rb +275 -0
  105. data/script/profile.rb +218 -0
  106. data/spec/lib/rspec_immediate_feedback_formatter.rb +54 -0
  107. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +68 -0
  108. data/spec/public/associations/many_to_many_spec.rb +197 -0
  109. data/spec/public/associations/many_to_one_spec.rb +83 -0
  110. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +40 -0
  111. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  112. data/spec/public/associations/one_to_many_spec.rb +81 -0
  113. data/spec/public/associations/one_to_one_spec.rb +176 -0
  114. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +46 -0
  115. data/spec/public/collection_spec.rb +69 -0
  116. data/spec/public/finalize_spec.rb +76 -0
  117. data/spec/public/model/hook_spec.rb +246 -0
  118. data/spec/public/model/property_spec.rb +88 -0
  119. data/spec/public/model/relationship_spec.rb +1040 -0
  120. data/spec/public/model_spec.rb +462 -0
  121. data/spec/public/property/binary_spec.rb +41 -0
  122. data/spec/public/property/boolean_spec.rb +22 -0
  123. data/spec/public/property/class_spec.rb +28 -0
  124. data/spec/public/property/date_spec.rb +22 -0
  125. data/spec/public/property/date_time_spec.rb +22 -0
  126. data/spec/public/property/decimal_spec.rb +23 -0
  127. data/spec/public/property/discriminator_spec.rb +135 -0
  128. data/spec/public/property/float_spec.rb +22 -0
  129. data/spec/public/property/integer_spec.rb +22 -0
  130. data/spec/public/property/object_spec.rb +107 -0
  131. data/spec/public/property/serial_spec.rb +22 -0
  132. data/spec/public/property/string_spec.rb +22 -0
  133. data/spec/public/property/text_spec.rb +63 -0
  134. data/spec/public/property/time_spec.rb +22 -0
  135. data/spec/public/property_spec.rb +341 -0
  136. data/spec/public/resource_spec.rb +288 -0
  137. data/spec/public/sel_spec.rb +53 -0
  138. data/spec/public/setup_spec.rb +145 -0
  139. data/spec/public/shared/association_collection_shared_spec.rb +309 -0
  140. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  141. data/spec/public/shared/collection_shared_spec.rb +1667 -0
  142. data/spec/public/shared/finder_shared_spec.rb +1629 -0
  143. data/spec/rcov.opts +6 -0
  144. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  145. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +13 -0
  146. data/spec/semipublic/associations/many_to_many_spec.rb +94 -0
  147. data/spec/semipublic/associations/many_to_one_spec.rb +63 -0
  148. data/spec/semipublic/associations/one_to_many_spec.rb +55 -0
  149. data/spec/semipublic/associations/one_to_one_spec.rb +53 -0
  150. data/spec/semipublic/associations/relationship_spec.rb +200 -0
  151. data/spec/semipublic/associations_spec.rb +177 -0
  152. data/spec/semipublic/collection_spec.rb +110 -0
  153. data/spec/semipublic/model_spec.rb +96 -0
  154. data/spec/semipublic/property/binary_spec.rb +13 -0
  155. data/spec/semipublic/property/boolean_spec.rb +47 -0
  156. data/spec/semipublic/property/class_spec.rb +33 -0
  157. data/spec/semipublic/property/date_spec.rb +43 -0
  158. data/spec/semipublic/property/date_time_spec.rb +46 -0
  159. data/spec/semipublic/property/decimal_spec.rb +83 -0
  160. data/spec/semipublic/property/discriminator_spec.rb +19 -0
  161. data/spec/semipublic/property/float_spec.rb +82 -0
  162. data/spec/semipublic/property/integer_spec.rb +82 -0
  163. data/spec/semipublic/property/lookup_spec.rb +29 -0
  164. data/spec/semipublic/property/serial_spec.rb +13 -0
  165. data/spec/semipublic/property/string_spec.rb +13 -0
  166. data/spec/semipublic/property/text_spec.rb +31 -0
  167. data/spec/semipublic/property/time_spec.rb +50 -0
  168. data/spec/semipublic/property_spec.rb +114 -0
  169. data/spec/semipublic/query/conditions/comparison_spec.rb +1501 -0
  170. data/spec/semipublic/query/conditions/operation_spec.rb +1294 -0
  171. data/spec/semipublic/query/path_spec.rb +471 -0
  172. data/spec/semipublic/query_spec.rb +3682 -0
  173. data/spec/semipublic/resource/state/clean_spec.rb +88 -0
  174. data/spec/semipublic/resource/state/deleted_spec.rb +78 -0
  175. data/spec/semipublic/resource/state/dirty_spec.rb +162 -0
  176. data/spec/semipublic/resource/state/immutable_spec.rb +105 -0
  177. data/spec/semipublic/resource/state/transient_spec.rb +162 -0
  178. data/spec/semipublic/resource/state_spec.rb +230 -0
  179. data/spec/semipublic/resource_spec.rb +23 -0
  180. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  181. data/spec/semipublic/shared/resource_shared_spec.rb +199 -0
  182. data/spec/semipublic/shared/resource_state_shared_spec.rb +79 -0
  183. data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
  184. data/spec/spec.opts +5 -0
  185. data/spec/spec_helper.rb +38 -0
  186. data/spec/support/core_ext/hash.rb +10 -0
  187. data/spec/support/core_ext/inheritable_attributes.rb +46 -0
  188. data/spec/support/properties/huge_integer.rb +17 -0
  189. data/spec/unit/array_spec.rb +23 -0
  190. data/spec/unit/blank_spec.rb +73 -0
  191. data/spec/unit/data_mapper/ordered_set/append_spec.rb +26 -0
  192. data/spec/unit/data_mapper/ordered_set/clear_spec.rb +24 -0
  193. data/spec/unit/data_mapper/ordered_set/delete_spec.rb +28 -0
  194. data/spec/unit/data_mapper/ordered_set/each_spec.rb +19 -0
  195. data/spec/unit/data_mapper/ordered_set/empty_spec.rb +20 -0
  196. data/spec/unit/data_mapper/ordered_set/entries_spec.rb +22 -0
  197. data/spec/unit/data_mapper/ordered_set/eql_spec.rb +51 -0
  198. data/spec/unit/data_mapper/ordered_set/equal_value_spec.rb +84 -0
  199. data/spec/unit/data_mapper/ordered_set/hash_spec.rb +12 -0
  200. data/spec/unit/data_mapper/ordered_set/include_spec.rb +23 -0
  201. data/spec/unit/data_mapper/ordered_set/index_spec.rb +28 -0
  202. data/spec/unit/data_mapper/ordered_set/initialize_spec.rb +32 -0
  203. data/spec/unit/data_mapper/ordered_set/merge_spec.rb +36 -0
  204. data/spec/unit/data_mapper/ordered_set/shared/append_spec.rb +24 -0
  205. data/spec/unit/data_mapper/ordered_set/shared/clear_spec.rb +9 -0
  206. data/spec/unit/data_mapper/ordered_set/shared/delete_spec.rb +25 -0
  207. data/spec/unit/data_mapper/ordered_set/shared/each_spec.rb +17 -0
  208. data/spec/unit/data_mapper/ordered_set/shared/empty_spec.rb +9 -0
  209. data/spec/unit/data_mapper/ordered_set/shared/entries_spec.rb +9 -0
  210. data/spec/unit/data_mapper/ordered_set/shared/include_spec.rb +9 -0
  211. data/spec/unit/data_mapper/ordered_set/shared/index_spec.rb +13 -0
  212. data/spec/unit/data_mapper/ordered_set/shared/initialize_spec.rb +28 -0
  213. data/spec/unit/data_mapper/ordered_set/shared/merge_spec.rb +28 -0
  214. data/spec/unit/data_mapper/ordered_set/shared/size_spec.rb +13 -0
  215. data/spec/unit/data_mapper/ordered_set/shared/to_ary_spec.rb +11 -0
  216. data/spec/unit/data_mapper/ordered_set/size_spec.rb +27 -0
  217. data/spec/unit/data_mapper/ordered_set/to_ary_spec.rb +23 -0
  218. data/spec/unit/data_mapper/subject_set/append_spec.rb +47 -0
  219. data/spec/unit/data_mapper/subject_set/clear_spec.rb +34 -0
  220. data/spec/unit/data_mapper/subject_set/delete_spec.rb +40 -0
  221. data/spec/unit/data_mapper/subject_set/each_spec.rb +30 -0
  222. data/spec/unit/data_mapper/subject_set/empty_spec.rb +31 -0
  223. data/spec/unit/data_mapper/subject_set/entries_spec.rb +31 -0
  224. data/spec/unit/data_mapper/subject_set/get_spec.rb +34 -0
  225. data/spec/unit/data_mapper/subject_set/include_spec.rb +32 -0
  226. data/spec/unit/data_mapper/subject_set/named_spec.rb +33 -0
  227. data/spec/unit/data_mapper/subject_set/shared/append_spec.rb +18 -0
  228. data/spec/unit/data_mapper/subject_set/shared/clear_spec.rb +9 -0
  229. data/spec/unit/data_mapper/subject_set/shared/delete_spec.rb +9 -0
  230. data/spec/unit/data_mapper/subject_set/shared/each_spec.rb +9 -0
  231. data/spec/unit/data_mapper/subject_set/shared/empty_spec.rb +9 -0
  232. data/spec/unit/data_mapper/subject_set/shared/entries_spec.rb +9 -0
  233. data/spec/unit/data_mapper/subject_set/shared/get_spec.rb +9 -0
  234. data/spec/unit/data_mapper/subject_set/shared/include_spec.rb +9 -0
  235. data/spec/unit/data_mapper/subject_set/shared/named_spec.rb +9 -0
  236. data/spec/unit/data_mapper/subject_set/shared/size_spec.rb +13 -0
  237. data/spec/unit/data_mapper/subject_set/shared/to_ary_spec.rb +9 -0
  238. data/spec/unit/data_mapper/subject_set/shared/values_at_spec.rb +44 -0
  239. data/spec/unit/data_mapper/subject_set/size_spec.rb +42 -0
  240. data/spec/unit/data_mapper/subject_set/to_ary_spec.rb +34 -0
  241. data/spec/unit/data_mapper/subject_set/values_at_spec.rb +57 -0
  242. data/spec/unit/hash_spec.rb +28 -0
  243. data/spec/unit/hook_spec.rb +1235 -0
  244. data/spec/unit/inflections_spec.rb +16 -0
  245. data/spec/unit/lazy_array_spec.rb +1949 -0
  246. data/spec/unit/mash_spec.rb +312 -0
  247. data/spec/unit/module_spec.rb +71 -0
  248. data/spec/unit/object_spec.rb +38 -0
  249. data/spec/unit/try_dup_spec.rb +46 -0
  250. data/tasks/ci.rake +1 -0
  251. data/tasks/spec.rake +38 -0
  252. data/tasks/yard.rake +9 -0
  253. data/tasks/yardstick.rake +19 -0
  254. metadata +365 -0
@@ -0,0 +1,1294 @@
1
+ require 'spec_helper'
2
+ module OperationMatchers
3
+ class HaveValidParent
4
+ def matches?(target)
5
+ stack = []
6
+ stack << [ target, target.operands ] if target.respond_to?(:operands)
7
+
8
+ while node = stack.pop
9
+ @expected, operands = node
10
+
11
+ operands.each do |operand|
12
+ @target = operand
13
+ @actual = @target.parent
14
+
15
+ return false unless @actual.equal?(@expected)
16
+
17
+ if @target.respond_to?(:operands)
18
+ stack << [ @target, @target.operands ]
19
+ end
20
+ end
21
+ end
22
+
23
+ true
24
+ end
25
+
26
+ def failure_message
27
+ <<-OUTPUT
28
+ expected: #{@expected.inspect}
29
+ got: #{@actual.inspect}
30
+ OUTPUT
31
+ end
32
+ end
33
+
34
+ def have_operands_with_valid_parent
35
+ HaveValidParent.new
36
+ end
37
+ end
38
+
39
+ shared_examples_for 'DataMapper::Query::Conditions::AbstractOperation' do
40
+ before :all do
41
+ module ::Blog
42
+ class Article
43
+ include DataMapper::Resource
44
+
45
+ property :id, Serial
46
+ property :title, String, :required => true
47
+ end
48
+ end
49
+ DataMapper.finalize
50
+
51
+ @model = Blog::Article
52
+ end
53
+
54
+ before do
55
+ class ::OtherOperation < DataMapper::Query::Conditions::AbstractOperation
56
+ slug :other
57
+ end
58
+ end
59
+
60
+ before do
61
+ @comparison = DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:title], 'A title')
62
+ @and_operation = DataMapper::Query::Conditions::Operation.new(:and)
63
+ @or_operation = DataMapper::Query::Conditions::Operation.new(:or)
64
+ @not_operation = DataMapper::Query::Conditions::Operation.new(:not)
65
+ @null_operation = DataMapper::Query::Conditions::Operation.new(:null)
66
+ @other = OtherOperation.new
67
+ end
68
+
69
+ it { @operation.class.should respond_to(:new) }
70
+
71
+ describe '.new' do
72
+ describe 'with no arguments' do
73
+ subject { @operation.class.new }
74
+
75
+ it { should be_kind_of(@operation.class) }
76
+ end
77
+
78
+ describe 'with arguments' do
79
+ subject { @operation.class.new(@comparison) }
80
+
81
+ it { should be_kind_of(@operation.class) }
82
+ end
83
+ end
84
+
85
+ it { @operation.class.should respond_to(:slug) }
86
+
87
+ describe '.slug' do
88
+ describe 'with no arguments' do
89
+ subject { @operation.class.slug }
90
+
91
+ it { should == @slug }
92
+ end
93
+
94
+ describe 'with an argument' do
95
+ subject { @operation.class.slug(:other) }
96
+
97
+ it { should == :other }
98
+
99
+ # reset the AndOperation slug
100
+ after { @operation.class.slug(@slug) }
101
+ end
102
+ end
103
+
104
+ it { should respond_to(:==) }
105
+
106
+ describe '#==' do
107
+ describe 'when the other AbstractOperation is equal' do
108
+ # artificially modify the object so #== will throw an
109
+ # exception if the equal? branch is not followed when heckling
110
+ before { @operation.singleton_class.send(:undef_method, :slug) }
111
+
112
+ subject { @operation == @operation }
113
+
114
+ it { should be(true) }
115
+ end
116
+
117
+ describe 'when the other AbstractOperation is the same class' do
118
+ subject { @operation == DataMapper::Query::Conditions::Operation.new(@slug) }
119
+
120
+ it { should be(true) }
121
+ end
122
+
123
+ describe 'when the other AbstractOperation is a different class, with the same slug' do
124
+ before { @other.class.slug(@slug) }
125
+
126
+ subject { @operation == @other }
127
+
128
+ it { should be(false) }
129
+
130
+ # reset the OtherOperation slug
131
+ after { @other.class.slug(:other) }
132
+ end
133
+
134
+ describe 'when the other AbstractOperation is the same class, with different operands' do
135
+ subject { @operation == DataMapper::Query::Conditions::Operation.new(@slug, @comparison) }
136
+
137
+ it { should be(false) }
138
+ end
139
+ end
140
+
141
+ it { should respond_to(:<<) }
142
+
143
+ describe '#<<' do
144
+ describe 'with a NullOperation' do
145
+ subject { @operation << @null_operation }
146
+
147
+ it { should equal(@operation) }
148
+
149
+ it 'should merge the operand' do
150
+ subject.to_a.should == [ @null_operation.class.new ]
151
+ end
152
+
153
+ it { should have_operands_with_valid_parent }
154
+ end
155
+
156
+ describe 'with a duplicate operand' do
157
+ before { @operation << @comparison.dup }
158
+
159
+ subject { @operation << @comparison.dup }
160
+
161
+ it { should equal(@operation) }
162
+
163
+ it 'should have unique operands' do
164
+ subject.to_a.should == [ @comparison ]
165
+ end
166
+
167
+ it { should have_operands_with_valid_parent }
168
+ end
169
+
170
+ describe 'with an invalid operand' do
171
+ subject { @operation << '' }
172
+
173
+ it { method(:subject).should raise_error(ArgumentError, '+operand+ should be DataMapper::Query::Conditions::AbstractOperation or DataMapper::Query::Conditions::AbstractComparison or Array, but was String') }
174
+ end
175
+ end
176
+
177
+ it { should respond_to(:children) }
178
+
179
+ describe '#children' do
180
+ subject { @operation.children }
181
+
182
+ it { should be_kind_of(Set) }
183
+
184
+ it { should be_empty }
185
+
186
+ it { should equal(@operation.operands) }
187
+ end
188
+
189
+ it { should respond_to(:clear) }
190
+
191
+ describe '#clear' do
192
+ before do
193
+ @operation << @other
194
+ @operation.should_not be_empty
195
+ end
196
+
197
+ subject { @operation.clear }
198
+
199
+ it { should equal(@operation) }
200
+
201
+ it 'should clear the operands' do
202
+ subject.should be_empty
203
+ end
204
+ end
205
+
206
+ [ :difference, :- ].each do |method|
207
+ it { should respond_to(method) }
208
+
209
+ describe "##{method}" do
210
+ subject { @operation.send(method, @comparison) }
211
+
212
+ it { should eql(@not_operation.class.new(@comparison)) }
213
+ end
214
+ end
215
+
216
+ it { should respond_to(:dup) }
217
+
218
+ describe '#dup' do
219
+ subject { @operation.dup }
220
+
221
+ it { should_not equal(@operation) }
222
+
223
+ it { subject.to_a.should == @operation.to_a }
224
+ end
225
+
226
+ it { should respond_to(:each) }
227
+
228
+ describe '#each' do
229
+ before do
230
+ @yield = []
231
+ @operation << @other
232
+ end
233
+
234
+ subject { @operation.each { |operand| @yield << operand } }
235
+
236
+ it { should equal(@operation) }
237
+
238
+ it 'should yield to every operand' do
239
+ subject
240
+ @yield.should == [ @other ]
241
+ end
242
+ end
243
+
244
+ it { should respond_to(:eql?) }
245
+
246
+ describe '#eql?' do
247
+ describe 'when the other AbstractOperation is equal' do
248
+ # artificially modify the object so #eql? will throw an
249
+ # exception if the equal? branch is not followed when heckling
250
+ before { @operation.singleton_class.send(:undef_method, :slug) }
251
+
252
+ subject { @operation.eql?(@operation) }
253
+
254
+ it { should be(true) }
255
+ end
256
+
257
+ describe 'when the other AbstractOperation is the same class' do
258
+ subject { @operation.eql?(DataMapper::Query::Conditions::Operation.new(@slug)) }
259
+
260
+ it { should be(true) }
261
+ end
262
+
263
+ describe 'when the other AbstractOperation is a different class' do
264
+ subject { @operation.eql?(DataMapper::Query::Conditions::Operation.new(:other)) }
265
+
266
+ it { should be(false) }
267
+ end
268
+
269
+ describe 'when the other AbstractOperation is the same class, with different operands' do
270
+ subject { @operation.eql?(DataMapper::Query::Conditions::Operation.new(@slug, @comparison)) }
271
+
272
+ it { should be(false) }
273
+ end
274
+
275
+ describe 'when operations contain more than one operand' do
276
+ before do
277
+ @operation << DataMapper::Query::Conditions::Operation.new(:and, @comparison, @other)
278
+ @other = @operation.dup
279
+ end
280
+
281
+ subject { @operation.eql?(@other) }
282
+
283
+ it { should be(true) }
284
+ end
285
+ end
286
+
287
+ it { should respond_to(:hash) }
288
+
289
+ describe '#hash' do
290
+ describe 'with operands' do
291
+ before do
292
+ @operation << @comparison
293
+ end
294
+
295
+ subject { @operation.hash }
296
+
297
+ it 'should match the same AbstractOperation with the same operands' do
298
+ should == DataMapper::Query::Conditions::Operation.new(@slug, @comparison.dup).hash
299
+ end
300
+
301
+ it 'should not match the same AbstractOperation with different operands' do
302
+ should_not == DataMapper::Query::Conditions::Operation.new(@slug).hash
303
+ end
304
+
305
+ it 'should not match a different AbstractOperation with the same operands' do
306
+ should_not == @other.class.new(@comparison.dup).hash
307
+ end
308
+
309
+ it 'should not match a different AbstractOperation with different operands' do
310
+ should_not == DataMapper::Query::Conditions::Operation.new(:or).hash
311
+ end
312
+ end
313
+ end
314
+
315
+ [ :intersection, :& ].each do |method|
316
+ it { should respond_to(method) }
317
+
318
+ describe "##{method}" do
319
+ subject { @operation.send(method, @other) }
320
+
321
+ it { should eql(@and_operation) }
322
+ end
323
+ end
324
+
325
+ it { should respond_to(:merge) }
326
+
327
+ describe '#merge' do
328
+ describe 'with a NullOperation' do
329
+ subject { @operation.merge([ @null_operation ]) }
330
+
331
+ it { should equal(@operation) }
332
+
333
+ it 'should merge the operand' do
334
+ subject.to_a.should == [ @null_operation.class.new ]
335
+ end
336
+
337
+ it { should have_operands_with_valid_parent }
338
+ end
339
+
340
+ describe 'with a duplicate operand' do
341
+ before { @operation << @comparison.dup }
342
+
343
+ subject { @operation.merge([ @comparison.dup ]) }
344
+
345
+ it { should equal(@operation) }
346
+
347
+ it 'should have unique operands' do
348
+ subject.to_a.should == [ @comparison ]
349
+ end
350
+
351
+ it { should have_operands_with_valid_parent }
352
+ end
353
+
354
+ describe 'with an invalid operand' do
355
+ subject { @operation.merge([ '' ]) }
356
+
357
+ it { method(:subject).should raise_error(ArgumentError) }
358
+ end
359
+ end
360
+
361
+ it { should respond_to(:operands) }
362
+
363
+ describe '#operands' do
364
+ subject { @operation.operands }
365
+
366
+ it { should be_kind_of(Set) }
367
+
368
+ it { should be_empty }
369
+
370
+ it { should equal(@operation.children) }
371
+ end
372
+
373
+ it { should respond_to(:parent) }
374
+
375
+ describe '#parent' do
376
+ describe 'when there is no parent' do
377
+ subject { @operation.parent }
378
+
379
+ it { should be_nil }
380
+ end
381
+
382
+ describe 'when there is a parent' do
383
+ before { @other << @operation }
384
+
385
+ subject { @operation.parent }
386
+
387
+ it { should equal(@other) }
388
+ end
389
+ end
390
+
391
+ it { should respond_to(:parent=) }
392
+
393
+ describe '#parent=' do
394
+ subject { @operation.parent = @other }
395
+
396
+ it { should equal(@other) }
397
+
398
+ it 'should change the parent' do
399
+ method(:subject).should change(@operation, :parent).
400
+ from(nil).
401
+ to(@other)
402
+ end
403
+ end
404
+
405
+ [ :union, :|, :+ ].each do |method|
406
+ it { should respond_to(method) }
407
+
408
+ describe "##{method}" do
409
+ subject { @operation.send(method, @null_operation) }
410
+
411
+ it { should eql(@null_operation) }
412
+ end
413
+ end
414
+
415
+ it { should respond_to(:valid?) }
416
+
417
+ describe '#valid?' do
418
+ subject { @operation.valid? }
419
+
420
+ describe 'with no operands' do
421
+ it { should be(false) }
422
+ end
423
+
424
+ describe 'with an operand that responds to #valid?' do
425
+ describe 'and is valid' do
426
+ before do
427
+ @operation << @comparison
428
+ end
429
+
430
+ it { should be(true) }
431
+ end
432
+
433
+ describe 'and is not valid' do
434
+ before do
435
+ @operation << @or_operation.dup
436
+ end
437
+
438
+ it { should be(false) }
439
+ end
440
+ end
441
+
442
+ describe 'with an operand that does not respond to #valid?' do
443
+ before do
444
+ @operation << [ 'raw = 1' ]
445
+ end
446
+
447
+ it { should be(true) }
448
+ end
449
+ end
450
+ end
451
+
452
+ describe DataMapper::Query::Conditions::Operation do
453
+ it { DataMapper::Query::Conditions::Operation.should respond_to(:new) }
454
+
455
+ describe '.new' do
456
+ {
457
+ :and => DataMapper::Query::Conditions::AndOperation,
458
+ :or => DataMapper::Query::Conditions::OrOperation,
459
+ :not => DataMapper::Query::Conditions::NotOperation,
460
+ :null => DataMapper::Query::Conditions::NullOperation,
461
+ }.each do |slug, klass|
462
+ describe "with a slug #{slug.inspect}" do
463
+ subject { DataMapper::Query::Conditions::Operation.new(slug) }
464
+
465
+ it { should be_kind_of(klass) }
466
+
467
+ it { subject.should be_empty }
468
+ end
469
+ end
470
+
471
+ describe 'with an invalid slug' do
472
+ subject { DataMapper::Query::Conditions::Operation.new(:invalid) }
473
+
474
+ it { method(:subject).should raise_error(ArgumentError, 'No Operation class for :invalid has been defined') }
475
+ end
476
+
477
+ describe 'with operands' do
478
+ before { @or_operation = DataMapper::Query::Conditions::Operation.new(:or) }
479
+
480
+ subject { DataMapper::Query::Conditions::Operation.new(:and, @or_operation) }
481
+
482
+ it { should be_kind_of(DataMapper::Query::Conditions::AndOperation) }
483
+
484
+ it 'should set the operands' do
485
+ subject.to_a.should == [ @or_operation ]
486
+ end
487
+ end
488
+ end
489
+ end
490
+
491
+ describe DataMapper::Query::Conditions::AndOperation do
492
+ include OperationMatchers
493
+
494
+ it_should_behave_like 'DataMapper::Query::Conditions::AbstractOperation'
495
+
496
+ before do
497
+ @operation = @and_operation
498
+ @slug = @operation.slug
499
+ end
500
+
501
+ it { should respond_to(:<<) }
502
+
503
+ describe '#<<' do
504
+ [
505
+ DataMapper::Query::Conditions::AndOperation,
506
+ DataMapper::Query::Conditions::OrOperation,
507
+ DataMapper::Query::Conditions::NotOperation,
508
+ ].each do |klass|
509
+ describe "with an #{klass.name.split('::').last}" do
510
+ before do
511
+ @other = klass.new(@comparison)
512
+ end
513
+
514
+ subject { @operation << @other }
515
+
516
+ it { should equal(@operation) }
517
+
518
+ if klass == DataMapper::Query::Conditions::AndOperation
519
+ it 'should flatten and merge the operand' do
520
+ subject.to_a.should == @other.operands.to_a
521
+ end
522
+ else
523
+ it 'should merge the operand' do
524
+ subject.to_a.should == [ @other ]
525
+ end
526
+ end
527
+
528
+ it { should have_operands_with_valid_parent }
529
+ end
530
+ end
531
+ end
532
+
533
+ it { should respond_to(:negated?) }
534
+
535
+ describe '#negated?' do
536
+ describe 'with a negated parent' do
537
+ before do
538
+ @not_operation.class.new(@operation)
539
+ end
540
+
541
+ subject { @operation.negated? }
542
+
543
+ it { should be(true) }
544
+ end
545
+
546
+ describe 'with a not negated parent' do
547
+ before do
548
+ @or_operation.class.new(@operation)
549
+ end
550
+
551
+ subject { @operation.negated? }
552
+
553
+ it { should be(false) }
554
+ end
555
+
556
+ describe 'after memoizing the negation, and switching parents' do
557
+ before do
558
+ @or_operation.class.new(@operation)
559
+ @operation.should_not be_negated
560
+ @not_operation.class.new(@operation)
561
+ end
562
+
563
+ subject { @operation.negated? }
564
+
565
+ it { should be(true) }
566
+ end
567
+ end
568
+
569
+ it { should respond_to(:matches?) }
570
+
571
+ describe '#matches?' do
572
+ before do
573
+ @operation << @comparison << @comparison.class.new(@model.properties[:id], 1)
574
+ end
575
+
576
+ supported_by :all do
577
+ describe 'with a matching Hash' do
578
+ subject { @operation.matches?('title' => 'A title', 'id' => 1) }
579
+
580
+ it { should be(true) }
581
+ end
582
+
583
+ describe 'with a not matching Hash' do
584
+ subject { @operation.matches?('title' => 'Not matching', 'id' => 1) }
585
+
586
+ it { should be(false) }
587
+ end
588
+
589
+ describe 'with a matching Resource' do
590
+ subject { @operation.matches?(@model.new(:title => 'A title', :id => 1)) }
591
+
592
+ it { should be(true) }
593
+ end
594
+
595
+ describe 'with a not matching Resource' do
596
+ subject { @operation.matches?(@model.new(:title => 'Not matching', :id => 1)) }
597
+
598
+ it { should be(false) }
599
+ end
600
+
601
+ describe 'with a raw condition' do
602
+ before do
603
+ @operation = @operation.class.new([ 'title = ?', 'Another title' ])
604
+ end
605
+
606
+ subject { @operation.matches?('title' => 'A title', 'id' => 1) }
607
+
608
+ it { should be(true) }
609
+ end
610
+ end
611
+ end
612
+
613
+ it { should respond_to(:minimize) }
614
+
615
+ describe '#minimize' do
616
+ subject { @operation.minimize }
617
+
618
+ describe 'with one empty operand' do
619
+ before do
620
+ @operation << @other
621
+ end
622
+
623
+ it { should equal(@operation) }
624
+
625
+ it { subject.should be_empty }
626
+ end
627
+
628
+ describe 'with more than one operation' do
629
+ before do
630
+ @operation.merge([ @comparison, @not_operation.class.new(@comparison) ])
631
+ end
632
+
633
+ it { should equal(@operation) }
634
+
635
+ it { subject.to_a.should =~ [ @comparison, @not_operation.class.new(@comparison) ] }
636
+ end
637
+
638
+ describe 'with one non-empty operand' do
639
+ before do
640
+ @operation << @comparison
641
+ end
642
+
643
+ it { should == @comparison }
644
+ end
645
+
646
+ describe 'with one null operation' do
647
+ before do
648
+ @operation << @null_operation
649
+ end
650
+
651
+ it { should eql(@null_operation) }
652
+ end
653
+
654
+ describe 'with one null operation and one non-null operation' do
655
+ before do
656
+ @operation.merge([ @null_operation, @comparison ])
657
+ end
658
+
659
+ it { should eql(@comparison) }
660
+ end
661
+ end
662
+
663
+ it { should respond_to(:merge) }
664
+
665
+ describe '#merge' do
666
+ [
667
+ DataMapper::Query::Conditions::AndOperation,
668
+ DataMapper::Query::Conditions::OrOperation,
669
+ DataMapper::Query::Conditions::NotOperation,
670
+ ].each do |klass|
671
+ describe "with an #{klass.name.split('::').last}" do
672
+ before do
673
+ @other = klass.new(@comparison)
674
+ end
675
+
676
+ subject { @operation.merge([ @other ]) }
677
+
678
+ it { should equal(@operation) }
679
+
680
+ if klass == DataMapper::Query::Conditions::AndOperation
681
+ it 'should flatten and merge the operand' do
682
+ subject.to_a.should == @other.operands.to_a
683
+ end
684
+ else
685
+ it 'should merge the operand' do
686
+ subject.to_a.should == [ @other ]
687
+ end
688
+ end
689
+
690
+ it { should have_operands_with_valid_parent }
691
+ end
692
+ end
693
+ end
694
+
695
+ it { should respond_to(:to_s) }
696
+
697
+ describe '#to_s' do
698
+ describe 'with no operands' do
699
+ subject { @operation.to_s }
700
+
701
+ it { should eql('') }
702
+ end
703
+
704
+ describe 'with operands' do
705
+ before do
706
+ @not_operation << @comparison.dup
707
+ @operation << @comparison << @not_operation
708
+ end
709
+
710
+ subject { @operation.to_s }
711
+
712
+ it { should eql('(NOT(title = "A title") AND title = "A title")') }
713
+ end
714
+ end
715
+
716
+ it { should respond_to(:valid?) }
717
+
718
+ describe '#valid?' do
719
+ describe 'with one valid operand, and one invalid operand' do
720
+ before do
721
+ @operation << @comparison
722
+ @operation << DataMapper::Query::Conditions::Comparison.new(:in, @model.properties[:id], [])
723
+ end
724
+
725
+ subject { @operation.valid? }
726
+
727
+ it { should be(false) }
728
+ end
729
+
730
+ describe 'with one invalid operand' do
731
+ before do
732
+ @operation << DataMapper::Query::Conditions::Comparison.new(:in, @model.properties[:id], [])
733
+ end
734
+
735
+ subject { @operation.valid? }
736
+
737
+ it { should be(false) }
738
+ end
739
+ end
740
+ end
741
+
742
+ describe DataMapper::Query::Conditions::OrOperation do
743
+ include OperationMatchers
744
+
745
+ it_should_behave_like 'DataMapper::Query::Conditions::AbstractOperation'
746
+
747
+ before do
748
+ @operation = @or_operation
749
+ @slug = @operation.slug
750
+ end
751
+
752
+ it { should respond_to(:<<) }
753
+
754
+ describe '#<<' do
755
+ [
756
+ DataMapper::Query::Conditions::AndOperation,
757
+ DataMapper::Query::Conditions::OrOperation,
758
+ DataMapper::Query::Conditions::NotOperation,
759
+ ].each do |klass|
760
+ describe "with an #{klass.name.split('::').last}" do
761
+ before do
762
+ @other = klass.new(@comparison)
763
+ end
764
+
765
+ subject { @operation << @other }
766
+
767
+ it { should equal(@operation) }
768
+
769
+ if klass == DataMapper::Query::Conditions::OrOperation
770
+ it 'should flatten and merge the operand' do
771
+ subject.to_a.should == @other.operands.to_a
772
+ end
773
+ else
774
+ it 'should merge the operand' do
775
+ subject.to_a.should == [ @other ]
776
+ end
777
+ end
778
+
779
+ it { should have_operands_with_valid_parent }
780
+ end
781
+ end
782
+ end
783
+
784
+ it { should respond_to(:negated?) }
785
+
786
+ describe '#negated?' do
787
+ describe 'with a negated parent' do
788
+ before do
789
+ @not_operation.class.new(@operation)
790
+ end
791
+
792
+ subject { @operation.negated? }
793
+
794
+ it { should be(true) }
795
+ end
796
+
797
+ describe 'with a not negated parent' do
798
+ before do
799
+ @and_operation.class.new(@operation)
800
+ end
801
+
802
+ subject { @operation.negated? }
803
+
804
+ it { should be(false) }
805
+ end
806
+
807
+ describe 'after memoizing the negation, and switching parents' do
808
+ before do
809
+ @or_operation.class.new(@operation)
810
+ @operation.should_not be_negated
811
+ @not_operation.class.new(@operation)
812
+ end
813
+
814
+ subject { @operation.negated? }
815
+
816
+ it { should be(true) }
817
+ end
818
+ end
819
+
820
+ it { should respond_to(:matches?) }
821
+
822
+ describe '#matches?' do
823
+ before do
824
+ @operation << @comparison << @comparison.class.new(@model.properties[:id], 1)
825
+ end
826
+
827
+ supported_by :all do
828
+ describe 'with a matching Hash' do
829
+ subject { @operation.matches?('title' => 'A title', 'id' => 2) }
830
+
831
+ it { should be(true) }
832
+ end
833
+
834
+ describe 'with a not matching Hash' do
835
+ subject { @operation.matches?('title' => 'Not matching', 'id' => 2) }
836
+
837
+ it { should be(false) }
838
+ end
839
+
840
+ describe 'with a matching Resource' do
841
+ subject { @operation.matches?(@model.new(:title => 'A title', :id => 2)) }
842
+
843
+ it { should be(true) }
844
+ end
845
+
846
+ describe 'with a not matching Resource' do
847
+ subject { @operation.matches?(@model.new(:title => 'Not matching', :id => 2)) }
848
+
849
+ it { should be(false) }
850
+ end
851
+
852
+ describe 'with a raw condition' do
853
+ before do
854
+ @operation = @operation.class.new([ 'title = ?', 'Another title' ])
855
+ end
856
+
857
+ subject { @operation.matches?('title' => 'A title', 'id' => 2) }
858
+
859
+ it { should be(true) }
860
+ end
861
+ end
862
+ end
863
+
864
+ it { should respond_to(:minimize) }
865
+
866
+ describe '#minimize' do
867
+ subject { @operation.minimize }
868
+
869
+ describe 'with one empty operand' do
870
+ before do
871
+ @operation << @other
872
+ end
873
+
874
+ it { should equal(@operation) }
875
+
876
+ it { subject.should be_empty }
877
+ end
878
+
879
+ describe 'with more than one operation' do
880
+ before do
881
+ @operation.merge([ @comparison, @not_operation.class.new(@comparison) ])
882
+ end
883
+
884
+ it { should equal(@operation) }
885
+
886
+ it { subject.to_a.should =~ [ @comparison, @not_operation.class.new(@comparison) ] }
887
+ end
888
+
889
+ describe 'with one non-empty operand' do
890
+ before do
891
+ @operation << @comparison
892
+ end
893
+
894
+ it { should == @comparison }
895
+ end
896
+
897
+ describe 'with one null operation' do
898
+ before do
899
+ @operation << @null_operation
900
+ end
901
+
902
+ it { should eql(@null_operation) }
903
+ end
904
+ end
905
+
906
+ it { should respond_to(:merge) }
907
+
908
+ describe '#merge' do
909
+ [
910
+ DataMapper::Query::Conditions::AndOperation,
911
+ DataMapper::Query::Conditions::OrOperation,
912
+ DataMapper::Query::Conditions::NotOperation,
913
+ ].each do |klass|
914
+ describe "with an #{klass.name.split('::').last}" do
915
+ before do
916
+ @other = klass.new(@comparison)
917
+ end
918
+
919
+ subject { @operation.merge([ @other ]) }
920
+
921
+ it { should equal(@operation) }
922
+
923
+ if klass == DataMapper::Query::Conditions::OrOperation
924
+ it 'should flatten and merge the operand' do
925
+ subject.to_a.should == @other.operands.to_a
926
+ end
927
+ else
928
+ it 'should merge the operand' do
929
+ subject.to_a.should == [ @other ]
930
+ end
931
+ end
932
+
933
+ it { should have_operands_with_valid_parent }
934
+ end
935
+ end
936
+ end
937
+
938
+ it { should respond_to(:valid?) }
939
+
940
+ describe '#valid?' do
941
+ describe 'with one valid operand, and one invalid operand' do
942
+ before do
943
+ @operation << @comparison
944
+ @operation << DataMapper::Query::Conditions::Comparison.new(:in, @model.properties[:id], [])
945
+ end
946
+
947
+ subject { @operation.valid? }
948
+
949
+ it { should be(true) }
950
+ end
951
+
952
+ describe 'with one invalid operand' do
953
+ before do
954
+ @operation << DataMapper::Query::Conditions::Comparison.new(:in, @model.properties[:id], [])
955
+ end
956
+
957
+ subject { @operation.valid? }
958
+
959
+ it { should be(false) }
960
+ end
961
+ end
962
+ end
963
+
964
+ describe DataMapper::Query::Conditions::NotOperation do
965
+ include OperationMatchers
966
+
967
+ it_should_behave_like 'DataMapper::Query::Conditions::AbstractOperation'
968
+
969
+ before do
970
+ @operation = @not_operation
971
+ @slug = @operation.slug
972
+ end
973
+
974
+ it { should respond_to(:<<) }
975
+
976
+ describe '#<<' do
977
+ [
978
+ DataMapper::Query::Conditions::AndOperation,
979
+ DataMapper::Query::Conditions::OrOperation,
980
+ DataMapper::Query::Conditions::NotOperation,
981
+ ].each do |klass|
982
+ describe "with an #{klass.name.split('::').last}" do
983
+ before do
984
+ @other = klass.new(@comparison)
985
+ end
986
+
987
+ subject { @operation << @other }
988
+
989
+ it { should equal(@operation) }
990
+
991
+ it 'should merge the operand' do
992
+ subject.to_a.should == [ @other ]
993
+ end
994
+
995
+ it { should have_operands_with_valid_parent }
996
+ end
997
+ end
998
+
999
+ describe 'with more than one operand' do
1000
+ subject { @operation << @comparison << @other }
1001
+
1002
+ it { method(:subject).should raise_error(ArgumentError) }
1003
+ end
1004
+
1005
+ describe 'with self as an operand' do
1006
+ subject { @operation << @operation }
1007
+
1008
+ it { method(:subject).should raise_error(ArgumentError, 'cannot append operand to itself') }
1009
+ end
1010
+ end
1011
+
1012
+ it { should respond_to(:negated?) }
1013
+
1014
+ describe '#negated?' do
1015
+ describe 'with a negated parent' do
1016
+ before do
1017
+ @not_operation.class.new(@operation)
1018
+ end
1019
+
1020
+ subject { @operation.negated? }
1021
+
1022
+ it { should be(false) }
1023
+ end
1024
+
1025
+ describe 'with a not negated parent' do
1026
+ before do
1027
+ @or_operation.class.new(@operation)
1028
+ end
1029
+
1030
+ subject { @operation.negated? }
1031
+
1032
+ it { should be(true) }
1033
+ end
1034
+
1035
+ describe 'after memoizing the negation, and switching parents' do
1036
+ before do
1037
+ @or_operation.class.new(@operation)
1038
+ @operation.should be_negated
1039
+ @not_operation.class.new(@operation)
1040
+ end
1041
+
1042
+ subject { @operation.negated? }
1043
+
1044
+ it { should be(false) }
1045
+ end
1046
+ end
1047
+
1048
+ it { should respond_to(:matches?) }
1049
+
1050
+ describe '#matches?' do
1051
+ before do
1052
+ @operation << @comparison.class.new(@model.properties[:id], 1)
1053
+ end
1054
+
1055
+ supported_by :all do
1056
+ describe 'with a matching Hash' do
1057
+ subject { @operation.matches?('id' => 2) }
1058
+
1059
+ it { should be(true) }
1060
+ end
1061
+
1062
+ describe 'with a not matching Hash' do
1063
+ subject { @operation.matches?('id' => 1) }
1064
+
1065
+ it { should be(false) }
1066
+ end
1067
+
1068
+ describe 'with a matching Resource' do
1069
+ subject { @operation.matches?(@model.new(:id => 2)) }
1070
+
1071
+ it { should be(true) }
1072
+ end
1073
+
1074
+ describe 'with a not matching Hash' do
1075
+ subject { @operation.matches?(@model.new(:id => 1)) }
1076
+
1077
+ it { should be(false) }
1078
+ end
1079
+
1080
+ describe 'with a raw condition' do
1081
+ before do
1082
+ @operation = @operation.class.new([ 'title = ?', 'Another title' ])
1083
+ end
1084
+
1085
+ subject { @operation.matches?('id' => 2) }
1086
+
1087
+ it { should be(true) }
1088
+ end
1089
+ end
1090
+ end
1091
+
1092
+ it { should respond_to(:merge) }
1093
+
1094
+ describe '#merge' do
1095
+ [
1096
+ DataMapper::Query::Conditions::AndOperation,
1097
+ DataMapper::Query::Conditions::OrOperation,
1098
+ DataMapper::Query::Conditions::NotOperation,
1099
+ ].each do |klass|
1100
+ describe "with an #{klass.name.split('::').last}" do
1101
+ before do
1102
+ @other = klass.new(@comparison)
1103
+ end
1104
+
1105
+ subject { @operation.merge([ @other ]) }
1106
+
1107
+ it { should equal(@operation) }
1108
+
1109
+ it 'should merge the operand' do
1110
+ subject.to_a.should == [ @other ]
1111
+ end
1112
+
1113
+ it { should have_operands_with_valid_parent }
1114
+ end
1115
+ end
1116
+
1117
+ describe 'with more than one operand' do
1118
+ subject { @operation.merge([ @comparison, @other ]) }
1119
+
1120
+ it { method(:subject).should raise_error(ArgumentError) }
1121
+ end
1122
+ end
1123
+
1124
+ it { should respond_to(:minimize) }
1125
+
1126
+ describe '#minimize' do
1127
+ subject { @operation.minimize }
1128
+
1129
+ describe 'when no operand' do
1130
+ it { should equal(@operation) }
1131
+ end
1132
+
1133
+ describe 'when operand is a NotOperation' do
1134
+ before do
1135
+ @operation << @not_operation.class.new(@comparison)
1136
+ end
1137
+
1138
+ it 'should remove the double negative' do
1139
+ should eql(@comparison)
1140
+ end
1141
+ end
1142
+
1143
+ describe 'when operand is not a NotOperation' do
1144
+ before do
1145
+ @operation << @comparison
1146
+ end
1147
+
1148
+ it { should equal(@operation) }
1149
+
1150
+ it { subject.to_a.should == [ @comparison ] }
1151
+ end
1152
+
1153
+ describe 'when operand is an empty operation' do
1154
+ before do
1155
+ @operation << @and_operation
1156
+ end
1157
+
1158
+ it { should equal(@operation) }
1159
+
1160
+ it { subject.should be_empty }
1161
+ end
1162
+
1163
+ describe 'when operand is an operation containing a Comparison' do
1164
+ before do
1165
+ @operation << @and_operation.class.new(@comparison)
1166
+ end
1167
+
1168
+ it { should equal(@operation) }
1169
+
1170
+ it { subject.to_a.should == [ @comparison ] }
1171
+ end
1172
+ end
1173
+
1174
+ it { should respond_to(:to_s) }
1175
+
1176
+ describe '#to_s' do
1177
+ describe 'with no operands' do
1178
+ subject { @operation.to_s }
1179
+
1180
+ it { should eql('') }
1181
+ end
1182
+
1183
+ describe 'with operands' do
1184
+ before do
1185
+ @operation << @comparison
1186
+ end
1187
+
1188
+ subject { @operation.to_s }
1189
+
1190
+ it { should eql('NOT(title = "A title")') }
1191
+ end
1192
+ end
1193
+
1194
+ it { should respond_to(:valid?) }
1195
+
1196
+ describe '#valid?' do
1197
+ describe 'with one invalid operand' do
1198
+ before do
1199
+ @operation << @not_operation.class.new(
1200
+ DataMapper::Query::Conditions::Comparison.new(:eql, @model.properties[:id], nil)
1201
+ )
1202
+ end
1203
+
1204
+ subject { @operation.valid? }
1205
+
1206
+ it { should be(false) }
1207
+ end
1208
+ end
1209
+ end
1210
+
1211
+ describe DataMapper::Query::Conditions::NullOperation do
1212
+ include OperationMatchers
1213
+
1214
+ before :all do
1215
+ module ::Blog
1216
+ class Article
1217
+ include DataMapper::Resource
1218
+
1219
+ property :id, Serial
1220
+ property :title, String, :required => true
1221
+ end
1222
+ end
1223
+
1224
+ @model = Blog::Article
1225
+ end
1226
+
1227
+ before do
1228
+ @null_operation = DataMapper::Query::Conditions::Operation.new(:null)
1229
+ @operation = @null_operation
1230
+ @slug = @operation.slug
1231
+ end
1232
+
1233
+ it { should respond_to(:slug) }
1234
+
1235
+ describe '#slug' do
1236
+ subject { @operation.slug }
1237
+
1238
+ it { should == :null }
1239
+ end
1240
+
1241
+ it { should respond_to(:matches?) }
1242
+
1243
+ describe '#matches?' do
1244
+ describe 'with a Hash' do
1245
+ subject { @operation.matches?({}) }
1246
+
1247
+ it { should be(true) }
1248
+ end
1249
+
1250
+ describe 'with a Resource' do
1251
+ subject { @operation.matches?(Blog::Article.new) }
1252
+
1253
+ it { should be(true) }
1254
+ end
1255
+
1256
+ describe 'with any other Object' do
1257
+ subject { @operation.matches?(Object.new) }
1258
+
1259
+ it { should be(false) }
1260
+ end
1261
+ end
1262
+
1263
+ it { should respond_to(:minimize) }
1264
+
1265
+ describe '#minimize' do
1266
+ subject { @operation.minimize }
1267
+
1268
+ it { should equal(@operation) }
1269
+ end
1270
+
1271
+ it { should respond_to(:valid?) }
1272
+
1273
+ describe '#valid?' do
1274
+ subject { @operation.valid? }
1275
+
1276
+ it { should be(true) }
1277
+ end
1278
+
1279
+ it { should respond_to(:nil?) }
1280
+
1281
+ describe '#nil?' do
1282
+ subject { @operation.nil? }
1283
+
1284
+ it { should be(true) }
1285
+ end
1286
+
1287
+ it { should respond_to(:inspect) }
1288
+
1289
+ describe '#inspect' do
1290
+ subject { @operation.inspect }
1291
+
1292
+ it { should == 'nil' }
1293
+ end
1294
+ end