hashie 3.6.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +228 -174
  3. data/CONTRIBUTING.md +13 -6
  4. data/README.md +127 -15
  5. data/UPGRADING.md +83 -7
  6. data/hashie.gemspec +13 -7
  7. data/lib/hashie.rb +21 -19
  8. data/lib/hashie/dash.rb +2 -1
  9. data/lib/hashie/extensions/active_support/core_ext/hash.rb +14 -0
  10. data/lib/hashie/extensions/coercion.rb +23 -16
  11. data/lib/hashie/extensions/dash/indifferent_access.rb +20 -1
  12. data/lib/hashie/extensions/dash/property_translation.rb +6 -3
  13. data/lib/hashie/extensions/deep_fetch.rb +4 -2
  14. data/lib/hashie/extensions/deep_find.rb +12 -3
  15. data/lib/hashie/extensions/deep_locate.rb +22 -7
  16. data/lib/hashie/extensions/deep_merge.rb +18 -1
  17. data/lib/hashie/extensions/indifferent_access.rb +1 -3
  18. data/lib/hashie/extensions/key_conflict_warning.rb +55 -0
  19. data/lib/hashie/extensions/mash/define_accessors.rb +90 -0
  20. data/lib/hashie/extensions/mash/keep_original_keys.rb +2 -1
  21. data/lib/hashie/extensions/mash/permissive_respond_to.rb +61 -0
  22. data/lib/hashie/extensions/mash/safe_assignment.rb +3 -1
  23. data/lib/hashie/extensions/method_access.rb +5 -2
  24. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +26 -4
  25. data/lib/hashie/extensions/ruby_version_check.rb +5 -1
  26. data/lib/hashie/extensions/strict_key_access.rb +8 -4
  27. data/lib/hashie/hash.rb +16 -9
  28. data/lib/hashie/mash.rb +114 -53
  29. data/lib/hashie/railtie.rb +7 -0
  30. data/lib/hashie/rash.rb +1 -1
  31. data/lib/hashie/utils.rb +28 -0
  32. data/lib/hashie/version.rb +1 -1
  33. metadata +19 -130
  34. data/spec/hashie/array_spec.rb +0 -29
  35. data/spec/hashie/clash_spec.rb +0 -70
  36. data/spec/hashie/dash_spec.rb +0 -598
  37. data/spec/hashie/extensions/autoload_spec.rb +0 -24
  38. data/spec/hashie/extensions/coercion_spec.rb +0 -639
  39. data/spec/hashie/extensions/dash/coercion_spec.rb +0 -13
  40. data/spec/hashie/extensions/dash/indifferent_access_spec.rb +0 -84
  41. data/spec/hashie/extensions/deep_fetch_spec.rb +0 -97
  42. data/spec/hashie/extensions/deep_find_spec.rb +0 -138
  43. data/spec/hashie/extensions/deep_locate_spec.rb +0 -137
  44. data/spec/hashie/extensions/deep_merge_spec.rb +0 -70
  45. data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -47
  46. data/spec/hashie/extensions/indifferent_access_spec.rb +0 -295
  47. data/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb +0 -208
  48. data/spec/hashie/extensions/key_conversion_spec.rb +0 -12
  49. data/spec/hashie/extensions/mash/keep_original_keys_spec.rb +0 -46
  50. data/spec/hashie/extensions/mash/safe_assignment_spec.rb +0 -50
  51. data/spec/hashie/extensions/mash/symbolize_keys_spec.rb +0 -39
  52. data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
  53. data/spec/hashie/extensions/method_access_spec.rb +0 -226
  54. data/spec/hashie/extensions/strict_key_access_spec.rb +0 -110
  55. data/spec/hashie/extensions/stringify_keys_spec.rb +0 -124
  56. data/spec/hashie/extensions/symbolize_keys_spec.rb +0 -129
  57. data/spec/hashie/hash_spec.rb +0 -84
  58. data/spec/hashie/mash_spec.rb +0 -771
  59. data/spec/hashie/parsers/yaml_erb_parser_spec.rb +0 -46
  60. data/spec/hashie/rash_spec.rb +0 -83
  61. data/spec/hashie/trash_spec.rb +0 -328
  62. data/spec/hashie/utils_spec.rb +0 -25
  63. data/spec/hashie/version_spec.rb +0 -7
  64. data/spec/hashie_spec.rb +0 -13
  65. data/spec/integration/elasticsearch/integration_spec.rb +0 -40
  66. data/spec/integration/omniauth-oauth2/app.rb +0 -52
  67. data/spec/integration/omniauth-oauth2/integration_spec.rb +0 -26
  68. data/spec/integration/omniauth-oauth2/some_site.rb +0 -38
  69. data/spec/integration/omniauth/app.rb +0 -11
  70. data/spec/integration/omniauth/integration_spec.rb +0 -38
  71. data/spec/integration/rails-without-dependency/integration_spec.rb +0 -15
  72. data/spec/integration/rails/app.rb +0 -47
  73. data/spec/integration/rails/integration_spec.rb +0 -26
  74. data/spec/spec_helper.rb +0 -23
  75. data/spec/support/integration_specs.rb +0 -36
  76. data/spec/support/logger.rb +0 -24
  77. data/spec/support/module_context.rb +0 -11
  78. data/spec/support/ruby_version_check.rb +0 -6
@@ -7,6 +7,13 @@ begin
7
7
  initializer 'hashie.configure_logger', after: 'initialize_logger' do
8
8
  Hashie.logger = Rails.logger
9
9
  end
10
+
11
+ initializer 'hashie.patch_hash_except', after: 'load_active_support' do
12
+ if Rails::VERSION::MAJOR >= 6
13
+ require 'hashie/extensions/active_support/core_ext/hash'
14
+ Hashie::Mash.send(:include, Hashie::Extensions::ActiveSupport::CoreExt::Hash)
15
+ end
16
+ end
10
17
  end
11
18
  end
12
19
  rescue LoadError => e
data/lib/hashie/rash.rb CHANGED
@@ -117,7 +117,7 @@ module Hashie
117
117
  end
118
118
 
119
119
  when Regexp
120
- # Reverse operation: `rash[/regexp/]` returns all the hash's string keys which match the regexp
120
+ # Reverse operation: `rash[/regexp/]` returns all string keys matching the regexp
121
121
  @hash.each do |key, val|
122
122
  yield val if key.is_a?(String) && query =~ key
123
123
  end
data/lib/hashie/utils.rb CHANGED
@@ -12,5 +12,33 @@ module Hashie
12
12
  "defined in #{bound_method.owner}"
13
13
  end
14
14
  end
15
+
16
+ # Duplicates a value or returns the value when it is not duplicable
17
+ #
18
+ # @api public
19
+ #
20
+ # @param value [Object] the value to safely duplicate
21
+ # @return [Object] the duplicated value
22
+ def self.safe_dup(value)
23
+ case value
24
+ when Complex, FalseClass, NilClass, Rational, Method, Symbol, TrueClass, *integer_classes
25
+ value
26
+ else
27
+ value.dup
28
+ end
29
+ end
30
+
31
+ # Lists the classes Ruby uses for integers
32
+ #
33
+ # @api private
34
+ # @return [Array<Class>]
35
+ def self.integer_classes
36
+ @integer_classes ||=
37
+ if 0.class == Integer
38
+ [Integer]
39
+ else
40
+ [Fixnum, Bignum] # rubocop:disable Lint/UnifiedInteger
41
+ end
42
+ end
15
43
  end
16
44
  end
@@ -1,3 +1,3 @@
1
1
  module Hashie
2
- VERSION = '3.6.0'.freeze
2
+ VERSION = '4.1.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hashie
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.0
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
@@ -9,50 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-08-13 00:00:00.000000000 Z
12
+ date: 2020-02-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: rake
15
+ name: bundler
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - "<"
18
+ - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '11'
20
+ version: '0'
21
21
  type: :development
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - "<"
25
+ - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: '11'
28
- - !ruby/object:Gem::Dependency
29
- name: rspec
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - "~>"
33
- - !ruby/object:Gem::Version
34
- version: '3.0'
35
- type: :development
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "~>"
40
- - !ruby/object:Gem::Version
41
- version: '3.0'
42
- - !ruby/object:Gem::Dependency
43
- name: rspec-pending_for
44
- requirement: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - "~>"
47
- - !ruby/object:Gem::Version
48
- version: '0.1'
49
- type: :development
50
- prerelease: false
51
- version_requirements: !ruby/object:Gem::Requirement
52
- requirements:
53
- - - "~>"
54
- - !ruby/object:Gem::Version
55
- version: '0.1'
27
+ version: '0'
56
28
  description: Hashie is a collection of classes and mixins that make hashes more powerful.
57
29
  email:
58
30
  - michael@intridea.com
@@ -73,6 +45,7 @@ files:
73
45
  - lib/hashie/array.rb
74
46
  - lib/hashie/clash.rb
75
47
  - lib/hashie/dash.rb
48
+ - lib/hashie/extensions/active_support/core_ext/hash.rb
76
49
  - lib/hashie/extensions/array/pretty_inspect.rb
77
50
  - lib/hashie/extensions/coercion.rb
78
51
  - lib/hashie/extensions/dash/coercion.rb
@@ -84,8 +57,11 @@ files:
84
57
  - lib/hashie/extensions/deep_merge.rb
85
58
  - lib/hashie/extensions/ignore_undeclared.rb
86
59
  - lib/hashie/extensions/indifferent_access.rb
60
+ - lib/hashie/extensions/key_conflict_warning.rb
87
61
  - lib/hashie/extensions/key_conversion.rb
62
+ - lib/hashie/extensions/mash/define_accessors.rb
88
63
  - lib/hashie/extensions/mash/keep_original_keys.rb
64
+ - lib/hashie/extensions/mash/permissive_respond_to.rb
89
65
  - lib/hashie/extensions/mash/safe_assignment.rb
90
66
  - lib/hashie/extensions/mash/symbolize_keys.rb
91
67
  - lib/hashie/extensions/merge_initializer.rb
@@ -105,55 +81,14 @@ files:
105
81
  - lib/hashie/trash.rb
106
82
  - lib/hashie/utils.rb
107
83
  - lib/hashie/version.rb
108
- - spec/hashie/array_spec.rb
109
- - spec/hashie/clash_spec.rb
110
- - spec/hashie/dash_spec.rb
111
- - spec/hashie/extensions/autoload_spec.rb
112
- - spec/hashie/extensions/coercion_spec.rb
113
- - spec/hashie/extensions/dash/coercion_spec.rb
114
- - spec/hashie/extensions/dash/indifferent_access_spec.rb
115
- - spec/hashie/extensions/deep_fetch_spec.rb
116
- - spec/hashie/extensions/deep_find_spec.rb
117
- - spec/hashie/extensions/deep_locate_spec.rb
118
- - spec/hashie/extensions/deep_merge_spec.rb
119
- - spec/hashie/extensions/ignore_undeclared_spec.rb
120
- - spec/hashie/extensions/indifferent_access_spec.rb
121
- - spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb
122
- - spec/hashie/extensions/key_conversion_spec.rb
123
- - spec/hashie/extensions/mash/keep_original_keys_spec.rb
124
- - spec/hashie/extensions/mash/safe_assignment_spec.rb
125
- - spec/hashie/extensions/mash/symbolize_keys_spec.rb
126
- - spec/hashie/extensions/merge_initializer_spec.rb
127
- - spec/hashie/extensions/method_access_spec.rb
128
- - spec/hashie/extensions/strict_key_access_spec.rb
129
- - spec/hashie/extensions/stringify_keys_spec.rb
130
- - spec/hashie/extensions/symbolize_keys_spec.rb
131
- - spec/hashie/hash_spec.rb
132
- - spec/hashie/mash_spec.rb
133
- - spec/hashie/parsers/yaml_erb_parser_spec.rb
134
- - spec/hashie/rash_spec.rb
135
- - spec/hashie/trash_spec.rb
136
- - spec/hashie/utils_spec.rb
137
- - spec/hashie/version_spec.rb
138
- - spec/hashie_spec.rb
139
- - spec/integration/elasticsearch/integration_spec.rb
140
- - spec/integration/omniauth-oauth2/app.rb
141
- - spec/integration/omniauth-oauth2/integration_spec.rb
142
- - spec/integration/omniauth-oauth2/some_site.rb
143
- - spec/integration/omniauth/app.rb
144
- - spec/integration/omniauth/integration_spec.rb
145
- - spec/integration/rails-without-dependency/integration_spec.rb
146
- - spec/integration/rails/app.rb
147
- - spec/integration/rails/integration_spec.rb
148
- - spec/spec_helper.rb
149
- - spec/support/integration_specs.rb
150
- - spec/support/logger.rb
151
- - spec/support/module_context.rb
152
- - spec/support/ruby_version_check.rb
153
- homepage: https://github.com/intridea/hashie
84
+ homepage: https://github.com/hashie/hashie
154
85
  licenses:
155
86
  - MIT
156
- metadata: {}
87
+ metadata:
88
+ bug_tracker_uri: https://github.com/hashie/hashie/issues
89
+ changelog_uri: https://github.com/hashie/hashie/blob/master/CHANGELOG.md
90
+ documentation_uri: https://www.rubydoc.info/gems/hashie
91
+ source_code_uri: https://github.com/hashie/hashie
157
92
  post_install_message:
158
93
  rdoc_options: []
159
94
  require_paths:
@@ -169,54 +104,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
104
  - !ruby/object:Gem::Version
170
105
  version: '0'
171
106
  requirements: []
172
- rubyforge_project:
173
- rubygems_version: 2.7.6
107
+ rubygems_version: 3.0.3
174
108
  signing_key:
175
109
  specification_version: 4
176
110
  summary: Your friendly neighborhood hash library.
177
- test_files:
178
- - spec/hashie/parsers/yaml_erb_parser_spec.rb
179
- - spec/hashie/array_spec.rb
180
- - spec/hashie/hash_spec.rb
181
- - spec/hashie/version_spec.rb
182
- - spec/hashie/clash_spec.rb
183
- - spec/hashie/mash_spec.rb
184
- - spec/hashie/extensions/stringify_keys_spec.rb
185
- - spec/hashie/extensions/deep_locate_spec.rb
186
- - spec/hashie/extensions/coercion_spec.rb
187
- - spec/hashie/extensions/mash/safe_assignment_spec.rb
188
- - spec/hashie/extensions/mash/symbolize_keys_spec.rb
189
- - spec/hashie/extensions/mash/keep_original_keys_spec.rb
190
- - spec/hashie/extensions/key_conversion_spec.rb
191
- - spec/hashie/extensions/ignore_undeclared_spec.rb
192
- - spec/hashie/extensions/autoload_spec.rb
193
- - spec/hashie/extensions/strict_key_access_spec.rb
194
- - spec/hashie/extensions/deep_fetch_spec.rb
195
- - spec/hashie/extensions/method_access_spec.rb
196
- - spec/hashie/extensions/deep_find_spec.rb
197
- - spec/hashie/extensions/deep_merge_spec.rb
198
- - spec/hashie/extensions/merge_initializer_spec.rb
199
- - spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb
200
- - spec/hashie/extensions/dash/coercion_spec.rb
201
- - spec/hashie/extensions/dash/indifferent_access_spec.rb
202
- - spec/hashie/extensions/symbolize_keys_spec.rb
203
- - spec/hashie/extensions/indifferent_access_spec.rb
204
- - spec/hashie/rash_spec.rb
205
- - spec/hashie/utils_spec.rb
206
- - spec/hashie/dash_spec.rb
207
- - spec/hashie/trash_spec.rb
208
- - spec/spec_helper.rb
209
- - spec/integration/rails-without-dependency/integration_spec.rb
210
- - spec/integration/omniauth-oauth2/integration_spec.rb
211
- - spec/integration/omniauth-oauth2/app.rb
212
- - spec/integration/omniauth-oauth2/some_site.rb
213
- - spec/integration/omniauth/integration_spec.rb
214
- - spec/integration/omniauth/app.rb
215
- - spec/integration/elasticsearch/integration_spec.rb
216
- - spec/integration/rails/integration_spec.rb
217
- - spec/integration/rails/app.rb
218
- - spec/support/module_context.rb
219
- - spec/support/logger.rb
220
- - spec/support/integration_specs.rb
221
- - spec/support/ruby_version_check.rb
222
- - spec/hashie_spec.rb
111
+ test_files: []
@@ -1,29 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Array do
4
- with_minimum_ruby('2.3.0') do
5
- describe '#dig' do
6
- let(:array) { Hashie::Array.new(%i[a b c]) }
7
-
8
- it 'works with a string index' do
9
- expect(array.dig('0')).to eq(:a)
10
- end
11
-
12
- it 'works with a numeric index' do
13
- expect(array.dig(1)).to eq(:b)
14
- end
15
-
16
- context 'when array is empty' do
17
- let(:array) { Hashie::Array.new([]) }
18
-
19
- it 'works with a first numeric and next string index' do
20
- expect(array.dig(0, 'hello')).to eq(nil)
21
- end
22
-
23
- it 'throws an error with first string and next numeric index' do
24
- expect { array.dig('hello', 0) }.to raise_error(TypeError)
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,70 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Hashie::Clash do
4
- it 'is able to set an attribute via method_missing' do
5
- subject.foo('bar')
6
- expect(subject[:foo]).to eq 'bar'
7
- end
8
-
9
- it 'is able to set multiple attributes' do
10
- subject.foo('bar').baz('wok')
11
- expect(subject).to eq(foo: 'bar', baz: 'wok')
12
- end
13
-
14
- it 'converts multiple arguments into an array' do
15
- subject.foo(1, 2, 3)
16
- expect(subject[:foo]).to eq [1, 2, 3]
17
- end
18
-
19
- it 'is able to use bang notation to create a new Clash on a key' do
20
- subject.foo!
21
- expect(subject[:foo]).to be_kind_of(Hashie::Clash)
22
- end
23
-
24
- it 'is able to chain onto the new Clash when using bang notation' do
25
- subject.foo!.bar('abc').baz(123)
26
- expect(subject).to eq(foo: { bar: 'abc', baz: 123 })
27
- end
28
-
29
- it 'is able to jump back up to the parent in the chain with #_end!' do
30
- subject.foo!.bar('abc')._end!.baz(123)
31
- expect(subject).to eq(foo: { bar: 'abc' }, baz: 123)
32
- end
33
-
34
- it 'merges rather than replaces existing keys' do
35
- subject.where(abc: 'def').where(hgi: 123)
36
- expect(subject).to eq(where: { abc: 'def', hgi: 123 })
37
- end
38
-
39
- it 'is able to replace all of its own keys with #replace' do
40
- subject.foo(:bar).hello(:world)
41
- expect(subject.replace(baz: 123, hgi: 123)).to eq(baz: 123, hgi: 123)
42
- expect(subject).to eq(baz: 123, hgi: 123)
43
- expect(subject[:foo]).to be_nil
44
- expect(subject[:hello]).to be_nil
45
- end
46
-
47
- it 'merges multiple bang notation calls' do
48
- subject.where!.foo(123)
49
- subject.where!.bar(321)
50
- expect(subject).to eq(where: { foo: 123, bar: 321 })
51
- end
52
-
53
- it 'raises an exception when method is missing' do
54
- expect { subject.boo }.to raise_error(NoMethodError)
55
- end
56
-
57
- describe 'when inherited' do
58
- subject { Class.new(described_class).new }
59
-
60
- it 'bang nodes are instances of a subclass' do
61
- subject.where!.foo(123)
62
- expect(subject[:where]).to be_instance_of(subject.class)
63
- end
64
-
65
- it 'merged nodes are instances of a subclass' do
66
- subject.where(abc: 'def').where(hgi: 123)
67
- expect(subject[:where]).to be_instance_of(subject.class)
68
- end
69
- end
70
- end
@@ -1,598 +0,0 @@
1
- require 'spec_helper'
2
-
3
- Hashie::Hash.class_eval do
4
- def self.inherited(klass)
5
- klass.instance_variable_set('@inheritance_test', true)
6
- end
7
- end
8
-
9
- class DashTest < Hashie::Dash
10
- property :first_name, required: true
11
- property :email
12
- property :count, default: 0
13
- end
14
-
15
- class DashTestDefaultProc < Hashie::Dash
16
- property :fields, default: -> { [] }
17
- end
18
-
19
- class DashNoRequiredTest < Hashie::Dash
20
- property :first_name
21
- property :email
22
- property :count, default: 0
23
- end
24
-
25
- class DashWithCoercion < Hashie::Dash
26
- include Hashie::Extensions::Coercion
27
- property :person
28
- property :city
29
-
30
- coerce_key :person, ::DashNoRequiredTest
31
- end
32
-
33
- class PropertyBangTest < Hashie::Dash
34
- property :important!
35
- end
36
-
37
- class SubclassedTest < DashTest
38
- property :last_name, required: true
39
- end
40
-
41
- class RequiredMessageTest < DashTest
42
- property :first_name, required: true, message: 'must be set.'
43
- end
44
-
45
- class DashDefaultTest < Hashie::Dash
46
- property :aliases, default: ['Snake']
47
- end
48
-
49
- class DeferredTest < Hashie::Dash
50
- property :created_at, default: proc { Time.now }
51
- end
52
-
53
- class DeferredWithSelfTest < Hashie::Dash
54
- property :created_at, default: -> { Time.now }
55
- property :updated_at, default: ->(test) { test.created_at }
56
- end
57
-
58
- describe DashTestDefaultProc do
59
- it 'as_json behaves correctly with default proc' do
60
- object = described_class.new
61
- expect(object.as_json).to be == { 'fields' => [] }
62
- end
63
- end
64
-
65
- describe DashTest do
66
- def property_required_error(property)
67
- [ArgumentError, "The property '#{property}' is required for #{subject.class.name}."]
68
- end
69
-
70
- def property_required_custom_error(property)
71
- [ArgumentError, "The property '#{property}' must be set."]
72
- end
73
-
74
- def property_message_without_required_error
75
- [ArgumentError, 'The :message option should be used with :required option.']
76
- end
77
-
78
- def no_property_error(property)
79
- [NoMethodError, "The property '#{property}' is not defined for #{subject.class.name}."]
80
- end
81
-
82
- subject { DashTest.new(first_name: 'Bob', email: 'bob@example.com') }
83
- let(:required_message) { RequiredMessageTest.new(first_name: 'Bob') }
84
-
85
- it('subclasses Hashie::Hash') { should respond_to(:to_mash) }
86
-
87
- describe '#to_s' do
88
- subject { super().to_s }
89
- it { should eq '#<DashTest count=0 email="bob@example.com" first_name="Bob">' }
90
- end
91
-
92
- it 'lists all set properties in inspect' do
93
- subject.first_name = 'Bob'
94
- subject.email = 'bob@example.com'
95
- expect(subject.inspect).to eq '#<DashTest count=0 email="bob@example.com" first_name="Bob">'
96
- end
97
-
98
- describe '#count' do
99
- subject { super().count }
100
- it { should be_zero }
101
- end
102
-
103
- it { should respond_to(:first_name) }
104
- it { should respond_to(:first_name=) }
105
- it { should_not respond_to(:nonexistent) }
106
-
107
- it 'errors out for a non-existent property' do
108
- expect { subject['nonexistent'] }.to raise_error(*no_property_error('nonexistent'))
109
- end
110
-
111
- it 'errors out when attempting to set a required property to nil' do
112
- expect { subject.first_name = nil }.to raise_error(*property_required_error('first_name'))
113
- end
114
-
115
- it 'errors out when message added to not required property' do
116
- expect do
117
- class DashMessageOptionWithoutRequiredTest < Hashie::Dash
118
- property :first_name, message: 'is required.'
119
- end
120
- end.to raise_error(*property_message_without_required_error)
121
-
122
- expect do
123
- class DashMessageOptionWithoutRequiredTest < Hashie::Dash
124
- property :first_name, required: false, message: 'is required.'
125
- end
126
- end.to raise_error(*property_message_without_required_error)
127
- end
128
-
129
- context 'writing to properties' do
130
- it 'fails writing a required property to nil' do
131
- expect { subject.first_name = nil }.to raise_error(*property_required_error('first_name'))
132
- expect { required_message.first_name = nil }.to raise_error(*property_required_custom_error('first_name'))
133
- end
134
-
135
- it 'fails writing a required property to nil using []=' do
136
- expect { subject[:first_name] = nil }.to raise_error(*property_required_error('first_name'))
137
- expect { required_message[:first_name] = nil }.to raise_error(*property_required_custom_error('first_name'))
138
- end
139
-
140
- it 'fails writing to a non-existent property using []=' do
141
- expect { subject['nonexistent'] = 123 }.to raise_error(*no_property_error('nonexistent'))
142
- end
143
-
144
- it 'works for an existing property using []=' do
145
- subject[:first_name] = 'Bob'
146
- expect(subject[:first_name]).to eq 'Bob'
147
- expect { subject['first_name'] }.to raise_error(*no_property_error('first_name'))
148
- end
149
-
150
- it 'works for an existing property using a method call' do
151
- subject.first_name = 'Franklin'
152
- expect(subject.first_name).to eq 'Franklin'
153
- end
154
- end
155
-
156
- context 'reading from properties' do
157
- it 'fails reading from a non-existent property using []' do
158
- expect { subject['nonexistent'] }.to raise_error(*no_property_error('nonexistent'))
159
- end
160
-
161
- it 'is able to retrieve properties through blocks' do
162
- subject[:first_name] = 'Aiden'
163
- value = nil
164
- subject.[](:first_name) { |v| value = v }
165
- expect(value).to eq 'Aiden'
166
- end
167
-
168
- it 'is able to retrieve properties through blocks with method calls' do
169
- subject[:first_name] = 'Frodo'
170
- value = nil
171
- subject.first_name { |v| value = v }
172
- expect(value).to eq 'Frodo'
173
- end
174
- end
175
-
176
- context 'reading from deferred properties' do
177
- it 'evaluates proc after initial read' do
178
- expect(DeferredTest.new[:created_at]).to be_instance_of(Time)
179
- end
180
-
181
- it 'does not evalute proc after subsequent reads' do
182
- deferred = DeferredTest.new
183
- expect(deferred[:created_at].object_id).to eq deferred[:created_at].object_id
184
- end
185
- end
186
-
187
- context 'reading from a deferred property based on context' do
188
- it 'provides the current hash as context for evaluation' do
189
- deferred = DeferredWithSelfTest.new
190
- expect(deferred[:created_at].object_id).to eq deferred[:created_at].object_id
191
- expect(deferred[:updated_at].object_id).to eq deferred[:created_at].object_id
192
- end
193
- end
194
-
195
- context 'converting from a Mash' do
196
- class ConvertingFromMash < Hashie::Dash
197
- property :property, required: true
198
- end
199
-
200
- context 'without keeping the original keys' do
201
- let(:mash) { Hashie::Mash.new(property: 'test') }
202
-
203
- it 'does not pick up the property from the stringified key' do
204
- expect { ConvertingFromMash.new(mash) }.to raise_error(NoMethodError)
205
- end
206
- end
207
-
208
- context 'when keeping the original keys' do
209
- class KeepingMash < Hashie::Mash
210
- include Hashie::Extensions::Mash::KeepOriginalKeys
211
- end
212
-
213
- let(:mash) { KeepingMash.new(property: 'test') }
214
-
215
- it 'picks up the property from the original key' do
216
- expect { ConvertingFromMash.new(mash) }.not_to raise_error
217
- end
218
- end
219
- end
220
-
221
- describe '#new' do
222
- it 'fails with non-existent properties' do
223
- expect { described_class.new(bork: '') }.to raise_error(*no_property_error('bork'))
224
- end
225
-
226
- it 'sets properties that it is able to' do
227
- obj = described_class.new first_name: 'Michael'
228
- expect(obj.first_name).to eq 'Michael'
229
- end
230
-
231
- it 'accepts nil' do
232
- expect { DashNoRequiredTest.new(nil) }.not_to raise_error
233
- end
234
-
235
- it 'accepts block to define a global default' do
236
- obj = described_class.new { |_, key| key.to_s.upcase }
237
- expect(obj.first_name).to eq 'FIRST_NAME'
238
- expect(obj.count).to be_zero
239
- end
240
-
241
- it 'fails when required values are missing' do
242
- expect { DashTest.new }.to raise_error(*property_required_error('first_name'))
243
- end
244
-
245
- it 'does not overwrite default values' do
246
- obj1 = DashDefaultTest.new
247
- obj1.aliases << 'El Rey'
248
- obj2 = DashDefaultTest.new
249
- expect(obj2.aliases).not_to include 'El Rey'
250
- end
251
- end
252
-
253
- describe '#merge' do
254
- it 'creates a new instance of the Dash' do
255
- new_dash = subject.merge(first_name: 'Robert')
256
- expect(subject.object_id).not_to eq new_dash.object_id
257
- end
258
-
259
- it 'merges the given hash' do
260
- new_dash = subject.merge(first_name: 'Robert', email: 'robert@example.com')
261
- expect(new_dash.first_name).to eq 'Robert'
262
- expect(new_dash.email).to eq 'robert@example.com'
263
- end
264
-
265
- it 'fails with non-existent properties' do
266
- expect { subject.merge(middle_name: 'James') }.to raise_error(*no_property_error('middle_name'))
267
- end
268
-
269
- it 'errors out when attempting to set a required property to nil' do
270
- expect { subject.merge(first_name: nil) }.to raise_error(*property_required_error('first_name'))
271
- end
272
-
273
- context 'given a block' do
274
- it "sets merged key's values to the block's return value" do
275
- expect(subject.merge(first_name: 'Jim') do |key, oldval, newval|
276
- "#{key}: #{newval} #{oldval}"
277
- end.first_name).to eq 'first_name: Jim Bob'
278
- end
279
- end
280
- end
281
-
282
- describe '#merge!' do
283
- it 'modifies the existing instance of the Dash' do
284
- original_dash = subject.merge!(first_name: 'Robert')
285
- expect(subject.object_id).to eq original_dash.object_id
286
- end
287
-
288
- it 'merges the given hash' do
289
- subject.merge!(first_name: 'Robert', email: 'robert@example.com')
290
- expect(subject.first_name).to eq 'Robert'
291
- expect(subject.email).to eq 'robert@example.com'
292
- end
293
-
294
- it 'fails with non-existent properties' do
295
- expect { subject.merge!(middle_name: 'James') }.to raise_error(NoMethodError)
296
- end
297
-
298
- it 'errors out when attempting to set a required property to nil' do
299
- expect { subject.merge!(first_name: nil) }.to raise_error(ArgumentError)
300
- end
301
-
302
- context 'given a block' do
303
- it "sets merged key's values to the block's return value" do
304
- expect(subject.merge!(first_name: 'Jim') do |key, oldval, newval|
305
- "#{key}: #{newval} #{oldval}"
306
- end.first_name).to eq 'first_name: Jim Bob'
307
- end
308
- end
309
- end
310
-
311
- describe 'properties' do
312
- it 'lists defined properties' do
313
- expect(described_class.properties).to eq Set.new(%i[first_name email count])
314
- end
315
-
316
- it 'checks if a property exists' do
317
- expect(described_class.property?(:first_name)).to be_truthy
318
- expect(described_class.property?('first_name')).to be_falsy
319
- end
320
-
321
- it 'checks if a property is required' do
322
- expect(described_class.required?(:first_name)).to be_truthy
323
- expect(described_class.required?('first_name')).to be_falsy
324
- end
325
-
326
- it 'doesnt include property from subclass' do
327
- expect(described_class.property?(:last_name)).to be_falsy
328
- end
329
-
330
- it 'lists declared defaults' do
331
- expect(described_class.defaults).to eq(count: 0)
332
- end
333
-
334
- it 'allows properties that end in bang' do
335
- expect(PropertyBangTest.property?(:important!)).to be_truthy
336
- end
337
- end
338
-
339
- describe '#replace' do
340
- before { subject.replace(first_name: 'Cain') }
341
-
342
- it 'return self' do
343
- expect(subject.replace(email: 'bar').to_hash).to eq(email: 'bar', count: 0)
344
- end
345
-
346
- it 'sets all specified keys to their corresponding values' do
347
- expect(subject.first_name).to eq 'Cain'
348
- end
349
-
350
- it 'leaves only specified keys and keys with default values' do
351
- expect(subject.keys.sort_by(&:to_s)).to eq %i[count first_name]
352
- expect(subject.email).to be_nil
353
- expect(subject.count).to eq 0
354
- end
355
-
356
- context 'when replacing keys with default values' do
357
- before { subject.replace(count: 3) }
358
-
359
- it 'sets all specified keys to their corresponding values' do
360
- expect(subject.count).to eq 3
361
- end
362
- end
363
- end
364
-
365
- describe '#update_attributes!(params)' do
366
- let(:params) { { first_name: 'Alice', email: 'alice@example.com' } }
367
-
368
- context 'when there is coercion' do
369
- let(:params_before) { { city: 'nyc', person: { first_name: 'Bob', email: 'bob@example.com' } } }
370
- let(:params_after) { { city: 'sfo', person: { first_name: 'Alice', email: 'alice@example.com' } } }
371
-
372
- subject { DashWithCoercion.new(params_before) }
373
-
374
- it 'update the attributes' do
375
- expect(subject.person.first_name).to eq params_before[:person][:first_name]
376
- subject.update_attributes!(params_after)
377
- expect(subject.person.first_name).to eq params_after[:person][:first_name]
378
- end
379
- end
380
-
381
- it 'update the attributes' do
382
- subject.update_attributes!(params)
383
- expect(subject.first_name).to eq params[:first_name]
384
- expect(subject.email).to eq params[:email]
385
- expect(subject.count).to eq subject.class.defaults[:count]
386
- end
387
-
388
- context 'when required property is update to nil' do
389
- let(:params) { { first_name: nil, email: 'alice@example.com' } }
390
-
391
- it 'raise an ArgumentError' do
392
- expect { subject.update_attributes!(params) }.to raise_error(ArgumentError)
393
- end
394
- end
395
-
396
- context 'when a default property is update to nil' do
397
- let(:params) { { count: nil, email: 'alice@example.com' } }
398
-
399
- it 'set the property back to the default value' do
400
- subject.update_attributes!(params)
401
- expect(subject.email).to eq params[:email]
402
- expect(subject.count).to eq subject.class.defaults[:count]
403
- end
404
- end
405
-
406
- context 'codependent attributes' do
407
- let(:codependent) do
408
- Class.new(Hashie::Dash) do
409
- property :a, required: -> { b.nil? }, message: 'is required if b is not set.'
410
- property :b, required: -> { a.nil? }, message: 'is required if a is not set.'
411
- end
412
- end
413
-
414
- it 'does not raise an error when only the first property is set' do
415
- expect { codependent.new(a: 'ant', b: nil) }.not_to raise_error
416
- end
417
-
418
- it 'does not raise an error when only the second property is set' do
419
- expect { codependent.new(a: nil, b: 'bat') }.not_to raise_error
420
- end
421
-
422
- it 'does not raise an error when both properties are set' do
423
- expect { codependent.new(a: 'ant', b: 'bat') }.not_to raise_error
424
- end
425
-
426
- it 'raises an error when neither property is set' do
427
- expect { codependent.new(a: nil, b: nil) }.to raise_error(ArgumentError)
428
- end
429
- end
430
- end
431
- end
432
-
433
- describe Hashie::Dash, 'inheritance' do
434
- before do
435
- @top = Class.new(Hashie::Dash)
436
- @middle = Class.new(@top)
437
- @bottom = Class.new(@middle)
438
- end
439
-
440
- it 'reports empty properties when nothing defined' do
441
- expect(@top.properties).to be_empty
442
- expect(@top.defaults).to be_empty
443
- end
444
-
445
- it 'inherits properties downwards' do
446
- @top.property :echo
447
- expect(@middle.properties).to include(:echo)
448
- expect(@bottom.properties).to include(:echo)
449
- end
450
-
451
- it 'doesnt inherit properties upwards' do
452
- @middle.property :echo
453
- expect(@top.properties).not_to include(:echo)
454
- expect(@bottom.properties).to include(:echo)
455
- end
456
-
457
- it 'allows overriding a default on an existing property' do
458
- @top.property :echo
459
- @middle.property :echo, default: 123
460
- expect(@bottom.properties.to_a).to eq [:echo]
461
- expect(@bottom.new.echo).to eq 123
462
- end
463
-
464
- it 'allows clearing an existing default' do
465
- @top.property :echo
466
- @middle.property :echo, default: 123
467
- @bottom.property :echo
468
- expect(@bottom.properties.to_a).to eq [:echo]
469
- expect(@bottom.new.echo).to be_nil
470
- end
471
-
472
- it 'allows nil defaults' do
473
- @bottom.property :echo, default: nil
474
- expect(@bottom.new).to have_key(:echo)
475
- expect(@bottom.new).to_not have_key('echo')
476
- end
477
- end
478
-
479
- describe SubclassedTest do
480
- subject { SubclassedTest.new(first_name: 'Bob', last_name: 'McNob', email: 'bob@example.com') }
481
-
482
- describe '#count' do
483
- subject { super().count }
484
- it { should be_zero }
485
- end
486
-
487
- it { should respond_to(:first_name) }
488
- it { should respond_to(:first_name=) }
489
- it { should respond_to(:last_name) }
490
- it { should respond_to(:last_name=) }
491
-
492
- it 'has one additional property' do
493
- expect(described_class.property?(:last_name)).to be_truthy
494
- end
495
-
496
- it "didn't override superclass inheritance logic" do
497
- expect(described_class.instance_variable_get('@inheritance_test')).to be_truthy
498
- end
499
- end
500
-
501
- class ConditionallyRequiredTest < Hashie::Dash
502
- property :username
503
- property :password, required: -> { !username.nil? }, message: 'must be set, too.'
504
- end
505
-
506
- describe ConditionallyRequiredTest do
507
- it 'does not allow a conditionally required property to be set to nil if required' do
508
- expect { ConditionallyRequiredTest.new(username: 'bob.smith', password: nil) }.to raise_error(ArgumentError, "The property 'password' must be set, too.")
509
- end
510
-
511
- it 'allows a conditionally required property to be set to nil if not required' do
512
- expect { ConditionallyRequiredTest.new(username: nil, password: nil) }.not_to raise_error
513
- end
514
-
515
- it 'allows a conditionally required property to be set if required' do
516
- expect { ConditionallyRequiredTest.new(username: 'bob.smith', password: '$ecure!') }.not_to raise_error
517
- end
518
- end
519
-
520
- class MixedPropertiesTest < Hashie::Dash
521
- property :symbol
522
- property 'string'
523
- end
524
-
525
- describe MixedPropertiesTest do
526
- subject { MixedPropertiesTest.new('string' => 'string', symbol: 'symbol') }
527
-
528
- it { should respond_to('string') }
529
- it { should respond_to(:symbol) }
530
-
531
- it 'property?' do
532
- expect(described_class.property?('string')).to be_truthy
533
- expect(described_class.property?(:symbol)).to be_truthy
534
- end
535
-
536
- it 'fetch' do
537
- expect(subject['string']).to eq('string')
538
- expect { subject[:string] }.to raise_error(NoMethodError)
539
- expect(subject[:symbol]).to eq('symbol')
540
- expect { subject['symbol'] }.to raise_error(NoMethodError)
541
- end
542
-
543
- it 'double define' do
544
- klass = Class.new(MixedPropertiesTest) do
545
- property 'symbol'
546
- end
547
- instance = klass.new(symbol: 'one', 'symbol' => 'two')
548
- expect(instance[:symbol]).to eq('one')
549
- expect(instance['symbol']).to eq('two')
550
- end
551
-
552
- it 'assign' do
553
- subject['string'] = 'updated'
554
- expect(subject['string']).to eq('updated')
555
-
556
- expect { subject[:string] = 'updated' }.to raise_error(NoMethodError)
557
-
558
- subject[:symbol] = 'updated'
559
- expect(subject[:symbol]).to eq('updated')
560
-
561
- expect { subject['symbol'] = 'updated' }.to raise_error(NoMethodError)
562
- end
563
- end
564
-
565
- context 'Dynamic Dash Class' do
566
- it 'define property' do
567
- klass = Class.new(Hashie::Dash)
568
- my_property = 'my_property'
569
- my_orig = my_property.dup
570
-
571
- klass.property(my_property)
572
-
573
- expect(my_property).to eq(my_orig)
574
- end
575
- end
576
-
577
- context 'with method access' do
578
- class DashWithMethodAccess < Hashie::Dash
579
- include Hashie::Extensions::IndifferentAccess
580
- include Hashie::Extensions::MethodQuery
581
-
582
- property :test
583
- end
584
-
585
- subject(:dash) { DashWithMethodAccess.new(test: 'value') }
586
-
587
- describe '#test' do
588
- subject { dash.test }
589
-
590
- it { is_expected.to eq('value') }
591
- end
592
-
593
- describe '#test?' do
594
- subject { dash.test? }
595
-
596
- it { is_expected.to eq true }
597
- end
598
- end