hashie 3.4.2 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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