rails_config 0.0.4
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.
- data/LICENSE +23 -0
- data/README +72 -0
- data/Rakefile +17 -0
- data/VERSION +1 -0
- data/lib/rails_config/railtie.rb +16 -0
- data/lib/rails_config/settings/builder.rb +170 -0
- data/lib/rails_config/settings/deep_merge.rb +185 -0
- data/lib/rails_config.rb +4 -0
- data/test/config_builder_test.rb +90 -0
- data/test/test_configs/app_config.yml +2 -0
- data/test/test_configs/bool_override/config1.yml +2 -0
- data/test/test_configs/bool_override/config2.yml +2 -0
- data/test/test_configs/deep_merge/config1.yml +28 -0
- data/test/test_configs/deep_merge/config2.yml +28 -0
- data/test/test_configs/deep_merge2/config1.yml +3 -0
- data/test/test_configs/deep_merge2/config2.yml +2 -0
- data/test/test_configs/development.yml +5 -0
- data/test/test_configs/empty1.yml +0 -0
- data/test/test_configs/empty2.yml +0 -0
- metadata +86 -0
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2010 Jacques Crocker
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
|
23
|
+
Deep Merge (deep_merge.rb) code Copyright (c) 2008 Steve Midgley, released under the MIT license
|
data/README
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
== Summary
|
2
|
+
RailsConfig helps you easily manage environment specific Rails settings in an easy and usable manner
|
3
|
+
|
4
|
+
== Author
|
5
|
+
Jacques Crocker
|
6
|
+
Modified from Original Project (AppConfig by Christopher J. Bottaro)
|
7
|
+
|
8
|
+
=== Compatibility
|
9
|
+
Rails 3.0
|
10
|
+
|
11
|
+
=== Installing on Rails 3
|
12
|
+
|
13
|
+
add this to your Gemfile
|
14
|
+
|
15
|
+
gem "rails_config"
|
16
|
+
|
17
|
+
=== Accessing the Settings object
|
18
|
+
After installing this plugin, the Settings object will be global available. Entries are accessed via object member notation:
|
19
|
+
Settings.my_config_entry
|
20
|
+
Nested entries are supported:
|
21
|
+
Settings.my_section.some_entry
|
22
|
+
|
23
|
+
=== Common config file
|
24
|
+
Config entries are compiled from
|
25
|
+
config/settings.yml
|
26
|
+
config/settings/#{environment}.yml
|
27
|
+
config/environments/#{environment}.yml
|
28
|
+
|
29
|
+
settings defined in files that are lower in the list override settings higher
|
30
|
+
|
31
|
+
=== Reloading config files
|
32
|
+
You can reload the Settings from file at any time by running Settings.reload!
|
33
|
+
|
34
|
+
=== Environment specific config files
|
35
|
+
You can have environment specific config files. Environment specific config entries take precedence over common config entries.
|
36
|
+
|
37
|
+
Example development environment config file:
|
38
|
+
#{Rails.root}/config/environments/development.yml
|
39
|
+
|
40
|
+
Example production environment config file:
|
41
|
+
#{Rails.root}/config/environments/production.yml
|
42
|
+
|
43
|
+
=== Embedded Ruby (ERB)
|
44
|
+
Embedded Ruby is allowed in the configuration files. See examples below.
|
45
|
+
|
46
|
+
=== Accessing Configuration Settings
|
47
|
+
Consider the two following config files.
|
48
|
+
|
49
|
+
#{Rails.root}/config/settings.yml:
|
50
|
+
size: 1
|
51
|
+
server: google.com
|
52
|
+
|
53
|
+
#{Rails.root}/config/environments/development.yml:
|
54
|
+
size: 2
|
55
|
+
computed: <%= 1 + 2 + 3 %>
|
56
|
+
section:
|
57
|
+
size: 3
|
58
|
+
servers: [ {name: yahoo.com}, {name: amazon.com} ]
|
59
|
+
|
60
|
+
Notice that the environment specific config entries overwrite the common entries.
|
61
|
+
Settings.size -> 2
|
62
|
+
Settings.server -> google.com
|
63
|
+
|
64
|
+
Notice the embedded Ruby.
|
65
|
+
Settings.computed -> 6
|
66
|
+
|
67
|
+
Notice that object member notation is maintained even in nested entries.
|
68
|
+
Settings.section.size -> 3
|
69
|
+
|
70
|
+
Notice array notation and object member notation is maintained.
|
71
|
+
Settings.section.servers[0].name -> yahoo.com
|
72
|
+
Settings.section.servers[1].name -> amazon.com
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |s|
|
6
|
+
s.name = "rails_config"
|
7
|
+
s.summary = "provides an Settings for rails3 that reads config/settings.yml"
|
8
|
+
s.email = "railsjedi@gmail.com"
|
9
|
+
s.homepage = "http://github.com/railsjedi/rails_config"
|
10
|
+
s.description = "Provides an easy to use Application Configuration object"
|
11
|
+
s.authors = ["Jacques Crocker"]
|
12
|
+
s.files = FileList["[A-Z]*", "{bin,generators,lib,test}/**/*"]
|
13
|
+
end
|
14
|
+
Jeweler::GemcutterTasks.new
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: gem install jeweler"
|
17
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.4
|
@@ -0,0 +1,16 @@
|
|
1
|
+
if defined?(Rails::Railtie)
|
2
|
+
module RailsConfig
|
3
|
+
class Railtie < Rails::Railtie
|
4
|
+
initializer :setup_rails_config do
|
5
|
+
::Settings = RailsConfig::Settings::Builder.load_files(
|
6
|
+
:paths => [
|
7
|
+
Rails.root.join("config", "settings.yml").to_s,
|
8
|
+
Rails.root.join("config", "settings", "#{Rails.env}.yml").to_s,
|
9
|
+
Rails.root.join("config", "environments", "#{Rails.env}.yml").to_s
|
10
|
+
],
|
11
|
+
:root_path => Rails.root
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'yaml'
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
module RailsConfig
|
6
|
+
module Settings
|
7
|
+
# == Summary
|
8
|
+
# This is API documentation, NOT documentation on how to use this plugin. For that, see the README.
|
9
|
+
class Builder
|
10
|
+
@@load_paths = []
|
11
|
+
@@expand_keys = []
|
12
|
+
@@root_path = ""
|
13
|
+
|
14
|
+
# Create a config object (OpenStruct) from a yaml file. If a second yaml file is given, then the sections of that file will overwrite the sections
|
15
|
+
# if the first file if they exist in the first file.
|
16
|
+
def self.load_files(options = {})
|
17
|
+
config = OpenStruct.new
|
18
|
+
|
19
|
+
@@load_paths = [options[:paths]].flatten.compact.uniq
|
20
|
+
@@expand_keys = [options[:expand_keys]].flatten.compact.uniq
|
21
|
+
@@root_path = options[:root_path]
|
22
|
+
|
23
|
+
# add singleton method to our Settings that reloads its settings from the load_paths options
|
24
|
+
def config.reload!
|
25
|
+
|
26
|
+
conf = {}
|
27
|
+
Builder.load_paths.to_a.each do |path|
|
28
|
+
file_conf = YAML.load(ERB.new(IO.read(path)).result) if path and File.exists?(path)
|
29
|
+
next unless file_conf
|
30
|
+
|
31
|
+
if conf.size > 0
|
32
|
+
DeepMerge.deep_merge!(file_conf, conf, :preserve_unmergeables => false)
|
33
|
+
else
|
34
|
+
conf = file_conf
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# expand the javascripts config to handle *.* paths
|
39
|
+
Builder.expand_keys.to_a.each do |expand_path|
|
40
|
+
expand_path = expand_path.to_s
|
41
|
+
if conf[expand_path]
|
42
|
+
conf[expand_path] = RailsConfig::Settings::Builder.expand(conf[expand_path], "#{Builder.root_path}/public/#{expand_path}")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# load all the new values into the openstruct
|
47
|
+
marshal_load(RailsConfig::Settings::Builder.convert(conf).marshal_dump)
|
48
|
+
|
49
|
+
return self
|
50
|
+
end
|
51
|
+
|
52
|
+
config.reload!
|
53
|
+
return config
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.load_paths
|
57
|
+
@@load_paths
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.expand_keys
|
61
|
+
@@expand_keys
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.root_path
|
65
|
+
@@root_path
|
66
|
+
end
|
67
|
+
|
68
|
+
# Recursively converts Hashes to OpenStructs (including Hashes inside Arrays)
|
69
|
+
def self.convert(h) #:nodoc:
|
70
|
+
s = OpenStruct.new
|
71
|
+
h.each do |k, v|
|
72
|
+
s.new_ostruct_member(k)
|
73
|
+
if v.is_a?(Hash)
|
74
|
+
s.send( (k+'=').to_sym, convert(v))
|
75
|
+
elsif v.is_a?(Array)
|
76
|
+
converted_array = v.collect { |e| e.instance_of?(Hash) ? convert(e) : e }
|
77
|
+
s.send("#{k}=".to_sym, converted_array)
|
78
|
+
else
|
79
|
+
s.send("#{k}=".to_sym, v)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
s
|
83
|
+
end
|
84
|
+
|
85
|
+
# expand a config val
|
86
|
+
def self.expand(config, base_path)
|
87
|
+
case config.class.to_s
|
88
|
+
when "Hash"
|
89
|
+
return expand_hash(config, base_path)
|
90
|
+
when "Array"
|
91
|
+
return expand_array(config, base_path)
|
92
|
+
when "String"
|
93
|
+
return expand_string(config, base_path)
|
94
|
+
end
|
95
|
+
return config
|
96
|
+
end
|
97
|
+
|
98
|
+
# expand a string and returns a list
|
99
|
+
def self.expand_string(config, base_path)
|
100
|
+
# puts "Expanding String: #{config.inspect}"
|
101
|
+
if config.include?("*")
|
102
|
+
results = Dir["#{base_path}/#{config}"].map{|i| i.to_s.gsub("#{base_path}/", "") }
|
103
|
+
|
104
|
+
# puts "EXPANDED PATH: #{base_path}/#{config}"
|
105
|
+
# puts results.inspect
|
106
|
+
return results
|
107
|
+
else
|
108
|
+
return config
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# expand a hash by cycling throw all the hash values
|
113
|
+
def self.expand_hash(config, base_path)
|
114
|
+
# puts "Expanding Hash: #{config.inspect}"
|
115
|
+
new_config = {}
|
116
|
+
config.each do |key, val|
|
117
|
+
new_config[key] = expand(val, base_path)
|
118
|
+
end
|
119
|
+
return new_config
|
120
|
+
end
|
121
|
+
|
122
|
+
# expand an array by cycling through all the values
|
123
|
+
def self.expand_array(config, base_path)
|
124
|
+
# puts "Expanding Array: #{config.inspect}"
|
125
|
+
new_config = []
|
126
|
+
config.each do |val|
|
127
|
+
new_val = expand(val, base_path)
|
128
|
+
if new_val.is_a?(Array)
|
129
|
+
new_val.each do |inner|
|
130
|
+
new_config << inner
|
131
|
+
end
|
132
|
+
else
|
133
|
+
new_config << new_val
|
134
|
+
end
|
135
|
+
end
|
136
|
+
return new_config.uniq
|
137
|
+
end
|
138
|
+
|
139
|
+
# Cycles through the array of single element hashes
|
140
|
+
# and deep merges any duplicates it finds
|
141
|
+
#
|
142
|
+
# This is needed so you can define stylesheet keys
|
143
|
+
# in multiple config files
|
144
|
+
def self.merge_assets(list)
|
145
|
+
assets = Array(list).map do |i|
|
146
|
+
if i.is_a?(OpenStruct)
|
147
|
+
i.marshal_dump
|
148
|
+
else
|
149
|
+
i
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# filter out the duplicate single hash keys
|
154
|
+
hash_keys = assets.select{|i| i.is_a?(Hash) and i.keys.size == 1}.group_by{|i| i.keys[0]}
|
155
|
+
hash_keys.each do |key, value|
|
156
|
+
if Array(value).size > 1
|
157
|
+
merged = value.inject({}){|merged, v| DeepMerge.deep_merge!(v,merged)}
|
158
|
+
value[0].replace(merged)
|
159
|
+
value[1..-1].each do |v|
|
160
|
+
v.clear
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
assets.select{|i| !i.blank? }
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
module RailsConfig
|
2
|
+
module Settings
|
3
|
+
module DeepMerge
|
4
|
+
|
5
|
+
class InvalidParameter < StandardError; end
|
6
|
+
|
7
|
+
DEFAULT_FIELD_KNOCKOUT_PREFIX = '--'
|
8
|
+
|
9
|
+
# Deep Merge core documentation.
|
10
|
+
# deep_merge! method permits merging of arbitrary child elements. The two top level
|
11
|
+
# elements must be hashes. These hashes can contain unlimited (to stack limit) levels
|
12
|
+
# of child elements. These child elements to not have to be of the same types.
|
13
|
+
# Where child elements are of the same type, deep_merge will attempt to merge them together.
|
14
|
+
# Where child elements are not of the same type, deep_merge will skip or optionally overwrite
|
15
|
+
# the destination element with the contents of the source element at that level.
|
16
|
+
# So if you have two hashes like this:
|
17
|
+
# source = {:x => [1,2,3], :y => 2}
|
18
|
+
# dest = {:x => [4,5,'6'], :y => [7,8,9]}
|
19
|
+
# dest.deep_merge!(source)
|
20
|
+
# Results: {:x => [1,2,3,4,5,'6'], :y => 2}
|
21
|
+
# By default, "deep_merge!" will overwrite any unmergeables and merge everything else.
|
22
|
+
# To avoid this, use "deep_merge" (no bang/exclamation mark)
|
23
|
+
#
|
24
|
+
# Options:
|
25
|
+
# Options are specified in the last parameter passed, which should be in hash format:
|
26
|
+
# hash.deep_merge!({:x => [1,2]}, {:knockout_prefix => '--'})
|
27
|
+
# :preserve_unmergeables DEFAULT: false
|
28
|
+
# Set to true to skip any unmergeable elements from source
|
29
|
+
# :knockout_prefix DEFAULT: nil
|
30
|
+
# Set to string value to signify prefix which deletes elements from existing element
|
31
|
+
# :sort_merged_arrays DEFAULT: false
|
32
|
+
# Set to true to sort all arrays that are merged together
|
33
|
+
# :unpack_arrays DEFAULT: nil
|
34
|
+
# Set to string value to run "Array::join" then "String::split" against all arrays
|
35
|
+
# :merge_debug DEFAULT: false
|
36
|
+
# Set to true to get console output of merge process for debugging
|
37
|
+
#
|
38
|
+
# Selected Options Details:
|
39
|
+
# :knockout_prefix => The purpose of this is to provide a way to remove elements
|
40
|
+
# from existing Hash by specifying them in a special way in incoming hash
|
41
|
+
# source = {:x => ['--1', '2']}
|
42
|
+
# dest = {:x => ['1', '3']}
|
43
|
+
# dest.ko_deep_merge!(source)
|
44
|
+
# Results: {:x => ['2','3']}
|
45
|
+
# Additionally, if the knockout_prefix is passed alone as a string, it will cause
|
46
|
+
# the entire element to be removed:
|
47
|
+
# source = {:x => '--'}
|
48
|
+
# dest = {:x => [1,2,3]}
|
49
|
+
# dest.ko_deep_merge!(source)
|
50
|
+
# Results: {:x => ""}
|
51
|
+
# :unpack_arrays => The purpose of this is to permit compound elements to be passed
|
52
|
+
# in as strings and to be converted into discrete array elements
|
53
|
+
# irsource = {:x => ['1,2,3', '4']}
|
54
|
+
# dest = {:x => ['5','6','7,8']}
|
55
|
+
# dest.deep_merge!(source, {:unpack_arrays => ','})
|
56
|
+
# Results: {:x => ['1','2','3','4','5','6','7','8'}
|
57
|
+
# Why: If receiving data from an HTML form, this makes it easy for a checkbox
|
58
|
+
# to pass multiple values from within a single HTML element
|
59
|
+
#
|
60
|
+
# There are many tests for this library - and you can learn more about the features
|
61
|
+
# and usages of deep_merge! by just browsing the test examples
|
62
|
+
def DeepMerge.deep_merge!(source, dest, options = {})
|
63
|
+
# turn on this line for stdout debugging text
|
64
|
+
merge_debug = options[:merge_debug] || false
|
65
|
+
overwrite_unmergeable = !options[:preserve_unmergeables]
|
66
|
+
knockout_prefix = options[:knockout_prefix] || nil
|
67
|
+
if knockout_prefix == ""; raise InvalidParameter, "knockout_prefix cannot be an empty string in deep_merge!"; end
|
68
|
+
if knockout_prefix && !overwrite_unmergeable; raise InvalidParameter, "overwrite_unmergeable must be true if knockout_prefix is specified in deep_merge!"; end
|
69
|
+
# if present: we will split and join arrays on this char before merging
|
70
|
+
array_split_char = options[:unpack_arrays] || false
|
71
|
+
# request that we sort together any arrays when they are merged
|
72
|
+
sort_merged_arrays = options[:sort_merged_arrays] || false
|
73
|
+
di = options[:debug_indent] || ''
|
74
|
+
# do nothing if source is nil
|
75
|
+
if source.nil? || (!source.is_a?(FalseClass) && source.respond_to?(:blank?) && source.blank?); return dest; end
|
76
|
+
# if dest doesn't exist, then simply copy source to it
|
77
|
+
if dest.nil? && overwrite_unmergeable; dest = source; return dest; end
|
78
|
+
|
79
|
+
puts "#{di}Source class: #{source.class.inspect} :: Dest class: #{dest.class.inspect}" if merge_debug
|
80
|
+
if source.kind_of?(Hash)
|
81
|
+
puts "#{di}Hashes: #{source.inspect} :: #{dest.inspect}" if merge_debug
|
82
|
+
source.each do |src_key, src_value|
|
83
|
+
if dest.kind_of?(Hash)
|
84
|
+
puts "#{di} looping: #{src_key.inspect} => #{src_value.inspect} :: #{dest.inspect}" if merge_debug
|
85
|
+
if !dest[src_key].nil?
|
86
|
+
puts "#{di} ==>merging: #{src_key.inspect} => #{src_value.inspect} :: #{dest[src_key].inspect}" if merge_debug
|
87
|
+
dest[src_key] = deep_merge!(src_value, dest[src_key], options.merge(:debug_indent => di + ' '))
|
88
|
+
else # dest[src_key] doesn't exist so we want to create and overwrite it (but we do this via deep_merge!)
|
89
|
+
puts "#{di} ==>merging over: #{src_key.inspect} => #{src_value.inspect}" if merge_debug
|
90
|
+
# note: we rescue here b/c some classes respond to "dup" but don't implement it (Numeric, TrueClass, FalseClass, NilClass among maybe others)
|
91
|
+
begin
|
92
|
+
src_dup = src_value.dup # we dup src_value if possible because we're going to merge into it (since dest is empty)
|
93
|
+
rescue TypeError
|
94
|
+
src_dup = src_value
|
95
|
+
end
|
96
|
+
dest[src_key] = deep_merge!(src_value, src_dup, options.merge(:debug_indent => di + ' '))
|
97
|
+
end
|
98
|
+
else # dest isn't a hash, so we overwrite it completely (if permitted)
|
99
|
+
if overwrite_unmergeable
|
100
|
+
puts "#{di} overwriting dest: #{src_key.inspect} => #{src_value.inspect} -over-> #{dest.inspect}" if merge_debug
|
101
|
+
dest = overwrite_unmergeables(source, dest, options)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
elsif source.kind_of?(Array)
|
106
|
+
puts "#{di}Arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
|
107
|
+
# if we are instructed, join/split any source arrays before processing
|
108
|
+
if array_split_char
|
109
|
+
puts "#{di} split/join on source: #{source.inspect}" if merge_debug
|
110
|
+
source = source.join(array_split_char).split(array_split_char)
|
111
|
+
if dest.kind_of?(Array); dest = dest.join(array_split_char).split(array_split_char); end
|
112
|
+
end
|
113
|
+
# if there's a naked knockout_prefix in source, that means we are to truncate dest
|
114
|
+
if source.index(knockout_prefix); dest = clear_or_nil(dest); source.delete(knockout_prefix); end
|
115
|
+
if dest.kind_of?(Array)
|
116
|
+
if knockout_prefix
|
117
|
+
print "#{di} knocking out: " if merge_debug
|
118
|
+
# remove knockout prefix items from both source and dest
|
119
|
+
source.delete_if do |ko_item|
|
120
|
+
retval = false
|
121
|
+
item = ko_item.respond_to?(:gsub) ? ko_item.gsub(%r{^#{knockout_prefix}}, "") : ko_item
|
122
|
+
if item != ko_item
|
123
|
+
print "#{ko_item} - " if merge_debug
|
124
|
+
dest.delete(item)
|
125
|
+
dest.delete(ko_item)
|
126
|
+
retval = true
|
127
|
+
end
|
128
|
+
retval
|
129
|
+
end
|
130
|
+
puts if merge_debug
|
131
|
+
end
|
132
|
+
puts "#{di} merging arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
|
133
|
+
dest = dest | source
|
134
|
+
if sort_merged_arrays; dest.sort!; end
|
135
|
+
elsif overwrite_unmergeable
|
136
|
+
puts "#{di} overwriting dest: #{source.inspect} -over-> #{dest.inspect}" if merge_debug
|
137
|
+
dest = overwrite_unmergeables(source, dest, options)
|
138
|
+
end
|
139
|
+
else # src_hash is not an array or hash, so we'll have to overwrite dest
|
140
|
+
puts "#{di}Others: #{source.inspect} :: #{dest.inspect}" if merge_debug
|
141
|
+
dest = overwrite_unmergeables(source, dest, options)
|
142
|
+
end
|
143
|
+
puts "#{di}Returning #{dest.inspect}" if merge_debug
|
144
|
+
dest
|
145
|
+
end # deep_merge!
|
146
|
+
|
147
|
+
# allows deep_merge! to uniformly handle overwriting of unmergeable entities
|
148
|
+
def DeepMerge::overwrite_unmergeables(source, dest, options)
|
149
|
+
merge_debug = options[:merge_debug] || false
|
150
|
+
overwrite_unmergeable = !options[:preserve_unmergeables]
|
151
|
+
knockout_prefix = options[:knockout_prefix] || false
|
152
|
+
di = options[:debug_indent] || ''
|
153
|
+
if knockout_prefix && overwrite_unmergeable
|
154
|
+
if source.kind_of?(String) # remove knockout string from source before overwriting dest
|
155
|
+
src_tmp = source.gsub(%r{^#{knockout_prefix}},"")
|
156
|
+
elsif source.kind_of?(Array) # remove all knockout elements before overwriting dest
|
157
|
+
src_tmp = source.delete_if {|ko_item| ko_item.kind_of?(String) && ko_item.match(%r{^#{knockout_prefix}}) }
|
158
|
+
else
|
159
|
+
src_tmp = source
|
160
|
+
end
|
161
|
+
if src_tmp == source # if we didn't find a knockout_prefix then we just overwrite dest
|
162
|
+
puts "#{di}#{src_tmp.inspect} -over-> #{dest.inspect}" if merge_debug
|
163
|
+
dest = src_tmp
|
164
|
+
else # if we do find a knockout_prefix, then we just delete dest
|
165
|
+
puts "#{di}\"\" -over-> #{dest.inspect}" if merge_debug
|
166
|
+
dest = ""
|
167
|
+
end
|
168
|
+
elsif overwrite_unmergeable
|
169
|
+
dest = source
|
170
|
+
end
|
171
|
+
dest
|
172
|
+
end
|
173
|
+
|
174
|
+
def DeepMerge::clear_or_nil(obj)
|
175
|
+
if obj.respond_to?(:clear)
|
176
|
+
obj.clear
|
177
|
+
else
|
178
|
+
obj = nil
|
179
|
+
end
|
180
|
+
obj
|
181
|
+
end
|
182
|
+
|
183
|
+
end # module DeepMerge
|
184
|
+
end
|
185
|
+
end
|
data/lib/rails_config.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'extlib'
|
4
|
+
require File.dirname(__FILE__)+'/../lib/application_config/config_builder'
|
5
|
+
require File.dirname(__FILE__)+'/../lib/application_config/deep_merge'
|
6
|
+
|
7
|
+
module RailsConfig
|
8
|
+
module Settings
|
9
|
+
class BuilderTest < Test::Unit::TestCase
|
10
|
+
def setup
|
11
|
+
@settings_path = File.dirname(__FILE__)+"/test_configs"
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_missing_files
|
15
|
+
files = ["#{@settings_path}/empty1.yml", "#{@settings_path}/empty2.yml"]
|
16
|
+
config = RailsConfig::Settings::Builder.load_files(:paths => files)
|
17
|
+
assert_equal OpenStruct.new, config
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_empty_files
|
21
|
+
files = ["#{@settings_path}/empty1.yml", "#{@settings_path}/empty2.yml"]
|
22
|
+
config = RailsConfig::Settings::Builder.load_files(:paths => files)
|
23
|
+
assert_equal OpenStruct.new, config
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_common
|
27
|
+
config = RailsConfig::Settings::Builder.load_files(:paths => "#{@settings_path}/settings.yml")
|
28
|
+
assert_equal 1, config.size
|
29
|
+
assert_equal 'google.com', config.server
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_environment_override
|
33
|
+
config = RailsConfig::Settings::Builder.load_files(:paths => ["#{@settings_path}/settings.yml", "#{@settings_path}/development.yml"])
|
34
|
+
assert_equal 2, config.size
|
35
|
+
assert_equal 'google.com', config.server
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_nested
|
39
|
+
config = RailsConfig::Settings::Builder.load_files(:paths => ["#{@settings_path}/development.yml"])
|
40
|
+
assert_equal 3, config.section.size
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_array
|
44
|
+
config = RailsConfig::Settings::Builder.load_files(:paths => "#{@settings_path}/development.yml")
|
45
|
+
assert_equal 'yahoo.com', config.section.servers[0].name
|
46
|
+
assert_equal 'amazon.com', config.section.servers[1].name
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_erb
|
50
|
+
config = RailsConfig::Settings::Builder.load_files(:paths => "#{@settings_path}/development.yml")
|
51
|
+
assert_equal 6, config.computed
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_merge_hashes_from_multiple_configs
|
55
|
+
config = RailsConfig::Settings::Builder.load_files(:paths => ["#{@settings_path}/deep_merge/config1.yml", "#{@settings_path}/deep_merge/config2.yml"])
|
56
|
+
|
57
|
+
assert_equal 3, config.inner.marshal_dump.keys.size
|
58
|
+
assert_equal 3, config.inner2.inner2_inner.marshal_dump.keys.size
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def test_merge_arrays_from_multiple_configs
|
63
|
+
config = RailsConfig::Settings::Builder.load_files(:paths => ["#{@settings_path}/deep_merge/config1.yml", "#{@settings_path}/deep_merge/config2.yml"])
|
64
|
+
assert_equal 6, config.arraylist1.size
|
65
|
+
assert_equal 6, config.arraylist2.inner.size
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_merge_assets
|
69
|
+
config = RailsConfig::Settings::Builder.load_files(:paths => ["#{@settings_path}/deep_merge/config1.yml", "#{@settings_path}/deep_merge/config2.yml"])
|
70
|
+
merged = RailsConfig::Settings::Builder.merge_assets(config.hash_array)
|
71
|
+
|
72
|
+
assert_equal 3, merged.size
|
73
|
+
assert_equal 6, merged.select{|i| i.is_a?(Hash)}.first[:inner].size
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_merge_assets2
|
77
|
+
config = RailsConfig::Settings::Builder.load_files(:paths => ["#{@settings_path}/deep_merge2/config1.yml", "#{@settings_path}/deep_merge2/config2.yml"])
|
78
|
+
|
79
|
+
assert_equal 500, config.tvrage.cache
|
80
|
+
assert_equal "http://url2", config.tvrage.service_url
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_boolean_overrides
|
84
|
+
config = RailsConfig::Settings::Builder.load_files(:paths => ["#{@settings_path}/bool_override/config1.yml", "#{@settings_path}/bool_override/config2.yml"])
|
85
|
+
assert_equal false, config.override_bool
|
86
|
+
assert_equal true, config.override_bool_opposite
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
size: 1
|
2
|
+
server: google.com
|
3
|
+
inner:
|
4
|
+
something1: "blah1"
|
5
|
+
something2: "blah2"
|
6
|
+
|
7
|
+
inner2:
|
8
|
+
inner2_inner:
|
9
|
+
foo1: "blah1"
|
10
|
+
|
11
|
+
|
12
|
+
arraylist1:
|
13
|
+
- 1
|
14
|
+
- 2
|
15
|
+
- 3
|
16
|
+
|
17
|
+
arraylist2:
|
18
|
+
inner:
|
19
|
+
- 1
|
20
|
+
- 2
|
21
|
+
- 3
|
22
|
+
|
23
|
+
hash_array:
|
24
|
+
- 1
|
25
|
+
- inner:
|
26
|
+
- 1
|
27
|
+
- 2
|
28
|
+
- 3
|
@@ -0,0 +1,28 @@
|
|
1
|
+
server: google.com
|
2
|
+
|
3
|
+
inner:
|
4
|
+
something3: "blah3"
|
5
|
+
|
6
|
+
inner2:
|
7
|
+
inner2_inner:
|
8
|
+
foo2: "blah2"
|
9
|
+
foo3: "blah3"
|
10
|
+
|
11
|
+
arraylist1:
|
12
|
+
- 4
|
13
|
+
- 5
|
14
|
+
- 6
|
15
|
+
|
16
|
+
|
17
|
+
arraylist2:
|
18
|
+
inner:
|
19
|
+
- 4
|
20
|
+
- 5
|
21
|
+
- 6
|
22
|
+
|
23
|
+
hash_array:
|
24
|
+
- 2
|
25
|
+
- inner:
|
26
|
+
- 4
|
27
|
+
- 5
|
28
|
+
- 6
|
File without changes
|
File without changes
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_config
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jacques Crocker
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-07 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Provides an easy to use Application Configuration object
|
23
|
+
email: railsjedi@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- LICENSE
|
30
|
+
- README
|
31
|
+
files:
|
32
|
+
- LICENSE
|
33
|
+
- README
|
34
|
+
- Rakefile
|
35
|
+
- VERSION
|
36
|
+
- lib/rails_config.rb
|
37
|
+
- lib/rails_config/railtie.rb
|
38
|
+
- lib/rails_config/settings/builder.rb
|
39
|
+
- lib/rails_config/settings/deep_merge.rb
|
40
|
+
- test/config_builder_test.rb
|
41
|
+
- test/test_configs/app_config.yml
|
42
|
+
- test/test_configs/bool_override/config1.yml
|
43
|
+
- test/test_configs/bool_override/config2.yml
|
44
|
+
- test/test_configs/deep_merge/config1.yml
|
45
|
+
- test/test_configs/deep_merge/config2.yml
|
46
|
+
- test/test_configs/deep_merge2/config1.yml
|
47
|
+
- test/test_configs/deep_merge2/config2.yml
|
48
|
+
- test/test_configs/development.yml
|
49
|
+
- test/test_configs/empty1.yml
|
50
|
+
- test/test_configs/empty2.yml
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://github.com/railsjedi/rails_config
|
53
|
+
licenses: []
|
54
|
+
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options:
|
57
|
+
- --charset=UTF-8
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
hash: 3
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
version: "0"
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.3.7
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: provides an Settings for rails3 that reads config/settings.yml
|
85
|
+
test_files:
|
86
|
+
- test/config_builder_test.rb
|