bidu-core_ext 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,83 @@
1
+ class Hash::DeepHashConstructor
2
+ attr_accessor :separator
3
+
4
+ def initialize(separator)
5
+ @separator = separator
6
+ end
7
+
8
+ def deep_hash(object)
9
+ if object.is_a? Array
10
+ array_deep_hash(object)
11
+ elsif object.is_a? Hash
12
+ hash_deep_hash(object)
13
+ else
14
+ object
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def array_deep_hash(array)
21
+ array.map { |v| v.is_a?(Hash) ? deep_hash(v) : v }
22
+ end
23
+
24
+ def hash_deep_hash(hash)
25
+ {}.tap do |new_hash|
26
+ hash.each do |k, v|
27
+ base_key, child_key = split_key(k, separator)
28
+ set_deep_hash_positioned_value(new_hash, base_key, v, child_key)
29
+ end
30
+
31
+ new_hash.each do |k, v|
32
+ new_hash[k] = deep_hash(v)
33
+ end
34
+ end
35
+ end
36
+
37
+ def split_key(key, separator)
38
+ separator_rxp = (separator == '.') ? "\\#{separator}" : separator
39
+ skipper = "[^#{separator}]"
40
+ regexp = Regexp.new("^(#{skipper}*)#{separator_rxp}(.*)")
41
+ match = key.match(regexp)
42
+
43
+ match ? match[1..2] : key
44
+ end
45
+
46
+ def set_deep_hash_array_value(hash, base_key, index, value, key = nil)
47
+ key_without_index = base_key.gsub("[#{index}]", '')
48
+ hash[key_without_index] ||= []
49
+
50
+ if key.nil?
51
+ hash[key_without_index][index] = value
52
+ else
53
+ hash[key_without_index][index] ||= {}
54
+ hash[key_without_index][index][key] = value
55
+ end
56
+ end
57
+
58
+ def set_deep_hash_positioned_value(new_hash, base_key, v, child_key)
59
+ index = array_index(base_key)
60
+
61
+ if index
62
+ set_deep_hash_array_value(new_hash, base_key, index, v, child_key)
63
+ else
64
+ set_deep_hash_value(new_hash, base_key, v, child_key)
65
+ end
66
+ end
67
+
68
+ def array_index(key)
69
+ match = key.match(/\[([^)]+)\]/)
70
+ if match
71
+ match[1].to_i
72
+ end
73
+ end
74
+
75
+ def set_deep_hash_value(hash, base_key, value, key = nil)
76
+ if key.nil?
77
+ hash[base_key] = value
78
+ else
79
+ hash[base_key] ||= {}
80
+ hash[base_key][key] = value
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,68 @@
1
+ class Hash::KeyChanger
2
+ attr_reader :hash, :block
3
+
4
+ def initialize(hash)
5
+ @hash = hash
6
+ end
7
+
8
+ def change_keys(settings = {}, &block)
9
+ merge_options({
10
+ recursive: true
11
+ }, settings)
12
+
13
+ if options[:recursive]
14
+ hash.deep_transform_keys!(&block)
15
+ else
16
+ hash.transform_keys!(&block)
17
+ end
18
+ end
19
+
20
+ def camelize_keys(settings = {})
21
+ merge_options({
22
+ uppercase_first_letter: true
23
+ }, settings)
24
+
25
+ type = options[:uppercase_first_letter] ? :upper : :lower
26
+
27
+ change_keys do |k|
28
+ k.camelize(type)
29
+ end
30
+ end
31
+
32
+ def change_text(options = {}, &block)
33
+ merge_options({
34
+ type: :keep
35
+ }, options)
36
+
37
+ change_keys do |key|
38
+ cast_new_key block.call(key), key.class
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def merge_options(default, custom)
45
+ @options = {}.merge!(default).merge!(custom).merge!(options)
46
+ end
47
+
48
+ def options
49
+ @options ||= {}
50
+ end
51
+
52
+ def cast_new_key(key, old_clazz)
53
+ case class_cast(old_clazz)
54
+ when :symbol then
55
+ key.to_sym
56
+ when :string then
57
+ key.to_s
58
+ end
59
+ end
60
+
61
+ def keep_class?
62
+ options[:type] == :keep
63
+ end
64
+
65
+ def class_cast(old_clazz)
66
+ keep_class? && old_clazz.to_s.downcase.to_sym || options[:type]
67
+ end
68
+ end
@@ -0,0 +1,52 @@
1
+ class Hash::ValueChanger
2
+ attr_accessor :options, :block
3
+
4
+ def initialize(options, &block)
5
+ @options = {
6
+ recursive: true,
7
+ skip_inner: true
8
+ }.merge(options)
9
+
10
+ @block = block
11
+ end
12
+
13
+ def change(object)
14
+ if object.is_a? Hash
15
+ change_hash(object)
16
+ elsif object.is_a? Array
17
+ change_array(object)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def change_hash(original_hash)
24
+ original_hash.tap do |hash|
25
+ original_hash.each do |key, value|
26
+ value = new_value(value)
27
+ hash[key] = value
28
+ end
29
+ end
30
+ end
31
+
32
+ def change_array(array)
33
+ array.each.with_index do |value, index|
34
+ value = value.change_values(options, &block) if value.is_a? Hash
35
+ value = change_array(value) if value.is_a? Array
36
+ array[index] = value
37
+ end
38
+ end
39
+
40
+ def change_value?(value)
41
+ !(value.is_a?(Hash) || value.is_a?(Array)) || !options[:skip_inner]
42
+ end
43
+
44
+ def new_value(value)
45
+ value = block.call(value) if change_value?(value)
46
+ apply_recursion?(value) ? change(value) : value
47
+ end
48
+
49
+ def apply_recursion?(value)
50
+ (value.is_a?(Hash) || value.is_a?(Array)) && options[:recursive]
51
+ end
52
+ end
data/lib/numeric.rb ADDED
@@ -0,0 +1,6 @@
1
+ class Numeric
2
+ def percent_of(n)
3
+ return Float::INFINITY if n == 0
4
+ (to_f / n.to_f) * 100.0
5
+ end
6
+ end
data/lib/symbol.rb ADDED
@@ -0,0 +1,5 @@
1
+ class Symbol
2
+ def camelize(type = :upper)
3
+ to_s.camelize(type).to_sym
4
+ end
5
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe Array do
4
+ describe '#chain_map' do
5
+ let(:array) { [ :a, :long_name, :sym ] }
6
+ let(:mapped) { array.chain_map(:to_s, :size, :to_s) }
7
+
8
+ it 'calls each argument as method of the mapped result' do
9
+ expect(mapped).to eq([ '1', '9', '3' ])
10
+ end
11
+
12
+ context 'when an extra block is given' do
13
+ let(:mapped) do
14
+ array.chain_map(:to_s, :size) do |v|
15
+ "final: #{v}"
16
+ end
17
+ end
18
+
19
+ it 'calls each argument as method of the mapped result' do
20
+ expect(mapped).to eq([ 'final: 1', 'final: 9', 'final: 3' ])
21
+ end
22
+ end
23
+ end
24
+
25
+ describe '#as_hash' do
26
+ let(:array) { [1, 2, 3] }
27
+ let(:keys) { %w(a b c) }
28
+ let(:expected) { { 'a' => 1, 'b' => 2, 'c' => 3 } }
29
+
30
+ it 'creates a hash using the array as value and the argument as keys' do
31
+ expect(array.as_hash(keys)).to eq(expected)
32
+ end
33
+
34
+ context 'when there are more keys than values' do
35
+ let(:keys) { %w(a b c d e f) }
36
+ let(:expected) { { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => nil, 'e' => nil, 'f' => nil } }
37
+
38
+ it 'creates a hash with nil values for the extra keys' do
39
+ expect(array.as_hash(keys)).to eq(expected)
40
+ end
41
+
42
+ it { expect { array.as_hash(keys) }.not_to change { keys } }
43
+ it { expect { array.as_hash(keys) }.not_to change { array } }
44
+ end
45
+
46
+ context 'when there are more values than keys' do
47
+ let(:array) { [1, 2, 3, 4, 5, 6, 7] }
48
+
49
+ it { expect { array.as_hash(keys) }.to raise_error(IndexError) }
50
+
51
+ it { expect { array.as_hash(keys) rescue nil }.not_to change { keys } }
52
+ it { expect { array.as_hash(keys) rescue nil }.not_to change { array } }
53
+ end
54
+ end
55
+
56
+ describe '#map_and_find' do
57
+ let(:array) { [1, 2, 3, 4] }
58
+ let(:value) { array.map_and_find(&block) }
59
+
60
+ context 'when block returns nil' do
61
+ let(:block) { proc {} }
62
+ it { expect(value).to be_nil }
63
+ end
64
+
65
+ context 'when block returns false' do
66
+ let(:block) { proc { false } }
67
+ it { expect(value).to be_nil }
68
+ end
69
+
70
+ context 'when block returns a true evaluated value' do
71
+ let(:block) { proc(&:to_s) }
72
+
73
+ it { expect(value).to eq('1') }
74
+
75
+ context 'but not for the first value' do
76
+ let(:transformer) { double(:transformer) }
77
+ let(:block) { proc { |v| transformer.transform(v) } }
78
+
79
+ before do
80
+ allow(transformer).to receive(:transform) do |v|
81
+ v.to_s if v > 1
82
+ end
83
+ value
84
+ end
85
+
86
+ it { expect(value).to eq('2') }
87
+ it 'calls the mapping only until it returns a valid value' do
88
+ expect(transformer).to have_received(:transform).exactly(2)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,31 @@
1
+ describe Enumerable do
2
+ describe '#clean!' do
3
+ it_behaves_like 'an array clean method', :clean!
4
+ it_behaves_like 'a hash clean method', :clean!
5
+
6
+ it 'changes the original hash' do
7
+ hash = { a: nil}
8
+ expect { hash.clean! }.to change { hash }
9
+ end
10
+
11
+ it 'changes original array' do
12
+ array = [{ a: nil}]
13
+ expect { array.clean! }.to change { array }
14
+ end
15
+ end
16
+
17
+ describe '#clean' do
18
+ it_behaves_like 'an array clean method', :clean
19
+ it_behaves_like 'a hash clean method', :clean
20
+
21
+ it 'does not change the original hash' do
22
+ hash = { a: nil}
23
+ expect { hash.clean }.not_to change { hash }
24
+ end
25
+
26
+ it 'does not change the original array' do
27
+ array = [{ a: nil}]
28
+ expect { array.clean }.not_to change { array }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hash::DeepHashConstructor do
4
+ let(:subject) { described_class.new('.') }
5
+ let(:deep_hash) { subject.deep_hash(hash) }
6
+
7
+ describe '.deep_hash' do
8
+ let(:hash) do
9
+ {
10
+ 'person.name' => 'Some name',
11
+ 'person.age' => 22,
12
+ 'status' => :success,
13
+ 'vehicle.fuel' => 'GASOLINE',
14
+ 'vehicle.doors' => 4
15
+ }
16
+ end
17
+
18
+ let(:expected) do
19
+ {
20
+ 'person' => { 'name' => 'Some name', 'age' => 22 },
21
+ 'vehicle' => { 'fuel' => 'GASOLINE', 'doors' => 4 },
22
+ 'status' => :success
23
+ }
24
+ end
25
+
26
+ it 'build a deep hash' do
27
+ expect(deep_hash).to eq(expected)
28
+ end
29
+
30
+ context 'with indexed keys' do
31
+ let(:hash) do
32
+ {
33
+ 'person[0].name' => 'First person',
34
+ 'person[0].age' => 22,
35
+ 'person[1].name' => 'Second person',
36
+ 'person[1].age' => 27,
37
+ 'device[0]' => 'GEAR_LOCK',
38
+ 'device[1]' => 'GPS',
39
+ 'zipCode' => '122345-123'
40
+ }
41
+ end
42
+
43
+ let(:expected) do
44
+ {
45
+ 'person' => [
46
+ { 'name' => 'First person', 'age' => 22 },
47
+ { 'name' => 'Second person', 'age' => 27 }
48
+ ],
49
+ 'device' => %w(GEAR_LOCK GPS),
50
+ 'zipCode' => '122345-123'
51
+ }
52
+ end
53
+
54
+ it 'build a deep hash with arrays' do
55
+ expect(deep_hash).to eq(expected)
56
+ end
57
+ end
58
+
59
+ context 'with a n level hash' do
60
+ let(:hash) do
61
+ {
62
+ 'quote_request.personal.person.name' => 'Some name',
63
+ 'quote_request.personal.person.age' => 22,
64
+ 'quote_request.insurance.vehicle.fuel' => 'GASOLINE',
65
+ 'quote_request.insurance.vehicle.doors' => 4,
66
+ 'request.status' => :success,
67
+ 'trials' => 3
68
+ }
69
+ end
70
+
71
+ let(:expected) do
72
+ {
73
+ 'quote_request' => {
74
+ 'personal' => {
75
+ 'person' => { 'name' => 'Some name', 'age' => 22 }
76
+ },
77
+ 'insurance' => {
78
+ 'vehicle' => { 'fuel' => 'GASOLINE', 'doors' => 4 }
79
+ }
80
+ },
81
+ 'request' => { 'status' => :success },
82
+ 'trials' => 3
83
+ }
84
+ end
85
+
86
+ it 'build a deep hash with arrays' do
87
+ expect(deep_hash).to eq(expected)
88
+ end
89
+ end
90
+
91
+ context 'with a n level hash and arrays' do
92
+ let(:hash) do
93
+ {
94
+ 'quote_request.personal.person[0].name' => 'Some name 1',
95
+ 'quote_request.personal.person[0].age' => 22,
96
+ 'quote_request.personal.person[1].name' => 'Some name 2',
97
+ 'quote_request.personal.person[1].age' => 23,
98
+ 'request[0].status.clazz' => String,
99
+ 'request[1].status.clazz' => Fixnum,
100
+ 'request[2].status.clazz' => Date,
101
+ 'trials' => 3
102
+ }
103
+ end
104
+
105
+ let(:expected) do
106
+ {
107
+ 'quote_request' => {
108
+ 'personal' => {
109
+ 'person' => [
110
+ { 'name' => 'Some name 1', 'age' => 22 },
111
+ { 'name' => 'Some name 2', 'age' => 23 }
112
+ ]
113
+ }
114
+ },
115
+ 'request' => [
116
+ { 'status' => { 'clazz' => String } },
117
+ { 'status' => { 'clazz' => Fixnum } },
118
+ { 'status' => { 'clazz' => Date } }
119
+ ],
120
+ 'trials' => 3
121
+ }
122
+ end
123
+
124
+ it 'build a deep hash with arrays' do
125
+ expect(deep_hash).to eq(expected)
126
+ end
127
+ end
128
+
129
+ context 'with custom separator' do
130
+ let(:subject) { described_class.new('_') }
131
+ let(:hash) do
132
+ {
133
+ 'person_name' => 'Some name',
134
+ 'person_age' => 22,
135
+ 'status' => :success,
136
+ 'vehicle_fuel' => 'GASOLINE',
137
+ 'vehicle_doors' => 4
138
+ }
139
+ end
140
+
141
+ it 'build a deep hash with arrays' do
142
+ expect(deep_hash).to eq(expected)
143
+ end
144
+ end
145
+
146
+ context 'with custom separator on n level deep hash' do
147
+ let(:subject) { described_class.new('_') }
148
+ let(:hash) do
149
+ {
150
+ 'person_name_clazz' => String
151
+ }
152
+ end
153
+
154
+ let(:expected) do
155
+ {
156
+ 'person' => {
157
+ 'name' => { 'clazz' => String }
158
+ }
159
+ }
160
+ end
161
+
162
+ it 'build a deep hash with arrays' do
163
+ expect(deep_hash).to eq(expected)
164
+ end
165
+ end
166
+ end
167
+ end