rconfig 0.3.3 → 0.4.0
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/.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
|
|