fuguta 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 (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
+