hashie 3.6.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -1,13 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Hashie::Extensions::Dash::Coercion do
4
- class DashWithCoercion < Hashie::Dash
5
- include Hashie::Extensions::Dash::Coercion
6
-
7
- property :type, coerce: Symbol
8
- end
9
-
10
- it 'does the coercion of properties' do
11
- expect(DashWithCoercion.new(type: 'something')).to eq(type: :something)
12
- end
13
- end
@@ -1,84 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Hashie::Extensions::Dash::IndifferentAccess do
4
- class TrashWithIndifferentAccess < Hashie::Trash
5
- include Hashie::Extensions::Dash::IndifferentAccess
6
- property :per_page, transform_with: ->(v) { v.to_i }
7
- property :total, from: :total_pages
8
- end
9
-
10
- class DashWithIndifferentAccess < Hashie::Dash
11
- include Hashie::Extensions::Dash::IndifferentAccess
12
- property :name
13
- end
14
-
15
- context 'when included in Trash' do
16
- let(:params) { { per_page: '1', total_pages: 2 } }
17
- subject { TrashWithIndifferentAccess.new(params) }
18
-
19
- it 'gets the expected behaviour' do
20
- expect(subject.per_page).to eq params[:per_page].to_i
21
- expect(subject.total).to eq params[:total_pages]
22
- end
23
- end
24
-
25
- context 'when included in Dash' do
26
- let(:patch) { Hashie::Extensions::Dash::IndifferentAccess::ClassMethods }
27
- let(:dash_class) { Class.new(Hashie::Dash) }
28
-
29
- it 'extends with the patch once' do
30
- expect(patch).to receive(:extended).with(dash_class).once
31
- dash_class.send(:include, Hashie::Extensions::Dash::IndifferentAccess)
32
- end
33
- end
34
-
35
- context 'initialized with' do
36
- it 'string' do
37
- instance = DashWithIndifferentAccess.new('name' => 'Name')
38
- expect(instance.name).to eq('Name')
39
- expect(instance['name']).to eq('Name')
40
- expect(instance[:name]).to eq('Name')
41
- expect(instance.inspect).to eq('#<DashWithIndifferentAccess name="Name">')
42
- expect(instance.to_hash).to eq('name' => 'Name')
43
- end
44
-
45
- it 'key' do
46
- instance = DashWithIndifferentAccess.new(name: 'Name')
47
- expect(instance.name).to eq('Name')
48
- expect(instance['name']).to eq('Name')
49
- expect(instance[:name]).to eq('Name')
50
- expect(instance.inspect).to eq('#<DashWithIndifferentAccess name="Name">')
51
- expect(instance.to_hash).to eq('name' => 'Name')
52
- end
53
- end
54
-
55
- it 'updates' do
56
- instance = DashWithIndifferentAccess.new
57
- instance['name'] = 'Updated String'
58
- expect(instance.name).to eq('Updated String')
59
- instance[:name] = 'Updated Symbol'
60
- expect(instance.name).to eq('Updated Symbol')
61
- instance.name = 'Updated Method'
62
- expect(instance.name).to eq('Updated Method')
63
- end
64
-
65
- context 'initialized with both prefers last assignment' do
66
- it 'string, then symbol' do
67
- instance = DashWithIndifferentAccess.new('name' => 'First', name: 'Last')
68
- expect(instance.name).to eq('Last')
69
- expect(instance['name']).to eq('Last')
70
- expect(instance[:name]).to eq('Last')
71
- expect(instance.inspect).to eq('#<DashWithIndifferentAccess name="Last">')
72
- expect(instance.to_hash).to eq('name' => 'Last')
73
- end
74
-
75
- it 'symbol then string' do
76
- instance = DashWithIndifferentAccess.new(name: 'Last', 'name' => 'First')
77
- expect(instance.name).to eq('First')
78
- expect(instance['name']).to eq('First')
79
- expect(instance[:name]).to eq('First')
80
- expect(instance.inspect).to eq('#<DashWithIndifferentAccess name="First">')
81
- expect(instance.to_hash).to eq('name' => 'First')
82
- end
83
- end
84
- end
@@ -1,97 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Hashie
4
- module Extensions
5
- describe DeepFetch do
6
- subject { Class.new(Hash) { include Hashie::Extensions::DeepFetch } }
7
- let(:hash) do
8
- {
9
- library: {
10
- books: [
11
- { title: 'Call of the Wild' },
12
- { title: 'Moby Dick' }
13
- ],
14
- shelves: nil,
15
- location: {
16
- address: '123 Library St.'
17
- }
18
- }
19
- }
20
- end
21
- let(:instance) { subject.new.update(hash) }
22
-
23
- describe '#deep_fetch' do
24
- it 'extracts a value from a nested hash' do
25
- expect(instance.deep_fetch(:library, :location, :address)).to eq('123 Library St.')
26
- end
27
-
28
- it 'extracts a value from a nested array' do
29
- expect(instance.deep_fetch(:library, :books, 1, :title)).to eq('Moby Dick')
30
- end
31
-
32
- context 'when one of the keys is not present' do
33
- context 'when a block is provided' do
34
- it 'returns the value of the block' do
35
- value = instance.deep_fetch(:library, :unknown_key, :location) { 'block value' }
36
- expect(value).to eq('block value')
37
- end
38
- end
39
-
40
- context 'when a block is not provided' do
41
- context 'when the nested object is an array' do
42
- it 'raises an UndefinedPathError' do
43
- expect do
44
- instance.deep_fetch(:library, :books, 2)
45
- end.to(
46
- raise_error(
47
- DeepFetch::UndefinedPathError,
48
- 'Could not fetch path (library > books > 2) at 2'
49
- )
50
- )
51
- end
52
- end
53
-
54
- context 'when the nested object is a hash' do
55
- it 'raises a UndefinedPathError' do
56
- expect do
57
- instance.deep_fetch(:library, :location, :unknown_key)
58
- end.to(
59
- raise_error(
60
- DeepFetch::UndefinedPathError,
61
- 'Could not fetch path (library > location > unknown_key) at unknown_key'
62
- )
63
- )
64
- end
65
- end
66
-
67
- context 'when the nested object is missing' do
68
- it 'raises an UndefinedPathError' do
69
- expect do
70
- instance.deep_fetch(:library, :unknown_key, :books)
71
- end.to(
72
- raise_error(
73
- DeepFetch::UndefinedPathError,
74
- 'Could not fetch path (library > unknown_key > books) at unknown_key'
75
- )
76
- )
77
- end
78
- end
79
-
80
- context 'when the nested object is nil' do
81
- it 'raises an UndefinedPathError' do
82
- expect do
83
- instance.deep_fetch(:library, :shelves, :address)
84
- end.to(
85
- raise_error(
86
- DeepFetch::UndefinedPathError,
87
- 'Could not fetch path (library > shelves > address) at address'
88
- )
89
- )
90
- end
91
- end
92
- end
93
- end
94
- end
95
- end
96
- end
97
- end
@@ -1,138 +0,0 @@
1
- require 'spec_helper'
2
- require 'active_support/core_ext/hash/indifferent_access'
3
-
4
- describe Hashie::Extensions::DeepFind do
5
- subject { Class.new(Hash) { include Hashie::Extensions::DeepFind } }
6
- let(:hash) do
7
- {
8
- library: {
9
- books: [
10
- { title: 'Call of the Wild' },
11
- { title: 'Moby Dick' }
12
- ],
13
- shelves: nil,
14
- location: {
15
- address: '123 Library St.',
16
- title: 'Main Library'
17
- }
18
- }
19
- }
20
- end
21
- let(:instance) { subject.new.update(hash) }
22
-
23
- describe '#deep_find' do
24
- it 'detects a value from a nested hash' do
25
- expect(instance.deep_find(:address)).to eq('123 Library St.')
26
- end
27
-
28
- it 'detects a value from a nested array' do
29
- expect(instance.deep_find(:title)).to eq('Call of the Wild')
30
- end
31
-
32
- it 'returns nil if it does not find a match' do
33
- expect(instance.deep_find(:wahoo)).to be_nil
34
- end
35
- end
36
-
37
- describe '#deep_find_all' do
38
- it 'detects all values from a nested hash' do
39
- expect(instance.deep_find_all(:title)).to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
40
- end
41
-
42
- it 'returns nil if it does not find any matches' do
43
- expect(instance.deep_find_all(:wahoo)).to be_nil
44
- end
45
-
46
- context 'when match value is hash itself' do
47
- let(:hash) do
48
- {
49
- title: {
50
- type: :string
51
- },
52
- library: {
53
- books: [
54
- { title: 'Call of the Wild' },
55
- { title: 'Moby Dick' }
56
- ],
57
- shelves: nil,
58
- location: {
59
- address: '123 Library St.',
60
- title: 'Main Library'
61
- }
62
- }
63
- }
64
- end
65
-
66
- it 'detects all values from a nested hash' do
67
- expect(instance.deep_find_all(:title)).to eq([{ type: :string }, 'Call of the Wild', 'Moby Dick', 'Main Library'])
68
- end
69
- end
70
- end
71
-
72
- context 'on an ActiveSupport::HashWithIndifferentAccess' do
73
- subject(:instance) { hash.with_indifferent_access.extend(Hashie::Extensions::DeepFind) }
74
-
75
- describe '#deep_find' do
76
- it 'indifferently detects a value from a nested hash' do
77
- expect(instance.deep_find(:address)).to eq('123 Library St.')
78
- expect(instance.deep_find('address')).to eq('123 Library St.')
79
- end
80
-
81
- it 'indifferently detects a value from a nested array' do
82
- expect(instance.deep_find(:title)).to eq('Call of the Wild')
83
- expect(instance.deep_find('title')).to eq('Call of the Wild')
84
- end
85
-
86
- it 'indifferently returns nil if it does not find a match' do
87
- expect(instance.deep_find(:wahoo)).to be_nil
88
- expect(instance.deep_find('wahoo')).to be_nil
89
- end
90
- end
91
-
92
- describe '#deep_find_all' do
93
- it 'indifferently detects all values from a nested hash' do
94
- expect(instance.deep_find_all(:title)).to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
95
- expect(instance.deep_find_all('title')).to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
96
- end
97
-
98
- it 'indifferently returns nil if it does not find any matches' do
99
- expect(instance.deep_find_all(:wahoo)).to be_nil
100
- expect(instance.deep_find_all('wahoo')).to be_nil
101
- end
102
- end
103
- end
104
-
105
- context 'on a Hash including Hashie::Extensions::IndifferentAccess' do
106
- let(:klass) { Class.new(Hash) { include Hashie::Extensions::IndifferentAccess } }
107
- subject(:instance) { klass[hash.dup].extend(Hashie::Extensions::DeepFind) }
108
-
109
- describe '#deep_find' do
110
- it 'indifferently detects a value from a nested hash' do
111
- expect(instance.deep_find(:address)).to eq('123 Library St.')
112
- expect(instance.deep_find('address')).to eq('123 Library St.')
113
- end
114
-
115
- it 'indifferently detects a value from a nested array' do
116
- expect(instance.deep_find(:title)).to eq('Call of the Wild')
117
- expect(instance.deep_find('title')).to eq('Call of the Wild')
118
- end
119
-
120
- it 'indifferently returns nil if it does not find a match' do
121
- expect(instance.deep_find(:wahoo)).to be_nil
122
- expect(instance.deep_find('wahoo')).to be_nil
123
- end
124
- end
125
-
126
- describe '#deep_find_all' do
127
- it 'indifferently detects all values from a nested hash' do
128
- expect(instance.deep_find_all(:title)).to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
129
- expect(instance.deep_find_all('title')).to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
130
- end
131
-
132
- it 'indifferently returns nil if it does not find any matches' do
133
- expect(instance.deep_find_all(:wahoo)).to be_nil
134
- expect(instance.deep_find_all('wahoo')).to be_nil
135
- end
136
- end
137
- end
138
- end
@@ -1,137 +0,0 @@
1
- require 'spec_helper'
2
- require 'active_support/core_ext/hash/indifferent_access'
3
-
4
- describe Hashie::Extensions::DeepLocate do
5
- let(:hash) do
6
- {
7
- from: 0,
8
- size: 25,
9
- query: {
10
- bool: {
11
- must: [
12
- {
13
- query_string: {
14
- query: 'foobar',
15
- default_operator: 'AND',
16
- fields: [
17
- 'title^2',
18
- '_all'
19
- ]
20
- }
21
- },
22
- {
23
- match: {
24
- field_1: 'value_1'
25
- }
26
- },
27
- {
28
- range: {
29
- lsr09: {
30
- gte: 2014
31
- }
32
- }
33
- }
34
- ],
35
- should: [
36
- {
37
- match: {
38
- field_2: 'value_2'
39
- }
40
- }
41
- ],
42
- must_not: [
43
- {
44
- range: {
45
- lsr10: {
46
- gte: 2014
47
- }
48
- }
49
- }
50
- ]
51
- }
52
- }
53
- }
54
- end
55
-
56
- describe '.deep_locate' do
57
- context 'if called with a non-callable comparator' do
58
- it 'creates a key comparator on-th-fly' do
59
- expect(described_class.deep_locate(:lsr10, hash)).to eq([hash[:query][:bool][:must_not][0][:range]])
60
- end
61
- end
62
-
63
- it 'locates enumerables for which the given comparator returns true for at least one element' do
64
- examples = [
65
- [
66
- ->(key, _value, _object) { key == :fields },
67
- [
68
- hash[:query][:bool][:must].first[:query_string]
69
- ]
70
- ],
71
- [
72
- ->(_key, value, _object) { value.is_a?(String) && value.include?('value') },
73
- [
74
- hash[:query][:bool][:must][1][:match],
75
- hash[:query][:bool][:should][0][:match]
76
- ]
77
- ],
78
- [
79
- lambda do |_key, _value, object|
80
- object.is_a?(Array) &&
81
- !object.extend(described_class).deep_locate(:match).empty?
82
- end,
83
- [
84
- hash[:query][:bool][:must],
85
- hash[:query][:bool][:should]
86
- ]
87
- ]
88
- ]
89
-
90
- examples.each do |comparator, expected_result|
91
- expect(described_class.deep_locate(comparator, hash)).to eq(expected_result)
92
- end
93
- end
94
-
95
- it 'returns an empty array if nothing was found' do
96
- expect(described_class.deep_locate(:muff, foo: 'bar')).to eq([])
97
- end
98
- end
99
-
100
- context 'if extending an existing object' do
101
- let(:extended_hash) do
102
- hash.extend(described_class)
103
- end
104
-
105
- it 'adds #deep_locate' do
106
- expect(extended_hash.deep_locate(:bool)).to eq([hash[:query]])
107
- end
108
- end
109
-
110
- context 'if included in a hash' do
111
- let(:derived_hash_with_extension_included) do
112
- Class.new(Hash) do
113
- include Hashie::Extensions::DeepLocate
114
- end
115
- end
116
-
117
- let(:instance) do
118
- derived_hash_with_extension_included.new.update(hash)
119
- end
120
-
121
- it 'adds #deep_locate' do
122
- expect(instance.deep_locate(:bool)).to eq([hash[:query]])
123
- end
124
- end
125
-
126
- context 'on an ActiveSupport::HashWithIndifferentAccess' do
127
- let(:instance) { hash.dup.with_indifferent_access }
128
-
129
- it 'can locate symbolic keys' do
130
- expect(described_class.deep_locate(:lsr10, instance)).to eq ['lsr10' => { 'gte' => 2014 }]
131
- end
132
-
133
- it 'can locate string keys' do
134
- expect(described_class.deep_locate('lsr10', instance)).to eq ['lsr10' => { 'gte' => 2014 }]
135
- end
136
- end
137
- end