hashie 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +213 -187
  3. data/CONTRIBUTING.md +13 -6
  4. data/README.md +33 -9
  5. data/UPGRADING.md +5 -5
  6. data/hashie.gemspec +11 -6
  7. data/lib/hashie.rb +1 -0
  8. data/lib/hashie/extensions/dash/property_translation.rb +1 -1
  9. data/lib/hashie/extensions/deep_merge.rb +18 -1
  10. data/lib/hashie/extensions/mash/permissive_respond_to.rb +61 -0
  11. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +19 -2
  12. data/lib/hashie/extensions/ruby_version_check.rb +5 -1
  13. data/lib/hashie/mash.rb +31 -26
  14. data/lib/hashie/utils.rb +28 -0
  15. data/lib/hashie/version.rb +1 -1
  16. metadata +16 -131
  17. data/spec/hashie/array_spec.rb +0 -29
  18. data/spec/hashie/clash_spec.rb +0 -70
  19. data/spec/hashie/dash_spec.rb +0 -608
  20. data/spec/hashie/extensions/autoload_spec.rb +0 -24
  21. data/spec/hashie/extensions/coercion_spec.rb +0 -648
  22. data/spec/hashie/extensions/dash/coercion_spec.rb +0 -13
  23. data/spec/hashie/extensions/dash/indifferent_access_spec.rb +0 -84
  24. data/spec/hashie/extensions/deep_fetch_spec.rb +0 -97
  25. data/spec/hashie/extensions/deep_find_spec.rb +0 -144
  26. data/spec/hashie/extensions/deep_locate_spec.rb +0 -138
  27. data/spec/hashie/extensions/deep_merge_spec.rb +0 -74
  28. data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -48
  29. data/spec/hashie/extensions/indifferent_access_spec.rb +0 -295
  30. data/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb +0 -208
  31. data/spec/hashie/extensions/key_conversion_spec.rb +0 -12
  32. data/spec/hashie/extensions/mash/define_accessors_spec.rb +0 -90
  33. data/spec/hashie/extensions/mash/keep_original_keys_spec.rb +0 -46
  34. data/spec/hashie/extensions/mash/safe_assignment_spec.rb +0 -50
  35. data/spec/hashie/extensions/mash/symbolize_keys_spec.rb +0 -39
  36. data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
  37. data/spec/hashie/extensions/method_access_spec.rb +0 -233
  38. data/spec/hashie/extensions/strict_key_access_spec.rb +0 -109
  39. data/spec/hashie/extensions/stringify_keys_spec.rb +0 -124
  40. data/spec/hashie/extensions/symbolize_keys_spec.rb +0 -131
  41. data/spec/hashie/hash_spec.rb +0 -123
  42. data/spec/hashie/mash_spec.rb +0 -1077
  43. data/spec/hashie/parsers/yaml_erb_parser_spec.rb +0 -46
  44. data/spec/hashie/rash_spec.rb +0 -83
  45. data/spec/hashie/trash_spec.rb +0 -334
  46. data/spec/hashie/utils_spec.rb +0 -25
  47. data/spec/hashie/version_spec.rb +0 -7
  48. data/spec/hashie_spec.rb +0 -13
  49. data/spec/integration/elasticsearch/integration_spec.rb +0 -41
  50. data/spec/integration/omniauth-oauth2/app.rb +0 -52
  51. data/spec/integration/omniauth-oauth2/integration_spec.rb +0 -26
  52. data/spec/integration/omniauth-oauth2/some_site.rb +0 -38
  53. data/spec/integration/omniauth/app.rb +0 -11
  54. data/spec/integration/omniauth/integration_spec.rb +0 -38
  55. data/spec/integration/rails-without-dependency/integration_spec.rb +0 -15
  56. data/spec/integration/rails/app.rb +0 -40
  57. data/spec/integration/rails/integration_spec.rb +0 -47
  58. data/spec/spec_helper.rb +0 -23
  59. data/spec/support/integration_specs.rb +0 -36
  60. data/spec/support/logger.rb +0 -24
  61. data/spec/support/module_context.rb +0 -11
  62. data/spec/support/ruby_version_check.rb +0 -6
@@ -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 = '4.0.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: 4.0.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: 2019-10-30 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
@@ -89,6 +61,7 @@ files:
89
61
  - lib/hashie/extensions/key_conversion.rb
90
62
  - lib/hashie/extensions/mash/define_accessors.rb
91
63
  - lib/hashie/extensions/mash/keep_original_keys.rb
64
+ - lib/hashie/extensions/mash/permissive_respond_to.rb
92
65
  - lib/hashie/extensions/mash/safe_assignment.rb
93
66
  - lib/hashie/extensions/mash/symbolize_keys.rb
94
67
  - lib/hashie/extensions/merge_initializer.rb
@@ -108,56 +81,14 @@ files:
108
81
  - lib/hashie/trash.rb
109
82
  - lib/hashie/utils.rb
110
83
  - lib/hashie/version.rb
111
- - spec/hashie/array_spec.rb
112
- - spec/hashie/clash_spec.rb
113
- - spec/hashie/dash_spec.rb
114
- - spec/hashie/extensions/autoload_spec.rb
115
- - spec/hashie/extensions/coercion_spec.rb
116
- - spec/hashie/extensions/dash/coercion_spec.rb
117
- - spec/hashie/extensions/dash/indifferent_access_spec.rb
118
- - spec/hashie/extensions/deep_fetch_spec.rb
119
- - spec/hashie/extensions/deep_find_spec.rb
120
- - spec/hashie/extensions/deep_locate_spec.rb
121
- - spec/hashie/extensions/deep_merge_spec.rb
122
- - spec/hashie/extensions/ignore_undeclared_spec.rb
123
- - spec/hashie/extensions/indifferent_access_spec.rb
124
- - spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb
125
- - spec/hashie/extensions/key_conversion_spec.rb
126
- - spec/hashie/extensions/mash/define_accessors_spec.rb
127
- - spec/hashie/extensions/mash/keep_original_keys_spec.rb
128
- - spec/hashie/extensions/mash/safe_assignment_spec.rb
129
- - spec/hashie/extensions/mash/symbolize_keys_spec.rb
130
- - spec/hashie/extensions/merge_initializer_spec.rb
131
- - spec/hashie/extensions/method_access_spec.rb
132
- - spec/hashie/extensions/strict_key_access_spec.rb
133
- - spec/hashie/extensions/stringify_keys_spec.rb
134
- - spec/hashie/extensions/symbolize_keys_spec.rb
135
- - spec/hashie/hash_spec.rb
136
- - spec/hashie/mash_spec.rb
137
- - spec/hashie/parsers/yaml_erb_parser_spec.rb
138
- - spec/hashie/rash_spec.rb
139
- - spec/hashie/trash_spec.rb
140
- - spec/hashie/utils_spec.rb
141
- - spec/hashie/version_spec.rb
142
- - spec/hashie_spec.rb
143
- - spec/integration/elasticsearch/integration_spec.rb
144
- - spec/integration/omniauth-oauth2/app.rb
145
- - spec/integration/omniauth-oauth2/integration_spec.rb
146
- - spec/integration/omniauth-oauth2/some_site.rb
147
- - spec/integration/omniauth/app.rb
148
- - spec/integration/omniauth/integration_spec.rb
149
- - spec/integration/rails-without-dependency/integration_spec.rb
150
- - spec/integration/rails/app.rb
151
- - spec/integration/rails/integration_spec.rb
152
- - spec/spec_helper.rb
153
- - spec/support/integration_specs.rb
154
- - spec/support/logger.rb
155
- - spec/support/module_context.rb
156
- - spec/support/ruby_version_check.rb
157
- homepage: https://github.com/intridea/hashie
84
+ homepage: https://github.com/hashie/hashie
158
85
  licenses:
159
86
  - MIT
160
- 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
161
92
  post_install_message:
162
93
  rdoc_options: []
163
94
  require_paths:
@@ -173,54 +104,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
104
  - !ruby/object:Gem::Version
174
105
  version: '0'
175
106
  requirements: []
176
- rubygems_version: 3.0.4
107
+ rubygems_version: 3.0.3
177
108
  signing_key:
178
109
  specification_version: 4
179
110
  summary: Your friendly neighborhood hash library.
180
- test_files:
181
- - spec/hashie/parsers/yaml_erb_parser_spec.rb
182
- - spec/hashie/array_spec.rb
183
- - spec/hashie/hash_spec.rb
184
- - spec/hashie/version_spec.rb
185
- - spec/hashie/clash_spec.rb
186
- - spec/hashie/mash_spec.rb
187
- - spec/hashie/extensions/stringify_keys_spec.rb
188
- - spec/hashie/extensions/deep_locate_spec.rb
189
- - spec/hashie/extensions/coercion_spec.rb
190
- - spec/hashie/extensions/mash/safe_assignment_spec.rb
191
- - spec/hashie/extensions/mash/define_accessors_spec.rb
192
- - spec/hashie/extensions/mash/symbolize_keys_spec.rb
193
- - spec/hashie/extensions/mash/keep_original_keys_spec.rb
194
- - spec/hashie/extensions/key_conversion_spec.rb
195
- - spec/hashie/extensions/ignore_undeclared_spec.rb
196
- - spec/hashie/extensions/autoload_spec.rb
197
- - spec/hashie/extensions/strict_key_access_spec.rb
198
- - spec/hashie/extensions/deep_fetch_spec.rb
199
- - spec/hashie/extensions/method_access_spec.rb
200
- - spec/hashie/extensions/deep_find_spec.rb
201
- - spec/hashie/extensions/deep_merge_spec.rb
202
- - spec/hashie/extensions/merge_initializer_spec.rb
203
- - spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb
204
- - spec/hashie/extensions/dash/coercion_spec.rb
205
- - spec/hashie/extensions/dash/indifferent_access_spec.rb
206
- - spec/hashie/extensions/symbolize_keys_spec.rb
207
- - spec/hashie/extensions/indifferent_access_spec.rb
208
- - spec/hashie/rash_spec.rb
209
- - spec/hashie/utils_spec.rb
210
- - spec/hashie/dash_spec.rb
211
- - spec/hashie/trash_spec.rb
212
- - spec/spec_helper.rb
213
- - spec/integration/rails-without-dependency/integration_spec.rb
214
- - spec/integration/omniauth-oauth2/integration_spec.rb
215
- - spec/integration/omniauth-oauth2/app.rb
216
- - spec/integration/omniauth-oauth2/some_site.rb
217
- - spec/integration/omniauth/integration_spec.rb
218
- - spec/integration/omniauth/app.rb
219
- - spec/integration/elasticsearch/integration_spec.rb
220
- - spec/integration/rails/integration_spec.rb
221
- - spec/integration/rails/app.rb
222
- - spec/support/module_context.rb
223
- - spec/support/logger.rb
224
- - spec/support/integration_specs.rb
225
- - spec/support/ruby_version_check.rb
226
- - 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,608 +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 }
133
- .to raise_error(*property_required_custom_error('first_name'))
134
- end
135
-
136
- it 'fails writing a required property to nil using []=' do
137
- expect { subject[:first_name] = nil }.to raise_error(*property_required_error('first_name'))
138
- expect { required_message[:first_name] = nil }
139
- .to raise_error(*property_required_custom_error('first_name'))
140
- end
141
-
142
- it 'fails writing to a non-existent property using []=' do
143
- expect { subject['nonexistent'] = 123 }.to raise_error(*no_property_error('nonexistent'))
144
- end
145
-
146
- it 'works for an existing property using []=' do
147
- subject[:first_name] = 'Bob'
148
- expect(subject[:first_name]).to eq 'Bob'
149
- expect { subject['first_name'] }.to raise_error(*no_property_error('first_name'))
150
- end
151
-
152
- it 'works for an existing property using a method call' do
153
- subject.first_name = 'Franklin'
154
- expect(subject.first_name).to eq 'Franklin'
155
- end
156
- end
157
-
158
- context 'reading from properties' do
159
- it 'fails reading from a non-existent property using []' do
160
- expect { subject['nonexistent'] }.to raise_error(*no_property_error('nonexistent'))
161
- end
162
-
163
- it 'is able to retrieve properties through blocks' do
164
- subject[:first_name] = 'Aiden'
165
- value = nil
166
- subject.[](:first_name) { |v| value = v }
167
- expect(value).to eq 'Aiden'
168
- end
169
-
170
- it 'is able to retrieve properties through blocks with method calls' do
171
- subject[:first_name] = 'Frodo'
172
- value = nil
173
- subject.first_name { |v| value = v }
174
- expect(value).to eq 'Frodo'
175
- end
176
- end
177
-
178
- context 'reading from deferred properties' do
179
- it 'evaluates proc after initial read' do
180
- expect(DeferredTest.new[:created_at]).to be_instance_of(Time)
181
- end
182
-
183
- it 'does not evalute proc after subsequent reads' do
184
- deferred = DeferredTest.new
185
- expect(deferred[:created_at].object_id).to eq deferred[:created_at].object_id
186
- end
187
- end
188
-
189
- context 'reading from a deferred property based on context' do
190
- it 'provides the current hash as context for evaluation' do
191
- deferred = DeferredWithSelfTest.new
192
- expect(deferred[:created_at].object_id).to eq deferred[:created_at].object_id
193
- expect(deferred[:updated_at].object_id).to eq deferred[:created_at].object_id
194
- end
195
- end
196
-
197
- context 'converting from a Mash' do
198
- class ConvertingFromMash < Hashie::Dash
199
- property :property, required: true
200
- end
201
-
202
- context 'without keeping the original keys' do
203
- let(:mash) { Hashie::Mash.new(property: 'test') }
204
-
205
- it 'does not pick up the property from the stringified key' do
206
- expect { ConvertingFromMash.new(mash) }.to raise_error(NoMethodError)
207
- end
208
- end
209
-
210
- context 'when keeping the original keys' do
211
- class KeepingMash < Hashie::Mash
212
- include Hashie::Extensions::Mash::KeepOriginalKeys
213
- end
214
-
215
- let(:mash) { KeepingMash.new(property: 'test') }
216
-
217
- it 'picks up the property from the original key' do
218
- expect { ConvertingFromMash.new(mash) }.not_to raise_error
219
- end
220
- end
221
- end
222
-
223
- describe '#new' do
224
- it 'fails with non-existent properties' do
225
- expect { described_class.new(bork: '') }.to raise_error(*no_property_error('bork'))
226
- end
227
-
228
- it 'sets properties that it is able to' do
229
- obj = described_class.new first_name: 'Michael'
230
- expect(obj.first_name).to eq 'Michael'
231
- end
232
-
233
- it 'accepts nil' do
234
- expect { DashNoRequiredTest.new(nil) }.not_to raise_error
235
- end
236
-
237
- it 'accepts block to define a global default' do
238
- obj = described_class.new { |_, key| key.to_s.upcase }
239
- expect(obj.first_name).to eq 'FIRST_NAME'
240
- expect(obj.count).to be_zero
241
- end
242
-
243
- it 'fails when required values are missing' do
244
- expect { DashTest.new }.to raise_error(*property_required_error('first_name'))
245
- end
246
-
247
- it 'does not overwrite default values' do
248
- obj1 = DashDefaultTest.new
249
- obj1.aliases << 'El Rey'
250
- obj2 = DashDefaultTest.new
251
- expect(obj2.aliases).not_to include 'El Rey'
252
- end
253
- end
254
-
255
- describe '#merge' do
256
- it 'creates a new instance of the Dash' do
257
- new_dash = subject.merge(first_name: 'Robert')
258
- expect(subject.object_id).not_to eq new_dash.object_id
259
- end
260
-
261
- it 'merges the given hash' do
262
- new_dash = subject.merge(first_name: 'Robert', email: 'robert@example.com')
263
- expect(new_dash.first_name).to eq 'Robert'
264
- expect(new_dash.email).to eq 'robert@example.com'
265
- end
266
-
267
- it 'fails with non-existent properties' do
268
- expect { subject.merge(middle_name: 'James') }
269
- .to raise_error(*no_property_error('middle_name'))
270
- end
271
-
272
- it 'errors out when attempting to set a required property to nil' do
273
- expect { subject.merge(first_name: nil) }
274
- .to raise_error(*property_required_error('first_name'))
275
- end
276
-
277
- context 'given a block' do
278
- it "sets merged key's values to the block's return value" do
279
- expect(subject.merge(first_name: 'Jim') do |key, oldval, newval|
280
- "#{key}: #{newval} #{oldval}"
281
- end.first_name).to eq 'first_name: Jim Bob'
282
- end
283
- end
284
- end
285
-
286
- describe '#merge!' do
287
- it 'modifies the existing instance of the Dash' do
288
- original_dash = subject.merge!(first_name: 'Robert')
289
- expect(subject.object_id).to eq original_dash.object_id
290
- end
291
-
292
- it 'merges the given hash' do
293
- subject.merge!(first_name: 'Robert', email: 'robert@example.com')
294
- expect(subject.first_name).to eq 'Robert'
295
- expect(subject.email).to eq 'robert@example.com'
296
- end
297
-
298
- it 'fails with non-existent properties' do
299
- expect { subject.merge!(middle_name: 'James') }.to raise_error(NoMethodError)
300
- end
301
-
302
- it 'errors out when attempting to set a required property to nil' do
303
- expect { subject.merge!(first_name: nil) }.to raise_error(ArgumentError)
304
- end
305
-
306
- context 'given a block' do
307
- it "sets merged key's values to the block's return value" do
308
- expect(subject.merge!(first_name: 'Jim') do |key, oldval, newval|
309
- "#{key}: #{newval} #{oldval}"
310
- end.first_name).to eq 'first_name: Jim Bob'
311
- end
312
- end
313
- end
314
-
315
- describe 'properties' do
316
- it 'lists defined properties' do
317
- expect(described_class.properties).to eq Set.new(%i[first_name email count])
318
- end
319
-
320
- it 'checks if a property exists' do
321
- expect(described_class.property?(:first_name)).to be_truthy
322
- expect(described_class.property?('first_name')).to be_falsy
323
- end
324
-
325
- it 'checks if a property is required' do
326
- expect(described_class.required?(:first_name)).to be_truthy
327
- expect(described_class.required?('first_name')).to be_falsy
328
- end
329
-
330
- it 'doesnt include property from subclass' do
331
- expect(described_class.property?(:last_name)).to be_falsy
332
- end
333
-
334
- it 'lists declared defaults' do
335
- expect(described_class.defaults).to eq(count: 0)
336
- end
337
-
338
- it 'allows properties that end in bang' do
339
- expect(PropertyBangTest.property?(:important!)).to be_truthy
340
- end
341
- end
342
-
343
- describe '#replace' do
344
- before { subject.replace(first_name: 'Cain') }
345
-
346
- it 'return self' do
347
- expect(subject.replace(email: 'bar').to_hash).to eq(email: 'bar', count: 0)
348
- end
349
-
350
- it 'sets all specified keys to their corresponding values' do
351
- expect(subject.first_name).to eq 'Cain'
352
- end
353
-
354
- it 'leaves only specified keys and keys with default values' do
355
- expect(subject.keys.sort_by(&:to_s)).to eq %i[count first_name]
356
- expect(subject.email).to be_nil
357
- expect(subject.count).to eq 0
358
- end
359
-
360
- context 'when replacing keys with default values' do
361
- before { subject.replace(count: 3) }
362
-
363
- it 'sets all specified keys to their corresponding values' do
364
- expect(subject.count).to eq 3
365
- end
366
- end
367
- end
368
-
369
- describe '#update_attributes!(params)' do
370
- let(:params) { { first_name: 'Alice', email: 'alice@example.com' } }
371
-
372
- context 'when there is coercion' do
373
- let(:params_before) do
374
- { city: 'nyc', person: { first_name: 'Bob', email: 'bob@example.com' } }
375
- end
376
- let(:params_after) do
377
- { city: 'sfo', person: { first_name: 'Alice', email: 'alice@example.com' } }
378
- end
379
-
380
- subject { DashWithCoercion.new(params_before) }
381
-
382
- it 'update the attributes' do
383
- expect(subject.person.first_name).to eq params_before[:person][:first_name]
384
- subject.update_attributes!(params_after)
385
- expect(subject.person.first_name).to eq params_after[:person][:first_name]
386
- end
387
- end
388
-
389
- it 'update the attributes' do
390
- subject.update_attributes!(params)
391
- expect(subject.first_name).to eq params[:first_name]
392
- expect(subject.email).to eq params[:email]
393
- expect(subject.count).to eq subject.class.defaults[:count]
394
- end
395
-
396
- context 'when required property is update to nil' do
397
- let(:params) { { first_name: nil, email: 'alice@example.com' } }
398
-
399
- it 'raise an ArgumentError' do
400
- expect { subject.update_attributes!(params) }.to raise_error(ArgumentError)
401
- end
402
- end
403
-
404
- context 'when a default property is update to nil' do
405
- let(:params) { { count: nil, email: 'alice@example.com' } }
406
-
407
- it 'set the property back to the default value' do
408
- subject.update_attributes!(params)
409
- expect(subject.email).to eq params[:email]
410
- expect(subject.count).to eq subject.class.defaults[:count]
411
- end
412
- end
413
-
414
- context 'codependent attributes' do
415
- let(:codependent) do
416
- Class.new(Hashie::Dash) do
417
- property :a, required: -> { b.nil? }, message: 'is required if b is not set.'
418
- property :b, required: -> { a.nil? }, message: 'is required if a is not set.'
419
- end
420
- end
421
-
422
- it 'does not raise an error when only the first property is set' do
423
- expect { codependent.new(a: 'ant', b: nil) }.not_to raise_error
424
- end
425
-
426
- it 'does not raise an error when only the second property is set' do
427
- expect { codependent.new(a: nil, b: 'bat') }.not_to raise_error
428
- end
429
-
430
- it 'does not raise an error when both properties are set' do
431
- expect { codependent.new(a: 'ant', b: 'bat') }.not_to raise_error
432
- end
433
-
434
- it 'raises an error when neither property is set' do
435
- expect { codependent.new(a: nil, b: nil) }.to raise_error(ArgumentError)
436
- end
437
- end
438
- end
439
- end
440
-
441
- describe Hashie::Dash, 'inheritance' do
442
- before do
443
- @top = Class.new(Hashie::Dash)
444
- @middle = Class.new(@top)
445
- @bottom = Class.new(@middle)
446
- end
447
-
448
- it 'reports empty properties when nothing defined' do
449
- expect(@top.properties).to be_empty
450
- expect(@top.defaults).to be_empty
451
- end
452
-
453
- it 'inherits properties downwards' do
454
- @top.property :echo
455
- expect(@middle.properties).to include(:echo)
456
- expect(@bottom.properties).to include(:echo)
457
- end
458
-
459
- it 'doesnt inherit properties upwards' do
460
- @middle.property :echo
461
- expect(@top.properties).not_to include(:echo)
462
- expect(@bottom.properties).to include(:echo)
463
- end
464
-
465
- it 'allows overriding a default on an existing property' do
466
- @top.property :echo
467
- @middle.property :echo, default: 123
468
- expect(@bottom.properties.to_a).to eq [:echo]
469
- expect(@bottom.new.echo).to eq 123
470
- end
471
-
472
- it 'allows clearing an existing default' do
473
- @top.property :echo
474
- @middle.property :echo, default: 123
475
- @bottom.property :echo
476
- expect(@bottom.properties.to_a).to eq [:echo]
477
- expect(@bottom.new.echo).to be_nil
478
- end
479
-
480
- it 'allows nil defaults' do
481
- @bottom.property :echo, default: nil
482
- expect(@bottom.new).to have_key(:echo)
483
- expect(@bottom.new).to_not have_key('echo')
484
- end
485
- end
486
-
487
- describe SubclassedTest do
488
- subject { SubclassedTest.new(first_name: 'Bob', last_name: 'McNob', email: 'bob@example.com') }
489
-
490
- describe '#count' do
491
- subject { super().count }
492
- it { should be_zero }
493
- end
494
-
495
- it { should respond_to(:first_name) }
496
- it { should respond_to(:first_name=) }
497
- it { should respond_to(:last_name) }
498
- it { should respond_to(:last_name=) }
499
-
500
- it 'has one additional property' do
501
- expect(described_class.property?(:last_name)).to be_truthy
502
- end
503
-
504
- it "didn't override superclass inheritance logic" do
505
- expect(described_class.instance_variable_get('@inheritance_test')).to be_truthy
506
- end
507
- end
508
-
509
- class ConditionallyRequiredTest < Hashie::Dash
510
- property :username
511
- property :password, required: -> { !username.nil? }, message: 'must be set, too.'
512
- end
513
-
514
- describe ConditionallyRequiredTest do
515
- it 'does not allow a conditionally required property to be set to nil if required' do
516
- expect { ConditionallyRequiredTest.new(username: 'bob.smith', password: nil) }
517
- .to raise_error(ArgumentError, "The property 'password' must be set, too.")
518
- end
519
-
520
- it 'allows a conditionally required property to be set to nil if not required' do
521
- expect { ConditionallyRequiredTest.new(username: nil, password: nil) }.not_to raise_error
522
- end
523
-
524
- it 'allows a conditionally required property to be set if required' do
525
- expect { ConditionallyRequiredTest.new(username: 'bob.smith', password: '$ecure!') }
526
- .not_to raise_error
527
- end
528
- end
529
-
530
- class MixedPropertiesTest < Hashie::Dash
531
- property :symbol
532
- property 'string'
533
- end
534
-
535
- describe MixedPropertiesTest do
536
- subject { MixedPropertiesTest.new('string' => 'string', symbol: 'symbol') }
537
-
538
- it { should respond_to('string') }
539
- it { should respond_to(:symbol) }
540
-
541
- it 'property?' do
542
- expect(described_class.property?('string')).to be_truthy
543
- expect(described_class.property?(:symbol)).to be_truthy
544
- end
545
-
546
- it 'fetch' do
547
- expect(subject['string']).to eq('string')
548
- expect { subject[:string] }.to raise_error(NoMethodError)
549
- expect(subject[:symbol]).to eq('symbol')
550
- expect { subject['symbol'] }.to raise_error(NoMethodError)
551
- end
552
-
553
- it 'double define' do
554
- klass = Class.new(MixedPropertiesTest) do
555
- property 'symbol'
556
- end
557
- instance = klass.new(symbol: 'one', 'symbol' => 'two')
558
- expect(instance[:symbol]).to eq('one')
559
- expect(instance['symbol']).to eq('two')
560
- end
561
-
562
- it 'assign' do
563
- subject['string'] = 'updated'
564
- expect(subject['string']).to eq('updated')
565
-
566
- expect { subject[:string] = 'updated' }.to raise_error(NoMethodError)
567
-
568
- subject[:symbol] = 'updated'
569
- expect(subject[:symbol]).to eq('updated')
570
-
571
- expect { subject['symbol'] = 'updated' }.to raise_error(NoMethodError)
572
- end
573
- end
574
-
575
- context 'Dynamic Dash Class' do
576
- it 'define property' do
577
- klass = Class.new(Hashie::Dash)
578
- my_property = 'my_property'
579
- my_orig = my_property.dup
580
-
581
- klass.property(my_property)
582
-
583
- expect(my_property).to eq(my_orig)
584
- end
585
- end
586
-
587
- context 'with method access' do
588
- class DashWithMethodAccess < Hashie::Dash
589
- include Hashie::Extensions::IndifferentAccess
590
- include Hashie::Extensions::MethodQuery
591
-
592
- property :test
593
- end
594
-
595
- subject(:dash) { DashWithMethodAccess.new(test: 'value') }
596
-
597
- describe '#test' do
598
- subject { dash.test }
599
-
600
- it { is_expected.to eq('value') }
601
- end
602
-
603
- describe '#test?' do
604
- subject { dash.test? }
605
-
606
- it { is_expected.to eq true }
607
- end
608
- end