fuguta 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/fuguta.rb +354 -0
  2. metadata +54 -0
@@ -0,0 +1,354 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Fuguta
4
+ class Configuration
5
+
6
+ class ValidationError < StandardError
7
+ attr_reader :errors
8
+ def initialize(errors)
9
+ super("validation error")
10
+ @errors = errors
11
+ end
12
+ end
13
+
14
+ def self.walk_tree(conf, &blk)
15
+ raise ArgumentError, "conf must be a 'Configuration'. Got '#{conf.class}'." unless conf.is_a?(Configuration)
16
+
17
+ blk.call(conf)
18
+ conf.config.values.each { |c|
19
+ case c
20
+ when Configuration
21
+ walk_tree(c, &blk)
22
+ when Hash
23
+ c.values.each { |c1| walk_tree(c1, &blk) if c1.is_a?(Configuration) }
24
+ when Array
25
+ c.each { |c1| walk_tree(c1, &blk) if c1.is_a?(Configuration) }
26
+ end
27
+ }
28
+ end
29
+
30
+ class Loader
31
+ def initialize(conf)
32
+ @conf = conf
33
+ end
34
+
35
+ def load(path)
36
+ buf = String.new
37
+ case path
38
+ when String
39
+ raise "does not exist: #{path}" unless File.exists?(path)
40
+ buf = File.read(path)
41
+ when IO
42
+ path.lines.each { |l| buf += l }
43
+ else
44
+ raise "Unknown type: #{path.class}"
45
+ end
46
+
47
+ @conf.parse_dsl do |me|
48
+ me.instance_eval(buf, path.to_s)
49
+ end
50
+ end
51
+
52
+ def validate
53
+ errors = []
54
+ Configuration.walk_tree(@conf) do |c|
55
+ c.validate(errors)
56
+ end
57
+ raise ValidationError, errors if errors.size > 0
58
+ end
59
+ end
60
+
61
+ class DSLProxy
62
+ def initialize(subject)
63
+ @subject = subject
64
+ @config = subject.config
65
+ end
66
+
67
+ def config
68
+ self
69
+ end
70
+ end
71
+
72
+ module ConfigurationMethods
73
+ module ClassMethods
74
+ # Helper method to define class specific configuration class.
75
+ #
76
+ # def_configuration(&blk) is available when you include this module
77
+ #
78
+ # # Example:
79
+ # class Base
80
+ # include Fuguta::Configuration::ConfigurationMethods
81
+ # def_configuration do
82
+ # param :xxxx
83
+ # param :yyyy
84
+ # end
85
+ # end
86
+ #
87
+ # Above example does exactly same thing as below:
88
+ #
89
+ # class Base
90
+ # class Configuration < Fuguta::Configuration
91
+ # param :xxxx
92
+ # param :yyyy
93
+ # end
94
+ # @configuration_class = Configuration
95
+ # end
96
+ #
97
+ # # Examples for new classes of Base inheritance.
98
+ # class A < Base
99
+ # def_configuration do
100
+ # param :zzzz
101
+ # end
102
+ # def_configuration do
103
+ # param :xyxy
104
+ # end
105
+ #
106
+ # p Configuration # => A::Configuration
107
+ # p Configuration.superclass # => Base::Configuration
108
+ # p @configuration_class # => A::Configuration
109
+ # end
110
+ #
111
+ # class B < A
112
+ # p self.configuration_class # => A::Configuration
113
+ # end
114
+ def def_configuration(&blk)
115
+ # create new configuration class if not exist.
116
+ if self.const_defined?(:Configuration, false)
117
+ unless self.const_get(:Configuration, false) < Fuguta::Configuration
118
+ raise TypeError, "#{self}::Configuration constant is defined already for another purpose."
119
+ end
120
+ else
121
+ self.const_set(:Configuration, Class.new(self.configuration_class || Fuguta::Configuration))
122
+ @configuration_class = self.const_get(:Configuration, false)
123
+ end
124
+ if blk
125
+ @configuration_class.module_eval(&blk)
126
+ end
127
+ end
128
+
129
+ def configuration_class
130
+ ConfigurationMethods.find_configuration_class(self)
131
+ end
132
+ end
133
+
134
+ def self.find_configuration_class(c)
135
+ begin
136
+ v = c.instance_variable_get(:@configuration_class)
137
+ return v if v
138
+ if c.const_defined?(:Configuration, false)
139
+ return c.const_get(:Configuration, false)
140
+ end
141
+ end while c = c.superclass
142
+ nil
143
+ end
144
+
145
+ private
146
+ def self.included(klass)
147
+ klass.extend ClassMethods
148
+ end
149
+ end
150
+
151
+ class << self
152
+ def on_initialize_hook(&blk)
153
+ @on_initialize_hooks << blk
154
+ end
155
+
156
+ def on_initialize_hooks
157
+ @on_initialize_hooks
158
+ end
159
+
160
+ # Show warning message if the old parameter is set.
161
+ def deprecated_warn_param(old_name, message=nil, &blk)
162
+ on_param_create_hook do |param_name, opts|
163
+ warn_msg = message || "WARN: Deprecated parameter: #{old_name}. Please use '#{param_name}'."
164
+
165
+ alias_param old_name, param_name
166
+ self.const_get(:DSL, false).class_eval %Q{
167
+ def #{old_name}(v)
168
+ STDERR.puts "#{warn_msg}"
169
+ #{param_name.to_s}(v)
170
+ end
171
+ }
172
+ end
173
+ end
174
+
175
+ # Raise an error if the old parameter is set.
176
+ def deprecated_error_param(old_name, message=nil)
177
+ on_param_create_hook do |param_name, opts|
178
+ err_msg = message || "ERROR: Parameter is no longer supported: #{old_name}. Please use '#{param_name}'."
179
+
180
+ alias_param old_name, param_name
181
+ self.const_get(:DSL, false).class_eval %Q{
182
+ def #{old_name}(v)
183
+ raise "#{err_msg}"
184
+ end
185
+ }
186
+ end
187
+ end
188
+
189
+ def alias_param (alias_name, ref_name)
190
+ # getter
191
+ self.class_eval %Q{
192
+ # Ruby alias show error if the method to be defined later is
193
+ # set. So create method to call the reference method.
194
+ def #{alias_name.to_s}()
195
+ #{ref_name}()
196
+ end
197
+ }
198
+
199
+ # DSL setter
200
+ self.const_get(:DSL, false).class_eval %Q{
201
+ def #{alias_name}(v)
202
+ #{ref_name.to_s}(v)
203
+ end
204
+ alias_method :#{alias_name.to_s}=, :#{alias_name.to_s}
205
+ }
206
+ end
207
+
208
+ def param(name, opts={})
209
+ opts = opts.merge(@opts)
210
+
211
+ case opts[:default]
212
+ when Proc
213
+ # create getter method if proc is set as default value
214
+ self.class_exec {
215
+ define_method(name.to_s.to_sym) do
216
+ @config[name.to_s.to_sym] || self.instance_exec(&opts[:default])
217
+ end
218
+ }
219
+ else
220
+ on_initialize_hook do |c|
221
+ @config[name.to_s.to_sym] = opts[:default]
222
+ end
223
+ end
224
+
225
+ @on_param_create_hooks.each { |blk|
226
+ blk.call(name.to_s.to_sym, opts)
227
+ }
228
+ self.const_get(:DSL, false).class_eval %Q{
229
+ def #{name}(v)
230
+ @config["#{name.to_s}".to_sym] = v
231
+ end
232
+ alias_method :#{name.to_s}=, :#{name.to_s}
233
+ }
234
+
235
+ @opts.clear
236
+ @on_param_create_hooks.clear
237
+ end
238
+
239
+ def load(*paths)
240
+ c = self.new
241
+
242
+ l = Loader.new(c)
243
+
244
+ paths.each { |path|
245
+ l.load(path)
246
+ }
247
+ l.validate
248
+
249
+ c
250
+ end
251
+
252
+ # Helper method defines "module DSL" under the current conf class.
253
+ #
254
+ # This does mostly same things as "module DSL" but using
255
+ # "module" statement get the "DSL" constant defined in unexpected
256
+ # location if you combind to use with other Ruby DSL syntax.
257
+ #
258
+ # Usage:
259
+ # class Conf1 < Configuration
260
+ # DSL do
261
+ # end
262
+ # end
263
+ def DSL(&blk)
264
+ self.const_get(:DSL, false).class_eval(&blk)
265
+ self
266
+ end
267
+
268
+ private
269
+ def inherited(klass)
270
+ super
271
+ klass.const_set(:DSL, Module.new)
272
+ klass.instance_eval {
273
+ @on_initialize_hooks = []
274
+ @opts = {}
275
+ @on_param_create_hooks = []
276
+ }
277
+
278
+ dsl_mods = []
279
+ c = klass
280
+ while c < Configuration && c.superclass.const_defined?(:DSL, false)
281
+ parent_dsl = c.superclass.const_get(:DSL, false)
282
+ if parent_dsl && parent_dsl.class === Module
283
+ dsl_mods << parent_dsl
284
+ end
285
+ c = c.superclass
286
+ end
287
+ # including order is ancestor -> descendants
288
+ dsl_mods.reverse.each { |i|
289
+ klass.const_get(:DSL, false).__send__(:include, i)
290
+ }
291
+ end
292
+
293
+ def on_param_create_hook(&blk)
294
+ @on_param_create_hooks << blk
295
+ end
296
+ end
297
+
298
+ attr_reader :config, :parent
299
+
300
+ def initialize(parent=nil)
301
+ unless parent.nil?
302
+ raise ArgumentError, "parent must be a 'Fuguta::Configuration'. Got '#{parent.class}'." unless parent.is_a?(Fuguta::Configuration)
303
+ end
304
+ @config = {}
305
+ @parent = parent
306
+
307
+ hook_lst = []
308
+ c = self.class
309
+ while c < Configuration
310
+ hook_lst << c.instance_variable_get(:@on_initialize_hooks)
311
+ c = c.superclass
312
+ end
313
+
314
+ hook_lst.reverse.each { |l|
315
+ l.each { |c|
316
+ self.instance_eval(&c)
317
+ }
318
+ }
319
+
320
+ after_initialize
321
+ end
322
+
323
+ def after_initialize
324
+ end
325
+
326
+ def validate(errors)
327
+ end
328
+
329
+ def parse_dsl(&blk)
330
+ dsl = self.class.const_get(:DSL, false)
331
+ raise "DSL module was not found" unless dsl && dsl.is_a?(Module)
332
+
333
+ cp_class = DSLProxy.dup
334
+ cp_class.__send__(:include, dsl)
335
+ cp = cp_class.new(self)
336
+
337
+ cp.instance_eval(&blk)
338
+
339
+ self
340
+ end
341
+
342
+ private
343
+ def method_missing(m, *args)
344
+ if @config.has_key?(m.to_sym)
345
+ @config[m.to_sym]
346
+ elsif @config.has_key?(m.to_s)
347
+ @config[m.to_s]
348
+ else
349
+ super
350
+ end
351
+ end
352
+
353
+ end
354
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fuguta
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: "1.0"
6
+ platform: ruby
7
+ authors:
8
+ - Axsh co. LTD
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2013-04-24 00:00:00 Z
14
+ dependencies: []
15
+
16
+ description: A configuration framework for Ruby programs
17
+ email:
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/fuguta.rb
26
+ homepage: https://github.com/axsh/fuguta
27
+ licenses: []
28
+
29
+ post_install_message:
30
+ rdoc_options: []
31
+
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: "0"
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ requirements: []
47
+
48
+ rubyforge_project:
49
+ rubygems_version: 1.8.25
50
+ signing_key:
51
+ specification_version: 3
52
+ summary: A configuration framework for Ruby programs
53
+ test_files: []
54
+