hashie 3.5.7 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +240 -164
  3. data/CONTRIBUTING.md +13 -6
  4. data/README.md +210 -29
  5. data/Rakefile +2 -2
  6. data/UPGRADING.md +83 -7
  7. data/hashie.gemspec +13 -7
  8. data/lib/hashie.rb +21 -19
  9. data/lib/hashie/clash.rb +12 -1
  10. data/lib/hashie/dash.rb +42 -21
  11. data/lib/hashie/extensions/active_support/core_ext/hash.rb +14 -0
  12. data/lib/hashie/extensions/coercion.rb +26 -19
  13. data/lib/hashie/extensions/dash/indifferent_access.rb +20 -1
  14. data/lib/hashie/extensions/dash/property_translation.rb +54 -28
  15. data/lib/hashie/extensions/deep_fetch.rb +5 -3
  16. data/lib/hashie/extensions/deep_find.rb +14 -5
  17. data/lib/hashie/extensions/deep_locate.rb +22 -8
  18. data/lib/hashie/extensions/deep_merge.rb +26 -10
  19. data/lib/hashie/extensions/indifferent_access.rb +8 -8
  20. data/lib/hashie/extensions/key_conflict_warning.rb +55 -0
  21. data/lib/hashie/extensions/mash/define_accessors.rb +90 -0
  22. data/lib/hashie/extensions/mash/keep_original_keys.rb +4 -5
  23. data/lib/hashie/extensions/mash/permissive_respond_to.rb +61 -0
  24. data/lib/hashie/extensions/mash/safe_assignment.rb +3 -1
  25. data/lib/hashie/extensions/mash/symbolize_keys.rb +1 -1
  26. data/lib/hashie/extensions/method_access.rb +47 -14
  27. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +28 -4
  28. data/lib/hashie/extensions/ruby_version_check.rb +5 -1
  29. data/lib/hashie/extensions/strict_key_access.rb +16 -13
  30. data/lib/hashie/extensions/stringify_keys.rb +1 -1
  31. data/lib/hashie/extensions/symbolize_keys.rb +1 -1
  32. data/lib/hashie/hash.rb +18 -11
  33. data/lib/hashie/mash.rb +131 -70
  34. data/lib/hashie/railtie.rb +7 -0
  35. data/lib/hashie/rash.rb +6 -6
  36. data/lib/hashie/utils.rb +28 -0
  37. data/lib/hashie/version.rb +1 -1
  38. metadata +19 -128
  39. data/spec/hashie/array_spec.rb +0 -29
  40. data/spec/hashie/clash_spec.rb +0 -70
  41. data/spec/hashie/dash_spec.rb +0 -573
  42. data/spec/hashie/extensions/autoload_spec.rb +0 -24
  43. data/spec/hashie/extensions/coercion_spec.rb +0 -631
  44. data/spec/hashie/extensions/dash/coercion_spec.rb +0 -13
  45. data/spec/hashie/extensions/dash/indifferent_access_spec.rb +0 -84
  46. data/spec/hashie/extensions/deep_fetch_spec.rb +0 -97
  47. data/spec/hashie/extensions/deep_find_spec.rb +0 -138
  48. data/spec/hashie/extensions/deep_locate_spec.rb +0 -137
  49. data/spec/hashie/extensions/deep_merge_spec.rb +0 -70
  50. data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -47
  51. data/spec/hashie/extensions/indifferent_access_spec.rb +0 -282
  52. data/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb +0 -208
  53. data/spec/hashie/extensions/key_conversion_spec.rb +0 -12
  54. data/spec/hashie/extensions/mash/keep_original_keys_spec.rb +0 -46
  55. data/spec/hashie/extensions/mash/safe_assignment_spec.rb +0 -50
  56. data/spec/hashie/extensions/mash/symbolize_keys_spec.rb +0 -39
  57. data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
  58. data/spec/hashie/extensions/method_access_spec.rb +0 -188
  59. data/spec/hashie/extensions/strict_key_access_spec.rb +0 -110
  60. data/spec/hashie/extensions/stringify_keys_spec.rb +0 -124
  61. data/spec/hashie/extensions/symbolize_keys_spec.rb +0 -129
  62. data/spec/hashie/hash_spec.rb +0 -84
  63. data/spec/hashie/mash_spec.rb +0 -763
  64. data/spec/hashie/parsers/yaml_erb_parser_spec.rb +0 -46
  65. data/spec/hashie/rash_spec.rb +0 -83
  66. data/spec/hashie/trash_spec.rb +0 -268
  67. data/spec/hashie/utils_spec.rb +0 -25
  68. data/spec/hashie/version_spec.rb +0 -7
  69. data/spec/hashie_spec.rb +0 -13
  70. data/spec/integration/omniauth-oauth2/app.rb +0 -53
  71. data/spec/integration/omniauth-oauth2/integration_spec.rb +0 -26
  72. data/spec/integration/omniauth-oauth2/some_site.rb +0 -38
  73. data/spec/integration/omniauth/app.rb +0 -11
  74. data/spec/integration/omniauth/integration_spec.rb +0 -38
  75. data/spec/integration/rails-without-dependency/integration_spec.rb +0 -15
  76. data/spec/integration/rails/app.rb +0 -48
  77. data/spec/integration/rails/integration_spec.rb +0 -26
  78. data/spec/spec_helper.rb +0 -23
  79. data/spec/support/integration_specs.rb +0 -36
  80. data/spec/support/logger.rb +0 -24
  81. data/spec/support/module_context.rb +0 -11
  82. 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