larrow-runner 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 556f8afefdbfa12b93f7c7f3029fcc5bf55b23f9
4
- data.tar.gz: 170901120bc4eeec5370639ae6bb00c33c6ebf14
3
+ metadata.gz: 649d405f97c00eba29947141c7c23f97da18b783
4
+ data.tar.gz: ab1fe09db103b64cb52fcc1003cf3ba3e4e0faa4
5
5
  SHA512:
6
- metadata.gz: 2b9e24d845add9b4f3e99cb968e5146541f2e8aa025fc20dc97df7aac5d2a85286e318fe10cec77c1dafdf02f95749280eefe55a1183fda70ccf016f8b105789
7
- data.tar.gz: 3597ae6bd3f599e1edb2b645fd9b752d46caf9a2b5c865303d1a7b8a10588e71e873b460e056dfca6d10349e8430e8485caecd2a2279dddcdbbd7bc95d11dff9
6
+ metadata.gz: 0ca859ab0d4d0f713a0a3e86d0340ca1b91d76e941acfe97e4318ead3e09fe9347d99f65118a357629be6a045edbc907e23ac9055da03c0eb10933765524dc23
7
+ data.tar.gz: f85aaf2b37ddfd2b8e0ef2daed67bbc591b3c8b75343efbf0b8f1c8105f30505d678e9b4dbcb8170fe3df1e887b55ee73d5a0c96ae3f46f8277519039163e8bc
data/README.md CHANGED
@@ -40,27 +40,26 @@ Congratulation! Now you can use larrow to help your develop works.
40
40
 
41
41
  ### testing
42
42
 
43
- unit test, integration test, system test, etc.)
43
+ Unit test, integration test, system test, etc.
44
44
  ```
45
45
  $ larrow go <source_url>
46
46
  ```
47
47
 
48
48
  ### application startup
49
49
 
50
- make a standalone application and start it(if necessary)
50
+ Make a standalone application and start it(if necessary)
51
51
  ```
52
52
  $ larrow build server <source_url>
53
53
  ```
54
54
 
55
55
  ### build image
56
56
 
57
- use image to speed-up your development
57
+ Use image to speed-up your development
58
58
 
59
59
  * build a image of your application
60
60
  ```
61
61
  $ larrow build image <source_url>
62
62
  ```
63
-
64
63
  * build a image from local LarrowFile
65
64
  ```
66
65
  $ larrow build image <larrow_file_path>
@@ -70,7 +69,7 @@ $ larrow build image <larrow_file_path>
70
69
 
71
70
  Larrow need to know how to setup/make/install/start... your application. So developer could write a `Larrow File` to declare these things.
72
71
 
73
- Larrow can be used as a CI worker like travis.
72
+ default larrow file: $source/.larrow.yml
74
73
  ## Contributing
75
74
 
76
75
  1. Fork it ( http://github.com/fsword/larrow-core/fork )
@@ -24,12 +24,9 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "simplecov", '~> 0.9'
25
25
  spec.add_development_dependency "parallel_tests", '~> 1.0'
26
26
 
27
- spec.add_runtime_dependency 'tilt', '~> 2'
28
27
  spec.add_runtime_dependency 'thor', '~> 0.19'
29
- spec.add_runtime_dependency "activesupport", "~> 4.1"
30
28
  spec.add_runtime_dependency "pry", '~> 0.10', '0.10.0'
31
29
  spec.add_runtime_dependency "pry-nav", '~> 0.2', '0.2.4'
32
- spec.add_runtime_dependency "minitest", '~> 5.4', '5.4.1'
33
30
 
34
31
  spec.add_runtime_dependency 'net-ssh', '~> 2.9'
35
32
  spec.add_runtime_dependency 'net-scp', '~> 1.2'
@@ -0,0 +1,7 @@
1
+ require 'active_support/core_ext/hash/compact'
2
+ require 'active_support/core_ext/hash/deep_merge'
3
+ require 'active_support/core_ext/hash/except'
4
+ require 'active_support/core_ext/hash/indifferent_access'
5
+ require 'active_support/core_ext/hash/keys'
6
+ require 'active_support/core_ext/hash/reverse_merge'
7
+ require 'active_support/core_ext/hash/slice'
@@ -0,0 +1,20 @@
1
+ class Hash
2
+ # Returns a hash with non +nil+ values.
3
+ #
4
+ # hash = { a: true, b: false, c: nil}
5
+ # hash.compact # => { a: true, b: false}
6
+ # hash # => { a: true, b: false, c: nil}
7
+ # { c: nil }.compact # => {}
8
+ def compact
9
+ self.select { |_, value| !value.nil? }
10
+ end
11
+
12
+ # Replaces current hash with non +nil+ values.
13
+ #
14
+ # hash = { a: true, b: false, c: nil}
15
+ # hash.compact! # => { a: true, b: false}
16
+ # hash # => { a: true, b: false}
17
+ def compact!
18
+ self.reject! { |_, value| value.nil? }
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ class Hash
2
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
3
+ #
4
+ # h1 = { a: true, b: { c: [1, 2, 3] } }
5
+ # h2 = { a: false, b: { x: [3, 4, 5] } }
6
+ #
7
+ # h1.deep_merge(h2) #=> { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
8
+ #
9
+ # Like with Hash#merge in the standard library, a block can be provided
10
+ # to merge values:
11
+ #
12
+ # h1 = { a: 100, b: 200, c: { c1: 100 } }
13
+ # h2 = { b: 250, c: { c1: 200 } }
14
+ # h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
15
+ # # => { a: 100, b: 450, c: { c1: 300 } }
16
+ def deep_merge(other_hash, &block)
17
+ dup.deep_merge!(other_hash, &block)
18
+ end
19
+
20
+ # Same as +deep_merge+, but modifies +self+.
21
+ def deep_merge!(other_hash, &block)
22
+ other_hash.each_pair do |current_key, other_value|
23
+ this_value = self[current_key]
24
+
25
+ self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
26
+ this_value.deep_merge(other_value, &block)
27
+ else
28
+ if block_given? && key?(current_key)
29
+ block.call(current_key, this_value, other_value)
30
+ else
31
+ other_value
32
+ end
33
+ end
34
+ end
35
+
36
+ self
37
+ end
38
+ end
@@ -0,0 +1,15 @@
1
+ class Hash
2
+ # Returns a hash that includes everything but the given keys. This is useful for
3
+ # limiting a set of parameters to everything but a few known toggles:
4
+ #
5
+ # @person.update(params[:person].except(:admin))
6
+ def except(*keys)
7
+ dup.except!(*keys)
8
+ end
9
+
10
+ # Replaces the hash without the given keys.
11
+ def except!(*keys)
12
+ keys.each { |key| delete(key) }
13
+ self
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ require 'active_support/hash_with_indifferent_access'
2
+
3
+ class Hash
4
+
5
+ # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
6
+ #
7
+ # { a: 1 }.with_indifferent_access['a'] # => 1
8
+ def with_indifferent_access
9
+ ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
10
+ end
11
+
12
+ # Called when object is nested under an object that receives
13
+ # #with_indifferent_access. This method will be called on the current object
14
+ # by the enclosing object and is aliased to #with_indifferent_access by
15
+ # default. Subclasses of Hash may overwrite this method to return +self+ if
16
+ # converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
17
+ # desirable.
18
+ #
19
+ # b = { b: 1 }
20
+ # { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
21
+ # # => {"b"=>32}
22
+ alias nested_under_indifferent_access with_indifferent_access
23
+ end
@@ -0,0 +1,162 @@
1
+ class Hash
2
+ # Returns a new hash with all keys converted using the block operation.
3
+ #
4
+ # hash = { name: 'Rob', age: '28' }
5
+ #
6
+ # hash.transform_keys{ |key| key.to_s.upcase }
7
+ # # => {"NAME"=>"Rob", "AGE"=>"28"}
8
+ def transform_keys
9
+ result = {}
10
+ each_key do |key|
11
+ result[yield(key)] = self[key]
12
+ end
13
+ result
14
+ end
15
+
16
+ # Destructively convert all keys using the block operations.
17
+ # Same as transform_keys but modifies +self+.
18
+ def transform_keys!
19
+ keys.each do |key|
20
+ self[yield(key)] = delete(key)
21
+ end
22
+ self
23
+ end
24
+
25
+ # Returns a new hash with all keys converted to strings.
26
+ #
27
+ # hash = { name: 'Rob', age: '28' }
28
+ #
29
+ # hash.stringify_keys
30
+ # # => { "name" => "Rob", "age" => "28" }
31
+ def stringify_keys
32
+ transform_keys{ |key| key.to_s }
33
+ end
34
+
35
+ # Destructively convert all keys to strings. Same as
36
+ # +stringify_keys+, but modifies +self+.
37
+ def stringify_keys!
38
+ transform_keys!{ |key| key.to_s }
39
+ end
40
+
41
+ # Returns a new hash with all keys converted to symbols, as long as
42
+ # they respond to +to_sym+.
43
+ #
44
+ # hash = { 'name' => 'Rob', 'age' => '28' }
45
+ #
46
+ # hash.symbolize_keys
47
+ # # => { name: "Rob", age: "28" }
48
+ def symbolize_keys
49
+ transform_keys{ |key| key.to_sym rescue key }
50
+ end
51
+ alias_method :to_options, :symbolize_keys
52
+
53
+ # Destructively convert all keys to symbols, as long as they respond
54
+ # to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
55
+ def symbolize_keys!
56
+ transform_keys!{ |key| key.to_sym rescue key }
57
+ end
58
+ alias_method :to_options!, :symbolize_keys!
59
+
60
+ # Validate all keys in a hash match <tt>*valid_keys</tt>, raising ArgumentError
61
+ # on a mismatch. Note that keys are NOT treated indifferently, meaning if you
62
+ # use strings for keys but assert symbols as keys, this will fail.
63
+ #
64
+ # { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age"
65
+ # { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'"
66
+ # { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
67
+ def assert_valid_keys(*valid_keys)
68
+ valid_keys.flatten!
69
+ each_key do |k|
70
+ unless valid_keys.include?(k)
71
+ raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}")
72
+ end
73
+ end
74
+ end
75
+
76
+ # Returns a new hash with all keys converted by the block operation.
77
+ # This includes the keys from the root hash and from all
78
+ # nested hashes and arrays.
79
+ #
80
+ # hash = { person: { name: 'Rob', age: '28' } }
81
+ #
82
+ # hash.deep_transform_keys{ |key| key.to_s.upcase }
83
+ # # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
84
+ def deep_transform_keys(&block)
85
+ _deep_transform_keys_in_object(self, &block)
86
+ end
87
+
88
+ # Destructively convert all keys by using the block operation.
89
+ # This includes the keys from the root hash and from all
90
+ # nested hashes and arrays.
91
+ def deep_transform_keys!(&block)
92
+ _deep_transform_keys_in_object!(self, &block)
93
+ end
94
+
95
+ # Returns a new hash with all keys converted to strings.
96
+ # This includes the keys from the root hash and from all
97
+ # nested hashes and arrays.
98
+ #
99
+ # hash = { person: { name: 'Rob', age: '28' } }
100
+ #
101
+ # hash.deep_stringify_keys
102
+ # # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
103
+ def deep_stringify_keys
104
+ deep_transform_keys{ |key| key.to_s }
105
+ end
106
+
107
+ # Destructively convert all keys to strings.
108
+ # This includes the keys from the root hash and from all
109
+ # nested hashes and arrays.
110
+ def deep_stringify_keys!
111
+ deep_transform_keys!{ |key| key.to_s }
112
+ end
113
+
114
+ # Returns a new hash with all keys converted to symbols, as long as
115
+ # they respond to +to_sym+. This includes the keys from the root hash
116
+ # and from all nested hashes and arrays.
117
+ #
118
+ # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
119
+ #
120
+ # hash.deep_symbolize_keys
121
+ # # => {:person=>{:name=>"Rob", :age=>"28"}}
122
+ def deep_symbolize_keys
123
+ deep_transform_keys{ |key| key.to_sym rescue key }
124
+ end
125
+
126
+ # Destructively convert all keys to symbols, as long as they respond
127
+ # to +to_sym+. This includes the keys from the root hash and from all
128
+ # nested hashes and arrays.
129
+ def deep_symbolize_keys!
130
+ deep_transform_keys!{ |key| key.to_sym rescue key }
131
+ end
132
+
133
+ private
134
+ # support methods for deep transforming nested hashes and arrays
135
+ def _deep_transform_keys_in_object(object, &block)
136
+ case object
137
+ when Hash
138
+ object.each_with_object({}) do |(key, value), result|
139
+ result[yield(key)] = _deep_transform_keys_in_object(value, &block)
140
+ end
141
+ when Array
142
+ object.map {|e| _deep_transform_keys_in_object(e, &block) }
143
+ else
144
+ object
145
+ end
146
+ end
147
+
148
+ def _deep_transform_keys_in_object!(object, &block)
149
+ case object
150
+ when Hash
151
+ object.keys.each do |key|
152
+ value = object.delete(key)
153
+ object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
154
+ end
155
+ object
156
+ when Array
157
+ object.map! {|e| _deep_transform_keys_in_object!(e, &block)}
158
+ else
159
+ object
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,22 @@
1
+ class Hash
2
+ # Merges the caller into +other_hash+. For example,
3
+ #
4
+ # options = options.reverse_merge(size: 25, velocity: 10)
5
+ #
6
+ # is equivalent to
7
+ #
8
+ # options = { size: 25, velocity: 10 }.merge(options)
9
+ #
10
+ # This is particularly useful for initializing an options hash
11
+ # with default values.
12
+ def reverse_merge(other_hash)
13
+ other_hash.merge(self)
14
+ end
15
+
16
+ # Destructive +reverse_merge+.
17
+ def reverse_merge!(other_hash)
18
+ # right wins if there is no left
19
+ merge!( other_hash ){|key,left,right| left }
20
+ end
21
+ alias_method :reverse_update, :reverse_merge!
22
+ end
@@ -0,0 +1,42 @@
1
+ class Hash
2
+ # Slice a hash to include only the given keys. This is useful for
3
+ # limiting an options hash to valid keys before passing to a method:
4
+ #
5
+ # def search(criteria = {})
6
+ # criteria.assert_valid_keys(:mass, :velocity, :time)
7
+ # end
8
+ #
9
+ # search(options.slice(:mass, :velocity, :time))
10
+ #
11
+ # If you have an array of keys you want to limit to, you should splat them:
12
+ #
13
+ # valid_keys = [:mass, :velocity, :time]
14
+ # search(options.slice(*valid_keys))
15
+ def slice(*keys)
16
+ keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
17
+ keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
18
+ end
19
+
20
+ # Replaces the hash with only the given keys.
21
+ # Returns a hash containing the removed key/value pairs.
22
+ #
23
+ # { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
24
+ # # => {:c=>3, :d=>4}
25
+ def slice!(*keys)
26
+ keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
27
+ omit = slice(*self.keys - keys)
28
+ hash = slice(*keys)
29
+ hash.default = default
30
+ hash.default_proc = default_proc if default_proc
31
+ replace(hash)
32
+ omit
33
+ end
34
+
35
+ # Removes and returns the key/value pairs matching the given keys.
36
+ #
37
+ # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
38
+ # { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
39
+ def extract!(*keys)
40
+ keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
41
+ end
42
+ end
@@ -0,0 +1,272 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+
3
+ module ActiveSupport
4
+ # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
5
+ # to be the same.
6
+ #
7
+ # rgb = ActiveSupport::HashWithIndifferentAccess.new
8
+ #
9
+ # rgb[:black] = '#000000'
10
+ # rgb[:black] # => '#000000'
11
+ # rgb['black'] # => '#000000'
12
+ #
13
+ # rgb['white'] = '#FFFFFF'
14
+ # rgb[:white] # => '#FFFFFF'
15
+ # rgb['white'] # => '#FFFFFF'
16
+ #
17
+ # Internally symbols are mapped to strings when used as keys in the entire
18
+ # writing interface (calling <tt>[]=</tt>, <tt>merge</tt>, etc). This
19
+ # mapping belongs to the public interface. For example, given:
20
+ #
21
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
22
+ #
23
+ # You are guaranteed that the key is returned as a string:
24
+ #
25
+ # hash.keys # => ["a"]
26
+ #
27
+ # Technically other types of keys are accepted:
28
+ #
29
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
30
+ # hash[0] = 0
31
+ # hash # => {"a"=>1, 0=>0}
32
+ #
33
+ # but this class is intended for use cases where strings or symbols are the
34
+ # expected keys and it is convenient to understand both as the same. For
35
+ # example the +params+ hash in Ruby on Rails.
36
+ #
37
+ # Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
38
+ #
39
+ # rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
40
+ #
41
+ # which may be handy.
42
+ class HashWithIndifferentAccess < Hash
43
+ # Returns +true+ so that <tt>Array#extract_options!</tt> finds members of
44
+ # this class.
45
+ def extractable_options?
46
+ true
47
+ end
48
+
49
+ def with_indifferent_access
50
+ dup
51
+ end
52
+
53
+ def nested_under_indifferent_access
54
+ self
55
+ end
56
+
57
+ def initialize(constructor = {})
58
+ if constructor.is_a?(Hash)
59
+ super()
60
+ update(constructor)
61
+ else
62
+ super(constructor)
63
+ end
64
+ end
65
+
66
+ def default(key = nil)
67
+ if key.is_a?(Symbol) && include?(key = key.to_s)
68
+ self[key]
69
+ else
70
+ super
71
+ end
72
+ end
73
+
74
+ def self.new_from_hash_copying_default(hash)
75
+ hash = hash.to_hash
76
+ new(hash).tap do |new_hash|
77
+ new_hash.default = hash.default
78
+ end
79
+ end
80
+
81
+ def self.[](*args)
82
+ new.merge!(Hash[*args])
83
+ end
84
+
85
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
86
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
87
+
88
+ # Assigns a new value to the hash:
89
+ #
90
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
91
+ # hash[:key] = 'value'
92
+ #
93
+ # This value can be later fetched using either +:key+ or +'key'+.
94
+ def []=(key, value)
95
+ regular_writer(convert_key(key), convert_value(value, for: :assignment))
96
+ end
97
+
98
+ alias_method :store, :[]=
99
+
100
+ # Updates the receiver in-place, merging in the hash passed as argument:
101
+ #
102
+ # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
103
+ # hash_1[:key] = 'value'
104
+ #
105
+ # hash_2 = ActiveSupport::HashWithIndifferentAccess.new
106
+ # hash_2[:key] = 'New Value!'
107
+ #
108
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
109
+ #
110
+ # The argument can be either an
111
+ # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
112
+ # In either case the merge respects the semantics of indifferent access.
113
+ #
114
+ # If the argument is a regular hash with keys +:key+ and +"key"+ only one
115
+ # of the values end up in the receiver, but which one is unspecified.
116
+ #
117
+ # When given a block, the value for duplicated keys will be determined
118
+ # by the result of invoking the block with the duplicated key, the value
119
+ # in the receiver, and the value in +other_hash+. The rules for duplicated
120
+ # keys follow the semantics of indifferent access:
121
+ #
122
+ # hash_1[:key] = 10
123
+ # hash_2['key'] = 12
124
+ # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
125
+ def update(other_hash)
126
+ if other_hash.is_a? HashWithIndifferentAccess
127
+ super(other_hash)
128
+ else
129
+ other_hash.to_hash.each_pair do |key, value|
130
+ if block_given? && key?(key)
131
+ value = yield(convert_key(key), self[key], value)
132
+ end
133
+ regular_writer(convert_key(key), convert_value(value))
134
+ end
135
+ self
136
+ end
137
+ end
138
+
139
+ alias_method :merge!, :update
140
+
141
+ # Checks the hash for a key matching the argument passed in:
142
+ #
143
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
144
+ # hash['key'] = 'value'
145
+ # hash.key?(:key) # => true
146
+ # hash.key?('key') # => true
147
+ def key?(key)
148
+ super(convert_key(key))
149
+ end
150
+
151
+ alias_method :include?, :key?
152
+ alias_method :has_key?, :key?
153
+ alias_method :member?, :key?
154
+
155
+ # Same as <tt>Hash#fetch</tt> where the key passed as argument can be
156
+ # either a string or a symbol:
157
+ #
158
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
159
+ # counters[:foo] = 1
160
+ #
161
+ # counters.fetch('foo') # => 1
162
+ # counters.fetch(:bar, 0) # => 0
163
+ # counters.fetch(:bar) { |key| 0 } # => 0
164
+ # counters.fetch(:zoo) # => KeyError: key not found: "zoo"
165
+ def fetch(key, *extras)
166
+ super(convert_key(key), *extras)
167
+ end
168
+
169
+ # Returns an array of the values at the specified indices:
170
+ #
171
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
172
+ # hash[:a] = 'x'
173
+ # hash[:b] = 'y'
174
+ # hash.values_at('a', 'b') # => ["x", "y"]
175
+ def values_at(*indices)
176
+ indices.collect { |key| self[convert_key(key)] }
177
+ end
178
+
179
+ # Returns an exact copy of the hash.
180
+ def dup
181
+ self.class.new(self).tap do |new_hash|
182
+ new_hash.default = default
183
+ end
184
+ end
185
+
186
+ # This method has the same semantics of +update+, except it does not
187
+ # modify the receiver but rather returns a new hash with indifferent
188
+ # access with the result of the merge.
189
+ def merge(hash, &block)
190
+ self.dup.update(hash, &block)
191
+ end
192
+
193
+ # Like +merge+ but the other way around: Merges the receiver into the
194
+ # argument and returns a new hash with indifferent access as result:
195
+ #
196
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
197
+ # hash['a'] = nil
198
+ # hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1}
199
+ def reverse_merge(other_hash)
200
+ super(self.class.new_from_hash_copying_default(other_hash))
201
+ end
202
+
203
+ # Same semantics as +reverse_merge+ but modifies the receiver in-place.
204
+ def reverse_merge!(other_hash)
205
+ replace(reverse_merge( other_hash ))
206
+ end
207
+
208
+ # Replaces the contents of this hash with other_hash.
209
+ #
210
+ # h = { "a" => 100, "b" => 200 }
211
+ # h.replace({ "c" => 300, "d" => 400 }) # => {"c"=>300, "d"=>400}
212
+ def replace(other_hash)
213
+ super(self.class.new_from_hash_copying_default(other_hash))
214
+ end
215
+
216
+ # Removes the specified key from the hash.
217
+ def delete(key)
218
+ super(convert_key(key))
219
+ end
220
+
221
+ def stringify_keys!; self end
222
+ def deep_stringify_keys!; self end
223
+ def stringify_keys; dup end
224
+ def deep_stringify_keys; dup end
225
+ undef :symbolize_keys!
226
+ undef :deep_symbolize_keys!
227
+ def symbolize_keys; to_hash.symbolize_keys! end
228
+ def deep_symbolize_keys; to_hash.deep_symbolize_keys! end
229
+ def to_options!; self end
230
+
231
+ def select(*args, &block)
232
+ dup.tap { |hash| hash.select!(*args, &block) }
233
+ end
234
+
235
+ def reject(*args, &block)
236
+ dup.tap { |hash| hash.reject!(*args, &block) }
237
+ end
238
+
239
+ # Convert to a regular hash with string keys.
240
+ def to_hash
241
+ _new_hash= {}
242
+ each do |key, value|
243
+ _new_hash[convert_key(key)] = convert_value(value, for: :to_hash)
244
+ end
245
+ Hash.new(default).merge!(_new_hash)
246
+ end
247
+
248
+ protected
249
+ def convert_key(key)
250
+ key.kind_of?(Symbol) ? key.to_s : key
251
+ end
252
+
253
+ def convert_value(value, options = {})
254
+ if value.is_a? Hash
255
+ if options[:for] == :to_hash
256
+ value.to_hash
257
+ else
258
+ value.nested_under_indifferent_access
259
+ end
260
+ elsif value.is_a?(Array)
261
+ unless options[:for] == :assignment
262
+ value = value.dup
263
+ end
264
+ value.map! { |e| convert_value(e, options) }
265
+ else
266
+ value
267
+ end
268
+ end
269
+ end
270
+ end
271
+
272
+ HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess