yacl 1.0.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/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
|