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.
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: []