yacl 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.rdoc +5 -0
- data/LICENSE +16 -0
- data/Manifest.txt +48 -0
- data/README.rdoc +55 -0
- data/Rakefile +308 -0
- data/example/myapp-simple/bin/myapp +16 -0
- data/example/myapp-simple/config/database.yml +8 -0
- data/example/myapp-simple/config/host.yml +2 -0
- data/example/myapp-simple/config/pipeline.yml +1 -0
- data/example/myapp-simple/lib/myapp.rb +53 -0
- data/example/myapp/bin/myapp +17 -0
- data/example/myapp/bin/myapp-job +10 -0
- data/example/myapp/config/database.yml +8 -0
- data/example/myapp/config/httpserver.yml +3 -0
- data/example/myapp/config/pipeline.yml +1 -0
- data/example/myapp/lib/myapp.rb +6 -0
- data/example/myapp/lib/myapp/cli.rb +92 -0
- data/example/myapp/lib/myapp/defaults.rb +28 -0
- data/example/myapp/lib/myapp/job.rb +56 -0
- data/lib/yacl.rb +12 -0
- data/lib/yacl/define.rb +9 -0
- data/lib/yacl/define/cli.rb +7 -0
- data/lib/yacl/define/cli/options.rb +97 -0
- data/lib/yacl/define/cli/parser.rb +112 -0
- data/lib/yacl/define/cli/runner.rb +82 -0
- data/lib/yacl/define/defaults.rb +58 -0
- data/lib/yacl/define/plan.rb +197 -0
- data/lib/yacl/loader.rb +80 -0
- data/lib/yacl/loader/env.rb +103 -0
- data/lib/yacl/loader/yaml_dir.rb +137 -0
- data/lib/yacl/loader/yaml_file.rb +102 -0
- data/lib/yacl/properties.rb +144 -0
- data/lib/yacl/simple.rb +52 -0
- data/spec/data/yaml_dir/database.yml +8 -0
- data/spec/data/yaml_dir/httpserver.yml +3 -0
- data/spec/define/cli/options_spec.rb +47 -0
- data/spec/define/cli/parser_spec.rb +64 -0
- data/spec/define/cli/runner_spec.rb +57 -0
- data/spec/define/defaults_spec.rb +24 -0
- data/spec/define/plan_spec.rb +77 -0
- data/spec/loader/env_spec.rb +32 -0
- data/spec/loader/yaml_dir_spec.rb +43 -0
- data/spec/loader/yaml_file_spec.rb +80 -0
- data/spec/loader_spec.rb +16 -0
- data/spec/properties_spec.rb +60 -0
- data/spec/simple_spec.rb +85 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/version_spec.rb +8 -0
- metadata +207 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
module Yacl::Define::Cli
|
2
|
+
# Public: The Runner class is to be used by app developers as the entry point
|
3
|
+
# for the commandline programs. A class that inherits from Runner is what is
|
4
|
+
# placed in a bin/myapp file in the project structure.
|
5
|
+
#
|
6
|
+
# The Runner is configured with the Plan to use for loading the configuratin
|
7
|
+
# properties and when the plan is loaded, the properties are available via the
|
8
|
+
# #properties method.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# class MyApp::Runner < Yacl::Define::Cli::Runner
|
13
|
+
# plan MyApp::Plan
|
14
|
+
#
|
15
|
+
# def run
|
16
|
+
# puts properties.database.server
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# And in your 'bin/myapp-cli' file you would have:
|
21
|
+
#
|
22
|
+
# # automatically populate with ARGV and ENV
|
23
|
+
# MyApp::Runner.go
|
24
|
+
#
|
25
|
+
# # or be explicit
|
26
|
+
# MyApp::Runner.go( ARGV, ENV )
|
27
|
+
#
|
28
|
+
class Runner
|
29
|
+
class Error < ::Yacl::Error; end
|
30
|
+
|
31
|
+
# Public: Execute the Runner
|
32
|
+
#
|
33
|
+
# argv - The commandline args to use (default: ARGV)
|
34
|
+
# env - The enviroment hash to use (default: ENV )
|
35
|
+
#
|
36
|
+
# It is assumed that when this method exits, the program is over.
|
37
|
+
#
|
38
|
+
# Returns nothing.
|
39
|
+
def self.go( argv = ARGV, env = ENV )
|
40
|
+
r = self.new( argv, env )
|
41
|
+
r.run
|
42
|
+
end
|
43
|
+
|
44
|
+
# Public: Define the Plan associated with this Runner.
|
45
|
+
#
|
46
|
+
# plan - A class you define that inherits from Yacl::Define::Plan
|
47
|
+
#
|
48
|
+
# Returns: the plan class if it is set, nil otherwise.
|
49
|
+
def self.plan( *args )
|
50
|
+
@plan_klass = args.first unless args.empty?
|
51
|
+
return @plan_klass
|
52
|
+
end
|
53
|
+
|
54
|
+
# Internal: Initalize the Runner.
|
55
|
+
#
|
56
|
+
# argv - The commandline args to use (default: ARGV)
|
57
|
+
# env - The enviroment hash to use (default: ENV )
|
58
|
+
#
|
59
|
+
# This method should be avoided by end users, they should call #go instead.
|
60
|
+
def initialize( argv = ARGV, env = ENV )
|
61
|
+
raise Error, "No plan class specified in #{self.class}" unless self.class.plan
|
62
|
+
@plan = plan_klass.new( :argv => argv, :env => env )
|
63
|
+
end
|
64
|
+
|
65
|
+
# Public: Access the Properties instance that is created by the Plan. This
|
66
|
+
# is the fully realized Properties of the plan and should be considered
|
67
|
+
# read-only.
|
68
|
+
#
|
69
|
+
# Returns a Properties instance.
|
70
|
+
def properties
|
71
|
+
@plan.properties
|
72
|
+
end
|
73
|
+
|
74
|
+
# Internal: return the Plan class defined for this Runner class
|
75
|
+
#
|
76
|
+
# Returns a Plan class
|
77
|
+
def plan_klass
|
78
|
+
self.class.plan
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Yacl::Define
|
2
|
+
# Public: A base class for use in defining your application's default
|
3
|
+
# configuration properties.
|
4
|
+
#
|
5
|
+
# For the API purposes, classes that inherit from Defaults may behave as
|
6
|
+
# both Loader and Properties classes.
|
7
|
+
#
|
8
|
+
# Examples:
|
9
|
+
#
|
10
|
+
# class MyDefaults < Yacl::Define::Defaults
|
11
|
+
# default 'host.name', 'localhost'
|
12
|
+
# default 'host.port', 4321
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# Now you can use an instances of MyDefaults to find your defaults
|
16
|
+
#
|
17
|
+
# d = MyDefaults.new
|
18
|
+
# d.host.name # => 'localhost'
|
19
|
+
class Defaults
|
20
|
+
class Error< ::Yacl::Error; end
|
21
|
+
extend Forwardable
|
22
|
+
|
23
|
+
# Internal: Create the instance of Properties that will be populated by
|
24
|
+
# calls to Properties::default.
|
25
|
+
#
|
26
|
+
# Returns: Properties
|
27
|
+
def self.properties
|
28
|
+
@properties ||= ::Yacl::Properties.new
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Define a property and its value.
|
32
|
+
#
|
33
|
+
# name - The String name of the property.
|
34
|
+
# value - The obj to be the default value for the given name.
|
35
|
+
#
|
36
|
+
# Returns nothing.
|
37
|
+
def self.default( name, value )
|
38
|
+
args = name.to_s.split('.')
|
39
|
+
args << value
|
40
|
+
properties.set( *args )
|
41
|
+
end
|
42
|
+
|
43
|
+
# Behave as if we are an instance of Properties
|
44
|
+
def_delegators :@properties, *Yacl::Properties.delegatable_methods
|
45
|
+
|
46
|
+
# Internal: Return the Properties so we behave like a Loader
|
47
|
+
attr_reader :properties
|
48
|
+
|
49
|
+
# Internal: Fake out being a Loader.
|
50
|
+
#
|
51
|
+
# This method is here to implement the LoaderAPI
|
52
|
+
#
|
53
|
+
# Returns nothing.
|
54
|
+
def initialize( opts = {} )
|
55
|
+
@properties = self.class.properties
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
module Yacl::Define
|
2
|
+
# Public: The Plan is the order an method by which the Properties of your
|
3
|
+
# application are loaded and looked up. This is a base class from which you
|
4
|
+
# will define your Property lookups.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# class MyPlan < Yacl::Define::Plan
|
9
|
+
# try MyApp::Cli::Parser
|
10
|
+
# try Yacl::Loader::Env, :prefix => 'MY_APP'
|
11
|
+
# try Yacl::Loader::YamlDir, :parameter => 'config.dir'
|
12
|
+
# try MyApp::Defaults
|
13
|
+
#
|
14
|
+
# on_error do |exception|
|
15
|
+
# $stderr.puts "ERROR: #{exception}"
|
16
|
+
# $stderr.puts "Try --help for help"
|
17
|
+
# exit 1
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# This creates a class MyPlan that when utilized by a Runner loads properties
|
22
|
+
# in the following cascading order:
|
23
|
+
#
|
24
|
+
# 1) Commandline is parsed and converted to Properties, these have the highest
|
25
|
+
# priority
|
26
|
+
# 2) The environemtn variables are used, only those that start with 'MY_APP'
|
27
|
+
# as the variable prefix. These have the 2nd higest priority
|
28
|
+
# 3) A configuration directory is loaded, using the 'config.dir' parameter
|
29
|
+
# that comes from either (1) or (2). Properties found in this config dir
|
30
|
+
# hve the 3rd highest priority
|
31
|
+
# 4) Built in defaults if the property is not found any any of the previous
|
32
|
+
# locations.
|
33
|
+
#
|
34
|
+
class Plan
|
35
|
+
class Error < ::Yacl::Error; end
|
36
|
+
|
37
|
+
# Internal: An internal class used to encapsulate the individual items of
|
38
|
+
# the lookup plan.
|
39
|
+
class Item
|
40
|
+
# Internal: Create a new Item. Thiis is what is created from a call to
|
41
|
+
# Plan.try
|
42
|
+
def initialize( klass, options )
|
43
|
+
@loader_klass = klass
|
44
|
+
@options = options
|
45
|
+
end
|
46
|
+
|
47
|
+
# Internal: load hte properties from the given Loader class
|
48
|
+
#
|
49
|
+
# Returns a Properties instance
|
50
|
+
def load_properties( params )
|
51
|
+
opt = @options.merge( params )
|
52
|
+
loader = @loader_klass.new( opt )
|
53
|
+
loader.properties
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class << self
|
58
|
+
# Public: Add the given Loader child class or Loader duck-type class to the
|
59
|
+
# definition of the Plan.
|
60
|
+
#
|
61
|
+
# klass - A Class that implements the Loader API.
|
62
|
+
# options - A Hash that will be passed to klass.new() when the klass is
|
63
|
+
# initialized
|
64
|
+
#
|
65
|
+
# Example:
|
66
|
+
#
|
67
|
+
# try Yacl::Loader::YamlDir, :parameter => 'config.dir'
|
68
|
+
#
|
69
|
+
# Returns nothing.
|
70
|
+
def try( klass , options = {} )
|
71
|
+
items << Item.new( klass, options )
|
72
|
+
end
|
73
|
+
|
74
|
+
# Public: Define a callable to be invoked should an error happen while
|
75
|
+
# loading the properties of your application.
|
76
|
+
#
|
77
|
+
# callable - the Callable to be invoked.
|
78
|
+
#
|
79
|
+
# Example:
|
80
|
+
#
|
81
|
+
# on_error( Proc.new{ fail "kaboom!" } )
|
82
|
+
#
|
83
|
+
# on_error do
|
84
|
+
# raise "Kaboom!"
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# Return the callable if no parameters are given and the callable is
|
88
|
+
# defined.
|
89
|
+
#
|
90
|
+
# Raises an error if no parameters are given and the callable is NOT
|
91
|
+
# defined.
|
92
|
+
#
|
93
|
+
def on_error( callable = nil, &block )
|
94
|
+
if callable then
|
95
|
+
@on_error = callable
|
96
|
+
elsif block_given?
|
97
|
+
@on_error = block
|
98
|
+
elsif defined? @on_error
|
99
|
+
return @on_error
|
100
|
+
else
|
101
|
+
raise Error, "on_error requires the use of a callable or a block"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Internal: Test to see if this class has an on_error callable defined.
|
106
|
+
#
|
107
|
+
# Returns true or false if the on_error is defined.
|
108
|
+
def has_on_error?
|
109
|
+
defined? @on_error
|
110
|
+
end
|
111
|
+
|
112
|
+
# Internal: Return the array of Items that are used in the child class
|
113
|
+
# definition.
|
114
|
+
#
|
115
|
+
# Returns: an Array of Items
|
116
|
+
def items
|
117
|
+
@items ||= []
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Internal: Return the array of Items defined for this class
|
122
|
+
#
|
123
|
+
# Returns an Array of Items.
|
124
|
+
def items
|
125
|
+
self.class.items
|
126
|
+
end
|
127
|
+
|
128
|
+
# Public: Return the Properties instance that results from instantiating the
|
129
|
+
# Plan.
|
130
|
+
#
|
131
|
+
# Returns an Properties instance.
|
132
|
+
attr_reader :properties
|
133
|
+
|
134
|
+
# Public: Create a new instance of the Plan. This should be invoked via
|
135
|
+
# #super in a child class.
|
136
|
+
#
|
137
|
+
def initialize( params = {} )
|
138
|
+
initial_properties = params[:initial_properties] || Yacl::Properties.new
|
139
|
+
@properties = load_properties( initial_properties, params, items )
|
140
|
+
rescue Yacl::Error => ye
|
141
|
+
if self.class.has_on_error? then
|
142
|
+
self.class.on_error.call( ye )
|
143
|
+
else
|
144
|
+
raise ye
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
#######
|
149
|
+
private
|
150
|
+
#######
|
151
|
+
|
152
|
+
# Internal: Create the Proprites from an initial properties, a set of params
|
153
|
+
# and the list of Items in the class.
|
154
|
+
#
|
155
|
+
# initial_properties - a blank Properties, or an initialized Properties
|
156
|
+
# instance to use as the highest priority properties
|
157
|
+
# params - A Hash that will be passed to each Item when it is
|
158
|
+
# loaded.
|
159
|
+
# items - An Array of Item instances
|
160
|
+
#
|
161
|
+
# The properties are loaded in definitiaion order, that is the order they
|
162
|
+
# appear in the class definition. The initial properties are recalculated on
|
163
|
+
# each iteration as the next Properties may use some information from the
|
164
|
+
# previous Properties to load itself.
|
165
|
+
#
|
166
|
+
# The final Properties that is returned is a merging of all the loaded
|
167
|
+
# properties in reverse order from the bottom to the top. This results in
|
168
|
+
# the last Item defined with #try being the "bottom" property with the last
|
169
|
+
# priority. It may be overwritten by any of the Properties that are loaded
|
170
|
+
# from higher in the loader stack
|
171
|
+
#
|
172
|
+
# Returns a Properties instance.
|
173
|
+
def load_properties( initial_properties, params, items )
|
174
|
+
loaded_properties = [ initial_properties ]
|
175
|
+
items.each do |item|
|
176
|
+
properties_so_far = merge_properties( loaded_properties )
|
177
|
+
item_params = params.merge( :properties => properties_so_far )
|
178
|
+
loaded_properties << item.load_properties( item_params )
|
179
|
+
end
|
180
|
+
return merge_properties( loaded_properties )
|
181
|
+
end
|
182
|
+
|
183
|
+
# Internal: Merge a list of Properties instances in the reverse order of the
|
184
|
+
# Array in which they exist
|
185
|
+
#
|
186
|
+
# properties_list - an Array of Properties
|
187
|
+
#
|
188
|
+
# Returns a Properties instances.
|
189
|
+
def merge_properties( properties_list )
|
190
|
+
props = ::Yacl::Properties.new
|
191
|
+
properties_list.reverse.each do |p|
|
192
|
+
props.merge!( p )
|
193
|
+
end
|
194
|
+
return props
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
data/lib/yacl/loader.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
module Yacl
|
3
|
+
# Loader - the base class of all Loaders. It defines the Loader API>
|
4
|
+
#
|
5
|
+
# The Loader API is:
|
6
|
+
#
|
7
|
+
# 1) The initializer method takes an Hash. This hash is stored in the
|
8
|
+
# @options and is avaialble to all child clasess via the #options
|
9
|
+
# method.
|
10
|
+
#
|
11
|
+
# 2) The #properties method takes no parameters and MUST return a Properties
|
12
|
+
# instance
|
13
|
+
#
|
14
|
+
# 3) If the options passed into the initializer has a :reference_properites
|
15
|
+
# key, its value is made available via the #reference_properties method.
|
16
|
+
#
|
17
|
+
# Loader also provides a couple of utility methods for use by child classes.
|
18
|
+
#
|
19
|
+
class Loader
|
20
|
+
class Error < ::Yacl::Error; end
|
21
|
+
|
22
|
+
# Internal: The options object this loader is associated with
|
23
|
+
attr_reader :options
|
24
|
+
|
25
|
+
# Create a new instance of the Loader
|
26
|
+
#
|
27
|
+
# opts - as Hash of options. Well known options are:
|
28
|
+
# :properties - A Properties object
|
29
|
+
# :path - A directory/file path
|
30
|
+
#
|
31
|
+
def initialize( opts = {} )
|
32
|
+
@options = opts
|
33
|
+
end
|
34
|
+
|
35
|
+
# Internal:
|
36
|
+
#
|
37
|
+
# Load the properties according to the type of loader it is
|
38
|
+
#
|
39
|
+
# Returns: Properties
|
40
|
+
def properties
|
41
|
+
Properties.new
|
42
|
+
end
|
43
|
+
|
44
|
+
# Internal:
|
45
|
+
#
|
46
|
+
# The properties that are passed in to use should we need them while loading
|
47
|
+
#
|
48
|
+
# Returns: properties
|
49
|
+
def reference_properties
|
50
|
+
@options[:properties]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Internal: Return param split on "."
|
54
|
+
#
|
55
|
+
# This will only be done if param is a String
|
56
|
+
#
|
57
|
+
# Returns an Array or param
|
58
|
+
def self.mapify_key( param )
|
59
|
+
return param unless param.kind_of?( String )
|
60
|
+
return param.split('.')
|
61
|
+
end
|
62
|
+
|
63
|
+
# Internal: Extract the value from :path and covert it to a Pathname
|
64
|
+
# If a :path key is found, then extract it and convert the value to a
|
65
|
+
# Pathname
|
66
|
+
#
|
67
|
+
# options - a Hash
|
68
|
+
# key = the key to extract as a path (default: 'path')
|
69
|
+
#
|
70
|
+
# Returns a Pathname or nil.
|
71
|
+
def self.extract_path( options, key = 'path' )
|
72
|
+
ps = options[key.to_sym] || options[key.to_s]
|
73
|
+
return nil unless ps
|
74
|
+
return Pathname.new( ps )
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
require 'yacl/loader/env'
|
79
|
+
require 'yacl/loader/yaml_file'
|
80
|
+
require 'yacl/loader/yaml_dir'
|
@@ -0,0 +1,103 @@
|
|
1
|
+
class Yacl::Loader
|
2
|
+
# Env loads a Configuration object from a hash, generally this would be ENV.
|
3
|
+
#
|
4
|
+
# The keys in the hash may be filtered by a prefix. For instance if you had
|
5
|
+
# keys in the hash of :
|
6
|
+
#
|
7
|
+
# MY_APP_OPTION_A => 'foo'
|
8
|
+
# MY_APP_OPTION_B => 'bar'
|
9
|
+
#
|
10
|
+
# And you wanted to have those loaded so they were accessible as
|
11
|
+
# option.a and option.b you would use Loader::Env like:
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# properties = Yacl::Loader::Env.new( ENV, 'MY_APP' ).properties
|
16
|
+
# properties.option.a # => 'foo'
|
17
|
+
# properties.option.b # => 'bar'
|
18
|
+
#
|
19
|
+
class Env < ::Yacl::Loader
|
20
|
+
class Error < ::Yacl::Loader::Error; end
|
21
|
+
|
22
|
+
# Create a new Env instance from the loaded options
|
23
|
+
#
|
24
|
+
# opts - a hash of options:
|
25
|
+
# :env => The environment has to pass in (default: ENV). required.
|
26
|
+
# :prefix => the prefix to strip off of each key in the :env hash. required.
|
27
|
+
#
|
28
|
+
def initialize( opts = {} )
|
29
|
+
super
|
30
|
+
@env = @options.fetch( :env, ENV )
|
31
|
+
@prefix = @options.fetch( :prefix, nil )
|
32
|
+
raise Error, "No environment variable prefix is given" unless @prefix
|
33
|
+
end
|
34
|
+
|
35
|
+
# Public: return the Properties that are created from the environment hash.
|
36
|
+
#
|
37
|
+
# Returns a Properties instance
|
38
|
+
def properties
|
39
|
+
load_properties( @env, @prefix )
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Internal: given a hash, and a key prefix, convert the hash into a
|
45
|
+
# Properties instance.
|
46
|
+
#
|
47
|
+
# env - a Hash of environment variables
|
48
|
+
# prefix - a String of indicated the prefix of keys in env that are to be
|
49
|
+
# stripped off
|
50
|
+
def load_properties( env, prefix )
|
51
|
+
dot_env = convert_to_dotted_keys( env )
|
52
|
+
dot_prefix = to_property_path( prefix )
|
53
|
+
prefix_only = prefix_keys_only( dot_prefix, dot_env )
|
54
|
+
p = Yacl::Properties.new( prefix_only )
|
55
|
+
p = p.scoped_by( dot_prefix ) if dot_prefix
|
56
|
+
return p
|
57
|
+
end
|
58
|
+
|
59
|
+
# Internal: Return a new Hash that has only those key/values where the key
|
60
|
+
# is prefixed with the given prefix item.
|
61
|
+
#
|
62
|
+
# prefix - a string to compare against the keys of the hash
|
63
|
+
# hash - the hash to return a subset from
|
64
|
+
#
|
65
|
+
# Returns a Hash.
|
66
|
+
def prefix_keys_only( prefix, hash)
|
67
|
+
only = {}
|
68
|
+
hash.each do |k,v|
|
69
|
+
only[k] = v if k =~ /#{prefix}/
|
70
|
+
end
|
71
|
+
return only
|
72
|
+
end
|
73
|
+
|
74
|
+
# Internal: Take the input Hash, convert all the keys to a dotted notation
|
75
|
+
# and return the new hash.
|
76
|
+
#
|
77
|
+
# The returnd hash will have keys that have been converted using
|
78
|
+
# #to_property_path
|
79
|
+
#
|
80
|
+
# hash - the hash to convert
|
81
|
+
#
|
82
|
+
# Returns a hash.
|
83
|
+
def convert_to_dotted_keys( hash )
|
84
|
+
converted = {}
|
85
|
+
hash.each do |k,v|
|
86
|
+
dot_k = to_property_path( k )
|
87
|
+
converted[dot_k] = v
|
88
|
+
end
|
89
|
+
return converted
|
90
|
+
end
|
91
|
+
|
92
|
+
# Internal: Convert the input String to a property style. The String is
|
93
|
+
# downcased and all _ are converted to .
|
94
|
+
#
|
95
|
+
# name - the String to convert
|
96
|
+
#
|
97
|
+
# Returns the converted String.
|
98
|
+
def to_property_path( name )
|
99
|
+
return nil unless name
|
100
|
+
name.downcase.gsub('_','.')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|