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,911 @@
|
|
|
1
|
+
module DataMapper
|
|
2
|
+
class Query
|
|
3
|
+
# The Conditions module contains classes used as part of a Query when
|
|
4
|
+
# filtering collections of resources.
|
|
5
|
+
#
|
|
6
|
+
# The Conditions module contains two types of class used for filtering
|
|
7
|
+
# queries: Comparison and Operation. Although these are used on all
|
|
8
|
+
# repository types -- not just SQL-based repos -- these classes are best
|
|
9
|
+
# thought of as being the DataMapper counterpart to an SQL WHERE clause.
|
|
10
|
+
#
|
|
11
|
+
# Comparisons compare properties and relationships with values, while
|
|
12
|
+
# operations tie Comparisons together to form more complex expressions.
|
|
13
|
+
#
|
|
14
|
+
# For example, the following SQL query fragment:
|
|
15
|
+
#
|
|
16
|
+
# ... WHERE my_field = my_value AND another_field = another_value ...
|
|
17
|
+
#
|
|
18
|
+
# ... would be represented as two EqualToComparison instances tied
|
|
19
|
+
# together with an AndOperation.
|
|
20
|
+
#
|
|
21
|
+
# Conditions -- together with the Query class -- allow DataMapper to
|
|
22
|
+
# represent SQL-like expressions in an ORM-agnostic manner, and are used
|
|
23
|
+
# for both in-memory filtering of loaded Collection instances, and by
|
|
24
|
+
# adapters to retrieve records directly from your repositories.
|
|
25
|
+
#
|
|
26
|
+
# The classes contained in the Conditions module are for internal use by
|
|
27
|
+
# DataMapper and DataMapper plugins, and are not intended to be used
|
|
28
|
+
# directly in your applications.
|
|
29
|
+
module Conditions
|
|
30
|
+
|
|
31
|
+
# An abstract class which provides easy access to comparison operators
|
|
32
|
+
#
|
|
33
|
+
# @example Creating a new comparison
|
|
34
|
+
# Comparison.new(:eql, MyClass.my_property, "value")
|
|
35
|
+
#
|
|
36
|
+
class Comparison
|
|
37
|
+
|
|
38
|
+
# Creates a new Comparison instance
|
|
39
|
+
#
|
|
40
|
+
# The returned instance will be suitable for matching the given
|
|
41
|
+
# subject (property or relationship) against the value.
|
|
42
|
+
#
|
|
43
|
+
# @param [Symbol] slug
|
|
44
|
+
# The type of comparison operator required. One of: :eql, :in, :gt,
|
|
45
|
+
# :gte, :lt, :lte, :regexp, :like.
|
|
46
|
+
# @param [Property, Associations::Relationship]
|
|
47
|
+
# The subject of the comparison - the value of the subject will be
|
|
48
|
+
# matched against the given value parameter.
|
|
49
|
+
# @param [Object] value
|
|
50
|
+
# The value for the comparison.
|
|
51
|
+
#
|
|
52
|
+
# @return [DataMapper::Query::Conditions::AbstractComparison]
|
|
53
|
+
#
|
|
54
|
+
# @example
|
|
55
|
+
# Comparison.new(:eql, MyClass.properties[:id], 1)
|
|
56
|
+
#
|
|
57
|
+
# @api semipublic
|
|
58
|
+
def self.new(slug, subject, value)
|
|
59
|
+
if klass = comparison_class(slug)
|
|
60
|
+
klass.new(subject, value)
|
|
61
|
+
else
|
|
62
|
+
raise ArgumentError, "No Comparison class for #{slug.inspect} has been defined"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns an array of all slugs registered with Comparison
|
|
67
|
+
#
|
|
68
|
+
# @return [Array<Symbol>]
|
|
69
|
+
#
|
|
70
|
+
# @api private
|
|
71
|
+
def self.slugs
|
|
72
|
+
AbstractComparison.descendants.map { |comparison_class| comparison_class.slug }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class << self
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
# Holds comparison subclasses keyed on their slug
|
|
79
|
+
#
|
|
80
|
+
# @return [Hash]
|
|
81
|
+
#
|
|
82
|
+
# @api private
|
|
83
|
+
def comparison_classes
|
|
84
|
+
@comparison_classes ||= {}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Returns the comparison class identified by the given slug
|
|
88
|
+
#
|
|
89
|
+
# @param [Symbol] slug
|
|
90
|
+
# See slug parameter for Comparison.new
|
|
91
|
+
#
|
|
92
|
+
# @return [AbstractComparison, nil]
|
|
93
|
+
#
|
|
94
|
+
# @api private
|
|
95
|
+
def comparison_class(slug)
|
|
96
|
+
comparison_classes[slug] ||= AbstractComparison.descendants.detect { |comparison_class| comparison_class.slug == slug }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end # class Comparison
|
|
100
|
+
|
|
101
|
+
# A base class for the various comparison classes.
|
|
102
|
+
class AbstractComparison
|
|
103
|
+
extend Equalizer
|
|
104
|
+
|
|
105
|
+
equalize :subject, :value
|
|
106
|
+
|
|
107
|
+
# @api semipublic
|
|
108
|
+
attr_accessor :parent
|
|
109
|
+
|
|
110
|
+
# The property or relationship which is being matched against
|
|
111
|
+
#
|
|
112
|
+
# @return [Property, Associations::Relationship]
|
|
113
|
+
#
|
|
114
|
+
# @api semipublic
|
|
115
|
+
attr_reader :subject
|
|
116
|
+
|
|
117
|
+
# Value to be compared with the subject
|
|
118
|
+
#
|
|
119
|
+
# This value is compared against that contained in the subject when
|
|
120
|
+
# filtering collections, or the value in the repository when
|
|
121
|
+
# performing queries.
|
|
122
|
+
#
|
|
123
|
+
# In the case of primitive property, this is the value as it
|
|
124
|
+
# is stored in the repository.
|
|
125
|
+
#
|
|
126
|
+
# @return [Object]
|
|
127
|
+
#
|
|
128
|
+
# @api semipublic
|
|
129
|
+
def value
|
|
130
|
+
dumped_value
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# The loaded/typecast value
|
|
134
|
+
#
|
|
135
|
+
# In the case of primitive types, this will be the same as +value+,
|
|
136
|
+
# however when using primitive property this stores the loaded value.
|
|
137
|
+
#
|
|
138
|
+
# If writing an adapter, you should use +value+, while plugin authors
|
|
139
|
+
# should refer to +loaded_value+.
|
|
140
|
+
#
|
|
141
|
+
#--
|
|
142
|
+
# As an example, you might use symbols with the Enum type in dm-types
|
|
143
|
+
#
|
|
144
|
+
# property :myprop, Enum[:open, :closed]
|
|
145
|
+
#
|
|
146
|
+
# These are stored in repositories as 1 and 2, respectively. +value+
|
|
147
|
+
# returns the 1 or 2, while +loaded_value+ returns the symbol.
|
|
148
|
+
#++
|
|
149
|
+
#
|
|
150
|
+
# @return [Object]
|
|
151
|
+
#
|
|
152
|
+
# @api semipublic
|
|
153
|
+
attr_reader :loaded_value
|
|
154
|
+
|
|
155
|
+
# Keeps track of AbstractComparison subclasses (used in Comparison)
|
|
156
|
+
#
|
|
157
|
+
# @return [Set<AbstractComparison>]
|
|
158
|
+
# @api private
|
|
159
|
+
def self.descendants
|
|
160
|
+
@descendants ||= DescendantSet.new
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Registers AbstractComparison subclasses (used in Comparison)
|
|
164
|
+
#
|
|
165
|
+
# @api private
|
|
166
|
+
def self.inherited(descendant)
|
|
167
|
+
descendants << descendant
|
|
168
|
+
super
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Setter/getter: allows subclasses to easily set their slug
|
|
172
|
+
#
|
|
173
|
+
# @param [Symbol] slug
|
|
174
|
+
# The slug to be set for this class. Passing nil returns the current
|
|
175
|
+
# value instead.
|
|
176
|
+
#
|
|
177
|
+
# @return [Symbol]
|
|
178
|
+
# The current slug set for the Comparison.
|
|
179
|
+
#
|
|
180
|
+
# @example Creating a MyComparison compairson with slug :exact.
|
|
181
|
+
# class MyComparison < AbstractComparison
|
|
182
|
+
# slug :exact
|
|
183
|
+
# end
|
|
184
|
+
#
|
|
185
|
+
# @api semipublic
|
|
186
|
+
def self.slug(slug = nil)
|
|
187
|
+
slug ? @slug = slug : @slug
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Return the comparison class slug
|
|
191
|
+
#
|
|
192
|
+
# @return [Symbol]
|
|
193
|
+
# the comparison class slug
|
|
194
|
+
#
|
|
195
|
+
# @api private
|
|
196
|
+
def slug
|
|
197
|
+
self.class.slug
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Test that the record value matches the comparison
|
|
201
|
+
#
|
|
202
|
+
# @param [Resource, Hash] record
|
|
203
|
+
# The record containing the value to be matched
|
|
204
|
+
#
|
|
205
|
+
# @return [Boolean]
|
|
206
|
+
#
|
|
207
|
+
# @api semipublic
|
|
208
|
+
def matches?(record)
|
|
209
|
+
match_property?(record)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Tests that the Comparison is valid
|
|
213
|
+
#
|
|
214
|
+
# Subclasses can overload this to customise the means by which they
|
|
215
|
+
# determine the validity of the comparison. #valid? is called prior to
|
|
216
|
+
# performing a query on the repository: each Comparison within a Query
|
|
217
|
+
# must be valid otherwise the query will not be performed.
|
|
218
|
+
#
|
|
219
|
+
# @see DataMapper::Property#valid?
|
|
220
|
+
# @see DataMapper::Associations::Relationship#valid?
|
|
221
|
+
#
|
|
222
|
+
# @return [Boolean]
|
|
223
|
+
#
|
|
224
|
+
# @api semipublic
|
|
225
|
+
def valid?
|
|
226
|
+
valid_for_subject?(loaded_value)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Returns whether the subject is a Relationship
|
|
230
|
+
#
|
|
231
|
+
# @return [Boolean]
|
|
232
|
+
#
|
|
233
|
+
# @api semipublic
|
|
234
|
+
def relationship?
|
|
235
|
+
false
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Returns whether the subject is a Property
|
|
239
|
+
#
|
|
240
|
+
# @return [Boolean]
|
|
241
|
+
#
|
|
242
|
+
# @api semipublic
|
|
243
|
+
def property?
|
|
244
|
+
subject.kind_of?(Property)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Returns a human-readable representation of this object
|
|
248
|
+
#
|
|
249
|
+
# @return [String]
|
|
250
|
+
#
|
|
251
|
+
# @api semipublic
|
|
252
|
+
def inspect
|
|
253
|
+
"#<#{self.class} @subject=#{@subject.inspect} " \
|
|
254
|
+
"@dumped_value=#{@dumped_value.inspect} @loaded_value=#{@loaded_value.inspect}>"
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Returns a string version of this Comparison object
|
|
258
|
+
#
|
|
259
|
+
# @example
|
|
260
|
+
# Comparison.new(:==, MyClass.my_property, "value")
|
|
261
|
+
# # => "my_property == value"
|
|
262
|
+
#
|
|
263
|
+
# @return [String]
|
|
264
|
+
#
|
|
265
|
+
# @api semipublic
|
|
266
|
+
def to_s
|
|
267
|
+
"#{subject.name} #{comparator_string} #{dumped_value.inspect}"
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# @api private
|
|
271
|
+
def negated?
|
|
272
|
+
parent = self.parent
|
|
273
|
+
parent ? parent.negated? : false
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
private
|
|
277
|
+
|
|
278
|
+
# @api private
|
|
279
|
+
attr_reader :dumped_value
|
|
280
|
+
|
|
281
|
+
# Creates a new AbstractComparison instance with +subject+ and +value+
|
|
282
|
+
#
|
|
283
|
+
# @param [Property, Associations::Relationship] subject
|
|
284
|
+
# The subject of the comparison - the value of the subject will be
|
|
285
|
+
# matched against the given value parameter.
|
|
286
|
+
# @param [Object] value
|
|
287
|
+
# The value for the comparison.
|
|
288
|
+
#
|
|
289
|
+
# @api semipublic
|
|
290
|
+
def initialize(subject, value)
|
|
291
|
+
@subject = subject
|
|
292
|
+
@loaded_value = typecast(value)
|
|
293
|
+
@dumped_value = dump
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# @api private
|
|
297
|
+
def match_property?(record, operator = :===)
|
|
298
|
+
expected.send(operator, record_value(record))
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Typecasts the given +val+ using subject#typecast
|
|
302
|
+
#
|
|
303
|
+
# If the subject has no typecast method the value is returned without
|
|
304
|
+
# any changes.
|
|
305
|
+
#
|
|
306
|
+
# @param [Object] val
|
|
307
|
+
# The object to attempt to typecast.
|
|
308
|
+
#
|
|
309
|
+
# @return [Object]
|
|
310
|
+
# The typecasted object.
|
|
311
|
+
#
|
|
312
|
+
# @see Property#typecast
|
|
313
|
+
#
|
|
314
|
+
# @api private
|
|
315
|
+
def typecast(value)
|
|
316
|
+
typecast_property(value)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# @api private
|
|
320
|
+
def typecast_property(value)
|
|
321
|
+
subject.typecast(value)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# Dumps the given loaded_value using subject#value
|
|
325
|
+
#
|
|
326
|
+
# This converts property values to the primitive as stored in the
|
|
327
|
+
# repository.
|
|
328
|
+
#
|
|
329
|
+
# @return [Object]
|
|
330
|
+
# The raw (dumped) object.
|
|
331
|
+
#
|
|
332
|
+
# @see Property#value
|
|
333
|
+
#
|
|
334
|
+
# @api private
|
|
335
|
+
def dump
|
|
336
|
+
dump_property(loaded_value)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# @api private
|
|
340
|
+
def dump_property(value)
|
|
341
|
+
subject.dump(value)
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Returns a value for the comparison +subject+
|
|
345
|
+
#
|
|
346
|
+
# Extracts value for the +subject+ property or relationship from the
|
|
347
|
+
# given +record+, where +record+ is a Resource instance or a Hash.
|
|
348
|
+
#
|
|
349
|
+
# @param [DataMapper::Resource, Hash] record
|
|
350
|
+
# The resource or hash from which to retrieve the value.
|
|
351
|
+
# @param [Property, Associations::Relationship]
|
|
352
|
+
# The subject of the comparison. For example, if this is a property,
|
|
353
|
+
# the value for the resources +subject+ property is retrieved.
|
|
354
|
+
# @param [Symbol] key_type
|
|
355
|
+
# In the event that +subject+ is a relationship, key_type indicated
|
|
356
|
+
# which key should be used to retrieve the value from the resource.
|
|
357
|
+
#
|
|
358
|
+
# @return [Object]
|
|
359
|
+
#
|
|
360
|
+
# @api semipublic
|
|
361
|
+
def record_value(record, key_type = :source_key)
|
|
362
|
+
subject = self.subject
|
|
363
|
+
case record
|
|
364
|
+
when Hash
|
|
365
|
+
record_value_from_hash(record, subject, key_type)
|
|
366
|
+
when Resource
|
|
367
|
+
record_value_from_resource(record, subject, key_type)
|
|
368
|
+
else
|
|
369
|
+
record
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# Returns a value from a record hash
|
|
374
|
+
#
|
|
375
|
+
# Retrieves value for the +subject+ property or relationship from the
|
|
376
|
+
# given +hash+.
|
|
377
|
+
#
|
|
378
|
+
# @return [Object]
|
|
379
|
+
#
|
|
380
|
+
# @see AbstractComparison#record_value
|
|
381
|
+
#
|
|
382
|
+
# @api private
|
|
383
|
+
def record_value_from_hash(hash, subject, key_type)
|
|
384
|
+
hash.fetch subject, case subject
|
|
385
|
+
when Property
|
|
386
|
+
subject.load(hash[subject.field])
|
|
387
|
+
when Associations::Relationship
|
|
388
|
+
subject.send(key_type).map { |property|
|
|
389
|
+
record_value_from_hash(hash, property, key_type)
|
|
390
|
+
}
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# Returns a value from a resource
|
|
395
|
+
#
|
|
396
|
+
# Extracts value for the +subject+ property or relationship from the
|
|
397
|
+
# given +resource+.
|
|
398
|
+
#
|
|
399
|
+
# @return [Object]
|
|
400
|
+
#
|
|
401
|
+
# @see AbstractComparison#record_value
|
|
402
|
+
#
|
|
403
|
+
# @api private
|
|
404
|
+
def record_value_from_resource(resource, subject, key_type)
|
|
405
|
+
case subject
|
|
406
|
+
when Property
|
|
407
|
+
subject.get!(resource)
|
|
408
|
+
when Associations::Relationship
|
|
409
|
+
subject.send(key_type).get!(resource)
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
# Retrieves the value of the +subject+
|
|
414
|
+
#
|
|
415
|
+
# @return [Object]
|
|
416
|
+
#
|
|
417
|
+
# @api semipublic
|
|
418
|
+
def expected(value = @loaded_value)
|
|
419
|
+
expected = record_value(value, :target_key)
|
|
420
|
+
|
|
421
|
+
if @subject.respond_to?(:source_key)
|
|
422
|
+
@subject.source_key.typecast(expected)
|
|
423
|
+
else
|
|
424
|
+
expected
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# Test the value to see if it is valid
|
|
429
|
+
#
|
|
430
|
+
# @return [Boolean] true if the value is valid
|
|
431
|
+
#
|
|
432
|
+
# @api semipublic
|
|
433
|
+
def valid_for_subject?(loaded_value)
|
|
434
|
+
subject.valid?(loaded_value, negated?)
|
|
435
|
+
end
|
|
436
|
+
end # class AbstractComparison
|
|
437
|
+
|
|
438
|
+
# Included into comparisons which are capable of supporting
|
|
439
|
+
# Relationships.
|
|
440
|
+
module RelationshipHandler
|
|
441
|
+
# Returns whether this comparison subject is a Relationship
|
|
442
|
+
#
|
|
443
|
+
# @return [Boolean]
|
|
444
|
+
#
|
|
445
|
+
# @api semipublic
|
|
446
|
+
def relationship?
|
|
447
|
+
subject.kind_of?(Associations::Relationship)
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
# Tests that the record value matches the comparison
|
|
451
|
+
#
|
|
452
|
+
# @param [Resource, Hash] record
|
|
453
|
+
# The record containing the value to be matched
|
|
454
|
+
#
|
|
455
|
+
# @return [Boolean]
|
|
456
|
+
#
|
|
457
|
+
# @api semipublic
|
|
458
|
+
def matches?(record)
|
|
459
|
+
if relationship? && expected.respond_to?(:query)
|
|
460
|
+
match_relationship?(record)
|
|
461
|
+
else
|
|
462
|
+
super
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
# Returns the conditions required to match the subject relationship
|
|
467
|
+
#
|
|
468
|
+
# @return [Hash]
|
|
469
|
+
#
|
|
470
|
+
# @api semipublic
|
|
471
|
+
def foreign_key_mapping
|
|
472
|
+
relationship = subject.inverse
|
|
473
|
+
relationship = relationship.links.first if relationship.respond_to?(:links)
|
|
474
|
+
|
|
475
|
+
Query.target_conditions(value, relationship.source_key, relationship.target_key)
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
private
|
|
479
|
+
|
|
480
|
+
# @api private
|
|
481
|
+
def match_relationship?(record)
|
|
482
|
+
expected.query.conditions.matches?(record_value(record))
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
# Typecasts each value in the inclusion set
|
|
486
|
+
#
|
|
487
|
+
# @return [Array<Object>]
|
|
488
|
+
#
|
|
489
|
+
# @see AbtractComparison#typecast
|
|
490
|
+
#
|
|
491
|
+
# @api private
|
|
492
|
+
def typecast(value)
|
|
493
|
+
if relationship?
|
|
494
|
+
typecast_relationship(value)
|
|
495
|
+
else
|
|
496
|
+
super
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# @api private
|
|
501
|
+
def dump
|
|
502
|
+
if relationship?
|
|
503
|
+
dump_relationship(loaded_value)
|
|
504
|
+
else
|
|
505
|
+
super
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
# @api private
|
|
510
|
+
def dump_relationship(value)
|
|
511
|
+
value
|
|
512
|
+
end
|
|
513
|
+
end # module RelationshipHandler
|
|
514
|
+
|
|
515
|
+
# Tests whether the value in the record is equal to the expected
|
|
516
|
+
# set for the Comparison.
|
|
517
|
+
class EqualToComparison < AbstractComparison
|
|
518
|
+
include RelationshipHandler
|
|
519
|
+
|
|
520
|
+
slug :eql
|
|
521
|
+
|
|
522
|
+
# Tests that the record value matches the comparison
|
|
523
|
+
#
|
|
524
|
+
# @param [Resource, Hash] record
|
|
525
|
+
# The record containing the value to be matched
|
|
526
|
+
#
|
|
527
|
+
# @return [Boolean]
|
|
528
|
+
#
|
|
529
|
+
# @api semipublic
|
|
530
|
+
def matches?(record)
|
|
531
|
+
if expected.nil?
|
|
532
|
+
record_value(record).nil?
|
|
533
|
+
else
|
|
534
|
+
super
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
private
|
|
539
|
+
|
|
540
|
+
# @api private
|
|
541
|
+
def typecast_relationship(value)
|
|
542
|
+
case value
|
|
543
|
+
when Hash then typecast_hash(value)
|
|
544
|
+
when Resource then typecast_resource(value)
|
|
545
|
+
end
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
# @api private
|
|
549
|
+
def typecast_hash(hash)
|
|
550
|
+
subject = self.subject
|
|
551
|
+
subject.target_model.new(subject.query.merge(hash))
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
# @api private
|
|
555
|
+
def typecast_resource(resource)
|
|
556
|
+
resource
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
# @return [String]
|
|
560
|
+
#
|
|
561
|
+
# @see AbstractComparison#to_s
|
|
562
|
+
#
|
|
563
|
+
# @api private
|
|
564
|
+
def comparator_string
|
|
565
|
+
'='
|
|
566
|
+
end
|
|
567
|
+
end # class EqualToComparison
|
|
568
|
+
|
|
569
|
+
# Tests whether the value in the record is contained in the
|
|
570
|
+
# expected set for the Comparison, where expected is an
|
|
571
|
+
# Array, Range, or Set.
|
|
572
|
+
class InclusionComparison < AbstractComparison
|
|
573
|
+
include RelationshipHandler
|
|
574
|
+
|
|
575
|
+
slug :in
|
|
576
|
+
|
|
577
|
+
# Checks that the Comparison is valid
|
|
578
|
+
#
|
|
579
|
+
# @see DataMapper::Query::Conditions::AbstractComparison#valid?
|
|
580
|
+
#
|
|
581
|
+
# @return [Boolean]
|
|
582
|
+
#
|
|
583
|
+
# @api semipublic
|
|
584
|
+
def valid?
|
|
585
|
+
loaded_value = self.loaded_value
|
|
586
|
+
case loaded_value
|
|
587
|
+
when Collection then valid_collection?(loaded_value)
|
|
588
|
+
when Range then valid_range?(loaded_value)
|
|
589
|
+
when Enumerable then valid_enumerable?(loaded_value)
|
|
590
|
+
else
|
|
591
|
+
false
|
|
592
|
+
end
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
private
|
|
596
|
+
|
|
597
|
+
# @api private
|
|
598
|
+
def match_property?(record)
|
|
599
|
+
super(record, :include?)
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
# Overloads AbtractComparison#expected
|
|
603
|
+
#
|
|
604
|
+
# @return [Array<Object>]
|
|
605
|
+
# @see AbtractComparison#expected
|
|
606
|
+
#
|
|
607
|
+
# @api private
|
|
608
|
+
def expected
|
|
609
|
+
loaded_value = self.loaded_value
|
|
610
|
+
if loaded_value.kind_of?(Range)
|
|
611
|
+
typecast_range(loaded_value)
|
|
612
|
+
elsif loaded_value.respond_to?(:map)
|
|
613
|
+
# FIXME: causes a lazy load when a Collection
|
|
614
|
+
loaded_value.map { |val| super(val) }
|
|
615
|
+
else
|
|
616
|
+
super
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
# @api private
|
|
621
|
+
def valid_collection?(collection)
|
|
622
|
+
valid_for_subject?(collection)
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
# @api private
|
|
626
|
+
def valid_range?(range)
|
|
627
|
+
(range.any? || negated?) && valid_for_subject?(range.first) && valid_for_subject?(range.last)
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
# @api private
|
|
631
|
+
def valid_enumerable?(enumerable)
|
|
632
|
+
(!enumerable.empty? || negated?) && enumerable.all? { |entry| valid_for_subject?(entry) }
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
# @api private
|
|
636
|
+
def typecast_property(value)
|
|
637
|
+
if value.kind_of?(Range)
|
|
638
|
+
typecast_range(value)
|
|
639
|
+
elsif value.respond_to?(:map) && !value.kind_of?(String)
|
|
640
|
+
value.map { |entry| super(entry) }
|
|
641
|
+
else
|
|
642
|
+
super
|
|
643
|
+
end
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
# @api private
|
|
647
|
+
def typecast_range(range)
|
|
648
|
+
range.class.new(typecast_property(range.first), typecast_property(range.last), range.exclude_end?)
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
# @api private
|
|
652
|
+
def typecast_relationship(value)
|
|
653
|
+
case value
|
|
654
|
+
when Hash then typecast_hash(value)
|
|
655
|
+
when Resource then typecast_resource(value)
|
|
656
|
+
when Collection then typecast_collection(value)
|
|
657
|
+
when Enumerable then typecast_enumerable(value)
|
|
658
|
+
end
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
# @api private
|
|
662
|
+
def typecast_hash(hash)
|
|
663
|
+
subject = self.subject
|
|
664
|
+
subject.target_model.all(subject.query.merge(hash))
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
# @api private
|
|
668
|
+
def typecast_resource(resource)
|
|
669
|
+
resource.collection_for_self
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
# @api private
|
|
673
|
+
def typecast_collection(collection)
|
|
674
|
+
collection
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
# @api private
|
|
678
|
+
def typecast_enumerable(enumerable)
|
|
679
|
+
collection = nil
|
|
680
|
+
enumerable.each do |entry|
|
|
681
|
+
typecasted = typecast_relationship(entry)
|
|
682
|
+
if collection
|
|
683
|
+
collection |= typecasted
|
|
684
|
+
else
|
|
685
|
+
collection = typecasted
|
|
686
|
+
end
|
|
687
|
+
end
|
|
688
|
+
collection
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
# Dumps the given +val+ using subject#value
|
|
692
|
+
#
|
|
693
|
+
# @return [Array<Object>]
|
|
694
|
+
#
|
|
695
|
+
# @see AbtractComparison#dump
|
|
696
|
+
#
|
|
697
|
+
# @api private
|
|
698
|
+
def dump
|
|
699
|
+
loaded_value = self.loaded_value
|
|
700
|
+
if subject.respond_to?(:dump) && loaded_value.respond_to?(:map) && !loaded_value.kind_of?(Range)
|
|
701
|
+
dumped_value = loaded_value.map { |value| dump_property(value) }
|
|
702
|
+
dumped_value.uniq!
|
|
703
|
+
dumped_value
|
|
704
|
+
else
|
|
705
|
+
super
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
# @return [String]
|
|
710
|
+
#
|
|
711
|
+
# @see AbstractComparison#to_s
|
|
712
|
+
#
|
|
713
|
+
# @api private
|
|
714
|
+
def comparator_string
|
|
715
|
+
'IN'
|
|
716
|
+
end
|
|
717
|
+
end # class InclusionComparison
|
|
718
|
+
|
|
719
|
+
# Tests whether the value in the record matches the expected
|
|
720
|
+
# regexp set for the Comparison.
|
|
721
|
+
class RegexpComparison < AbstractComparison
|
|
722
|
+
slug :regexp
|
|
723
|
+
|
|
724
|
+
# Checks that the Comparison is valid
|
|
725
|
+
#
|
|
726
|
+
# @see AbstractComparison#valid?
|
|
727
|
+
#
|
|
728
|
+
# @api semipublic
|
|
729
|
+
def valid?
|
|
730
|
+
loaded_value.kind_of?(Regexp)
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
private
|
|
734
|
+
|
|
735
|
+
# Returns the value untouched
|
|
736
|
+
#
|
|
737
|
+
# @return [Object]
|
|
738
|
+
#
|
|
739
|
+
# @api private
|
|
740
|
+
def typecast(value)
|
|
741
|
+
value
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
# @return [String]
|
|
745
|
+
#
|
|
746
|
+
# @see AbstractComparison#to_s
|
|
747
|
+
#
|
|
748
|
+
# @api private
|
|
749
|
+
def comparator_string
|
|
750
|
+
'=~'
|
|
751
|
+
end
|
|
752
|
+
end # class RegexpComparison
|
|
753
|
+
|
|
754
|
+
# Tests whether the value in the record is like the expected set
|
|
755
|
+
# for the Comparison. Equivalent to a LIKE clause in an SQL database.
|
|
756
|
+
#
|
|
757
|
+
# TODO: move this to dm-more with DataObjectsAdapter plugins
|
|
758
|
+
class LikeComparison < AbstractComparison
|
|
759
|
+
slug :like
|
|
760
|
+
|
|
761
|
+
private
|
|
762
|
+
|
|
763
|
+
# Overloads the +expected+ method in AbstractComparison
|
|
764
|
+
#
|
|
765
|
+
# Return a regular expression suitable for matching against the
|
|
766
|
+
# records value.
|
|
767
|
+
#
|
|
768
|
+
# @return [Regexp]
|
|
769
|
+
#
|
|
770
|
+
# @see AbtractComparison#expected
|
|
771
|
+
#
|
|
772
|
+
# @api semipublic
|
|
773
|
+
def expected
|
|
774
|
+
Regexp.new('\A' << super.gsub('%', '.*').tr('_', '.') << '\z')
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
# @return [String]
|
|
778
|
+
#
|
|
779
|
+
# @see AbstractComparison#to_s
|
|
780
|
+
#
|
|
781
|
+
# @api private
|
|
782
|
+
def comparator_string
|
|
783
|
+
'LIKE'
|
|
784
|
+
end
|
|
785
|
+
end # class LikeComparison
|
|
786
|
+
|
|
787
|
+
# Tests whether the value in the record is greater than the
|
|
788
|
+
# expected set for the Comparison.
|
|
789
|
+
class GreaterThanComparison < AbstractComparison
|
|
790
|
+
slug :gt
|
|
791
|
+
|
|
792
|
+
# Tests that the record value matches the comparison
|
|
793
|
+
#
|
|
794
|
+
# @param [Resource, Hash] record
|
|
795
|
+
# The record containing the value to be matched
|
|
796
|
+
#
|
|
797
|
+
# @return [Boolean]
|
|
798
|
+
#
|
|
799
|
+
# @api semipublic
|
|
800
|
+
def matches?(record)
|
|
801
|
+
return false if expected.nil?
|
|
802
|
+
record_value = record_value(record)
|
|
803
|
+
!record_value.nil? && record_value > expected
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
private
|
|
807
|
+
|
|
808
|
+
# @return [String]
|
|
809
|
+
#
|
|
810
|
+
# @see AbstractComparison#to_s
|
|
811
|
+
#
|
|
812
|
+
# @api private
|
|
813
|
+
def comparator_string
|
|
814
|
+
'>'
|
|
815
|
+
end
|
|
816
|
+
end # class GreaterThanComparison
|
|
817
|
+
|
|
818
|
+
# Tests whether the value in the record is less than the expected
|
|
819
|
+
# set for the Comparison.
|
|
820
|
+
class LessThanComparison < AbstractComparison
|
|
821
|
+
slug :lt
|
|
822
|
+
|
|
823
|
+
# Tests that the record value matches the comparison
|
|
824
|
+
#
|
|
825
|
+
# @param [Resource, Hash] record
|
|
826
|
+
# The record containing the value to be matched
|
|
827
|
+
#
|
|
828
|
+
# @return [Boolean]
|
|
829
|
+
#
|
|
830
|
+
# @api semipublic
|
|
831
|
+
def matches?(record)
|
|
832
|
+
return false if expected.nil?
|
|
833
|
+
record_value = record_value(record)
|
|
834
|
+
!record_value.nil? && record_value < expected
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
private
|
|
838
|
+
|
|
839
|
+
# @return [String]
|
|
840
|
+
#
|
|
841
|
+
# @see AbstractComparison#to_s
|
|
842
|
+
#
|
|
843
|
+
# @api private
|
|
844
|
+
def comparator_string
|
|
845
|
+
'<'
|
|
846
|
+
end
|
|
847
|
+
end # class LessThanComparison
|
|
848
|
+
|
|
849
|
+
# Tests whether the value in the record is greater than, or equal to,
|
|
850
|
+
# the expected set for the Comparison.
|
|
851
|
+
class GreaterThanOrEqualToComparison < AbstractComparison
|
|
852
|
+
slug :gte
|
|
853
|
+
|
|
854
|
+
# Tests that the record value matches the comparison
|
|
855
|
+
#
|
|
856
|
+
# @param [Resource, Hash] record
|
|
857
|
+
# The record containing the value to be matched
|
|
858
|
+
#
|
|
859
|
+
# @return [Boolean]
|
|
860
|
+
#
|
|
861
|
+
# @api semipublic
|
|
862
|
+
def matches?(record)
|
|
863
|
+
return false if expected.nil?
|
|
864
|
+
record_value = record_value(record)
|
|
865
|
+
!record_value.nil? && record_value >= expected
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
private
|
|
869
|
+
|
|
870
|
+
# @see AbstractComparison#to_s
|
|
871
|
+
#
|
|
872
|
+
# @api private
|
|
873
|
+
def comparator_string
|
|
874
|
+
'>='
|
|
875
|
+
end
|
|
876
|
+
end # class GreaterThanOrEqualToComparison
|
|
877
|
+
|
|
878
|
+
# Tests whether the value in the record is less than, or equal to, the
|
|
879
|
+
# expected set for the Comparison.
|
|
880
|
+
class LessThanOrEqualToComparison < AbstractComparison
|
|
881
|
+
slug :lte
|
|
882
|
+
|
|
883
|
+
# Tests that the record value matches the comparison
|
|
884
|
+
#
|
|
885
|
+
# @param [Resource, Hash] record
|
|
886
|
+
# The record containing the value to be matched
|
|
887
|
+
#
|
|
888
|
+
# @return [Boolean]
|
|
889
|
+
#
|
|
890
|
+
# @api semipublic
|
|
891
|
+
def matches?(record)
|
|
892
|
+
return false if expected.nil?
|
|
893
|
+
record_value = record_value(record)
|
|
894
|
+
!record_value.nil? && record_value <= expected
|
|
895
|
+
end
|
|
896
|
+
|
|
897
|
+
private
|
|
898
|
+
|
|
899
|
+
# @return [String]
|
|
900
|
+
#
|
|
901
|
+
# @see AbstractComparison#to_s
|
|
902
|
+
#
|
|
903
|
+
# @api private
|
|
904
|
+
def comparator_string
|
|
905
|
+
'<='
|
|
906
|
+
end
|
|
907
|
+
end # class LessThanOrEqualToComparison
|
|
908
|
+
|
|
909
|
+
end # module Conditions
|
|
910
|
+
end # class Query
|
|
911
|
+
end # module DataMapper
|