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,176 @@
|
|
|
1
|
+
module Ardm
|
|
2
|
+
# This class has dubious semantics and we only have it so that people can write
|
|
3
|
+
# params[:key] instead of params['key'].
|
|
4
|
+
class Mash < Hash
|
|
5
|
+
|
|
6
|
+
# Initializes a new mash.
|
|
7
|
+
#
|
|
8
|
+
# @param [Hash, Object] constructor
|
|
9
|
+
# The default value for the mash. If +constructor+ is a Hash, a new mash
|
|
10
|
+
# will be created based on the keys of the hash and no default value will
|
|
11
|
+
# be set.
|
|
12
|
+
def initialize(constructor = {})
|
|
13
|
+
if constructor.is_a?(Hash)
|
|
14
|
+
super()
|
|
15
|
+
update(constructor)
|
|
16
|
+
else
|
|
17
|
+
super(constructor)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Gets the default value for the mash.
|
|
22
|
+
#
|
|
23
|
+
# @param [Object] key
|
|
24
|
+
# The default value for the mash. If +key+ is a Symbol and it is a key in
|
|
25
|
+
# the mash, then the default value will be set to the value matching the
|
|
26
|
+
# key.
|
|
27
|
+
def default(key = nil)
|
|
28
|
+
if key.is_a?(Symbol) && include?(key = key.to_s)
|
|
29
|
+
self[key]
|
|
30
|
+
else
|
|
31
|
+
super
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
|
36
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
|
37
|
+
|
|
38
|
+
# Sets the +value+ associated with the specified +key+.
|
|
39
|
+
#
|
|
40
|
+
# @param [Object] key The key to set.
|
|
41
|
+
# @param [Object] value The value to set the key to.
|
|
42
|
+
def []=(key, value)
|
|
43
|
+
regular_writer(convert_key(key), convert_value(value))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Updates the mash with the key/value pairs from the specified hash.
|
|
47
|
+
#
|
|
48
|
+
# @param [Hash] other_hash
|
|
49
|
+
# A hash to update values in the mash with. The keys and the values will be
|
|
50
|
+
# converted to Mash format.
|
|
51
|
+
#
|
|
52
|
+
# @return [Mash] The updated mash.
|
|
53
|
+
def update(other_hash)
|
|
54
|
+
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
alias_method :merge!, :update
|
|
59
|
+
|
|
60
|
+
# Determines whether the mash contains the specified +key+.
|
|
61
|
+
#
|
|
62
|
+
# @param [Object] key The key to check for.
|
|
63
|
+
# @return [Boolean] True if the key exists in the mash.
|
|
64
|
+
def key?(key)
|
|
65
|
+
super(convert_key(key))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
alias_method :include?, :key?
|
|
69
|
+
alias_method :has_key?, :key?
|
|
70
|
+
alias_method :member?, :key?
|
|
71
|
+
|
|
72
|
+
# @param [Object] key The key to fetch.
|
|
73
|
+
# @param [Array] *extras Default value.
|
|
74
|
+
#
|
|
75
|
+
# @return [Object] The value at key or the default value.
|
|
76
|
+
def fetch(key, *extras)
|
|
77
|
+
super(convert_key(key), *extras)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @param [Array] *indices
|
|
81
|
+
# The keys to retrieve values for.
|
|
82
|
+
#
|
|
83
|
+
# @return [Array] The values at each of the provided keys.
|
|
84
|
+
def values_at(*indices)
|
|
85
|
+
indices.collect {|key| self[convert_key(key)]}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @param [Hash] hash The hash to merge with the mash.
|
|
89
|
+
#
|
|
90
|
+
# @return [Mash] A new mash with the hash values merged in.
|
|
91
|
+
def merge(hash)
|
|
92
|
+
self.dup.update(hash)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @param [Object] key The key to delete from the mash.
|
|
96
|
+
def delete(key)
|
|
97
|
+
super(convert_key(key))
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Returns a mash that includes everything but the given +keys+.
|
|
101
|
+
#
|
|
102
|
+
# @param [Array<String, Symbol>] *keys The mash keys to exclude.
|
|
103
|
+
#
|
|
104
|
+
# @return [Mash] A new mash without the selected keys.
|
|
105
|
+
#
|
|
106
|
+
# @example
|
|
107
|
+
# { :one => 1, :two => 2, :three => 3 }.except(:one)
|
|
108
|
+
# #=> { "two" => 2, "three" => 3 }
|
|
109
|
+
def except(*keys)
|
|
110
|
+
self.dup.except!(*keys.map {|k| convert_key(k)})
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Removes the specified +keys+ from the mash.
|
|
114
|
+
#
|
|
115
|
+
# @param [Array] *keys The mash keys to exclude.
|
|
116
|
+
#
|
|
117
|
+
# @return [Hash] +hash+
|
|
118
|
+
#
|
|
119
|
+
# @example
|
|
120
|
+
# mash = { :one => 1, :two => 2, :three => 3 }
|
|
121
|
+
# mash.except!(:one, :two)
|
|
122
|
+
# mash # => { :three => 3 }
|
|
123
|
+
def except!(*keys)
|
|
124
|
+
keys.each { |key| delete(key) }
|
|
125
|
+
self
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Used to provide the same interface as Hash.
|
|
129
|
+
#
|
|
130
|
+
# @return [Mash] This mash unchanged.
|
|
131
|
+
def stringify_keys!; self end
|
|
132
|
+
|
|
133
|
+
# @return [Hash] The mash as a Hash with symbolized keys.
|
|
134
|
+
def symbolize_keys
|
|
135
|
+
h = Hash.new(default)
|
|
136
|
+
each { |key, val| h[key.to_sym] = val }
|
|
137
|
+
h
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @return [Hash] The mash as a Hash with string keys.
|
|
141
|
+
def to_hash
|
|
142
|
+
Hash.new(default).merge(self)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
protected
|
|
146
|
+
# @param [Object] key The key to convert.
|
|
147
|
+
#
|
|
148
|
+
# @param [Object]
|
|
149
|
+
# The converted key. If the key was a symbol, it will be converted to a
|
|
150
|
+
# string.
|
|
151
|
+
#
|
|
152
|
+
# @api private
|
|
153
|
+
def convert_key(key)
|
|
154
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# @param [Object] value The value to convert.
|
|
158
|
+
#
|
|
159
|
+
# @return [Object]
|
|
160
|
+
# The converted value. A Hash or an Array of hashes, will be converted to
|
|
161
|
+
# their Mash equivalents.
|
|
162
|
+
#
|
|
163
|
+
# @api private
|
|
164
|
+
def convert_value(value)
|
|
165
|
+
if value.class == Hash
|
|
166
|
+
mash = Mash.new(value)
|
|
167
|
+
mash.default = value.default
|
|
168
|
+
mash
|
|
169
|
+
elsif value.is_a?(Array)
|
|
170
|
+
value.collect { |e| convert_value(e) }
|
|
171
|
+
else
|
|
172
|
+
value
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module Ardm
|
|
2
|
+
|
|
3
|
+
# Use these modules to establish naming conventions.
|
|
4
|
+
# The default is UnderscoredAndPluralized.
|
|
5
|
+
# You assign a naming convention like so:
|
|
6
|
+
#
|
|
7
|
+
# connection.adapter.resource_naming_convention = NamingConventions::Resource::Underscored
|
|
8
|
+
#
|
|
9
|
+
# You can also easily assign a custom convention with a Proc:
|
|
10
|
+
#
|
|
11
|
+
# connection.adapter.resource_naming_convention = lambda do |value|
|
|
12
|
+
# 'tbl' + value.camelize(true)
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# Or by simply defining your own module in NamingConventions that responds to
|
|
16
|
+
# ::call.
|
|
17
|
+
#
|
|
18
|
+
# NOTE: It's important to set the convention before accessing your models
|
|
19
|
+
# since the resource_names are cached after first accessed.
|
|
20
|
+
# Ardm.setup(name, uri) returns the Adapter for convenience, so you can
|
|
21
|
+
# use code like this:
|
|
22
|
+
#
|
|
23
|
+
# adapter = Ardm.setup(:default, 'mock://localhost/mock')
|
|
24
|
+
# adapter.resource_naming_convention = NamingConventions::Resource::Underscored
|
|
25
|
+
module NamingConventions
|
|
26
|
+
|
|
27
|
+
module Resource
|
|
28
|
+
|
|
29
|
+
module UnderscoredAndPluralized
|
|
30
|
+
def self.call(name)
|
|
31
|
+
Ardm::Inflector.pluralize(Ardm::Inflector.underscore(name)).gsub('/', '_')
|
|
32
|
+
end
|
|
33
|
+
end # module UnderscoredAndPluralized
|
|
34
|
+
|
|
35
|
+
module UnderscoredAndPluralizedWithoutModule
|
|
36
|
+
def self.call(name)
|
|
37
|
+
Ardm::Inflector.pluralize(Ardm::Inflector.underscore(Ardm::Inflector.demodulize(name)))
|
|
38
|
+
end
|
|
39
|
+
end # module UnderscoredAndPluralizedWithoutModule
|
|
40
|
+
|
|
41
|
+
module UnderscoredAndPluralizedWithoutLeadingModule
|
|
42
|
+
def self.call(name)
|
|
43
|
+
UnderscoredAndPluralized.call(name.to_s.gsub(/^[^:]*::/,''))
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
module Underscored
|
|
48
|
+
def self.call(name)
|
|
49
|
+
Ardm::Inflector.underscore(name)
|
|
50
|
+
end
|
|
51
|
+
end # module Underscored
|
|
52
|
+
|
|
53
|
+
module Yaml
|
|
54
|
+
def self.call(name)
|
|
55
|
+
"#{Ardm::Inflector.pluralize(Ardm::Inflector.underscore(name))}.yaml"
|
|
56
|
+
end
|
|
57
|
+
end # module Yaml
|
|
58
|
+
|
|
59
|
+
end # module Resource
|
|
60
|
+
|
|
61
|
+
module Field
|
|
62
|
+
|
|
63
|
+
module UnderscoredAndPluralized
|
|
64
|
+
def self.call(property)
|
|
65
|
+
Ardm::Inflector.pluralize(Ardm::Inflector.underscore(property.name.to_s)).gsub('/', '_')
|
|
66
|
+
end
|
|
67
|
+
end # module UnderscoredAndPluralized
|
|
68
|
+
|
|
69
|
+
module UnderscoredAndPluralizedWithoutModule
|
|
70
|
+
def self.call(property)
|
|
71
|
+
Ardm::Inflector.pluralize(Ardm::Inflector.underscore(Ardm::Inflector.demodulize(property.name.to_s)))
|
|
72
|
+
end
|
|
73
|
+
end # module UnderscoredAndPluralizedWithoutModule
|
|
74
|
+
|
|
75
|
+
module Underscored
|
|
76
|
+
def self.call(property)
|
|
77
|
+
Ardm::Inflector.underscore(property.name.to_s)
|
|
78
|
+
end
|
|
79
|
+
end # module Underscored
|
|
80
|
+
|
|
81
|
+
module Yaml
|
|
82
|
+
def self.call(property)
|
|
83
|
+
"#{Ardm::Inflector.pluralize(Ardm::Inflector.underscore(property.name.to_s))}.yaml"
|
|
84
|
+
end
|
|
85
|
+
end # module Yaml
|
|
86
|
+
|
|
87
|
+
end # module Field
|
|
88
|
+
|
|
89
|
+
end # module NamingConventions
|
|
90
|
+
end # module Ardm
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
module Ardm
|
|
2
|
+
|
|
3
|
+
# An ordered set of things
|
|
4
|
+
#
|
|
5
|
+
# {OrderedSet} implements set behavior and keeps
|
|
6
|
+
# track of the order in which entries were added.
|
|
7
|
+
#
|
|
8
|
+
# {OrderedSet} allows to inject a class that implements
|
|
9
|
+
# {OrderedSet::Cache::API} at construction time, and will
|
|
10
|
+
# use that cache implementation to enforce set semantics
|
|
11
|
+
# and perform internal caching of insertion order.
|
|
12
|
+
#
|
|
13
|
+
# @see OrderedSet::Cache::API
|
|
14
|
+
# @see OrderedSet::Cache
|
|
15
|
+
# @see SubjectSet::NameCache
|
|
16
|
+
#
|
|
17
|
+
# @api private
|
|
18
|
+
class OrderedSet
|
|
19
|
+
|
|
20
|
+
# The default cache used by {OrderedSet}
|
|
21
|
+
#
|
|
22
|
+
# Uses a {Hash} as internal storage and enforces set semantics
|
|
23
|
+
# by calling #eql? and #hash on the set's entries.
|
|
24
|
+
#
|
|
25
|
+
# @api private
|
|
26
|
+
class Cache
|
|
27
|
+
|
|
28
|
+
# The default implementation of the {API} that {OrderedSet} expects from
|
|
29
|
+
# the cache object that it uses to
|
|
30
|
+
#
|
|
31
|
+
# 1. keep track of insertion order
|
|
32
|
+
# 2. enforce set semantics.
|
|
33
|
+
#
|
|
34
|
+
# Classes including {API} must customize the behavior of the cache in 2 ways:
|
|
35
|
+
#
|
|
36
|
+
# They must determine the value to use as cache key and thus set discriminator,
|
|
37
|
+
# by implementing the {#key_for} method. The {#key_for} method accepts an arbitrary
|
|
38
|
+
# object as param and the method is free to return whatever value from that method.
|
|
39
|
+
# Obviously this will most likely be some attribute or value otherwise derived from
|
|
40
|
+
# the object that got passed in.
|
|
41
|
+
#
|
|
42
|
+
# They must determine which objects are valid set entries by overwriting the
|
|
43
|
+
# {#valid?} method. The {#valid?} method accepts an arbitrary object as param and
|
|
44
|
+
# the overwriting method must return either true or false.
|
|
45
|
+
#
|
|
46
|
+
# The motivation behind this is that set semantics cannot always be enforced
|
|
47
|
+
# by calling {#eql?} and {#hash} on the set's entries. For example, two entries
|
|
48
|
+
# might be considered unique wrt the set if their names are the same, but other
|
|
49
|
+
# internal state differs. This is exactly the case for {Ardm::Property} and
|
|
50
|
+
# {Ardm::Associations::Relationship} objects.
|
|
51
|
+
#
|
|
52
|
+
# @see Ardm::SubjectSet::NameCache
|
|
53
|
+
#
|
|
54
|
+
# @api private
|
|
55
|
+
module API
|
|
56
|
+
|
|
57
|
+
# Initialize a new Cache
|
|
58
|
+
#
|
|
59
|
+
# @api private
|
|
60
|
+
def initialize
|
|
61
|
+
@cache = {}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Tests if the given entry qualifies to be added to the cache
|
|
65
|
+
#
|
|
66
|
+
# @param [Object] entry
|
|
67
|
+
# the entry to be checked
|
|
68
|
+
#
|
|
69
|
+
# @return [Boolean]
|
|
70
|
+
# true if the entry qualifies to be added to the cache
|
|
71
|
+
#
|
|
72
|
+
# @api private
|
|
73
|
+
def valid?(entry)
|
|
74
|
+
raise NotImplementedError, "#{self}#valid? must be implemented"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Given an entry, return the key to be used in the cache
|
|
78
|
+
#
|
|
79
|
+
# @param [Object] entry
|
|
80
|
+
# the entry to get the key for
|
|
81
|
+
#
|
|
82
|
+
# @return [Object, nil]
|
|
83
|
+
# a value derived from the entry that is used as key in the cache
|
|
84
|
+
#
|
|
85
|
+
# @api private
|
|
86
|
+
def key_for(entry)
|
|
87
|
+
raise NotImplementedError, "#{self}#key_for must be implemented"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Check if the entry exists in the cache
|
|
91
|
+
#
|
|
92
|
+
# @param [Object] entry
|
|
93
|
+
# the entry to test for
|
|
94
|
+
#
|
|
95
|
+
# @return [Boolean]
|
|
96
|
+
# true if entry is included in the cache
|
|
97
|
+
#
|
|
98
|
+
# @api private
|
|
99
|
+
def include?(entry)
|
|
100
|
+
@cache.has_key?(key_for(entry))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Return the index for the entry in the cache
|
|
104
|
+
#
|
|
105
|
+
# @param [Object] entry
|
|
106
|
+
# the entry to get the index for
|
|
107
|
+
#
|
|
108
|
+
# @return [Integer, nil]
|
|
109
|
+
# the index for the entry, or nil if it does not exist
|
|
110
|
+
#
|
|
111
|
+
# @api private
|
|
112
|
+
def [](entry)
|
|
113
|
+
@cache[key_for(entry)]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Set the index for the entry in the cache
|
|
117
|
+
#
|
|
118
|
+
# @param [Object] entry
|
|
119
|
+
# the entry to set the index for
|
|
120
|
+
# @param [Integer] index
|
|
121
|
+
# the index to assign to the given entry
|
|
122
|
+
#
|
|
123
|
+
# @return [Integer]
|
|
124
|
+
# the given index for the entry
|
|
125
|
+
#
|
|
126
|
+
# @api private
|
|
127
|
+
def []=(entry, index)
|
|
128
|
+
if valid?(entry)
|
|
129
|
+
@cache[key_for(entry)] = index
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Delete an entry from the cache
|
|
134
|
+
#
|
|
135
|
+
# @param [Object] entry
|
|
136
|
+
# the entry to delete from the cache
|
|
137
|
+
#
|
|
138
|
+
# @return [API] self
|
|
139
|
+
#
|
|
140
|
+
# @api private
|
|
141
|
+
def delete(entry)
|
|
142
|
+
deleted_index = @cache.delete(key_for(entry))
|
|
143
|
+
if deleted_index
|
|
144
|
+
@cache.each do |key, index|
|
|
145
|
+
@cache[key] -= 1 if index > deleted_index
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
deleted_index
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Removes all entries and returns self
|
|
152
|
+
#
|
|
153
|
+
# @return [API] self
|
|
154
|
+
#
|
|
155
|
+
# @api private
|
|
156
|
+
def clear
|
|
157
|
+
@cache.clear
|
|
158
|
+
self
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
end # module API
|
|
162
|
+
|
|
163
|
+
include API
|
|
164
|
+
|
|
165
|
+
# Tests if the given entry qualifies to be added to the cache
|
|
166
|
+
#
|
|
167
|
+
# @param [Object] entry
|
|
168
|
+
# the entry to be checked
|
|
169
|
+
#
|
|
170
|
+
# @return [true] true
|
|
171
|
+
#
|
|
172
|
+
# @api private
|
|
173
|
+
def valid?(entry)
|
|
174
|
+
true
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Given an entry, return the key to be used in the cache
|
|
178
|
+
#
|
|
179
|
+
# @param [Object] entry
|
|
180
|
+
# the entry to get the key for
|
|
181
|
+
#
|
|
182
|
+
# @return [Object]
|
|
183
|
+
# the passed in entry
|
|
184
|
+
#
|
|
185
|
+
# @api private
|
|
186
|
+
def key_for(entry)
|
|
187
|
+
entry
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
end # class Cache
|
|
191
|
+
|
|
192
|
+
include Enumerable
|
|
193
|
+
extend Equalizer
|
|
194
|
+
|
|
195
|
+
# This set's entries
|
|
196
|
+
#
|
|
197
|
+
# The order in this Array is not guaranteed
|
|
198
|
+
# to be the order in which the entries were
|
|
199
|
+
# inserted. Use #each to access the entries
|
|
200
|
+
# in insertion order.
|
|
201
|
+
#
|
|
202
|
+
# @return [Array]
|
|
203
|
+
# this set's entries
|
|
204
|
+
#
|
|
205
|
+
# @api private
|
|
206
|
+
attr_reader :entries
|
|
207
|
+
|
|
208
|
+
equalize :entries
|
|
209
|
+
|
|
210
|
+
# Initialize an OrderedSet
|
|
211
|
+
#
|
|
212
|
+
# @param [#each] entries
|
|
213
|
+
# the entries to initialize this set with
|
|
214
|
+
# @param [Class<Cache::API>] cache
|
|
215
|
+
# the cache implementation to use
|
|
216
|
+
#
|
|
217
|
+
# @api private
|
|
218
|
+
def initialize(entries = [], cache = Cache)
|
|
219
|
+
@cache = cache.new
|
|
220
|
+
@entries = []
|
|
221
|
+
merge(entries.to_ary)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Initialize a copy of OrderedSet
|
|
225
|
+
#
|
|
226
|
+
# @api private
|
|
227
|
+
def initialize_copy(*)
|
|
228
|
+
@cache = @cache.dup
|
|
229
|
+
@entries = @entries.dup
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Get the entry at the given index
|
|
233
|
+
#
|
|
234
|
+
# @param [Integer] index
|
|
235
|
+
# the index of the desired entry
|
|
236
|
+
#
|
|
237
|
+
# @return [Object, nil]
|
|
238
|
+
# the entry at the given index, or nil if no entry is present
|
|
239
|
+
#
|
|
240
|
+
# @api private
|
|
241
|
+
def [](index)
|
|
242
|
+
entries[index]
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Add or update an entry in the set
|
|
246
|
+
#
|
|
247
|
+
# If the entry to add isn't part of the set already,
|
|
248
|
+
# it will be added. If an entry with the same cache
|
|
249
|
+
# key as the entry to add is part of the set already,
|
|
250
|
+
# it will be replaced with the given entry.
|
|
251
|
+
#
|
|
252
|
+
# @param [Object] entry
|
|
253
|
+
# the entry to be added
|
|
254
|
+
#
|
|
255
|
+
# @return [OrderedSet] self
|
|
256
|
+
#
|
|
257
|
+
# @api private
|
|
258
|
+
def <<(entry)
|
|
259
|
+
if index = @cache[entry]
|
|
260
|
+
entries[index] = entry
|
|
261
|
+
else
|
|
262
|
+
@cache[entry] = size
|
|
263
|
+
entries << entry
|
|
264
|
+
end
|
|
265
|
+
self
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Merge with another Enumerable object
|
|
269
|
+
#
|
|
270
|
+
# @param [#each] other
|
|
271
|
+
# the Enumerable to merge with this OrderedSet
|
|
272
|
+
#
|
|
273
|
+
# @return [OrderedSet] self
|
|
274
|
+
#
|
|
275
|
+
# @api private
|
|
276
|
+
def merge(other)
|
|
277
|
+
other.each { |entry| self << entry }
|
|
278
|
+
self
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Delete an entry from this OrderedSet
|
|
282
|
+
#
|
|
283
|
+
# @param [Object] entry
|
|
284
|
+
# the entry to delete
|
|
285
|
+
#
|
|
286
|
+
# @return [Object, nil]
|
|
287
|
+
# the deleted entry or nil
|
|
288
|
+
#
|
|
289
|
+
# @api private
|
|
290
|
+
def delete(entry)
|
|
291
|
+
if index = @cache.delete(entry)
|
|
292
|
+
entries.delete_at(index)
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Removes all entries and returns self
|
|
297
|
+
#
|
|
298
|
+
# @return [OrderedSet] self
|
|
299
|
+
#
|
|
300
|
+
# @api private
|
|
301
|
+
def clear
|
|
302
|
+
@cache.clear
|
|
303
|
+
entries.clear
|
|
304
|
+
self
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# Iterate over each entry in the set
|
|
308
|
+
#
|
|
309
|
+
# @yield [entry]
|
|
310
|
+
# all entries in the set
|
|
311
|
+
#
|
|
312
|
+
# @yieldparam [Object] entry
|
|
313
|
+
# an entry in the set
|
|
314
|
+
#
|
|
315
|
+
# @return [OrderedSet] self
|
|
316
|
+
#
|
|
317
|
+
# @api private
|
|
318
|
+
def each
|
|
319
|
+
entries.each { |entry| yield(entry) }
|
|
320
|
+
self
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# The number of entries
|
|
324
|
+
#
|
|
325
|
+
# @return [Integer]
|
|
326
|
+
# the number of entries
|
|
327
|
+
#
|
|
328
|
+
# @api private
|
|
329
|
+
def size
|
|
330
|
+
entries.size
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Check if there are any entries
|
|
334
|
+
#
|
|
335
|
+
# @return [Boolean]
|
|
336
|
+
# true if the set is empty
|
|
337
|
+
#
|
|
338
|
+
# @api private
|
|
339
|
+
def empty?
|
|
340
|
+
entries.empty?
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Check if the entry exists in the set
|
|
344
|
+
#
|
|
345
|
+
# @param [Object] entry
|
|
346
|
+
# the entry to test for
|
|
347
|
+
#
|
|
348
|
+
# @return [Boolean]
|
|
349
|
+
# true if entry is included in the set
|
|
350
|
+
#
|
|
351
|
+
# @api private
|
|
352
|
+
def include?(entry)
|
|
353
|
+
entries.include?(entry)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# Return the index for the entry in the set
|
|
357
|
+
#
|
|
358
|
+
# @param [Object] entry
|
|
359
|
+
# the entry to check the set for
|
|
360
|
+
#
|
|
361
|
+
# @return [Integer, nil]
|
|
362
|
+
# the index for the entry, or nil if it does not exist
|
|
363
|
+
#
|
|
364
|
+
# @api private
|
|
365
|
+
def index(entry)
|
|
366
|
+
@cache[entry]
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# Convert the OrderedSet into an Array
|
|
370
|
+
#
|
|
371
|
+
# @return [Array]
|
|
372
|
+
# an array containing all the OrderedSet's entries
|
|
373
|
+
#
|
|
374
|
+
# @api private
|
|
375
|
+
def to_ary
|
|
376
|
+
entries
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
end # class OrderedSet
|
|
380
|
+
end # module Ardm
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Ardm
|
|
2
|
+
module Subject
|
|
3
|
+
# Returns a default value of the subject for given resource
|
|
4
|
+
#
|
|
5
|
+
# When default value is a callable object, it is called with resource
|
|
6
|
+
# and subject passed as arguments.
|
|
7
|
+
#
|
|
8
|
+
# @param [Resource] resource
|
|
9
|
+
# the model instance for which the default is to be set
|
|
10
|
+
#
|
|
11
|
+
# @return [Object]
|
|
12
|
+
# the default value of this subject for +resource+
|
|
13
|
+
#
|
|
14
|
+
# @api semipublic
|
|
15
|
+
def default_for(resource)
|
|
16
|
+
if @default.respond_to?(:call)
|
|
17
|
+
@default.call(resource, self)
|
|
18
|
+
else
|
|
19
|
+
Ardm::Ext.try_dup(@default)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Returns true if the subject has a default value
|
|
24
|
+
#
|
|
25
|
+
# @return [Boolean]
|
|
26
|
+
# true if the subject has a default value
|
|
27
|
+
#
|
|
28
|
+
# @api semipublic
|
|
29
|
+
def default?
|
|
30
|
+
@options.key?(:default)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|