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.
- data/.autotest +29 -0
- data/.document +5 -0
- data/.gitignore +35 -0
- data/.yardopts +1 -0
- data/Gemfile +65 -0
- data/LICENSE +20 -0
- data/README.md +269 -0
- data/Rakefile +4 -0
- data/dm-core.gemspec +24 -0
- data/lib/dm-core.rb +292 -0
- data/lib/dm-core/adapters.rb +222 -0
- data/lib/dm-core/adapters/abstract_adapter.rb +237 -0
- data/lib/dm-core/adapters/in_memory_adapter.rb +113 -0
- data/lib/dm-core/associations/many_to_many.rb +499 -0
- data/lib/dm-core/associations/many_to_one.rb +290 -0
- data/lib/dm-core/associations/one_to_many.rb +348 -0
- data/lib/dm-core/associations/one_to_one.rb +86 -0
- data/lib/dm-core/associations/relationship.rb +663 -0
- data/lib/dm-core/backwards.rb +13 -0
- data/lib/dm-core/collection.rb +1515 -0
- data/lib/dm-core/core_ext/kernel.rb +23 -0
- data/lib/dm-core/core_ext/pathname.rb +6 -0
- data/lib/dm-core/core_ext/symbol.rb +10 -0
- data/lib/dm-core/identity_map.rb +7 -0
- data/lib/dm-core/model.rb +874 -0
- data/lib/dm-core/model/hook.rb +103 -0
- data/lib/dm-core/model/is.rb +32 -0
- data/lib/dm-core/model/property.rb +249 -0
- data/lib/dm-core/model/relationship.rb +378 -0
- data/lib/dm-core/model/scope.rb +89 -0
- data/lib/dm-core/property.rb +866 -0
- data/lib/dm-core/property/binary.rb +21 -0
- data/lib/dm-core/property/boolean.rb +20 -0
- data/lib/dm-core/property/class.rb +17 -0
- data/lib/dm-core/property/date.rb +10 -0
- data/lib/dm-core/property/date_time.rb +10 -0
- data/lib/dm-core/property/decimal.rb +36 -0
- data/lib/dm-core/property/discriminator.rb +44 -0
- data/lib/dm-core/property/float.rb +16 -0
- data/lib/dm-core/property/integer.rb +22 -0
- data/lib/dm-core/property/invalid_value_error.rb +22 -0
- data/lib/dm-core/property/lookup.rb +27 -0
- data/lib/dm-core/property/numeric.rb +38 -0
- data/lib/dm-core/property/object.rb +34 -0
- data/lib/dm-core/property/serial.rb +14 -0
- data/lib/dm-core/property/string.rb +38 -0
- data/lib/dm-core/property/text.rb +9 -0
- data/lib/dm-core/property/time.rb +10 -0
- data/lib/dm-core/property_set.rb +177 -0
- data/lib/dm-core/query.rb +1366 -0
- data/lib/dm-core/query/conditions/comparison.rb +911 -0
- data/lib/dm-core/query/conditions/operation.rb +721 -0
- data/lib/dm-core/query/direction.rb +36 -0
- data/lib/dm-core/query/operator.rb +35 -0
- data/lib/dm-core/query/path.rb +114 -0
- data/lib/dm-core/query/sort.rb +39 -0
- data/lib/dm-core/relationship_set.rb +72 -0
- data/lib/dm-core/repository.rb +226 -0
- data/lib/dm-core/resource.rb +1214 -0
- data/lib/dm-core/resource/persistence_state.rb +75 -0
- data/lib/dm-core/resource/persistence_state/clean.rb +40 -0
- data/lib/dm-core/resource/persistence_state/deleted.rb +30 -0
- data/lib/dm-core/resource/persistence_state/dirty.rb +96 -0
- data/lib/dm-core/resource/persistence_state/immutable.rb +34 -0
- data/lib/dm-core/resource/persistence_state/persisted.rb +29 -0
- data/lib/dm-core/resource/persistence_state/transient.rb +80 -0
- data/lib/dm-core/spec/lib/adapter_helpers.rb +64 -0
- data/lib/dm-core/spec/lib/collection_helpers.rb +21 -0
- data/lib/dm-core/spec/lib/counter_adapter.rb +38 -0
- data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
- data/lib/dm-core/spec/lib/spec_helper.rb +74 -0
- data/lib/dm-core/spec/setup.rb +174 -0
- data/lib/dm-core/spec/shared/adapter_spec.rb +341 -0
- data/lib/dm-core/spec/shared/public/property_spec.rb +229 -0
- data/lib/dm-core/spec/shared/resource_spec.rb +1232 -0
- data/lib/dm-core/spec/shared/sel_spec.rb +111 -0
- data/lib/dm-core/spec/shared/semipublic/property_spec.rb +176 -0
- data/lib/dm-core/spec/shared/semipublic/query/conditions/abstract_comparison_spec.rb +261 -0
- data/lib/dm-core/support/assertions.rb +8 -0
- data/lib/dm-core/support/chainable.rb +18 -0
- data/lib/dm-core/support/deprecate.rb +12 -0
- data/lib/dm-core/support/descendant_set.rb +89 -0
- data/lib/dm-core/support/equalizer.rb +48 -0
- data/lib/dm-core/support/ext/array.rb +22 -0
- data/lib/dm-core/support/ext/blank.rb +25 -0
- data/lib/dm-core/support/ext/hash.rb +67 -0
- data/lib/dm-core/support/ext/module.rb +47 -0
- data/lib/dm-core/support/ext/object.rb +57 -0
- data/lib/dm-core/support/ext/string.rb +24 -0
- data/lib/dm-core/support/ext/try_dup.rb +12 -0
- data/lib/dm-core/support/hook.rb +405 -0
- data/lib/dm-core/support/inflections.rb +60 -0
- data/lib/dm-core/support/inflector/inflections.rb +211 -0
- data/lib/dm-core/support/inflector/methods.rb +151 -0
- data/lib/dm-core/support/lazy_array.rb +451 -0
- data/lib/dm-core/support/local_object_space.rb +13 -0
- data/lib/dm-core/support/logger.rb +201 -0
- data/lib/dm-core/support/mash.rb +176 -0
- data/lib/dm-core/support/naming_conventions.rb +90 -0
- data/lib/dm-core/support/ordered_set.rb +380 -0
- data/lib/dm-core/support/subject.rb +33 -0
- data/lib/dm-core/support/subject_set.rb +250 -0
- data/lib/dm-core/version.rb +3 -0
- data/script/performance.rb +275 -0
- data/script/profile.rb +218 -0
- data/spec/lib/rspec_immediate_feedback_formatter.rb +54 -0
- data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +68 -0
- data/spec/public/associations/many_to_many_spec.rb +197 -0
- data/spec/public/associations/many_to_one_spec.rb +83 -0
- data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +40 -0
- data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
- data/spec/public/associations/one_to_many_spec.rb +81 -0
- data/spec/public/associations/one_to_one_spec.rb +176 -0
- data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +46 -0
- data/spec/public/collection_spec.rb +69 -0
- data/spec/public/finalize_spec.rb +76 -0
- data/spec/public/model/hook_spec.rb +246 -0
- data/spec/public/model/property_spec.rb +88 -0
- data/spec/public/model/relationship_spec.rb +1040 -0
- data/spec/public/model_spec.rb +462 -0
- data/spec/public/property/binary_spec.rb +41 -0
- data/spec/public/property/boolean_spec.rb +22 -0
- data/spec/public/property/class_spec.rb +28 -0
- data/spec/public/property/date_spec.rb +22 -0
- data/spec/public/property/date_time_spec.rb +22 -0
- data/spec/public/property/decimal_spec.rb +23 -0
- data/spec/public/property/discriminator_spec.rb +135 -0
- data/spec/public/property/float_spec.rb +22 -0
- data/spec/public/property/integer_spec.rb +22 -0
- data/spec/public/property/object_spec.rb +107 -0
- data/spec/public/property/serial_spec.rb +22 -0
- data/spec/public/property/string_spec.rb +22 -0
- data/spec/public/property/text_spec.rb +63 -0
- data/spec/public/property/time_spec.rb +22 -0
- data/spec/public/property_spec.rb +341 -0
- data/spec/public/resource_spec.rb +288 -0
- data/spec/public/sel_spec.rb +53 -0
- data/spec/public/setup_spec.rb +145 -0
- data/spec/public/shared/association_collection_shared_spec.rb +309 -0
- data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
- data/spec/public/shared/collection_shared_spec.rb +1667 -0
- data/spec/public/shared/finder_shared_spec.rb +1629 -0
- data/spec/rcov.opts +6 -0
- data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
- data/spec/semipublic/adapters/in_memory_adapter_spec.rb +13 -0
- data/spec/semipublic/associations/many_to_many_spec.rb +94 -0
- data/spec/semipublic/associations/many_to_one_spec.rb +63 -0
- data/spec/semipublic/associations/one_to_many_spec.rb +55 -0
- data/spec/semipublic/associations/one_to_one_spec.rb +53 -0
- data/spec/semipublic/associations/relationship_spec.rb +200 -0
- data/spec/semipublic/associations_spec.rb +177 -0
- data/spec/semipublic/collection_spec.rb +110 -0
- data/spec/semipublic/model_spec.rb +96 -0
- data/spec/semipublic/property/binary_spec.rb +13 -0
- data/spec/semipublic/property/boolean_spec.rb +47 -0
- data/spec/semipublic/property/class_spec.rb +33 -0
- data/spec/semipublic/property/date_spec.rb +43 -0
- data/spec/semipublic/property/date_time_spec.rb +46 -0
- data/spec/semipublic/property/decimal_spec.rb +83 -0
- data/spec/semipublic/property/discriminator_spec.rb +19 -0
- data/spec/semipublic/property/float_spec.rb +82 -0
- data/spec/semipublic/property/integer_spec.rb +82 -0
- data/spec/semipublic/property/lookup_spec.rb +29 -0
- data/spec/semipublic/property/serial_spec.rb +13 -0
- data/spec/semipublic/property/string_spec.rb +13 -0
- data/spec/semipublic/property/text_spec.rb +31 -0
- data/spec/semipublic/property/time_spec.rb +50 -0
- data/spec/semipublic/property_spec.rb +114 -0
- data/spec/semipublic/query/conditions/comparison_spec.rb +1501 -0
- data/spec/semipublic/query/conditions/operation_spec.rb +1294 -0
- data/spec/semipublic/query/path_spec.rb +471 -0
- data/spec/semipublic/query_spec.rb +3682 -0
- data/spec/semipublic/resource/state/clean_spec.rb +88 -0
- data/spec/semipublic/resource/state/deleted_spec.rb +78 -0
- data/spec/semipublic/resource/state/dirty_spec.rb +162 -0
- data/spec/semipublic/resource/state/immutable_spec.rb +105 -0
- data/spec/semipublic/resource/state/transient_spec.rb +162 -0
- data/spec/semipublic/resource/state_spec.rb +230 -0
- data/spec/semipublic/resource_spec.rb +23 -0
- data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
- data/spec/semipublic/shared/resource_shared_spec.rb +199 -0
- data/spec/semipublic/shared/resource_state_shared_spec.rb +79 -0
- data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/core_ext/hash.rb +10 -0
- data/spec/support/core_ext/inheritable_attributes.rb +46 -0
- data/spec/support/properties/huge_integer.rb +17 -0
- data/spec/unit/array_spec.rb +23 -0
- data/spec/unit/blank_spec.rb +73 -0
- data/spec/unit/data_mapper/ordered_set/append_spec.rb +26 -0
- data/spec/unit/data_mapper/ordered_set/clear_spec.rb +24 -0
- data/spec/unit/data_mapper/ordered_set/delete_spec.rb +28 -0
- data/spec/unit/data_mapper/ordered_set/each_spec.rb +19 -0
- data/spec/unit/data_mapper/ordered_set/empty_spec.rb +20 -0
- data/spec/unit/data_mapper/ordered_set/entries_spec.rb +22 -0
- data/spec/unit/data_mapper/ordered_set/eql_spec.rb +51 -0
- data/spec/unit/data_mapper/ordered_set/equal_value_spec.rb +84 -0
- data/spec/unit/data_mapper/ordered_set/hash_spec.rb +12 -0
- data/spec/unit/data_mapper/ordered_set/include_spec.rb +23 -0
- data/spec/unit/data_mapper/ordered_set/index_spec.rb +28 -0
- data/spec/unit/data_mapper/ordered_set/initialize_spec.rb +32 -0
- data/spec/unit/data_mapper/ordered_set/merge_spec.rb +36 -0
- data/spec/unit/data_mapper/ordered_set/shared/append_spec.rb +24 -0
- data/spec/unit/data_mapper/ordered_set/shared/clear_spec.rb +9 -0
- data/spec/unit/data_mapper/ordered_set/shared/delete_spec.rb +25 -0
- data/spec/unit/data_mapper/ordered_set/shared/each_spec.rb +17 -0
- data/spec/unit/data_mapper/ordered_set/shared/empty_spec.rb +9 -0
- data/spec/unit/data_mapper/ordered_set/shared/entries_spec.rb +9 -0
- data/spec/unit/data_mapper/ordered_set/shared/include_spec.rb +9 -0
- data/spec/unit/data_mapper/ordered_set/shared/index_spec.rb +13 -0
- data/spec/unit/data_mapper/ordered_set/shared/initialize_spec.rb +28 -0
- data/spec/unit/data_mapper/ordered_set/shared/merge_spec.rb +28 -0
- data/spec/unit/data_mapper/ordered_set/shared/size_spec.rb +13 -0
- data/spec/unit/data_mapper/ordered_set/shared/to_ary_spec.rb +11 -0
- data/spec/unit/data_mapper/ordered_set/size_spec.rb +27 -0
- data/spec/unit/data_mapper/ordered_set/to_ary_spec.rb +23 -0
- data/spec/unit/data_mapper/subject_set/append_spec.rb +47 -0
- data/spec/unit/data_mapper/subject_set/clear_spec.rb +34 -0
- data/spec/unit/data_mapper/subject_set/delete_spec.rb +40 -0
- data/spec/unit/data_mapper/subject_set/each_spec.rb +30 -0
- data/spec/unit/data_mapper/subject_set/empty_spec.rb +31 -0
- data/spec/unit/data_mapper/subject_set/entries_spec.rb +31 -0
- data/spec/unit/data_mapper/subject_set/get_spec.rb +34 -0
- data/spec/unit/data_mapper/subject_set/include_spec.rb +32 -0
- data/spec/unit/data_mapper/subject_set/named_spec.rb +33 -0
- data/spec/unit/data_mapper/subject_set/shared/append_spec.rb +18 -0
- data/spec/unit/data_mapper/subject_set/shared/clear_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/delete_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/each_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/empty_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/entries_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/get_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/include_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/named_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/size_spec.rb +13 -0
- data/spec/unit/data_mapper/subject_set/shared/to_ary_spec.rb +9 -0
- data/spec/unit/data_mapper/subject_set/shared/values_at_spec.rb +44 -0
- data/spec/unit/data_mapper/subject_set/size_spec.rb +42 -0
- data/spec/unit/data_mapper/subject_set/to_ary_spec.rb +34 -0
- data/spec/unit/data_mapper/subject_set/values_at_spec.rb +57 -0
- data/spec/unit/hash_spec.rb +28 -0
- data/spec/unit/hook_spec.rb +1235 -0
- data/spec/unit/inflections_spec.rb +16 -0
- data/spec/unit/lazy_array_spec.rb +1949 -0
- data/spec/unit/mash_spec.rb +312 -0
- data/spec/unit/module_spec.rb +71 -0
- data/spec/unit/object_spec.rb +38 -0
- data/spec/unit/try_dup_spec.rb +46 -0
- data/tasks/ci.rake +1 -0
- data/tasks/spec.rake +38 -0
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- 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
|