rconfig 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/ChangeLog +50 -0
- data/Credits +13 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +30 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +0 -0
- data/Rakefile +11 -0
- data/demo/application.conf +3 -0
- data/demo/demo.conf +13 -0
- data/demo/demo.rb +14 -0
- data/demo/demo.xml +13 -0
- data/demo/demo.yml +12 -0
- data/doc/classes/ClassVariables.html +111 -0
- data/doc/classes/ConfigError.html +120 -0
- data/doc/classes/ConfigHash.html +354 -0
- data/doc/classes/Constants.html +226 -0
- data/doc/classes/Hash.html +269 -0
- data/doc/classes/InvalidConfigPathError.html +119 -0
- data/doc/classes/Object.html +220 -0
- data/doc/classes/PropertiesFileParser.html +282 -0
- data/doc/classes/RConfig.html +1745 -0
- data/doc/created.rid +1 -0
- data/doc/files/README_rdoc.html +271 -0
- data/doc/files/lib/rconfig/class_variables_rb.html +107 -0
- data/doc/files/lib/rconfig/config_hash_rb.html +114 -0
- data/doc/files/lib/rconfig/constants_rb.html +101 -0
- data/doc/files/lib/rconfig/core_ext/hash_rb.html +114 -0
- data/doc/files/lib/rconfig/core_ext/object_rb.html +101 -0
- data/doc/files/lib/rconfig/core_ext_rb.html +114 -0
- data/doc/files/lib/rconfig/exceptions_rb.html +110 -0
- data/doc/files/lib/rconfig/properties_file_parser_rb.html +146 -0
- data/doc/files/lib/rconfig/rconfig_rb.html +186 -0
- data/doc/files/lib/rconfig_rb.html +117 -0
- data/doc/fr_class_index.html +35 -0
- data/doc/fr_file_index.html +37 -0
- data/doc/fr_method_index.html +75 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +208 -0
- data/lib/generators/rconfig/install_generator.rb +13 -0
- data/lib/generators/rconfig/templates/rconfig.rb +82 -0
- data/lib/rconfig.rb +98 -32
- data/lib/rconfig/callbacks.rb +46 -0
- data/lib/rconfig/cascade.rb +56 -0
- data/lib/rconfig/config.rb +78 -0
- data/lib/rconfig/constants.rb +58 -0
- data/lib/rconfig/core_ext/array.rb +3 -0
- data/lib/rconfig/core_ext/hash.rb +56 -57
- data/lib/rconfig/core_ext/nil.rb +5 -0
- data/lib/rconfig/core_ext/string.rb +3 -0
- data/lib/rconfig/core_methods.rb +276 -0
- data/lib/rconfig/exceptions.rb +21 -8
- data/lib/rconfig/load_paths.rb +55 -0
- data/lib/rconfig/logger.rb +86 -0
- data/lib/rconfig/properties_file.rb +138 -0
- data/lib/rconfig/reload.rb +75 -0
- data/lib/rconfig/settings.rb +93 -0
- data/lib/rconfig/utils.rb +175 -0
- data/lib/tasks/gem.rake +14 -0
- data/lib/tasks/rdoc.rake +11 -0
- data/lib/tasks/spec.rake +25 -0
- data/rconfig.gemspec +33 -0
- data/spec/core_ext/object_spec.rb +44 -0
- data/spec/rconfig_spec.rb +7 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +16 -0
- metadata +128 -32
- data/lib/rconfig/config_hash.rb +0 -105
- data/lib/rconfig/core_ext.rb +0 -6
- data/lib/rconfig/properties_file_parser.rb +0 -80
- data/lib/rconfig/rconfig.rb +0 -871
- data/test/rconfig_test.rb +0 -381
data/lib/rconfig.rb
CHANGED
@@ -1,39 +1,105 @@
|
|
1
|
-
|
1
|
+
##
|
2
|
+
#
|
2
3
|
# Copyright (c) 2009 Rahmal Conda <rahmal@gmail.com>
|
4
|
+
# -------------------------------------------------------------------
|
5
|
+
# The complete solution for Ruby Configuration Management. RConfig is a Ruby library that
|
6
|
+
# manages configuration within Ruby applications. It bridges the gap between yaml, xml, and
|
7
|
+
# key/value based properties files, by providing a centralized solution to handle application
|
8
|
+
# configuration from one location. It provides the simplicity of hash-based access, that
|
9
|
+
# Rubyists have come to know and love, supporting your configuration style of choice, while
|
10
|
+
# providing many new features, and an elegant API.
|
11
|
+
#
|
12
|
+
# -------------------------------------------------------------------
|
13
|
+
# * Simple, easy to install and use.
|
14
|
+
# * Supports yaml, xml, and properties files.
|
15
|
+
# * Yaml and xml files supprt infinite level of configuration grouping.
|
16
|
+
# * Intuitive dot-notation 'key chaining' argument access.
|
17
|
+
# * Simple well-known hash/array based argument access.
|
18
|
+
# * Implements multilevel caching to reduce disk access.
|
19
|
+
# * Short-hand access to 'global' application configuration, and shell environment.
|
20
|
+
# * Overlays multiple configuration files to support environment, host, and
|
21
|
+
# even locale-specific configuration.
|
22
|
+
#
|
23
|
+
# -------------------------------------------------------------------
|
24
|
+
# The overlay order of the config files is defined by SUFFIXES:
|
25
|
+
# * nil
|
26
|
+
# * _local
|
27
|
+
# * _config
|
28
|
+
# * _local_config
|
29
|
+
# * _{environment} (.i.e _development)
|
30
|
+
# * _{environment}_local (.i.e _development_local)
|
31
|
+
# * _{hostname} (.i.e _whiskey)
|
32
|
+
# * _{hostname}_config_local (.i.e _whiskey_config_local)
|
33
|
+
#
|
34
|
+
# -------------------------------------------------------------------
|
35
|
+
#
|
36
|
+
# Example:
|
37
|
+
#
|
38
|
+
# shell/console =>
|
39
|
+
# export LANG=en
|
40
|
+
#
|
41
|
+
# demo.yml =>
|
42
|
+
# server:
|
43
|
+
# address: host.domain.com
|
44
|
+
# port: 81
|
45
|
+
# ...
|
3
46
|
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
#++
|
47
|
+
# application.properties =>
|
48
|
+
# debug_level = verbose
|
49
|
+
# ...
|
50
|
+
#
|
51
|
+
# demo.rb =>
|
52
|
+
# require 'rconfig'
|
53
|
+
# RConfig.load_paths = ['$HOME/config', '#{APP_ROOT}/config', '/demo/conf']
|
54
|
+
# RConfig.demo[:server][:port] => 81
|
55
|
+
# RConfig.demo.server.address => 'host.domain.com'
|
56
|
+
#
|
57
|
+
# RConfig[:debug_level] => 'verbose'
|
58
|
+
# RConfig[:lang] => 'en'
|
59
|
+
# ...
|
60
|
+
#
|
61
|
+
require 'active_support'
|
62
|
+
require 'rconfig/core_ext/array'
|
63
|
+
require 'rconfig/core_ext/hash'
|
64
|
+
require 'rconfig/core_ext/nil'
|
23
65
|
|
24
|
-
|
66
|
+
module RConfig
|
67
|
+
VERSION = '0.4.0'
|
25
68
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
69
|
+
autoload :Socket, 'socket'
|
70
|
+
autoload :YAML, 'yaml'
|
71
|
+
autoload :ERB, 'erb'
|
72
|
+
autoload :Logger, 'logger'
|
73
|
+
autoload :Singleton, 'singleton'
|
74
|
+
|
75
|
+
autoload :Concern, 'active_support/concern'
|
76
|
+
autoload :Hash, 'active_support/core_ext/hash/conversions'
|
77
|
+
autoload :HashWithIndifferentAccess, 'active_support/core_ext/hash/indifferent_access'
|
31
78
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
79
|
+
autoload :Config, 'rconfig/config'
|
80
|
+
autoload :Logger, 'rconfig/logger'
|
81
|
+
autoload :Exceptions, 'rconfig/exceptions'
|
82
|
+
autoload :Utils, 'rconfig/utils'
|
83
|
+
autoload :Constants, 'rconfig/constants'
|
84
|
+
autoload :Settings, 'rconfig/settings'
|
85
|
+
autoload :ConfigError, 'rconfig/exceptions'
|
86
|
+
autoload :LoadPaths, 'rconfig/load_paths'
|
87
|
+
autoload :Cascade, 'rconfig/cascade'
|
88
|
+
autoload :Callbacks, 'rconfig/callbacks'
|
89
|
+
autoload :Reload, 'rconfig/reload'
|
90
|
+
autoload :CoreMethods, 'rconfig/core_methods'
|
91
|
+
autoload :PropertiesFile, 'rconfig/properties_file'
|
92
|
+
autoload :InstallGenerator, 'generators/rconfig/install_generator'
|
36
93
|
|
37
|
-
|
38
|
-
$config = RConfig.instance
|
94
|
+
extend ActiveSupport::Concern
|
39
95
|
|
96
|
+
extend Utils
|
97
|
+
extend Constants
|
98
|
+
extend Settings
|
99
|
+
extend Exceptions
|
100
|
+
extend LoadPaths
|
101
|
+
extend Cascade
|
102
|
+
extend Callbacks
|
103
|
+
extend Reload
|
104
|
+
extend CoreMethods
|
105
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module RConfig
|
2
|
+
module Callbacks
|
3
|
+
|
4
|
+
##
|
5
|
+
# Register a callback when a config has been reloaded. If no config name
|
6
|
+
# is specified, the callback will be registered under the name :ANY. The
|
7
|
+
# name :ANY will register a callback for any config file change.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# class MyClass
|
12
|
+
# self.my_config = { }
|
13
|
+
# RConfig.on_load(:cache) do
|
14
|
+
# self.my_config = { }
|
15
|
+
# end
|
16
|
+
# def my_config
|
17
|
+
# self.my_config ||= something_expensive_thing_on_config(RConfig.cache.memory_limit)
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
def on_load(*args, &blk)
|
22
|
+
args << :ANY if args.empty?
|
23
|
+
proc = blk.to_proc
|
24
|
+
|
25
|
+
# Call proc on registration.
|
26
|
+
proc.call()
|
27
|
+
|
28
|
+
# Register callback proc.
|
29
|
+
args.each do |name|
|
30
|
+
(self.callbacks[name.to_s] ||= []) << proc
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Executes all of the reload callbacks registered to the specified config name,
|
36
|
+
# and all of the callbacks registered to run on any config, as specified by the
|
37
|
+
# :ANY symbol.
|
38
|
+
def fire_on_load(name)
|
39
|
+
procs = (self.callbacks['ANY'] || RConfig::EMPTY_ARRAY) + (self.callbacks[name] || RConfig::EMPTY_ARRAY)
|
40
|
+
procs.uniq!
|
41
|
+
logger.debug "fire_on_load(#{name.inspect}): callbacks[#{procs.inspect}]" unless procs.empty?
|
42
|
+
procs.each { |proc| proc.call() }
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module RConfig
|
2
|
+
module Cascade
|
3
|
+
include Constants
|
4
|
+
|
5
|
+
##
|
6
|
+
# Sets a custome overlay for
|
7
|
+
def overlay=(value)
|
8
|
+
reload(false) if self.overlay != value
|
9
|
+
self.overlay = value && value.dup.freeze
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Returns a list of suffixes to try for a given config name.
|
14
|
+
#
|
15
|
+
# A config name with an explicit overlay (e.g.: 'name_GB')
|
16
|
+
# overrides any current _overlay.
|
17
|
+
#
|
18
|
+
# This allows code to specifically ask for config overlays
|
19
|
+
# for a particular locale.
|
20
|
+
#
|
21
|
+
def suffixes_for(name)
|
22
|
+
name = name.to_s
|
23
|
+
self.suffixes[name] ||= begin
|
24
|
+
ol = overlay
|
25
|
+
name_x = name.dup
|
26
|
+
if name_x.sub!(/_([A-Z]+)$/, '')
|
27
|
+
ol = $1
|
28
|
+
end
|
29
|
+
name_x.freeze
|
30
|
+
result = if ol
|
31
|
+
ol_ = ol.upcase
|
32
|
+
ol = ol.downcase
|
33
|
+
x = []
|
34
|
+
SUFFIXES.each do |suffix|
|
35
|
+
# Standard, no overlay:
|
36
|
+
# e.g.: database_<suffix>.yml
|
37
|
+
x << suffix
|
38
|
+
|
39
|
+
# Overlay:
|
40
|
+
# e.g.: database_(US|GB)_<suffix>.yml
|
41
|
+
x << [ol_, suffix]
|
42
|
+
end
|
43
|
+
[name_x, x.freeze]
|
44
|
+
else
|
45
|
+
[name.dup.freeze, SUFFIXES.freeze]
|
46
|
+
end
|
47
|
+
result.freeze
|
48
|
+
|
49
|
+
logger.debug "suffixes(#{name}) => #{result.inspect}"
|
50
|
+
|
51
|
+
result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
##
|
2
|
+
# Copyright (c) 2009 Rahmal Conda <rahmal@gmail.com>
|
3
|
+
#
|
4
|
+
# Config is a special class, derived from HashWithIndifferentAccess.
|
5
|
+
# It was specifically created for handling config data or creating mock
|
6
|
+
# objects from yaml files. It provides a dotted notation for accessing
|
7
|
+
# embedded hash values, similar to the way one might traverse a object tree.
|
8
|
+
#
|
9
|
+
module RConfig
|
10
|
+
class Config < ::HashWithIndifferentAccess
|
11
|
+
|
12
|
+
##
|
13
|
+
# HashWithIndifferentAccess#default is broken in early versions of Rails.
|
14
|
+
# This is defined to use the hash version in Config#default
|
15
|
+
define_method(:hash_default, Hash.instance_method(:default))
|
16
|
+
|
17
|
+
##
|
18
|
+
# Dotted notation can be used with arguments (useful for creating mock objects)
|
19
|
+
# in the YAML file the method name is a key, argument(s) form a nested key,
|
20
|
+
# so that the correct value is retrieved and returned.
|
21
|
+
#
|
22
|
+
# For example loading to variable foo a yaml file that looks like:
|
23
|
+
# customer:
|
24
|
+
# id: 12345678
|
25
|
+
# verified:
|
26
|
+
# phone: verified
|
27
|
+
# :address: info_not_available
|
28
|
+
# ? [name, employer]
|
29
|
+
# : not_verified
|
30
|
+
#
|
31
|
+
# Allows the following calls:
|
32
|
+
# foo.customer.id => 12345678
|
33
|
+
# foo.customer.verified.phone => verified
|
34
|
+
# foo.customer.verified("phone") => verified
|
35
|
+
# foo.customer.verified(:address) => info_not_available
|
36
|
+
# foo.customer.verified("name", "employer") => not_verified
|
37
|
+
#
|
38
|
+
# Note that :address is specified as a symbol, where phone is just a string.
|
39
|
+
# Depending on what kind of parameter the method being mocked out is going
|
40
|
+
# to be called with, define in the YAML file either a string or a symbol.
|
41
|
+
# This also works inside the composite array keys.
|
42
|
+
def method_missing(method, *args)
|
43
|
+
method = method.to_s
|
44
|
+
return if method == 'default_key'
|
45
|
+
value = self[method]
|
46
|
+
case args.size
|
47
|
+
when 0 # e.g.: RConfig.application.method
|
48
|
+
value
|
49
|
+
when 1 # e.g.: RConfig.application.method(one_arg)
|
50
|
+
value.send(args[0])
|
51
|
+
else # e.g.: RConfig.application.method(arg_one, args_two, ...)
|
52
|
+
value[args]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Allow hash.default => hash['default']
|
58
|
+
# without breaking Hash's usage of default(key)
|
59
|
+
def default(key = self.default_key)
|
60
|
+
key = key.to_s if key.is_a?(Symbol)
|
61
|
+
if key == self.default_key
|
62
|
+
self['default'] if key?('default')
|
63
|
+
else
|
64
|
+
hash_default(key)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
##
|
71
|
+
# Override HashWithIndifferentAccess#convert_value
|
72
|
+
# return instance of Config for Hash values.
|
73
|
+
def convert_value(value)
|
74
|
+
value.is_a?(Hash) ? self.class.new(value).freeze : super
|
75
|
+
end
|
76
|
+
|
77
|
+
end # class Config
|
78
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module RConfig
|
2
|
+
module Constants
|
3
|
+
|
4
|
+
# Sets CONFIG_ROOT to RAILS_ROOT/config unless it has already
|
5
|
+
# been defined (i.e. in rails env, or calling ruby app).
|
6
|
+
CONFIG_ROOT = File.join(::Rails.root || '', 'config').gsub(/^\//, '').gsub(/\/$/,'') if defined?(::Rails) && !defined?(CONFIG_ROOT)
|
7
|
+
|
8
|
+
# ENV TIER i.e. (development, integration, staging, or production)
|
9
|
+
# Defaults to RAILS_ENV if running in Rails, otherwise, it checks
|
10
|
+
# if ENV['TIER'] is present. If not, it assumes production.
|
11
|
+
ENV_TIER = (defined?(RAILS_ENV) ? RAILS_ENV : (ENV['TIER'] || 'production')) unless defined? ENV_TIER
|
12
|
+
|
13
|
+
# yml, yaml => yaml files, parsable by YAML library
|
14
|
+
YML_FILE_TYPES = [:yml, :yaml].freeze unless defined? YML_FILE_TYPES
|
15
|
+
|
16
|
+
# xml => self-explanatory
|
17
|
+
XML_FILE_TYPES = [:xml].freeze unless defined? XML_FILE_TYPES
|
18
|
+
|
19
|
+
# conf, properties => <key=value> based config files
|
20
|
+
CNF_FILE_TYPES = [:cnf, :conf, :config, :properties].freeze unless defined? CNF_FILE_TYPES
|
21
|
+
|
22
|
+
# The type of file used for config. Valid choices
|
23
|
+
# include (yml, yaml, xml, conf, config, properties)
|
24
|
+
CONFIG_FILE_TYPES = (YML_FILE_TYPES + XML_FILE_TYPES + CNF_FILE_TYPES).freeze unless defined? CONFIG_FILE_TYPES
|
25
|
+
|
26
|
+
# Use CONFIG_HOSTNAME environment variable to
|
27
|
+
# test host-based configurations.
|
28
|
+
HOSTNAME = ENV['CONFIG_HOSTNAME'] || Socket.gethostname unless defined? HOSTNAME
|
29
|
+
|
30
|
+
# Short Hostname: removes all chars from HOSTNAME, after first "."
|
31
|
+
# Used to specify machine-specific config files.
|
32
|
+
HOSTNAME_SHORT = HOSTNAME.sub(/\..*$/, '').freeze unless defined? HOSTNAME_SHORT
|
33
|
+
|
34
|
+
# This is an array of filename suffixes facilitates cascading
|
35
|
+
# configuration overrides (i.e. 'services_local', 'services_development').
|
36
|
+
# These files get loaded in the order of the array. Meaning the last file
|
37
|
+
# loaded overrides everything before it. So config files suffixed with
|
38
|
+
# hostname has the highest precedence, and therefore overrides everything.
|
39
|
+
# Example:
|
40
|
+
# database_local.yml overrides database.yml
|
41
|
+
# database_staging.yml overrides database_local.yml
|
42
|
+
# database_appsvr01.yml overrides database_integration.yml
|
43
|
+
SUFFIXES = [
|
44
|
+
nil, # Empty suffix, used for default config file (i.e. database.yml).
|
45
|
+
:local, # Allows user to create 'local' overrides (i.e. database_local.yml), primarily used for development.
|
46
|
+
:config, :local_config,
|
47
|
+
ENV_TIER, [ENV_TIER, :local], # Environment configs (i.e. development, test, production).
|
48
|
+
HOSTNAME_SHORT, [HOSTNAME_SHORT, :config_local], # Short hostname (i.e. appsvr01), for server-specific configs.
|
49
|
+
HOSTNAME, [HOSTNAME, :config_local] # Hostname (i.e. appsvr01.acme.com), for server/domain-specific configs.
|
50
|
+
] unless defined? SUFFIXES
|
51
|
+
|
52
|
+
# Used in place of undefined but expected arrays,
|
53
|
+
# to prevent creating a bunch of unecesary arrays
|
54
|
+
# in memory. See ConfigCore.fire_on_load
|
55
|
+
EMPTY_ARRAY = [].freeze unless defined? EMPTY_ARRAY
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
#++
|
1
|
+
##
|
3
2
|
# source: http://rubyforge.org/projects/facets/
|
4
3
|
# version: 1.7.46
|
5
4
|
# license: Ruby License
|
@@ -7,12 +6,12 @@
|
|
7
6
|
# BUG: weave is destructive to values in the source hash that are arrays!
|
8
7
|
# (this is acceptable for RConfig's use as the basis for weave!)
|
9
8
|
#
|
10
|
-
|
9
|
+
#
|
11
10
|
class Hash
|
12
11
|
|
13
12
|
##
|
14
13
|
# Weaves the contents of two hashes producing a new hash.
|
15
|
-
def weave(other_hash,
|
14
|
+
def weave(other_hash, clobber=false)
|
16
15
|
return self unless other_hash
|
17
16
|
unless other_hash.kind_of?(Hash)
|
18
17
|
raise ArgumentError, "RConfig: (Hash#weave) expected <Hash>, but was <#{other_hash.class}>"
|
@@ -24,68 +23,68 @@ class Hash
|
|
24
23
|
|
25
24
|
self_dup[key] =
|
26
25
|
|
27
|
-
|
26
|
+
if self_node = self_dup[key]
|
28
27
|
|
29
|
-
|
30
|
-
|
28
|
+
case self_node
|
29
|
+
when Hash
|
31
30
|
|
32
|
-
|
33
|
-
|
31
|
+
# hash1, hash2 => hash3 (recursive +)
|
32
|
+
if other_node.is_a?(Hash)
|
34
33
|
|
35
|
-
|
34
|
+
self_node.weave(other_node, clobber)
|
36
35
|
|
37
|
-
|
38
|
-
|
36
|
+
# hash, array => error (Can't weave'em, must clobber.)
|
37
|
+
elsif other_node.is_a?(Array) && !clobber
|
39
38
|
|
40
|
-
|
39
|
+
raise(ArgumentError, "RConfig: (Hash#weave) Can't weave Hash and Array")
|
41
40
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
# hash, array => hash[key] = array
|
42
|
+
# hash, value => hash[key] = value
|
43
|
+
else
|
44
|
+
other_node
|
45
|
+
end
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
47
|
+
when Array
|
48
|
+
|
49
|
+
# array, hash => array << hash
|
50
|
+
# array1, array2 => array1 + array2
|
51
|
+
# array, value => array << value
|
52
|
+
unless clobber
|
53
|
+
case other_node
|
54
|
+
when Hash
|
55
|
+
self_node << other_node
|
56
|
+
when Array
|
57
|
+
self_node + other_node
|
58
|
+
else
|
59
|
+
self_node << other_node
|
60
|
+
end
|
61
|
+
|
62
|
+
# array, hash => hash
|
63
|
+
# array1, array2 => array2
|
64
|
+
# array, value => value
|
65
|
+
else
|
66
|
+
other_node
|
67
|
+
end
|
68
|
+
|
69
|
+
else
|
69
70
|
|
70
|
-
|
71
|
+
# value, array => array.unshift(value)
|
72
|
+
if other_node.is_a?(Array) && !clobber
|
73
|
+
other_node.unshift(self_node)
|
71
74
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
+
# value1, value2 => value2
|
76
|
+
else
|
77
|
+
other_node
|
78
|
+
end
|
75
79
|
|
76
|
-
|
77
|
-
else
|
78
|
-
other_node
|
79
|
-
end
|
80
|
-
|
81
|
-
end # case self_node
|
80
|
+
end # case self_node
|
82
81
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
82
|
+
# Target hash didn't have a node matching the key,
|
83
|
+
# so just add it from the source hash.
|
84
|
+
# !self_dup.has_key?(key) => self_dup.add(key, other_node)
|
85
|
+
else
|
86
|
+
other_node
|
87
|
+
end
|
89
88
|
|
90
89
|
} # other_hash.each
|
91
90
|
|
@@ -95,8 +94,8 @@ class Hash
|
|
95
94
|
##
|
96
95
|
# Same as self.weave(other_hash, dont_clobber) except that it weaves other hash
|
97
96
|
# to itself, rather than create a new hash.
|
98
|
-
def weave!(other_hash,
|
99
|
-
weaved_hash = self.weave(other_hash,
|
97
|
+
def weave!(other_hash, clobber=false)
|
98
|
+
weaved_hash = self.weave(other_hash, clobber)
|
100
99
|
self.merge!(weaved_hash)
|
101
100
|
end
|
102
101
|
|