rack-config-flexible 0.1.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/LICENSE +22 -0
- data/README.md +117 -0
- data/lib/rack/config/flexible.rb +280 -0
- metadata +54 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012, Tim Hentenaar
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
rack-config-flexible
|
2
|
+
====================
|
3
|
+
|
4
|
+
An alternative to Rack::Config, offering much greater flexibility.
|
5
|
+
|
6
|
+
Licensing
|
7
|
+
=========
|
8
|
+
|
9
|
+
This software is licensed under the [Simplified BSD License](http://en.wikipedia.org/wiki/BSD_licenses#2-clause_license_.28.22Simplified_BSD_License.22_or_.22FreeBSD_License.22.29) as described in the LICENSE file.
|
10
|
+
|
11
|
+
Installation
|
12
|
+
============
|
13
|
+
|
14
|
+
gem install rack-config-flexible
|
15
|
+
|
16
|
+
Usage
|
17
|
+
=====
|
18
|
+
|
19
|
+
Just add something like this to your _config.ru_:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'rack/config/simple'
|
23
|
+
|
24
|
+
use Rack::Config::Flexible do
|
25
|
+
environment :production
|
26
|
+
section :data
|
27
|
+
set :key => 'value'
|
28
|
+
|
29
|
+
environment :development
|
30
|
+
section :data
|
31
|
+
set :key => 'dev_value'
|
32
|
+
|
33
|
+
# Set the current environment
|
34
|
+
environment :production
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
Of course, individual environments, sections, and even the entire configuration can be loaded from yaml files.
|
39
|
+
|
40
|
+
Accessing the Configuration Data
|
41
|
+
================================
|
42
|
+
|
43
|
+
The configuration can be accessed by downstream middleware via the Rack environment. In the Usage example,
|
44
|
+
you could access _key_'s value as follows:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
env['rack.config']['data.key']
|
48
|
+
```
|
49
|
+
|
50
|
+
and you can even modify values as follows:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
env['rack.config']['data.key'] = 'new_value'
|
54
|
+
```
|
55
|
+
|
56
|
+
if, and only if, the given key exists. The format for the hash key is _section.key_.
|
57
|
+
|
58
|
+
Loading an Environment/Section from Yaml
|
59
|
+
========================================
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
require 'rack/config/simple'
|
63
|
+
|
64
|
+
use Rack::Config::Flexible do
|
65
|
+
environment :production
|
66
|
+
section :data, 'cfg/production/data.yaml'
|
67
|
+
|
68
|
+
environment :development, 'cfg/development.yaml'
|
69
|
+
|
70
|
+
# Set the current environment
|
71
|
+
environment :production
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
Any calls to _set_ after _environment_ or _section_ will override
|
76
|
+
data loaded from the yaml file if the same key is specified.
|
77
|
+
Otherwise, they'll just add the values to the hash per usual.
|
78
|
+
|
79
|
+
Loading the Entire Configuration from a Yaml
|
80
|
+
============================================
|
81
|
+
|
82
|
+
You can load the entire configuration from a single file, or a
|
83
|
+
directory tree.
|
84
|
+
|
85
|
+
This example loads from a single file:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
require 'rack/config/flexible'
|
89
|
+
|
90
|
+
use Rack::Config::Flexible :from_file => 'settings.yaml' do
|
91
|
+
# Set the current environment to production
|
92
|
+
environment :production
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
This one from a directory tree:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
require 'rack/config/flexible'
|
100
|
+
|
101
|
+
use Rack::Config::Flexible :from_file => 'settings' do
|
102
|
+
# Set the current environment to production
|
103
|
+
environment :production
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
The directory tree is expected to be laid out like:
|
108
|
+
|
109
|
+
settings/environment/section.yaml
|
110
|
+
|
111
|
+
Where each directory under _settings_ is an _environment_,
|
112
|
+
containg a separate yaml file for each _section_.
|
113
|
+
The YAML file itself will only hold key-value pairs for
|
114
|
+
that particular _section_.
|
115
|
+
|
116
|
+
See the inline documentation for more details.
|
117
|
+
|
@@ -0,0 +1,280 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Config
|
5
|
+
#
|
6
|
+
# === Overview
|
7
|
+
#
|
8
|
+
# Rack::Config::Flexible is an alternative to Rack::Config,
|
9
|
+
# offering much greater flexibility.
|
10
|
+
#
|
11
|
+
# Configuration options are stored as key-value pairs in _sections_,
|
12
|
+
# partitioned by _environments_. For example:
|
13
|
+
#
|
14
|
+
# + environment
|
15
|
+
# + section
|
16
|
+
# key -> value pairs
|
17
|
+
#
|
18
|
+
# A simple DSL is provided and can be used either within a passed
|
19
|
+
# configuration block (to ::new), or to the #configuration method.
|
20
|
+
#
|
21
|
+
# Facilities are also provided to load whole environments, and sections
|
22
|
+
# from either a single YAML file structured like, or from a directory tree.
|
23
|
+
#
|
24
|
+
# Note that values from a file/directory tree can be overridden with #configure, or
|
25
|
+
# by passing a block to ::new.
|
26
|
+
#
|
27
|
+
# === DSL Example
|
28
|
+
#
|
29
|
+
# Here's an example showing the intended usage of the provided DSL:
|
30
|
+
#
|
31
|
+
#
|
32
|
+
# require 'rack/config/flexible'
|
33
|
+
#
|
34
|
+
# use Rack::Config::Flexible do
|
35
|
+
# environment :production
|
36
|
+
# section :data
|
37
|
+
# set :key => 'value'
|
38
|
+
#
|
39
|
+
# environment :development
|
40
|
+
# section :data
|
41
|
+
# set :key => 'dev_value'
|
42
|
+
#
|
43
|
+
# # Set the current environment
|
44
|
+
# environment :production
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# +data.key+ would return 'value' in *production*, and 'dev_value' in *development*; and
|
48
|
+
# would be accessed via the Rack environment like so:
|
49
|
+
#
|
50
|
+
# env['rack.config']['data.key']
|
51
|
+
#
|
52
|
+
# Values can also be replaced by:
|
53
|
+
#
|
54
|
+
# env['rack.config']['data.key'] = 'new_value'
|
55
|
+
#
|
56
|
+
# Keep in mind that anything that you would be able to do inside the block you're passing
|
57
|
+
# to ::new, can also be done in a block passed to #configure since they both run in the
|
58
|
+
# same scope.
|
59
|
+
#
|
60
|
+
# === Single-File Example
|
61
|
+
#
|
62
|
+
# Loading settings from a single file is extremely easy:
|
63
|
+
#
|
64
|
+
# require 'rack/config/flexible'
|
65
|
+
#
|
66
|
+
# use Rack::Config::Flexible :from_file => 'settings.yaml' do
|
67
|
+
# # Set the current environment to production
|
68
|
+
# environment :production
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# This expects the file to be laid out as follows:
|
72
|
+
#
|
73
|
+
# environment:
|
74
|
+
# section:
|
75
|
+
# key: value
|
76
|
+
# ...
|
77
|
+
#
|
78
|
+
# It's important to note that when loading from YAML files, environment names and
|
79
|
+
# section names will be converted to +Symbol+s.
|
80
|
+
#
|
81
|
+
# === Directory Tree Example
|
82
|
+
#
|
83
|
+
# This is just as easy as loading from a single file. In this case, instead of
|
84
|
+
# specifying a file name, we specify a path to a directory tree.
|
85
|
+
#
|
86
|
+
# require 'rack/config/flexible'
|
87
|
+
#
|
88
|
+
# use Rack::Config::Flexible :from_file => 'settings' do
|
89
|
+
# # Set the current environment to production
|
90
|
+
# environment :production
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# This expects the directory tree to be laid out as follows:
|
94
|
+
#
|
95
|
+
# settings/environment/section.yaml
|
96
|
+
#
|
97
|
+
# where each directory under _settings_ is an _environment_, containg a separate YAML
|
98
|
+
# file for each _section_. The YAML file itself will only hold
|
99
|
+
# key-value pairs for that particular _section_.
|
100
|
+
#
|
101
|
+
# === Loading Individual Environments/Sections from YAML
|
102
|
+
#
|
103
|
+
# You can load individual _environment_ and _section_ bits from a YAML file as follows:
|
104
|
+
#
|
105
|
+
# require 'rack/config/flexible'
|
106
|
+
#
|
107
|
+
# use Rack::Config::Flexible do
|
108
|
+
# environment :production
|
109
|
+
# section :data, 'cfg/production/data.yaml'
|
110
|
+
#
|
111
|
+
# environment :development, 'cfg/development.yaml'
|
112
|
+
#
|
113
|
+
# # Set the current environment
|
114
|
+
# environment :production
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# Any other calls to #set after #environment or #section will override the data
|
118
|
+
# loaded from the YAML file, if the same key is specified. Otherwise, they
|
119
|
+
# will just add to the Hash per usual.
|
120
|
+
#
|
121
|
+
class Flexible
|
122
|
+
def initialize(app,options={},&block)
|
123
|
+
@app = app
|
124
|
+
@values = {}
|
125
|
+
raise ArgumentError.new('`options\' must be a Hash') unless options.is_a?(Hash)
|
126
|
+
|
127
|
+
if options.has_key?(:from_file) && File.directory?(options[:from_file])
|
128
|
+
# Load from a directory tree
|
129
|
+
Dir[options[:from_file] + '/*'].each { |env|
|
130
|
+
next unless File.directory?(env)
|
131
|
+
environment File.basename(env)
|
132
|
+
|
133
|
+
Dir[env + '/*.yaml'].each { |sec|
|
134
|
+
next unless File.file?(sec)
|
135
|
+
section File.basename(sec,'.yaml'),sec
|
136
|
+
}
|
137
|
+
}
|
138
|
+
elsif options.has_key?(:from_file) && File.exist?(options[:from_file])
|
139
|
+
# Load from a single file
|
140
|
+
@values = Hash[YAML.load_file(options[:from_file]).map { |k,v|
|
141
|
+
[k.to_sym, Hash[v.map { |k,v| [ k.to_sym,v ]}]] if k.is_a?(String) && v.is_a?(Hash)
|
142
|
+
}]
|
143
|
+
@env = @values.keys.first
|
144
|
+
@sec = @values[@env].keys.last
|
145
|
+
end
|
146
|
+
|
147
|
+
instance_eval(&block) if block_given?
|
148
|
+
end
|
149
|
+
|
150
|
+
def call(env)
|
151
|
+
dup._call(env) # For thread safety...
|
152
|
+
end
|
153
|
+
|
154
|
+
def _call(env)
|
155
|
+
env['rack.config'] = self
|
156
|
+
@app.call(env)
|
157
|
+
end
|
158
|
+
|
159
|
+
def configure(&block)
|
160
|
+
instance_eval(&block) if block_given?
|
161
|
+
end
|
162
|
+
|
163
|
+
# :category:DSL
|
164
|
+
#
|
165
|
+
# Set the current environment
|
166
|
+
#
|
167
|
+
# [env]
|
168
|
+
# Environment to use. Defaults to +:production+
|
169
|
+
#
|
170
|
+
# [data]
|
171
|
+
# If this is a +String+, it's assumed to be the location of a YAML file to load from.
|
172
|
+
# Otherwise, a +Hash+ of data for the environment. (Optional)
|
173
|
+
#
|
174
|
+
def environment(env,data=nil)
|
175
|
+
raise ArgumentError.new('`env\' must be a String or Symbol') unless env.is_a?(String) || env.is_a?(Symbol)
|
176
|
+
|
177
|
+
# Load from a hash (if specified) or create a new hash
|
178
|
+
@env = env.to_sym
|
179
|
+
@values[@env] = {} unless @values.has_key?(@env) && @values[@env].is_a?(Hash)
|
180
|
+
|
181
|
+
# Load from a file, or hash, if specified
|
182
|
+
if data.is_a?(String) && File.exist?(data)
|
183
|
+
@values[@env].merge!(Hash[YAML.load_file(data).map { |k,v| [k.to_sym, v] if k.is_a?(String) }])
|
184
|
+
@sec = @values[@env].keys.last
|
185
|
+
elsif data.is_a?(Hash)
|
186
|
+
@values[@env].merge!(data)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# :category:DSL
|
191
|
+
#
|
192
|
+
# Set the current section
|
193
|
+
#
|
194
|
+
# [sec] Section to use. Defaults to +:default+
|
195
|
+
# [vals]
|
196
|
+
# Values to prepopulate this section with.
|
197
|
+
# If this is a +String+, it's assumed to be the location of a YAML file to load from.
|
198
|
+
#
|
199
|
+
def section(sec,vals=nil)
|
200
|
+
raise ArgumentError.new('`sec\' must be a String or Symbol') unless sec.is_a?(String) || sec.is_a?(Symbol)
|
201
|
+
|
202
|
+
@sec = sec.to_sym
|
203
|
+
@values[@env][@sec] = {} unless @values[@env][@sec].is_a?(Hash)
|
204
|
+
@values[@env][@sec].merge!(vals) if vals.is_a?(Hash)
|
205
|
+
|
206
|
+
# If vals is a string, it's assumed to be a YAML file
|
207
|
+
@values[@env][@sec].merge!(YAML.load_file(vals)) if vals.is_a?(String) && File.exist?(vals)
|
208
|
+
end
|
209
|
+
|
210
|
+
# :category:DSL
|
211
|
+
#
|
212
|
+
# Add/Update a value to/in the current section
|
213
|
+
#
|
214
|
+
# [vals] +Hash+ Keys/Value(s) to set
|
215
|
+
#
|
216
|
+
def set(vals)
|
217
|
+
raise ArgumentError.new('`vals\' must be a Hash') unless vals.is_a?(Hash)
|
218
|
+
@values[@env][@sec].merge!(vals)
|
219
|
+
end
|
220
|
+
|
221
|
+
#
|
222
|
+
# Hash-like accessor for config data
|
223
|
+
#
|
224
|
+
# [idx] Path to the requested item, starting with the section (e.g. +default.key.subkey+)
|
225
|
+
#
|
226
|
+
# Returns the value, or +nil+ if the value cannot be located
|
227
|
+
#
|
228
|
+
def [](idx)
|
229
|
+
manipulate_element(idx)
|
230
|
+
end
|
231
|
+
|
232
|
+
#
|
233
|
+
# Hash-like modifier for config data
|
234
|
+
#
|
235
|
+
# This will replace the value at +idx+ with +value+ if, and only if,
|
236
|
+
# +idx+ already exists.
|
237
|
+
#
|
238
|
+
# [idx] Path to the requested item, starting with the section (e.g. +default.key.subkey+)
|
239
|
+
# [value] New value to set
|
240
|
+
#
|
241
|
+
def []=(idx,value)
|
242
|
+
raise ArgumentError.new('`idx\' must be a String') unless idx.is_a?(String)
|
243
|
+
manipulate_element(idx,value)
|
244
|
+
end
|
245
|
+
|
246
|
+
protected
|
247
|
+
#
|
248
|
+
# Manipulate (or lookup) the element specified by +idx+
|
249
|
+
#
|
250
|
+
def manipulate_element(idx,value=nil)
|
251
|
+
raise ArgumentError.new('`idx\' must be a String') unless idx.is_a?(String)
|
252
|
+
idx = idx + '.' unless idx.index('.')
|
253
|
+
tmph = nil ; pieces = idx.split(/\.+/)
|
254
|
+
|
255
|
+
pieces.each_with_index { |part,i|
|
256
|
+
# Look for the section
|
257
|
+
if tmph.nil?
|
258
|
+
tmph = @values[@env][part]
|
259
|
+
tmph ||= @values[@env][part.to_sym]
|
260
|
+
next
|
261
|
+
end
|
262
|
+
|
263
|
+
# Return nil if we can't find the value
|
264
|
+
return nil unless tmph.is_a?(Hash) && (tmph.has_key?(part) || tmph.has_key?(part.to_sym))
|
265
|
+
|
266
|
+
# Otherwise, return or replace the value when we do find it
|
267
|
+
if i == (pieces.size - 1) && !value.nil?
|
268
|
+
tmph.has_key?(part) ? tmph[part] = value : tmph[part.to_sym] = value
|
269
|
+
else
|
270
|
+
tmph = tmph.has_key?(part) ? tmph[part] : tmph[part.to_sym]
|
271
|
+
end
|
272
|
+
}
|
273
|
+
|
274
|
+
return tmph
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# vi:set ts=2 sw=2 expandtab sta:
|
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-config-flexible
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tim Hentenaar
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-17 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! " Rack::Config::Flexible is an alternative to Rack::Config,\n offering
|
15
|
+
much greater flexibility.\n \n Configuration options are stored as key-value pairs
|
16
|
+
in _sections_,\n partitioned by _environments_. For example:\n \n + environment\n
|
17
|
+
\ + section\n key -> value pairs\n \n A simple DSL is provided and
|
18
|
+
can be used either within a passed\n configuration block (to ::new), or to the
|
19
|
+
#configuration method.\n \n Facilities are also provided to load whole environments,
|
20
|
+
and sections\n from either a single YAML file structured like, or from a directory
|
21
|
+
tree.\n\n See the README file or RDoc documentation for more info.\n"
|
22
|
+
email: tim.hentenaar@gmail.com
|
23
|
+
executables: []
|
24
|
+
extensions: []
|
25
|
+
extra_rdoc_files: []
|
26
|
+
files:
|
27
|
+
- lib/rack/config/flexible.rb
|
28
|
+
- README.md
|
29
|
+
- LICENSE
|
30
|
+
homepage: https://github.com/thentenaar/rack-config-flexible
|
31
|
+
licenses: []
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ! '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
requirements: []
|
49
|
+
rubyforge_project:
|
50
|
+
rubygems_version: 1.8.24
|
51
|
+
signing_key:
|
52
|
+
specification_version: 3
|
53
|
+
summary: An alternative to Rack::Config, offering much greater flexibility
|
54
|
+
test_files: []
|