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,88 @@
1
+ module Jekyll
2
+ module Tags
3
+ class PostComparer
4
+ MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)$/
5
+
6
+ attr_reader :path, :date, :slug, :name
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ all, @path, @date, @slug = *name.sub(/^\//, "").match(MATCHER)
11
+ raise ArgumentError.new("'#{name}' does not contain valid date and/or title.") unless all
12
+
13
+ @name_regex = /^#{path}#{date}-#{slug}\.[^.]+/
14
+ end
15
+
16
+ def ==(other)
17
+ other.basename.match(@name_regex)
18
+ end
19
+
20
+ def deprecated_equality(other)
21
+ date = Utils.parse_date(name, "'#{name}' does not contain valid date and/or title.")
22
+ slug == post_slug(other) &&
23
+ date.year == other.date.year &&
24
+ date.month == other.date.month &&
25
+ date.day == other.date.day
26
+ end
27
+
28
+ private
29
+ # Construct the directory-aware post slug for a Jekyll::Post
30
+ #
31
+ # other - the Jekyll::Post
32
+ #
33
+ # Returns the post slug with the subdirectory (relative to _posts)
34
+ def post_slug(other)
35
+ path = other.basename.split("/")[0...-1].join("/")
36
+ if path.nil? || path == ""
37
+ other.data['slug']
38
+ else
39
+ path + '/' + other.data['slug']
40
+ end
41
+ end
42
+ end
43
+
44
+ class PostUrl < Liquid::Tag
45
+ def initialize(tag_name, post, tokens)
46
+ super
47
+ @orig_post = post.strip
48
+ begin
49
+ @post = PostComparer.new(@orig_post)
50
+ rescue
51
+ raise ArgumentError.new <<-eos
52
+ Could not parse name of post "#{@orig_post}" in tag 'post_url'.
53
+
54
+ Make sure the post exists and the name is correct.
55
+ eos
56
+ end
57
+ end
58
+
59
+ def render(context)
60
+ site = context.registers[:site]
61
+
62
+ site.posts.docs.each do |p|
63
+ return p.url if @post == p
64
+ end
65
+
66
+ # New matching method did not match, fall back to old method
67
+ # with deprecation warning if this matches
68
+
69
+ site.posts.docs.each do |p|
70
+ next unless @post.deprecated_equality p
71
+ Jekyll::Deprecator.deprecation_message "A call to '{{ post_url #{@post.name} }}' did not match " \
72
+ "a post using the new matching method of checking name " \
73
+ "(path-date-slug) equality. Please make sure that you " \
74
+ "change this tag to match the post's name exactly."
75
+ return p.url
76
+ end
77
+
78
+ raise ArgumentError.new <<-eos
79
+ Could not find post "#{@orig_post}" in tag 'post_url'.
80
+
81
+ Make sure the post exists and the name is correct.
82
+ eos
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ Liquid::Template.register_tag('post_url', Jekyll::Tags::PostUrl)
data/lib/jekyll/url.rb ADDED
@@ -0,0 +1,136 @@
1
+ require 'uri'
2
+
3
+ # Public: Methods that generate a URL for a resource such as a Post or a Page.
4
+ #
5
+ # Examples
6
+ #
7
+ # URL.new({
8
+ # :template => /:categories/:title.html",
9
+ # :placeholders => {:categories => "ruby", :title => "something"}
10
+ # }).to_s
11
+ #
12
+ module Jekyll
13
+ class URL
14
+ # options - One of :permalink or :template must be supplied.
15
+ # :template - The String used as template for URL generation,
16
+ # for example "/:path/:basename:output_ext", where
17
+ # a placeholder is prefixed with a colon.
18
+ # :placeholders - A hash containing the placeholders which will be
19
+ # replaced when used inside the template. E.g.
20
+ # { "year" => Time.now.strftime("%Y") } would replace
21
+ # the placeholder ":year" with the current year.
22
+ # :permalink - If supplied, no URL will be generated from the
23
+ # template. Instead, the given permalink will be
24
+ # used as URL.
25
+ def initialize(options)
26
+ @template = options[:template]
27
+ @placeholders = options[:placeholders] || {}
28
+ @permalink = options[:permalink]
29
+
30
+ if (@template || @permalink).nil?
31
+ raise ArgumentError, "One of :template or :permalink must be supplied."
32
+ end
33
+ end
34
+
35
+ # The generated relative URL of the resource
36
+ #
37
+ # Returns the String URL
38
+ def to_s
39
+ sanitize_url(generated_permalink || generated_url)
40
+ end
41
+
42
+ # Generates a URL from the permalink
43
+ #
44
+ # Returns the _unsanitized String URL
45
+ def generated_permalink
46
+ (@generated_permalink ||= generate_url(@permalink)) if @permalink
47
+ end
48
+
49
+ # Generates a URL from the template
50
+ #
51
+ # Returns the unsanitized String URL
52
+ def generated_url
53
+ @generated_url ||= generate_url(@template)
54
+ end
55
+
56
+ # Internal: Generate the URL by replacing all placeholders with their
57
+ # respective values in the given template
58
+ #
59
+ # Returns the unsanitized String URL
60
+ def generate_url(template)
61
+ if @placeholders.is_a? Drops::UrlDrop
62
+ generate_url_from_drop(template)
63
+ else
64
+ generate_url_from_hash(template)
65
+ end
66
+ end
67
+
68
+ def generate_url_from_hash(template)
69
+ @placeholders.inject(template) do |result, token|
70
+ break result if result.index(':').nil?
71
+ if token.last.nil?
72
+ # Remove leading '/' to avoid generating urls with `//`
73
+ result.gsub(/\/:#{token.first}/, '')
74
+ else
75
+ result.gsub(/:#{token.first}/, self.class.escape_path(token.last))
76
+ end
77
+ end
78
+ end
79
+
80
+ def generate_url_from_drop(template)
81
+ template.gsub(/:([a-z_]+)/.freeze) do |match|
82
+ replacement = @placeholders.public_send(match.sub(':'.freeze, ''.freeze))
83
+ if replacement.nil?
84
+ ''.freeze
85
+ else
86
+ self.class.escape_path(replacement)
87
+ end
88
+ end.gsub(/\/\//.freeze, '/'.freeze)
89
+ end
90
+
91
+ # Returns a sanitized String URL, stripping "../../" and multiples of "/",
92
+ # as well as the beginning "/" so we can enforce and ensure it.
93
+
94
+ def sanitize_url(str)
95
+ "/" + str.gsub(/\/{2,}/, "/").gsub(/\.+\/|\A\/+/, "")
96
+ end
97
+
98
+ # Escapes a path to be a valid URL path segment
99
+ #
100
+ # path - The path to be escaped.
101
+ #
102
+ # Examples:
103
+ #
104
+ # URL.escape_path("/a b")
105
+ # # => "/a%20b"
106
+ #
107
+ # Returns the escaped path.
108
+ def self.escape_path(path)
109
+ # Because URI.escape doesn't escape '?', '[' and ']' by default,
110
+ # specify unsafe string (except unreserved, sub-delims, ":", "@" and "/").
111
+ #
112
+ # URI path segment is defined in RFC 3986 as follows:
113
+ # segment = *pchar
114
+ # pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
115
+ # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
116
+ # pct-encoded = "%" HEXDIG HEXDIG
117
+ # sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
118
+ # / "*" / "+" / "," / ";" / "="
119
+ URI.escape(path, /[^a-zA-Z\d\-._~!$&'()*+,;=:@\/]/).encode('utf-8')
120
+ end
121
+
122
+ # Unescapes a URL path segment
123
+ #
124
+ # path - The path to be unescaped.
125
+ #
126
+ # Examples:
127
+ #
128
+ # URL.unescape_path("/a%20b")
129
+ # # => "/a b"
130
+ #
131
+ # Returns the unescaped path.
132
+ def self.unescape_path(path)
133
+ URI.unescape(path.encode('utf-8'))
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,300 @@
1
+ module Jekyll
2
+ module Utils
3
+ extend self
4
+ autoload :Platforms, 'jekyll/utils/platforms'
5
+ autoload :Ansi, "jekyll/utils/ansi"
6
+
7
+ # Constants for use in #slugify
8
+ SLUGIFY_MODES = %w(raw default pretty)
9
+ SLUGIFY_RAW_REGEXP = Regexp.new('\\s+').freeze
10
+ SLUGIFY_DEFAULT_REGEXP = Regexp.new('[^[:alnum:]]+').freeze
11
+ SLUGIFY_PRETTY_REGEXP = Regexp.new("[^[:alnum:]._~!$&'()+,;=@]+").freeze
12
+
13
+ # Takes an indented string and removes the preceding spaces on each line
14
+
15
+ def strip_heredoc(str)
16
+ str.gsub(/^[ \t]{#{(str.scan(/^[ \t]*(?=\S)/).min || "").size}}/, "")
17
+ end
18
+
19
+ # Takes a slug and turns it into a simple title.
20
+
21
+ def titleize_slug(slug)
22
+ slug.split("-").map! do |val|
23
+ val.capitalize
24
+ end.join(" ")
25
+ end
26
+
27
+ # Non-destructive version of deep_merge_hashes! See that method.
28
+ #
29
+ # Returns the merged hashes.
30
+ def deep_merge_hashes(master_hash, other_hash)
31
+ deep_merge_hashes!(master_hash.dup, other_hash)
32
+ end
33
+
34
+ # Merges a master hash with another hash, recursively.
35
+ #
36
+ # master_hash - the "parent" hash whose values will be overridden
37
+ # other_hash - the other hash whose values will be persisted after the merge
38
+ #
39
+ # This code was lovingly stolen from some random gem:
40
+ # http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
41
+ #
42
+ # Thanks to whoever made it.
43
+ def deep_merge_hashes!(target, overwrite)
44
+ target.merge!(overwrite) do |key, old_val, new_val|
45
+ if new_val.nil?
46
+ old_val
47
+ else
48
+ mergable?(old_val) && mergable?(new_val) ? deep_merge_hashes(old_val, new_val) : new_val
49
+ end
50
+ end
51
+
52
+ if target.respond_to?(:default_proc) && overwrite.respond_to?(:default_proc) && target.default_proc.nil?
53
+ target.default_proc = overwrite.default_proc
54
+ end
55
+
56
+ target.each do |key, val|
57
+ target[key] = val.dup if val.frozen? && duplicable?(val)
58
+ end
59
+
60
+ target
61
+ end
62
+
63
+ def mergable?(value)
64
+ value.is_a?(Hash) || value.is_a?(Drops::Drop)
65
+ end
66
+
67
+ def duplicable?(obj)
68
+ case obj
69
+ when nil, false, true, Symbol, Numeric
70
+ false
71
+ else
72
+ true
73
+ end
74
+ end
75
+
76
+ # Read array from the supplied hash favouring the singular key
77
+ # and then the plural key, and handling any nil entries.
78
+ #
79
+ # hash - the hash to read from
80
+ # singular_key - the singular key
81
+ # plural_key - the plural key
82
+ #
83
+ # Returns an array
84
+ def pluralized_array_from_hash(hash, singular_key, plural_key)
85
+ [].tap do |array|
86
+ array << (value_from_singular_key(hash, singular_key) || value_from_plural_key(hash, plural_key))
87
+ end.flatten.compact
88
+ end
89
+
90
+ def value_from_singular_key(hash, key)
91
+ hash[key] if hash.key?(key) || (hash.default_proc && hash[key])
92
+ end
93
+
94
+ def value_from_plural_key(hash, key)
95
+ if hash.key?(key) || (hash.default_proc && hash[key])
96
+ val = hash[key]
97
+ case val
98
+ when String
99
+ val.split
100
+ when Array
101
+ val.compact
102
+ end
103
+ end
104
+ end
105
+
106
+ def transform_keys(hash)
107
+ result = {}
108
+ hash.each_key do |key|
109
+ result[yield(key)] = hash[key]
110
+ end
111
+ result
112
+ end
113
+
114
+ # Apply #to_sym to all keys in the hash
115
+ #
116
+ # hash - the hash to which to apply this transformation
117
+ #
118
+ # Returns a new hash with symbolized keys
119
+ def symbolize_hash_keys(hash)
120
+ transform_keys(hash) { |key| key.to_sym rescue key }
121
+ end
122
+
123
+ # Apply #to_s to all keys in the Hash
124
+ #
125
+ # hash - the hash to which to apply this transformation
126
+ #
127
+ # Returns a new hash with stringified keys
128
+ def stringify_hash_keys(hash)
129
+ transform_keys(hash) { |key| key.to_s rescue key }
130
+ end
131
+
132
+ # Parse a date/time and throw an error if invalid
133
+ #
134
+ # input - the date/time to parse
135
+ # msg - (optional) the error message to show the user
136
+ #
137
+ # Returns the parsed date if successful, throws a FatalException
138
+ # if not
139
+ def parse_date(input, msg = "Input could not be parsed.")
140
+ Time.parse(input).localtime
141
+ rescue ArgumentError
142
+ raise Errors::FatalException.new("Invalid date '#{input}': " + msg)
143
+ end
144
+
145
+ # Determines whether a given file has
146
+ #
147
+ # Returns true if the YAML front matter is present.
148
+ def has_yaml_header?(file)
149
+ !!(File.open(file, 'rb') { |f| f.readline } =~ /\A---\s*\r?\n/)
150
+ rescue EOFError
151
+ false
152
+ end
153
+
154
+ # Slugify a filename or title.
155
+ #
156
+ # string - the filename or title to slugify
157
+ # mode - how string is slugified
158
+ # cased - whether to replace all uppercase letters with their
159
+ # lowercase counterparts
160
+ #
161
+ # When mode is "none", return the given string.
162
+ #
163
+ # When mode is "raw", return the given string,
164
+ # with every sequence of spaces characters replaced with a hyphen.
165
+ #
166
+ # When mode is "default" or nil, non-alphabetic characters are
167
+ # replaced with a hyphen too.
168
+ #
169
+ # When mode is "pretty", some non-alphabetic characters (._~!$&'()+,;=@)
170
+ # are not replaced with hyphen.
171
+ #
172
+ # If cased is true, all uppercase letters in the result string are
173
+ # replaced with their lowercase counterparts.
174
+ #
175
+ # Examples:
176
+ # slugify("The _config.yml file")
177
+ # # => "the-config-yml-file"
178
+ #
179
+ # slugify("The _config.yml file", "pretty")
180
+ # # => "the-_config.yml-file"
181
+ #
182
+ # slugify("The _config.yml file", "pretty", true)
183
+ # # => "The-_config.yml file"
184
+ #
185
+ # Returns the slugified string.
186
+ def slugify(string, mode: nil, cased: false)
187
+ mode ||= 'default'
188
+ return nil if string.nil?
189
+
190
+ unless SLUGIFY_MODES.include?(mode)
191
+ return cased ? string : string.downcase
192
+ end
193
+
194
+ # Replace each character sequence with a hyphen
195
+ re =
196
+ case mode
197
+ when 'raw'
198
+ SLUGIFY_RAW_REGEXP
199
+ when 'default'
200
+ SLUGIFY_DEFAULT_REGEXP
201
+ when 'pretty'
202
+ # "._~!$&'()+,;=@" is human readable (not URI-escaped) in URL
203
+ # and is allowed in both extN and NTFS.
204
+ SLUGIFY_PRETTY_REGEXP
205
+ end
206
+
207
+ # Strip according to the mode
208
+ slug = string.gsub(re, '-')
209
+
210
+ # Remove leading/trailing hyphen
211
+ slug.gsub!(/^\-|\-$/i, '')
212
+
213
+ slug.downcase! unless cased
214
+ slug
215
+ end
216
+
217
+ # Add an appropriate suffix to template so that it matches the specified
218
+ # permalink style.
219
+ #
220
+ # template - permalink template without trailing slash or file extension
221
+ # permalink_style - permalink style, either built-in or custom
222
+ #
223
+ # The returned permalink template will use the same ending style as
224
+ # specified in permalink_style. For example, if permalink_style contains a
225
+ # trailing slash (or is :pretty, which indirectly has a trailing slash),
226
+ # then so will the returned template. If permalink_style has a trailing
227
+ # ":output_ext" (or is :none, :date, or :ordinal) then so will the returned
228
+ # template. Otherwise, template will be returned without modification.
229
+ #
230
+ # Examples:
231
+ # add_permalink_suffix("/:basename", :pretty)
232
+ # # => "/:basename/"
233
+ #
234
+ # add_permalink_suffix("/:basename", :date)
235
+ # # => "/:basename:output_ext"
236
+ #
237
+ # add_permalink_suffix("/:basename", "/:year/:month/:title/")
238
+ # # => "/:basename/"
239
+ #
240
+ # add_permalink_suffix("/:basename", "/:year/:month/:title")
241
+ # # => "/:basename"
242
+ #
243
+ # Returns the updated permalink template
244
+ def add_permalink_suffix(template, permalink_style)
245
+ case permalink_style
246
+ when :pretty
247
+ template << "/"
248
+ when :date, :ordinal, :none
249
+ template << ":output_ext"
250
+ else
251
+ template << "/" if permalink_style.to_s.end_with?("/")
252
+ template << ":output_ext" if permalink_style.to_s.end_with?(":output_ext")
253
+ end
254
+ template
255
+ end
256
+
257
+ # Work the same way as Dir.glob but seperating the input into two parts
258
+ # ('dir' + '/' + 'pattern') to make sure the first part('dir') does not act
259
+ # as a pattern.
260
+ #
261
+ # For example, Dir.glob("path[/*") always returns an empty array,
262
+ # because the method fails to find the closing pattern to '[' which is ']'
263
+ #
264
+ # Examples:
265
+ # safe_glob("path[", "*")
266
+ # # => ["path[/file1", "path[/file2"]
267
+ #
268
+ # safe_glob("path", "*", File::FNM_DOTMATCH)
269
+ # # => ["path/.", "path/..", "path/file1"]
270
+ #
271
+ # safe_glob("path", ["**", "*"])
272
+ # # => ["path[/file1", "path[/folder/file2"]
273
+ #
274
+ # dir - the dir where glob will be executed under
275
+ # (the dir will be included to each result)
276
+ # patterns - the patterns (or the pattern) which will be applied under the dir
277
+ # flags - the flags which will be applied to the pattern
278
+ #
279
+ # Returns matched pathes
280
+ def safe_glob(dir, patterns, flags = 0)
281
+ return [] unless Dir.exist?(dir)
282
+ pattern = File.join(Array patterns)
283
+ return [dir] if pattern.empty?
284
+ Dir.chdir(dir) do
285
+ Dir.glob(pattern, flags).map { |f| File.join(dir, f) }
286
+ end
287
+ end
288
+
289
+ # Returns merged option hash for File.read of self.site (if exists)
290
+ # and a given param
291
+ def merged_file_read_opts(site, opts)
292
+ merged = (site ? site.file_read_opts : {}).merge(opts)
293
+ if merged["encoding"] && !merged["encoding"].start_with?("bom|")
294
+ merged["encoding"].insert(0, "bom|")
295
+ end
296
+ merged
297
+ end
298
+
299
+ end
300
+ end