configuru 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5db1722e4793d1f8627e6c8c8b796cdac8e83d50
4
- data.tar.gz: 9e45658f486a5d39da387eb2479c172b7aee5f4b
3
+ metadata.gz: 9afa59fbf6810cce708f1d83b2c10f0c66023072
4
+ data.tar.gz: 7db11437ea98c4b7d884bd5ac33f3323dc851f79
5
5
  SHA512:
6
- metadata.gz: 6e8073b7a2cbfd96c44f92ed190c796a7a61cfcbc7363ecca705521a517f5bca2c0a7fe6fd0d88258e5316e1f3cc6542f18d5ffb33f76cc5dd6e66e536104474
7
- data.tar.gz: ef1b06f4d8c2238f7414538a2b54ddfe70e2c0d857c86f1bd1d388a0ab3a0c755aeef2078a499b247445c42a6402c52c55137679309f84ae380a05f852bbfb13
6
+ metadata.gz: 8c3153582db20d9bccebf17ee2648af211b66bc8ba6a23c5373aea1f09d0630f3f062c7fd1d0aea90059f008be44af8ef58bccedba11a2572543854279d2d095
7
+ data.tar.gz: b54582a521c9a7ea4525ef4966b05ceb55a8a721955655e41f47dce8a894a5b7c692d67ef42765d1e14948828e279478eea6ebf3fd53162f37fa9f5fe549de0a
data/README.md CHANGED
@@ -8,12 +8,22 @@ Requires Ruby version >= 2.1.
8
8
 
9
9
  ## Installation
10
10
 
11
- Add this line to your application's Gemfile:
11
+ To add to an application, add this line to your application's Gemfile:
12
12
 
13
13
  ```ruby
14
14
  gem 'configuru'
15
15
  ```
16
16
 
17
+ To add to a gem, add the following line to your gemspec file:
18
+
19
+ ```ruby
20
+ Gem::Specification.new do |spec|
21
+ ...
22
+ spec.add_dependency 'configuru'
23
+ ...
24
+ end
25
+ ```
26
+
17
27
  And then execute:
18
28
 
19
29
  $ bundle
@@ -24,7 +34,11 @@ Or install it yourself as:
24
34
 
25
35
  ## Usage
26
36
 
27
- The typical usage scenario is provided below:
37
+ ### Overview
38
+
39
+ This gem allows to add convenient configuration API to modules, classes and instances in your gem or application. It is intentially designed to minimize "behind-the-scenes auto-magic" and yet keep it easy to use and the calling code concise.
40
+
41
+ Here is an example of how Configuru can be used to provide configuration for `MyClass`:
28
42
 
29
43
  ```ruby
30
44
  require 'configuru'
@@ -55,7 +69,255 @@ my_inst.configuration.secret_key #=> "VERY-SECR-ETKY"
55
69
  my_inst.configuration.percentage #=> 100
56
70
  ```
57
71
 
72
+ ### Making your objects configurable
73
+
74
+ To make your class or module configurable you need to do two things:
75
+
76
+ 1. `include Configuru::Configurable`
77
+ 2. then call `provide_configuration` with an optional parameter as defined below:
78
+
79
+ ```ruby
80
+ provide_configuration :base # Using one of these three options will add configuration API
81
+ provide_configuration :class # only to your class/module. The individual instances of that class
82
+ provide_configuration :module # will not be given any configuration API.
83
+
84
+ provide_configuration :instances # Using one of these two options will add configuration API to
85
+ provide_configuration :instance # individual instances of the class, but not for the class itself.
86
+
87
+ provide_configuration # Using these options will add configuration API both to the class
88
+ provide_configuration :all # and to the instances of that class
89
+ ```
90
+
91
+ ### Configuration API
92
+
93
+ #### Defining possible configuration parameters
94
+ The class module, inside which you called `provide_configuration`, will get a `def_config_param` method for defining possible configuration parameters. This method takes a parameter name and various options for it:
95
+
96
+ * Provide default value for the configuration parameter. The value is evaluated at the time `def_config_param` is called. The checks and conversions specified for the parameter (see below) are *not* applied to the default value.
97
+
98
+ ```ruby
99
+ def_config_param :some_name, default: ""
100
+ ```
101
+
102
+ * Prevent the parameter from changing once configuration is locked (see section "Locking configuration" below)
103
+
104
+ ```ruby
105
+ def_config_param :some_name, lockable: true
106
+ ```
107
+
108
+ * When setting, check that the value is not nil or not empty. "Not empty" means that both `.nil?` and `.empty?` for it return `false`. Raise ArgumentError exception if the check fails.
109
+
110
+ ```ruby
111
+ def_config_param :some_name, not_nil: true # Check for "not nil"
112
+ def_config_param :some_name, not_empty: true # Check for "not empty"
113
+ ```
114
+
115
+ * When setting, check that the value is of a certain type (`is_a?` returns true). If an array of types is passed, the value must be of *any* of the types from that array. Raise ArgumentError exception if the check fails.
116
+
117
+ ```ruby
118
+ def_config_param :some_name, must_be: String
119
+ def_config_param :some_name, must_be: [IO,File,StringIO]
120
+ ```
121
+
122
+ * When setting, perform duck-type-checking. Check that the value resonds to a certain method. If an array of method names is passed, the value must respond to *all* specified methods. Raise ArgumentError exception if the check fails.
123
+
124
+ ```ruby
125
+ def_config_param :some_name, must_respond_to: :read
126
+ def_config_param :some_name, must_respond_to: [:read,:seek]
127
+ ```
128
+
129
+ * When setting, convert the value to a specific type
130
+
131
+ ```ruby
132
+ def_config_param :some_name, make_hash: true # Hash(value)
133
+ def_config_param :some_name, make_array: true # Array(value)
134
+ def_config_param :some_name, make_string: true # String(value)
135
+ def_config_param :some_name, make_int: true # value.to_i
136
+ def_config_param :some_name, make_float: true # value.to_f
137
+ def_config_param :some_name, make_bool: true # !!value
138
+ ```
139
+
140
+ * When setting, check that the value is within the specified boundaries. Raise ArgumentError exception if the check fails.
141
+
142
+ ```ruby
143
+ def_config_param :some_name, max: 10 # Raise exception if trying to set value > 10
144
+ def_config_param :some_name, min: 0.01 # Raise exception if trying to set value < 0.01
145
+ def_config_param :some_name, in: ('a'..'z') # Raise exception unless ('a'..'z').include?(value)
146
+ ```
147
+
148
+ * When setting, perform call the conversion method. The original value is passed as the only parameter. The method should either return the converted value, or raise some exception. If a symbol is passed, the method with that name is called on the object under configuration.
149
+
150
+ ```ruby
151
+ class MyClass {
152
+ def_config_param :some_name1, convert: :some_conversion
153
+ def_config_param :some_name2, convert: ->(x) { x.abs }
154
+ }
155
+
156
+ my_inst = MyClass.new
157
+ my_inst.configure( some_name1: "abc" ) # will call my_inst.some_conversion("abc") and assign the result to some_name1 parameter
158
+ ```
159
+
160
+ A single parameter may have several checks and conversions associated with it. If several are defined, they will be applied in the order they are defined in the list above.
161
+ For example, if the parameter is defined as
162
+
163
+ ```ruby
164
+ class MyClass {
165
+ include Configuru::Configurable
166
+ provide_configuration :class
167
+
168
+ def_config_param :myparam, lockable: true, not_nil: true, make_int: true, in: (-3..3), convert: ->(x) { x.abs }
169
+ }
170
+ ```
171
+
172
+ If you call `MyClass.configure( myparam: "-1" )` then Configuru will:
173
+ 1. Check that `MyClass.configuration` is not locked yet (and raise exception if it is).
174
+ 2. Check that the provided value is not nil (and raise exception if it is).
175
+ 3. Convert the value to int (and raise exception if it can't do that).
176
+ 4. Check that it is included in the -3..3 range (and raise exception if it is not).
177
+ 5. Convert it: get the absolute value as specifed in the provided lambda.
178
+ 6. Assign the resulting value to the `myparam` configuration parameter.
179
+
180
+ In this case (unless the configuration was locked) the parameter will eventually be set to 1.
181
+
182
+
183
+ #### Working with configuration parameters through configuration object
184
+ The object that you made configurable using `provide_configuration` will get a `configuration` method that returns its configuration object. The configuration object has all the parameters defined through `def_config_param` as its attributes and allows reading and writing them. The same parameter can be set several times, and each later value replaces the one set earlier.
185
+
186
+ ```ruby
187
+ require 'configuru'
188
+
189
+ class MyClass
190
+ include Configuru::Configurable
191
+ provide_configuration :instance
192
+
193
+ def_config_param :secret_key, make_string: true, default: (ENV['SECRET_KEY_'] || '???')
194
+ def_config_param :color, default: :green,
195
+ convert: ->(val) { raise "Huh?" unless [:red,:green].include?(val); val }
196
+ def_config_param :percentage, make_float:true, min:0, max:100, default:100
197
+ end
198
+
199
+
200
+ my_inst = MyClass.new
201
+
202
+ my_inst.configuration.secret_key = "VERY-SECR-ETKY"
203
+ my_inst.configuration.color = :red
204
+
205
+ my_inst.configuration.color #=> :red
206
+ my_inst.configuration.secret_key #=> "VERY-SECR-ETKY"
207
+ my_inst.configuration.percentage #=> 100
208
+ ```
209
+
210
+ ##### Configure method
211
+ The object that you made configurable using `provide_configuration` also gets a `configure` convenience method. The method allows you to either provide configuration parameters as a hash or call the provided block with the configuration object.
212
+
213
+ ```ruby
214
+ require 'configuru'
215
+
216
+ class MyClass
217
+ include Configuru::Configurable
218
+ provide_configuration :instance
219
+
220
+ def_config_param :secret_key, make_string: true, default: (ENV['SECRET_KEY_'] || '???')
221
+ def_config_param :color, default: :green,
222
+ convert: ->(val) { raise "Huh?" unless [:red,:green].include?(val); val }
223
+ def_config_param :percentage, make_float:true, min:0, max:100, default:100
224
+ end
225
+
226
+
227
+ my_inst = MyClass.new
228
+
229
+ # Setting parameters using Hash
230
+ my_inst.configure secret_key: "VERY-SECR-ETKY", color: :red
231
+
232
+ # Setting parameters using the block called on the configuration object
233
+ my_inst.configure do |config|
234
+ config.secret_key = "VERY-SECR-ETKY"
235
+ config.color = :red
236
+ end
237
+ ```
238
+
239
+ It is also possible to use Hash and block in the same call to configure. In this case first the Hash will be precessed, then the block will be called. So, any values set in the block will supersede the values set through Hash.
240
+
241
+ ```ruby
242
+ my_inst.configure(color: :red) do |config|
243
+ config.secret_key = "VERY-SECR-ETKY"
244
+ end
245
+ ```
246
+
247
+ ##### Reading configuration from file
248
+ The configuration object can also read configuration from a YAML file using `options_source=` call on the configuration object or `options_source` key in the Hash provided to configure. The `options_source=` can be called several times for a configuration object, and the parameters will be loaded from files in the order they are called. The file reading can also be nested: YAML files themselves may have the `options_source` parameter defined inside. As with direct parameter setting, if a parameter is set several times in multiple files, the latest value replaces all previous values.
249
+
250
+ ```ruby
251
+ require 'configuru'
252
+
253
+ class MyClass
254
+ include Configuru::Configurable
255
+ provide_configuration :instance
256
+
257
+ # bunch of def_config_param calls ...
258
+ end
58
259
 
260
+ my_inst = MyClass.new
261
+
262
+ # Directly call on the configuraton object - this will load the parameters from the three
263
+ # files in the order they are specified
264
+ my_inst.configuration.options_source = "/my/path/to/file/with/options1.yml"
265
+ my_inst.configuration.options_source = "/my/path/to/file/with/options2.yml"
266
+ my_inst.configure do |config|
267
+ config.options_source = "/my/path/to/file/with/options3.yml"
268
+ end
269
+
270
+ # Or, pass it as a part of Hash to configure
271
+ my_inst.configure options_source: "/my/path/to/file/with/options1.yml"
272
+ ```
273
+
274
+ Using `options_source` may be combined with directly setting the parameters. And, again, the values provided later overwrite previously provided values for the same parameter.
275
+
276
+ ```ruby
277
+ my_inst = MyClass.new do |config|
278
+ config.secret_key = "VERY-SECR-ETKY"
279
+ config.color = :red
280
+ config.options_source = "/my/path/to/file/with/options.yml" # will overwrite color and secret_key if defined in YAML
281
+ end
282
+ ```
283
+
284
+ ##### Locking configuration
285
+ The configuration object also has a `lock` method. Calling this method prevents the parameters defined as lockable from further changes. The parameters, for which the `lockable` option was not set, are not affected. Locking is useful if you somehow cache configuration parameters in other parts of your application/gem, and further changes to the parameter will not affect the configuration behavior.
286
+
287
+ For example, if one of your parameters is a database name or an AWS access key, once you use this parameter at the beginning of your applicaton to establish connection to the database or get access to your AWS resources, further changes to it won't make your application to reconnect to a different database or start accessing AWS using a different set of credentials. In this case, locking helps to catch configuraton bugs, when the parameter is set or changed too late.
288
+
289
+ Locking affects all parameters marked as `lockable`. There is no locking at individual parameter level.
290
+
291
+ If a lockable parameter is accessed after `lock` has been called, `ArgumentError` is raised.
292
+
293
+ ```ruby
294
+ require 'configuru'
295
+
296
+ class MyClass
297
+ include Configuru::Configurable
298
+ provide_configuration :instance
299
+
300
+ def_config_param :database, lockable: true
301
+ end
302
+
303
+ my_inst = MyClass.new
304
+
305
+ my_inst.configuration.database = 'db_production'
306
+ my_inst.configuration.lock
307
+ my_inst.configuration.database = 'db_staging' # Raises ArgumentError
308
+ ```
309
+
310
+ It is also possible to unlock the parameters after they were locked by calling `lock(false)`. Using the previous example:
311
+
312
+ ```ruby
313
+ my_inst.configuration.database = 'db_production'
314
+ my_inst.configuration.lock
315
+ my_inst.configuration.database = 'db_staging' # Raises ArgumentError
316
+ my_inst.configuration.lock(false)
317
+ my_inst.configuration.database = 'db_development' # Works fine
318
+ ```
319
+
320
+ Both `lock` and `lock(false)` can be called several times. But there is no counter: after calling `lock` multiple times, a single `lock(false)` would unlock all parameters.
59
321
 
60
322
  ## Versioning
61
323
 
@@ -74,9 +336,26 @@ The public API should not be considered stable.
74
336
 
75
337
  Requires Ruby version >= 2.1
76
338
 
339
+ ## Backlog
340
+
341
+ The following features may be included in the future versions of this gem, if a need for them is identified.
342
+ If you'd like to see one of these features or something else implemented in Configuru, please let the authors know.
343
+
344
+ * Provide callbacks for when parameters are changed (would allow reconnecting to a DB, for example when a database name is changed)
345
+ * Provide a choice for locked parameters: throw an exception or silently ignore, may be with a callback
346
+ * Add logging and make it configurable (simplify life for people who want to see the parameter values logged)
347
+ * Lock at parameter level (are there any real-life scenarios when that is needed?)
348
+ * Dump parameters to Hash/YAML file (allows saving/restoring configuration)
349
+ * Add write-once parameters: auto-locking after setting for the 1st time (does anybody need that?)
350
+ * Add required parameters, when reqding it w/o setting it first would raise an exception rather than using the default value
351
+ * Allow iterating through all parameters/parameter names (useful for alt storage mechanisms - e.g. store in DB)
352
+ * Better integration with ENV - automatically add variables as parameters using a defined list of names
353
+ * Other formats besides Hash & YAML? - probably not, it's easy to add on top after dumping and/or iterating are implemented
354
+ * Use gem-specific exceptions, not ArgumentError for all cases
355
+
77
356
  ## Contributing
78
357
 
79
- 1. Fork it ( https://github.com/[my-github-username]/configuru/fork )
358
+ 1. Fork it ( https://github.com/moonfly/configuru/fork )
80
359
  2. Create your feature branch (`git checkout -b my-new-feature`)
81
360
  3. Commit your changes (`git commit -am 'Add some feature'`)
82
361
  4. Push to the branch (`git push origin my-new-feature`)
@@ -51,10 +51,10 @@ module Configuru
51
51
  value = value.to_f if options[:make_float]
52
52
  value = !!value if options[:make_bool]
53
53
  if options.has_key?(:max) && (value > options[:max])
54
- raise ArgumentError.new("'#{name.to_s}' must no be more than #{options[:max]}")
54
+ raise ArgumentError.new("'#{name.to_s}' must be not more than #{options[:max]}")
55
55
  end
56
56
  if options.has_key?(:min) && (value < options[:min])
57
- raise ArgumentError.new("'#{name.to_s}' must no be less than #{options[:min]}")
57
+ raise ArgumentError.new("'#{name.to_s}' must be not less than #{options[:min]}")
58
58
  end
59
59
  if options.has_key?(:in) && !options[:in].include?(value)
60
60
  raise ArgumentError.new("'#{name.to_s}' is out of range")
@@ -1,3 +1,3 @@
1
1
  module Configuru
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: configuru
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - moonfly (Andrey Pronin)