hashie 3.4.2 → 5.0.0

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 (73) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +518 -122
  3. data/CONTRIBUTING.md +24 -7
  4. data/LICENSE +1 -1
  5. data/README.md +455 -48
  6. data/Rakefile +18 -1
  7. data/UPGRADING.md +157 -7
  8. data/hashie.gemspec +14 -7
  9. data/lib/hashie/array.rb +21 -0
  10. data/lib/hashie/clash.rb +24 -12
  11. data/lib/hashie/dash.rb +56 -31
  12. data/lib/hashie/extensions/active_support/core_ext/hash.rb +14 -0
  13. data/lib/hashie/extensions/array/pretty_inspect.rb +19 -0
  14. data/lib/hashie/extensions/coercion.rb +91 -52
  15. data/lib/hashie/extensions/dash/coercion.rb +25 -0
  16. data/lib/hashie/extensions/dash/indifferent_access.rb +30 -1
  17. data/lib/hashie/extensions/dash/predefined_values.rb +88 -0
  18. data/lib/hashie/extensions/dash/property_translation.rb +59 -30
  19. data/lib/hashie/extensions/deep_fetch.rb +5 -3
  20. data/lib/hashie/extensions/deep_find.rb +14 -5
  21. data/lib/hashie/extensions/deep_locate.rb +40 -21
  22. data/lib/hashie/extensions/deep_merge.rb +28 -10
  23. data/lib/hashie/extensions/ignore_undeclared.rb +6 -4
  24. data/lib/hashie/extensions/indifferent_access.rb +49 -8
  25. data/lib/hashie/extensions/key_conflict_warning.rb +55 -0
  26. data/lib/hashie/extensions/mash/define_accessors.rb +90 -0
  27. data/lib/hashie/extensions/mash/keep_original_keys.rb +53 -0
  28. data/lib/hashie/extensions/mash/permissive_respond_to.rb +61 -0
  29. data/lib/hashie/extensions/mash/safe_assignment.rb +3 -1
  30. data/lib/hashie/extensions/mash/symbolize_keys.rb +38 -0
  31. data/lib/hashie/extensions/method_access.rb +77 -19
  32. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +29 -5
  33. data/lib/hashie/extensions/ruby_version.rb +60 -0
  34. data/lib/hashie/extensions/ruby_version_check.rb +21 -0
  35. data/lib/hashie/extensions/strict_key_access.rb +77 -0
  36. data/lib/hashie/extensions/stringify_keys.rb +8 -5
  37. data/lib/hashie/extensions/symbolize_keys.rb +21 -7
  38. data/lib/hashie/hash.rb +18 -11
  39. data/lib/hashie/logger.rb +18 -0
  40. data/lib/hashie/mash.rb +196 -55
  41. data/lib/hashie/railtie.rb +21 -0
  42. data/lib/hashie/rash.rb +7 -7
  43. data/lib/hashie/utils.rb +44 -0
  44. data/lib/hashie/version.rb +1 -1
  45. data/lib/hashie.rb +34 -16
  46. metadata +30 -79
  47. data/spec/hashie/clash_spec.rb +0 -48
  48. data/spec/hashie/dash_spec.rb +0 -513
  49. data/spec/hashie/extensions/autoload_spec.rb +0 -24
  50. data/spec/hashie/extensions/coercion_spec.rb +0 -625
  51. data/spec/hashie/extensions/dash/indifferent_access_spec.rb +0 -84
  52. data/spec/hashie/extensions/deep_fetch_spec.rb +0 -97
  53. data/spec/hashie/extensions/deep_find_spec.rb +0 -45
  54. data/spec/hashie/extensions/deep_locate_spec.rb +0 -124
  55. data/spec/hashie/extensions/deep_merge_spec.rb +0 -45
  56. data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -46
  57. data/spec/hashie/extensions/indifferent_access_spec.rb +0 -219
  58. data/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb +0 -208
  59. data/spec/hashie/extensions/key_conversion_spec.rb +0 -12
  60. data/spec/hashie/extensions/mash/safe_assignment_spec.rb +0 -23
  61. data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
  62. data/spec/hashie/extensions/method_access_spec.rb +0 -184
  63. data/spec/hashie/extensions/stringify_keys_spec.rb +0 -101
  64. data/spec/hashie/extensions/symbolize_keys_spec.rb +0 -106
  65. data/spec/hashie/hash_spec.rb +0 -84
  66. data/spec/hashie/mash_spec.rb +0 -683
  67. data/spec/hashie/parsers/yaml_erb_parser_spec.rb +0 -29
  68. data/spec/hashie/rash_spec.rb +0 -77
  69. data/spec/hashie/trash_spec.rb +0 -268
  70. data/spec/hashie/version_spec.rb +0 -7
  71. data/spec/spec_helper.rb +0 -15
  72. data/spec/support/module_context.rb +0 -11
  73. 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
- task default: [:rubocop, :spec]
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/intridea/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.
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/intridea/hashie/issues/89), resolved in [#104](https://github.com/intridea/hashie/pull/104). In version 2.2 this behavior has been removed in [#147](https://github.com/intridea/hashie/pull/147) in favor of a mixin and finally extracted into a separate gem in Hashie 3.0.
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/intridea/hashie/pull/154) and [Mash and Rails 4 Strong Parameters](README.md#mash-and-rails-4-strong-parameters) for more details.
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/intridea/hashie/issues/151).
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/intridea/hashie/pull/152) for more information.
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/intridea/hashie'
10
+ gem.homepage = 'https://github.com/hashie/hashie'
11
11
  gem.license = 'MIT'
12
12
 
13
13
  gem.require_paths = ['lib']
14
- gem.files = %w(.yardopts CHANGELOG.md CONTRIBUTING.md LICENSE README.md UPGRADING.md Rakefile hashie.gemspec)
15
- gem.files += Dir['lib/**/*.rb']
16
- gem.files += Dir['spec/**/*.rb']
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.add_development_dependency 'rake'
20
- gem.add_development_dependency 'rspec', '~> 3.0'
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
@@ -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
- name = name.to_s
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
- if self[key].nil?
72
- self[key] = Clash.new({}, self)
73
- elsif self[key].is_a?(::Hash) && !self[key].is_a?(Clash)
74
- self[key] = Clash.new(self[key], self)
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
- fail ChainError, 'Tried to chain into a non-hash key.'
78
+ raise ChainError, 'Tried to chain into a non-hash key.'
77
79
  end
78
-
79
- self[key]
80
80
  elsif args.any?
81
- key = name.to_sym
82
- merge_store(key, *args)
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
- alias_method :to_s, :inspect
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
- unless instance_methods.map(&:to_s).include?("#{property_name}=")
46
- define_method(property_name) { |&block| self.[](property_name, &block) }
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
- else
60
- fail ArgumentError, 'The :message option should be used with :required option.' if options.key?(:message)
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
- alias_method :_regular_reader, :[]
111
- alias_method :_regular_writer, :[]=
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
- initialize_attributes(attributes)
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 if self[prop].nil?
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 if attributes
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
- fail ArgumentError, "The property '#{property}' #{self.class.required_properties[property][:message]}"
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
- fail NoMethodError, "The property '#{property}' is not defined for #{self.class.name}."
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 !!(instance_exec(&condition))
211
- when Symbol then !!(send(condition))
212
- else !!(condition)
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,14 @@
1
+ module Hashie
2
+ module Extensions
3
+ module ActiveSupport
4
+ module CoreExt
5
+ module Hash
6
+ def except(*keys)
7
+ string_keys = keys.map { |key| convert_key(key) }
8
+ super(*string_keys)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ 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