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,721 @@
|
|
|
1
|
+
module DataMapper
|
|
2
|
+
class Query
|
|
3
|
+
module Conditions
|
|
4
|
+
class Operation
|
|
5
|
+
# Factory method to initialize an operation
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# operation = Operation.new(:and, comparison)
|
|
9
|
+
#
|
|
10
|
+
# @param [Symbol] slug
|
|
11
|
+
# the identifier for the operation class
|
|
12
|
+
# @param [Array] *operands
|
|
13
|
+
# the operands to initialize the operation with
|
|
14
|
+
#
|
|
15
|
+
# @return [AbstractOperation]
|
|
16
|
+
# the operation matching the slug
|
|
17
|
+
#
|
|
18
|
+
# @api semipublic
|
|
19
|
+
def self.new(slug, *operands)
|
|
20
|
+
if klass = operation_class(slug)
|
|
21
|
+
klass.new(*operands)
|
|
22
|
+
else
|
|
23
|
+
raise ArgumentError, "No Operation class for #{slug.inspect} has been defined"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Return an Array of all the slugs for the operation classes
|
|
28
|
+
#
|
|
29
|
+
# @return [Array]
|
|
30
|
+
# the slugs of all the operation classes
|
|
31
|
+
#
|
|
32
|
+
# @api private
|
|
33
|
+
def self.slugs
|
|
34
|
+
AbstractOperation.descendants.map { |operation_class| operation_class.slug }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class << self
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# Returns a Hash mapping the slugs to each class
|
|
41
|
+
#
|
|
42
|
+
# @return [Hash]
|
|
43
|
+
# Hash mapping the slug to the class
|
|
44
|
+
#
|
|
45
|
+
# @api private
|
|
46
|
+
def operation_classes
|
|
47
|
+
@operation_classes ||= {}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Lookup the operation class based on the slug
|
|
51
|
+
#
|
|
52
|
+
# @example
|
|
53
|
+
# operation_class = Operation.operation_class(:and)
|
|
54
|
+
#
|
|
55
|
+
# @param [Symbol] slug
|
|
56
|
+
# the identifier for the operation class
|
|
57
|
+
#
|
|
58
|
+
# @return [Class]
|
|
59
|
+
# the operation class
|
|
60
|
+
#
|
|
61
|
+
# @api private
|
|
62
|
+
def operation_class(slug)
|
|
63
|
+
operation_classes[slug] ||= AbstractOperation.descendants.detect { |operation_class| operation_class.slug == slug }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end # class Operation
|
|
67
|
+
|
|
68
|
+
class AbstractOperation
|
|
69
|
+
include DataMapper::Assertions
|
|
70
|
+
include Enumerable
|
|
71
|
+
extend Equalizer
|
|
72
|
+
|
|
73
|
+
equalize :sorted_operands
|
|
74
|
+
|
|
75
|
+
# Returns the parent operation
|
|
76
|
+
#
|
|
77
|
+
# @return [AbstractOperation]
|
|
78
|
+
# the parent operation
|
|
79
|
+
#
|
|
80
|
+
# @api semipublic
|
|
81
|
+
attr_accessor :parent
|
|
82
|
+
|
|
83
|
+
# Returns the child operations and comparisons
|
|
84
|
+
#
|
|
85
|
+
# @return [Set<AbstractOperation, AbstractComparison, Array>]
|
|
86
|
+
# the set of operations and comparisons
|
|
87
|
+
#
|
|
88
|
+
# @api semipublic
|
|
89
|
+
attr_reader :operands
|
|
90
|
+
|
|
91
|
+
alias_method :children, :operands
|
|
92
|
+
|
|
93
|
+
# Returns the classes that inherit from AbstractComparison
|
|
94
|
+
#
|
|
95
|
+
# @return [Set]
|
|
96
|
+
# the descendant classes
|
|
97
|
+
#
|
|
98
|
+
# @api private
|
|
99
|
+
def self.descendants
|
|
100
|
+
@descendants ||= DescendantSet.new
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Hook executed when inheriting from AbstractComparison
|
|
104
|
+
#
|
|
105
|
+
# @return [undefined]
|
|
106
|
+
#
|
|
107
|
+
# @api private
|
|
108
|
+
def self.inherited(descendant)
|
|
109
|
+
descendants << descendant
|
|
110
|
+
super
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Get and set the slug for the operation class
|
|
114
|
+
#
|
|
115
|
+
# @param [Symbol] slug
|
|
116
|
+
# optionally set the slug for the operation class
|
|
117
|
+
#
|
|
118
|
+
# @return [Symbol]
|
|
119
|
+
# the slug for the operation class
|
|
120
|
+
#
|
|
121
|
+
# @api semipublic
|
|
122
|
+
def self.slug(slug = nil)
|
|
123
|
+
slug ? @slug = slug : @slug
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Return the comparison class slug
|
|
127
|
+
#
|
|
128
|
+
# @return [Symbol]
|
|
129
|
+
# the comparison class slug
|
|
130
|
+
#
|
|
131
|
+
# @api private
|
|
132
|
+
def slug
|
|
133
|
+
self.class.slug
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Get the first operand
|
|
137
|
+
#
|
|
138
|
+
# @return [AbstractOperation, AbstractComparison, Array]
|
|
139
|
+
# returns the first operand
|
|
140
|
+
#
|
|
141
|
+
# @api semipublic
|
|
142
|
+
def first
|
|
143
|
+
each { |operand| return operand }
|
|
144
|
+
nil
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Iterate through each operand in the operation
|
|
148
|
+
#
|
|
149
|
+
# @yield [operand]
|
|
150
|
+
# yields to each operand
|
|
151
|
+
#
|
|
152
|
+
# @yieldparam [AbstractOperation, AbstractComparison, Array] operand
|
|
153
|
+
# each operand
|
|
154
|
+
#
|
|
155
|
+
# @return [self]
|
|
156
|
+
# returns the operation
|
|
157
|
+
#
|
|
158
|
+
# @api semipublic
|
|
159
|
+
def each
|
|
160
|
+
@operands.each { |op| yield op }
|
|
161
|
+
self
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Test to see if there are operands
|
|
165
|
+
#
|
|
166
|
+
# @return [Boolean]
|
|
167
|
+
# returns true if there are operands
|
|
168
|
+
#
|
|
169
|
+
# @api semipublic
|
|
170
|
+
def empty?
|
|
171
|
+
@operands.empty?
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Test to see if there is one operand
|
|
175
|
+
#
|
|
176
|
+
# @return [Boolean]
|
|
177
|
+
# true if there is only one operand
|
|
178
|
+
#
|
|
179
|
+
# @api semipublic
|
|
180
|
+
def one?
|
|
181
|
+
@operands.size == 1
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Test if the operation is valid
|
|
185
|
+
#
|
|
186
|
+
# @return [Boolean]
|
|
187
|
+
# true if the operation is valid, false if not
|
|
188
|
+
#
|
|
189
|
+
# @api semipublic
|
|
190
|
+
def valid?
|
|
191
|
+
any? && all? { |op| valid_operand?(op) }
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Add an operand to the operation
|
|
195
|
+
#
|
|
196
|
+
# @param [AbstractOperation, AbstractComparison, Array] operand
|
|
197
|
+
# the operand to add
|
|
198
|
+
#
|
|
199
|
+
# @return [self]
|
|
200
|
+
# the operation
|
|
201
|
+
#
|
|
202
|
+
# @api semipublic
|
|
203
|
+
def <<(operand)
|
|
204
|
+
assert_valid_operand_type(operand)
|
|
205
|
+
@operands << relate_operand(operand)
|
|
206
|
+
self
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Add operands to the operation
|
|
210
|
+
#
|
|
211
|
+
# @param [#each] operands
|
|
212
|
+
# the operands to add
|
|
213
|
+
#
|
|
214
|
+
# @return [self]
|
|
215
|
+
# the operation
|
|
216
|
+
#
|
|
217
|
+
# @api semipublic
|
|
218
|
+
def merge(operands)
|
|
219
|
+
operands.each { |op| self << op }
|
|
220
|
+
self
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Return the union with another operand
|
|
224
|
+
#
|
|
225
|
+
# @param [AbstractOperation] other
|
|
226
|
+
# the operand to union with
|
|
227
|
+
#
|
|
228
|
+
# @return [OrOperation]
|
|
229
|
+
# the union of the operation and operand
|
|
230
|
+
#
|
|
231
|
+
# @api semipublic
|
|
232
|
+
def union(other)
|
|
233
|
+
Operation.new(:or, dup, other.dup).minimize
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
alias_method :|, :union
|
|
237
|
+
alias_method :+, :union
|
|
238
|
+
|
|
239
|
+
# Return the intersection of the operation and another operand
|
|
240
|
+
#
|
|
241
|
+
# @param [AbstractOperation] other
|
|
242
|
+
# the operand to intersect with
|
|
243
|
+
#
|
|
244
|
+
# @return [AndOperation]
|
|
245
|
+
# the intersection of the operation and operand
|
|
246
|
+
#
|
|
247
|
+
# @api semipublic
|
|
248
|
+
def intersection(other)
|
|
249
|
+
Operation.new(:and, dup, other.dup).minimize
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
alias_method :&, :intersection
|
|
253
|
+
|
|
254
|
+
# Return the difference of the operation and another operand
|
|
255
|
+
#
|
|
256
|
+
# @param [AbstractOperation] other
|
|
257
|
+
# the operand to not match
|
|
258
|
+
#
|
|
259
|
+
# @return [AndOperation]
|
|
260
|
+
# the intersection of the operation and operand
|
|
261
|
+
#
|
|
262
|
+
# @api semipublic
|
|
263
|
+
def difference(other)
|
|
264
|
+
Operation.new(:and, dup, Operation.new(:not, other.dup)).minimize
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
alias_method :-, :difference
|
|
268
|
+
|
|
269
|
+
# Minimize the operation
|
|
270
|
+
#
|
|
271
|
+
# @return [self]
|
|
272
|
+
# the minimized operation
|
|
273
|
+
#
|
|
274
|
+
# @api semipublic
|
|
275
|
+
def minimize
|
|
276
|
+
self
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Clear the operands
|
|
280
|
+
#
|
|
281
|
+
# @return [self]
|
|
282
|
+
# the operation
|
|
283
|
+
#
|
|
284
|
+
# @api semipublic
|
|
285
|
+
def clear
|
|
286
|
+
@operands.clear
|
|
287
|
+
self
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Return the string representation of the operation
|
|
291
|
+
#
|
|
292
|
+
# @return [String]
|
|
293
|
+
# the string representation of the operation
|
|
294
|
+
#
|
|
295
|
+
# @api semipublic
|
|
296
|
+
def to_s
|
|
297
|
+
empty? ? '' : "(#{sort_by { |op| op.to_s }.map { |op| op.to_s }.join(" #{slug.to_s.upcase} ")})"
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Test if the operation is negated
|
|
301
|
+
#
|
|
302
|
+
# Defaults to return false.
|
|
303
|
+
#
|
|
304
|
+
# @return [Boolean]
|
|
305
|
+
# true if the operation is negated, false if not
|
|
306
|
+
#
|
|
307
|
+
# @api private
|
|
308
|
+
def negated?
|
|
309
|
+
parent = self.parent
|
|
310
|
+
parent ? parent.negated? : false
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# Return a list of operands in predictable order
|
|
314
|
+
#
|
|
315
|
+
# @return [Array<AbstractOperation, AbstractComparison, Array>]
|
|
316
|
+
# list of operands sorted in deterministic order
|
|
317
|
+
#
|
|
318
|
+
# @api private
|
|
319
|
+
def sorted_operands
|
|
320
|
+
sort_by { |op| op.hash }
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
private
|
|
324
|
+
|
|
325
|
+
# Initialize an operation
|
|
326
|
+
#
|
|
327
|
+
# @param [Array<AbstractOperation, AbstractComparison, Array>] *operands
|
|
328
|
+
# the operands to include in the operation
|
|
329
|
+
#
|
|
330
|
+
# @return [AbstractOperation]
|
|
331
|
+
# the operation
|
|
332
|
+
#
|
|
333
|
+
# @api semipublic
|
|
334
|
+
def initialize(*operands)
|
|
335
|
+
@operands = Set.new
|
|
336
|
+
merge(operands)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# Copy an operation
|
|
340
|
+
#
|
|
341
|
+
# @param [AbstractOperation] original
|
|
342
|
+
# the original operation
|
|
343
|
+
#
|
|
344
|
+
# @return [undefined]
|
|
345
|
+
#
|
|
346
|
+
# @api semipublic
|
|
347
|
+
def initialize_copy(*)
|
|
348
|
+
@operands = map { |op| op.dup }.to_set
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# Minimize the operands recursively
|
|
352
|
+
#
|
|
353
|
+
# @return [undefined]
|
|
354
|
+
#
|
|
355
|
+
# @api private
|
|
356
|
+
def minimize_operands
|
|
357
|
+
# FIXME: why does Set#map! not work here?
|
|
358
|
+
@operands = map do |op|
|
|
359
|
+
relate_operand(op.respond_to?(:minimize) ? op.minimize : op)
|
|
360
|
+
end.to_set
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Prune empty operands recursively
|
|
364
|
+
#
|
|
365
|
+
# @return [undefined]
|
|
366
|
+
#
|
|
367
|
+
# @api private
|
|
368
|
+
def prune_operands
|
|
369
|
+
@operands.delete_if { |op| op.respond_to?(:empty?) ? op.empty? : false }
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Test if the operand is valid
|
|
373
|
+
#
|
|
374
|
+
# @param [AbstractOperation, AbstractComparison, Array] operand
|
|
375
|
+
# the operand to test
|
|
376
|
+
#
|
|
377
|
+
# @return [Boolean]
|
|
378
|
+
# true if the operand is valid
|
|
379
|
+
#
|
|
380
|
+
# @api private
|
|
381
|
+
def valid_operand?(operand)
|
|
382
|
+
if operand.respond_to?(:valid?)
|
|
383
|
+
operand.valid?
|
|
384
|
+
else
|
|
385
|
+
true
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# Set self to be the operand's parent
|
|
390
|
+
#
|
|
391
|
+
# @return [AbstractOperation, AbstractComparison, Array]
|
|
392
|
+
# the operand that was related to self
|
|
393
|
+
#
|
|
394
|
+
# @api private
|
|
395
|
+
def relate_operand(operand)
|
|
396
|
+
operand.parent = self if operand.respond_to?(:parent=)
|
|
397
|
+
operand
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# Assert that the operand is a valid type
|
|
401
|
+
#
|
|
402
|
+
# @param [AbstractOperation, AbstractComparison, Array] operand
|
|
403
|
+
# the operand to test
|
|
404
|
+
#
|
|
405
|
+
# @return [undefined]
|
|
406
|
+
#
|
|
407
|
+
# @raise [ArgumentError]
|
|
408
|
+
# raised if the operand is not a valid type
|
|
409
|
+
#
|
|
410
|
+
# @api private
|
|
411
|
+
def assert_valid_operand_type(operand)
|
|
412
|
+
assert_kind_of 'operand', operand, AbstractOperation, AbstractComparison, Array
|
|
413
|
+
end
|
|
414
|
+
end # class AbstractOperation
|
|
415
|
+
|
|
416
|
+
module FlattenOperation
|
|
417
|
+
# Add an operand to the operation, flattening the same types
|
|
418
|
+
#
|
|
419
|
+
# Flattening means that if the operand is the same as the
|
|
420
|
+
# operation, we should just include the operand's operands
|
|
421
|
+
# in the operation and prune that part of the tree. This results
|
|
422
|
+
# in a shallower tree, is faster to match and usually generates
|
|
423
|
+
# more efficient queries in the adapters.
|
|
424
|
+
#
|
|
425
|
+
# @param [AbstractOperation, AbstractComparison, Array] operand
|
|
426
|
+
# the operand to add
|
|
427
|
+
#
|
|
428
|
+
# @return [self]
|
|
429
|
+
# the operation
|
|
430
|
+
#
|
|
431
|
+
# @api semipublic
|
|
432
|
+
def <<(operand)
|
|
433
|
+
if kind_of?(operand.class)
|
|
434
|
+
merge(operand.operands)
|
|
435
|
+
else
|
|
436
|
+
super
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
end # module FlattenOperation
|
|
440
|
+
|
|
441
|
+
class AndOperation < AbstractOperation
|
|
442
|
+
include FlattenOperation
|
|
443
|
+
|
|
444
|
+
slug :and
|
|
445
|
+
|
|
446
|
+
# Match the record
|
|
447
|
+
#
|
|
448
|
+
# @example with a Hash
|
|
449
|
+
# operation.matches?({ :id => 1 }) # => true
|
|
450
|
+
#
|
|
451
|
+
# @example with a Resource
|
|
452
|
+
# operation.matches?(Blog::Article.new(:id => 1)) # => true
|
|
453
|
+
#
|
|
454
|
+
# @param [Resource, Hash] record
|
|
455
|
+
# the resource to match
|
|
456
|
+
#
|
|
457
|
+
# @return [true]
|
|
458
|
+
# true if the record matches, false if not
|
|
459
|
+
#
|
|
460
|
+
# @api semipublic
|
|
461
|
+
def matches?(record)
|
|
462
|
+
all? { |op| op.respond_to?(:matches?) ? op.matches?(record) : true }
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# Minimize the operation
|
|
466
|
+
#
|
|
467
|
+
# @return [self]
|
|
468
|
+
# the minimized AndOperation
|
|
469
|
+
# @return [AbstractOperation, AbstractComparison, Array]
|
|
470
|
+
# the minimized operation
|
|
471
|
+
#
|
|
472
|
+
# @api semipublic
|
|
473
|
+
def minimize
|
|
474
|
+
minimize_operands
|
|
475
|
+
|
|
476
|
+
return Operation.new(:null) if any? && all? { |op| op.nil? }
|
|
477
|
+
|
|
478
|
+
prune_operands
|
|
479
|
+
|
|
480
|
+
one? ? first : self
|
|
481
|
+
end
|
|
482
|
+
end # class AndOperation
|
|
483
|
+
|
|
484
|
+
class OrOperation < AbstractOperation
|
|
485
|
+
include FlattenOperation
|
|
486
|
+
|
|
487
|
+
slug :or
|
|
488
|
+
|
|
489
|
+
# Match the record
|
|
490
|
+
#
|
|
491
|
+
# @param [Resource, Hash] record
|
|
492
|
+
# the resource to match
|
|
493
|
+
#
|
|
494
|
+
# @return [true]
|
|
495
|
+
# true if the record matches, false if not
|
|
496
|
+
#
|
|
497
|
+
# @api semipublic
|
|
498
|
+
def matches?(record)
|
|
499
|
+
any? { |op| op.respond_to?(:matches?) ? op.matches?(record) : true }
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# Test if the operation is valid
|
|
503
|
+
#
|
|
504
|
+
# An OrOperation is valid if one of it's operands is valid.
|
|
505
|
+
#
|
|
506
|
+
# @return [Boolean]
|
|
507
|
+
# true if the operation is valid, false if not
|
|
508
|
+
#
|
|
509
|
+
# @api semipublic
|
|
510
|
+
def valid?
|
|
511
|
+
any? { |op| valid_operand?(op) }
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
# Minimize the operation
|
|
515
|
+
#
|
|
516
|
+
# @return [self]
|
|
517
|
+
# the minimized OrOperation
|
|
518
|
+
# @return [AbstractOperation, AbstractComparison, Array]
|
|
519
|
+
# the minimized operation
|
|
520
|
+
#
|
|
521
|
+
# @api semipublic
|
|
522
|
+
def minimize
|
|
523
|
+
minimize_operands
|
|
524
|
+
|
|
525
|
+
return Operation.new(:null) if any? { |op| op.nil? }
|
|
526
|
+
|
|
527
|
+
prune_operands
|
|
528
|
+
|
|
529
|
+
one? ? first : self
|
|
530
|
+
end
|
|
531
|
+
end # class OrOperation
|
|
532
|
+
|
|
533
|
+
class NotOperation < AbstractOperation
|
|
534
|
+
slug :not
|
|
535
|
+
|
|
536
|
+
# Match the record
|
|
537
|
+
#
|
|
538
|
+
# @param [Resource, Hash] record
|
|
539
|
+
# the resource to match
|
|
540
|
+
#
|
|
541
|
+
# @return [true]
|
|
542
|
+
# true if the record matches, false if not
|
|
543
|
+
#
|
|
544
|
+
# @api semipublic
|
|
545
|
+
def matches?(record)
|
|
546
|
+
operand = self.operand
|
|
547
|
+
operand.respond_to?(:matches?) ? !operand.matches?(record) : true
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
# Add an operand to the operation
|
|
551
|
+
#
|
|
552
|
+
# This will only allow a single operand to be added.
|
|
553
|
+
#
|
|
554
|
+
# @param [AbstractOperation, AbstractComparison, Array] operand
|
|
555
|
+
# the operand to add
|
|
556
|
+
#
|
|
557
|
+
# @return [self]
|
|
558
|
+
# the operation
|
|
559
|
+
#
|
|
560
|
+
# @api semipublic
|
|
561
|
+
def <<(operand)
|
|
562
|
+
assert_one_operand(operand)
|
|
563
|
+
assert_no_self_reference(operand)
|
|
564
|
+
super
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
# Return the only operand in the operation
|
|
568
|
+
#
|
|
569
|
+
# @return [AbstractOperation, AbstractComparison, Array]
|
|
570
|
+
# the operand
|
|
571
|
+
#
|
|
572
|
+
# @api semipublic
|
|
573
|
+
def operand
|
|
574
|
+
first
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
# Minimize the operation
|
|
578
|
+
#
|
|
579
|
+
# @return [self]
|
|
580
|
+
# the minimized NotOperation
|
|
581
|
+
# @return [AbstractOperation, AbstractComparison, Array]
|
|
582
|
+
# the minimized operation
|
|
583
|
+
#
|
|
584
|
+
# @api semipublic
|
|
585
|
+
def minimize
|
|
586
|
+
minimize_operands
|
|
587
|
+
prune_operands
|
|
588
|
+
|
|
589
|
+
# factor out double negatives if possible
|
|
590
|
+
operand = self.operand
|
|
591
|
+
one? && instance_of?(operand.class) ? operand.operand : self
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# Return the string representation of the operation
|
|
595
|
+
#
|
|
596
|
+
# @return [String]
|
|
597
|
+
# the string representation of the operation
|
|
598
|
+
#
|
|
599
|
+
# @api semipublic
|
|
600
|
+
def to_s
|
|
601
|
+
empty? ? '' : "NOT(#{operand.to_s})"
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
# Test if the operation is negated
|
|
605
|
+
#
|
|
606
|
+
# Defaults to return false.
|
|
607
|
+
#
|
|
608
|
+
# @return [Boolean]
|
|
609
|
+
# true if the operation is negated, false if not
|
|
610
|
+
#
|
|
611
|
+
# @api private
|
|
612
|
+
def negated?
|
|
613
|
+
parent = self.parent
|
|
614
|
+
parent ? !parent.negated? : true
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
private
|
|
618
|
+
|
|
619
|
+
# Assert there is only one operand
|
|
620
|
+
#
|
|
621
|
+
# @param [AbstractOperation, AbstractComparison, Array] operand
|
|
622
|
+
# the operand to test
|
|
623
|
+
#
|
|
624
|
+
# @return [undefined]
|
|
625
|
+
#
|
|
626
|
+
# @raise [ArgumentError]
|
|
627
|
+
# raised if the operand is not a valid type
|
|
628
|
+
#
|
|
629
|
+
# @api private
|
|
630
|
+
def assert_one_operand(operand)
|
|
631
|
+
unless empty? || self.operand == operand
|
|
632
|
+
raise ArgumentError, "#{self.class} cannot have more than one operand"
|
|
633
|
+
end
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
# Assert the operand is not equal to self
|
|
637
|
+
#
|
|
638
|
+
# @param [AbstractOperation, AbstractComparison, Array] operand
|
|
639
|
+
# the operand to test
|
|
640
|
+
#
|
|
641
|
+
# @return [undefined]
|
|
642
|
+
#
|
|
643
|
+
# @raise [ArgumentError]
|
|
644
|
+
# raised if object is appended to itself
|
|
645
|
+
#
|
|
646
|
+
# @api private
|
|
647
|
+
def assert_no_self_reference(operand)
|
|
648
|
+
if equal?(operand)
|
|
649
|
+
raise ArgumentError, 'cannot append operand to itself'
|
|
650
|
+
end
|
|
651
|
+
end
|
|
652
|
+
end # class NotOperation
|
|
653
|
+
|
|
654
|
+
class NullOperation < AbstractOperation
|
|
655
|
+
undef_method :<<
|
|
656
|
+
undef_method :merge
|
|
657
|
+
|
|
658
|
+
slug :null
|
|
659
|
+
|
|
660
|
+
# Match the record
|
|
661
|
+
#
|
|
662
|
+
# A NullOperation matches every record.
|
|
663
|
+
#
|
|
664
|
+
# @param [Resource, Hash] record
|
|
665
|
+
# the resource to match
|
|
666
|
+
#
|
|
667
|
+
# @return [true]
|
|
668
|
+
# every record matches
|
|
669
|
+
#
|
|
670
|
+
# @api semipublic
|
|
671
|
+
def matches?(record)
|
|
672
|
+
record.kind_of?(Hash) || record.kind_of?(Resource)
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
# Test validity of the operation
|
|
676
|
+
#
|
|
677
|
+
# A NullOperation is always valid.
|
|
678
|
+
#
|
|
679
|
+
# @return [true]
|
|
680
|
+
# always valid
|
|
681
|
+
#
|
|
682
|
+
# @api semipublic
|
|
683
|
+
def valid?
|
|
684
|
+
true
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
# Treat the operation the same as nil
|
|
688
|
+
#
|
|
689
|
+
# @return [true]
|
|
690
|
+
# should be treated as nil
|
|
691
|
+
#
|
|
692
|
+
# @api semipublic
|
|
693
|
+
def nil?
|
|
694
|
+
true
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
# Inspecting the operation should return the same as nil
|
|
698
|
+
#
|
|
699
|
+
# @return [String]
|
|
700
|
+
# return the string 'nil'
|
|
701
|
+
#
|
|
702
|
+
# @api semipublic
|
|
703
|
+
def inspect
|
|
704
|
+
'nil'
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
private
|
|
708
|
+
|
|
709
|
+
# Initialize a NullOperation
|
|
710
|
+
#
|
|
711
|
+
# @return [NullOperation]
|
|
712
|
+
# the operation
|
|
713
|
+
#
|
|
714
|
+
# @api semipublic
|
|
715
|
+
def initialize
|
|
716
|
+
@operands = Set.new
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
end # module Conditions
|
|
720
|
+
end # class Query
|
|
721
|
+
end # module DataMapper
|