hashie 3.6.0 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +228 -174
- data/CONTRIBUTING.md +13 -6
- data/README.md +127 -15
- data/UPGRADING.md +83 -7
- data/hashie.gemspec +13 -7
- data/lib/hashie.rb +21 -19
- data/lib/hashie/dash.rb +2 -1
- data/lib/hashie/extensions/active_support/core_ext/hash.rb +14 -0
- data/lib/hashie/extensions/coercion.rb +23 -16
- data/lib/hashie/extensions/dash/indifferent_access.rb +20 -1
- data/lib/hashie/extensions/dash/property_translation.rb +6 -3
- data/lib/hashie/extensions/deep_fetch.rb +4 -2
- data/lib/hashie/extensions/deep_find.rb +12 -3
- data/lib/hashie/extensions/deep_locate.rb +22 -7
- data/lib/hashie/extensions/deep_merge.rb +18 -1
- data/lib/hashie/extensions/indifferent_access.rb +1 -3
- data/lib/hashie/extensions/key_conflict_warning.rb +55 -0
- data/lib/hashie/extensions/mash/define_accessors.rb +90 -0
- data/lib/hashie/extensions/mash/keep_original_keys.rb +2 -1
- data/lib/hashie/extensions/mash/permissive_respond_to.rb +61 -0
- data/lib/hashie/extensions/mash/safe_assignment.rb +3 -1
- data/lib/hashie/extensions/method_access.rb +5 -2
- data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +26 -4
- data/lib/hashie/extensions/ruby_version_check.rb +5 -1
- data/lib/hashie/extensions/strict_key_access.rb +8 -4
- data/lib/hashie/hash.rb +16 -9
- data/lib/hashie/mash.rb +114 -53
- data/lib/hashie/railtie.rb +7 -0
- data/lib/hashie/rash.rb +1 -1
- data/lib/hashie/utils.rb +28 -0
- data/lib/hashie/version.rb +1 -1
- metadata +19 -130
- data/spec/hashie/array_spec.rb +0 -29
- data/spec/hashie/clash_spec.rb +0 -70
- data/spec/hashie/dash_spec.rb +0 -598
- data/spec/hashie/extensions/autoload_spec.rb +0 -24
- data/spec/hashie/extensions/coercion_spec.rb +0 -639
- data/spec/hashie/extensions/dash/coercion_spec.rb +0 -13
- data/spec/hashie/extensions/dash/indifferent_access_spec.rb +0 -84
- data/spec/hashie/extensions/deep_fetch_spec.rb +0 -97
- data/spec/hashie/extensions/deep_find_spec.rb +0 -138
- data/spec/hashie/extensions/deep_locate_spec.rb +0 -137
- data/spec/hashie/extensions/deep_merge_spec.rb +0 -70
- data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -47
- data/spec/hashie/extensions/indifferent_access_spec.rb +0 -295
- data/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb +0 -208
- data/spec/hashie/extensions/key_conversion_spec.rb +0 -12
- data/spec/hashie/extensions/mash/keep_original_keys_spec.rb +0 -46
- data/spec/hashie/extensions/mash/safe_assignment_spec.rb +0 -50
- data/spec/hashie/extensions/mash/symbolize_keys_spec.rb +0 -39
- data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
- data/spec/hashie/extensions/method_access_spec.rb +0 -226
- data/spec/hashie/extensions/strict_key_access_spec.rb +0 -110
- data/spec/hashie/extensions/stringify_keys_spec.rb +0 -124
- data/spec/hashie/extensions/symbolize_keys_spec.rb +0 -129
- data/spec/hashie/hash_spec.rb +0 -84
- data/spec/hashie/mash_spec.rb +0 -771
- data/spec/hashie/parsers/yaml_erb_parser_spec.rb +0 -46
- data/spec/hashie/rash_spec.rb +0 -83
- data/spec/hashie/trash_spec.rb +0 -328
- data/spec/hashie/utils_spec.rb +0 -25
- data/spec/hashie/version_spec.rb +0 -7
- data/spec/hashie_spec.rb +0 -13
- data/spec/integration/elasticsearch/integration_spec.rb +0 -40
- data/spec/integration/omniauth-oauth2/app.rb +0 -52
- data/spec/integration/omniauth-oauth2/integration_spec.rb +0 -26
- data/spec/integration/omniauth-oauth2/some_site.rb +0 -38
- data/spec/integration/omniauth/app.rb +0 -11
- data/spec/integration/omniauth/integration_spec.rb +0 -38
- data/spec/integration/rails-without-dependency/integration_spec.rb +0 -15
- data/spec/integration/rails/app.rb +0 -47
- data/spec/integration/rails/integration_spec.rb +0 -26
- data/spec/spec_helper.rb +0 -23
- data/spec/support/integration_specs.rb +0 -36
- data/spec/support/logger.rb +0 -24
- data/spec/support/module_context.rb +0 -11
- data/spec/support/ruby_version_check.rb +0 -6
data/lib/hashie/dash.rb
CHANGED
@@ -219,7 +219,8 @@ module Hashie
|
|
219
219
|
end
|
220
220
|
|
221
221
|
def fail_property_required_error!(property)
|
222
|
-
raise ArgumentError,
|
222
|
+
raise ArgumentError,
|
223
|
+
"The property '#{property}' #{self.class.required_properties[property][:message]}"
|
223
224
|
end
|
224
225
|
|
225
226
|
def fail_no_property_error!(property)
|
@@ -1,5 +1,9 @@
|
|
1
1
|
module Hashie
|
2
|
-
class CoercionError < StandardError
|
2
|
+
class CoercionError < StandardError
|
3
|
+
def initialize(key, value, into, message)
|
4
|
+
super("Cannot coerce property #{key.inspect} from #{value.class} to #{into}: #{message}")
|
5
|
+
end
|
6
|
+
end
|
3
7
|
|
4
8
|
module Extensions
|
5
9
|
module Coercion
|
@@ -12,20 +16,22 @@ module Hashie
|
|
12
16
|
Symbol => :to_sym
|
13
17
|
}.freeze
|
14
18
|
|
15
|
-
ABSTRACT_CORE_TYPES =
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
ABSTRACT_CORE_TYPES =
|
20
|
+
if RubyVersion.new(RUBY_VERSION) >= RubyVersion.new('2.4.0')
|
21
|
+
{ Numeric => [Integer, Float, Complex, Rational] }
|
22
|
+
else
|
23
|
+
{
|
24
|
+
Integer => [Fixnum, Bignum],
|
25
|
+
Numeric => [Fixnum, Bignum, Float, Complex, Rational]
|
26
|
+
}
|
27
|
+
end
|
23
28
|
|
24
29
|
def self.included(base)
|
25
30
|
base.send :include, InstanceMethods
|
26
|
-
base.extend ClassMethods
|
27
|
-
|
28
|
-
|
31
|
+
base.extend ClassMethods
|
32
|
+
unless base.method_defined?(:set_value_without_coercion)
|
33
|
+
base.send :alias_method, :set_value_without_coercion, :[]=
|
34
|
+
end
|
29
35
|
base.send :alias_method, :[]=, :set_value_with_coercion
|
30
36
|
end
|
31
37
|
|
@@ -37,7 +43,7 @@ module Hashie
|
|
37
43
|
begin
|
38
44
|
value = self.class.fetch_coercion(into).call(value)
|
39
45
|
rescue NoMethodError, TypeError => e
|
40
|
-
raise CoercionError,
|
46
|
+
raise CoercionError.new(key, value, into, e.message)
|
41
47
|
end
|
42
48
|
end
|
43
49
|
|
@@ -97,7 +103,8 @@ module Hashie
|
|
97
103
|
#
|
98
104
|
# @param [Class] from the type you would like coerced.
|
99
105
|
# @param [Class] into the class into which you would like the value coerced.
|
100
|
-
# @option options [Boolean] :strict (true) whether use exact source class
|
106
|
+
# @option options [Boolean] :strict (true) whether use exact source class
|
107
|
+
# only or include ancestors
|
101
108
|
#
|
102
109
|
# @example Coerce all hashes into this special type of hash
|
103
110
|
# class SpecialHash < Hash
|
@@ -159,10 +166,10 @@ module Hashie
|
|
159
166
|
|
160
167
|
def build_coercion(type)
|
161
168
|
if type.is_a? Enumerable
|
162
|
-
if type.class
|
169
|
+
if type.class == ::Hash
|
163
170
|
type, key_type, value_type = type.class, *type.first
|
164
171
|
build_hash_coercion(type, key_type, value_type)
|
165
|
-
else
|
172
|
+
else
|
166
173
|
value_type = type.first
|
167
174
|
type = type.class
|
168
175
|
build_container_coercion(type, value_type)
|
@@ -7,11 +7,23 @@ module Hashie
|
|
7
7
|
base.send :include, Hashie::Extensions::IndifferentAccess
|
8
8
|
end
|
9
9
|
|
10
|
+
def self.maybe_extend(base)
|
11
|
+
return unless requires_class_methods?(base)
|
12
|
+
|
13
|
+
base.extend(ClassMethods)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.requires_class_methods?(klass)
|
17
|
+
klass <= Hashie::Dash &&
|
18
|
+
!klass.singleton_class.included_modules.include?(ClassMethods)
|
19
|
+
end
|
20
|
+
private_class_method :requires_class_methods?
|
21
|
+
|
10
22
|
module ClassMethods
|
11
23
|
# Check to see if the specified property has already been
|
12
24
|
# defined.
|
13
25
|
def property?(name)
|
14
|
-
name = translations[name.to_sym] if
|
26
|
+
name = translations[name.to_sym] if translation_for?(name)
|
15
27
|
name = name.to_s
|
16
28
|
!!properties.find { |property| property.to_s == name }
|
17
29
|
end
|
@@ -30,6 +42,13 @@ module Hashie
|
|
30
42
|
name = name.to_s
|
31
43
|
!!transforms.keys.find { |key| key.to_s == name }
|
32
44
|
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def translation_for?(name)
|
49
|
+
included_modules.include?(Hashie::Extensions::Dash::PropertyTranslation) &&
|
50
|
+
translation_exists?(name)
|
51
|
+
end
|
33
52
|
end
|
34
53
|
end
|
35
54
|
end
|
@@ -35,7 +35,7 @@ module Hashie
|
|
35
35
|
# end
|
36
36
|
#
|
37
37
|
# model = DataModelHash.new(id: '123', created: '2014-04-25 22:35:28')
|
38
|
-
# model.id.class #=> Fixnum
|
38
|
+
# model.id.class #=> Integer (Fixnum if you are using Ruby 2.3 or lower)
|
39
39
|
# model.created_at.class #=> Time
|
40
40
|
module PropertyTranslation
|
41
41
|
def self.included(base)
|
@@ -58,7 +58,9 @@ module Hashie
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def permitted_input_keys
|
61
|
-
@permitted_input_keys ||=
|
61
|
+
@permitted_input_keys ||=
|
62
|
+
properties
|
63
|
+
.map { |property| inverse_translations.fetch property, property }
|
62
64
|
end
|
63
65
|
|
64
66
|
# Defines a property on the Trash. Options are as follows:
|
@@ -135,7 +137,8 @@ module Hashie
|
|
135
137
|
end
|
136
138
|
|
137
139
|
def fail_self_transformation_error!(property_name)
|
138
|
-
raise ArgumentError,
|
140
|
+
raise ArgumentError,
|
141
|
+
"Property name (#{property_name}) and :from option must not be the same"
|
139
142
|
end
|
140
143
|
|
141
144
|
def valid_transformer?(transformer)
|
@@ -9,7 +9,8 @@ module Hashie
|
|
9
9
|
#
|
10
10
|
# options.deep_fetch(:user, :non_existent_key) { 'a value' } #=> 'a value'
|
11
11
|
#
|
12
|
-
# This is particularly useful for fetching values from deeply nested api responses
|
12
|
+
# This is particularly useful for fetching values from deeply nested api responses
|
13
|
+
# or params hashes.
|
13
14
|
module DeepFetch
|
14
15
|
class UndefinedPathError < StandardError; end
|
15
16
|
|
@@ -20,7 +21,8 @@ module Hashie
|
|
20
21
|
obj.fetch(arg)
|
21
22
|
rescue ArgumentError, IndexError, NoMethodError => e
|
22
23
|
break yield(arg) if block
|
23
|
-
raise UndefinedPathError,
|
24
|
+
raise UndefinedPathError,
|
25
|
+
"Could not fetch path (#{args.join(' > ')}) at #{arg}", e.backtrace
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'hashie/extensions/deep_locate'
|
1
2
|
module Hashie
|
2
3
|
module Extensions
|
3
4
|
module DeepFind
|
@@ -24,7 +25,12 @@ module Hashie
|
|
24
25
|
# Performs a depth-first search on deeply nested data structures for
|
25
26
|
# a key and returns all occurrences of the key.
|
26
27
|
#
|
27
|
-
# options = {
|
28
|
+
# options = {
|
29
|
+
# users: [
|
30
|
+
# { location: {address: '123 Street'} },
|
31
|
+
# { location: {address: '234 Street'}}
|
32
|
+
# ]
|
33
|
+
# }
|
28
34
|
# options.extend(Hashie::Extensions::DeepFind)
|
29
35
|
# options.deep_find_all(:address) # => ['123 Street', '234 Street']
|
30
36
|
#
|
@@ -33,7 +39,10 @@ module Hashie
|
|
33
39
|
# end
|
34
40
|
#
|
35
41
|
# my_hash = MyHash.new
|
36
|
-
# my_hash[:users] = [
|
42
|
+
# my_hash[:users] = [
|
43
|
+
# {location: {address: '123 Street'}},
|
44
|
+
# {location: {address: '234 Street'}}
|
45
|
+
# ]
|
37
46
|
# my_hash.deep_find_all(:address) # => ['123 Street', '234 Street']
|
38
47
|
def deep_find_all(key)
|
39
48
|
matches = _deep_find_all(key)
|
@@ -49,7 +58,7 @@ module Hashie
|
|
49
58
|
end
|
50
59
|
|
51
60
|
def _deep_find_all(key, object = self, matches = [])
|
52
|
-
deep_locate_result =
|
61
|
+
deep_locate_result = DeepLocate.deep_locate(key, object).tap do |result|
|
53
62
|
result.map! { |element| element[key] }
|
54
63
|
end
|
55
64
|
|
@@ -14,10 +14,12 @@ module Hashie
|
|
14
14
|
# ...
|
15
15
|
# ]
|
16
16
|
#
|
17
|
-
#
|
17
|
+
# DeepLocate.deep_locate -> (key, value, object) { key == :title }, books
|
18
18
|
# # => [{:title=>"Ruby for beginners", :pages=>120}, ...]
|
19
19
|
def self.deep_locate(comparator, object)
|
20
|
-
|
20
|
+
unless comparator.respond_to?(:call)
|
21
|
+
comparator = _construct_key_comparator(comparator, object)
|
22
|
+
end
|
21
23
|
|
22
24
|
_deep_locate(comparator, object)
|
23
25
|
end
|
@@ -53,17 +55,21 @@ module Hashie
|
|
53
55
|
# # http://ruby-journal.com/becareful-with-space-in-lambda-hash-rocket-syntax-between-ruby-1-dot-9-and-2-dot-0/
|
54
56
|
#
|
55
57
|
# books.deep_locate -> (key, value, object) { key == :title && value.include?("Ruby") }
|
56
|
-
# # => [{:title=>"Ruby for beginners", :pages=>120},
|
58
|
+
# # => [{:title=>"Ruby for beginners", :pages=>120},
|
59
|
+
# # {:title=>"Ruby for the rest of us", :pages=>576}]
|
57
60
|
#
|
58
61
|
# books.deep_locate -> (key, value, object) { key == :pages && value <= 120 }
|
59
|
-
# # => [{:title=>"Ruby for beginners", :pages=>120},
|
62
|
+
# # => [{:title=>"Ruby for beginners", :pages=>120},
|
63
|
+
# # {:title=>"CSS for intermediates", :pages=>80}]
|
60
64
|
def deep_locate(comparator)
|
61
65
|
Hashie::Extensions::DeepLocate.deep_locate(comparator, self)
|
62
66
|
end
|
63
67
|
|
64
68
|
def self._construct_key_comparator(search_key, object)
|
65
|
-
|
66
|
-
|
69
|
+
if object.respond_to?(:indifferent_access?) && object.indifferent_access? ||
|
70
|
+
activesupport_indifferent?(object)
|
71
|
+
search_key = search_key.to_s
|
72
|
+
end
|
67
73
|
|
68
74
|
lambda do |non_callable_object|
|
69
75
|
->(key, _, _) { key == non_callable_object }
|
@@ -73,7 +79,10 @@ module Hashie
|
|
73
79
|
|
74
80
|
def self._deep_locate(comparator, object, result = [])
|
75
81
|
if object.is_a?(::Enumerable)
|
76
|
-
|
82
|
+
if object.any? { |value| _match_comparator?(value, comparator, object) }
|
83
|
+
result.push object
|
84
|
+
end
|
85
|
+
|
77
86
|
(object.respond_to?(:values) ? object.values : object.entries).each do |value|
|
78
87
|
_deep_locate(comparator, value, result)
|
79
88
|
end
|
@@ -93,6 +102,12 @@ module Hashie
|
|
93
102
|
comparator.call(key, value, object)
|
94
103
|
end
|
95
104
|
private_class_method :_match_comparator?
|
105
|
+
|
106
|
+
def self.activesupport_indifferent?(object)
|
107
|
+
defined?(::ActiveSupport::HashWithIndifferentAccess) &&
|
108
|
+
object.is_a?(::ActiveSupport::HashWithIndifferentAccess)
|
109
|
+
end
|
110
|
+
private_class_method :activesupport_indifferent?
|
96
111
|
end
|
97
112
|
end
|
98
113
|
end
|
@@ -3,7 +3,7 @@ module Hashie
|
|
3
3
|
module DeepMerge
|
4
4
|
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
5
5
|
def deep_merge(other_hash, &block)
|
6
|
-
copy =
|
6
|
+
copy = _deep_dup(self)
|
7
7
|
copy.extend(Hashie::Extensions::DeepMerge) unless copy.respond_to?(:deep_merge!)
|
8
8
|
copy.deep_merge!(other_hash, &block)
|
9
9
|
end
|
@@ -18,11 +18,28 @@ module Hashie
|
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
+
def _deep_dup(hash)
|
22
|
+
copy = hash.dup
|
23
|
+
|
24
|
+
copy.each do |key, value|
|
25
|
+
copy[key] =
|
26
|
+
if value.is_a?(::Hash)
|
27
|
+
_deep_dup(value)
|
28
|
+
else
|
29
|
+
Hashie::Utils.safe_dup(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
copy
|
34
|
+
end
|
35
|
+
|
21
36
|
def _recursive_merge(hash, other_hash, &block)
|
22
37
|
other_hash.each do |k, v|
|
23
38
|
hash[k] =
|
24
39
|
if hash.key?(k) && hash[k].is_a?(::Hash) && v.is_a?(::Hash)
|
25
40
|
_recursive_merge(hash[k], v, &block)
|
41
|
+
elsif v.is_a?(::Hash)
|
42
|
+
_recursive_merge({}, v, &block)
|
26
43
|
elsif hash.key?(k) && block_given?
|
27
44
|
yield(k, hash[k], v)
|
28
45
|
else
|
@@ -24,9 +24,7 @@ module Hashie
|
|
24
24
|
#
|
25
25
|
module IndifferentAccess
|
26
26
|
def self.included(base)
|
27
|
-
Hashie::Extensions::Dash::IndifferentAccess
|
28
|
-
base.extend(extension) if base <= Hashie::Dash && !base.singleton_class.included_modules.include?(extension)
|
29
|
-
end
|
27
|
+
Hashie::Extensions::Dash::IndifferentAccess.maybe_extend(base)
|
30
28
|
|
31
29
|
base.class_eval do
|
32
30
|
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Hashie
|
2
|
+
module Extensions
|
3
|
+
module KeyConflictWarning
|
4
|
+
class CannotDisableMashWarnings < StandardError
|
5
|
+
def initialize
|
6
|
+
super(
|
7
|
+
'You cannot disable warnings on the base Mash class. ' \
|
8
|
+
'Please subclass the Mash and disable it in the subclass.'
|
9
|
+
)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Disable the logging of warnings based on keys conflicting keys/methods
|
14
|
+
#
|
15
|
+
# @api semipublic
|
16
|
+
# @return [void]
|
17
|
+
def disable_warnings(*method_keys)
|
18
|
+
raise CannotDisableMashWarnings if self == Hashie::Mash
|
19
|
+
if method_keys.any?
|
20
|
+
disabled_warnings.concat(method_keys).tap(&:flatten!).uniq!
|
21
|
+
else
|
22
|
+
disabled_warnings.clear
|
23
|
+
end
|
24
|
+
|
25
|
+
@disable_warnings = true
|
26
|
+
end
|
27
|
+
|
28
|
+
# Checks whether this class disables warnings for conflicting keys/methods
|
29
|
+
#
|
30
|
+
# @api semipublic
|
31
|
+
# @return [Boolean]
|
32
|
+
def disable_warnings?(method_key = nil)
|
33
|
+
return disabled_warnings.include?(method_key) if disabled_warnings.any? && method_key
|
34
|
+
@disable_warnings ||= false
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns an array of methods that this class disables warnings for.
|
38
|
+
#
|
39
|
+
# @api semipublic
|
40
|
+
# @return [Boolean]
|
41
|
+
def disabled_warnings
|
42
|
+
@_disabled_warnings ||= []
|
43
|
+
end
|
44
|
+
|
45
|
+
# Inheritance hook that sets class configuration when inherited.
|
46
|
+
#
|
47
|
+
# @api semipublic
|
48
|
+
# @return [void]
|
49
|
+
def inherited(subclass)
|
50
|
+
super
|
51
|
+
subclass.disable_warnings(disabled_warnings) if disable_warnings?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Hashie
|
2
|
+
module Extensions
|
3
|
+
module Mash
|
4
|
+
module DefineAccessors
|
5
|
+
def self.included(klass)
|
6
|
+
klass.class_eval do
|
7
|
+
mod = Ext.new
|
8
|
+
include mod
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.extended(obj)
|
13
|
+
included(obj.singleton_class)
|
14
|
+
end
|
15
|
+
|
16
|
+
class Ext < Module
|
17
|
+
def initialize
|
18
|
+
mod = self
|
19
|
+
define_method(:method_missing) do |method_name, *args, &block|
|
20
|
+
key, suffix = method_name_and_suffix(method_name)
|
21
|
+
case suffix
|
22
|
+
when '='.freeze
|
23
|
+
mod.define_writer(key, method_name)
|
24
|
+
when '?'.freeze
|
25
|
+
mod.define_predicate(key, method_name)
|
26
|
+
when '!'.freeze
|
27
|
+
mod.define_initializing_reader(key, method_name)
|
28
|
+
when '_'.freeze
|
29
|
+
mod.define_underbang_reader(key, method_name)
|
30
|
+
else
|
31
|
+
mod.define_reader(key, method_name)
|
32
|
+
end
|
33
|
+
send(method_name, *args, &block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def define_reader(key, method_name)
|
38
|
+
define_method(method_name) do |&block|
|
39
|
+
if key? method_name
|
40
|
+
self.[](method_name, &block)
|
41
|
+
else
|
42
|
+
self.[](key, &block)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def define_writer(key, method_name)
|
48
|
+
define_method(method_name) do |value = nil|
|
49
|
+
if key? method_name
|
50
|
+
self.[](method_name, &proc)
|
51
|
+
else
|
52
|
+
assign_property(key, value)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def define_predicate(key, method_name)
|
58
|
+
define_method(method_name) do
|
59
|
+
if key? method_name
|
60
|
+
self.[](method_name, &proc)
|
61
|
+
else
|
62
|
+
!!self[key]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def define_initializing_reader(key, method_name)
|
68
|
+
define_method(method_name) do
|
69
|
+
if key? method_name
|
70
|
+
self.[](method_name, &proc)
|
71
|
+
else
|
72
|
+
initializing_reader(key)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def define_underbang_reader(key, method_name)
|
78
|
+
define_method(method_name) do
|
79
|
+
if key? method_name
|
80
|
+
self.[](key, &proc)
|
81
|
+
else
|
82
|
+
underbang_reader(key)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|