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.
Files changed (179) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +35 -0
  3. data/Gemfile +13 -0
  4. data/LICENSE +21 -0
  5. data/README.md +70 -0
  6. data/Rakefile +4 -0
  7. data/ardm.gemspec +29 -0
  8. data/db/.gitignore +1 -0
  9. data/lib/ardm/active_record/associations.rb +80 -0
  10. data/lib/ardm/active_record/base.rb +49 -0
  11. data/lib/ardm/active_record/dirty.rb +25 -0
  12. data/lib/ardm/active_record/hooks.rb +31 -0
  13. data/lib/ardm/active_record/inheritance.rb +37 -0
  14. data/lib/ardm/active_record/is/state_machine.rb +21 -0
  15. data/lib/ardm/active_record/is.rb +22 -0
  16. data/lib/ardm/active_record/not_found.rb +7 -0
  17. data/lib/ardm/active_record/predicate_builder/array_handler.rb +31 -0
  18. data/lib/ardm/active_record/predicate_builder/rails3.rb +147 -0
  19. data/lib/ardm/active_record/predicate_builder/rails4.rb +139 -0
  20. data/lib/ardm/active_record/predicate_builder/relation_handler.rb +15 -0
  21. data/lib/ardm/active_record/predicate_builder.rb +19 -0
  22. data/lib/ardm/active_record/property.rb +357 -0
  23. data/lib/ardm/active_record/query.rb +108 -0
  24. data/lib/ardm/active_record/record.rb +70 -0
  25. data/lib/ardm/active_record/relation.rb +83 -0
  26. data/lib/ardm/active_record/repository.rb +38 -0
  27. data/lib/ardm/active_record/serialization.rb +164 -0
  28. data/lib/ardm/active_record/storage_names.rb +28 -0
  29. data/lib/ardm/active_record/validations.rb +111 -0
  30. data/lib/ardm/active_record.rb +43 -0
  31. data/lib/ardm/data_mapper/not_found.rb +5 -0
  32. data/lib/ardm/data_mapper/record.rb +41 -0
  33. data/lib/ardm/data_mapper.rb +5 -0
  34. data/lib/ardm/env.rb +5 -0
  35. data/lib/ardm/property/api_key.rb +30 -0
  36. data/lib/ardm/property/bcrypt_hash.rb +31 -0
  37. data/lib/ardm/property/binary.rb +23 -0
  38. data/lib/ardm/property/boolean.rb +29 -0
  39. data/lib/ardm/property/class.rb +19 -0
  40. data/lib/ardm/property/comma_separated_list.rb +28 -0
  41. data/lib/ardm/property/csv.rb +35 -0
  42. data/lib/ardm/property/date.rb +12 -0
  43. data/lib/ardm/property/datetime.rb +12 -0
  44. data/lib/ardm/property/decimal.rb +38 -0
  45. data/lib/ardm/property/discriminator.rb +65 -0
  46. data/lib/ardm/property/enum.rb +51 -0
  47. data/lib/ardm/property/epoch_time.rb +38 -0
  48. data/lib/ardm/property/file_path.rb +25 -0
  49. data/lib/ardm/property/flag.rb +65 -0
  50. data/lib/ardm/property/float.rb +18 -0
  51. data/lib/ardm/property/integer.rb +24 -0
  52. data/lib/ardm/property/invalid_value_error.rb +22 -0
  53. data/lib/ardm/property/ip_address.rb +35 -0
  54. data/lib/ardm/property/json.rb +49 -0
  55. data/lib/ardm/property/lookup.rb +29 -0
  56. data/lib/ardm/property/numeric.rb +40 -0
  57. data/lib/ardm/property/object.rb +36 -0
  58. data/lib/ardm/property/paranoid_boolean.rb +18 -0
  59. data/lib/ardm/property/paranoid_datetime.rb +17 -0
  60. data/lib/ardm/property/regexp.rb +22 -0
  61. data/lib/ardm/property/serial.rb +16 -0
  62. data/lib/ardm/property/slug.rb +29 -0
  63. data/lib/ardm/property/string.rb +40 -0
  64. data/lib/ardm/property/support/dirty_minder.rb +169 -0
  65. data/lib/ardm/property/support/flags.rb +41 -0
  66. data/lib/ardm/property/support/paranoid_base.rb +78 -0
  67. data/lib/ardm/property/text.rb +11 -0
  68. data/lib/ardm/property/time.rb +12 -0
  69. data/lib/ardm/property/uri.rb +27 -0
  70. data/lib/ardm/property/uuid.rb +65 -0
  71. data/lib/ardm/property/validation.rb +208 -0
  72. data/lib/ardm/property/yaml.rb +38 -0
  73. data/lib/ardm/property.rb +891 -0
  74. data/lib/ardm/property_set.rb +152 -0
  75. data/lib/ardm/query/expression.rb +85 -0
  76. data/lib/ardm/query/ext/symbol.rb +37 -0
  77. data/lib/ardm/query/operator.rb +64 -0
  78. data/lib/ardm/record.rb +1 -0
  79. data/lib/ardm/support/assertions.rb +8 -0
  80. data/lib/ardm/support/deprecate.rb +12 -0
  81. data/lib/ardm/support/descendant_set.rb +89 -0
  82. data/lib/ardm/support/equalizer.rb +48 -0
  83. data/lib/ardm/support/ext/array.rb +22 -0
  84. data/lib/ardm/support/ext/blank.rb +25 -0
  85. data/lib/ardm/support/ext/hash.rb +67 -0
  86. data/lib/ardm/support/ext/module.rb +47 -0
  87. data/lib/ardm/support/ext/object.rb +57 -0
  88. data/lib/ardm/support/ext/string.rb +24 -0
  89. data/lib/ardm/support/ext/try_dup.rb +12 -0
  90. data/lib/ardm/support/hook.rb +405 -0
  91. data/lib/ardm/support/lazy_array.rb +451 -0
  92. data/lib/ardm/support/local_object_space.rb +13 -0
  93. data/lib/ardm/support/logger.rb +201 -0
  94. data/lib/ardm/support/mash.rb +176 -0
  95. data/lib/ardm/support/naming_conventions.rb +90 -0
  96. data/lib/ardm/support/ordered_set.rb +380 -0
  97. data/lib/ardm/support/subject.rb +33 -0
  98. data/lib/ardm/support/subject_set.rb +250 -0
  99. data/lib/ardm/version.rb +3 -0
  100. data/lib/ardm.rb +56 -0
  101. data/spec/fixtures/api_user.rb +11 -0
  102. data/spec/fixtures/article.rb +22 -0
  103. data/spec/fixtures/bookmark.rb +14 -0
  104. data/spec/fixtures/invention.rb +5 -0
  105. data/spec/fixtures/network_node.rb +23 -0
  106. data/spec/fixtures/person.rb +17 -0
  107. data/spec/fixtures/software_package.rb +22 -0
  108. data/spec/fixtures/ticket.rb +12 -0
  109. data/spec/fixtures/tshirt.rb +15 -0
  110. data/spec/integration/api_key_spec.rb +25 -0
  111. data/spec/integration/bcrypt_hash_spec.rb +45 -0
  112. data/spec/integration/comma_separated_list_spec.rb +85 -0
  113. data/spec/integration/dirty_minder_spec.rb +197 -0
  114. data/spec/integration/enum_spec.rb +79 -0
  115. data/spec/integration/epoch_time_spec.rb +59 -0
  116. data/spec/integration/file_path_spec.rb +158 -0
  117. data/spec/integration/flag_spec.rb +72 -0
  118. data/spec/integration/ip_address_spec.rb +151 -0
  119. data/spec/integration/json_spec.rb +70 -0
  120. data/spec/integration/slug_spec.rb +65 -0
  121. data/spec/integration/uri_spec.rb +136 -0
  122. data/spec/integration/uuid_spec.rb +102 -0
  123. data/spec/integration/yaml_spec.rb +85 -0
  124. data/spec/public/property/binary_spec.rb +41 -0
  125. data/spec/public/property/boolean_spec.rb +30 -0
  126. data/spec/public/property/class_spec.rb +28 -0
  127. data/spec/public/property/date_spec.rb +22 -0
  128. data/spec/public/property/date_time_spec.rb +22 -0
  129. data/spec/public/property/decimal_spec.rb +23 -0
  130. data/spec/public/property/discriminator_spec.rb +133 -0
  131. data/spec/public/property/float_spec.rb +22 -0
  132. data/spec/public/property/integer_spec.rb +22 -0
  133. data/spec/public/property/object_spec.rb +103 -0
  134. data/spec/public/property/serial_spec.rb +22 -0
  135. data/spec/public/property/string_spec.rb +22 -0
  136. data/spec/public/property/text_spec.rb +23 -0
  137. data/spec/public/property/time_spec.rb +22 -0
  138. data/spec/public/property_spec.rb +316 -0
  139. data/spec/rcov.opts +6 -0
  140. data/spec/schema.rb +86 -0
  141. data/spec/semipublic/property/binary_spec.rb +14 -0
  142. data/spec/semipublic/property/boolean_spec.rb +48 -0
  143. data/spec/semipublic/property/class_spec.rb +36 -0
  144. data/spec/semipublic/property/date_spec.rb +44 -0
  145. data/spec/semipublic/property/date_time_spec.rb +47 -0
  146. data/spec/semipublic/property/decimal_spec.rb +83 -0
  147. data/spec/semipublic/property/discriminator_spec.rb +22 -0
  148. data/spec/semipublic/property/float_spec.rb +83 -0
  149. data/spec/semipublic/property/integer_spec.rb +83 -0
  150. data/spec/semipublic/property/lookup_spec.rb +27 -0
  151. data/spec/semipublic/property/serial_spec.rb +14 -0
  152. data/spec/semipublic/property/string_spec.rb +14 -0
  153. data/spec/semipublic/property/text_spec.rb +30 -0
  154. data/spec/semipublic/property/time_spec.rb +49 -0
  155. data/spec/semipublic/property_spec.rb +51 -0
  156. data/spec/shared/flags_shared_spec.rb +36 -0
  157. data/spec/shared/identity_function_group.rb +5 -0
  158. data/spec/shared/public_property_spec.rb +229 -0
  159. data/spec/shared/semipublic_property_spec.rb +159 -0
  160. data/spec/spec.opts +4 -0
  161. data/spec/spec_helper.rb +58 -0
  162. data/spec/unit/bcrypt_hash_spec.rb +154 -0
  163. data/spec/unit/csv_spec.rb +139 -0
  164. data/spec/unit/dirty_minder_spec.rb +64 -0
  165. data/spec/unit/enum_spec.rb +125 -0
  166. data/spec/unit/epoch_time_spec.rb +72 -0
  167. data/spec/unit/file_path_spec.rb +75 -0
  168. data/spec/unit/flag_spec.rb +114 -0
  169. data/spec/unit/ip_address_spec.rb +109 -0
  170. data/spec/unit/json_spec.rb +127 -0
  171. data/spec/unit/paranoid_boolean_spec.rb +142 -0
  172. data/spec/unit/paranoid_datetime_spec.rb +149 -0
  173. data/spec/unit/regexp_spec.rb +62 -0
  174. data/spec/unit/uri_spec.rb +64 -0
  175. data/spec/unit/yaml_spec.rb +111 -0
  176. data/tasks/spec.rake +40 -0
  177. data/tasks/yard.rake +9 -0
  178. data/tasks/yardstick.rake +19 -0
  179. 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'
@@ -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