configurable 0.1.0 → 0.3.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.
data/History ADDED
@@ -0,0 +1,9 @@
1
+ == 0.3.0 / 2009-02-17
2
+
3
+ Significant rework of the 0.1.0 release:
4
+
5
+ * Added CDoc
6
+ * Expanded use of nested configs
7
+ * Added dumping of configs for config files
8
+ * Updates to Range/Regexp validations
9
+ * Numerous bug fixes
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008, Regents of the University of Colorado.
1
+ Copyright (c) 2008-2009, Regents of the University of Colorado.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this
4
4
  software and associated documentation files (the "Software"), to deal in the Software
data/README CHANGED
@@ -1,25 +1,27 @@
1
1
  = Configurable[http://tap.rubyforge.org/configurable]
2
2
 
3
- Class configurations that map to the command line. Configurable is used by the
4
- Tap[http://tap.rubyforge.org] framework.
3
+ Class configurations that map to the command line.
5
4
 
6
5
  == Description
7
6
 
8
- Configurable allows the declaration of inheritable, class-based configurations
9
- that map to methods but may be accessed like a hash; a setup that is both fast
10
- and convenient. Configurable facilitates the use of configuration files, and
11
- parsing of configurations from the command line.
7
+ Configurable allows the declaration of class configurations. Configurations
8
+ are inheritable, delegate to methods, and have hash-like access. Configurable
9
+ maps configurations to the command line through ConfigParser and is used by
10
+ the Tap[http://tap.rubyforge.org] framework.
12
11
 
13
12
  Check out these links for development, and bug tracking.
14
13
 
15
14
  * Website[http://tap.rubyforge.org/configurable]
16
15
  * Github[http://github.com/bahuvrihi/configurable/tree/master]
17
- * Lighthouse[]
16
+ * Lighthouse[http://bahuvrihi.lighthouseapp.com/projects/21202-configurable/tickets?q=state%3Aopen]
18
17
  * {Google Group}[http://groups.google.com/group/ruby-on-tap]
19
18
 
20
19
  == Usage
21
20
 
22
- === Quickstart
21
+ Use the config method to declare class configurations. A block may be provided
22
+ to validate inputs, and many standard validations are available through the 'c'
23
+ method (an alias for the {Validation}[link:classes/Configurable/Validation.html]
24
+ module).
23
25
 
24
26
  class ConfigClass
25
27
  include Configurable
@@ -38,23 +40,12 @@ Check out these links for development, and bug tracking.
38
40
  end
39
41
  end
40
42
 
41
- Configurations are present and documented in the class ConfigParser, the
42
- Configurable equivalent of OptionParser:
43
+ A ConfigParser can parse configurations from command line arguments, and turn
44
+ them into a documented help string:
43
45
 
44
- parser = ConfigClass.parser
45
- parser.class # => ConfigParser
46
- "\n" + parser.to_s
47
- # => %Q{
48
- # -k, --key KEY a simple config with short
49
- # --flag a flag config
50
- # --[no-]switch a --[no-]switch config
51
- # --num NUM integer only
52
- # --range RANGE range only
53
- # --upcase UPCASE custom transformation
54
- # }
46
+ parser = ConfigParser.new
47
+ parser.add(ConfigClass.configurations)
55
48
 
56
- Command line arguments parse as expected:
57
-
58
49
  parser.parse "one two --key=value --flag --no-switch --num 8 --range a..z three"
59
50
  # => ['one', 'two', 'three']
60
51
 
@@ -67,21 +58,37 @@ Command line arguments parse as expected:
67
58
  # :range => 'a..z',
68
59
  # :upcase => 'default'
69
60
  # }
61
+
62
+ "\n" + parser.to_s
63
+ # => %Q{
64
+ # -k, --key KEY a simple config with short
65
+ # --flag a flag config
66
+ # --[no-]switch a --[no-]switch config
67
+ # --num NUM integer only
68
+ # --range RANGE range only
69
+ # --upcase UPCASE custom transformation
70
+ # }
70
71
 
71
- Validations/transformations occur upon initialization:
72
+ Configurable classes typically call initialize_config to set configurations
73
+ during initialization. The validation/transformation blocks are called as
74
+ configurations are set. Notice how the :range and :upcase values have been
75
+ transformed from the input config.
72
76
 
73
77
  c = ConfigClass.new(parser.config)
74
78
  c.config.to_hash
75
79
  # => {
76
80
  # :key => 'value',
77
- # :flag => true,
78
- # :switch => false,
81
+ # :flag => true,
82
+ # :switch => false,
79
83
  # :num => 8,
80
- # :range => 'a'..'z',
81
- # :upcase => 'DEFAULT'
84
+ # :range => 'a'..'z', # notice these values
85
+ # :upcase => 'DEFAULT' # have been transformed
82
86
  # }
83
87
 
84
- Configurations have accessors, and are accessible through config.
88
+ Configurations automatically generate accessors (the blocks are basically
89
+ writer methods), but they are also accessible through the hash-like config
90
+ object. Configurations are validated every time they are set, regardless of
91
+ whether they are set through an accessor or config.
85
92
 
86
93
  c.upcase # => 'DEFAULT'
87
94
 
@@ -91,12 +98,8 @@ Configurations have accessors, and are accessible through config.
91
98
  c.upcase = 'fiNal Value'
92
99
  c.config[:upcase] # => 'FINAL VALUE'
93
100
 
94
- Note that configurations are validated every time they are set:
95
-
96
- c.num = 'blue' # !> ValidationError
97
-
98
- By default config treats strings and symbols as the same, so YAML config files
99
- are easily created and used.
101
+ By default config treats string and symbol keys identically, making YAML an
102
+ obvious choice for configuration files.
100
103
 
101
104
  yaml_str = %Q{
102
105
  key: a new value
@@ -114,113 +117,8 @@ are easily created and used.
114
117
  # :range => 1..100,
115
118
  # :upcase => 'FINAL VALUE'
116
119
  # }
117
-
118
- === Declarations
119
-
120
- Configurations are added to classes via declarations. Declarations are a lot
121
- like specifying an attribute reader, writer, and the initialization code.
122
-
123
- class ConfigClass
124
- include Configurable
125
-
126
- config :key, 'value' do |input|
127
- input.upcase
128
- end
129
-
130
- def initialize
131
- initialize_config
132
- end
133
- end
134
-
135
- Is basically the same as:
136
-
137
- class RegularClass
138
- attr_reader :key
139
120
 
140
- def key=(input)
141
- @key = input.upcase
142
- end
143
-
144
- def initialize
145
- self.key = 'value'
146
- end
147
- end
148
-
149
- As far as the reader/writer goes, the analogy is quite good. The writer
150
- method is defined so it sets the instance variable using the return of the
151
- block. To literally define the writer with the block, use config_attr.
152
-
153
- class ConfigAttrClass
154
- include Configurable
155
-
156
- config_attr :key, 'value' do |input|
157
- @key = input.upcase
158
- end
159
- end
160
-
161
- Literally defines methods:
162
-
163
- class RegularClass
164
- attr_reader :key
165
-
166
- def key=(input)
167
- @key = input.upcase
168
- end
169
- end
170
-
171
- === Validation
172
-
173
- When configurations are parsed from the command line, the config writers will
174
- inevitably receive a string (even though the code may want a different object).
175
- The {Validation}[link:classes/Configurable/Validation.html] module provides
176
- standard blocks for validating and transforming string inputs and is accessible
177
- in classes via the <tt>c</tt> method (ex: <tt>c.integer</tt> or
178
- <tt>c.regexp</tt>). These blocks (generally) load string inputs as YAML and
179
- validate that the result is the correct class; non-string inputs are simply
180
- validated.
181
-
182
- class ValidatingClass
183
- include Configurable
184
-
185
- config :int, 1, &c.integer # assures the input is an integer
186
- config :int_or_nil, 1, &c.integer_or_nil # integer or nil only
187
- config :array, [], &c.array # you get the idea
188
- end
189
-
190
- vc = ValidatingClass.new
191
-
192
- vc.array = [:a, :b, :c]
193
- vc.array # => [:a, :b, :c]
194
-
195
- vc.array = "[1, 2, 3]"
196
- vc.array # => [1, 2, 3]
197
-
198
- vc.array = "string" # !> ValidationError
199
-
200
- Validation blocks sometimes imply metadata. For instance <tt>c.flag</tt> causes
201
- the config to appear as a flag on the command line. Metadata can be manually
202
- specified in the options:
203
-
204
- class ManualMetadata
205
- include Configurable
206
-
207
- config :key, 'default', :type => :flag do
208
- # this block is only called if --key
209
- # is specified, and will not take a
210
- # value
211
- end
212
- end
213
-
214
- === Documentation
215
-
216
- Documentation on the command line is pulled from the code directly using
217
- Lazydoc[http://tap.rubyforge.org/lazydoc/]. Documentation is a kind of
218
- metadata for configurations, and may be specified manually as an option:
219
-
220
- class ManualDocumentation
221
- include Configurable
222
- config :key, 'default', :desc => 'this is the command line description'
223
- end
121
+ See the Configurable module for more details.
224
122
 
225
123
  == Installation
226
124
 
@@ -230,7 +128,7 @@ Configurable is available as a gem on RubyForge[http://rubyforge.org/projects/ta
230
128
 
231
129
  == Info
232
130
 
233
- Copyright (c) 2008, Regents of the University of Colorado.
131
+ Copyright (c) 2008-2009, Regents of the University of Colorado.
234
132
  Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com], {Biomolecular Structure Program}[http://biomol.uchsc.edu/], {Hansen Lab}[http://hsc-proteomics.uchsc.edu/hansenlab/]
235
133
  Support:: CU Denver School of Medicine Deans Academic Enrichment Fund
236
134
  Licence:: {MIT-Style}[link:files/MIT-LICENSE.html]
data/lib/cdoc.rb ADDED
@@ -0,0 +1,413 @@
1
+ # RDoc creates a namespace conflict with IRB within 'rdoc/parsers/parse_rb'
2
+ # In that file, RubyToken and RubyLex get defined in the Object namespace,
3
+ # which will conflict with prior definitions from, for instance, IRB.
4
+ #
5
+ # This code redefines the RDoc RubyToken and RubyLex within the RDoc
6
+ # namespace. RDoc is not affected because all includes and uses of
7
+ # RubyToken and RubyLex are set when RDoc is loaded. The single exception
8
+ # I know of are several calls to class methods of RubyLex (ex RubyLex.debug?).
9
+ # These calls will be routed to the existing RubyLex.
10
+ #
11
+ # Uses of the existing RubyToken and RubyLex (as by irb) should be
12
+ # unaffected as the constants are reset after RDoc loads.
13
+ #
14
+ if Object.const_defined?(:RubyToken) || Object.const_defined?(:RubyLex)
15
+ class Object # :nodoc:
16
+ old_ruby_token = const_defined?(:RubyToken) ? remove_const(:RubyToken) : nil
17
+ old_ruby_lex = const_defined?(:RubyLex) ? remove_const(:RubyLex) : nil
18
+
19
+ require 'rdoc/rdoc'
20
+
21
+ # if by chance rdoc has ALREADY been loaded then requiring
22
+ # rdoc will not reset RubyToken and RubyLex... in this case
23
+ # the old constants are what you want.
24
+ new_ruby_token = const_defined?(:RubyToken) ? remove_const(:RubyToken) : old_ruby_token
25
+ new_ruby_lex = const_defined?(:RubyLex) ? remove_const(:RubyLex) : old_ruby_lex
26
+
27
+ RDoc.const_set(:RubyToken, new_ruby_token)
28
+ RDoc.const_set(:RubyLex, new_ruby_lex)
29
+
30
+ const_set(:RubyToken, old_ruby_token) unless old_ruby_token == nil
31
+ const_set(:RubyLex, old_ruby_lex) unless old_ruby_lex == nil
32
+ end
33
+ else
34
+ require 'rdoc/rdoc'
35
+
36
+ if Object.const_defined?(:RubyToken) && !RDoc.const_defined?(:RubyToken)
37
+ class Object # :nodoc:
38
+ RDoc.const_set(:RubyToken, remove_const(:RubyToken))
39
+ end
40
+ end
41
+
42
+ if Object.const_defined?(:RubyLex) && !RDoc.const_defined?(:RubyLex)
43
+ class Object # :nodoc:
44
+ RDoc.const_set(:RubyLex, remove_const(:RubyLex))
45
+ RDoc::RubyLex.const_set(:RubyLex, RDoc::RubyLex)
46
+ end
47
+ end
48
+ end
49
+
50
+ unless Object.const_defined?(:TokenStream)
51
+ TokenStream = RDoc::TokenStream
52
+ Options = RDoc::Options
53
+ end
54
+
55
+ # CDoc hooks into and extends RDoc to make Configurable documentation available
56
+ # as attributes. CDoc provides an extension to the standard RDoc HTMLGenerator
57
+ # and template.
58
+ #
59
+ # === Usage
60
+ # To generate task documentation with configuration information, CDoc must be
61
+ # loaded and the appropriate flags passed to rdoc . Essentially what you want
62
+ # is:
63
+ #
64
+ # % rdoc --fmt cdoc --template cdoc/cdoc_html_template [file_names....]
65
+ #
66
+ # Unfortunately, there is no way to load or require a file into the rdoc
67
+ # utility directly; the above code causes an 'Invalid output formatter' error.
68
+ # However, CDoc is easy to utilize from a Rake::RDocTask:
69
+ #
70
+ # require 'rake'
71
+ # require 'rake/rdoctask'
72
+ #
73
+ # desc 'Generate documentation.'
74
+ # Rake::RDocTask.new(:rdoc) do |rdoc|
75
+ # require 'cdoc'
76
+ # rdoc.template = 'cdoc/cdoc_html_template'
77
+ # rdoc.options << '--fmt' << 'cdoc'
78
+ #
79
+ # # specify whatever else you need
80
+ # # rdoc.rdoc_files.include(...)
81
+ # end
82
+ #
83
+ # Now execute the rake task like:
84
+ #
85
+ # % rake rdoc
86
+ #
87
+ # === Implementation
88
+ #
89
+ # RDoc is a beast to utilize in a non-standard way. One way to make RDoc parse
90
+ # unexpected flags like 'config' or 'config_attr' is to use the '--accessor'
91
+ # option (see 'rdoc --help' or the RDoc documentation for more details).
92
+ #
93
+ # CDoc hooks into the '--accessor' parsing process to pull out configuration
94
+ # attributes and format them into their own Configuration section on an RDoc
95
+ # html page. When 'cdoc' is specified as an rdoc option, CDoc in effect sets
96
+ # accessor flags for all the standard Task configuration methods, and then
97
+ # extends the RDoc::RubyParser handle these specially.
98
+ #
99
+ # If cdoc is not specified as the rdoc format, CDoc does not affect the RDoc
100
+ # output. Similarly, the configuration attributes will not appear in the
101
+ # output unless you specify a template that utilizes them.
102
+ #
103
+ # ==== Namespace conflicts
104
+ #
105
+ # RDoc creates a namespace conflict with other libraries that define RubyToken
106
+ # and RubyLex in the Object namespace (the prime example being IRB). CDoc checks
107
+ # for such a conflict and redfines the RDoc versions of RubyToken and RubyLex
108
+ # within the RDoc namespace. Essentially:
109
+ #
110
+ # original constant redefined constant
111
+ # RubyToken RDoc::RubyToken
112
+ # RubyLex RDoc::RubyLex
113
+ #
114
+ # The redefinition should not affect the existing (non RDoc) RubyToken and
115
+ # RubyLex constants, but if you directly use the RDoc versions after loading
116
+ # CDoc, you should be aware that they must be accessed through the new
117
+ # constants. Unfortunatley the trick is not seamless. The RDoc RubyLex makes
118
+ # a few calls to the RubyLex class method 'debug?'... these will be issued to
119
+ # the existing (non RDoc) RubyLex method and not the redefined
120
+ # RDoc::RubyLex.debug?
121
+ #
122
+ # In addition, because of the RubyLex calls, the RDoc::RubyLex cannot be fully
123
+ # hidden when CDoc is loaded before the conflicting RubyLex; you cannot load
124
+ # CDoc before loading IRB without raising warnings.
125
+ #
126
+ # Luckily all these troubles can be avoided very easily by not loading CDoc or
127
+ # RDoc when you're in irb. On the plus side, going against what I just said,
128
+ # you can now access/use RDoc within irb by requiring <tt>'cdoc'</tt>.
129
+ #
130
+ #--
131
+ # Note that tap-0.10.0 heavily refactored CDoc functionality out of the old CDoc
132
+ # and into Lazydoc, and changed the declaration syntax for configurations. These
133
+ # changes also affected the implementation of CDoc. Mostly the changes are hacks
134
+ # to get the old system to work in the new system... as hacky as the old CDoc was,
135
+ # now this CDoc is hacky AND may have cruft. Until it breaks completely, I leave
136
+ # it as is... ugly and hard to fathom.
137
+ #
138
+ module CDoc
139
+
140
+ # Encasulates information about the configuration. Designed to be utilized
141
+ # by the CDocHTMLGenerator as similarly as possible to standard attributes.
142
+ class ConfigAttr < RDoc::Attr # :nodoc:
143
+ # Contains the actual declaration for the config attribute. ex: "c [:key, 'value'] # comment"
144
+ attr_accessor :config_declaration, :default
145
+
146
+ def initialize(*args)
147
+ @comment = nil # suppress a warning in Ruby 1.9
148
+ super
149
+ end
150
+
151
+ alias original_comment comment
152
+
153
+ def desc
154
+ case text.to_s
155
+ when /^#--(.*)/ then $1.strip
156
+ when /^#(.*)/ then $1.strip
157
+ else
158
+ nil
159
+ end
160
+ end
161
+
162
+ # The description for the config. Comment is formed from the standard
163
+ # attribute comment and the text following the attribute, which is slightly
164
+ # different than normal:
165
+ #
166
+ # # standard comment
167
+ # attr_accessor :attribute
168
+ #
169
+ # # standard comment
170
+ # config_accessor :config # ...added to standard comment
171
+ #
172
+ # c [:key, 'value'] # hence you can comment inline like this.
173
+ #
174
+ # The comments for each of these will be:
175
+ # attribute:: standard comment
176
+ # config:: standard comment ...added to standard comment
177
+ # key:: hence you can comment inline like this.
178
+ #
179
+ def comment(add_default=true)
180
+ # this would include the trailing comment...
181
+ # text_comment = text.to_s.sub(/^#--.*/m, '')
182
+ #original_comment.to_s + text_comment + (default && add_default ? " (#{default})" : "")
183
+ comment = original_comment.to_s.strip
184
+ comment = desc.to_s if comment.empty?
185
+ comment + (default && add_default ? " (<tt>#{default}</tt>)" : "")
186
+ end
187
+ end
188
+
189
+ module CodeObjectAccess # :nodoc:
190
+ def comment_sections(section_regexp=//, normalize_comments=false)
191
+ res = {}
192
+
193
+ section = nil
194
+ lines = []
195
+ comment_lines = comment.split(/\r?\n/)
196
+ comment_lines << nil
197
+ comment_lines.each do |line|
198
+ case line
199
+ when nil, /^\s*#\s*=+(.*)/
200
+ next_section = (line == nil ? nil : $1.to_s.strip)
201
+
202
+ if section =~ section_regexp
203
+ lines << "" unless normalize_comments
204
+ res[section] = lines.join("\n") unless section == nil
205
+ end
206
+
207
+ section = next_section
208
+ lines = []
209
+ else
210
+ if normalize_comments
211
+ line =~ /^\s*#\s?(.*)/
212
+ line = $1.to_s
213
+ end
214
+
215
+ lines << line
216
+ end
217
+ end
218
+
219
+ res
220
+ end
221
+ end
222
+
223
+ module ClassModuleAccess # :nodoc:
224
+ def find_class_or_module_named(name)
225
+ return self if full_name == name
226
+ (@classes.values + @modules.values).each do |c|
227
+ res = c.find_class_or_module_named(name)
228
+ return res if res
229
+ end
230
+ nil
231
+ end
232
+
233
+ def configurations
234
+ @attributes.select do |attribute|
235
+ attribute.kind_of?(CDoc::ConfigAttr)
236
+ end
237
+ end
238
+
239
+ def find_configuration_named(name)
240
+ @attributes.each do |attribute|
241
+ next unless attribute.kind_of?(CDoc::ConfigAttr)
242
+ return attribute if attribute.name == name
243
+ end
244
+ nil
245
+ end
246
+ end
247
+
248
+ # Overrides the new method automatically extend the new object with
249
+ # ConfigParser. Intended to be used like:
250
+ # RDoc::RubyParser.extend InitializeConfigParser
251
+ module InitializeConfigParser # :nodoc:
252
+ def new(*args)
253
+ parser = super
254
+ parser.extend ConfigParser
255
+ #parser.config_mode = 'config_accessor'
256
+ parser
257
+ end
258
+ end
259
+
260
+ # Provides methods extending an RDoc::RubyParser such that the parser will produce
261
+ # CDoc::ConfigAttr instances in the place of RDoc::Attr instances during attribute
262
+ # parsing.
263
+ module ConfigParser # :nodoc:
264
+ include RDoc::RubyToken
265
+ include TokenStream
266
+
267
+ CONFIG_ACCESSORS = ['config', 'config_attr']
268
+
269
+ # Gets tokens until the next TkNL
270
+ def get_tk_to_nl
271
+ tokens = []
272
+ while !(tk = get_tk).kind_of?(TkNL)
273
+ tokens.push tk
274
+ end
275
+ unget_tk(tk)
276
+ tokens
277
+ end
278
+
279
+ # Works like the original parse_attr_accessor, except that the arg
280
+ # name is parsed from the config syntax and added attribute will
281
+ # be a CDoc::ConfigAttr. For example:
282
+ #
283
+ # class ConfigClass
284
+ # include Configurable
285
+ # config :key, 'value' # comment
286
+ # end
287
+ #
288
+ # produces an attribute named :key in the current config_rw mode.
289
+ #
290
+ # (see 'rdoc/parsers/parse_rb' line 2509)
291
+ def parse_config(context, single, tk, comment)
292
+ tks = get_tk_to_nl
293
+
294
+ key_tk = nil
295
+ value_tk = nil
296
+
297
+ tks.each do |token|
298
+ next if token.kind_of?(TkSPACE)
299
+
300
+ if key_tk == nil
301
+ case token
302
+ when TkSYMBOL then key_tk = token
303
+ when TkLPAREN then next
304
+ else break
305
+ end
306
+ else
307
+ case token
308
+ when TkCOMMA then value_tk = token
309
+ else
310
+ value_tk = token if value_tk.kind_of?(TkCOMMA)
311
+ break
312
+ end
313
+ end
314
+ end
315
+
316
+ text = ""
317
+ if tks.last.kind_of?(TkCOMMENT)
318
+ text = tks.last.text.chomp("\n").chomp("\r")
319
+ unget_tk(tks.last)
320
+
321
+ # If nodoc is given, don't document
322
+
323
+ tmp = RDoc::CodeObject.new
324
+ read_documentation_modifiers(tmp, RDoc::ATTR_MODIFIERS)
325
+ text = nil unless tmp.document_self
326
+ end
327
+
328
+ tks.reverse_each {|token| unget_tk(token) }
329
+ return if key_tk == nil || text == nil
330
+
331
+ arg = key_tk.text[1..-1]
332
+ default = nil
333
+ if value_tk
334
+ if text =~ /(.*):no_default:(.*)/
335
+ text = $1 + $2
336
+ else
337
+ default = value_tk.text
338
+ end
339
+ end
340
+ att = CDoc::ConfigAttr.new(text, arg, "RW", comment)
341
+ att.config_declaration = get_tkread
342
+ att.default = default
343
+
344
+ context.add_attribute(att)
345
+ end
346
+
347
+ # Overrides the standard parse_attr_accessor method to hook in parsing
348
+ # of the config accessors. If the input token is not named as one of the
349
+ # CONFIG_ACCESSORS, it will be processed normally.
350
+ def parse_attr_accessor(context, single, tk, comment)
351
+ case tk.name
352
+ when 'config', 'config_attr'
353
+ parse_config(context, single, tk, comment)
354
+ else
355
+ super
356
+ end
357
+ end
358
+ end
359
+ end
360
+
361
+ # Register the CDoc generator (in case you want to actually use it).
362
+ # method echos RDoc generator registration (see 'rdoc/rdoc' line 76)
363
+ Generator = Struct.new(:file_name, :class_name, :key)
364
+ RDoc::RDoc::GENERATORS['cdoc'] = Generator.new(
365
+ "cdoc/cdoc_html_generator.rb",
366
+ "CDocHTMLGenerator".intern,
367
+ "cdoc")
368
+
369
+ # Add the extended accessors to context classes.
370
+ module RDoc # :nodoc:
371
+ class CodeObject # :nodoc:
372
+ include CDoc::CodeObjectAccess
373
+ end
374
+
375
+ class ClassModule # :nodoc:
376
+ include CDoc::ClassModuleAccess
377
+ end
378
+ end
379
+
380
+ # Override methods in Options to in effect incorporate the accessor
381
+ # flags for CDoc parsing. (see 'rdoc/options') Raise an error if an
382
+ # accessor flag has already been specified.
383
+ class Options # :nodoc:
384
+ alias cdoc_original_parse parse
385
+
386
+ def parse(argv, generators)
387
+ cdoc_original_parse(argv, generators)
388
+ return unless @generator_name == 'cdoc'
389
+
390
+ accessors = CDoc::ConfigParser::CONFIG_ACCESSORS
391
+
392
+ # check the config_accessor_flags for accessor conflicts
393
+ extra_accessor_flags.each_pair do |accessor, flag|
394
+ if accessors.include?(accessor)
395
+ raise OptionList.error("cdoc format already handles the accessor '#{accessor}'")
396
+ end
397
+ end
398
+
399
+ # extra_accessors will be nil if no extra accessors were
400
+ # specifed, otherwise it'll be a regexp like /^(...)$/
401
+ # the string subset assumes
402
+ # regexp.to_s # => /(?-mix:^(...)$)/
403
+ @extra_accessors ||= /^()$/
404
+ current_accessors_str = @extra_accessors.to_s[9..-4]
405
+
406
+ # echos the Regexp production code in rdoc/options.rb
407
+ # (see the parse method, line 501)
408
+ re = '^(' + current_accessors_str + accessors.map{|a| Regexp.quote(a)}.join('|') + ')$'
409
+ @extra_accessors = Regexp.new(re)
410
+
411
+ RDoc::RubyParser.extend CDoc::InitializeConfigParser
412
+ end
413
+ end