jekyll 2.5.3 → 3.0.0.pre.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of jekyll might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/LICENSE +3 -3
- data/README.markdown +4 -4
- data/bin/jekyll +4 -6
- data/lib/jekyll.rb +8 -1
- data/lib/jekyll/cleaner.rb +8 -0
- data/lib/jekyll/collection.rb +1 -2
- data/lib/jekyll/command.rb +1 -0
- data/lib/jekyll/commands/build.rb +3 -1
- data/lib/jekyll/commands/clean.rb +42 -0
- data/lib/jekyll/configuration.rb +13 -11
- data/lib/jekyll/converters/markdown.rb +3 -6
- data/lib/jekyll/converters/markdown/kramdown_parser.rb +2 -2
- data/lib/jekyll/converters/markdown/rdiscount_parser.rb +1 -1
- data/lib/jekyll/converters/markdown/redcarpet_parser.rb +3 -3
- data/lib/jekyll/convertible.rb +6 -0
- data/lib/jekyll/deprecator.rb +0 -17
- data/lib/jekyll/document.rb +5 -3
- data/lib/jekyll/excerpt.rb +1 -2
- data/lib/jekyll/external.rb +59 -0
- data/lib/jekyll/filters.rb +12 -20
- data/lib/jekyll/frontmatter_defaults.rb +1 -1
- data/lib/jekyll/layout.rb +4 -0
- data/lib/jekyll/log_adapter.rb +5 -2
- data/lib/jekyll/post.rb +15 -6
- data/lib/jekyll/regenerator.rb +141 -0
- data/lib/jekyll/renderer.rb +12 -5
- data/lib/jekyll/site.rb +32 -17
- data/lib/jekyll/static_file.rb +4 -5
- data/lib/jekyll/tags/highlight.rb +1 -1
- data/lib/jekyll/tags/include.rb +10 -1
- data/lib/jekyll/utils.rb +52 -15
- data/lib/jekyll/version.rb +1 -1
- data/lib/site_template/_includes/footer.html +5 -5
- data/lib/site_template/_includes/head.html +1 -1
- data/lib/site_template/_layouts/page.html +4 -4
- data/lib/site_template/_layouts/post.html +6 -6
- data/lib/site_template/_sass/_layout.scss +4 -4
- metadata +11 -248
- data/lib/jekyll/commands/docs.rb +0 -30
- data/lib/jekyll/converters/textile.rb +0 -56
data/lib/jekyll/filters.rb
CHANGED
@@ -1,19 +1,9 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'json'
|
3
|
+
require 'date'
|
3
4
|
|
4
5
|
module Jekyll
|
5
6
|
module Filters
|
6
|
-
# Convert a Textile string into HTML output.
|
7
|
-
#
|
8
|
-
# input - The Textile String to convert.
|
9
|
-
#
|
10
|
-
# Returns the HTML formatted String.
|
11
|
-
def textilize(input)
|
12
|
-
site = @context.registers[:site]
|
13
|
-
converter = site.getConverterImpl(Jekyll::Converters::Textile)
|
14
|
-
converter.convert(input)
|
15
|
-
end
|
16
|
-
|
17
7
|
# Convert a Markdown string into HTML output.
|
18
8
|
#
|
19
9
|
# input - The Markdown String to convert.
|
@@ -21,7 +11,7 @@ module Jekyll
|
|
21
11
|
# Returns the HTML formatted String.
|
22
12
|
def markdownify(input)
|
23
13
|
site = @context.registers[:site]
|
24
|
-
converter = site.
|
14
|
+
converter = site.find_converter_instance(Jekyll::Converters::Markdown)
|
25
15
|
converter.convert(input)
|
26
16
|
end
|
27
17
|
|
@@ -32,7 +22,7 @@ module Jekyll
|
|
32
22
|
# Returns the CSS formatted String.
|
33
23
|
def sassify(input)
|
34
24
|
site = @context.registers[:site]
|
35
|
-
converter = site.
|
25
|
+
converter = site.find_converter_instance(Jekyll::Converters::Sass)
|
36
26
|
converter.convert(input)
|
37
27
|
end
|
38
28
|
|
@@ -43,19 +33,19 @@ module Jekyll
|
|
43
33
|
# Returns the CSS formatted String.
|
44
34
|
def scssify(input)
|
45
35
|
site = @context.registers[:site]
|
46
|
-
converter = site.
|
36
|
+
converter = site.find_converter_instance(Jekyll::Converters::Scss)
|
47
37
|
converter.convert(input)
|
48
38
|
end
|
49
39
|
|
50
40
|
# Slugify a filename or title.
|
51
41
|
#
|
52
42
|
# input - The filename or title to slugify.
|
43
|
+
# mode - how string is slugified
|
53
44
|
#
|
54
|
-
# Returns the given filename or title as a lowercase String
|
55
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
Utils.slugify(input)
|
45
|
+
# Returns the given filename or title as a lowercase URL String.
|
46
|
+
# See Utils.slugify for more detail.
|
47
|
+
def slugify(input, mode=nil)
|
48
|
+
Utils.slugify(input, mode)
|
59
49
|
end
|
60
50
|
|
61
51
|
# Format a date in short format e.g. "27 Jan 2011".
|
@@ -302,6 +292,8 @@ module Jekyll
|
|
302
292
|
case input
|
303
293
|
when Time
|
304
294
|
input
|
295
|
+
when Date
|
296
|
+
input.to_time
|
305
297
|
when String
|
306
298
|
Time.parse(input) rescue Time.at(input.to_i)
|
307
299
|
when Numeric
|
@@ -309,7 +301,7 @@ module Jekyll
|
|
309
301
|
else
|
310
302
|
Jekyll.logger.error "Invalid Date:", "'#{input}' is not a valid datetime."
|
311
303
|
exit(1)
|
312
|
-
end
|
304
|
+
end.localtime
|
313
305
|
end
|
314
306
|
|
315
307
|
def groupable?(element)
|
@@ -168,7 +168,7 @@ module Jekyll
|
|
168
168
|
end.compact
|
169
169
|
end
|
170
170
|
|
171
|
-
# Sanitizes the given path by removing a leading and
|
171
|
+
# Sanitizes the given path by removing a leading and adding a trailing slash
|
172
172
|
def sanitize_path(path)
|
173
173
|
if path.nil? || path.empty?
|
174
174
|
""
|
data/lib/jekyll/layout.rb
CHANGED
@@ -8,6 +8,9 @@ module Jekyll
|
|
8
8
|
# Gets the name of this layout.
|
9
9
|
attr_reader :name
|
10
10
|
|
11
|
+
# Gets the path to this layout.
|
12
|
+
attr_reader :path
|
13
|
+
|
11
14
|
# Gets/Sets the extension of this layout.
|
12
15
|
attr_accessor :ext
|
13
16
|
|
@@ -26,6 +29,7 @@ module Jekyll
|
|
26
29
|
@site = site
|
27
30
|
@base = base
|
28
31
|
@name = name
|
32
|
+
@path = site.in_source_dir(base, name)
|
29
33
|
|
30
34
|
self.data = {}
|
31
35
|
|
data/lib/jekyll/log_adapter.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Jekyll
|
2
2
|
class LogAdapter
|
3
|
-
attr_reader :writer
|
3
|
+
attr_reader :writer, :messages
|
4
4
|
|
5
5
|
LOG_LEVELS = {
|
6
6
|
:debug => ::Logger::DEBUG,
|
@@ -16,6 +16,7 @@ module Jekyll
|
|
16
16
|
#
|
17
17
|
# Returns nothing
|
18
18
|
def initialize(writer, level = :info)
|
19
|
+
@messages = []
|
19
20
|
@writer = writer
|
20
21
|
self.log_level = level
|
21
22
|
end
|
@@ -87,7 +88,9 @@ module Jekyll
|
|
87
88
|
#
|
88
89
|
# Returns the formatted message
|
89
90
|
def message(topic, message)
|
90
|
-
formatted_topic(topic) + message.to_s.gsub(/\s+/, ' ')
|
91
|
+
msg = formatted_topic(topic) + message.to_s.gsub(/\s+/, ' ')
|
92
|
+
messages << msg
|
93
|
+
msg
|
91
94
|
end
|
92
95
|
|
93
96
|
# Internal: Format the topic
|
data/lib/jekyll/post.rb
CHANGED
@@ -23,6 +23,7 @@ module Jekyll
|
|
23
23
|
ATTRIBUTES_FOR_LIQUID = EXCERPT_ATTRIBUTES_FOR_LIQUID + %w[
|
24
24
|
content
|
25
25
|
excerpt
|
26
|
+
excerpt_separator
|
26
27
|
]
|
27
28
|
|
28
29
|
# Post name validator. Post filenames must be like:
|
@@ -52,12 +53,12 @@ module Jekyll
|
|
52
53
|
@base = containing_dir(dir)
|
53
54
|
@name = name
|
54
55
|
|
55
|
-
self.categories = dir.
|
56
|
+
self.categories = dir.split('/').reject { |x| x.empty? }
|
56
57
|
process(name)
|
57
58
|
read_yaml(@base, name)
|
58
59
|
|
59
60
|
data.default_proc = proc do |hash, key|
|
60
|
-
site.frontmatter_defaults.find(
|
61
|
+
site.frontmatter_defaults.find(relative_path, type, key)
|
61
62
|
end
|
62
63
|
|
63
64
|
if data.key?('date')
|
@@ -80,7 +81,7 @@ module Jekyll
|
|
80
81
|
categories_from_data = Utils.pluralized_array_from_hash(data, 'category', 'categories')
|
81
82
|
self.categories = (
|
82
83
|
Array(categories) + categories_from_data
|
83
|
-
).map {|c| c.to_s
|
84
|
+
).map { |c| c.to_s }.flatten.uniq
|
84
85
|
end
|
85
86
|
|
86
87
|
def populate_tags
|
@@ -118,6 +119,14 @@ module Jekyll
|
|
118
119
|
data.fetch('title') { titleized_slug }
|
119
120
|
end
|
120
121
|
|
122
|
+
# Public: the Post excerpt_separator, from the YAML Front-Matter or site default
|
123
|
+
# excerpt_separator value
|
124
|
+
#
|
125
|
+
# Returns the post excerpt_separator
|
126
|
+
def excerpt_separator
|
127
|
+
(data['excerpt_separator'] || site.config['excerpt_separator']).to_s
|
128
|
+
end
|
129
|
+
|
121
130
|
# Turns the post slug into a suitable title
|
122
131
|
def titleized_slug
|
123
132
|
slug.split('-').select {|w| w.capitalize! || w }.join(' ')
|
@@ -218,7 +227,7 @@ module Jekyll
|
|
218
227
|
:title => slug,
|
219
228
|
:i_day => date.strftime("%-d"),
|
220
229
|
:i_month => date.strftime("%-m"),
|
221
|
-
:categories => (categories || []).map { |c| c.to_s }.join('/'),
|
230
|
+
:categories => (categories || []).map { |c| c.to_s.downcase }.uniq.join('/'),
|
222
231
|
:short_month => date.strftime("%b"),
|
223
232
|
:short_year => date.strftime("%y"),
|
224
233
|
:y_day => date.strftime("%j"),
|
@@ -269,7 +278,7 @@ module Jekyll
|
|
269
278
|
def destination(dest)
|
270
279
|
# The url needs to be unescaped in order to preserve the correct filename
|
271
280
|
path = site.in_dest_dir(dest, URL.unescape_path(url))
|
272
|
-
path = File.join(path, "index.html") if
|
281
|
+
path = File.join(path, "index.html") if self.url =~ /\/$/
|
273
282
|
path
|
274
283
|
end
|
275
284
|
|
@@ -307,7 +316,7 @@ module Jekyll
|
|
307
316
|
end
|
308
317
|
|
309
318
|
def generate_excerpt?
|
310
|
-
!
|
319
|
+
!excerpt_separator.empty?
|
311
320
|
end
|
312
321
|
end
|
313
322
|
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Jekyll
|
2
|
+
class Regenerator
|
3
|
+
attr_reader :site, :metadata, :cache
|
4
|
+
|
5
|
+
def initialize(site)
|
6
|
+
@site = site
|
7
|
+
|
8
|
+
# Read metadata from file
|
9
|
+
read_metadata
|
10
|
+
|
11
|
+
# Initialize cache to an empty hash
|
12
|
+
@cache = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# Checks if a renderable object needs to be regenerated
|
16
|
+
#
|
17
|
+
# Returns a boolean.
|
18
|
+
def regenerate?(document)
|
19
|
+
case document
|
20
|
+
when Post, Page
|
21
|
+
document.asset_file? || document.data['regenerate'] ||
|
22
|
+
modified?(site.in_source_dir(document.relative_path))
|
23
|
+
when Document
|
24
|
+
!document.write? || document.data['regenerate'] || modified?(document.path)
|
25
|
+
else
|
26
|
+
if document.respond_to?(:path)
|
27
|
+
modified?(document.path)
|
28
|
+
else
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Add a path to the metadata
|
35
|
+
#
|
36
|
+
# Returns true, also on failure.
|
37
|
+
def add(path)
|
38
|
+
return true unless File.exist?(path)
|
39
|
+
|
40
|
+
metadata[path] = {
|
41
|
+
"mtime" => File.mtime(path),
|
42
|
+
"deps" => []
|
43
|
+
}
|
44
|
+
cache[path] = true
|
45
|
+
end
|
46
|
+
|
47
|
+
# Force a path to regenerate
|
48
|
+
#
|
49
|
+
# Returns true.
|
50
|
+
def force(path)
|
51
|
+
cache[path] = true
|
52
|
+
end
|
53
|
+
|
54
|
+
# Clear the metadata and cache
|
55
|
+
#
|
56
|
+
# Returns nothing
|
57
|
+
def clear
|
58
|
+
@metadata = {}
|
59
|
+
@cache = {}
|
60
|
+
end
|
61
|
+
|
62
|
+
# Checks if a path's (or one of its dependencies)
|
63
|
+
# mtime has changed
|
64
|
+
#
|
65
|
+
# Returns a boolean.
|
66
|
+
def modified?(path)
|
67
|
+
return true if disabled?
|
68
|
+
|
69
|
+
# Check for path in cache
|
70
|
+
if cache.has_key? path
|
71
|
+
return cache[path]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Check path that exists in metadata
|
75
|
+
data = metadata[path]
|
76
|
+
if data
|
77
|
+
data["deps"].each do |dependency|
|
78
|
+
if modified?(dependency)
|
79
|
+
return cache[dependency] = cache[path] = true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
if data["mtime"].eql? File.mtime(path)
|
83
|
+
return cache[path] = false
|
84
|
+
else
|
85
|
+
return add(path)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Path does not exist in metadata, add it
|
90
|
+
return add(path)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Add a dependency of a path
|
94
|
+
#
|
95
|
+
# Returns nothing.
|
96
|
+
def add_dependency(path, dependency)
|
97
|
+
return if (metadata[path].nil? || @disabled)
|
98
|
+
|
99
|
+
metadata[path]["deps"] << dependency unless metadata[path]["deps"].include? dependency
|
100
|
+
regenerate? dependency
|
101
|
+
end
|
102
|
+
|
103
|
+
# Write the metadata to disk
|
104
|
+
#
|
105
|
+
# Returns nothing.
|
106
|
+
def write_metadata
|
107
|
+
File.open(metadata_file, 'w') do |f|
|
108
|
+
f.write(metadata.to_yaml)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Produce the absolute path of the metadata file
|
113
|
+
#
|
114
|
+
# Returns the String path of the file.
|
115
|
+
def metadata_file
|
116
|
+
site.in_source_dir('.jekyll-metadata')
|
117
|
+
end
|
118
|
+
|
119
|
+
# Check if metadata has been disabled
|
120
|
+
#
|
121
|
+
# Returns a Boolean (true for disabled, false for enabled).
|
122
|
+
def disabled?
|
123
|
+
@disabled = site.full_rebuild? if @disabled.nil?
|
124
|
+
@disabled
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Read metadata from the metadata file, if no file is found,
|
130
|
+
# initialize with an empty hash
|
131
|
+
#
|
132
|
+
# Returns the read metadata.
|
133
|
+
def read_metadata
|
134
|
+
@metadata = if !disabled? && File.file?(metadata_file)
|
135
|
+
SafeYAML.load(File.read(metadata_file))
|
136
|
+
else
|
137
|
+
{}
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/lib/jekyll/renderer.rb
CHANGED
@@ -3,11 +3,12 @@
|
|
3
3
|
module Jekyll
|
4
4
|
class Renderer
|
5
5
|
|
6
|
-
attr_reader :document, :site
|
6
|
+
attr_reader :document, :site, :site_payload
|
7
7
|
|
8
|
-
def initialize(site, document)
|
9
|
-
@site
|
10
|
-
@document
|
8
|
+
def initialize(site, document, site_payload = nil)
|
9
|
+
@site = site
|
10
|
+
@document = document
|
11
|
+
@site_payload = site_payload
|
11
12
|
end
|
12
13
|
|
13
14
|
# Determine which converters to use based on this document's
|
@@ -32,7 +33,7 @@ module Jekyll
|
|
32
33
|
def run
|
33
34
|
payload = Utils.deep_merge_hashes({
|
34
35
|
"page" => document.to_liquid
|
35
|
-
}, site.site_payload)
|
36
|
+
}, site_payload || site.site_payload)
|
36
37
|
|
37
38
|
info = {
|
38
39
|
filters: [Jekyll::Filters],
|
@@ -138,6 +139,12 @@ module Jekyll
|
|
138
139
|
File.join(site.config['layouts'], layout.name)
|
139
140
|
)
|
140
141
|
|
142
|
+
# Add layout to dependency tree
|
143
|
+
site.regenerator.add_dependency(
|
144
|
+
site.in_source_dir(document.path),
|
145
|
+
site.in_source_dir(layout.path)
|
146
|
+
) if document.write?
|
147
|
+
|
141
148
|
if layout = site.layouts[layout.data["layout"]]
|
142
149
|
if used.include?(layout)
|
143
150
|
layout = nil # avoid recursive chain
|
data/lib/jekyll/site.rb
CHANGED
@@ -11,6 +11,7 @@ module Jekyll
|
|
11
11
|
:gems, :plugin_manager
|
12
12
|
|
13
13
|
attr_accessor :converters, :generators
|
14
|
+
attr_reader :regenerator
|
14
15
|
|
15
16
|
# Public: Initialize a new Site.
|
16
17
|
#
|
@@ -27,6 +28,9 @@ module Jekyll
|
|
27
28
|
@source = File.expand_path(config['source']).freeze
|
28
29
|
@dest = File.expand_path(config['destination']).freeze
|
29
30
|
|
31
|
+
# Initialize incremental regenerator
|
32
|
+
@regenerator = Regenerator.new(self)
|
33
|
+
|
30
34
|
self.plugin_manager = Jekyll::PluginManager.new(self)
|
31
35
|
self.plugins = plugin_manager.plugins_path
|
32
36
|
|
@@ -183,6 +187,7 @@ module Jekyll
|
|
183
187
|
end
|
184
188
|
|
185
189
|
pages.sort_by!(&:name)
|
190
|
+
static_files.sort_by!(&:relative_path)
|
186
191
|
end
|
187
192
|
|
188
193
|
# Read all the files in <source>/<dir>/_posts and create a new Post
|
@@ -287,17 +292,22 @@ module Jekyll
|
|
287
292
|
def render
|
288
293
|
relative_permalinks_deprecation_method
|
289
294
|
|
295
|
+
payload = site_payload
|
290
296
|
collections.each do |label, collection|
|
291
297
|
collection.docs.each do |document|
|
292
|
-
|
298
|
+
if regenerator.regenerate?(document)
|
299
|
+
document.output = Jekyll::Renderer.new(self, document, payload).run
|
300
|
+
end
|
293
301
|
end
|
294
302
|
end
|
295
303
|
|
296
304
|
payload = site_payload
|
297
305
|
[posts, pages].flatten.each do |page_or_post|
|
298
|
-
|
306
|
+
if regenerator.regenerate?(page_or_post)
|
307
|
+
page_or_post.render(layouts, payload)
|
308
|
+
end
|
299
309
|
end
|
300
|
-
rescue Errno::ENOENT
|
310
|
+
rescue Errno::ENOENT
|
301
311
|
# ignore missing layout dir
|
302
312
|
end
|
303
313
|
|
@@ -312,7 +322,10 @@ module Jekyll
|
|
312
322
|
#
|
313
323
|
# Returns nothing.
|
314
324
|
def write
|
315
|
-
each_site_file { |item|
|
325
|
+
each_site_file { |item|
|
326
|
+
item.write(dest) if regenerator.regenerate?(item)
|
327
|
+
}
|
328
|
+
regenerator.write_metadata unless full_rebuild?
|
316
329
|
end
|
317
330
|
|
318
331
|
# Construct a Hash of Posts indexed by the specified Post attribute.
|
@@ -377,7 +390,7 @@ module Jekyll
|
|
377
390
|
"time" => time,
|
378
391
|
"posts" => posts.sort { |a, b| b <=> a },
|
379
392
|
"pages" => pages,
|
380
|
-
"static_files" => static_files
|
393
|
+
"static_files" => static_files,
|
381
394
|
"html_pages" => pages.select { |page| page.html? || page.url.end_with?("/") },
|
382
395
|
"categories" => post_attr_hash('categories'),
|
383
396
|
"tags" => post_attr_hash('tags'),
|
@@ -405,13 +418,8 @@ module Jekyll
|
|
405
418
|
# klass - The Class of the Converter to fetch.
|
406
419
|
#
|
407
420
|
# Returns the Converter instance implementing the given Converter.
|
408
|
-
def
|
409
|
-
|
410
|
-
if impl = matches.first
|
411
|
-
impl
|
412
|
-
else
|
413
|
-
raise "Converter implementation not found for #{klass}"
|
414
|
-
end
|
421
|
+
def find_converter_instance(klass)
|
422
|
+
converters.find { |c| c.class == klass } || proc { raise "No converter for #{klass}" }.call
|
415
423
|
end
|
416
424
|
|
417
425
|
# Create array of instances of the subclasses of the class or module
|
@@ -453,7 +461,7 @@ module Jekyll
|
|
453
461
|
|
454
462
|
def relative_permalinks_deprecation_method
|
455
463
|
if config['relative_permalinks'] && has_relative_page?
|
456
|
-
Jekyll.logger.warn "Deprecation:", "
|
464
|
+
Jekyll.logger.warn "Deprecation:", "Since v2.0, permalinks for pages" +
|
457
465
|
" in subfolders must be relative to the" +
|
458
466
|
" site source directory, not the parent" +
|
459
467
|
" directory. Check http://jekyllrb.com/docs/upgrading/"+
|
@@ -483,6 +491,17 @@ module Jekyll
|
|
483
491
|
@frontmatter_defaults ||= FrontmatterDefaults.new(self)
|
484
492
|
end
|
485
493
|
|
494
|
+
# Whether to perform a full rebuild without incremental regeneration
|
495
|
+
#
|
496
|
+
# Returns a Boolean: true for a full rebuild, false for normal build
|
497
|
+
def full_rebuild?(override = {})
|
498
|
+
override['full_rebuild'] || config['full_rebuild']
|
499
|
+
end
|
500
|
+
|
501
|
+
def publisher
|
502
|
+
@publisher ||= Publisher.new(self)
|
503
|
+
end
|
504
|
+
|
486
505
|
private
|
487
506
|
|
488
507
|
def has_relative_page?
|
@@ -503,9 +522,5 @@ module Jekyll
|
|
503
522
|
name.gsub!(/(^|\b\s)\s+($|\s?\b)/, '\\1\\2')
|
504
523
|
name.gsub(/\s+/, '_')
|
505
524
|
end
|
506
|
-
|
507
|
-
def publisher
|
508
|
-
@publisher ||= Publisher.new(self)
|
509
|
-
end
|
510
525
|
end
|
511
526
|
end
|