hash_params 0.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +8 -8
  3. data/README.md +167 -109
  4. data/coverage/assets/0.9.0/application.css +799 -0
  5. data/coverage/assets/0.9.0/application.js +1707 -0
  6. data/coverage/assets/0.9.0/colorbox/border.png +0 -0
  7. data/coverage/assets/0.9.0/colorbox/controls.png +0 -0
  8. data/coverage/assets/0.9.0/colorbox/loading.gif +0 -0
  9. data/coverage/assets/0.9.0/colorbox/loading_background.png +0 -0
  10. data/coverage/assets/0.9.0/favicon_green.png +0 -0
  11. data/coverage/assets/0.9.0/favicon_red.png +0 -0
  12. data/coverage/assets/0.9.0/favicon_yellow.png +0 -0
  13. data/coverage/assets/0.9.0/loading.gif +0 -0
  14. data/coverage/assets/0.9.0/magnify.png +0 -0
  15. data/coverage/assets/0.9.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  16. data/coverage/assets/0.9.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  17. data/coverage/assets/0.9.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  18. data/coverage/assets/0.9.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  19. data/coverage/assets/0.9.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  20. data/coverage/assets/0.9.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  21. data/coverage/assets/0.9.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  22. data/coverage/assets/0.9.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  23. data/coverage/assets/0.9.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
  24. data/coverage/assets/0.9.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  25. data/coverage/assets/0.9.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
  26. data/coverage/assets/0.9.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
  27. data/coverage/assets/0.9.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  28. data/coverage/index.html +1597 -369
  29. data/hash_params.gemspec +14 -5
  30. data/lib/hash_params/binding_validator.rb +26 -0
  31. data/lib/hash_params/hash_validator.rb +71 -0
  32. data/lib/hash_params/validator.rb +119 -0
  33. data/lib/hash_params/yaml_params.rb +93 -0
  34. data/lib/hash_params.rb +15 -142
  35. data/spec/hash_params_spec.rb +100 -63
  36. data/spec/spec_helper.rb +0 -11
  37. data/tmp/hash_params.rb +181 -0
  38. data/tmp/hash_params_spec.rb +102 -0
  39. data/tmp/module_spec.rb +27 -0
  40. data/tmp/var_spec.rb +9 -0
  41. data/tmp/yaml_params.rb +109 -0
  42. metadata +36 -3
@@ -2,85 +2,122 @@ require_relative 'spec_helper'
2
2
 
3
3
 
4
4
  describe HashParams do
5
+ let(:v) { HashParams }
5
6
 
6
- let (:r) {
7
- HashParams.new(
8
- {
9
- ignored: "this will be ignored because it's not mentioned",
10
- to_be_renamed: :to_be_renamed,
11
- integer_coercion: "1",
12
- bad_number: '1aaa2',
13
- array_with_delim: '1|2|3',
14
- hash_as_string: "{a => 1,b => 2,c => d}",
15
- proc_validation: "is_this_valid?",
16
- some_number: 122,
17
- some_string: 'this is a test string' ,
18
- is_true: 'true',
19
- is_false: 'f',
20
- recursive: {}
21
- }
22
- ) do
23
- param :doesnt_exist, required: true
24
- param :to_be_renamed, as: :renamed
25
- param :no_value, default: 1
26
- #proc default relying on previously set value
27
- param :proc_default, default: lambda { |o| o[:no_value] * 5 }
28
- param :integer_coercion, coerce: Integer
29
- #chained coersions of various types
30
- param :bad_number, coerce: [lambda { |o| o.gsub('a', '') }, :to_i, Float]
31
- #arrays and hashes
32
- param :array_with_delim, coerce: Array, delimiter: '|'
33
- param :hash_as_string, coerce: Hash, delimiter: ',', separator: '=>'
34
- param :proc_validation, validate: lambda { |v| v == 'Failed_proc_validation' }
35
- #validations
36
- param :some_number, min: 120, max: 500, in: (100..200), is: 122
37
- param :some_string, min_length: 21, max_length: 30, format: /^t.*g$/
38
- #combinations
39
- param :missing_with_validation, coerce: Integer, :default => 60 * 60, :validate => lambda { |v| v >= 60 * 60 }
40
- param :is_true, coerce: :boolean
41
- param :is_false, coerce: :boolean
7
+ it 'raises error if required and missing' do
8
+ proc {
9
+ v.validate(nil, nil, required: true)
10
+ }.must_raise HashParams::Validator::ValidationError
11
+ end
12
+ it 'runs multiple coersions' do
13
+ v.validate('1aaa2', Float, coerce: [lambda { |o| o.gsub('a', '') }, :to_i]).must_equal(12.0)
14
+ end
15
+
16
+ it 'defaults missing values' do
17
+ v.validate(nil, Integer, default: 1).must_equal(1)
18
+ v.validate(nil, Integer, default: lambda { 2 * 5 }).must_equal(10)
19
+ end
20
+
21
+ it 'validates with lambdas' do
22
+ v.validate(nil, Integer, :validate => lambda { |v| v = 60 * 60 }).must_equal(60 * 60)
23
+ end
24
+
25
+ it 'validates with procs' do
26
+ v.validate('is_this_valid?', String) do |v|
27
+ v = 'Validated with Proc'
28
+ end.must_equal('Validated with Proc')
29
+ end
30
+
31
+ it 'verifies numbers with common params' do
32
+ v.validate(122, Integer, min: 120, max: 500, in: (100..200), is: 122).must_equal(122)
33
+ end
34
+
35
+ it 'verifies strings with common params' do
36
+ v.validate('this is a test string', String, min_length: 21, max_length: 30, format: /^t.*g$/).must_equal 'this is a test string'
37
+ end
38
+
39
+ it 'coerces true' do
40
+ v.validate('t', :boolean).must_equal true
41
+ v.validate('true', :boolean).must_equal true
42
+ v.validate('yes', :boolean).must_equal true
43
+ v.validate('1', :boolean).must_equal true
44
+ v.validate(1, :boolean).must_equal true
45
+ end
46
+
47
+ it 'coerces false' do
48
+ v.validate('f', :boolean).must_equal false
49
+ v.validate('false', :boolean).must_equal false
50
+ v.validate('no', :boolean).must_equal false
51
+ v.validate('0', :boolean).must_equal false
52
+ v.validate(0, :boolean).must_equal false
53
+ end
54
+
55
+
56
+ it 'coerces array' do
57
+ v.validate('1|2|3', Array, delimiter: '|').must_equal ["1", "2", "3"]
58
+ end
59
+
60
+ it 'coerces hash' do
61
+ v.validate('{a => 1,b => 2,c => d}', Hash, delimiter: ',', separator: '=>').must_equal({"a" => "1", "b" => "2", "c" => "d"})
62
+ end
63
+
64
+ it 'validates a hash using a block' do
65
+ h = {
66
+ ignored: "this will be ignored because it's not mentioned",
67
+ to_be_renamed: :renamed_value,
68
+ 'integer_coercion': "1",
69
+ proc_validation: "is_this_valid?",
70
+ recursive: {}
71
+ }
72
+ r = v.validate(h, Hash, symbolize_keys: true) do
73
+ key :doesnt_exist, nil, required: true
74
+ key :to_be_renamed, Symbol, as: :renamed
75
+ #Default Lambdas take no parameters
76
+ key :proc_default, Integer, default: lambda { 1 * 5 }
77
+ key :proc_validation, String, validate: lambda { |v| v = 'Validated in proc' }
42
78
  #recursive
43
- param :recursive do
44
- param :wasnt_here_before, default: true
79
+ key :recursive, Hash do
80
+ key :wasnt_here_before, :boolean, default: true
45
81
  end
46
82
  end
47
- }
48
83
 
49
-
50
- it 'does amazing things' do
51
84
  (r.valid?).must_equal false
52
85
  r[:ignored].must_be_nil
53
- r[:no_value].must_equal 1
54
- r[:proc_default].must_equal 5
55
- r[:renamed].must_equal :to_be_renamed
56
- r[:integer_coercion].must_equal 1
57
- r[:bad_number].must_equal 12.0
58
-
59
- r[:array_with_delim].must_equal ["1", "2", "3"]
60
- r[:hash_as_string].must_equal ({ "a" => "1", "b" => "2", "c" => "d" })
61
- r[:missing_with_validation].must_equal 60 * 60
62
- r[:is_true].must_equal true
63
- r[:is_false].must_equal false
64
-
86
+ # r[:proc_default].must_equal 5
87
+ r[:renamed].must_equal :renamed_value
65
88
  #recursive checking
66
89
  r[:recursive][:wasnt_here_before].must_equal true
67
90
 
68
91
  #failed items don't show up
69
- r.errors.size.must_equal 2
70
92
  r[:doesnt_exist].must_be_nil
71
- r[:proc_validation].must_be_nil
72
- r.errors[0].must_equal 'Parameter doesnt_exist is required and missing'
73
- r.errors[1].must_equal 'is_this_valid? failed validation using proc'
93
+ r[:proc_validation].must_equal 'Validated in proc'
94
+
95
+ r.validation_errors.size.must_equal 1
96
+ r.validation_errors[0].must_equal "Error processing key 'doesnt_exist': Required Parameter missing and has no default specified"
74
97
 
75
98
  end
76
99
 
77
- it 'injects into current class' do
78
- r = HashParams.new({will_be_injected: 12345}, self) do
79
- param :will_be_injected
100
+ it 'validates variables in the local binding' do
101
+
102
+ x='10'
103
+ y='this is a test string'
104
+ z={}
105
+ will_be_int='100'
106
+
107
+ HashParams.with_binding do
108
+ var :x, Integer, min: 10, max: 100
109
+ var :y, String, min_length: 21, max_length: 30, format: /^t.*g$/
110
+ var :z, Hash, raise_errors: true, symbolize_keys: true, make_methods: true do
111
+ key :blah, String, default: 1
112
+ end
113
+ var :will_be_int, :to_i
80
114
  end
81
- r[:will_be_injected].must_equal 12345
82
- @will_be_injected.must_equal 12345
83
- will_be_injected.must_equal 12345
115
+ x.must_equal 10
116
+ y.must_equal 'this is a test string'
117
+ z[:blah].must_equal '1'
118
+ z.blah.must_equal '1'
119
+ will_be_int.must_equal 100
84
120
  end
85
121
 
122
+
86
123
  end
data/spec/spec_helper.rb CHANGED
@@ -11,14 +11,3 @@ require 'minitest/spec'
11
11
  require 'minitest/autorun'
12
12
  require 'pry'
13
13
 
14
- #require 'minitest/mock'
15
-
16
- # require 'rack/test'
17
- #
18
- # require 'dummy/app'
19
- #
20
- # def app
21
- # App
22
- # end
23
- #
24
- # #include Rack::Test::Methods
@@ -0,0 +1,181 @@
1
+ class HashParams < Hash
2
+ VERSION = '0.0.3'
3
+
4
+ attr :errors
5
+
6
+ def initialize(incoming_hash={}, opts={})
7
+ @opts = opts
8
+ @incoming_hash = incoming_hash
9
+ @errors =[]
10
+ # @parent = code.binding.eval 'self'
11
+
12
+ #@opts[:injection_target] = self if @opts[:injection_target] == :self
13
+
14
+ #binding.pry
15
+
16
+ if block_given?
17
+ instance_eval(&Proc.new)
18
+ else
19
+ #no proc was given. This means pass all values
20
+ @incoming_hash.each do |k, v|
21
+ set_key_value k, v
22
+ end
23
+ end
24
+ end
25
+
26
+ def valid?
27
+ @errors.empty?
28
+ end
29
+
30
+ def param(key, h = {})
31
+ begin
32
+ #What happens if value is FalseClass ? Need something a little better
33
+ val = @incoming_hash[key] || @incoming_hash[key.to_sym] || @incoming_hash[key.to_s]
34
+ if val.nil? && h[:default]
35
+ val = h[:default].respond_to?(:call) ? h[:default].call(self) : h[:default]
36
+ end
37
+ #don't bother with the rest if required parameter is missing
38
+ raise "Parameter #{key} is required and missing" if h[:required] && val.nil?
39
+ #do all coercion and transformation first there could be an array of coersions they will be run in order
40
+ Array(h[:coerce]).each do |c|
41
+ begin
42
+ val = HashParams.coerce(val, c, h)
43
+ rescue => e
44
+ @errors << e.to_s
45
+ end
46
+ end
47
+ binding.pry if key == :integer_coercion
48
+ #coersion could return a nil which won't validate, it could return a false which will attempt to validate
49
+ begin
50
+ if HashParams.validate(val, h)
51
+ #The value is valid add it
52
+ set_key_value key, val, h[:as]
53
+ end
54
+ rescue => e
55
+ @errors << e.to_s
56
+ end
57
+
58
+ #after all that see if a block is given and process that
59
+ if block_given? && val.is_a?(Hash)
60
+ #Proc.new references the implict block
61
+ val = HashParams.new(val, {}, &Proc.new)
62
+ set_key_value key, val, h[:as]
63
+ end
64
+ val
65
+ rescue => e
66
+ @errors << e.to_s
67
+ end
68
+ if @errors.length > 0 && @opts[:raise_errors]
69
+ raise @errors.join("\n")
70
+ end
71
+ val
72
+ end
73
+
74
+ def inject_into_target(target, var_name, val)
75
+ if target
76
+ #for read write methods
77
+ target.singleton_class.class_eval do
78
+ attr_accessor var_name;
79
+ end
80
+ target.send("#{var_name}=", val)
81
+ end
82
+ end
83
+
84
+ def self.validate(param, options ={})
85
+ return false if param.nil?
86
+ is_valid = true
87
+ errors =[]
88
+ options.each do |key, value|
89
+
90
+ error = case key
91
+ when :validate
92
+ "#{param.to_s} failed validation using proc" if value.respond_to?(:call) && !value.call(param)
93
+ when :blank
94
+ 'Parameter cannot be blank' if !value && blank?(param)
95
+ when :format
96
+ 'Parameter must be a string if using the format validation' && next unless param.kind_of?(String)
97
+ "Parameter must match format #{value}" unless param =~ value
98
+ when :is
99
+ "Parameter must be #{value}" unless param === value
100
+ when :in, :within, :range
101
+ "Parameter must be within #{value}" unless value.respond_to?(:include) ? value.include?(param) : Array(value).include?(param)
102
+ when :min
103
+ "Parameter cannot be less than #{value}" unless value <= param
104
+ when :max
105
+ "Parameter cannot be greater than #{value}" unless value >= param
106
+ when :min_length
107
+ "Parameter cannot have length less than #{value}" unless value <= param.length
108
+ when :max_length
109
+ "Parameter cannot have length greater than #{value}" unless value >= param.length
110
+ else
111
+ nil
112
+ end
113
+ if error
114
+ errors << error
115
+ is_valid = false
116
+ end
117
+ end
118
+
119
+ #return true or false depending on if it validated
120
+ unless errors.empty?
121
+ raise errors.join("\n")
122
+ end
123
+ is_valid
124
+ end
125
+
126
+
127
+ def self.coerce(val, type, h)
128
+
129
+ # exceptions bubble up
130
+ #order is important
131
+ return val if type.nil? || val.nil?
132
+
133
+ #two special types of transforms
134
+ #There is no Boolean type so we handle them special
135
+ if type == :boolean || type =='boolean'
136
+ return val if (val == true || val == false)
137
+ return false if /(false|f|no|n|0)$/i === val.to_s.downcase
138
+ return true if /(true|t|yes|y|1)$/i === val.to_s.downcase
139
+
140
+ # if we can't parse we return a nil
141
+ # maybe !!val is a better return?
142
+ return nil
143
+ end
144
+ #they have given us a coercion which is a string or symbol which is not "boolean", we are going to cast this into a proc and run it
145
+
146
+ return type.to_proc.call(val) if type.is_a?(Symbol) || type.is_a?(String)
147
+ #could be a proc
148
+
149
+ return type.call(val) if type.respond_to?(:call)
150
+ #nothing but simple types left
151
+ return val if val.is_a?(type)
152
+ return Integer(val) if type == Integer
153
+ return Float(val) if type == Float
154
+ return String(val) if type == String
155
+ return Date.parse(val) if type == Date
156
+ return Time.parse(val) if type == Time
157
+ return DateTime.parse(val) if type == DateTime
158
+ return Array(val.split(h[:delimiter] || ',')) if type == Array
159
+ return Hash[val.gsub(/[{}]/, '').gsub('}', '').split(h[:delimiter] || ',').map { |c| c.split(h[:separator] ||':').map { |i| i.strip } }] if type == Hash
160
+
161
+ nil
162
+ end
163
+
164
+ def present?(object)
165
+ !blank?(object)
166
+ end
167
+
168
+ def blank?(object)
169
+ object.nil? || (object.respond_to?(:empty) && object.empty)
170
+ end
171
+
172
+ def set_key_value(key, value, as = nil)
173
+ key = as unless as.nil?
174
+ key = key.to_s.to_sym if @opts[:symbolize_keys]
175
+ inject_into_target(@opts[:injection_target], key, value) if @opts[:injection_target]
176
+ inject_into_target(self, key, value) if @opts[:make_methods]
177
+ self[key]=value
178
+ end
179
+
180
+
181
+ end
@@ -0,0 +1,102 @@
1
+ require_relative 'spec_helper'
2
+
3
+
4
+ describe HashParams do
5
+
6
+ let (:r) {
7
+ HashParams.new(
8
+ {
9
+ ignored: "this will be ignored because it's not mentioned",
10
+ to_be_renamed: :to_be_renamed,
11
+ integer_coercion: "1",
12
+ bad_number: '1aaa2',
13
+ array_with_delim: '1|2|3',
14
+ hash_as_string: "{a => 1,b => 2,c => d}",
15
+ proc_validation: "is_this_valid?",
16
+ some_number: 122,
17
+ some_string: 'this is a test string' ,
18
+ is_true: 'true',
19
+ is_false: 'f',
20
+ recursive: {}
21
+ }
22
+ ) do
23
+ param :doesnt_exist, required: true
24
+ param :to_be_renamed, as: :renamed
25
+ param :no_value, default: 1
26
+ #proc default relying on previously set value
27
+ param :proc_default, default: lambda { |o| o[:no_value] * 5 }
28
+ param :integer_coercion, coerce: Integer
29
+ #chained coersions of various types
30
+ param :bad_number, coerce: [lambda { |o| o.gsub('a', '') }, :to_i, Float]
31
+ #arrays and hashes
32
+ param :array_with_delim, coerce: Array, delimiter: '|'
33
+ param :hash_as_string, coerce: Hash, delimiter: ',', separator: '=>'
34
+ param :proc_validation, validate: lambda { |v| v == 'Failed_proc_validation' }
35
+ #validations
36
+ param :some_number, min: 120, max: 500, in: (100..200), is: 122
37
+ param :some_string, min_length: 21, max_length: 30, format: /^t.*g$/
38
+ #combinations
39
+ param :missing_with_validation, coerce: Integer, :default => 60 * 60, :validate => lambda { |v| v >= 60 * 60 }
40
+ param :is_true, coerce: :boolean
41
+ param :is_false, coerce: :boolean
42
+ #recursive
43
+ param :recursive do
44
+ param :wasnt_here_before, default: true
45
+ end
46
+ end
47
+ }
48
+
49
+
50
+ it 'does amazing things' do
51
+ (r.valid?).must_equal false
52
+ r[:ignored].must_be_nil
53
+ r[:no_value].must_equal 1
54
+ r[:proc_default].must_equal 5
55
+ r[:renamed].must_equal :to_be_renamed
56
+ r[:integer_coercion].must_equal 1
57
+ r[:bad_number].must_equal 12.0
58
+
59
+ r[:array_with_delim].must_equal ["1", "2", "3"]
60
+ r[:hash_as_string].must_equal ({ "a" => "1", "b" => "2", "c" => "d" })
61
+ r[:missing_with_validation].must_equal 60 * 60
62
+ r[:is_true].must_equal true
63
+ r[:is_false].must_equal false
64
+
65
+ #recursive checking
66
+ r[:recursive][:wasnt_here_before].must_equal true
67
+
68
+ #failed items don't show up
69
+ r.errors.size.must_equal 2
70
+ r[:doesnt_exist].must_be_nil
71
+ r[:proc_validation].must_be_nil
72
+ r.errors[0].must_equal 'Parameter doesnt_exist is required and missing'
73
+ r.errors[1].must_equal 'is_this_valid? failed validation using proc'
74
+
75
+ end
76
+
77
+ it 'injects into current class' do
78
+ r = HashParams.new({will_be_injected: 12345}, injection_target: self) do
79
+ param :will_be_injected
80
+ end
81
+ r[:will_be_injected].must_equal 12345
82
+ @will_be_injected.must_equal 12345
83
+ will_be_injected.must_equal 12345
84
+ end
85
+
86
+ it 'passes through without alteration' do
87
+ r=HashParams.new(test: 1, two: {three: :four})
88
+ r[:test].must_equal 1
89
+ r[:two][:three].must_equal :four
90
+ end
91
+
92
+ it 'validates plain objects' do
93
+ HashParams.validate(1, min: 0, max: 10, is: 1).must_equal true
94
+ end
95
+ it 'coerces plain objects' do
96
+ hash_as_string = "{a => 1,b => 2,c => d}"
97
+ h=HashParams.coerce(hash_as_string, Hash, delimiter: ',', separator: '=>' )
98
+ h.must_be_instance_of Hash
99
+
100
+
101
+ end
102
+ end
@@ -0,0 +1,27 @@
1
+ require 'pry'
2
+ module M1
3
+ def im
4
+ puts 'm1 im'
5
+ end
6
+ end
7
+
8
+ module M2
9
+ def self.cm
10
+ puts 'm2 cm'
11
+ end
12
+ end
13
+
14
+
15
+ module T1
16
+ include M1
17
+ end
18
+ module T2
19
+ extend M1
20
+ end
21
+ module T3
22
+ include M2
23
+ end
24
+ module T4
25
+ extend M2
26
+ end
27
+
data/tmp/var_spec.rb ADDED
@@ -0,0 +1,9 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe HashParams do
4
+ let(:v) { HashParams }
5
+ it 'does things' do
6
+ HashParams::BindingValidator("test")
7
+ binding.pry
8
+ end
9
+ end
@@ -0,0 +1,109 @@
1
+ class YamlParams < HashParams
2
+
3
+ ENVIRONMENT = ENV['HASH_PARAMS_ENV'] || (defined?(Rails) && Rails.env) || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
4
+
5
+ def initialize(file_name=nil, opts={})
6
+
7
+ env ||= opts[:env] || opts[:environment] || ENVIRONMENT
8
+
9
+ file_name = nil if [:defaults, :default, :auto].include?(file_name)
10
+
11
+ h =if file_name
12
+ hash_from_yaml_file(file_name)
13
+ else
14
+ hash_from_default_yaml_files(opts[:app_name], env, opts[:roots], opts[:file_separator], opts[:file_extension])
15
+ end
16
+
17
+ if block_given?
18
+ # warn '[DEPRECATION] Passing blocks into the constructor is deprecated. Please use validate or strictly validate in the future'
19
+ super(h, opts, &Proc.new)
20
+ else
21
+ super(h, opts)
22
+ end
23
+ end
24
+
25
+
26
+ def hash_from_yaml_file(filename, env=ENVIRONMENT)
27
+ r = File.exists?(filename) ? YAML::load(ERB.new(File.read(filename)).result) : {}
28
+ r[env] || r
29
+ end
30
+
31
+
32
+ def hash_from_default_yaml_files(app_name=nil, env=nil, roots=nil, file_separator=nil, file_extension=nil)
33
+ #if a nil is passed in we still use the defaults
34
+ app_name ||= ''
35
+ env ||= ENVIRONMENT
36
+ roots ||= nil
37
+ file_separator ||= '_'
38
+ file_extension ||= 'yml'
39
+
40
+ h = {}
41
+ home_dir = File.expand_path('~')
42
+ hostname = Socket.gethostname
43
+
44
+ base_file_names = %W(
45
+ settings.#{file_extension}
46
+ default.#{file_extension}
47
+
48
+ #{env}.#{file_extension}
49
+
50
+ #{hostname}.#{file_extension}
51
+ #{hostname}#{file_separator}#{env}.#{file_extension}
52
+
53
+ local.#{file_extension}
54
+ local#{file_separator}#{env}.#{file_extension}
55
+ settings.local.#{file_extension}
56
+ settings.local#{file_separator}#{env}.#{file_extension}
57
+ config.local.#{file_extension}
58
+ config.local#{file_separator}#{env}.#{file_extension}
59
+ #{app_name}#{file_separator}settings.#{file_extension}
60
+ #{app_name}#{file_separator}config.#{file_extension}
61
+ #{app_name}#{file_separator}default.#{file_extension}
62
+ #{app_name}#{file_separator}#{env}.#{file_extension}
63
+ #{app_name}#{file_separator}#{hostname}.#{file_extension}
64
+ #{app_name}#{file_separator}#{hostname}#{file_separator}#{env}.#{file_extension}
65
+ #{app_name}#{file_separator}local.#{file_extension}
66
+ #{app_name}#{file_separator}local#{file_separator}#{env}.#{file_extension}
67
+ #{app_name}#{file_separator}settings.local.#{file_extension}
68
+ #{app_name}#{file_separator}settings.local#{file_separator}#{env}.#{file_extension}
69
+ #{app_name}#{file_separator}config.local.#{file_extension}
70
+ #{app_name}#{file_separator}config.local#{file_separator}#{env}.#{file_extension}
71
+
72
+ )
73
+
74
+ all_roots = Array(roots) if roots
75
+ all_roots ||= [
76
+ Dir.pwd,
77
+ File.join('/etc', app_name.to_s),
78
+ File.join('/usr', 'local', 'etc', app_name.to_s),
79
+ File.join(home_dir, 'etc', app_name.to_s),
80
+ File.join(home_dir, ".#{app_name}"),
81
+ File.join(home_dir, '.hash_params', app_name.to_s),
82
+ File.join(Dir.pwd, 'config'),
83
+ File.join(Dir.pwd, 'settings')
84
+ ]
85
+ if defined?(Rails)
86
+ all_roots << Rails.root.join('config')
87
+ end
88
+
89
+ all_roots.each do |root|
90
+ base_file_names.each do |fname|
91
+ file = File.join(root, fname)
92
+ h = deep_merge(h, hash_from_yaml_file(file)) if File.exists?(file)
93
+ end
94
+ end
95
+ h
96
+ end
97
+
98
+ def deep_merge(hash, other_hash)
99
+ if other_hash.is_a?(::Hash) && hash.is_a?(::Hash)
100
+ other_hash.each do |k, v|
101
+ hash[k] = hash.key?(k) ? deep_merge(hash[k], v) : v
102
+ end
103
+ hash
104
+ else
105
+ other_hash
106
+ end
107
+ end
108
+ end
109
+