ardm-core 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (259) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +29 -0
  3. data/.document +5 -0
  4. data/.gitignore +35 -0
  5. data/.travis.yml +23 -0
  6. data/.yardopts +1 -0
  7. data/Gemfile +63 -0
  8. data/LICENSE +20 -0
  9. data/README.rdoc +237 -0
  10. data/Rakefile +4 -0
  11. data/VERSION +1 -0
  12. data/ardm-core.gemspec +25 -0
  13. data/lib/ardm-core.rb +1 -0
  14. data/lib/dm-core.rb +285 -0
  15. data/lib/dm-core/adapters.rb +222 -0
  16. data/lib/dm-core/adapters/abstract_adapter.rb +236 -0
  17. data/lib/dm-core/adapters/in_memory_adapter.rb +113 -0
  18. data/lib/dm-core/associations/many_to_many.rb +496 -0
  19. data/lib/dm-core/associations/many_to_one.rb +296 -0
  20. data/lib/dm-core/associations/one_to_many.rb +345 -0
  21. data/lib/dm-core/associations/one_to_one.rb +86 -0
  22. data/lib/dm-core/associations/relationship.rb +663 -0
  23. data/lib/dm-core/backwards.rb +13 -0
  24. data/lib/dm-core/collection.rb +1514 -0
  25. data/lib/dm-core/core_ext/kernel.rb +23 -0
  26. data/lib/dm-core/core_ext/pathname.rb +6 -0
  27. data/lib/dm-core/core_ext/symbol.rb +10 -0
  28. data/lib/dm-core/identity_map.rb +7 -0
  29. data/lib/dm-core/model.rb +869 -0
  30. data/lib/dm-core/model/hook.rb +102 -0
  31. data/lib/dm-core/model/is.rb +32 -0
  32. data/lib/dm-core/model/property.rb +253 -0
  33. data/lib/dm-core/model/relationship.rb +377 -0
  34. data/lib/dm-core/model/scope.rb +89 -0
  35. data/lib/dm-core/property.rb +839 -0
  36. data/lib/dm-core/property/binary.rb +22 -0
  37. data/lib/dm-core/property/boolean.rb +31 -0
  38. data/lib/dm-core/property/class.rb +24 -0
  39. data/lib/dm-core/property/date.rb +45 -0
  40. data/lib/dm-core/property/date_time.rb +44 -0
  41. data/lib/dm-core/property/decimal.rb +50 -0
  42. data/lib/dm-core/property/discriminator.rb +46 -0
  43. data/lib/dm-core/property/float.rb +28 -0
  44. data/lib/dm-core/property/integer.rb +32 -0
  45. data/lib/dm-core/property/lookup.rb +29 -0
  46. data/lib/dm-core/property/numeric.rb +40 -0
  47. data/lib/dm-core/property/object.rb +28 -0
  48. data/lib/dm-core/property/serial.rb +13 -0
  49. data/lib/dm-core/property/string.rb +50 -0
  50. data/lib/dm-core/property/text.rb +12 -0
  51. data/lib/dm-core/property/time.rb +46 -0
  52. data/lib/dm-core/property/typecast/numeric.rb +32 -0
  53. data/lib/dm-core/property/typecast/time.rb +33 -0
  54. data/lib/dm-core/property_set.rb +177 -0
  55. data/lib/dm-core/query.rb +1444 -0
  56. data/lib/dm-core/query/conditions/comparison.rb +910 -0
  57. data/lib/dm-core/query/conditions/operation.rb +720 -0
  58. data/lib/dm-core/query/direction.rb +36 -0
  59. data/lib/dm-core/query/operator.rb +35 -0
  60. data/lib/dm-core/query/path.rb +114 -0
  61. data/lib/dm-core/query/sort.rb +39 -0
  62. data/lib/dm-core/relationship_set.rb +72 -0
  63. data/lib/dm-core/repository.rb +226 -0
  64. data/lib/dm-core/resource.rb +1228 -0
  65. data/lib/dm-core/resource/persistence_state.rb +75 -0
  66. data/lib/dm-core/resource/persistence_state/clean.rb +40 -0
  67. data/lib/dm-core/resource/persistence_state/deleted.rb +30 -0
  68. data/lib/dm-core/resource/persistence_state/dirty.rb +96 -0
  69. data/lib/dm-core/resource/persistence_state/immutable.rb +34 -0
  70. data/lib/dm-core/resource/persistence_state/persisted.rb +29 -0
  71. data/lib/dm-core/resource/persistence_state/transient.rb +78 -0
  72. data/lib/dm-core/spec/lib/adapter_helpers.rb +54 -0
  73. data/lib/dm-core/spec/lib/collection_helpers.rb +20 -0
  74. data/lib/dm-core/spec/lib/counter_adapter.rb +38 -0
  75. data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
  76. data/lib/dm-core/spec/lib/spec_helper.rb +74 -0
  77. data/lib/dm-core/spec/setup.rb +173 -0
  78. data/lib/dm-core/spec/shared/adapter_spec.rb +326 -0
  79. data/lib/dm-core/spec/shared/public/property_spec.rb +229 -0
  80. data/lib/dm-core/spec/shared/resource_spec.rb +1236 -0
  81. data/lib/dm-core/spec/shared/sel_spec.rb +111 -0
  82. data/lib/dm-core/spec/shared/semipublic/property_spec.rb +134 -0
  83. data/lib/dm-core/spec/shared/semipublic/query/conditions/abstract_comparison_spec.rb +261 -0
  84. data/lib/dm-core/support/assertions.rb +8 -0
  85. data/lib/dm-core/support/chainable.rb +18 -0
  86. data/lib/dm-core/support/deprecate.rb +12 -0
  87. data/lib/dm-core/support/descendant_set.rb +89 -0
  88. data/lib/dm-core/support/equalizer.rb +48 -0
  89. data/lib/dm-core/support/ext/array.rb +22 -0
  90. data/lib/dm-core/support/ext/blank.rb +25 -0
  91. data/lib/dm-core/support/ext/hash.rb +67 -0
  92. data/lib/dm-core/support/ext/module.rb +47 -0
  93. data/lib/dm-core/support/ext/object.rb +57 -0
  94. data/lib/dm-core/support/ext/string.rb +24 -0
  95. data/lib/dm-core/support/ext/try_dup.rb +12 -0
  96. data/lib/dm-core/support/hook.rb +402 -0
  97. data/lib/dm-core/support/inflections.rb +60 -0
  98. data/lib/dm-core/support/inflector/inflections.rb +211 -0
  99. data/lib/dm-core/support/inflector/methods.rb +151 -0
  100. data/lib/dm-core/support/lazy_array.rb +451 -0
  101. data/lib/dm-core/support/local_object_space.rb +12 -0
  102. data/lib/dm-core/support/logger.rb +199 -0
  103. data/lib/dm-core/support/mash.rb +176 -0
  104. data/lib/dm-core/support/naming_conventions.rb +90 -0
  105. data/lib/dm-core/support/ordered_set.rb +380 -0
  106. data/lib/dm-core/support/subject.rb +33 -0
  107. data/lib/dm-core/support/subject_set.rb +250 -0
  108. data/lib/dm-core/version.rb +3 -0
  109. data/script/performance.rb +275 -0
  110. data/script/profile.rb +218 -0
  111. data/spec/lib/rspec_immediate_feedback_formatter.rb +54 -0
  112. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +68 -0
  113. data/spec/public/associations/many_to_many_spec.rb +197 -0
  114. data/spec/public/associations/many_to_one_spec.rb +83 -0
  115. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +40 -0
  116. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  117. data/spec/public/associations/one_to_many_spec.rb +81 -0
  118. data/spec/public/associations/one_to_one_spec.rb +176 -0
  119. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +46 -0
  120. data/spec/public/collection_spec.rb +69 -0
  121. data/spec/public/finalize_spec.rb +76 -0
  122. data/spec/public/model/hook_spec.rb +246 -0
  123. data/spec/public/model/property_spec.rb +88 -0
  124. data/spec/public/model/relationship_spec.rb +1040 -0
  125. data/spec/public/model_spec.rb +458 -0
  126. data/spec/public/property/binary_spec.rb +41 -0
  127. data/spec/public/property/boolean_spec.rb +22 -0
  128. data/spec/public/property/class_spec.rb +28 -0
  129. data/spec/public/property/date_spec.rb +22 -0
  130. data/spec/public/property/date_time_spec.rb +22 -0
  131. data/spec/public/property/decimal_spec.rb +23 -0
  132. data/spec/public/property/discriminator_spec.rb +135 -0
  133. data/spec/public/property/float_spec.rb +22 -0
  134. data/spec/public/property/integer_spec.rb +22 -0
  135. data/spec/public/property/object_spec.rb +107 -0
  136. data/spec/public/property/serial_spec.rb +22 -0
  137. data/spec/public/property/string_spec.rb +22 -0
  138. data/spec/public/property/text_spec.rb +63 -0
  139. data/spec/public/property/time_spec.rb +22 -0
  140. data/spec/public/property_spec.rb +341 -0
  141. data/spec/public/resource_spec.rb +284 -0
  142. data/spec/public/sel_spec.rb +53 -0
  143. data/spec/public/setup_spec.rb +145 -0
  144. data/spec/public/shared/association_collection_shared_spec.rb +309 -0
  145. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  146. data/spec/public/shared/collection_shared_spec.rb +1669 -0
  147. data/spec/public/shared/finder_shared_spec.rb +1629 -0
  148. data/spec/rcov.opts +6 -0
  149. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  150. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
  151. data/spec/semipublic/associations/many_to_many_spec.rb +94 -0
  152. data/spec/semipublic/associations/many_to_one_spec.rb +63 -0
  153. data/spec/semipublic/associations/one_to_many_spec.rb +55 -0
  154. data/spec/semipublic/associations/one_to_one_spec.rb +53 -0
  155. data/spec/semipublic/associations/relationship_spec.rb +200 -0
  156. data/spec/semipublic/associations_spec.rb +177 -0
  157. data/spec/semipublic/collection_spec.rb +110 -0
  158. data/spec/semipublic/model_spec.rb +96 -0
  159. data/spec/semipublic/property/binary_spec.rb +13 -0
  160. data/spec/semipublic/property/boolean_spec.rb +47 -0
  161. data/spec/semipublic/property/class_spec.rb +33 -0
  162. data/spec/semipublic/property/date_spec.rb +43 -0
  163. data/spec/semipublic/property/date_time_spec.rb +46 -0
  164. data/spec/semipublic/property/decimal_spec.rb +83 -0
  165. data/spec/semipublic/property/discriminator_spec.rb +19 -0
  166. data/spec/semipublic/property/float_spec.rb +82 -0
  167. data/spec/semipublic/property/integer_spec.rb +82 -0
  168. data/spec/semipublic/property/lookup_spec.rb +29 -0
  169. data/spec/semipublic/property/serial_spec.rb +13 -0
  170. data/spec/semipublic/property/string_spec.rb +13 -0
  171. data/spec/semipublic/property/text_spec.rb +31 -0
  172. data/spec/semipublic/property/time_spec.rb +50 -0
  173. data/spec/semipublic/property_spec.rb +114 -0
  174. data/spec/semipublic/query/conditions/comparison_spec.rb +1501 -0
  175. data/spec/semipublic/query/conditions/operation_spec.rb +1294 -0
  176. data/spec/semipublic/query/path_spec.rb +471 -0
  177. data/spec/semipublic/query_spec.rb +3777 -0
  178. data/spec/semipublic/resource/state/clean_spec.rb +88 -0
  179. data/spec/semipublic/resource/state/deleted_spec.rb +78 -0
  180. data/spec/semipublic/resource/state/dirty_spec.rb +156 -0
  181. data/spec/semipublic/resource/state/immutable_spec.rb +105 -0
  182. data/spec/semipublic/resource/state/transient_spec.rb +162 -0
  183. data/spec/semipublic/resource/state_spec.rb +230 -0
  184. data/spec/semipublic/resource_spec.rb +23 -0
  185. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  186. data/spec/semipublic/shared/resource_shared_spec.rb +199 -0
  187. data/spec/semipublic/shared/resource_state_shared_spec.rb +79 -0
  188. data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
  189. data/spec/spec.opts +5 -0
  190. data/spec/spec_helper.rb +37 -0
  191. data/spec/support/core_ext/hash.rb +10 -0
  192. data/spec/support/core_ext/inheritable_attributes.rb +46 -0
  193. data/spec/support/properties/huge_integer.rb +17 -0
  194. data/spec/unit/array_spec.rb +23 -0
  195. data/spec/unit/blank_spec.rb +73 -0
  196. data/spec/unit/data_mapper/ordered_set/append_spec.rb +26 -0
  197. data/spec/unit/data_mapper/ordered_set/clear_spec.rb +24 -0
  198. data/spec/unit/data_mapper/ordered_set/delete_spec.rb +28 -0
  199. data/spec/unit/data_mapper/ordered_set/each_spec.rb +19 -0
  200. data/spec/unit/data_mapper/ordered_set/empty_spec.rb +20 -0
  201. data/spec/unit/data_mapper/ordered_set/entries_spec.rb +22 -0
  202. data/spec/unit/data_mapper/ordered_set/eql_spec.rb +51 -0
  203. data/spec/unit/data_mapper/ordered_set/equal_value_spec.rb +84 -0
  204. data/spec/unit/data_mapper/ordered_set/hash_spec.rb +12 -0
  205. data/spec/unit/data_mapper/ordered_set/include_spec.rb +23 -0
  206. data/spec/unit/data_mapper/ordered_set/index_spec.rb +28 -0
  207. data/spec/unit/data_mapper/ordered_set/initialize_spec.rb +32 -0
  208. data/spec/unit/data_mapper/ordered_set/merge_spec.rb +36 -0
  209. data/spec/unit/data_mapper/ordered_set/shared/append_spec.rb +24 -0
  210. data/spec/unit/data_mapper/ordered_set/shared/clear_spec.rb +9 -0
  211. data/spec/unit/data_mapper/ordered_set/shared/delete_spec.rb +25 -0
  212. data/spec/unit/data_mapper/ordered_set/shared/each_spec.rb +17 -0
  213. data/spec/unit/data_mapper/ordered_set/shared/empty_spec.rb +9 -0
  214. data/spec/unit/data_mapper/ordered_set/shared/entries_spec.rb +9 -0
  215. data/spec/unit/data_mapper/ordered_set/shared/include_spec.rb +9 -0
  216. data/spec/unit/data_mapper/ordered_set/shared/index_spec.rb +13 -0
  217. data/spec/unit/data_mapper/ordered_set/shared/initialize_spec.rb +28 -0
  218. data/spec/unit/data_mapper/ordered_set/shared/merge_spec.rb +28 -0
  219. data/spec/unit/data_mapper/ordered_set/shared/size_spec.rb +13 -0
  220. data/spec/unit/data_mapper/ordered_set/shared/to_ary_spec.rb +11 -0
  221. data/spec/unit/data_mapper/ordered_set/size_spec.rb +27 -0
  222. data/spec/unit/data_mapper/ordered_set/to_ary_spec.rb +23 -0
  223. data/spec/unit/data_mapper/subject_set/append_spec.rb +47 -0
  224. data/spec/unit/data_mapper/subject_set/clear_spec.rb +34 -0
  225. data/spec/unit/data_mapper/subject_set/delete_spec.rb +40 -0
  226. data/spec/unit/data_mapper/subject_set/each_spec.rb +30 -0
  227. data/spec/unit/data_mapper/subject_set/empty_spec.rb +31 -0
  228. data/spec/unit/data_mapper/subject_set/entries_spec.rb +31 -0
  229. data/spec/unit/data_mapper/subject_set/get_spec.rb +34 -0
  230. data/spec/unit/data_mapper/subject_set/include_spec.rb +32 -0
  231. data/spec/unit/data_mapper/subject_set/named_spec.rb +33 -0
  232. data/spec/unit/data_mapper/subject_set/shared/append_spec.rb +18 -0
  233. data/spec/unit/data_mapper/subject_set/shared/clear_spec.rb +9 -0
  234. data/spec/unit/data_mapper/subject_set/shared/delete_spec.rb +9 -0
  235. data/spec/unit/data_mapper/subject_set/shared/each_spec.rb +9 -0
  236. data/spec/unit/data_mapper/subject_set/shared/empty_spec.rb +9 -0
  237. data/spec/unit/data_mapper/subject_set/shared/entries_spec.rb +9 -0
  238. data/spec/unit/data_mapper/subject_set/shared/get_spec.rb +9 -0
  239. data/spec/unit/data_mapper/subject_set/shared/include_spec.rb +9 -0
  240. data/spec/unit/data_mapper/subject_set/shared/named_spec.rb +9 -0
  241. data/spec/unit/data_mapper/subject_set/shared/size_spec.rb +13 -0
  242. data/spec/unit/data_mapper/subject_set/shared/to_ary_spec.rb +9 -0
  243. data/spec/unit/data_mapper/subject_set/shared/values_at_spec.rb +44 -0
  244. data/spec/unit/data_mapper/subject_set/size_spec.rb +42 -0
  245. data/spec/unit/data_mapper/subject_set/to_ary_spec.rb +34 -0
  246. data/spec/unit/data_mapper/subject_set/values_at_spec.rb +57 -0
  247. data/spec/unit/hash_spec.rb +28 -0
  248. data/spec/unit/hook_spec.rb +1235 -0
  249. data/spec/unit/lazy_array_spec.rb +1949 -0
  250. data/spec/unit/mash_spec.rb +312 -0
  251. data/spec/unit/module_spec.rb +71 -0
  252. data/spec/unit/object_spec.rb +38 -0
  253. data/spec/unit/try_dup_spec.rb +46 -0
  254. data/tasks/ci.rake +1 -0
  255. data/tasks/db.rake +11 -0
  256. data/tasks/spec.rake +38 -0
  257. data/tasks/yard.rake +9 -0
  258. data/tasks/yardstick.rake +19 -0
  259. metadata +491 -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