blackboard 3.1.6 → 3.1.7

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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +132 -0
  3. data/exe/jekyll +55 -0
  4. data/lib/jekyll.rb +179 -0
  5. data/lib/jekyll/cleaner.rb +105 -0
  6. data/lib/jekyll/collection.rb +205 -0
  7. data/lib/jekyll/command.rb +65 -0
  8. data/lib/jekyll/commands/build.rb +77 -0
  9. data/lib/jekyll/commands/clean.rb +42 -0
  10. data/lib/jekyll/commands/doctor.rb +114 -0
  11. data/lib/jekyll/commands/help.rb +31 -0
  12. data/lib/jekyll/commands/new.rb +82 -0
  13. data/lib/jekyll/commands/serve.rb +205 -0
  14. data/lib/jekyll/commands/serve/servlet.rb +61 -0
  15. data/lib/jekyll/configuration.rb +348 -0
  16. data/lib/jekyll/converter.rb +48 -0
  17. data/lib/jekyll/converters/identity.rb +21 -0
  18. data/lib/jekyll/converters/markdown.rb +92 -0
  19. data/lib/jekyll/converters/markdown/kramdown_parser.rb +117 -0
  20. data/lib/jekyll/converters/markdown/rdiscount_parser.rb +33 -0
  21. data/lib/jekyll/converters/markdown/redcarpet_parser.rb +102 -0
  22. data/lib/jekyll/converters/smartypants.rb +34 -0
  23. data/lib/jekyll/convertible.rb +300 -0
  24. data/lib/jekyll/deprecator.rb +46 -0
  25. data/lib/jekyll/document.rb +447 -0
  26. data/lib/jekyll/drops/collection_drop.rb +22 -0
  27. data/lib/jekyll/drops/document_drop.rb +60 -0
  28. data/lib/jekyll/drops/drop.rb +200 -0
  29. data/lib/jekyll/drops/excerpt_drop.rb +15 -0
  30. data/lib/jekyll/drops/jekyll_drop.rb +33 -0
  31. data/lib/jekyll/drops/site_drop.rb +38 -0
  32. data/lib/jekyll/drops/unified_payload_drop.rb +25 -0
  33. data/lib/jekyll/drops/url_drop.rb +83 -0
  34. data/lib/jekyll/entry_filter.rb +72 -0
  35. data/lib/jekyll/errors.rb +10 -0
  36. data/lib/jekyll/excerpt.rb +124 -0
  37. data/lib/jekyll/external.rb +59 -0
  38. data/lib/jekyll/filters.rb +367 -0
  39. data/lib/jekyll/frontmatter_defaults.rb +188 -0
  40. data/lib/jekyll/generator.rb +3 -0
  41. data/lib/jekyll/hooks.rb +101 -0
  42. data/lib/jekyll/layout.rb +49 -0
  43. data/lib/jekyll/liquid_extensions.rb +22 -0
  44. data/lib/jekyll/liquid_renderer.rb +39 -0
  45. data/lib/jekyll/liquid_renderer/file.rb +50 -0
  46. data/lib/jekyll/liquid_renderer/table.rb +94 -0
  47. data/lib/jekyll/log_adapter.rb +115 -0
  48. data/lib/jekyll/mime.types +800 -0
  49. data/lib/jekyll/page.rb +180 -0
  50. data/lib/jekyll/plugin.rb +96 -0
  51. data/lib/jekyll/plugin_manager.rb +95 -0
  52. data/lib/jekyll/publisher.rb +21 -0
  53. data/lib/jekyll/reader.rb +126 -0
  54. data/lib/jekyll/readers/collection_reader.rb +20 -0
  55. data/lib/jekyll/readers/data_reader.rb +69 -0
  56. data/lib/jekyll/readers/layout_reader.rb +53 -0
  57. data/lib/jekyll/readers/page_reader.rb +21 -0
  58. data/lib/jekyll/readers/post_reader.rb +62 -0
  59. data/lib/jekyll/readers/static_file_reader.rb +21 -0
  60. data/lib/jekyll/regenerator.rb +175 -0
  61. data/lib/jekyll/related_posts.rb +56 -0
  62. data/lib/jekyll/renderer.rb +194 -0
  63. data/lib/jekyll/site.rb +392 -0
  64. data/lib/jekyll/static_file.rb +141 -0
  65. data/lib/jekyll/stevenson.rb +58 -0
  66. data/lib/jekyll/tags/highlight.rb +122 -0
  67. data/lib/jekyll/tags/include.rb +190 -0
  68. data/lib/jekyll/tags/post_url.rb +88 -0
  69. data/lib/jekyll/url.rb +136 -0
  70. data/lib/jekyll/utils.rb +300 -0
  71. data/lib/jekyll/utils/ansi.rb +59 -0
  72. data/lib/jekyll/utils/platforms.rb +30 -0
  73. data/lib/jekyll/version.rb +3 -0
  74. data/lib/site_template/.gitignore +3 -0
  75. data/lib/site_template/_config.yml +24 -0
  76. data/lib/site_template/_includes/footer.html +38 -0
  77. data/lib/site_template/_includes/head.html +12 -0
  78. data/lib/site_template/_includes/header.html +27 -0
  79. data/lib/site_template/_includes/icon-github.html +1 -0
  80. data/lib/site_template/_includes/icon-github.svg +1 -0
  81. data/lib/site_template/_includes/icon-twitter.html +1 -0
  82. data/lib/site_template/_includes/icon-twitter.svg +1 -0
  83. data/lib/site_template/_layouts/default.html +20 -0
  84. data/lib/site_template/_layouts/page.html +14 -0
  85. data/lib/site_template/_layouts/post.html +15 -0
  86. data/lib/site_template/_pages/about.md +15 -0
  87. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +25 -0
  88. data/lib/site_template/_sass/_base.scss +206 -0
  89. data/lib/site_template/_sass/_layout.scss +242 -0
  90. data/lib/site_template/_sass/_syntax-highlighting.scss +71 -0
  91. data/lib/site_template/css/main.scss +53 -0
  92. data/lib/site_template/feed.xml +30 -0
  93. data/lib/site_template/index.html +23 -0
  94. data/lib/theme_template/CODE_OF_CONDUCT.md.erb +74 -0
  95. data/lib/theme_template/Gemfile +2 -0
  96. data/lib/theme_template/LICENSE.txt.erb +21 -0
  97. data/lib/theme_template/README.md.erb +46 -0
  98. data/lib/theme_template/Rakefile.erb +74 -0
  99. data/lib/theme_template/_layouts/default.html +1 -0
  100. data/lib/theme_template/_layouts/page.html +5 -0
  101. data/lib/theme_template/_layouts/post.html +5 -0
  102. data/lib/theme_template/example/_config.yml.erb +1 -0
  103. data/lib/theme_template/example/_post.md +12 -0
  104. data/lib/theme_template/example/index.html +14 -0
  105. data/lib/theme_template/example/style.scss +7 -0
  106. data/lib/theme_template/gitignore.erb +4 -0
  107. data/lib/theme_template/theme.gemspec.erb +22 -0
  108. metadata +109 -2
@@ -0,0 +1,300 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'set'
4
+
5
+ # Convertible provides methods for converting a pagelike item
6
+ # from a certain type of markup into actual content
7
+ #
8
+ # Requires
9
+ # self.site -> Jekyll::Site
10
+ # self.content
11
+ # self.content=
12
+ # self.data=
13
+ # self.ext=
14
+ # self.output=
15
+ # self.name
16
+ # self.path
17
+ # self.type -> :page, :post or :draft
18
+
19
+ module Jekyll
20
+ module Convertible
21
+ # Returns the contents as a String.
22
+ def to_s
23
+ content || ''
24
+ end
25
+
26
+ # Whether the file is published or not, as indicated in YAML front-matter
27
+ def published?
28
+ !(data.key?('published') && data['published'] == false)
29
+ end
30
+
31
+ # Read the YAML frontmatter.
32
+ #
33
+ # base - The String path to the dir containing the file.
34
+ # name - The String filename of the file.
35
+ # opts - optional parameter to File.read, default at site configs
36
+ #
37
+ # Returns nothing.
38
+ def read_yaml(base, name, opts = {})
39
+ filename = File.join(base, name)
40
+
41
+ begin
42
+ self.content = File.read(site.in_source_dir(base, name),
43
+ Utils.merged_file_read_opts(site, opts))
44
+ if content =~ /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
45
+ self.content = $POSTMATCH
46
+ self.data = SafeYAML.load(Regexp.last_match(1))
47
+ end
48
+ rescue SyntaxError => e
49
+ Jekyll.logger.warn "YAML Exception reading #{filename}: #{e.message}"
50
+ rescue Exception => e
51
+ Jekyll.logger.warn "Error reading file #{filename}: #{e.message}"
52
+ end
53
+
54
+ self.data ||= {}
55
+
56
+ validate_data! filename
57
+ validate_permalink! filename
58
+
59
+ self.data
60
+ end
61
+
62
+ def validate_data!(filename)
63
+ unless self.data.is_a?(Hash)
64
+ raise Errors::InvalidYAMLFrontMatterError, "Invalid YAML front matter in #{filename}"
65
+ end
66
+ end
67
+
68
+ def validate_permalink!(filename)
69
+ if self.data['permalink'] && self.data['permalink'].size == 0
70
+ raise Errors::InvalidPermalinkError, "Invalid permalink in #{filename}"
71
+ end
72
+ end
73
+
74
+ # Transform the contents based on the content type.
75
+ #
76
+ # Returns the transformed contents.
77
+ def transform
78
+ converters.reduce(content) do |output, converter|
79
+ begin
80
+ converter.convert output
81
+ rescue => e
82
+ Jekyll.logger.error "Conversion error:", "#{converter.class} encountered an error while converting '#{path}':"
83
+ Jekyll.logger.error("", e.to_s)
84
+ raise e
85
+ end
86
+ end
87
+ end
88
+
89
+ # Determine the extension depending on content_type.
90
+ #
91
+ # Returns the String extension for the output file.
92
+ # e.g. ".html" for an HTML output file.
93
+ def output_ext
94
+ Jekyll::Renderer.new(site, self).output_ext
95
+ end
96
+
97
+ # Determine which converter to use based on this convertible's
98
+ # extension.
99
+ #
100
+ # Returns the Converter instance.
101
+ def converters
102
+ @converters ||= site.converters.select { |c| c.matches(ext) }.sort
103
+ end
104
+
105
+ # Render Liquid in the content
106
+ #
107
+ # content - the raw Liquid content to render
108
+ # payload - the payload for Liquid
109
+ # info - the info for Liquid
110
+ #
111
+ # Returns the converted content
112
+ def render_liquid(content, payload, info, path)
113
+ site.liquid_renderer.file(path).parse(content).render!(payload, info)
114
+ rescue Tags::IncludeTagError => e
115
+ Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{e.path}, included in #{path || self.path}"
116
+ raise e
117
+ rescue Exception => e
118
+ Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{path || self.path}"
119
+ raise e
120
+ end
121
+
122
+ # Convert this Convertible's data to a Hash suitable for use by Liquid.
123
+ #
124
+ # Returns the Hash representation of this Convertible.
125
+ def to_liquid(attrs = nil)
126
+ further_data = Hash[(attrs || self.class::ATTRIBUTES_FOR_LIQUID).map do |attribute|
127
+ [attribute, send(attribute)]
128
+ end]
129
+
130
+ defaults = site.frontmatter_defaults.all(relative_path, type)
131
+ Utils.deep_merge_hashes defaults, Utils.deep_merge_hashes(data, further_data)
132
+ end
133
+
134
+ # The type of a document,
135
+ # i.e., its classname downcase'd and to_sym'd.
136
+ #
137
+ # Returns the type of self.
138
+ def type
139
+ if is_a?(Page)
140
+ :pages
141
+ end
142
+ end
143
+
144
+ # returns the owner symbol for hook triggering
145
+ def hook_owner
146
+ if is_a?(Page)
147
+ :pages
148
+ end
149
+ end
150
+
151
+ # Determine whether the document is an asset file.
152
+ # Asset files include CoffeeScript files and Sass/SCSS files.
153
+ #
154
+ # Returns true if the extname belongs to the set of extensions
155
+ # that asset files use.
156
+ def asset_file?
157
+ sass_file? || coffeescript_file?
158
+ end
159
+
160
+ # Determine whether the document is a Sass file.
161
+ #
162
+ # Returns true if extname == .sass or .scss, false otherwise.
163
+ def sass_file?
164
+ %w(.sass .scss).include?(ext)
165
+ end
166
+
167
+ # Determine whether the document is a CoffeeScript file.
168
+ #
169
+ # Returns true if extname == .coffee, false otherwise.
170
+ def coffeescript_file?
171
+ '.coffee'.eql?(ext)
172
+ end
173
+
174
+ # Determine whether the file should be rendered with Liquid.
175
+ #
176
+ # Always returns true.
177
+ def render_with_liquid?
178
+ true
179
+ end
180
+
181
+ # Determine whether the file should be placed into layouts.
182
+ #
183
+ # Returns false if the document is an asset file.
184
+ def place_in_layout?
185
+ !asset_file?
186
+ end
187
+
188
+ # Checks if the layout specified in the document actually exists
189
+ #
190
+ # layout - the layout to check
191
+ #
192
+ # Returns true if the layout is invalid, false if otherwise
193
+ def invalid_layout?(layout)
194
+ !data["layout"].nil? && layout.nil? && !(self.is_a? Jekyll::Excerpt)
195
+ end
196
+
197
+ # Recursively render layouts
198
+ #
199
+ # layouts - a list of the layouts
200
+ # payload - the payload for Liquid
201
+ # info - the info for Liquid
202
+ #
203
+ # Returns nothing
204
+ def render_all_layouts(layouts, payload, info)
205
+ # recursively render layouts
206
+ layout = layouts[data["layout"]]
207
+
208
+ Jekyll.logger.warn("Build Warning:", "Layout '#{data["layout"]}' requested in #{path} does not exist.") if invalid_layout? layout
209
+
210
+ used = Set.new([layout])
211
+
212
+ # Reset the payload layout data to ensure it starts fresh for each page.
213
+ payload["layout"] = nil
214
+
215
+ while layout
216
+ Jekyll.logger.debug "Rendering Layout:", path
217
+ payload["content"] = output
218
+ payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
219
+
220
+ self.output = render_liquid(layout.content,
221
+ payload,
222
+ info,
223
+ File.join(site.config['layouts_dir'], layout.name))
224
+
225
+ # Add layout to dependency tree
226
+ site.regenerator.add_dependency(
227
+ site.in_source_dir(path),
228
+ site.in_source_dir(layout.path)
229
+ )
230
+
231
+ if layout = layouts[layout.data["layout"]]
232
+ if used.include?(layout)
233
+ layout = nil # avoid recursive chain
234
+ else
235
+ used << layout
236
+ end
237
+ end
238
+ end
239
+ end
240
+
241
+ # Add any necessary layouts to this convertible document.
242
+ #
243
+ # payload - The site payload Drop or Hash.
244
+ # layouts - A Hash of {"name" => "layout"}.
245
+ #
246
+ # Returns nothing.
247
+ def do_layout(payload, layouts)
248
+ Jekyll.logger.debug "Rendering:", self.relative_path
249
+
250
+ Jekyll.logger.debug "Pre-Render Hooks:", self.relative_path
251
+ Jekyll::Hooks.trigger hook_owner, :pre_render, self, payload
252
+ info = { :filters => [Jekyll::Filters], :registers => { :site => site, :page => payload["page"] } }
253
+
254
+ # render and transform content (this becomes the final content of the object)
255
+ payload["highlighter_prefix"] = converters.first.highlighter_prefix
256
+ payload["highlighter_suffix"] = converters.first.highlighter_suffix
257
+
258
+ if render_with_liquid?
259
+ Jekyll.logger.debug "Rendering Liquid:", self.relative_path
260
+ self.content = render_liquid(content, payload, info, path)
261
+ end
262
+ Jekyll.logger.debug "Rendering Markup:", self.relative_path
263
+ self.content = transform
264
+
265
+ # output keeps track of what will finally be written
266
+ self.output = content
267
+
268
+ render_all_layouts(layouts, payload, info) if place_in_layout?
269
+ Jekyll.logger.debug "Post-Render Hooks:", self.relative_path
270
+ Jekyll::Hooks.trigger hook_owner, :post_render, self
271
+ end
272
+
273
+ # Write the generated page file to the destination directory.
274
+ #
275
+ # dest - The String path to the destination dir.
276
+ #
277
+ # Returns nothing.
278
+ def write(dest)
279
+ path = destination(dest)
280
+ FileUtils.mkdir_p(File.dirname(path))
281
+ File.open(path, 'wb') do |f|
282
+ f.write(output)
283
+ end
284
+ Jekyll::Hooks.trigger hook_owner, :post_write, self
285
+ end
286
+
287
+ # Accessor for data properties by Liquid.
288
+ #
289
+ # property - The String name of the property to retrieve.
290
+ #
291
+ # Returns the String value or nil if the property isn't included.
292
+ def [](property)
293
+ if self.class::ATTRIBUTES_FOR_LIQUID.include?(property)
294
+ send(property)
295
+ else
296
+ data[property]
297
+ end
298
+ end
299
+ end
300
+ end
@@ -0,0 +1,46 @@
1
+ module Jekyll
2
+ module Deprecator
3
+ extend self
4
+
5
+ def process(args)
6
+ arg_is_present? args, "--server", "The --server command has been replaced by the \
7
+ 'serve' subcommand."
8
+ arg_is_present? args, "--serve", "The --server command has been replaced by the \
9
+ 'serve' subcommand."
10
+ arg_is_present? args, "--no-server", "To build Jekyll without launching a server, \
11
+ use the 'build' subcommand."
12
+ arg_is_present? args, "--auto", "The switch '--auto' has been replaced with '--watch'."
13
+ arg_is_present? args, "--no-auto", "To disable auto-replication, simply leave off \
14
+ the '--watch' switch."
15
+ arg_is_present? args, "--pygments", "The 'pygments'settings has been removed in \
16
+ favour of 'highlighter'."
17
+ arg_is_present? args, "--paginate", "The 'paginate' setting can only be set in your \
18
+ config files."
19
+ arg_is_present? args, "--url", "The 'url' setting can only be set in your config files."
20
+ no_subcommand(args)
21
+ end
22
+
23
+ def no_subcommand(args)
24
+ if args.size > 0 && args.first =~ /^--/ && !%w(--help --version).include?(args.first)
25
+ deprecation_message "Jekyll now uses subcommands instead of just switches. Run `jekyll help` to find out more."
26
+ abort
27
+ end
28
+ end
29
+
30
+ def arg_is_present?(args, deprecated_argument, message)
31
+ if args.include?(deprecated_argument)
32
+ deprecation_message(message)
33
+ end
34
+ end
35
+
36
+ def deprecation_message(message)
37
+ Jekyll.logger.error "Deprecation:", message
38
+ end
39
+
40
+ def defaults_deprecate_type(old, current)
41
+ Jekyll.logger.warn "Defaults:", "The '#{old}' type has become '#{current}'."
42
+ Jekyll.logger.warn "Defaults:", "Please update your front-matter defaults to use 'type: #{current}'."
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,447 @@
1
+ # encoding: UTF-8
2
+
3
+ module Jekyll
4
+ class Document
5
+ include Comparable
6
+
7
+ attr_reader :path, :site, :extname, :collection
8
+ attr_accessor :content, :output
9
+
10
+ YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
11
+ DATELESS_FILENAME_MATCHER = /^(.+\/)*(.*)(\.[^.]+)$/
12
+ DATE_FILENAME_MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
13
+
14
+ # Create a new Document.
15
+ #
16
+ # path - the path to the file
17
+ # relations - a hash with keys :site and :collection, the values of which
18
+ # are the Jekyll::Site and Jekyll::Collection to which this
19
+ # Document belong.
20
+ #
21
+ # Returns nothing.
22
+ def initialize(path, relations = {})
23
+ @site = relations[:site]
24
+ @path = path
25
+ @extname = File.extname(path)
26
+ @collection = relations[:collection]
27
+ @has_yaml_header = nil
28
+
29
+ if draft?
30
+ categories_from_path("_drafts")
31
+ else
32
+ categories_from_path(collection.relative_directory)
33
+ end
34
+
35
+ data.default_proc = proc do |_, key|
36
+ site.frontmatter_defaults.find(relative_path, collection.label, key)
37
+ end
38
+
39
+ trigger_hooks(:post_init)
40
+ end
41
+
42
+ # Fetch the Document's data.
43
+ #
44
+ # Returns a Hash containing the data. An empty hash is returned if
45
+ # no data was read.
46
+ def data
47
+ @data ||= {}
48
+ end
49
+
50
+ # Merge some data in with this document's data.
51
+ #
52
+ # Returns the merged data.
53
+ def merge_data!(other, source: "YAML front matter")
54
+ if other.key?('categories') && !other['categories'].nil?
55
+ if other['categories'].is_a?(String)
56
+ other['categories'] = other['categories'].split(" ").map(&:strip)
57
+ end
58
+ other['categories'] = (data['categories'] || []) | other['categories']
59
+ end
60
+ Utils.deep_merge_hashes!(data, other)
61
+ if data.key?('date') && !data['date'].is_a?(Time)
62
+ data['date'] = Utils.parse_date(
63
+ data['date'].to_s,
64
+ "Document '#{relative_path}' does not have a valid date in the #{source}."
65
+ )
66
+ end
67
+ data
68
+ end
69
+
70
+ def date
71
+ data['date'] ||= site.time
72
+ end
73
+
74
+ # Returns whether the document is a draft. This is only the case if
75
+ # the document is in the 'posts' collection but in a different
76
+ # directory than '_posts'.
77
+ #
78
+ # Returns whether the document is a draft.
79
+ def draft?
80
+ data['draft'] ||= relative_path.index(collection.relative_directory).nil? && collection.label == "posts"
81
+ end
82
+
83
+ # The path to the document, relative to the site source.
84
+ #
85
+ # Returns a String path which represents the relative path
86
+ # from the site source to this document
87
+ def relative_path
88
+ @relative_path ||= Pathname.new(path).relative_path_from(Pathname.new(site.source)).to_s
89
+ end
90
+
91
+ # The output extension of the document.
92
+ #
93
+ # Returns the output extension
94
+ def output_ext
95
+ Jekyll::Renderer.new(site, self).output_ext
96
+ end
97
+
98
+ # The base filename of the document, without the file extname.
99
+ #
100
+ # Returns the basename without the file extname.
101
+ def basename_without_ext
102
+ @basename_without_ext ||= File.basename(path, '.*')
103
+ end
104
+
105
+ # The base filename of the document.
106
+ #
107
+ # Returns the base filename of the document.
108
+ def basename
109
+ @basename ||= File.basename(path)
110
+ end
111
+
112
+ # Produces a "cleaned" relative path.
113
+ # The "cleaned" relative path is the relative path without the extname
114
+ # and with the collection's directory removed as well.
115
+ # This method is useful when building the URL of the document.
116
+ #
117
+ # Examples:
118
+ # When relative_path is "_methods/site/generate.md":
119
+ # cleaned_relative_path
120
+ # # => "/site/generate"
121
+ #
122
+ # Returns the cleaned relative path of the document.
123
+ def cleaned_relative_path
124
+ @cleaned_relative_path ||=
125
+ relative_path[0..-extname.length - 1].sub(collection.relative_directory, "")
126
+ end
127
+
128
+ # Determine whether the document is a YAML file.
129
+ #
130
+ # Returns true if the extname is either .yml or .yaml, false otherwise.
131
+ def yaml_file?
132
+ %w(.yaml .yml).include?(extname)
133
+ end
134
+
135
+ # Determine whether the document is an asset file.
136
+ # Asset files include CoffeeScript files and Sass/SCSS files.
137
+ #
138
+ # Returns true if the extname belongs to the set of extensions
139
+ # that asset files use.
140
+ def asset_file?
141
+ sass_file? || coffeescript_file?
142
+ end
143
+
144
+ # Determine whether the document is a Sass file.
145
+ #
146
+ # Returns true if extname == .sass or .scss, false otherwise.
147
+ def sass_file?
148
+ %w(.sass .scss).include?(extname)
149
+ end
150
+
151
+ # Determine whether the document is a CoffeeScript file.
152
+ #
153
+ # Returns true if extname == .coffee, false otherwise.
154
+ def coffeescript_file?
155
+ '.coffee'.eql?(extname)
156
+ end
157
+
158
+ # Determine whether the file should be rendered with Liquid.
159
+ #
160
+ # Returns false if the document is either an asset file or a yaml file,
161
+ # true otherwise.
162
+ def render_with_liquid?
163
+ !(coffeescript_file? || yaml_file?)
164
+ end
165
+
166
+ # Determine whether the file should be placed into layouts.
167
+ #
168
+ # Returns false if the document is either an asset file or a yaml file,
169
+ # true otherwise.
170
+ def place_in_layout?
171
+ !(asset_file? || yaml_file?)
172
+ end
173
+
174
+ # The URL template where the document would be accessible.
175
+ #
176
+ # Returns the URL template for the document.
177
+ def url_template
178
+ collection.url_template
179
+ end
180
+
181
+ # Construct a Hash of key-value pairs which contain a mapping between
182
+ # a key in the URL template and the corresponding value for this document.
183
+ #
184
+ # Returns the Hash of key-value pairs for replacement in the URL.
185
+ def url_placeholders
186
+ @url_placeholders ||= Drops::UrlDrop.new(self)
187
+ end
188
+
189
+ # The permalink for this Document.
190
+ # Permalink is set via the data Hash.
191
+ #
192
+ # Returns the permalink or nil if no permalink was set in the data.
193
+ def permalink
194
+ data && data.is_a?(Hash) && data['permalink']
195
+ end
196
+
197
+ # The computed URL for the document. See `Jekyll::URL#to_s` for more details.
198
+ #
199
+ # Returns the computed URL for the document.
200
+ def url
201
+ @url = URL.new({
202
+ :template => url_template,
203
+ :placeholders => url_placeholders,
204
+ :permalink => permalink
205
+ }).to_s
206
+ end
207
+
208
+ def [](key)
209
+ data[key]
210
+ end
211
+
212
+ # The full path to the output file.
213
+ #
214
+ # base_directory - the base path of the output directory
215
+ #
216
+ # Returns the full path to the output file of this document.
217
+ def destination(base_directory)
218
+ dest = site.in_dest_dir(base_directory)
219
+ path = site.in_dest_dir(dest, URL.unescape_path(url))
220
+ if url.end_with? "/"
221
+ path = File.join(path, "index.html")
222
+ else
223
+ path << output_ext unless path.end_with? output_ext
224
+ end
225
+ path
226
+ end
227
+
228
+ # Write the generated Document file to the destination directory.
229
+ #
230
+ # dest - The String path to the destination dir.
231
+ #
232
+ # Returns nothing.
233
+ def write(dest)
234
+ path = destination(dest)
235
+ FileUtils.mkdir_p(File.dirname(path))
236
+ File.open(path, 'wb') do |f|
237
+ f.write(output)
238
+ end
239
+
240
+ trigger_hooks(:post_write)
241
+ end
242
+
243
+ # Whether the file is published or not, as indicated in YAML front-matter
244
+ #
245
+ # Returns true if the 'published' key is specified in the YAML front-matter and not `false`.
246
+ def published?
247
+ !(data.key?('published') && data['published'] == false)
248
+ end
249
+
250
+ # Read in the file and assign the content and data based on the file contents.
251
+ # Merge the frontmatter of the file with the frontmatter default
252
+ # values
253
+ #
254
+ # Returns nothing.
255
+ def read(opts = {})
256
+ Jekyll.logger.debug "Reading:", relative_path
257
+
258
+ if yaml_file?
259
+ @data = SafeYAML.load_file(path)
260
+ else
261
+ begin
262
+ defaults = @site.frontmatter_defaults.all(relative_path, collection.label.to_sym)
263
+ merge_data!(defaults, source: "front matter defaults") unless defaults.empty?
264
+
265
+ self.content = File.read(path, Utils.merged_file_read_opts(site, opts))
266
+ if content =~ YAML_FRONT_MATTER_REGEXP
267
+ self.content = $POSTMATCH
268
+ data_file = SafeYAML.load(Regexp.last_match(1))
269
+ merge_data!(data_file, source: "YAML front matter") if data_file
270
+ end
271
+
272
+ post_read
273
+ rescue SyntaxError => e
274
+ Jekyll.logger.error "Error:", "YAML Exception reading #{path}: #{e.message}"
275
+ rescue Exception => e
276
+ if e.is_a? Jekyll::Errors::FatalException
277
+ raise e
278
+ end
279
+ Jekyll.logger.error "Error:", "could not read file #{path}: #{e.message}"
280
+ end
281
+ end
282
+ end
283
+
284
+ def post_read
285
+ if relative_path =~ DATE_FILENAME_MATCHER
286
+ date, slug, ext = $2, $3, $4
287
+ if !data['date'] || data['date'].to_i == site.time.to_i
288
+ merge_data!({"date" => date}, source: "filename")
289
+ end
290
+ elsif relative_path =~ DATELESS_FILENAME_MATCHER
291
+ slug, ext = $2, $3
292
+ end
293
+
294
+ # Try to ensure the user gets a title.
295
+ data["title"] ||= Utils.titleize_slug(slug)
296
+ # Only overwrite slug & ext if they aren't specified.
297
+ data['slug'] ||= slug
298
+ data['ext'] ||= ext
299
+
300
+ populate_categories
301
+ populate_tags
302
+ generate_excerpt
303
+ end
304
+
305
+ # Add superdirectories of the special_dir to categories.
306
+ # In the case of es/_posts, 'es' is added as a category.
307
+ # In the case of _posts/es, 'es' is NOT added as a category.
308
+ #
309
+ # Returns nothing.
310
+ def categories_from_path(special_dir)
311
+ superdirs = relative_path.sub(/#{special_dir}(.*)/, '').split(File::SEPARATOR).reject do |c|
312
+ c.empty? || c.eql?(special_dir) || c.eql?(basename)
313
+ end
314
+ merge_data!({ 'categories' => superdirs }, source: "file path")
315
+ end
316
+
317
+ def populate_categories
318
+ merge_data!({
319
+ 'categories' => (
320
+ Array(data['categories']) + Utils.pluralized_array_from_hash(data, 'category', 'categories')
321
+ ).map(&:to_s).flatten.uniq
322
+ })
323
+ end
324
+
325
+ def populate_tags
326
+ merge_data!({
327
+ "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten
328
+ })
329
+ end
330
+
331
+ # Create a Liquid-understandable version of this Document.
332
+ #
333
+ # Returns a Hash representing this Document's data.
334
+ def to_liquid
335
+ @to_liquid ||= Drops::DocumentDrop.new(self)
336
+ end
337
+
338
+ # The inspect string for this document.
339
+ # Includes the relative path and the collection label.
340
+ #
341
+ # Returns the inspect string for this document.
342
+ def inspect
343
+ "#<Jekyll::Document #{relative_path} collection=#{collection.label}>"
344
+ end
345
+
346
+ # The string representation for this document.
347
+ #
348
+ # Returns the content of the document
349
+ def to_s
350
+ output || content || 'NO CONTENT'
351
+ end
352
+
353
+ # Compare this document against another document.
354
+ # Comparison is a comparison between the 2 paths of the documents.
355
+ #
356
+ # Returns -1, 0, +1 or nil depending on whether this doc's path is less than,
357
+ # equal or greater than the other doc's path. See String#<=> for more details.
358
+ def <=>(other)
359
+ return nil unless other.respond_to?(:data)
360
+ cmp = data['date'] <=> other.data['date']
361
+ cmp = path <=> other.path if cmp.nil? || cmp == 0
362
+ cmp
363
+ end
364
+
365
+ # Determine whether this document should be written.
366
+ # Based on the Collection to which it belongs.
367
+ #
368
+ # True if the document has a collection and if that collection's #write?
369
+ # method returns true, otherwise false.
370
+ def write?
371
+ collection && collection.write?
372
+ end
373
+
374
+ # The Document excerpt_separator, from the YAML Front-Matter or site
375
+ # default excerpt_separator value
376
+ #
377
+ # Returns the document excerpt_separator
378
+ def excerpt_separator
379
+ (data['excerpt_separator'] || site.config['excerpt_separator']).to_s
380
+ end
381
+
382
+ # Whether to generate an excerpt
383
+ #
384
+ # Returns true if the excerpt separator is configured.
385
+ def generate_excerpt?
386
+ !excerpt_separator.empty?
387
+ end
388
+
389
+ def next_doc
390
+ pos = collection.docs.index { |post| post.equal?(self) }
391
+ if pos && pos < collection.docs.length - 1
392
+ collection.docs[pos + 1]
393
+ else
394
+ nil
395
+ end
396
+ end
397
+
398
+ def previous_doc
399
+ pos = collection.docs.index { |post| post.equal?(self) }
400
+ if pos && pos > 0
401
+ collection.docs[pos - 1]
402
+ else
403
+ nil
404
+ end
405
+ end
406
+
407
+ def trigger_hooks(hook_name, *args)
408
+ Jekyll::Hooks.trigger collection.label.to_sym, hook_name, self, *args if collection
409
+ Jekyll::Hooks.trigger :documents, hook_name, self, *args
410
+ end
411
+
412
+ def id
413
+ @id ||= File.join(File.dirname(url), (data['slug'] || basename_without_ext).to_s)
414
+ end
415
+
416
+ # Calculate related posts.
417
+ #
418
+ # Returns an Array of related Posts.
419
+ def related_posts
420
+ Jekyll::RelatedPosts.new(self).build
421
+ end
422
+
423
+ # Override of normal respond_to? to match method_missing's logic for
424
+ # looking in @data.
425
+ def respond_to?(method, include_private = false)
426
+ data.key?(method.to_s) || super
427
+ end
428
+
429
+ # Override of method_missing to check in @data for the key.
430
+ def method_missing(method, *args, &blck)
431
+ if data.key?(method.to_s)
432
+ Jekyll.logger.warn "Deprecation:", "Document##{method} is now a key in the #data hash."
433
+ Jekyll.logger.warn "", "Called by #{caller.first}."
434
+ data[method.to_s]
435
+ else
436
+ super
437
+ end
438
+ end
439
+
440
+ private # :nodoc:
441
+ def generate_excerpt
442
+ if generate_excerpt?
443
+ data["excerpt"] ||= Jekyll::Excerpt.new(self)
444
+ end
445
+ end
446
+ end
447
+ end