ardm 0.0.1
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.
- checksums.yaml +15 -0
- data/.gitignore +35 -0
- data/Gemfile +13 -0
- data/LICENSE +21 -0
- data/README.md +70 -0
- data/Rakefile +4 -0
- data/ardm.gemspec +29 -0
- data/db/.gitignore +1 -0
- data/lib/ardm/active_record/associations.rb +80 -0
- data/lib/ardm/active_record/base.rb +49 -0
- data/lib/ardm/active_record/dirty.rb +25 -0
- data/lib/ardm/active_record/hooks.rb +31 -0
- data/lib/ardm/active_record/inheritance.rb +37 -0
- data/lib/ardm/active_record/is/state_machine.rb +21 -0
- data/lib/ardm/active_record/is.rb +22 -0
- data/lib/ardm/active_record/not_found.rb +7 -0
- data/lib/ardm/active_record/predicate_builder/array_handler.rb +31 -0
- data/lib/ardm/active_record/predicate_builder/rails3.rb +147 -0
- data/lib/ardm/active_record/predicate_builder/rails4.rb +139 -0
- data/lib/ardm/active_record/predicate_builder/relation_handler.rb +15 -0
- data/lib/ardm/active_record/predicate_builder.rb +19 -0
- data/lib/ardm/active_record/property.rb +357 -0
- data/lib/ardm/active_record/query.rb +108 -0
- data/lib/ardm/active_record/record.rb +70 -0
- data/lib/ardm/active_record/relation.rb +83 -0
- data/lib/ardm/active_record/repository.rb +38 -0
- data/lib/ardm/active_record/serialization.rb +164 -0
- data/lib/ardm/active_record/storage_names.rb +28 -0
- data/lib/ardm/active_record/validations.rb +111 -0
- data/lib/ardm/active_record.rb +43 -0
- data/lib/ardm/data_mapper/not_found.rb +5 -0
- data/lib/ardm/data_mapper/record.rb +41 -0
- data/lib/ardm/data_mapper.rb +5 -0
- data/lib/ardm/env.rb +5 -0
- data/lib/ardm/property/api_key.rb +30 -0
- data/lib/ardm/property/bcrypt_hash.rb +31 -0
- data/lib/ardm/property/binary.rb +23 -0
- data/lib/ardm/property/boolean.rb +29 -0
- data/lib/ardm/property/class.rb +19 -0
- data/lib/ardm/property/comma_separated_list.rb +28 -0
- data/lib/ardm/property/csv.rb +35 -0
- data/lib/ardm/property/date.rb +12 -0
- data/lib/ardm/property/datetime.rb +12 -0
- data/lib/ardm/property/decimal.rb +38 -0
- data/lib/ardm/property/discriminator.rb +65 -0
- data/lib/ardm/property/enum.rb +51 -0
- data/lib/ardm/property/epoch_time.rb +38 -0
- data/lib/ardm/property/file_path.rb +25 -0
- data/lib/ardm/property/flag.rb +65 -0
- data/lib/ardm/property/float.rb +18 -0
- data/lib/ardm/property/integer.rb +24 -0
- data/lib/ardm/property/invalid_value_error.rb +22 -0
- data/lib/ardm/property/ip_address.rb +35 -0
- data/lib/ardm/property/json.rb +49 -0
- data/lib/ardm/property/lookup.rb +29 -0
- data/lib/ardm/property/numeric.rb +40 -0
- data/lib/ardm/property/object.rb +36 -0
- data/lib/ardm/property/paranoid_boolean.rb +18 -0
- data/lib/ardm/property/paranoid_datetime.rb +17 -0
- data/lib/ardm/property/regexp.rb +22 -0
- data/lib/ardm/property/serial.rb +16 -0
- data/lib/ardm/property/slug.rb +29 -0
- data/lib/ardm/property/string.rb +40 -0
- data/lib/ardm/property/support/dirty_minder.rb +169 -0
- data/lib/ardm/property/support/flags.rb +41 -0
- data/lib/ardm/property/support/paranoid_base.rb +78 -0
- data/lib/ardm/property/text.rb +11 -0
- data/lib/ardm/property/time.rb +12 -0
- data/lib/ardm/property/uri.rb +27 -0
- data/lib/ardm/property/uuid.rb +65 -0
- data/lib/ardm/property/validation.rb +208 -0
- data/lib/ardm/property/yaml.rb +38 -0
- data/lib/ardm/property.rb +891 -0
- data/lib/ardm/property_set.rb +152 -0
- data/lib/ardm/query/expression.rb +85 -0
- data/lib/ardm/query/ext/symbol.rb +37 -0
- data/lib/ardm/query/operator.rb +64 -0
- data/lib/ardm/record.rb +1 -0
- data/lib/ardm/support/assertions.rb +8 -0
- data/lib/ardm/support/deprecate.rb +12 -0
- data/lib/ardm/support/descendant_set.rb +89 -0
- data/lib/ardm/support/equalizer.rb +48 -0
- data/lib/ardm/support/ext/array.rb +22 -0
- data/lib/ardm/support/ext/blank.rb +25 -0
- data/lib/ardm/support/ext/hash.rb +67 -0
- data/lib/ardm/support/ext/module.rb +47 -0
- data/lib/ardm/support/ext/object.rb +57 -0
- data/lib/ardm/support/ext/string.rb +24 -0
- data/lib/ardm/support/ext/try_dup.rb +12 -0
- data/lib/ardm/support/hook.rb +405 -0
- data/lib/ardm/support/lazy_array.rb +451 -0
- data/lib/ardm/support/local_object_space.rb +13 -0
- data/lib/ardm/support/logger.rb +201 -0
- data/lib/ardm/support/mash.rb +176 -0
- data/lib/ardm/support/naming_conventions.rb +90 -0
- data/lib/ardm/support/ordered_set.rb +380 -0
- data/lib/ardm/support/subject.rb +33 -0
- data/lib/ardm/support/subject_set.rb +250 -0
- data/lib/ardm/version.rb +3 -0
- data/lib/ardm.rb +56 -0
- data/spec/fixtures/api_user.rb +11 -0
- data/spec/fixtures/article.rb +22 -0
- data/spec/fixtures/bookmark.rb +14 -0
- data/spec/fixtures/invention.rb +5 -0
- data/spec/fixtures/network_node.rb +23 -0
- data/spec/fixtures/person.rb +17 -0
- data/spec/fixtures/software_package.rb +22 -0
- data/spec/fixtures/ticket.rb +12 -0
- data/spec/fixtures/tshirt.rb +15 -0
- data/spec/integration/api_key_spec.rb +25 -0
- data/spec/integration/bcrypt_hash_spec.rb +45 -0
- data/spec/integration/comma_separated_list_spec.rb +85 -0
- data/spec/integration/dirty_minder_spec.rb +197 -0
- data/spec/integration/enum_spec.rb +79 -0
- data/spec/integration/epoch_time_spec.rb +59 -0
- data/spec/integration/file_path_spec.rb +158 -0
- data/spec/integration/flag_spec.rb +72 -0
- data/spec/integration/ip_address_spec.rb +151 -0
- data/spec/integration/json_spec.rb +70 -0
- data/spec/integration/slug_spec.rb +65 -0
- data/spec/integration/uri_spec.rb +136 -0
- data/spec/integration/uuid_spec.rb +102 -0
- data/spec/integration/yaml_spec.rb +85 -0
- data/spec/public/property/binary_spec.rb +41 -0
- data/spec/public/property/boolean_spec.rb +30 -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 +133 -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 +103 -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 +23 -0
- data/spec/public/property/time_spec.rb +22 -0
- data/spec/public/property_spec.rb +316 -0
- data/spec/rcov.opts +6 -0
- data/spec/schema.rb +86 -0
- data/spec/semipublic/property/binary_spec.rb +14 -0
- data/spec/semipublic/property/boolean_spec.rb +48 -0
- data/spec/semipublic/property/class_spec.rb +36 -0
- data/spec/semipublic/property/date_spec.rb +44 -0
- data/spec/semipublic/property/date_time_spec.rb +47 -0
- data/spec/semipublic/property/decimal_spec.rb +83 -0
- data/spec/semipublic/property/discriminator_spec.rb +22 -0
- data/spec/semipublic/property/float_spec.rb +83 -0
- data/spec/semipublic/property/integer_spec.rb +83 -0
- data/spec/semipublic/property/lookup_spec.rb +27 -0
- data/spec/semipublic/property/serial_spec.rb +14 -0
- data/spec/semipublic/property/string_spec.rb +14 -0
- data/spec/semipublic/property/text_spec.rb +30 -0
- data/spec/semipublic/property/time_spec.rb +49 -0
- data/spec/semipublic/property_spec.rb +51 -0
- data/spec/shared/flags_shared_spec.rb +36 -0
- data/spec/shared/identity_function_group.rb +5 -0
- data/spec/shared/public_property_spec.rb +229 -0
- data/spec/shared/semipublic_property_spec.rb +159 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/unit/bcrypt_hash_spec.rb +154 -0
- data/spec/unit/csv_spec.rb +139 -0
- data/spec/unit/dirty_minder_spec.rb +64 -0
- data/spec/unit/enum_spec.rb +125 -0
- data/spec/unit/epoch_time_spec.rb +72 -0
- data/spec/unit/file_path_spec.rb +75 -0
- data/spec/unit/flag_spec.rb +114 -0
- data/spec/unit/ip_address_spec.rb +109 -0
- data/spec/unit/json_spec.rb +127 -0
- data/spec/unit/paranoid_boolean_spec.rb +142 -0
- data/spec/unit/paranoid_datetime_spec.rb +149 -0
- data/spec/unit/regexp_spec.rb +62 -0
- data/spec/unit/uri_spec.rb +64 -0
- data/spec/unit/yaml_spec.rb +111 -0
- data/tasks/spec.rake +40 -0
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- metadata +350 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
require 'ardm/support/subject_set'
|
|
2
|
+
require 'ardm/property/discriminator'
|
|
3
|
+
|
|
4
|
+
module Ardm
|
|
5
|
+
# Set of Property objects, used to associate
|
|
6
|
+
# queries with set of fields it performed over,
|
|
7
|
+
# to represent composite keys (esp. for associations)
|
|
8
|
+
# and so on.
|
|
9
|
+
class PropertySet < SubjectSet
|
|
10
|
+
include Enumerable
|
|
11
|
+
|
|
12
|
+
def <<(property)
|
|
13
|
+
clear_cache
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Make sure that entry is part of this PropertySet
|
|
18
|
+
#
|
|
19
|
+
# @param [#to_s] name
|
|
20
|
+
# @param [#name] entry
|
|
21
|
+
#
|
|
22
|
+
# @return [#name]
|
|
23
|
+
# the entry that is now part of this PropertySet
|
|
24
|
+
#
|
|
25
|
+
# @api semipublic
|
|
26
|
+
def []=(name, entry)
|
|
27
|
+
warn "#{self.class}#[]= is deprecated. Use #{self.class}#<< instead: #{caller.first}"
|
|
28
|
+
raise "#{entry.class} is not added with the correct name" unless name && name.to_s == entry.name.to_s
|
|
29
|
+
self << entry
|
|
30
|
+
entry
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def |(other)
|
|
34
|
+
raise(ArgumentError, "Cannot coerce #{other.inspect} into an array") unless other.respond_to?(:to_a)
|
|
35
|
+
self.class.new(to_a | other.to_a)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def &(other)
|
|
39
|
+
raise(ArgumentError, "Cannot coerce #{other.inspect} into an array") unless other.respond_to?(:to_a)
|
|
40
|
+
self.class.new(to_a & other.to_a)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def -(other)
|
|
44
|
+
raise(ArgumentError, "Cannot coerce #{other.inspect} into an array") unless other.respond_to?(:to_a)
|
|
45
|
+
self.class.new(to_a - other.to_a)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def +(other)
|
|
49
|
+
raise(ArgumentError, "Cannot coerce #{other.inspect} into an array") unless other.respond_to?(:to_a)
|
|
50
|
+
self.class.new(to_a + other.to_a)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def ==(other)
|
|
54
|
+
other.respond_to?(:to_a) && to_a == other.to_a
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# TODO: make PropertySet#reject return a PropertySet instance
|
|
58
|
+
# @api semipublic
|
|
59
|
+
def defaults
|
|
60
|
+
@defaults ||= self.class.new(key | [ discriminator ].compact | reject { |property| property.lazy? }).freeze
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @api semipublic
|
|
64
|
+
def key
|
|
65
|
+
@key ||= self.class.new(select { |property| property.key? }).freeze
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @api semipublic
|
|
69
|
+
def discriminator
|
|
70
|
+
@discriminator ||= detect { |property| property.kind_of?(Property::Discriminator) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @api semipublic
|
|
74
|
+
def indexes
|
|
75
|
+
index_hash = {}
|
|
76
|
+
each { |property| parse_index(property.index, property.field, index_hash) }
|
|
77
|
+
index_hash
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @api semipublic
|
|
81
|
+
def unique_indexes
|
|
82
|
+
index_hash = {}
|
|
83
|
+
each { |property| parse_index(property.unique_index, property.field, index_hash) }
|
|
84
|
+
index_hash
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @api semipublic
|
|
88
|
+
def get(resource)
|
|
89
|
+
return [] if resource.nil?
|
|
90
|
+
map { |property| resource.__send__(property.name) }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# @api semipublic
|
|
94
|
+
def get!(resource)
|
|
95
|
+
map { |property| property.get!(resource) }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @api semipublic
|
|
99
|
+
def set(resource, values)
|
|
100
|
+
zip(values) { |property, value| resource.__send__("#{property.name}=", value) }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @api semipublic
|
|
104
|
+
def set!(resource, values)
|
|
105
|
+
zip(values) { |property, value| property.set!(resource, value) }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# @api semipublic
|
|
109
|
+
def loaded?(resource)
|
|
110
|
+
all? { |property| property.loaded?(resource) }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# @api semipublic
|
|
114
|
+
def valid?(values)
|
|
115
|
+
zip(values.nil? ? [] : values).all? { |property, value| property.valid?(value) }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @api semipublic
|
|
119
|
+
def typecast(values)
|
|
120
|
+
zip(values.nil? ? [] : values).map { |property, value| property.typecast(value) }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# @api private
|
|
124
|
+
def field_map
|
|
125
|
+
Hash[ map { |property| [ property.field, property ] } ]
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def inspect
|
|
129
|
+
to_a.inspect
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
# @api private
|
|
135
|
+
def clear_cache
|
|
136
|
+
@defaults, @key, @discriminator = nil
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# @api private
|
|
140
|
+
def parse_index(index, property, index_hash)
|
|
141
|
+
case index
|
|
142
|
+
when true
|
|
143
|
+
index_hash[property] = [ property ]
|
|
144
|
+
when Symbol
|
|
145
|
+
index_hash[index] ||= []
|
|
146
|
+
index_hash[index] << property
|
|
147
|
+
when Array
|
|
148
|
+
index.each { |idx| parse_index(idx, property, index_hash) }
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end # class PropertySet
|
|
152
|
+
end # module Ardm
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module Ardm
|
|
2
|
+
module Query
|
|
3
|
+
class Expression
|
|
4
|
+
attr_reader :relation, :target, :operator, :value
|
|
5
|
+
|
|
6
|
+
def self.build_scope(relation, target, value)
|
|
7
|
+
new(relation, target, value).scope
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(relation, target, value)
|
|
11
|
+
@relation = relation
|
|
12
|
+
@value = value
|
|
13
|
+
|
|
14
|
+
case target
|
|
15
|
+
when Ardm::Query::Operator
|
|
16
|
+
@target = target.target
|
|
17
|
+
@operator = target.operator
|
|
18
|
+
when Symbol, String
|
|
19
|
+
@target = target
|
|
20
|
+
@operator = :eq
|
|
21
|
+
else
|
|
22
|
+
raise ArgumentError, "Unknown target #{target.inspect} in Expresion"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def resolved_target
|
|
27
|
+
target_from_association || target
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def arel_target
|
|
31
|
+
arel_table[resolved_target]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_arel
|
|
35
|
+
arel_target.send(arel_operator, arel_value)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def scope
|
|
39
|
+
relation.where to_arel
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def arel_table
|
|
45
|
+
relation.arel_table
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def association
|
|
49
|
+
@association ||= relation.reflect_on_association(target)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def target_from_association
|
|
53
|
+
if association
|
|
54
|
+
if association.macro == :belongs_to
|
|
55
|
+
association.foreign_key.to_sym
|
|
56
|
+
else
|
|
57
|
+
$stderr.puts "WARNING: #{association.macro} based queries not yet supported?"
|
|
58
|
+
association.primary_key.to_sym
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def arel_operator
|
|
64
|
+
value.respond_to?(:to_ary) ? operator.for_array : operator
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def arel_value(val = value)
|
|
68
|
+
if val.respond_to?(:to_ary)
|
|
69
|
+
return val.map {|v| arel_value(v) }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
case val
|
|
73
|
+
when ::ActiveRecord::Base
|
|
74
|
+
val.id
|
|
75
|
+
when ::ActiveRecord::Relation
|
|
76
|
+
arel_value(val.to_ary)
|
|
77
|
+
when ::Array
|
|
78
|
+
val.map {|v| arel_value(v) }
|
|
79
|
+
else
|
|
80
|
+
val
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'ardm/query/operator'
|
|
2
|
+
|
|
3
|
+
module Ardm
|
|
4
|
+
module Query
|
|
5
|
+
module Ext
|
|
6
|
+
module Symbol
|
|
7
|
+
Ardm::Query::Operator::OPERATORS.each do |dm, arel|
|
|
8
|
+
define_method dm do
|
|
9
|
+
Ardm::Query::Operator.new(self, arel)
|
|
10
|
+
end
|
|
11
|
+
#class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
12
|
+
# def #{dm}
|
|
13
|
+
# #{"raise \"explicit use of '#{dm}' operator is deprecated (#{caller.first})\"" if dm == :eql || dm == :in}
|
|
14
|
+
# Ardm::Query::Operator.new(self, #{arel.inspect})
|
|
15
|
+
# end
|
|
16
|
+
#RUBY
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# FIXME: handle aliased columns
|
|
20
|
+
# It's easier to turn these into strings for now,
|
|
21
|
+
# but I think it will break for aliased columns.
|
|
22
|
+
Ardm::Query::Operator::ORDERS.each do |dm, arel|
|
|
23
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
24
|
+
def #{dm}
|
|
25
|
+
"\#{self} #{arel.upcase}"
|
|
26
|
+
end
|
|
27
|
+
RUBY
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class ::Symbol
|
|
35
|
+
Ardm::Query::Operator::ALL.keys.each { |meth| method_defined?(meth) && remove_method(meth) }
|
|
36
|
+
include Ardm::Query::Ext::Symbol
|
|
37
|
+
end # class Symbol
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'ardm/support/equalizer'
|
|
2
|
+
module Ardm
|
|
3
|
+
module Query
|
|
4
|
+
class Operator
|
|
5
|
+
extend Ardm::Equalizer
|
|
6
|
+
|
|
7
|
+
OPERATORS = {
|
|
8
|
+
# DM => ARel
|
|
9
|
+
:eql => :eq,
|
|
10
|
+
:not => :not,
|
|
11
|
+
:in => :in,
|
|
12
|
+
:gt => :gt,
|
|
13
|
+
:gte => :gteq,
|
|
14
|
+
:lt => :lt,
|
|
15
|
+
:lte => :lteq,
|
|
16
|
+
:like => :matches,
|
|
17
|
+
:not_like => :does_not_match,
|
|
18
|
+
:regexp => :regexp,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
ORDERS = {
|
|
22
|
+
:asc => :asc,
|
|
23
|
+
:desc => :desc,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
ALL = OPERATORS.merge(ORDERS)
|
|
27
|
+
|
|
28
|
+
equalize :target, :operator
|
|
29
|
+
|
|
30
|
+
# @api private
|
|
31
|
+
attr_reader :target
|
|
32
|
+
|
|
33
|
+
# @api private
|
|
34
|
+
attr_reader :operator
|
|
35
|
+
|
|
36
|
+
# @api private
|
|
37
|
+
def inspect
|
|
38
|
+
"#<#{self.class.name} #{target.inspect}.#{operator.inspect}>"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
FOR_ARRAY = {
|
|
42
|
+
:eq => :in,
|
|
43
|
+
:not_eq => :not_in
|
|
44
|
+
}.freeze
|
|
45
|
+
|
|
46
|
+
def for_array
|
|
47
|
+
FOR_ARRAY[operator]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_arel(arel_table, value)
|
|
51
|
+
Ardm::Query::Expression.new(arel_table, target, operator, value)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# @api private
|
|
57
|
+
def initialize(target, operator)
|
|
58
|
+
@target, @operator = target, operator.to_sym
|
|
59
|
+
end
|
|
60
|
+
end # class Operator
|
|
61
|
+
end # module Query
|
|
62
|
+
end # module Ardm
|
|
63
|
+
|
|
64
|
+
require 'ardm/query/ext/symbol'
|
data/lib/ardm/record.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "ardm/#{Ardm.orm}/record"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module Ardm
|
|
2
|
+
module Assertions
|
|
3
|
+
def assert_kind_of(name, value, *klasses)
|
|
4
|
+
klasses.each { |k| return if value.kind_of?(k) }
|
|
5
|
+
raise ArgumentError, "+#{name}+ should be #{klasses.map { |k| k.name } * ' or '}, but was #{value.class.name}", caller(2)
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Ardm
|
|
2
|
+
module Deprecate
|
|
3
|
+
def deprecate(old_method, new_method)
|
|
4
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
5
|
+
def #{old_method}(*args, &block)
|
|
6
|
+
warn "\#{self.class}##{old_method} is deprecated, use \#{self.class}##{new_method} instead (\#{caller.first})"
|
|
7
|
+
send(#{new_method.inspect}, *args, &block)
|
|
8
|
+
end
|
|
9
|
+
RUBY
|
|
10
|
+
end
|
|
11
|
+
end # module Deprecate
|
|
12
|
+
end # module Ardm
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Ardm
|
|
2
|
+
class DescendantSet
|
|
3
|
+
include Enumerable
|
|
4
|
+
|
|
5
|
+
# Initialize a DescendantSet instance
|
|
6
|
+
#
|
|
7
|
+
# @param [#to_ary] descendants
|
|
8
|
+
# initialize with the descendants
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
def initialize(descendants = [])
|
|
12
|
+
@descendants = SubjectSet.new(descendants)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Copy a DescendantSet instance
|
|
16
|
+
#
|
|
17
|
+
# @param [DescendantSet] original
|
|
18
|
+
# the original descendants
|
|
19
|
+
#
|
|
20
|
+
# @api private
|
|
21
|
+
def initialize_copy(original)
|
|
22
|
+
@descendants = @descendants.dup
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Add a descendant
|
|
26
|
+
#
|
|
27
|
+
# @param [Module] descendant
|
|
28
|
+
#
|
|
29
|
+
# @return [DescendantSet]
|
|
30
|
+
# self
|
|
31
|
+
#
|
|
32
|
+
# @api private
|
|
33
|
+
def <<(descendant)
|
|
34
|
+
@descendants << descendant
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Remove a descendant
|
|
39
|
+
#
|
|
40
|
+
# Also removes from all descendants
|
|
41
|
+
#
|
|
42
|
+
# @param [Module] descendant
|
|
43
|
+
#
|
|
44
|
+
# @return [DescendantSet]
|
|
45
|
+
# self
|
|
46
|
+
#
|
|
47
|
+
# @api private
|
|
48
|
+
def delete(descendant)
|
|
49
|
+
@descendants.delete(descendant)
|
|
50
|
+
each { |d| d.descendants.delete(descendant) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Iterate over each descendant
|
|
54
|
+
#
|
|
55
|
+
# @yield [descendant]
|
|
56
|
+
# @yieldparam [Module] descendant
|
|
57
|
+
#
|
|
58
|
+
# @return [DescendantSet]
|
|
59
|
+
# self
|
|
60
|
+
#
|
|
61
|
+
# @api private
|
|
62
|
+
def each
|
|
63
|
+
@descendants.each do |descendant|
|
|
64
|
+
yield descendant
|
|
65
|
+
descendant.descendants.each { |dd| yield dd }
|
|
66
|
+
end
|
|
67
|
+
self
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Test if there are any descendants
|
|
71
|
+
#
|
|
72
|
+
# @return [Boolean]
|
|
73
|
+
#
|
|
74
|
+
# @api private
|
|
75
|
+
def empty?
|
|
76
|
+
@descendants.empty?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Removes all entries and returns self
|
|
80
|
+
#
|
|
81
|
+
# @return [DescendantSet] self
|
|
82
|
+
#
|
|
83
|
+
# @api private
|
|
84
|
+
def clear
|
|
85
|
+
@descendants.clear
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end # class DescendantSet
|
|
89
|
+
end # module Ardm
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Ardm
|
|
2
|
+
module Equalizer
|
|
3
|
+
def equalize(*methods)
|
|
4
|
+
define_eql_method(methods)
|
|
5
|
+
define_equivalent_method(methods)
|
|
6
|
+
define_hash_method(methods)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def define_eql_method(methods)
|
|
12
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
13
|
+
def eql?(other)
|
|
14
|
+
return true if equal?(other)
|
|
15
|
+
instance_of?(other.class) &&
|
|
16
|
+
#{methods.map { |method| "#{method}.eql?(other.#{method})" }.join(' && ')}
|
|
17
|
+
end
|
|
18
|
+
RUBY
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def define_equivalent_method(methods)
|
|
22
|
+
respond_to = []
|
|
23
|
+
equivalent = []
|
|
24
|
+
|
|
25
|
+
methods.each do |method|
|
|
26
|
+
respond_to << "other.respond_to?(#{method.inspect})"
|
|
27
|
+
equivalent << "#{method} == other.#{method}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
31
|
+
def ==(other)
|
|
32
|
+
return true if equal?(other)
|
|
33
|
+
return false unless kind_of?(other.class) || other.kind_of?(self.class)
|
|
34
|
+
#{respond_to.join(' && ')} &&
|
|
35
|
+
#{equivalent.join(' && ')}
|
|
36
|
+
end
|
|
37
|
+
RUBY
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def define_hash_method(methods)
|
|
41
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
42
|
+
def hash
|
|
43
|
+
self.class.hash ^ #{methods.map { |method| "#{method}.hash" }.join(' ^ ')}
|
|
44
|
+
end
|
|
45
|
+
RUBY
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Ardm; module Ext
|
|
2
|
+
module Array
|
|
3
|
+
# Transforms an Array of key/value pairs into a {Mash}.
|
|
4
|
+
#
|
|
5
|
+
# This is a better idiom than using Mash[*array.flatten] in Ruby 1.8.6
|
|
6
|
+
# because it is not possible to limit the flattening to a single
|
|
7
|
+
# level.
|
|
8
|
+
#
|
|
9
|
+
# @param [Array] array
|
|
10
|
+
# The array of key/value pairs to transform.
|
|
11
|
+
#
|
|
12
|
+
# @return [Mash]
|
|
13
|
+
# A {Mash} where each entry in the Array is turned into a key/value.
|
|
14
|
+
#
|
|
15
|
+
# @api semipublic
|
|
16
|
+
def self.to_mash(array)
|
|
17
|
+
m = Mash.new
|
|
18
|
+
array.each { |k,v| m[k] = v }
|
|
19
|
+
m
|
|
20
|
+
end
|
|
21
|
+
end # class Array
|
|
22
|
+
end; end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Ardm
|
|
2
|
+
module Ext
|
|
3
|
+
# Determines whether the specified +value+ is blank.
|
|
4
|
+
#
|
|
5
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
|
6
|
+
# For example, "", " ", +nil+, [], and {} are blank.
|
|
7
|
+
#
|
|
8
|
+
# @api semipublic
|
|
9
|
+
def self.blank?(value)
|
|
10
|
+
return value.blank? if value.respond_to?(:blank?)
|
|
11
|
+
case value
|
|
12
|
+
when ::NilClass, ::FalseClass
|
|
13
|
+
true
|
|
14
|
+
when ::TrueClass, ::Numeric
|
|
15
|
+
false
|
|
16
|
+
when ::Array, ::Hash
|
|
17
|
+
value.empty?
|
|
18
|
+
when ::String
|
|
19
|
+
value !~ /\S/
|
|
20
|
+
else
|
|
21
|
+
value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module Ardm; module Ext
|
|
2
|
+
module Hash
|
|
3
|
+
# Creates a hash with *only* the specified key/value pairs from +hash+.
|
|
4
|
+
#
|
|
5
|
+
# @param [Hash] hash The hash from which to pick the key/value pairs.
|
|
6
|
+
# @param [Array] *keys The hash keys to include.
|
|
7
|
+
#
|
|
8
|
+
# @return [Hash] A new hash with only the selected keys.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# hash = { :one => 1, :two => 2, :three => 3 }
|
|
12
|
+
# Ext::Hash.only(hash, :one, :two) # => { :one => 1, :two => 2 }
|
|
13
|
+
#
|
|
14
|
+
# @api semipublic
|
|
15
|
+
def self.only(hash, *keys)
|
|
16
|
+
h = {}
|
|
17
|
+
keys.each {|k| h[k] = hash[k] if hash.has_key?(k) }
|
|
18
|
+
h
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Returns a hash that includes everything but the given +keys+.
|
|
22
|
+
#
|
|
23
|
+
# @param [Hash] hash The hash from which to pick the key/value pairs.
|
|
24
|
+
# @param [Array] *keys The hash keys to exclude.
|
|
25
|
+
#
|
|
26
|
+
# @return [Hash] A new hash without the specified keys.
|
|
27
|
+
#
|
|
28
|
+
# @example
|
|
29
|
+
# hash = { :one => 1, :two => 2, :three => 3 }
|
|
30
|
+
# Ext::Hash.except(hash, :one, :two) # => { :three => 3 }
|
|
31
|
+
#
|
|
32
|
+
# @api semipublic
|
|
33
|
+
def self.except(hash, *keys)
|
|
34
|
+
self.except!(hash.dup, *keys)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Removes the specified +keys+ from the given +hash+.
|
|
38
|
+
#
|
|
39
|
+
# @param [Hash] hash The hash to modify.
|
|
40
|
+
# @param [Array] *keys The hash keys to exclude.
|
|
41
|
+
#
|
|
42
|
+
# @return [Hash] +hash+
|
|
43
|
+
#
|
|
44
|
+
# @example
|
|
45
|
+
# hash = { :one => 1, :two => 2, :three => 3 }
|
|
46
|
+
# Ext::Hash.except!(hash, :one, :two)
|
|
47
|
+
# hash # => { :three => 3 }
|
|
48
|
+
#
|
|
49
|
+
# @api semipublic
|
|
50
|
+
def self.except!(hash, *keys)
|
|
51
|
+
keys.each { |key| hash.delete(key) }
|
|
52
|
+
hash
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Converts the specified +hash+ to a {Mash}.
|
|
56
|
+
#
|
|
57
|
+
# @param [Hash] hash The hash to convert.
|
|
58
|
+
# @return [Mash] The {Mash} for the specified +hash+.
|
|
59
|
+
#
|
|
60
|
+
# @api semipublic
|
|
61
|
+
def self.to_mash(hash)
|
|
62
|
+
h = Mash.new(hash)
|
|
63
|
+
h.default = hash.default
|
|
64
|
+
h
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end; end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Ardm; module Ext
|
|
2
|
+
module Module
|
|
3
|
+
|
|
4
|
+
# @api semipublic
|
|
5
|
+
def self.find_const(mod, const_name)
|
|
6
|
+
if const_name[0..1] == '::'
|
|
7
|
+
Ardm::Ext::Object.full_const_get(const_name[2..-1])
|
|
8
|
+
else
|
|
9
|
+
nested_const_lookup(mod, const_name)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
# Doesn't do any caching since constants can change with remove_const
|
|
16
|
+
def self.nested_const_lookup(mod, const_name)
|
|
17
|
+
unless mod.equal?(::Object)
|
|
18
|
+
constants = []
|
|
19
|
+
|
|
20
|
+
mod.name.split('::').each do |part|
|
|
21
|
+
const = constants.last || ::Object
|
|
22
|
+
constants << const.const_get(part)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
parts = const_name.split('::')
|
|
26
|
+
|
|
27
|
+
# from most to least specific constant, use each as a base and try
|
|
28
|
+
# to find a constant with the name const_name within them
|
|
29
|
+
constants.reverse_each do |const|
|
|
30
|
+
# return the nested constant if available
|
|
31
|
+
return const if parts.all? do |part|
|
|
32
|
+
const = if RUBY_VERSION >= '1.9.0'
|
|
33
|
+
const.const_defined?(part, false) ? const.const_get(part, false) : nil
|
|
34
|
+
else
|
|
35
|
+
const.const_defined?(part) ? const.const_get(part) : nil
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# no relative constant found, fallback to an absolute lookup and
|
|
42
|
+
# use const_missing if not found
|
|
43
|
+
Ardm::Ext::Object.full_const_get(const_name)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
end; end
|