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