rack-config-flexible 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/LICENSE +22 -0
  2. data/README.md +117 -0
  3. data/lib/rack/config/flexible.rb +280 -0
  4. 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: []