blackboard 3.1.6 → 3.1.7

Sign up to get free protection for your applications and to get access to all the features.
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