lazydoc 0.9.0 → 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History CHANGED
@@ -1,3 +1,20 @@
1
+ == 1.0.0 / 2009-12-05
2
+
3
+ * optimized regexps
4
+ * updated Lazydoc::Attributes to automatically set source_file
5
+ when inherited
6
+ * added document method to Lazydoc to look up document for
7
+ source_file without automatically creating the document (as
8
+ is the case with Lazydoc[])
9
+ * register_file now guesses a default constant name based on
10
+ the path relative to a matching LOAD_PATH
11
+ * updated Lazydoc::Attributes to allow manually registered
12
+ comments across multiple source files
13
+ * added inheritance for lazy attributes
14
+ * changed registered_methods to return an array of the methods
15
+ whose documentation will be registered (no longer a hash by
16
+ default)
17
+
1
18
  == 0.9.0 / 2009-05-25
2
19
 
3
20
  * lazy attributes can now be accessed without auto-resolve
@@ -1,19 +1,20 @@
1
1
  Copyright (c) 2008-2009, Regents of the University of Colorado.
2
+ Copyright (c) 2009, Simon Chiang.
2
3
 
3
- Permission is hereby granted, free of charge, to any person obtaining a copy of this
4
- software and associated documentation files (the "Software"), to deal in the Software
5
- without restriction, including without limitation the rights to use, copy, modify, merge,
6
- publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7
- to whom the Software is furnished to do so, subject to the following conditions:
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
8
10
 
9
- The above copyright notice and this permission notice shall be included in all copies or
10
- substantial portions of the Software.
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
11
13
 
12
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
16
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
19
- OTHER DEALINGS IN THE SOFTWARE.
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
data/README CHANGED
@@ -6,9 +6,8 @@ Tap[http://tap.rubyforge.org] framework.
6
6
 
7
7
  == Description
8
8
 
9
- Lazydoc allows you to define lazy attributes that act as markers for
10
- documentation in a source file. When you call the lazy attribute,
11
- Lazydoc pulls out the documentation:
9
+ Lazydoc allows you to define lazy attributes that access documentation in a
10
+ source file.
12
11
 
13
12
  # Sample::key <value>
14
13
  # This is the comment content. A content
@@ -35,8 +34,7 @@ Comments support wrapping, allowing for easy presentation:
35
34
  # ..............................
36
35
  # }
37
36
 
38
- In addition, Lazydoc provides helpers to register individual lines of code,
39
- particularly method definitions:
37
+ Lazydoc also provides helpers to register method documentation:
40
38
 
41
39
  class Helpers
42
40
  extend Lazydoc::Attributes
@@ -49,7 +47,7 @@ particularly method definitions:
49
47
  end
50
48
 
51
49
  # register_caller will register the line
52
- # that *calls* method_two
50
+ # that *calls* method_two (see below)
53
51
  def method_two
54
52
  Lazydoc.register_caller
55
53
  end
@@ -59,19 +57,18 @@ particularly method definitions:
59
57
  # registered by method_two
60
58
  Helpers.const_attrs[:method_two] = Helpers.new.method_two
61
59
 
62
- doc = Helpers.lazydoc
63
- doc.resolve
64
-
65
60
  one = Helpers.const_attrs[:method_one]
61
+ one.resolve
66
62
  one.method_name # => "method_one"
67
63
  one.arguments # => ["a", "b='str'", "&c"]
68
64
  one.to_s # => "method_one is registered whenever it gets defined"
69
65
 
70
66
  two = Helpers.const_attrs[:method_two]
67
+ two.resolve
71
68
  two.subject # => "Helpers.const_attrs[:method_two] = Helpers.new.method_two"
72
69
  two.to_s # => "*THIS* is the line that gets registered by method_two"
73
70
 
74
- Lazy accessors may be defined to map the registered lines as well:
71
+ Lazy accessors may be defined to access the registered lines more easily:
75
72
 
76
73
  class Helpers
77
74
  lazy_attr(:one, :method_one)
@@ -81,11 +78,10 @@ Lazy accessors may be defined to map the registered lines as well:
81
78
  Helpers.one.method_name # => "method_one"
82
79
  Helpers.two.subject # => "Helpers.const_attrs[:method_two] = Helpers.new.method_two"
83
80
 
84
- Check out these links for development, and bug tracking.
81
+ Check out these links for developments and bug tracking.
85
82
 
86
83
  * Website[http://tap.rubyforge.org/lazydoc]
87
84
  * Github[http://github.com/bahuvrihi/lazydoc/tree/master]
88
- * Lighthouse[http://bahuvrihi.lighthouseapp.com/projects/19948-lazydoc/tickets?q=all]
89
85
  * {Google Group}[http://groups.google.com/group/ruby-on-tap]
90
86
 
91
87
  == Usage
@@ -96,9 +92,9 @@ are represented by Comment objects.
96
92
 
97
93
  === Constant Attributes
98
94
 
99
- Constant attributes are like constants in Ruby, but with an extra 'key'
100
- that must consist of only lowercase letters and/or underscores. For
101
- example, these are constant attributes:
95
+ Constant attributes are defined in the documentation to look like constants,
96
+ but with an extra 'key' that must consist of only lowercase letters and/or
97
+ underscores. For example, these are constant attributes:
102
98
 
103
99
  # Const::Name::key
104
100
  # Const::Name::key_with_underscores
@@ -111,7 +107,7 @@ While these are not:
111
107
  # Const::Name::k@y
112
108
 
113
109
  Lazydoc parses a Lazydoc::Comment for each constant attribute by using the
114
- remainder of the line as a value (ie subject) and parsing down for content.
110
+ remainder of the line as a value (ie subject) and trailing lines as content.
115
111
  Parsing continues until a non-comment line, an end key, or a new attribute
116
112
  is reached; the comment is then stored by constant name and key.
117
113
 
@@ -139,8 +135,8 @@ is reached; the comment is then stored by constant name and key.
139
135
  # 'another' => ['value for another', 'comment for another parsed to an end key']}
140
136
  # }
141
137
 
142
- Constant attributes are only parsed from commented lines. To turn off
143
- attribute parsing for a section of documentation, use start/stop keys:
138
+ Constant attributes are only parsed from comment lines. To turn off attribute
139
+ parsing for a section of documentation, use start/stop keys:
144
140
 
145
141
  str = %Q{
146
142
  Const::Name::not_parsed
@@ -155,9 +151,9 @@ attribute parsing for a section of documentation, use start/stop keys:
155
151
  doc.resolve(str)
156
152
  doc.summarize {|comment| comment.value } # => {'Const::Name' => {'parsed' => 'value'}}
157
153
 
158
- To hide attributes from RDoc, make use of the RDoc <tt>:startdoc:</tt>
159
- document modifier like this (note that spaces are added to prevent RDoc
160
- from hiding the example):
154
+ To hide attributes from RDoc, make use of the RDoc <tt>:startdoc:</tt>
155
+ document modifier like this (note the modifiers have an extra space in them to
156
+ prevent RDoc from hiding the example):
161
157
 
162
158
  # :start doc::Const::Name::one hidden in RDoc
163
159
  # * This line is visible in RDoc.
@@ -171,16 +167,17 @@ from hiding the example):
171
167
  #
172
168
  # * This line is also visible in RDoc.
173
169
 
174
- As a side note, 'Const::Name::key' is not a reference to the 'key'
175
- constant (that would be invalid). In *very* idiomatic ruby
176
- 'Const::Name::key' is equivalent to the method call 'Const::Name.key'.
170
+ As a side note, the constant attribute syntax is designed to echo how the
171
+ Lazydoc::Attributes module makes comments accessible in code. In *very*
172
+ idiomatic Ruby 'Const::Name::key' is equivalent to the method call
173
+ 'Const::Name.key'.
177
174
 
178
175
  === Code Comments
179
176
 
180
- Code comments are lines registered for parsing if and when a Lazydoc gets
177
+ Code comments are lines registered for parsing if and when a Lazydoc gets
181
178
  resolved. Unlike constant attributes, the registered line is the comment
182
- subject (ie value) and contents are parsed up from it (basically mimicking
183
- the behavior of RDoc).
179
+ subject (ie value) and the content consists of the preceding documentation
180
+ (basically mimicking the behavior of RDoc).
184
181
 
185
182
  str = %Q{
186
183
  # comment lines for
@@ -211,6 +208,31 @@ of a Regexp, the first matching line is used; Procs receive an array of
211
208
  lines and should return the line number that should be used. See
212
209
  {Comment#parse_up}[link://classes/Lazydoc/Comment.html] for more details.
213
210
 
211
+ Manually registering lines for documentation can be quite cumbersome. The
212
+ Lazydoc::Attributes module provides helpers to register method documentation
213
+ on classes with method-like inheritance.
214
+
215
+ class A
216
+ extend Lazydoc::Attributes
217
+ lazy_attr(:one, :method_one)
218
+ lazy_register(:method_one)
219
+
220
+ # documentation for method one
221
+ def method_one; end
222
+ end
223
+
224
+ class B < A
225
+ end
226
+
227
+ class C < B
228
+ # overriding documentation for method one
229
+ def method_one; end
230
+ end
231
+
232
+ A::one.comment # => "documentation for method one"
233
+ B::one.comment # => "documentation for method one"
234
+ C::one.comment # => "overriding documentation for method one"
235
+
214
236
  == Installation
215
237
 
216
238
  Lazydoc is available as a gem on RubyForge[http://rubyforge.org/projects/tap]. Use:
@@ -219,7 +241,6 @@ Lazydoc is available as a gem on RubyForge[http://rubyforge.org/projects/tap].
219
241
 
220
242
  == Info
221
243
 
222
- Copyright (c) 2008-2009, Regents of the University of Colorado.
223
- Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com], {Biomolecular Structure Program}[http://biomol.uchsc.edu/], {Hansen Lab}[http://hsc-proteomics.uchsc.edu/hansenlab/]
224
- Support:: CU Denver School of Medicine Deans Academic Enrichment Fund
244
+ Copyright (c) 2009, Simon Chiang.
245
+ Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com]
225
246
  License:: {MIT-Style}[link:files/MIT-LICENSE.html]
@@ -1,3 +1,4 @@
1
+ require 'lazydoc/version'
1
2
  require 'lazydoc/document'
2
3
 
3
4
  module Lazydoc
@@ -8,22 +9,54 @@ module Lazydoc
8
9
  @registry ||= []
9
10
  end
10
11
 
11
- # Returns the Document in registry for the specified source file.
12
- # If no such Document exists, one will be created for it.
12
+ # Returns the document registered to the source file, or nil if no such
13
+ # document exists.
14
+ def document(source_file)
15
+ source_file = File.expand_path(source_file.to_s)
16
+ registry.find {|doc| doc.source_file == source_file }
17
+ end
18
+
19
+ # Returns the document registered to the source file. If no such document
20
+ # exists, one will be created for it.
13
21
  def [](source_file)
22
+ document(source_file) || register_file(source_file)
23
+ end
24
+
25
+ # Guesses the default constant name for the source file by camelizing the
26
+ # shortest relative path from a matching $LOAD_PATH to the source file.
27
+ # Returns nil if the source file is not relative to any load path.
28
+ #
29
+ # ==== Code Credit
30
+ #
31
+ # The camelize algorithm is taken from the ActiveSupport {Inflections}[http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/String/Inflections.html]
32
+ # module. See the {Tap::Env::StringExt}[http://tap.rubyforge.org/rdoc/classes/Tap/Env/StringExt.html]
33
+ # module (which uses the same) for a proper credit and license.
34
+ #
35
+ def guess_const_name(source_file)
14
36
  source_file = File.expand_path(source_file.to_s)
15
- registry.find {|doc| doc.source_file == source_file } || register_file(source_file)
37
+
38
+ load_paths = []
39
+ $LOAD_PATH.each do |load_path|
40
+ load_path = File.expand_path(load_path)
41
+ if source_file.rindex(load_path, 0) == 0
42
+ load_paths << load_path
43
+ end
44
+ end
45
+
46
+ return nil if load_paths.empty?
47
+
48
+ load_path = load_paths.sort_by {|load_path| load_path.length}.pop
49
+ extname = File.extname(source_file)
50
+ relative_path = source_file[(load_path.length + 1)..(-1 - extname.length)]
51
+ relative_path.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
16
52
  end
17
53
 
18
- # Generates a Document the source_file and default_const_name and adds it to
19
- # registry, or returns the document already registered to source_file. An
54
+ # Generates a document for the source_file and default_const_name and adds it to
55
+ # registry, or returns the document already registered to the source file. An
20
56
  # error is raised if you try to re-register a source_file with an inconsistent
21
57
  # default_const_name.
22
- def register_file(source_file, default_const_name=nil)
23
- source_file = File.expand_path(source_file.to_s)
24
- lazydoc = registry.find {|doc| doc.source_file == source_file }
25
-
26
- unless lazydoc
58
+ def register_file(source_file, default_const_name=guess_const_name(source_file))
59
+ unless lazydoc = document(source_file)
27
60
  lazydoc = Document.new(source_file)
28
61
  registry << lazydoc
29
62
  end
@@ -32,16 +65,15 @@ module Lazydoc
32
65
  lazydoc
33
66
  end
34
67
 
35
- # Registers the line number to the document for source_file and
36
- # returns the corresponding comment.
68
+ # Registers the line number to the document for source_file and returns the
69
+ # new comment.
37
70
  def register(source_file, line_number, comment_class=Comment)
38
71
  Lazydoc[source_file].register(line_number, comment_class)
39
72
  end
40
73
 
41
- # Registers the method at the specified index in the call stack to
42
- # the file where the method was called. Using the default index of
43
- # 1, register_caller registers the caller of the method where
44
- # register_caller is called (whew!). For instance:
74
+ # Registers the method to the line where it was called. To do so,
75
+ # register_caller examines the specified index in the call stack
76
+ # and extracts a file and line number. For instance:
45
77
  #
46
78
  # module Sample
47
79
  # module_function
@@ -59,7 +91,7 @@ module Lazydoc
59
91
  #
60
92
  def register_caller(comment_class=Comment, caller_index=1)
61
93
  caller[caller_index] =~ CALLER_REGEXP
62
- Lazydoc[$1].register($3.to_i - 1, comment_class)
94
+ Lazydoc[$1].register($2.to_i - 1, comment_class)
63
95
  end
64
96
 
65
97
  # Parses the usage for a file (ie the first comment in the file
@@ -1,7 +1,7 @@
1
1
  module Lazydoc
2
2
 
3
3
  # Attributes adds methods to declare class-level accessors for constant
4
- # attributes.
4
+ # attributes associated with the class.
5
5
  #
6
6
  # # ConstName::key value
7
7
  # class ConstName
@@ -9,132 +9,287 @@ module Lazydoc
9
9
  # lazy_attr :key
10
10
  # end
11
11
  #
12
- # ConstName.source_file # => __FILE__
13
- # ConstName::key.subject # => 'value'
12
+ # ConstName::key.subject # => 'value'
13
+ #
14
+ # Lazy attributes are inherited, but can be overridden.
15
+ #
16
+ # class SubclassA < ConstName; end
17
+ # SubclassA::key.subject # => 'value'
18
+ #
19
+ # # SubclassB::key overridden value
20
+ # class SubclassB < ConstName; end
21
+ # SubclassB::key.subject # => 'overridden value'
22
+ #
23
+ # You can use Attributes to register methods on modules, but currently the
24
+ # inheritance is a bit wonky; the accessors are methods on the extended
25
+ # class/module and so standard module inclusion will not pass them on.
26
+ # To work around you need to extend and redefine the accessors. Note,
27
+ # however, that methods do not need to be re-registered.
28
+ #
29
+ # module A
30
+ # extend Lazydoc::Attributes
31
+ # lazy_attr(:one, :method_one)
32
+ # lazy_register(:method_one)
33
+ #
34
+ # # documentation for method one
35
+ # def method_one; end
36
+ # end
14
37
  #
38
+ # class B
39
+ # include A
40
+ # extend Lazydoc::Attributes
41
+ # lazy_attr(:one, :method_one)
42
+ # end
43
+ #
44
+ # class C < B
45
+ # # overriding documentation for method one
46
+ # def method_one; end
47
+ # end
48
+ #
49
+ # A::one.comment # => "documentation for method one"
50
+ # B::one.comment # => "documentation for method one"
51
+ # C::one.comment # => "overriding documentation for method one"
52
+ #
15
53
  # ==== Keys and Register
16
54
  #
17
- # Note that constant attributes parsed from a source file are stored in
18
- # const_attrs, and will ALWAYS be keyed using a string (since the
19
- # 'ConstName::key' syntax specifies a string key).
55
+ # Constant attributes parsed from a source file will ALWAYS be stored in
56
+ # const_attrs using a string (since the 'ConstName::key' syntax always
57
+ # results in a string key). A lazy_attr is basically shorthand for either
58
+ # of these statements:
59
+ #
60
+ # ConstName.const_attrs['key'].subject # => 'value'
61
+ # Lazydoc::Document['ConstName']['key'].subject # => 'value'
62
+ #
63
+ # By default a lazy_attr maps to the constant attribute with the same name
64
+ # as the accessor, but this can be overridden by specifying the string key
65
+ # for another attribute.
66
+ #
67
+ # class ConstName
68
+ # lazy_attr :alt, 'key'
69
+ # end
20
70
  #
21
- # ConstName.const_attrs['key'] # => ConstName::key
71
+ # ConstName::alt.subject # => 'value'
72
+ # ConstName.const_attrs['alt'] # => nil
22
73
  #
23
- # 'Constant Attributes' specified by non-string keys are sometimes used to
24
- # tie comments to a constant that will NOT be resolved from the constant
25
- # attribute syntax. For instance you could register a method like this:
74
+ # Comments specified by non-string keys may also be stored in const_attrs;
75
+ # these will not conflict with constant attributes parsed from a source
76
+ # file. For instance you could manually register a comment to a symbol key
77
+ # using lazy_register:
26
78
  #
27
79
  # class Sample
28
80
  # extend Lazydoc::Attributes
29
81
  #
30
- # const_attrs[:method_one] = register___
82
+ # lazy_register(:method_one)
83
+ #
31
84
  # # this is the method one comment
32
85
  # def method_one
33
86
  # end
34
87
  # end
35
88
  #
36
- # Sample.lazydoc.resolve
37
- # Sample.const_attrs[:method_one].comment # => "this is the method one comment"
89
+ # Sample.const_attrs[:method_one].comment # => "this is the method one comment"
38
90
  #
39
- # For easier access, you could define a lazy_attr to access the registered
40
- # comment. And in the simplest case, you pair a lazy_register with a
41
- # lazy_attr.
91
+ # Manually-registered comments may then be paired with a lazy_attr. As
92
+ # before the key for the comment is provided in the definition.
42
93
  #
43
94
  # class Paired
44
95
  # extend Lazydoc::Attributes
45
96
  #
46
97
  # lazy_attr(:one, :method_one)
47
- # lazy_attr(:two, :method_two)
48
- # lazy_register(:method_two)
98
+ # lazy_register(:method_one)
49
99
  #
50
- # const_attrs[:method_one] = register___
51
- # # this is the manually-registered method one comment
100
+ # # this is the method one comment
52
101
  # def method_one
53
102
  # end
54
- #
55
- # # this is the lazyily-registered method two comment
56
- # def method_two
57
- # end
58
103
  # end
59
104
  #
60
- # Paired.lazydoc.resolve
61
- # Paired.one.comment # => "this is the manually-registered method one comment"
62
- # Paired.two.comment # => "this is the lazyily-registered method two comment"
105
+ # Paired::one.comment # => "this is the method one comment"
106
+ #
107
+ # ==== Troubleshooting
63
108
  #
109
+ # Under most circumstances Attributes will register all the necessary files
110
+ # to make constant attributes available. These include:
111
+ #
112
+ # * the file where the class is extended
113
+ # * the file where a subclass inherits from an extended class
114
+ # * files that declare a lazy_attr
115
+ #
116
+ # Be sure to call register_lazydoc in files that are not covered by one of
117
+ # these cases but nonetheless contain constant attributes that should be
118
+ # available to a lazy_attr.
64
119
  module Attributes
65
-
66
- # The source file for the extended class. By default source_file
67
- # is set to the file where Attributes extends the class (if you
68
- # include Attributes, you must set source_file manually).
69
- attr_accessor :source_file
70
120
 
71
- def self.extended(base) # :nodoc:
121
+ # Sets source_file as the file where Attributes first extends the class.
122
+ def self.extended(base)
72
123
  caller[1] =~ CALLER_REGEXP
73
- base.source_file ||= $1
74
- end
75
-
76
- # Inherits registered_methods from parent to child.
77
- def inherited(child)
78
- child.registered_methods.merge!(registered_methods)
124
+ unless base.instance_variable_defined?(:@lazydocs)
125
+ base.instance_variable_set(:@lazydocs, [Lazydoc[$1]])
126
+ end
127
+
128
+ unless base.instance_variable_defined?(:@lazy_registry)
129
+ base.instance_variable_set(:@lazy_registry, {})
130
+ end
131
+
79
132
  super
80
133
  end
81
134
 
82
- # Lazily registers the added method if marked for lazy registration.
83
- def method_added(sym)
84
- if args = registered_methods[sym]
85
- const_attrs[sym] ||= Lazydoc.register_caller(*args)
135
+ # Returns the documents registered to the extending class.
136
+ #
137
+ # By default lazydocs contains a Document for the file where Attributes
138
+ # extends the class, or where a subclass first inherits from an extended
139
+ # class (if you include Attributes, you must set lazydocs manually).
140
+ #
141
+ # Additional documents may be added by calling register_lazydoc.
142
+ attr_reader :lazydocs
143
+
144
+ # Returns an array of the methods whose documentation will be automatically
145
+ # registered by Attributes. Set as_registry to true to return a hash of
146
+ # of (method_name, [comment_class, caller_index]) pairs where the
147
+ # registration arguments are the hash values.
148
+ def registered_methods(as_registry=false)
149
+ methods = {}
150
+ ancestors.reverse.each do |ancestor|
151
+ if ancestor.kind_of?(Attributes)
152
+ methods.merge!(ancestor.lazy_registry)
153
+ end
86
154
  end
87
155
 
88
- super
156
+ as_registry ? methods : methods.keys
89
157
  end
90
158
 
91
159
  # Returns the constant attributes resolved for the extended class.
92
160
  def const_attrs
93
161
  Document[to_s]
94
162
  end
95
-
96
- # Returns the Document for source_file
97
- def lazydoc
98
- Lazydoc[source_file]
99
- end
163
+
164
+ protected
100
165
 
101
166
  # A hash of (method_name, [comment_class, caller_index]) pairs indicating
102
- # methods to lazily register, and the inputs to Lazydoc.register_caller
103
- # used to register the method.
104
- def registered_methods
105
- @registered_methods ||= {}
167
+ # methods to lazily register, and the inputs used to register the method.
168
+ #
169
+ # The lazy_registry only contains methods lazily registered within the
170
+ # current class or module. To return methods registered throughout the
171
+ # inheritance hierarchy, use registered_methods(true)
172
+ attr_reader :lazy_registry
173
+
174
+ # Registers the calling file into lazydocs. Registration occurs by
175
+ # examining the call stack at the specified index.
176
+ def register_lazydoc(caller_index=0)
177
+ caller[caller_index] =~ CALLER_REGEXP
178
+ lazydocs << Lazydoc[File.expand_path($1)]
179
+ lazydocs.uniq!
180
+ self
106
181
  end
107
182
 
108
183
  # Creates a method that reads and resolves the constant attribute specified
109
- # by key, which should normally be a string (see above for more details).
110
- # If writable is true, a corresponding writer is also created.
184
+ # by key. The method has a signature like:
185
+ #
186
+ # def method(resolve=true)
187
+ # end
188
+ #
189
+ # To return the constant attribute without resolving, call the method with
190
+ # resolve == false. If writable is true, a corresponding writer is also
191
+ # created.
111
192
  def lazy_attr(symbol, key=symbol.to_s, writable=true)
112
- key = case key
113
- when String, Symbol, Numeric, true, false, nil then key.inspect
114
- else "YAML.load(\'#{YAML.dump(key)}\')"
193
+ unless key.kind_of?(String) || key.kind_of?(Symbol)
194
+ raise "invalid lazy_attr key: #{key.inspect} (#{key.class})"
115
195
  end
116
196
 
117
- instance_eval %Q{
118
- def #{symbol}(resolve=true)
119
- comment = const_attrs[#{key}] ||= Subject.new(nil, lazydoc)
120
- resolve && comment.kind_of?(Comment) ? comment.resolve : comment
121
- end}
122
-
123
- instance_eval(%Q{
124
- def #{symbol}=(comment)
125
- const_attrs[#{key}] = comment
126
- end}) if writable
197
+ key = key.inspect
198
+ instance_eval %Q{def #{symbol}(resolve=true); get_const_attr(#{key}, resolve); end}
199
+ instance_eval(%Q{def #{symbol}=(comment); const_attrs[#{key}] = comment; end}) if writable
200
+
201
+ register_lazydoc(1)
127
202
  end
128
203
 
129
204
  # Marks the method for lazy registration. When the method is registered,
130
205
  # it will be stored in const_attrs by method_name.
131
206
  def lazy_register(method_name, comment_class=Method, caller_index=1)
132
- registered_methods[method_name.to_sym] = [comment_class, caller_index]
207
+ lazy_registry[method_name.to_sym] = [comment_class, caller_index]
133
208
  end
134
209
 
135
- # Registers the next comment (by default as a Method).
136
- def register___(comment_class=Method)
137
- lazydoc.register___(comment_class, 1)
210
+ # Manually registers the next comment into const_attrs. Note a lazy_attr
211
+ # will still need to be defined to access this comment as an attribute.
212
+ def register___(key, comment_class=Method)
213
+ caller[0] =~ CALLER_REGEXP
214
+ source_file = File.expand_path($1)
215
+ const_attrs[key] = Lazydoc[source_file].register___(comment_class, 1)
216
+ end
217
+
218
+ private
219
+
220
+ # Inherits lazy_registry from parent to child. Also registers the
221
+ # source_file for the child as the file where the inheritance first
222
+ # occurs.
223
+ def inherited(child)
224
+ unless child.instance_variable_defined?(:@lazydocs)
225
+ caller.each do |call|
226
+ next if call =~ /in `inherited'$/
227
+
228
+ call =~ CALLER_REGEXP
229
+ child.instance_variable_set(:@lazydocs, [Lazydoc[$1]])
230
+ break
231
+ end
232
+ end
233
+
234
+ unless child.instance_variable_defined?(:@lazy_registry)
235
+ child.instance_variable_set(:@lazy_registry, {})
236
+ end
237
+
238
+ super
239
+ end
240
+
241
+ # Helper to traverse the inheritance hierarchy. The logic of this method
242
+ # is described in the dsl pattern: http://gist.github.com/181961
243
+ def each_ancestor # :nodoc:
244
+ yield(self)
245
+
246
+ blank, *ancestors = self.ancestors
247
+ ancestors.each do |ancestor|
248
+ yield(ancestor) if ancestor.kind_of?(Attributes)
249
+ end
250
+
251
+ nil
252
+ end
253
+
254
+ # Lazily registers the added method if marked for lazy registration.
255
+ def method_added(sym)
256
+ current = nil
257
+ each_ancestor do |ancestor|
258
+ if ancestor.lazy_registry.has_key?(sym)
259
+ current = ancestor
260
+ break
261
+ end
262
+ end
263
+
264
+ if current
265
+ args = current.lazy_registry[sym]
266
+ const_attrs[sym] ||= Lazydoc.register_caller(*args)
267
+ end
268
+
269
+ super
270
+ end
271
+
272
+ # helper to traverse up the inheritance hierarchy looking for the first
273
+ # const_attr assigned to key. the lazydocs for each class will be
274
+ # resolved along the way, if specified.
275
+ def get_const_attr(key, resolve) # :nodoc:
276
+ each_ancestor do |ancestor|
277
+ const_attrs = ancestor.const_attrs
278
+
279
+ unless const_attrs.has_key?(key)
280
+ next unless resolve
281
+
282
+ ancestor.lazydocs.each {|doc| doc.resolve }
283
+ next unless const_attrs.has_key?(key)
284
+ end
285
+
286
+ const_attr = const_attrs[key]
287
+ if resolve && const_attr.kind_of?(Comment)
288
+ const_attr.resolve
289
+ end
290
+
291
+ return const_attr
292
+ end
138
293
  end
139
294
  end
140
295
  end
@@ -10,22 +10,22 @@ module Lazydoc
10
10
  # A regexp matching an attribute start or end. After a match:
11
11
  #
12
12
  # $1:: const_name
13
- # $3:: key
14
- # $4:: end flag
13
+ # $2:: key
14
+ # $3:: end flag
15
15
  #
16
- ATTRIBUTE_REGEXP = /([A-Z][A-z]*(::[A-Z][A-z]*)*)?::([a-z_]+)(-?)/
16
+ ATTRIBUTE_REGEXP = /([A-Z][A-z]*(?:::[A-Z][A-z]*)*)?::([a-z_]+)(-?)/
17
17
 
18
18
  # A regexp matching constants from the ATTRIBUTE_REGEXP leader
19
- CONSTANT_REGEXP = /#.*?([A-Z][A-z]*(::[A-Z][A-z]*)*)?$/
19
+ CONSTANT_REGEXP = /#.*?([A-Z][A-z]*(?:::[A-Z][A-z]*)*)?$/
20
20
 
21
21
  # A regexp matching a caller line, to extract the calling file
22
22
  # and line number. After a match:
23
23
  #
24
24
  # $1:: file
25
- # $3:: line number (as a string, obviously)
25
+ # $2:: line number (as a string, obviously)
26
26
  #
27
27
  # Note that line numbers in caller start at 1, not 0.
28
- CALLER_REGEXP = /^(([A-z]:)?[^:]+):(\d+)/
28
+ CALLER_REGEXP = /^((?:[A-z]:)?[^:]+):(\d+)/
29
29
 
30
30
  # A Document resolves constant attributes and code comments for a particular
31
31
  # source file. Documents may be assigned a default_const_name to be used
@@ -40,9 +40,8 @@ module Lazydoc
40
40
  # Document['Const::Name']['key'].value # => 'value a'
41
41
  # Document['Default']['key'].value # => 'value b'
42
42
  #
43
- # As shown in the example, constant attibutes for all documents are cached in
44
- # the class-level const_attrs hash and are normally consumed through Document
45
- # itself.
43
+ # As in the example, constant attibutes for all documents may be accessed
44
+ # from Document[].
46
45
  class Document
47
46
  class << self
48
47
 
@@ -136,6 +135,9 @@ module Lazydoc
136
135
  # Returns the attributes for the specified const_name. If an empty
137
136
  # const_name ('') is specified, and a default_const_name is set,
138
137
  # the default_const_name will be used instead.
138
+ #
139
+ # Note this method will return ALL attributes associated with const_name,
140
+ # not just attributes associated with self.
139
141
  def [](const_name)
140
142
  const_name = default_const_name if default_const_name && const_name == ''
141
143
  Document[const_name]
@@ -184,7 +186,7 @@ module Lazydoc
184
186
  def register___(comment_class=Comment, caller_index=0)
185
187
  caller[caller_index] =~ CALLER_REGEXP
186
188
  block = lambda do |scanner, lines|
187
- n = $3.to_i
189
+ n = $2.to_i
188
190
  n += 1 while lines[n] =~ /^\s*(#.*)?$/
189
191
  n
190
192
  end
@@ -216,7 +218,7 @@ module Lazydoc
216
218
  comment.parse_down(scanner, lines) do |line|
217
219
  if line =~ ATTRIBUTE_REGEXP
218
220
  # rewind to capture the next attribute unless an end is specified.
219
- scanner.unscan unless $4 == '-' && $3 == key && $1.to_s == const_name
221
+ scanner.unscan unless $3 == '-' && $2 == key && $1.to_s == const_name
220
222
  true
221
223
  else false
222
224
  end
@@ -0,0 +1,7 @@
1
+ module Lazydoc
2
+ MAJOR = 1
3
+ MINOR = 0
4
+ TINY = 0
5
+
6
+ VERSION="#{MAJOR}.#{MINOR}.#{TINY}"
7
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lazydoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: "1.0"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Chiang
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-25 00:00:00 -06:00
12
+ date: 2009-12-05 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -33,11 +33,14 @@ files:
33
33
  - lib/lazydoc/subject.rb
34
34
  - lib/lazydoc/trailer.rb
35
35
  - lib/lazydoc/utils.rb
36
+ - lib/lazydoc/version.rb
36
37
  - README
37
38
  - MIT-LICENSE
38
39
  - History
39
40
  has_rdoc: true
40
41
  homepage: http://tap.rubyforge.org/lazydoc
42
+ licenses: []
43
+
41
44
  post_install_message:
42
45
  rdoc_options:
43
46
  - --title
@@ -63,9 +66,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
66
  requirements: []
64
67
 
65
68
  rubyforge_project: tap
66
- rubygems_version: 1.3.1
69
+ rubygems_version: 1.3.5
67
70
  signing_key:
68
- specification_version: 2
71
+ specification_version: 3
69
72
  summary: Lazily pull documentation out of source files.
70
73
  test_files: []
71
74