configurable 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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