rote 0.3.2 → 0.3.2.2
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/README +2 -2
- data/Rakefile +35 -8
- data/doc/pages/guide/index.html +97 -30
- data/doc/pages/index.html +18 -14
- data/lib/rote.rb +2 -2
- data/lib/rote/app.rb +1 -1
- data/lib/rote/cache.rb +29 -16
- data/lib/rote/filters.rb +1 -1
- data/lib/rote/filters/base.rb +1 -1
- data/lib/rote/filters/rdoc.rb +1 -1
- data/lib/rote/filters/redcloth.rb +1 -1
- data/lib/rote/filters/syntax.rb +20 -6
- data/lib/rote/filters/tidy.rb +1 -1
- data/lib/rote/filters/toc.rb +3 -2
- data/lib/rote/format.rb +1 -1
- data/lib/rote/format/html.rb +1 -1
- data/lib/rote/page.rb +150 -44
- data/lib/rote/project/README +1 -1
- data/lib/rote/project/Rakefile +1 -1
- data/lib/rote/rotetasks.rb +16 -4
- data/test/layouts/nestme.txt +1 -1
- data/test/pages/nested/COMMON.rb +1 -0
- data/test/pages/nested/inner/COMMON.rb +11 -0
- data/test/pages/nested/inner/nested-override.txt +3 -0
- data/test/test_cache.rb +16 -7
- data/test/test_filters.rb +2 -2
- data/test/test_page.rb +65 -10
- metadata +7 -2
data/lib/rote/filters.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# (c)2005, 2006 Ross Bamford (and contributors)
|
4
4
|
#
|
5
5
|
# See 'rote.rb' or LICENSE for licence information.
|
6
|
-
# $Id: filters.rb
|
6
|
+
# $Id: filters.rb 156 2006-01-05 01:13:43 +0000 (Thu, 05 Jan 2006) roscopeco $
|
7
7
|
#++
|
8
8
|
|
9
9
|
# Everyone requires this, we need to get it loaded first.
|
data/lib/rote/filters/base.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# (c)2005 Ross Bamford (and contributors)
|
4
4
|
#
|
5
5
|
# See 'rote.rb' or LICENSE for licence information.
|
6
|
-
# $Id: base.rb
|
6
|
+
# $Id: base.rb 135 2005-12-12 15:01:07 +0000 (Mon, 12 Dec 2005) roscopeco $
|
7
7
|
#++
|
8
8
|
module Rote
|
9
9
|
module Filters
|
data/lib/rote/filters/rdoc.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# (c)2005 Ross Bamford (and contributors)
|
4
4
|
#
|
5
5
|
# See 'rote.rb' or LICENSE for licence information.
|
6
|
-
# $Id: rdoc.rb
|
6
|
+
# $Id: rdoc.rb 119 2005-12-11 02:18:37 +0000 (Sun, 11 Dec 2005) roscopeco $
|
7
7
|
#++
|
8
8
|
|
9
9
|
require 'rdoc/markup/simple_markup'
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# (c)2005, 2006 Ross Bamford (and contributors)
|
4
4
|
#
|
5
5
|
# See 'rote.rb' or LICENSE for licence information.
|
6
|
-
# $Id: redcloth.rb
|
6
|
+
# $Id: redcloth.rb 155 2006-01-05 00:50:32 +0000 (Thu, 05 Jan 2006) roscopeco $
|
7
7
|
#++
|
8
8
|
|
9
9
|
require 'redcloth'
|
data/lib/rote/filters/syntax.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# (c)2005 Ross Bamford (and contributors)
|
4
4
|
#
|
5
5
|
# See 'rote.rb' or LICENSE for licence information.
|
6
|
-
# $Id: syntax.rb
|
6
|
+
# $Id: syntax.rb 170 2006-01-11 23:38:51 +0000 (Wed, 11 Jan 2006) roscopeco $
|
7
7
|
#++
|
8
8
|
|
9
9
|
require 'syntax'
|
@@ -14,24 +14,38 @@ module Rote
|
|
14
14
|
module Filters
|
15
15
|
|
16
16
|
#####
|
17
|
-
## Page filter that supports syntax highlighting for Ruby code
|
18
|
-
## via the
|
19
|
-
##
|
17
|
+
## Page filter that supports syntax highlighting for Ruby code,
|
18
|
+
## XML and YAML via the (extensible) Syntax (http://syntax.rubyforge.org/)
|
19
|
+
## library. Code is expected to be enclosed by the +code+ macro:
|
20
20
|
##
|
21
21
|
## #:code#ruby#
|
22
22
|
## def amethod(arg)
|
23
23
|
## puts arg
|
24
24
|
## end
|
25
25
|
## #:code#
|
26
|
-
##
|
26
|
+
##
|
27
|
+
## Where the macro argument may be 'ruby', 'xml', 'yaml', or a
|
28
|
+
## custom identifier registered (with Syntax) for a custom highlighter.
|
29
|
+
##
|
30
|
+
## The macro output will resemble:
|
31
|
+
##
|
32
|
+
## <pre class='#{lang}'><code class=#{lang}'>
|
33
|
+
## ...
|
34
|
+
## </code></pre>
|
35
|
+
##
|
36
|
+
## Syntax uses <span> tags for highlighting, with CSS classes used to apply
|
37
|
+
## formatting. See http://syntax.rubyforge.org/chapter-2.html for a list
|
38
|
+
## of recognised classes.
|
39
|
+
##
|
27
40
|
class Syntax < MacroFilter
|
28
41
|
def initialize(macro_re = MACRO_RE)
|
29
42
|
super([],macro_re)
|
30
43
|
end
|
31
44
|
|
45
|
+
# Implementation of the +code+ macro.
|
32
46
|
def macro_code(lang,body,raw)
|
33
47
|
converter = ::Syntax::Convertors::HTML.for_syntax(lang)
|
34
|
-
"<pre class='#{lang}'><code>#{converter.convert(body,false)}</code></pre>"
|
48
|
+
"<pre class='#{lang}'><code class='#{lang}'>#{converter.convert(body,false)}</code></pre>"
|
35
49
|
end
|
36
50
|
end
|
37
51
|
end
|
data/lib/rote/filters/tidy.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# (c)2005 Ross Bamford (and contributors)
|
4
4
|
#
|
5
5
|
# See 'rote.rb' or LICENSE for licence information.
|
6
|
-
# $Id: tidy.rb
|
6
|
+
# $Id: tidy.rb 125 2005-12-12 02:27:41 +0000 (Mon, 12 Dec 2005) roscopeco $
|
7
7
|
#++
|
8
8
|
|
9
9
|
module Rote
|
data/lib/rote/filters/toc.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# (c)2005 Ross Bamford (and contributors)
|
4
4
|
#
|
5
5
|
# See 'rote.rb' or LICENSE for licence information.
|
6
|
-
# $Id: toc.rb
|
6
|
+
# $Id: toc.rb 170 2006-01-11 23:38:51 +0000 (Wed, 11 Jan 2006) roscopeco $
|
7
7
|
#++
|
8
8
|
|
9
9
|
module Rote
|
@@ -57,7 +57,8 @@ module Rote
|
|
57
57
|
# <%= links.join(" - ") %>
|
58
58
|
#
|
59
59
|
# *Note* that this isn't populated until after
|
60
|
-
# the filter is run.
|
60
|
+
# the filter is run. The @links array will usually
|
61
|
+
# be used in layout.
|
61
62
|
attr_reader :headings
|
62
63
|
alias :links :headings
|
63
64
|
|
data/lib/rote/format.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# (c)2005 Ross Bamford (and contributors)
|
4
4
|
#
|
5
5
|
# See 'rote.rb' or LICENSE for licence information.
|
6
|
-
# $Id: format.rb
|
6
|
+
# $Id: format.rb 120 2005-12-11 06:05:48 +0000 (Sun, 11 Dec 2005) roscopeco $
|
7
7
|
#++
|
8
8
|
|
9
9
|
Dir[File.join(File.dirname(__FILE__), 'format/*.rb')].each { |f| require f }
|
data/lib/rote/format/html.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# (c)2005 Ross Bamford (and contributors)
|
4
4
|
#
|
5
5
|
# See 'rote.rb' or LICENSE for licence information.
|
6
|
-
# $Id: html.rb
|
6
|
+
# $Id: html.rb 113 2005-12-09 01:28:21 +0000 (Fri, 09 Dec 2005) roscopeco $
|
7
7
|
#++
|
8
8
|
require 'erb'
|
9
9
|
|
data/lib/rote/page.rb
CHANGED
@@ -3,35 +3,115 @@
|
|
3
3
|
# (c)2005, 2006 Ross Bamford (and contributors)
|
4
4
|
#
|
5
5
|
# See 'rote.rb' or LICENSE for licence information.
|
6
|
-
# $Id: page.rb
|
6
|
+
# $Id: page.rb 170 2006-01-11 23:38:51 +0000 (Wed, 11 Jan 2006) roscopeco $
|
7
7
|
#++
|
8
8
|
|
9
9
|
require 'erb'
|
10
10
|
require 'rote/cache'
|
11
|
+
require 'rote/filters'
|
11
12
|
|
12
13
|
module Rote
|
13
14
|
STRIP_SLASHES = /^\/?(.*?)\/?$/
|
14
15
|
FILE_EXT = /\..*$/
|
15
16
|
|
16
17
|
#####
|
17
|
-
## A +Page+ object represents an individual
|
18
|
-
##
|
19
|
-
##
|
20
|
-
##
|
21
|
-
##
|
18
|
+
## A +Page+ object represents an individual page in the final
|
19
|
+
## documentation set, bringing together a source template,
|
20
|
+
## optional _page_ _code_ (in Ruby) obtained from
|
21
|
+
## various sources (see below), and an optional layout template
|
22
|
+
## (with it's own code) to produce rendered output as a +String+.
|
23
|
+
## Specifically, Page provides the following capabilities:
|
22
24
|
##
|
23
|
-
##
|
24
|
-
##
|
25
|
-
##
|
26
|
-
##
|
27
|
-
##
|
28
|
-
##
|
29
|
-
##
|
30
|
-
##
|
25
|
+
## * Read _template_ _files_ containing ERB code and render them
|
26
|
+
## to create output.
|
27
|
+
## * Locate and evaluate all _common_ _and_ _page_ _code_ in the binding
|
28
|
+
## of the Page instance itself.
|
29
|
+
## * Apply _layout_ to rendered content using multiple render
|
30
|
+
## passes.
|
31
|
+
## * Apply user-supplied _filters_ to the output to allow
|
32
|
+
## transformations and additional processing.
|
33
|
+
##
|
34
|
+
## In normal use the instantiation and initialization of Pages
|
35
|
+
## will be handled internally by Rote. From the user point of
|
36
|
+
## view most interaction with Rote from user code takes place
|
37
|
+
## via the instance methods of this class.
|
38
|
+
##
|
39
|
+
## == Template lookup and evaluation
|
40
|
+
##
|
41
|
+
## Each +Page+ instance is provided at instantiation with base paths
|
42
|
+
## from which it should resolve both template and layout files when
|
43
|
+
## required. Usually these paths are supplied by the Rake task
|
44
|
+
## configuration. The attributes that provide information on template
|
45
|
+
## and layout paths (e.g. +template_name+, +base_layout_name+, and
|
46
|
+
## so on) give those paths relative to the +base_path+ and +layout_path+
|
47
|
+
## as appropriate.
|
48
|
+
##
|
49
|
+
## === Common, page and layout code evaluation
|
50
|
+
##
|
51
|
+
## Code applied to a given page is found and evaluated in the following
|
52
|
+
## order:
|
53
|
+
##
|
54
|
+
## * A block supplied to the <%= section_link 'extension mappings', 'extension mapping' %>
|
55
|
+
## that matched this page (if any).
|
56
|
+
## * Any COMMON.rb files from the filesystem root down to this directory.
|
57
|
+
## * This page's ruby code, _basename_.rb.
|
58
|
+
## * In the template itself (via ERB).
|
59
|
+
##
|
60
|
+
## When a +Page+ instance is created, Rote looks for these, and if found evaluates
|
61
|
+
## them, in order, in the +Page+ instance binding.
|
62
|
+
##
|
63
|
+
## Additionally, when layout is used the following evaluation takes place
|
64
|
+
## *after rendering the template text* and can be used to make variables
|
65
|
+
## available for the layout pass(es), and apply nested layout:
|
66
|
+
##
|
67
|
+
## * This layout's ruby code, _layout_basename_.rb.
|
68
|
+
## * In the layout itself (again, with ERB).
|
69
|
+
##
|
70
|
+
## As mentioned, +Page+ instances serve as the context for page code
|
71
|
+
## execution - All user-supplied code (COMMON.rb, page and layout code, and
|
72
|
+
## ERB in the templates themselves) is executed in the binding of an instance
|
73
|
+
## of this class.
|
74
|
+
##
|
75
|
+
## == Layout
|
76
|
+
##
|
77
|
+
## All pages support layout, which allow common template to be applied across
|
78
|
+
## several pages. This is handled via multiple render passes, with each layout
|
79
|
+
## responsible for including the previously rendered content (via ERB).
|
80
|
+
##
|
81
|
+
## Layout templates include the content rendered by the page (or previous layout,
|
82
|
+
## see below) render pass using the instance variable @content_for_layout.
|
83
|
+
## This should be a familar pattern for those familiar with the Rails framework.
|
84
|
+
##
|
85
|
+
## To apply layout to a page, the +layout+ method should be called, passing
|
86
|
+
## in the base-name (with extension if different from the page template).
|
87
|
+
## When issued from common or page code, multiple calls to this method will
|
88
|
+
## override any previous setting. It may be called again from layout code,
|
89
|
+
## however, in which case the output of the currently-rendering layout will
|
90
|
+
## be passed (via the @content_to_layout instance variable) to the specified
|
91
|
+
## layout. In this way, Rote allows layouts to be nested to any level.
|
92
|
+
##
|
93
|
+
## == Filtering
|
94
|
+
##
|
95
|
+
## The +page_filter+ and +post_filter+ methods allow _filters_ to be applied
|
96
|
+
## to a page. Filters can be used to support any kind of textual transformation,
|
97
|
+
## macro expansion (page filters), or post-render processing (post filters).
|
98
|
+
## Rote includes a number of filters as standard, supporting plain-text markup,
|
99
|
+
## syntax highlighting, HTMLTidy postprocessing, and more.
|
100
|
+
##
|
101
|
+
## See +Rote::Filters+ for details of standard filters and their individual use.
|
102
|
+
##
|
103
|
+
## Filters are written in Ruby, and Rote provides base-classes from which filters
|
104
|
+
## can be derived with just a few lines of code (See Rote::Filters::TextFilter
|
105
|
+
## and Rote::Filters::MacroFilter). Additionally, the page and post filter
|
106
|
+
## methods allow text filters to be created from a supplied block.
|
107
|
+
##
|
108
|
+
## == Rendering
|
109
|
+
##
|
110
|
+
## Rendering occurs only once for a given page object, when the +render+ method
|
111
|
+
## is first called. Once a page has been rendered, the instance it is frozen
|
112
|
+
## to prevent further modification, and the rendered output is cached. Future
|
113
|
+
## calls to +render+ will return the cached output.
|
31
114
|
##
|
32
|
-
## Rendering happens only once for a given page object, when the
|
33
|
-
## +render+ method is first called. Once a page has been rendered
|
34
|
-
## it is frozen.
|
35
115
|
class Page
|
36
116
|
|
37
117
|
class << self
|
@@ -39,7 +119,7 @@ module Rote
|
|
39
119
|
# This does not check that the source exists - use the +ruby_filename+
|
40
120
|
# instance method to get the actual filename (if any) of source
|
41
121
|
# associated with a given page instance.
|
42
|
-
def page_ruby_filename(template_fn)
|
122
|
+
def page_ruby_filename(template_fn) # :nodoc:
|
43
123
|
fn = nil
|
44
124
|
if (template_fn)
|
45
125
|
if (fn = template_fn.dup) =~ FILE_EXT
|
@@ -52,7 +132,7 @@ module Rote
|
|
52
132
|
end
|
53
133
|
|
54
134
|
# Find all COMMON.rb files from given dir up to FS root.
|
55
|
-
def resolve_common_rubys(dir, arr = [])
|
135
|
+
def resolve_common_rubys(dir, arr = []) # :nodoc:
|
56
136
|
# defer to parent dir first
|
57
137
|
parent = File.expand_path(File.join(dir, '..'))
|
58
138
|
resolve_common_rubys(parent,arr) unless parent == dir # at root
|
@@ -72,24 +152,36 @@ module Rote
|
|
72
152
|
# and operates only on the innermost layout.
|
73
153
|
attr_reader :layout_text # Deprecated vv0.3.2 v-0.4
|
74
154
|
|
75
|
-
# The
|
76
|
-
#
|
77
|
-
attr_reader :template_name
|
155
|
+
# The basename from which this page's template was read,
|
156
|
+
# relative to the +base_path+.
|
157
|
+
attr_reader :template_name
|
158
|
+
|
159
|
+
attr_reader :layout_names # :nodoc:
|
78
160
|
|
79
|
-
#
|
80
|
-
#
|
81
|
-
|
161
|
+
# The filename of the innermost layout, usually specified by the page
|
162
|
+
# itself, relative to the +layout_path+. This method should not be used
|
163
|
+
# from COMMON.rb since its behaviour is undefined until all page code is
|
164
|
+
# evaluated and the final base_layout is known.
|
165
|
+
def base_layout_name; layout_names.first; end
|
166
|
+
alias :layout_name :base_layout_name # Compat alias, please migrate vv0.3.3 v-0.4
|
82
167
|
|
83
|
-
# The base
|
84
|
-
|
85
|
-
attr_reader :base_path, :layout_path
|
168
|
+
# The base path for template resolution.
|
169
|
+
attr_reader :base_path
|
86
170
|
|
87
|
-
# The
|
88
|
-
|
89
|
-
|
90
|
-
#
|
171
|
+
# The base path for layout resolution
|
172
|
+
attr_reader :layout_path
|
173
|
+
|
174
|
+
# The array of page filters (applied to this page during the first render
|
175
|
+
# pass, *before* layout is applied). You can use +page_filter+ to
|
176
|
+
# add new page filters, which gives implicit block => Filters::TextFilter conversion
|
177
|
+
# and checks for nil.
|
178
|
+
attr_reader :page_filters
|
179
|
+
|
180
|
+
# The array of post filters (applied to this page output *after*
|
181
|
+
# layout is applied). You can use +post_filter+ to add
|
182
|
+
# new post filters, which gives implicit block => Filters::TextFilter conversion
|
91
183
|
# and checks for nil.
|
92
|
-
attr_reader :
|
184
|
+
attr_reader :post_filters
|
93
185
|
|
94
186
|
# Reads the template, and evaluates the global and page scripts, if
|
95
187
|
# available, using the current binding. You may define any instance
|
@@ -102,7 +194,7 @@ module Rote
|
|
102
194
|
#
|
103
195
|
# If a block is supplied, it is executed _before_ the global / page
|
104
196
|
# code. This will be the block supplied by the file-extension mapping.
|
105
|
-
def initialize(template_name, pages_dir = '.', layout_dir = pages_dir
|
197
|
+
def initialize(template_name, pages_dir = '.', layout_dir = pages_dir)
|
106
198
|
@template_text = nil
|
107
199
|
@template_name = nil
|
108
200
|
@layout_names = []
|
@@ -122,14 +214,15 @@ module Rote
|
|
122
214
|
tfn = template_name
|
123
215
|
read_template(tfn)
|
124
216
|
|
125
|
-
|
217
|
+
# Yield to the (extension mapping) block
|
218
|
+
yield self if block_given?
|
126
219
|
|
127
220
|
# Eval COMMON.rb's
|
128
221
|
eval_common_rubys
|
129
222
|
|
130
223
|
# get script filenames, and eval them if found
|
131
224
|
tfn = ruby_filename # nil if no file
|
132
|
-
instance_eval(File.read(tfn),tfn) if tfn
|
225
|
+
instance_eval(File.read(tfn),tfn) if tfn
|
133
226
|
end
|
134
227
|
|
135
228
|
# Returns the full filename of this Page's template. This is obtained by
|
@@ -140,9 +233,10 @@ module Rote
|
|
140
233
|
|
141
234
|
# Returns the full filename of the first queued layout. This is
|
142
235
|
# the innermost layout, usually specified by the page itself.
|
143
|
-
def
|
236
|
+
def base_layout_filename
|
144
237
|
layout_fn(layout_name)
|
145
238
|
end
|
239
|
+
alias :layout_filename :base_layout_filename # Compat alias, please migrate vv0.3.3 v-0.4
|
146
240
|
|
147
241
|
# Returns the full filename of this Page's ruby source. If no source is
|
148
242
|
# found for this page (not including common source) this returns +nil+.
|
@@ -160,7 +254,7 @@ module Rote
|
|
160
254
|
page_filters << filter
|
161
255
|
else
|
162
256
|
if block
|
163
|
-
page_filters << Filters::
|
257
|
+
page_filters << Filters::TextFilter.new(&block)
|
164
258
|
end
|
165
259
|
end
|
166
260
|
end
|
@@ -172,7 +266,7 @@ module Rote
|
|
172
266
|
post_filters << filter
|
173
267
|
else
|
174
268
|
if block
|
175
|
-
post_filters << Filters::
|
269
|
+
post_filters << Filters::TextFilter.new(&block)
|
176
270
|
end
|
177
271
|
end
|
178
272
|
end
|
@@ -186,17 +280,19 @@ module Rote
|
|
186
280
|
|
187
281
|
alias :to_s :render
|
188
282
|
|
189
|
-
#
|
283
|
+
# Sets the page's base-layout as specified, or applies _nested_ _layout_
|
284
|
+
# if called during a layout render pass. The specified
|
190
285
|
# basename should be the name of the layout file relative to the
|
191
|
-
# +
|
286
|
+
# +layout_path+. If the layout has the same extension as the page source
|
287
|
+
# template, it may be omitted.
|
192
288
|
#
|
193
289
|
# *The* *layout* *is* *not* *read* *by* *this* *method*. It, and
|
194
290
|
# it's source, are loaded only at rendering time. This prevents
|
195
291
|
# multiple calls by various scoped COMMON code, for example, from
|
196
292
|
# making a mess in the Page binding.
|
197
293
|
#
|
198
|
-
# This can only be called before the first call to +render
|
199
|
-
# that the instance is frozen.
|
294
|
+
# This can only be called before the first call to +render+ returns it's
|
295
|
+
# result. After that the Page instance is frozen.
|
200
296
|
def layout(basename)
|
201
297
|
if basename
|
202
298
|
self.layout_names << "#{basename}#{@layout_defext if File.extname(basename).empty?}"
|
@@ -267,6 +363,16 @@ module Rote
|
|
267
363
|
@content_for_layout = render_page_filters( erb.result(binding) )
|
268
364
|
end
|
269
365
|
|
366
|
+
# FIXME: Quick fix for incorrect COMMON.rb layout nesting.
|
367
|
+
# All we do here is reset the layout to be the last layout
|
368
|
+
# added.
|
369
|
+
#
|
370
|
+
# If it turns out that the ability to nest from COMMON/page
|
371
|
+
# really is useless, we should remove the layout queue entirely,
|
372
|
+
# and then just have the render layout loop run until
|
373
|
+
# layout at end == layout at start.
|
374
|
+
@layout_names = [@layout_names.last] unless layout_names.empty?
|
375
|
+
|
270
376
|
# Do layout _after_ page eval. As we go through this, the layouts
|
271
377
|
# we load may add to the end of the layout names array, so nested
|
272
378
|
# layout is supported by just chasing the end of the array until
|
@@ -299,7 +405,7 @@ module Rote
|
|
299
405
|
@result
|
300
406
|
end
|
301
407
|
|
302
|
-
def inherit_common # inherit_common is implicit now vv0.2.99 v-0.
|
408
|
+
def inherit_common # inherit_common is implicit now vv0.2.99 v-0.4
|
303
409
|
warn "Warning: inherit_common is deprecated (inheritance is now implicit)"
|
304
410
|
end
|
305
411
|
|