hashie 3.4.2 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +518 -122
- data/CONTRIBUTING.md +24 -7
- data/LICENSE +1 -1
- data/README.md +455 -48
- data/Rakefile +18 -1
- data/UPGRADING.md +157 -7
- data/hashie.gemspec +14 -7
- data/lib/hashie/array.rb +21 -0
- data/lib/hashie/clash.rb +24 -12
- data/lib/hashie/dash.rb +56 -31
- data/lib/hashie/extensions/active_support/core_ext/hash.rb +14 -0
- data/lib/hashie/extensions/array/pretty_inspect.rb +19 -0
- data/lib/hashie/extensions/coercion.rb +91 -52
- data/lib/hashie/extensions/dash/coercion.rb +25 -0
- data/lib/hashie/extensions/dash/indifferent_access.rb +30 -1
- data/lib/hashie/extensions/dash/predefined_values.rb +88 -0
- data/lib/hashie/extensions/dash/property_translation.rb +59 -30
- data/lib/hashie/extensions/deep_fetch.rb +5 -3
- data/lib/hashie/extensions/deep_find.rb +14 -5
- data/lib/hashie/extensions/deep_locate.rb +40 -21
- data/lib/hashie/extensions/deep_merge.rb +28 -10
- data/lib/hashie/extensions/ignore_undeclared.rb +6 -4
- data/lib/hashie/extensions/indifferent_access.rb +49 -8
- 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 +53 -0
- 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/mash/symbolize_keys.rb +38 -0
- data/lib/hashie/extensions/method_access.rb +77 -19
- data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +29 -5
- data/lib/hashie/extensions/ruby_version.rb +60 -0
- data/lib/hashie/extensions/ruby_version_check.rb +21 -0
- data/lib/hashie/extensions/strict_key_access.rb +77 -0
- data/lib/hashie/extensions/stringify_keys.rb +8 -5
- data/lib/hashie/extensions/symbolize_keys.rb +21 -7
- data/lib/hashie/hash.rb +18 -11
- data/lib/hashie/logger.rb +18 -0
- data/lib/hashie/mash.rb +196 -55
- data/lib/hashie/railtie.rb +21 -0
- data/lib/hashie/rash.rb +7 -7
- data/lib/hashie/utils.rb +44 -0
- data/lib/hashie/version.rb +1 -1
- data/lib/hashie.rb +34 -16
- metadata +30 -79
- data/spec/hashie/clash_spec.rb +0 -48
- data/spec/hashie/dash_spec.rb +0 -513
- data/spec/hashie/extensions/autoload_spec.rb +0 -24
- data/spec/hashie/extensions/coercion_spec.rb +0 -625
- 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 -45
- data/spec/hashie/extensions/deep_locate_spec.rb +0 -124
- data/spec/hashie/extensions/deep_merge_spec.rb +0 -45
- data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -46
- data/spec/hashie/extensions/indifferent_access_spec.rb +0 -219
- 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/safe_assignment_spec.rb +0 -23
- data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
- data/spec/hashie/extensions/method_access_spec.rb +0 -184
- data/spec/hashie/extensions/stringify_keys_spec.rb +0 -101
- data/spec/hashie/extensions/symbolize_keys_spec.rb +0 -106
- data/spec/hashie/hash_spec.rb +0 -84
- data/spec/hashie/mash_spec.rb +0 -683
- data/spec/hashie/parsers/yaml_erb_parser_spec.rb +0 -29
- data/spec/hashie/rash_spec.rb +0 -77
- data/spec/hashie/trash_spec.rb +0 -268
- data/spec/hashie/version_spec.rb +0 -7
- data/spec/spec_helper.rb +0 -15
- data/spec/support/module_context.rb +0 -11
- data/spec/support/ruby_version.rb +0 -10
data/Rakefile
CHANGED
@@ -7,9 +7,26 @@ Bundler::GemHelper.install_tasks
|
|
7
7
|
require 'rspec/core/rake_task'
|
8
8
|
RSpec::Core::RakeTask.new do |spec|
|
9
9
|
spec.pattern = 'spec/**/*_spec.rb'
|
10
|
+
spec.exclude_pattern = 'spec/integration/**/*_spec.rb'
|
10
11
|
end
|
11
12
|
|
12
13
|
require 'rubocop/rake_task'
|
13
14
|
RuboCop::RakeTask.new(:rubocop)
|
14
15
|
|
15
|
-
|
16
|
+
require_relative 'spec/support/integration_specs'
|
17
|
+
task :integration_specs do
|
18
|
+
next if ENV['CI']
|
19
|
+
status_codes = []
|
20
|
+
handler = lambda do |status_code|
|
21
|
+
status_codes << status_code unless status_code.zero?
|
22
|
+
end
|
23
|
+
|
24
|
+
run_all_integration_specs(handler: handler, logger: ->(msg) { puts msg })
|
25
|
+
|
26
|
+
if status_codes.any?
|
27
|
+
warn "#{status_codes.size} integration test(s) failed"
|
28
|
+
exit status_codes.last
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
task default: %i[rubocop spec integration_specs]
|
data/UPGRADING.md
CHANGED
@@ -1,6 +1,158 @@
|
|
1
1
|
Upgrading Hashie
|
2
2
|
================
|
3
3
|
|
4
|
+
### Upgrading to 5.0.0
|
5
|
+
|
6
|
+
#### Mash initialization key conversion
|
7
|
+
|
8
|
+
Mash initialization now only converts to string keys which can be represented as symbols.
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
Hashie::Mash.new(
|
12
|
+
{foo: "bar"} => "baz",
|
13
|
+
"1" => "one string",
|
14
|
+
:"1" => "one sym",
|
15
|
+
1 => "one num"
|
16
|
+
)
|
17
|
+
|
18
|
+
# Before
|
19
|
+
{"{:foo=>\"bar\"}"=>"baz", "1"=>"one num"}
|
20
|
+
|
21
|
+
# After
|
22
|
+
{{:foo=>"bar"}=>"baz", "1"=>"one sym", 1=>"one num"}
|
23
|
+
```
|
24
|
+
|
25
|
+
#### Mash#dig with numeric keys
|
26
|
+
|
27
|
+
`Hashie::Mash#dig` no longer considers numeric keys for indifferent access.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
my_mash = Hashie::Mash.new("1" => "a") # => {"1"=>"a"}
|
31
|
+
|
32
|
+
my_mash.dig("1") # => "a"
|
33
|
+
my_mash.dig(:"1") # => "a"
|
34
|
+
|
35
|
+
# Before
|
36
|
+
my_mash.dig(1) # => "a"
|
37
|
+
|
38
|
+
# After
|
39
|
+
my_mash.dig(1) # => nil
|
40
|
+
```
|
41
|
+
|
42
|
+
### Upgrading to 4.0.0
|
43
|
+
|
44
|
+
#### Non-destructive Hash methods called on Mash
|
45
|
+
|
46
|
+
The following non-destructive Hash methods called on Mash will now return an instance of the class it was called on.
|
47
|
+
|
48
|
+
| method | ruby |
|
49
|
+
| ----------------- | ---- |
|
50
|
+
| #compact | |
|
51
|
+
| #invert | |
|
52
|
+
| #reject | |
|
53
|
+
| #select | |
|
54
|
+
| #slice | 2.5 |
|
55
|
+
| #transform_keys | 2.5 |
|
56
|
+
| #transform_values | 2.4 |
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
class Parents < Hashie::Mash; end
|
60
|
+
|
61
|
+
parents = Parents.new(father: 'Dad', mother: 'Mom')
|
62
|
+
cool_parents = parents.transform_values { |v| v + v[-1] + 'io'}
|
63
|
+
|
64
|
+
p cool_parents
|
65
|
+
|
66
|
+
# before:
|
67
|
+
{"father"=>"Daddio", "mother"=>"Mommio"}
|
68
|
+
=> {"father"=>"Daddio", "mother"=>"Mommio"}
|
69
|
+
|
70
|
+
# after:
|
71
|
+
#<Parents father="Daddio" mother="Mommio">
|
72
|
+
=> {"father"=>"Dad", "mother"=>"Mom"}
|
73
|
+
```
|
74
|
+
|
75
|
+
This may make places where you had to re-make the Mash redundant, and may cause unintended side effects if your application was expecting a plain old ruby Hash.
|
76
|
+
|
77
|
+
#### Ruby 2.6: Mash#merge and Mash#merge!
|
78
|
+
|
79
|
+
In Ruby > 2.6.0, Hashie now supports passing multiple hash and Mash objects to Mash#merge and Mash#merge!.
|
80
|
+
|
81
|
+
#### Hashie::Mash::CannotDisableMashWarnings error class is removed
|
82
|
+
|
83
|
+
There shouldn't really be a case that anyone was relying on catching this specific error, but if so, they should change it to rescue Hashie::Extensions::KeyConflictWarning::CannotDisableMashWarnings
|
84
|
+
|
85
|
+
### Upgrading to 3.7.0
|
86
|
+
|
87
|
+
#### Mash#load takes options
|
88
|
+
|
89
|
+
The `Hashie::Mash#load` method now accepts options, changing the interface of `Parser#initialize`. If you have a custom parser, you must update its `initialize` method.
|
90
|
+
|
91
|
+
For example, `Hashie::Extensions::Parsers::YamlErbParser` now accepts `permitted_classes`, `permitted_symbols` and `aliases` options.
|
92
|
+
|
93
|
+
Before:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
class Hashie::Extensions::Parsers::YamlErbParser
|
97
|
+
def initialize(file_path)
|
98
|
+
@file_path = file_path
|
99
|
+
end
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
After:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
class Hashie::Extensions::Parsers::YamlErbParser
|
107
|
+
def initialize(file_path, options = {})
|
108
|
+
@file_path = file_path
|
109
|
+
@options = options
|
110
|
+
end
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
Options can now be passed into `Mash#load`.
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
Mash.load(filename, permitted_classes: [])
|
118
|
+
```
|
119
|
+
|
120
|
+
### Upgrading to 3.5.2
|
121
|
+
|
122
|
+
#### Disable logging in Mash subclasses
|
123
|
+
|
124
|
+
If you subclass `Hashie::Mash`, you can now disable the logging we do about
|
125
|
+
overriding existing methods with keys. This looks like:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
class MyMash < Hashie::Mash
|
129
|
+
disable_warnings
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
### Upgrading to 3.4.7
|
134
|
+
|
135
|
+
#### Procs as default values for Dash
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
class MyHash < Hashie::Dash
|
139
|
+
property :time, default: -> { Time.now }
|
140
|
+
end
|
141
|
+
```
|
142
|
+
|
143
|
+
In versions < 3.4.7 `Time.now` will be evaluated when `time` property is accessed directly first time.
|
144
|
+
In version >= 3.4.7 `Time.now` is evaluated in time of object initialization.
|
145
|
+
### Upgrading to 3.4.4
|
146
|
+
|
147
|
+
#### Mash subclasses and reverse_merge
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
class MyMash < Hashie::Mash
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
154
|
+
In versions >= 3.4.4 `MyMash#reverse_merge` returns an instance of `MyMash` but in previous versions it was a `Hashie::Mash` instance.
|
155
|
+
|
4
156
|
### Upgrading to 3.2.2
|
5
157
|
|
6
158
|
#### Testing if key defined
|
@@ -29,7 +181,7 @@ h.abb? # => false
|
|
29
181
|
|
30
182
|
#### Possible coercion changes
|
31
183
|
|
32
|
-
The improvements made to coercions in version 3.2.1 [issue #200](https://github.com/
|
184
|
+
The improvements made to coercions in version 3.2.1 [issue #200](https://github.com/hashie/hashie/pull/200) do not break the documented API, but are significant enough that changes may effect undocumented side-effects. Applications that depended on those side-effects will need to be updated.
|
33
185
|
|
34
186
|
**Change**: Type coercion no longer creates new objects if the input matches the target type. Previously coerced properties always resulted in the creation of a new object, even when it wasn't necessary. This had the effect of a `dup` or `clone` on coerced properties but not uncoerced ones.
|
35
187
|
|
@@ -45,7 +197,7 @@ Applications that were attempting to rescuing the internal errors should be upda
|
|
45
197
|
|
46
198
|
#### Compatibility with Rails 4 Strong Parameters
|
47
199
|
|
48
|
-
Version 2.1 introduced support to prevent default Rails 4 mass-assignment protection behavior. This was [issue #89](https://github.com/
|
200
|
+
Version 2.1 introduced support to prevent default Rails 4 mass-assignment protection behavior. This was [issue #89](https://github.com/hashie/hashie/issues/89), resolved in [#104](https://github.com/hashie/hashie/pull/104). In version 2.2 this behavior has been removed in [#147](https://github.com/hashie/hashie/pull/147) in favor of a mixin and finally extracted into a separate gem in Hashie 3.0.
|
49
201
|
|
50
202
|
To enable 2.1 compatible behavior with Rails 4, use the [hashie_rails](http://rubygems.org/gems/hashie_rails) gem.
|
51
203
|
|
@@ -53,7 +205,7 @@ To enable 2.1 compatible behavior with Rails 4, use the [hashie_rails](http://ru
|
|
53
205
|
gem 'hashie_rails'
|
54
206
|
```
|
55
207
|
|
56
|
-
See [#154](https://github.com/
|
208
|
+
See [#154](https://github.com/hashie/hashie/pull/154) and [Mash and Rails 4 Strong Parameters](README.md#mash-and-rails-4-strong-parameters) for more details.
|
57
209
|
|
58
210
|
#### Key Conversions in Hashie::Dash and Hashie::Trash
|
59
211
|
|
@@ -81,7 +233,7 @@ p.inspect # => { 'name' => 'dB.' }
|
|
81
233
|
p.to_hash # => { 'name' => 'dB.' }
|
82
234
|
```
|
83
235
|
|
84
|
-
It was not possible to achieve the behavior of preserving keys, as described in [issue #151](https://github.com/
|
236
|
+
It was not possible to achieve the behavior of preserving keys, as described in [issue #151](https://github.com/hashie/hashie/issues/151).
|
85
237
|
|
86
238
|
Version 2.2 does not perform this conversion by default.
|
87
239
|
|
@@ -128,6 +280,4 @@ instance.to_hash # => { :first => 'First', "last" => 'Last' }
|
|
128
280
|
|
129
281
|
The behavior with `symbolize_keys` and `stringify_keys` is unchanged.
|
130
282
|
|
131
|
-
See [#152](https://github.com/
|
132
|
-
|
133
|
-
|
283
|
+
See [#152](https://github.com/hashie/hashie/pull/152) for more information.
|
data/hashie.gemspec
CHANGED
@@ -7,15 +7,22 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.email = ['michael@intridea.com', 'jollyjerry@gmail.com']
|
8
8
|
gem.description = 'Hashie is a collection of classes and mixins that make hashes more powerful.'
|
9
9
|
gem.summary = 'Your friendly neighborhood hash library.'
|
10
|
-
gem.homepage = 'https://github.com/
|
10
|
+
gem.homepage = 'https://github.com/hashie/hashie'
|
11
11
|
gem.license = 'MIT'
|
12
12
|
|
13
13
|
gem.require_paths = ['lib']
|
14
|
-
gem.files
|
15
|
-
gem.files
|
16
|
-
gem.files
|
17
|
-
gem.test_files = Dir['spec/**/*.rb']
|
14
|
+
gem.files = %w[.yardopts CHANGELOG.md CONTRIBUTING.md LICENSE README.md UPGRADING.md]
|
15
|
+
gem.files += %w[Rakefile hashie.gemspec]
|
16
|
+
gem.files += Dir['lib/**/*.rb']
|
18
17
|
|
19
|
-
gem.
|
20
|
-
|
18
|
+
if gem.respond_to?(:metadata)
|
19
|
+
gem.metadata = {
|
20
|
+
'bug_tracker_uri' => 'https://github.com/hashie/hashie/issues',
|
21
|
+
'changelog_uri' => 'https://github.com/hashie/hashie/blob/master/CHANGELOG.md',
|
22
|
+
'documentation_uri' => 'https://www.rubydoc.info/gems/hashie',
|
23
|
+
'source_code_uri' => 'https://github.com/hashie/hashie'
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
gem.add_development_dependency 'bundler'
|
21
28
|
end
|
data/lib/hashie/array.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'hashie/extensions/array/pretty_inspect'
|
2
|
+
require 'hashie/extensions/ruby_version_check'
|
3
|
+
|
4
|
+
module Hashie
|
5
|
+
class Array < ::Array
|
6
|
+
include Hashie::Extensions::Array::PrettyInspect
|
7
|
+
include Hashie::Extensions::RubyVersionCheck
|
8
|
+
with_minimum_ruby('2.3.0') do
|
9
|
+
def dig(*indexes)
|
10
|
+
converted_indexes = indexes.map do |idx|
|
11
|
+
begin
|
12
|
+
Integer(idx)
|
13
|
+
rescue ArgumentError
|
14
|
+
idx
|
15
|
+
end
|
16
|
+
end
|
17
|
+
super(*converted_indexes)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/hashie/clash.rb
CHANGED
@@ -54,7 +54,7 @@ module Hashie
|
|
54
54
|
case args.length
|
55
55
|
when 1
|
56
56
|
val = args.first
|
57
|
-
val = self[key].merge(val) if self[key].is_a?(::Hash) && val.is_a?(::Hash)
|
57
|
+
val = self.class.new(self[key]).merge(val) if self[key].is_a?(::Hash) && val.is_a?(::Hash)
|
58
58
|
else
|
59
59
|
val = args
|
60
60
|
end
|
@@ -64,22 +64,34 @@ module Hashie
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def method_missing(name, *args) #:nodoc:
|
67
|
-
|
68
|
-
if name.match(/!$/) && args.empty?
|
67
|
+
if args.empty? && name.to_s.end_with?('!')
|
69
68
|
key = name[0...-1].to_sym
|
70
69
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
70
|
+
case self[key]
|
71
|
+
when NilClass
|
72
|
+
self[key] = self.class.new({}, self)
|
73
|
+
when Clash
|
74
|
+
self[key]
|
75
|
+
when Hash
|
76
|
+
self[key] = self.class.new(self[key], self)
|
75
77
|
else
|
76
|
-
|
78
|
+
raise ChainError, 'Tried to chain into a non-hash key.'
|
77
79
|
end
|
78
|
-
|
79
|
-
self[key]
|
80
80
|
elsif args.any?
|
81
|
-
|
82
|
-
|
81
|
+
merge_store(name, *args)
|
82
|
+
else
|
83
|
+
super
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def respond_to_missing?(method_name, _include_private = false)
|
88
|
+
method_name = method_name.to_s
|
89
|
+
|
90
|
+
if method_name.end_with?('!')
|
91
|
+
key = method_name[0...-1].to_sym
|
92
|
+
[NilClass, Clash, Hash].include?(self[key].class)
|
93
|
+
else
|
94
|
+
true
|
83
95
|
end
|
84
96
|
end
|
85
97
|
end
|
data/lib/hashie/dash.rb
CHANGED
@@ -15,7 +15,7 @@ module Hashie
|
|
15
15
|
class Dash < Hash
|
16
16
|
include Hashie::Extensions::PrettyInspect
|
17
17
|
|
18
|
-
|
18
|
+
alias to_s inspect
|
19
19
|
|
20
20
|
# Defines a property on the Dash. Options are
|
21
21
|
# as follows:
|
@@ -42,30 +42,27 @@ module Hashie
|
|
42
42
|
defaults.delete property_name
|
43
43
|
end
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
property_assignment = "#{property_name}=".to_sym
|
48
|
-
define_method(property_assignment) { |value| self.[]=(property_name, value) }
|
49
|
-
end
|
45
|
+
define_getter_for(property_name)
|
46
|
+
define_setter_for(property_name)
|
50
47
|
|
51
|
-
if defined? @subclasses
|
52
|
-
@subclasses.each { |klass| klass.property(property_name, options) }
|
53
|
-
end
|
48
|
+
@subclasses.each { |klass| klass.property(property_name, options) } if defined? @subclasses
|
54
49
|
|
55
50
|
condition = options.delete(:required)
|
56
51
|
if condition
|
57
52
|
message = options.delete(:message) || "is required for #{name}."
|
58
53
|
required_properties[property_name] = { condition: condition, message: message }
|
59
|
-
|
60
|
-
|
54
|
+
elsif options.key?(:message)
|
55
|
+
raise ArgumentError, 'The :message option should be used with :required option.'
|
61
56
|
end
|
62
57
|
end
|
63
58
|
|
64
59
|
class << self
|
65
60
|
attr_reader :properties, :defaults
|
61
|
+
attr_reader :getters
|
66
62
|
attr_reader :required_properties
|
67
63
|
end
|
68
64
|
instance_variable_set('@properties', Set.new)
|
65
|
+
instance_variable_set('@getters', Set.new)
|
69
66
|
instance_variable_set('@defaults', {})
|
70
67
|
instance_variable_set('@required_properties', {})
|
71
68
|
|
@@ -73,6 +70,7 @@ module Hashie
|
|
73
70
|
super
|
74
71
|
(@subclasses ||= Set.new) << klass
|
75
72
|
klass.instance_variable_set('@properties', properties.dup)
|
73
|
+
klass.instance_variable_set('@getters', getters.dup)
|
76
74
|
klass.instance_variable_set('@defaults', defaults.dup)
|
77
75
|
klass.instance_variable_set('@required_properties', required_properties.dup)
|
78
76
|
end
|
@@ -89,26 +87,29 @@ module Hashie
|
|
89
87
|
required_properties.key? name
|
90
88
|
end
|
91
89
|
|
90
|
+
private_class_method def self.define_getter_for(property_name)
|
91
|
+
return if getters.include?(property_name)
|
92
|
+
define_method(property_name) { |&block| self.[](property_name, &block) }
|
93
|
+
getters << property_name
|
94
|
+
end
|
95
|
+
|
96
|
+
private_class_method def self.define_setter_for(property_name)
|
97
|
+
setter = :"#{property_name}="
|
98
|
+
return if instance_methods.include?(setter)
|
99
|
+
define_method(setter) { |value| self.[]=(property_name, value) }
|
100
|
+
end
|
101
|
+
|
92
102
|
# You may initialize a Dash with an attributes hash
|
93
103
|
# just like you would many other kinds of data objects.
|
94
104
|
def initialize(attributes = {}, &block)
|
95
105
|
super(&block)
|
96
106
|
|
97
|
-
self.class.defaults.each_pair do |prop, value|
|
98
|
-
self[prop] = begin
|
99
|
-
val = value.dup
|
100
|
-
val.is_a?(Proc) && val.arity > 0 ? val.call(self) : val
|
101
|
-
rescue TypeError
|
102
|
-
value
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
107
|
initialize_attributes(attributes)
|
107
108
|
assert_required_attributes_set!
|
108
109
|
end
|
109
110
|
|
110
|
-
|
111
|
-
|
111
|
+
alias _regular_reader []
|
112
|
+
alias _regular_writer []=
|
112
113
|
private :_regular_reader, :_regular_writer
|
113
114
|
|
114
115
|
# Retrieve a value from the Dash (will return the
|
@@ -155,25 +156,48 @@ module Hashie
|
|
155
156
|
self
|
156
157
|
end
|
157
158
|
|
159
|
+
def to_h
|
160
|
+
defaults = ::Hash[self.class.properties.map { |prop| [prop, self.class.defaults[prop]] }]
|
161
|
+
|
162
|
+
defaults.merge(self)
|
163
|
+
end
|
164
|
+
alias to_hash to_h
|
165
|
+
|
158
166
|
def update_attributes!(attributes)
|
159
|
-
|
167
|
+
update_attributes(attributes)
|
160
168
|
|
161
169
|
self.class.defaults.each_pair do |prop, value|
|
170
|
+
next unless fetch(prop, nil).nil?
|
162
171
|
self[prop] = begin
|
163
|
-
value.dup
|
172
|
+
val = value.dup
|
173
|
+
if val.is_a?(Proc)
|
174
|
+
val.arity == 1 ? val.call(self) : val.call
|
175
|
+
else
|
176
|
+
val
|
177
|
+
end
|
164
178
|
rescue TypeError
|
165
179
|
value
|
166
|
-
end
|
180
|
+
end
|
167
181
|
end
|
182
|
+
|
168
183
|
assert_required_attributes_set!
|
169
184
|
end
|
170
185
|
|
171
186
|
private
|
172
187
|
|
173
188
|
def initialize_attributes(attributes)
|
189
|
+
return unless attributes
|
190
|
+
|
191
|
+
cleaned_attributes = attributes.reject { |_attr, value| value.nil? }
|
192
|
+
update_attributes!(cleaned_attributes)
|
193
|
+
end
|
194
|
+
|
195
|
+
def update_attributes(attributes)
|
196
|
+
return unless attributes
|
197
|
+
|
174
198
|
attributes.each_pair do |att, value|
|
175
199
|
self[att] = value
|
176
|
-
end
|
200
|
+
end
|
177
201
|
end
|
178
202
|
|
179
203
|
def assert_property_exists!(property)
|
@@ -195,11 +219,12 @@ module Hashie
|
|
195
219
|
end
|
196
220
|
|
197
221
|
def fail_property_required_error!(property)
|
198
|
-
|
222
|
+
raise ArgumentError,
|
223
|
+
"The property '#{property}' #{self.class.required_properties[property][:message]}"
|
199
224
|
end
|
200
225
|
|
201
226
|
def fail_no_property_error!(property)
|
202
|
-
|
227
|
+
raise NoMethodError, "The property '#{property}' is not defined for #{self.class.name}."
|
203
228
|
end
|
204
229
|
|
205
230
|
def required?(property)
|
@@ -207,9 +232,9 @@ module Hashie
|
|
207
232
|
|
208
233
|
condition = self.class.required_properties[property][:condition]
|
209
234
|
case condition
|
210
|
-
when Proc then !!
|
211
|
-
when Symbol then !!
|
212
|
-
else !!
|
235
|
+
when Proc then !!instance_exec(&condition)
|
236
|
+
when Symbol then !!send(condition)
|
237
|
+
else !!condition
|
213
238
|
end
|
214
239
|
end
|
215
240
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Hashie
|
2
|
+
module Extensions
|
3
|
+
module Array
|
4
|
+
module PrettyInspect
|
5
|
+
def self.included(base)
|
6
|
+
base.send :alias_method, :array_inspect, :inspect
|
7
|
+
base.send :alias_method, :inspect, :hashie_inspect
|
8
|
+
end
|
9
|
+
|
10
|
+
def hashie_inspect
|
11
|
+
ret = "#<#{self.class} ["
|
12
|
+
ret << to_a.map(&:inspect).join(', ')
|
13
|
+
ret << ']>'
|
14
|
+
ret
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|