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 +9 -0
- data/MIT-LICENSE +1 -1
- data/README +40 -142
- data/lib/cdoc.rb +413 -0
- data/lib/cdoc/cdoc_html_generator.rb +38 -0
- data/lib/cdoc/cdoc_html_template.rb +42 -0
- data/lib/config_parser.rb +302 -52
- data/lib/config_parser/option.rb +70 -21
- data/lib/config_parser/switch.rb +25 -10
- data/lib/config_parser/utils.rb +41 -27
- data/lib/configurable.rb +64 -40
- data/lib/configurable/class_methods.rb +245 -100
- data/lib/configurable/delegate.rb +18 -2
- data/lib/configurable/delegate_hash.rb +112 -69
- data/lib/configurable/indifferent_access.rb +21 -8
- data/lib/configurable/utils.rb +193 -0
- data/lib/configurable/validation.rb +112 -112
- metadata +16 -15
data/History
ADDED
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.
|
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
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
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
|
-
|
42
|
-
|
43
|
+
A ConfigParser can parse configurations from command line arguments, and turn
|
44
|
+
them into a documented help string:
|
43
45
|
|
44
|
-
parser =
|
45
|
-
parser.
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|