adhearsion-loquacious 1.9.2
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 +23 -0
- data/Guardfile +5 -0
- data/History.txt +127 -0
- data/README.rdoc +229 -0
- data/Rakefile +30 -0
- data/adhearsion-loquacious.gemspec +32 -0
- data/examples/gutters.rb +29 -0
- data/examples/nested.rb +43 -0
- data/examples/simple.rb +20 -0
- data/lib/loquacious.rb +165 -0
- data/lib/loquacious/configuration.rb +406 -0
- data/lib/loquacious/configuration/help.rb +249 -0
- data/lib/loquacious/configuration/iterator.rb +158 -0
- data/lib/loquacious/core_ext/string.rb +75 -0
- data/lib/loquacious/undefined.rb +92 -0
- data/lib/loquacious/utility.rb +14 -0
- data/loquacious.gemspec +32 -0
- data/spec/configuration_spec.rb +513 -0
- data/spec/help_spec.rb +369 -0
- data/spec/iterator_spec.rb +70 -0
- data/spec/loquacious_spec.rb +76 -0
- data/spec/spec_helper.rb +70 -0
- data/spec/string_spec.rb +53 -0
- data/spec/utility_spec.rb +28 -0
- data/version.txt +1 -0
- metadata +102 -0
data/examples/gutters.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Using Ruby heredocs for descriptions, the Loquacious configuration will
|
|
2
|
+
# strip out leading whitespace but preserve line breaks. Gutter lines can be
|
|
3
|
+
# used to mark where leading whitespace should be preserved. This is useful
|
|
4
|
+
# if you need to provide example code in your descriptions.
|
|
5
|
+
|
|
6
|
+
require 'loquacious'
|
|
7
|
+
include Loquacious
|
|
8
|
+
|
|
9
|
+
Configuration.for(:gutters) {
|
|
10
|
+
log_path "log/development.log", :desc => <<-__
|
|
11
|
+
The path to the log file to use. Defaults to log/\#{environment}.log
|
|
12
|
+
(e.g. log/development.log or log/production.log).
|
|
13
|
+
|
|
|
14
|
+
| config.log_path = File.join(ROOT, "log", "\#{environment}.log
|
|
15
|
+
|
|
|
16
|
+
__
|
|
17
|
+
|
|
18
|
+
log_level :warn, :desc => <<-__
|
|
19
|
+
|The log level to use for the default Rails logger. In production mode,
|
|
20
|
+
|this defaults to :info. In development mode, it defaults to :debug.
|
|
21
|
+
|
|
|
22
|
+
| config.log_level = 'debug'
|
|
23
|
+
| config.log_level = :warn
|
|
24
|
+
|
|
|
25
|
+
__
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
help = Configuration.help_for :gutters
|
|
29
|
+
help.show :values => true
|
data/examples/nested.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Here we show how to used nested configuration options by taking a subset
|
|
2
|
+
# of some common Rails configuration options. Also, descriptions can be give
|
|
3
|
+
# before the option or they can be given inline using Ruby hash notation. If
|
|
4
|
+
# both are present, then the inline description takes precedence.
|
|
5
|
+
#
|
|
6
|
+
# Multiline descriptions are provided using Ruby heredocs. Leading
|
|
7
|
+
# whitespace is stripped and line breaks are preserved when descriptions
|
|
8
|
+
# are printed using the help object.
|
|
9
|
+
|
|
10
|
+
require 'loquacious'
|
|
11
|
+
include Loquacious
|
|
12
|
+
|
|
13
|
+
Configuration.for(:nested) {
|
|
14
|
+
root_path '.', :desc => "The application's base directory."
|
|
15
|
+
|
|
16
|
+
desc "Configuration options for ActiveRecord::Base."
|
|
17
|
+
active_record {
|
|
18
|
+
colorize_logging true, :desc => <<-__
|
|
19
|
+
Determines whether to use ANSI codes to colorize the logging statements committed
|
|
20
|
+
by the connection adapter. These colors make it much easier to overview things
|
|
21
|
+
during debugging (when used through a reader like +tail+ and on a black background),
|
|
22
|
+
but may complicate matters if you use software like syslog. This is true, by default.
|
|
23
|
+
__
|
|
24
|
+
|
|
25
|
+
default_timezone :local, :desc => <<-__
|
|
26
|
+
Determines whether to use Time.local (using :local) or Time.utc (using :utc)
|
|
27
|
+
when pulling dates and times from the database. This is set to :local by default.
|
|
28
|
+
__
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
log_level :info, :desc => <<-__
|
|
32
|
+
The log level to use for the default Rails logger. In production mode,
|
|
33
|
+
this defaults to :info. In development mode, it defaults to :debug.
|
|
34
|
+
__
|
|
35
|
+
|
|
36
|
+
log_path 'log/development.log', :desc => <<-__
|
|
37
|
+
The path to the log file to use. Defaults to log/\#{environment}.log
|
|
38
|
+
(e.g. log/development.log or log/production.log).
|
|
39
|
+
__
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
help = Configuration.help_for :nested
|
|
43
|
+
help.show :values => true
|
data/examples/simple.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# A simple example that configures three options (a b c) along with
|
|
2
|
+
# descriptions for each option. The descriptions along with the
|
|
3
|
+
# values for the configuration options are printed to the terminal.
|
|
4
|
+
|
|
5
|
+
require 'loquacious'
|
|
6
|
+
include Loquacious
|
|
7
|
+
|
|
8
|
+
Configuration.for(:simple) {
|
|
9
|
+
desc 'Your first configuration option'
|
|
10
|
+
a "value for 'a'"
|
|
11
|
+
|
|
12
|
+
desc 'To be or not to be'
|
|
13
|
+
b "William Shakespeare"
|
|
14
|
+
|
|
15
|
+
desc 'The underpinings of Ruby'
|
|
16
|
+
c 42
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
help = Configuration.help_for :simple
|
|
20
|
+
help.show :values => true
|
data/lib/loquacious.rb
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
module Loquacious
|
|
2
|
+
|
|
3
|
+
# :stopdoc:
|
|
4
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
|
5
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
|
6
|
+
KEEPERS = (RUBY_PLATFORM == 'java') ?
|
|
7
|
+
%r/^__|^object_id$|^initialize$|^instance_eval$|^singleton_method_added$|^\w+\?$/ :
|
|
8
|
+
%r/^__|^object_id$|^initialize$|^instance_eval$|^\w+\?$/
|
|
9
|
+
# :startdoc:
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
# These control respectively if ENV overrides are used, and which prefix is used
|
|
14
|
+
# Defaults are true and LOQ, and are declared at the bottom"
|
|
15
|
+
attr_accessor :env_config
|
|
16
|
+
attr_accessor :env_prefix
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Returns the configuration associated with the given _name_. If a
|
|
20
|
+
# _block_ is given, then it will be used to create the configuration.
|
|
21
|
+
#
|
|
22
|
+
# The same _name_ can be used multiple times with different
|
|
23
|
+
# configuration blocks. Each different block will be used to add to the
|
|
24
|
+
# configuration; i.e. the configurations are additive.
|
|
25
|
+
#
|
|
26
|
+
def configuration_for( name, &block )
|
|
27
|
+
::Loquacious::Configuration.for(name, &block)
|
|
28
|
+
end
|
|
29
|
+
alias :configuration :configuration_for
|
|
30
|
+
alias :config_for :configuration_for
|
|
31
|
+
alias :config :configuration_for
|
|
32
|
+
|
|
33
|
+
# Set the default properties for the configuration associated with the
|
|
34
|
+
# given _name_. A _block_ must be provided to this method.
|
|
35
|
+
#
|
|
36
|
+
# The same _name_ can be used multiple times with different configuration
|
|
37
|
+
# blocks. Each block will add or modify the configuration; i.e. the
|
|
38
|
+
# configurations are additive.
|
|
39
|
+
#
|
|
40
|
+
def defaults_for( name, &block )
|
|
41
|
+
::Loquacious::Configuration.defaults_for(name, &block)
|
|
42
|
+
end
|
|
43
|
+
alias :defaults :defaults_for
|
|
44
|
+
|
|
45
|
+
# Returns a Help instance for the configuration associated with the
|
|
46
|
+
# given _name_. See the Help#initialize method for the options that
|
|
47
|
+
# can be used with this method.
|
|
48
|
+
#
|
|
49
|
+
def help_for( name, opts = {} )
|
|
50
|
+
::Loquacious::Configuration.help_for(name, opts)
|
|
51
|
+
end
|
|
52
|
+
alias :help :help_for
|
|
53
|
+
|
|
54
|
+
# Returns the version string for the library.
|
|
55
|
+
#
|
|
56
|
+
def version
|
|
57
|
+
@version ||= File.read(path('version.txt')).strip
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns the library path for the module. If any arguments are given,
|
|
61
|
+
# they will be joined to the end of the libray path using
|
|
62
|
+
# <tt>File.join</tt>.
|
|
63
|
+
#
|
|
64
|
+
def libpath( *args, &block )
|
|
65
|
+
rv = args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
|
66
|
+
if block
|
|
67
|
+
begin
|
|
68
|
+
$LOAD_PATH.unshift LIBPATH
|
|
69
|
+
rv = block.call
|
|
70
|
+
ensure
|
|
71
|
+
$LOAD_PATH.shift
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
return rv
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Returns the lpath for the module. If any arguments are given, they
|
|
78
|
+
# will be joined to the end of the path using <tt>File.join</tt>.
|
|
79
|
+
#
|
|
80
|
+
def path( *args, &block )
|
|
81
|
+
rv = args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
|
82
|
+
if block
|
|
83
|
+
begin
|
|
84
|
+
$LOAD_PATH.unshift PATH
|
|
85
|
+
rv = block.call
|
|
86
|
+
ensure
|
|
87
|
+
$LOAD_PATH.shift
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
return rv
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# This is merely a convenience method to remove methods from the
|
|
94
|
+
# Loquacious::Configuration class. Some ruby gems add lots of crap to the
|
|
95
|
+
# Kernel module, and this interferes with the configuration system. The
|
|
96
|
+
# remove method should be used to anihilate unwanted methods from the
|
|
97
|
+
# configuration class as needed.
|
|
98
|
+
#
|
|
99
|
+
# Loquacious.remove :gem # courtesy of rubygems
|
|
100
|
+
# Loquacious.remove :test, :file # courtesy of rake
|
|
101
|
+
# Loquacious.remove :main # courtesy of main
|
|
102
|
+
# Loquacious.remove :timeout # courtesy of timeout
|
|
103
|
+
#
|
|
104
|
+
def remove( *args )
|
|
105
|
+
args.each { |name|
|
|
106
|
+
name = name.to_s.delete('=')
|
|
107
|
+
code = <<-__
|
|
108
|
+
undef_method :#{name} rescue nil
|
|
109
|
+
undef_method :#{name}= rescue nil
|
|
110
|
+
__
|
|
111
|
+
Loquacious::Configuration.module_eval code
|
|
112
|
+
Loquacious::Configuration::DSL.module_eval code
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# A helper method that will create a deep copy of a given Configuration
|
|
117
|
+
# object. This method accepts either a Configuration instance or a name
|
|
118
|
+
# that can be used to lookup the Configuration instance (via the
|
|
119
|
+
# "Loquacious.configuration_for" method).
|
|
120
|
+
#
|
|
121
|
+
# Loquacious.copy(config)
|
|
122
|
+
# Loquacious.copy('name')
|
|
123
|
+
#
|
|
124
|
+
# Optionally a block can be given. It will be used to modify the returned
|
|
125
|
+
# copy with the given values. The Configuration object being copied is
|
|
126
|
+
# never modified by this method.
|
|
127
|
+
#
|
|
128
|
+
# Loquacious.copy(config) {
|
|
129
|
+
# foo 'bar'
|
|
130
|
+
# baz 'buz'
|
|
131
|
+
# }
|
|
132
|
+
#
|
|
133
|
+
def copy( config, &block )
|
|
134
|
+
config = Configuration.for(config) unless config.instance_of? Configuration
|
|
135
|
+
return unless config
|
|
136
|
+
|
|
137
|
+
rv = Configuration.new
|
|
138
|
+
rv.merge!(config)
|
|
139
|
+
|
|
140
|
+
# deep copy
|
|
141
|
+
rv.__desc.each do |key,desc|
|
|
142
|
+
value = rv.__send(key)
|
|
143
|
+
next unless value.instance_of? Configuration
|
|
144
|
+
rv.__send("#{key}=", ::Loquacious.copy(value))
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
rv.merge!(Configuration::DSL.evaluate(&block)) if block
|
|
148
|
+
rv
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
end # class << self
|
|
152
|
+
|
|
153
|
+
@env_config = false
|
|
154
|
+
@env_prefix = "LOQ"
|
|
155
|
+
end # module Loquacious
|
|
156
|
+
|
|
157
|
+
Loquacious.libpath {
|
|
158
|
+
require 'loquacious/core_ext/string'
|
|
159
|
+
require 'loquacious/undefined'
|
|
160
|
+
require 'loquacious/utility'
|
|
161
|
+
require 'loquacious/configuration'
|
|
162
|
+
require 'loquacious/configuration/iterator'
|
|
163
|
+
require 'loquacious/configuration/help'
|
|
164
|
+
}
|
|
165
|
+
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
|
|
2
|
+
module Loquacious
|
|
3
|
+
|
|
4
|
+
# A Configuration provides a "blank slate" for storing configuration
|
|
5
|
+
# properties along with descriptions and default values. Configurations are
|
|
6
|
+
# accessed by name, and hence, the configuration properties can be retrieved
|
|
7
|
+
# from any location in your code.
|
|
8
|
+
#
|
|
9
|
+
# Each property has an associated description that can be displayed to the
|
|
10
|
+
# user via the Configuration::Help class. This is the main point of the
|
|
11
|
+
# Loquacious library - tell the user what all yoru configruation properties
|
|
12
|
+
# actually do!
|
|
13
|
+
#
|
|
14
|
+
# Each configurationp property can also have a default value that is
|
|
15
|
+
# returned if no value has been set for that property. Each property should
|
|
16
|
+
# hae a sensible default - the user should not have to configure every
|
|
17
|
+
# property in order to use a piece of code.
|
|
18
|
+
#
|
|
19
|
+
class Configuration
|
|
20
|
+
|
|
21
|
+
# :stopdoc:
|
|
22
|
+
class Error < StandardError; end
|
|
23
|
+
@table = Hash.new
|
|
24
|
+
# :startdoc:
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
# call-seq:
|
|
28
|
+
# Configuration.for( name )
|
|
29
|
+
# Configuration.for( name ) { block }
|
|
30
|
+
#
|
|
31
|
+
# Returns the configuration associated with the given _name_. If a
|
|
32
|
+
# _block_ is given, then it will be used to create the configuration.
|
|
33
|
+
#
|
|
34
|
+
# The same _name_ can be used multiple times with different
|
|
35
|
+
# configuration blocks. Each different block will be used to add to the
|
|
36
|
+
# configuration; i.e. the configurations are additive.
|
|
37
|
+
#
|
|
38
|
+
def for( name, &block )
|
|
39
|
+
if block.nil?
|
|
40
|
+
return @table.has_key?(name) ? @table[name] : nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if @table.has_key? name
|
|
44
|
+
DSL.evaluate(:config_name => name, :config => @table[name], &block)
|
|
45
|
+
else
|
|
46
|
+
@table[name] = DSL.evaluate(:config_name => name, &block)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# call-seq:
|
|
51
|
+
# Configuration.defaults_for( name ) { block }
|
|
52
|
+
#
|
|
53
|
+
# Set the default values for the configuration associated with the given
|
|
54
|
+
# _name_. A _block_ is required by this method.
|
|
55
|
+
#
|
|
56
|
+
# Default values do not interfere with normal configuration values. If
|
|
57
|
+
# both are defined for a particualr configruation setting, then the
|
|
58
|
+
# regular configuration value will be returned.
|
|
59
|
+
#
|
|
60
|
+
# Defaults allow the user to define configuration values before the
|
|
61
|
+
# library defaults have been loaded. They prevent library defaults from
|
|
62
|
+
# overriding user settings.
|
|
63
|
+
#
|
|
64
|
+
def defaults_for( name, &block )
|
|
65
|
+
raise "defaults require a block" if block.nil?
|
|
66
|
+
|
|
67
|
+
if @table.has_key? name
|
|
68
|
+
DSL.evaluate(:config => @table[name], :defaults_mode => true, &block)
|
|
69
|
+
else
|
|
70
|
+
@table[name] = DSL.evaluate(:config_name => name, :defaults_mode => true, &block)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# call-seq:
|
|
75
|
+
# Configuration.help_for( name, opts = {} )
|
|
76
|
+
#
|
|
77
|
+
# Returns a Help instance for the configuration associated with the
|
|
78
|
+
# given _name_. See the Help#initialize method for the options that
|
|
79
|
+
# can be used with this method.
|
|
80
|
+
#
|
|
81
|
+
def help_for( name, opts = {} )
|
|
82
|
+
::Loquacious::Configuration::Help.new(name, opts)
|
|
83
|
+
end
|
|
84
|
+
alias :help :help_for
|
|
85
|
+
|
|
86
|
+
# call-seq:
|
|
87
|
+
# Configuration.to_hash( config )
|
|
88
|
+
#
|
|
89
|
+
# Recursively convert a configuration object to a hash.
|
|
90
|
+
#
|
|
91
|
+
def to_hash( config )
|
|
92
|
+
cache = { nil => {} }
|
|
93
|
+
|
|
94
|
+
Iterator.new(config).each do |node|
|
|
95
|
+
ary = node.name.split('.')
|
|
96
|
+
name = ary.pop.to_sym
|
|
97
|
+
parent = ary.empty? ? nil : ary.join('.')
|
|
98
|
+
|
|
99
|
+
if node.config?
|
|
100
|
+
cache[node.name] = cache[parent][name] = {}
|
|
101
|
+
else
|
|
102
|
+
cache[parent][name] = node.obj
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
return cache[nil]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Returns a string array with the parent tree for the config
|
|
110
|
+
#
|
|
111
|
+
def parent_list(config)
|
|
112
|
+
current_parent = config.__parent
|
|
113
|
+
parents = []
|
|
114
|
+
until current_parent.nil? do
|
|
115
|
+
parents.unshift current_parent.__name
|
|
116
|
+
current_parent = current_parent.__parent
|
|
117
|
+
end
|
|
118
|
+
parents
|
|
119
|
+
end
|
|
120
|
+
end#self methods
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
instance_methods(true).each do |m|
|
|
124
|
+
next if m[::Loquacious::KEEPERS]
|
|
125
|
+
undef_method m
|
|
126
|
+
end
|
|
127
|
+
Kernel.methods.each do |m|
|
|
128
|
+
next if m[::Loquacious::KEEPERS]
|
|
129
|
+
module_eval <<-CODE
|
|
130
|
+
def #{m}( *args, &block )
|
|
131
|
+
self.method_missing('#{m}', *args, &block)
|
|
132
|
+
end
|
|
133
|
+
CODE
|
|
134
|
+
end
|
|
135
|
+
undef_method :method_missing rescue nil
|
|
136
|
+
|
|
137
|
+
# Accessor for the description hash.
|
|
138
|
+
attr_reader :__desc
|
|
139
|
+
|
|
140
|
+
# Accessor for configuration values
|
|
141
|
+
attr_reader :__values
|
|
142
|
+
|
|
143
|
+
# Accessor for configuration defaults
|
|
144
|
+
attr_reader :__defaults
|
|
145
|
+
|
|
146
|
+
# Flag to switch the configuration object into defaults mode. This allows
|
|
147
|
+
# default values to be set instead regular values.
|
|
148
|
+
attr_accessor :__defaults_mode
|
|
149
|
+
|
|
150
|
+
# Name for this configuration object
|
|
151
|
+
attr_accessor :__name
|
|
152
|
+
|
|
153
|
+
# Name of the parent configuration object, used for traversal
|
|
154
|
+
attr_accessor :__parent
|
|
155
|
+
|
|
156
|
+
# Hash holding the transform procs
|
|
157
|
+
attr_accessor :__transforms
|
|
158
|
+
|
|
159
|
+
# Create a new configuration object and initialize it using an optional
|
|
160
|
+
# _block_ of code.
|
|
161
|
+
#
|
|
162
|
+
def initialize( &block )
|
|
163
|
+
@__desc = Hash.new
|
|
164
|
+
@__values = Hash.new
|
|
165
|
+
@__defaults = Hash.new
|
|
166
|
+
@__transforms = Hash.new
|
|
167
|
+
@__defaults_mode = false
|
|
168
|
+
@__parent = nil
|
|
169
|
+
DSL.evaluate(:config => self, &block) if block
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# When invoked, an attribute reader and writer are defined for the
|
|
173
|
+
# _method_. Any arguments given are used to set the value of the
|
|
174
|
+
# attributes. If a _block_ is given, then the attribute is a nested
|
|
175
|
+
# configuration and the _block_ is evaluated in the context of a new
|
|
176
|
+
# configuration object.
|
|
177
|
+
#
|
|
178
|
+
def method_missing( method, *args, &block )
|
|
179
|
+
m = method.to_s.delete('=').to_sym
|
|
180
|
+
|
|
181
|
+
__eigenclass_eval "def #{m}=( value ) @__values[#{m.inspect}] = value; end", __FILE__, __LINE__
|
|
182
|
+
__eigenclass_eval <<-CODE, __FILE__, __LINE__+1
|
|
183
|
+
def #{m}( *args, &block )
|
|
184
|
+
value = @__values[#{m.inspect}]
|
|
185
|
+
|
|
186
|
+
if args.empty? and !block
|
|
187
|
+
return value if value.kind_of?(Configuration)
|
|
188
|
+
value = @__defaults[#{m.inspect}] if value.kind_of?(Loquacious::Undefined) and @__defaults.has_key? #{m.inspect}
|
|
189
|
+
if Loquacious.env_config
|
|
190
|
+
env_name = Loquacious::Utility.env_var_name(__method__, self)
|
|
191
|
+
if ENV.has_key? env_name
|
|
192
|
+
if @__transforms.has_key? __method__
|
|
193
|
+
return @__transforms[__method__].call ENV[env_name]
|
|
194
|
+
else
|
|
195
|
+
return ENV[env_name]
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
return value.respond_to?(:call) ? value.call : value
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
if block
|
|
203
|
+
v = DSL.evaluate(:parent_config => self, :config_name => __method__.to_s, :defaults_mode => __defaults_mode, &block)
|
|
204
|
+
if value.kind_of?(Configuration)
|
|
205
|
+
value.merge! v
|
|
206
|
+
else
|
|
207
|
+
@__values[#{m.inspect}] = v
|
|
208
|
+
end
|
|
209
|
+
else
|
|
210
|
+
v = (1 == args.length ? args.first : args)
|
|
211
|
+
if __defaults_mode
|
|
212
|
+
@__defaults[#{m.inspect}] = v
|
|
213
|
+
else
|
|
214
|
+
@__values[#{m.inspect}] = v
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
CODE
|
|
219
|
+
|
|
220
|
+
__desc[m] = nil unless __desc.has_key? m
|
|
221
|
+
|
|
222
|
+
default = ((__defaults_mode or args.empty?) and !block) ? Loquacious::Undefined.new(m.to_s) : nil
|
|
223
|
+
self.__send("#{m}=", default)
|
|
224
|
+
self.__send("#{m}", *args, &block)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Only invoke public methods on the Configuration instances.
|
|
228
|
+
#
|
|
229
|
+
def __send( symbol, *args, &block )
|
|
230
|
+
if self.respond_to? symbol
|
|
231
|
+
self.__send__(symbol, *args, &block)
|
|
232
|
+
else
|
|
233
|
+
self.method_missing(symbol, *args, &block)
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Evaluate the given _code_ string in the context of this object's
|
|
238
|
+
# eigenclass (singleton class).
|
|
239
|
+
#
|
|
240
|
+
def __eigenclass_eval( code, file, line )
|
|
241
|
+
ec = class << self; self; end
|
|
242
|
+
ec.module_eval code, file, line
|
|
243
|
+
rescue StandardError
|
|
244
|
+
Kernel.raise Error, "cannot evalutate this code:\n#{code}\n"
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Merge the contents of the _other_ configuration into this one. Values
|
|
248
|
+
# from the _other_ configuratin will overwite values in this
|
|
249
|
+
# configuration.
|
|
250
|
+
#
|
|
251
|
+
# This function is recursive. Nested configurations will be merged with
|
|
252
|
+
# their counterparts in the _other_ configuration.
|
|
253
|
+
#
|
|
254
|
+
def merge!( other )
|
|
255
|
+
return self if other.equal? self
|
|
256
|
+
Kernel.raise Error, "can only merge another Configuration" unless other.kind_of?(Configuration)
|
|
257
|
+
|
|
258
|
+
other_values = other.__values
|
|
259
|
+
other_defaults = other.__defaults
|
|
260
|
+
|
|
261
|
+
other.__desc.each do |key,desc|
|
|
262
|
+
value = @__values[key]
|
|
263
|
+
other_value = other_values[key]
|
|
264
|
+
|
|
265
|
+
if value.kind_of?(Configuration) and other_value.kind_of?(Configuration)
|
|
266
|
+
value.merge! other_value
|
|
267
|
+
elsif !other_value.kind_of?(Loquacious::Undefined)
|
|
268
|
+
self.__send__(key, other_value)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
if other_defaults.has_key? key
|
|
272
|
+
@__defaults[key] = other_defaults[key]
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
if desc
|
|
276
|
+
__desc[key] = desc
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
self
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Provides hash accessor notation for configuration values.
|
|
284
|
+
#
|
|
285
|
+
# config = Configuration.for('app') {
|
|
286
|
+
# port 1234
|
|
287
|
+
# }
|
|
288
|
+
# config[:port] #=> 1234
|
|
289
|
+
# config.port #=> 1234
|
|
290
|
+
#
|
|
291
|
+
def []( key )
|
|
292
|
+
self.__send(key)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Provides hash accessor notation for configuration values.
|
|
296
|
+
#
|
|
297
|
+
# config = Configuration.for('app')
|
|
298
|
+
# config[:port] = 8808
|
|
299
|
+
# config.port #=> 8808
|
|
300
|
+
#
|
|
301
|
+
def []=( key, value )
|
|
302
|
+
self.__send(key, value)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Recursively convert the configuration object to a hash.
|
|
306
|
+
#
|
|
307
|
+
def to_hash
|
|
308
|
+
::Loquacious::Configuration.to_hash(self)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Returns an array of the parents in descending order
|
|
312
|
+
def parent_list
|
|
313
|
+
::Loquacious::Configuration.parent_list(self)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Implementation of a domain specific language for creating configuration
|
|
317
|
+
# objects. Blocks of code are evaluted by the DSL which returns a new
|
|
318
|
+
# configuration object.
|
|
319
|
+
#
|
|
320
|
+
class DSL
|
|
321
|
+
instance_methods(true).each do |m|
|
|
322
|
+
next if m[::Loquacious::KEEPERS]
|
|
323
|
+
undef_method m
|
|
324
|
+
end
|
|
325
|
+
private_instance_methods(true).each do |m|
|
|
326
|
+
next if m[::Loquacious::KEEPERS]
|
|
327
|
+
undef_method m
|
|
328
|
+
end
|
|
329
|
+
Kernel.methods.each do |m|
|
|
330
|
+
next if m[::Loquacious::KEEPERS]
|
|
331
|
+
module_eval <<-CODE, __FILE__, __LINE__+1
|
|
332
|
+
def #{m}( *args, &block )
|
|
333
|
+
self.method_missing('#{m}', *args, &block)
|
|
334
|
+
end
|
|
335
|
+
CODE
|
|
336
|
+
end
|
|
337
|
+
undef_method :method_missing rescue nil
|
|
338
|
+
|
|
339
|
+
# Create a new DSL and evaluate the given _block_ in the context of
|
|
340
|
+
# the DSL. Returns a newly created configuration object.
|
|
341
|
+
#
|
|
342
|
+
def self.evaluate( opts = {}, &block )
|
|
343
|
+
dsl = self.new(opts, &block)
|
|
344
|
+
dsl.__config
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# Returns the configuration object.
|
|
348
|
+
attr_reader :__config
|
|
349
|
+
|
|
350
|
+
# Creates a new DSL and evaluates the given _block_ in the context of
|
|
351
|
+
# the DSL.
|
|
352
|
+
#
|
|
353
|
+
def initialize( opts = {}, &block )
|
|
354
|
+
@description = nil
|
|
355
|
+
@transform_to_store = nil
|
|
356
|
+
@__config = opts[:config] || Configuration.new
|
|
357
|
+
@__config.__defaults_mode = opts.key?(:defaults_mode) ? opts[:defaults_mode] : false
|
|
358
|
+
@__config.__name = opts[:config_name] || nil
|
|
359
|
+
@__config.__parent = opts[:parent_config] || nil
|
|
360
|
+
instance_eval(&block)
|
|
361
|
+
ensure
|
|
362
|
+
@__config.__defaults_mode = false
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
# Dynamically adds the given _method_ to the configuration as an
|
|
366
|
+
# attribute. The _args_ will be used to set the value of the
|
|
367
|
+
# attribute. If a _block_ is given then the _args_ are ignored and the
|
|
368
|
+
# attribute will be a nested configuration object.
|
|
369
|
+
#
|
|
370
|
+
def method_missing( method, *args, &block )
|
|
371
|
+
m = method.to_s.delete('=').to_sym
|
|
372
|
+
|
|
373
|
+
if args.length > 1
|
|
374
|
+
opts = args.last.instance_of?(Hash) ? args.pop : {}
|
|
375
|
+
self.desc(opts[:desc]) if opts.has_key? :desc
|
|
376
|
+
self.transform(opts[:transform]) if opts.has_key? :transform
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
rv = __config.__send(m, *args, &block)
|
|
380
|
+
__config.__desc[m] = @description if @description
|
|
381
|
+
__config.__transforms[m] = @transform_to_store if @transform_to_store
|
|
382
|
+
@description = nil
|
|
383
|
+
@transform_to_store = nil
|
|
384
|
+
rv
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Store the _string_ as the description for the next attribute that
|
|
388
|
+
# will be configured. This description will be overwritten if the
|
|
389
|
+
# attribute has a description passed as an options hash.
|
|
390
|
+
#
|
|
391
|
+
def desc( string )
|
|
392
|
+
string = string.to_s
|
|
393
|
+
string.strip!
|
|
394
|
+
string.gutter!
|
|
395
|
+
@description = string.empty? ? nil : string
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def transform( transform_proc )
|
|
399
|
+
@transform_to_store = transform_proc
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
end # class DSL
|
|
403
|
+
|
|
404
|
+
end # class Configuration
|
|
405
|
+
end # module Loquacious
|
|
406
|
+
|