jekyll 1.2.1 → 1.3.0.rc
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/History.markdown +58 -0
- data/README.markdown +2 -1
- data/Rakefile +8 -1
- data/bin/jekyll +14 -17
- data/features/create_sites.feature +11 -0
- data/features/data.feature +65 -0
- data/features/include_tag.feature +13 -0
- data/features/site_configuration.feature +29 -0
- data/features/step_definitions/jekyll_steps.rb +27 -12
- data/features/support/env.rb +36 -0
- data/jekyll.gemspec +22 -10
- data/lib/jekyll.rb +2 -1
- data/lib/jekyll/cleaner.rb +8 -8
- data/lib/jekyll/commands/build.rb +14 -8
- data/lib/jekyll/commands/serve.rb +2 -0
- data/lib/jekyll/configuration.rb +5 -1
- data/lib/jekyll/converters/markdown/kramdown_parser.rb +1 -1
- data/lib/jekyll/convertible.rb +17 -6
- data/lib/jekyll/core_ext.rb +15 -0
- data/lib/jekyll/filters.rb +10 -0
- data/lib/jekyll/post.rb +2 -2
- data/lib/jekyll/site.rb +68 -19
- data/lib/jekyll/tags/gist.rb +9 -1
- data/lib/jekyll/tags/highlight.rb +1 -1
- data/lib/jekyll/tags/include.rb +72 -28
- data/lib/jekyll/tags/post_url.rb +4 -2
- data/lib/site_template/css/main.css +15 -15
- data/site/_includes/docs_contents.html +1 -1
- data/site/_includes/docs_contents_mobile.html +1 -1
- data/site/_posts/2013-10-28-jekyll-1-3-0-rc1-released.markdown +19 -0
- data/site/docs/configuration.md +18 -0
- data/site/docs/datafiles.md +63 -0
- data/site/docs/installation.md +10 -0
- data/site/docs/migrations.md +12 -3
- data/site/docs/pagination.md +16 -37
- data/site/docs/plugins.md +66 -6
- data/site/docs/structure.md +17 -0
- data/site/docs/templates.md +31 -7
- data/site/docs/upgrading.md +3 -3
- data/site/docs/usage.md +1 -1
- data/site/docs/variables.md +2 -2
- data/test/helper.rb +4 -1
- data/test/source/_data/languages.yml +2 -0
- data/test/source/_data/members.yaml +7 -0
- data/test/source/_data/products.yml +4 -0
- data/test/source/_layouts/post/simple.html +1 -0
- data/test/source/products.yml +4 -0
- data/test/test_convertible.rb +1 -1
- data/test/test_filters.rb +11 -0
- data/test/test_kramdown.rb +32 -5
- data/test/test_site.rb +58 -1
- data/test/test_tags.rb +21 -23
- metadata +71 -27
data/lib/jekyll.rb
CHANGED
@@ -20,6 +20,7 @@ require 'fileutils'
|
|
20
20
|
require 'time'
|
21
21
|
require 'safe_yaml'
|
22
22
|
require 'English'
|
23
|
+
require 'pathname'
|
23
24
|
|
24
25
|
# 3rd party
|
25
26
|
require 'liquid'
|
@@ -60,7 +61,7 @@ require_all 'jekyll/tags'
|
|
60
61
|
SafeYAML::OPTIONS[:suppress_warnings] = true
|
61
62
|
|
62
63
|
module Jekyll
|
63
|
-
VERSION = '1.
|
64
|
+
VERSION = '1.3.0.rc'
|
64
65
|
|
65
66
|
# Public: Generate a Jekyll configuration Hash by merging the default
|
66
67
|
# options with anything in _config.yml, and adding the given options on top.
|
data/lib/jekyll/cleaner.rb
CHANGED
@@ -2,7 +2,7 @@ require 'set'
|
|
2
2
|
|
3
3
|
module Jekyll
|
4
4
|
class Site
|
5
|
-
# Handles the cleanup of a site's destination before
|
5
|
+
# Handles the cleanup of a site's destination before it is built.
|
6
6
|
class Cleaner
|
7
7
|
def initialize(site)
|
8
8
|
@site = site
|
@@ -15,14 +15,14 @@ module Jekyll
|
|
15
15
|
|
16
16
|
private
|
17
17
|
|
18
|
-
# Private: The list of files and directories to be deleted during
|
18
|
+
# Private: The list of files and directories to be deleted during cleanup process
|
19
19
|
#
|
20
|
-
# Returns an Array
|
20
|
+
# Returns an Array of the file and directory paths
|
21
21
|
def obsolete_files
|
22
22
|
(existing_files - new_files - new_dirs + replaced_files).to_a
|
23
23
|
end
|
24
24
|
|
25
|
-
# Private: The list of existing files,
|
25
|
+
# Private: The list of existing files, apart from those included in keep_files and hidden files.
|
26
26
|
#
|
27
27
|
# Returns a Set with the file paths
|
28
28
|
def existing_files
|
@@ -33,7 +33,7 @@ module Jekyll
|
|
33
33
|
files
|
34
34
|
end
|
35
35
|
|
36
|
-
# Private: The list of files to be created when
|
36
|
+
# Private: The list of files to be created when site is built.
|
37
37
|
#
|
38
38
|
# Returns a Set with the file paths
|
39
39
|
def new_files
|
@@ -42,7 +42,7 @@ module Jekyll
|
|
42
42
|
files
|
43
43
|
end
|
44
44
|
|
45
|
-
# Private: The list of directories to be created when
|
45
|
+
# Private: The list of directories to be created when site is built.
|
46
46
|
# These are the parent directories of the files in #new_files.
|
47
47
|
#
|
48
48
|
# Returns a Set with the directory paths
|
@@ -57,7 +57,7 @@ module Jekyll
|
|
57
57
|
new_dirs.select { |dir| File.file?(dir) }.to_set
|
58
58
|
end
|
59
59
|
|
60
|
-
# Private:
|
60
|
+
# Private: Creates a regular expression from the config's keep_files array
|
61
61
|
#
|
62
62
|
# Examples
|
63
63
|
# ['.git','.svn'] creates the following regex: /\/(\.git|\/.svn)/
|
@@ -70,4 +70,4 @@ module Jekyll
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
end
|
73
|
-
end
|
73
|
+
end
|
@@ -31,27 +31,33 @@ module Jekyll
|
|
31
31
|
#
|
32
32
|
# Returns nothing.
|
33
33
|
def self.watch(site, options)
|
34
|
-
require '
|
34
|
+
require 'listen'
|
35
35
|
|
36
36
|
source = options['source']
|
37
37
|
destination = options['destination']
|
38
38
|
|
39
|
-
|
39
|
+
begin
|
40
|
+
dest = Pathname.new(destination).relative_path_from(Pathname.new(source)).to_path
|
41
|
+
ignored = Regexp.new(Regexp.escape(dest))
|
42
|
+
rescue ArgumentError
|
43
|
+
# Destination is outside the source, no need to ignore it.
|
44
|
+
ignored = nil
|
45
|
+
end
|
40
46
|
|
41
|
-
|
42
|
-
dw.interval = 1
|
47
|
+
Jekyll.logger.info "Auto-regeneration:", "enabled"
|
43
48
|
|
44
|
-
|
49
|
+
listener = Listen.to(source, :ignore => ignored) do |modified, added, removed|
|
45
50
|
t = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
46
|
-
|
51
|
+
n = modified.length + added.length + removed.length
|
52
|
+
print Jekyll.logger.formatted_topic("Regenerating:") + "#{n} files at #{t} "
|
47
53
|
self.process_site(site)
|
48
54
|
puts "...done."
|
49
55
|
end
|
50
|
-
|
51
|
-
dw.start
|
56
|
+
listener.start
|
52
57
|
|
53
58
|
unless options['serving']
|
54
59
|
trap("INT") do
|
60
|
+
listener.stop
|
55
61
|
puts " Halting auto-regeneration."
|
56
62
|
exit 0
|
57
63
|
end
|
@@ -18,6 +18,8 @@ module Jekyll
|
|
18
18
|
|
19
19
|
s.mount(options['baseurl'], HTTPServlet::FileHandler, destination, fh_option)
|
20
20
|
|
21
|
+
Jekyll.logger.info "Server address:", "http://#{s.config[:BindAddress]}:#{s.config[:Port]}"
|
22
|
+
|
21
23
|
if options['detach'] # detach the server
|
22
24
|
pid = Process.fork { s.start }
|
23
25
|
Process.detach(pid)
|
data/lib/jekyll/configuration.rb
CHANGED
@@ -10,10 +10,14 @@ module Jekyll
|
|
10
10
|
'destination' => File.join(Dir.pwd, '_site'),
|
11
11
|
'plugins' => '_plugins',
|
12
12
|
'layouts' => '_layouts',
|
13
|
+
'data_source' => '_data',
|
13
14
|
'keep_files' => ['.git','.svn'],
|
15
|
+
'gems' => [],
|
14
16
|
|
15
17
|
'timezone' => nil, # use the local timezone
|
16
18
|
|
19
|
+
'encoding' => nil, # use the system encoding
|
20
|
+
|
17
21
|
'safe' => false,
|
18
22
|
'detach' => false, # default to not detaching the server
|
19
23
|
'show_drafts' => nil,
|
@@ -23,7 +27,7 @@ module Jekyll
|
|
23
27
|
'pygments' => true,
|
24
28
|
|
25
29
|
'relative_permalinks' => true, # backwards-compatibility with < 1.0
|
26
|
-
# will be set to false once
|
30
|
+
# will be set to false once 2.0 hits
|
27
31
|
|
28
32
|
'markdown' => 'maruku',
|
29
33
|
'permalink' => 'date',
|
@@ -16,7 +16,7 @@ module Jekyll
|
|
16
16
|
if @config['kramdown']['use_coderay']
|
17
17
|
%w[wrap line_numbers line_numbers_start tab_width bold_every css default_lang].each do |opt|
|
18
18
|
key = "coderay_#{opt}"
|
19
|
-
@config['kramdown'][key
|
19
|
+
@config['kramdown'][key] = @config['kramdown']['coderay'][key] unless @config['kramdown'].has_key?(key)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
data/lib/jekyll/convertible.rb
CHANGED
@@ -20,16 +20,23 @@ module Jekyll
|
|
20
20
|
self.content || ''
|
21
21
|
end
|
22
22
|
|
23
|
+
# Returns merged optin hash for File.read of self.site (if exists)
|
24
|
+
# and a given param
|
25
|
+
def merged_file_read_opts(opts)
|
26
|
+
(self.site ? self.site.file_read_opts : {}).merge(opts)
|
27
|
+
end
|
28
|
+
|
23
29
|
# Read the YAML frontmatter.
|
24
30
|
#
|
25
31
|
# base - The String path to the dir containing the file.
|
26
32
|
# name - The String filename of the file.
|
33
|
+
# opts - optional parameter to File.read, default at site configs
|
27
34
|
#
|
28
35
|
# Returns nothing.
|
29
|
-
def read_yaml(base, name)
|
36
|
+
def read_yaml(base, name, opts = {})
|
30
37
|
begin
|
31
|
-
self.content = File.
|
32
|
-
|
38
|
+
self.content = File.read_with_options(File.join(base, name),
|
39
|
+
merged_file_read_opts(opts))
|
33
40
|
if self.content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
34
41
|
self.content = $POSTMATCH
|
35
42
|
self.data = YAML.safe_load($1)
|
@@ -77,10 +84,13 @@ module Jekyll
|
|
77
84
|
# info - the info for Liquid
|
78
85
|
#
|
79
86
|
# Returns the converted content
|
80
|
-
def render_liquid(content, payload, info)
|
87
|
+
def render_liquid(content, payload, info, path = nil)
|
81
88
|
Liquid::Template.parse(content).render!(payload, info)
|
89
|
+
rescue Tags::IncludeTagError => e
|
90
|
+
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{e.path}"
|
91
|
+
raise e
|
82
92
|
rescue Exception => e
|
83
|
-
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{self.path}"
|
93
|
+
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{path || self.path}"
|
84
94
|
raise e
|
85
95
|
end
|
86
96
|
|
@@ -111,7 +121,8 @@ module Jekyll
|
|
111
121
|
|
112
122
|
self.output = self.render_liquid(layout.content,
|
113
123
|
payload,
|
114
|
-
info
|
124
|
+
info,
|
125
|
+
File.join(self.site.config['layouts'], layout.name))
|
115
126
|
|
116
127
|
if layout = layouts[layout.data["layout"]]
|
117
128
|
if used.include?(layout)
|
data/lib/jekyll/core_ext.rb
CHANGED
@@ -69,3 +69,18 @@ module Enumerable
|
|
69
69
|
any? { |exp| File.fnmatch?(exp, e) }
|
70
70
|
end
|
71
71
|
end
|
72
|
+
|
73
|
+
# Ruby 1.8's File.read don't support option.
|
74
|
+
# read_with_options ignore optional parameter for 1.8,
|
75
|
+
# and act as alias for 1.9 or later.
|
76
|
+
class File
|
77
|
+
if RUBY_VERSION < '1.9'
|
78
|
+
def self.read_with_options(path, opts = {})
|
79
|
+
self.read(path)
|
80
|
+
end
|
81
|
+
else
|
82
|
+
def self.read_with_options(path, opts = {})
|
83
|
+
self.read(path, opts)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/jekyll/filters.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'uri'
|
2
|
+
require 'json'
|
2
3
|
|
3
4
|
module Jekyll
|
4
5
|
module Filters
|
@@ -148,6 +149,15 @@ module Jekyll
|
|
148
149
|
end
|
149
150
|
end
|
150
151
|
|
152
|
+
# Convert the input into json string
|
153
|
+
#
|
154
|
+
# input - The Array or Hash to be converted
|
155
|
+
#
|
156
|
+
# Returns the converted json string
|
157
|
+
def jsonify(input)
|
158
|
+
input.to_json
|
159
|
+
end
|
160
|
+
|
151
161
|
private
|
152
162
|
def time(input)
|
153
163
|
case input
|
data/lib/jekyll/post.rb
CHANGED
@@ -19,10 +19,10 @@ module Jekyll
|
|
19
19
|
]
|
20
20
|
|
21
21
|
# Attributes for Liquid templates
|
22
|
-
ATTRIBUTES_FOR_LIQUID = EXCERPT_ATTRIBUTES_FOR_LIQUID
|
22
|
+
ATTRIBUTES_FOR_LIQUID = EXCERPT_ATTRIBUTES_FOR_LIQUID + %w[
|
23
23
|
content
|
24
24
|
excerpt
|
25
|
-
]
|
25
|
+
]
|
26
26
|
|
27
27
|
# Post name validator. Post filenames must be like:
|
28
28
|
# 2008-11-05-my-awesome-post.textile
|
data/lib/jekyll/site.rb
CHANGED
@@ -3,7 +3,7 @@ module Jekyll
|
|
3
3
|
attr_accessor :config, :layouts, :posts, :pages, :static_files,
|
4
4
|
:categories, :exclude, :include, :source, :dest, :lsi, :pygments,
|
5
5
|
:permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts,
|
6
|
-
:show_drafts, :keep_files, :baseurl
|
6
|
+
:show_drafts, :keep_files, :baseurl, :data, :file_read_opts, :gems
|
7
7
|
|
8
8
|
attr_accessor :converters, :generators
|
9
9
|
|
@@ -13,7 +13,7 @@ module Jekyll
|
|
13
13
|
def initialize(config)
|
14
14
|
self.config = config.clone
|
15
15
|
|
16
|
-
%w[safe lsi pygments baseurl exclude include future show_drafts limit_posts keep_files].each do |opt|
|
16
|
+
%w[safe lsi pygments baseurl exclude include future show_drafts limit_posts keep_files gems].each do |opt|
|
17
17
|
self.send("#{opt}=", config[opt])
|
18
18
|
end
|
19
19
|
|
@@ -22,6 +22,9 @@ module Jekyll
|
|
22
22
|
self.plugins = plugins_path
|
23
23
|
self.permalink_style = config['permalink'].to_sym
|
24
24
|
|
25
|
+
self.file_read_opts = {}
|
26
|
+
self.file_read_opts[:encoding] = config['encoding'] if config['encoding']
|
27
|
+
|
25
28
|
self.reset
|
26
29
|
self.setup
|
27
30
|
end
|
@@ -53,6 +56,7 @@ module Jekyll
|
|
53
56
|
self.static_files = []
|
54
57
|
self.categories = Hash.new { |hash, key| hash[key] = [] }
|
55
58
|
self.tags = Hash.new { |hash, key| hash[key] = [] }
|
59
|
+
self.data = {}
|
56
60
|
|
57
61
|
if self.limit_posts < 0
|
58
62
|
raise ArgumentError, "limit_posts must be a non-negative number"
|
@@ -63,11 +67,7 @@ module Jekyll
|
|
63
67
|
#
|
64
68
|
# Returns nothing.
|
65
69
|
def setup
|
66
|
-
|
67
|
-
# parent to the source dir.
|
68
|
-
if self.source =~ /^#{self.dest}/
|
69
|
-
raise FatalException.new "Destination directory cannot be or contain the Source directory."
|
70
|
-
end
|
70
|
+
ensure_not_in_dest
|
71
71
|
|
72
72
|
# If safe mode is off, load in any Ruby files under the plugins
|
73
73
|
# directory.
|
@@ -77,12 +77,26 @@ module Jekyll
|
|
77
77
|
require f
|
78
78
|
end
|
79
79
|
end
|
80
|
+
self.gems.each do |gem|
|
81
|
+
require gem
|
82
|
+
end
|
80
83
|
end
|
81
84
|
|
82
85
|
self.converters = instantiate_subclasses(Jekyll::Converter)
|
83
86
|
self.generators = instantiate_subclasses(Jekyll::Generator)
|
84
87
|
end
|
85
88
|
|
89
|
+
# Check that the destination dir isn't the source dir or a directory
|
90
|
+
# parent to the source dir.
|
91
|
+
def ensure_not_in_dest
|
92
|
+
dest = Pathname.new(self.dest)
|
93
|
+
Pathname.new(self.source).ascend do |path|
|
94
|
+
if path == dest
|
95
|
+
raise FatalException.new "Destination directory cannot be or contain the Source directory."
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
86
100
|
# Internal: Setup the plugin search path
|
87
101
|
#
|
88
102
|
# Returns an Array of plugin search paths
|
@@ -100,6 +114,7 @@ module Jekyll
|
|
100
114
|
def read
|
101
115
|
self.read_layouts
|
102
116
|
self.read_directories
|
117
|
+
self.read_data(config['data_source'])
|
103
118
|
end
|
104
119
|
|
105
120
|
# Read all the files in <source>/<layouts> and create a new Layout object
|
@@ -110,7 +125,7 @@ module Jekyll
|
|
110
125
|
base = File.join(self.source, self.config['layouts'])
|
111
126
|
return unless File.exists?(base)
|
112
127
|
entries = []
|
113
|
-
Dir.chdir(base) { entries = filter_entries(Dir['
|
128
|
+
Dir.chdir(base) { entries = filter_entries(Dir['**/*.*']) }
|
114
129
|
|
115
130
|
entries.each do |f|
|
116
131
|
name = f.split(".")[0..-2].join(".")
|
@@ -187,6 +202,25 @@ module Jekyll
|
|
187
202
|
end
|
188
203
|
end
|
189
204
|
|
205
|
+
# Read and parse all yaml files under <source>/<dir>
|
206
|
+
#
|
207
|
+
# Returns nothing
|
208
|
+
def read_data(dir)
|
209
|
+
base = File.join(self.source, dir)
|
210
|
+
return unless File.directory?(base) && (!self.safe || !File.symlink?(base))
|
211
|
+
|
212
|
+
entries = Dir.chdir(base) { Dir['*.{yaml,yml}'] }
|
213
|
+
entries.delete_if { |e| File.directory?(File.join(base, e)) }
|
214
|
+
|
215
|
+
entries.each do |entry|
|
216
|
+
path = File.join(self.source, dir, entry)
|
217
|
+
next if File.symlink?(path) && self.safe
|
218
|
+
|
219
|
+
key = sanitize_filename(File.basename(entry, '.*'))
|
220
|
+
self.data[key] = YAML.safe_load_file(path)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
190
224
|
# Run each of the Generators.
|
191
225
|
#
|
192
226
|
# Returns nothing.
|
@@ -200,14 +234,11 @@ module Jekyll
|
|
200
234
|
#
|
201
235
|
# Returns nothing.
|
202
236
|
def render
|
203
|
-
|
204
|
-
self.posts.each do |post|
|
205
|
-
post.render(self.layouts, payload)
|
206
|
-
end
|
237
|
+
relative_permalinks_deprecation_method
|
207
238
|
|
208
|
-
|
209
|
-
|
210
|
-
|
239
|
+
payload = site_payload
|
240
|
+
[self.posts, self.pages].flatten.each do |page_or_post|
|
241
|
+
page_or_post.render(self.layouts, payload)
|
211
242
|
end
|
212
243
|
|
213
244
|
self.categories.values.map { |ps| ps.sort! { |a, b| b <=> a } }
|
@@ -252,6 +283,14 @@ module Jekyll
|
|
252
283
|
hash
|
253
284
|
end
|
254
285
|
|
286
|
+
# Prepare site data for site payload. The method maintains backward compatibility
|
287
|
+
# if the key 'data' is already used in _config.yml.
|
288
|
+
#
|
289
|
+
# Returns the Hash to be hooked to site.data.
|
290
|
+
def site_data
|
291
|
+
self.config['data'] || self.data
|
292
|
+
end
|
293
|
+
|
255
294
|
# The Hash payload containing site-wide data.
|
256
295
|
#
|
257
296
|
# Returns the Hash: { "site" => data } where data is a Hash with keys:
|
@@ -273,7 +312,8 @@ module Jekyll
|
|
273
312
|
"pages" => self.pages,
|
274
313
|
"html_pages" => self.pages.reject { |page| !page.html? },
|
275
314
|
"categories" => post_attr_hash('categories'),
|
276
|
-
"tags" => post_attr_hash('tags')
|
315
|
+
"tags" => post_attr_hash('tags'),
|
316
|
+
"data" => site_data})}
|
277
317
|
end
|
278
318
|
|
279
319
|
# Filter out any files/directories that are hidden or backup files (start
|
@@ -349,15 +389,14 @@ module Jekyll
|
|
349
389
|
end
|
350
390
|
|
351
391
|
def relative_permalinks_deprecation_method
|
352
|
-
if config['relative_permalinks'] &&
|
392
|
+
if config['relative_permalinks'] && has_relative_page?
|
353
393
|
$stderr.puts # Places newline after "Generating..."
|
354
|
-
Jekyll.logger.warn "Deprecation:", "Starting in
|
394
|
+
Jekyll.logger.warn "Deprecation:", "Starting in 2.0, permalinks for pages" +
|
355
395
|
" in subfolders must be relative to the" +
|
356
396
|
" site source directory, not the parent" +
|
357
397
|
" directory. Check http://jekyllrb.com/docs/upgrading/"+
|
358
398
|
" for more info."
|
359
399
|
$stderr.print Jekyll.logger.formatted_topic("") + "..." # for "done."
|
360
|
-
@deprecated_relative_permalinks = true
|
361
400
|
end
|
362
401
|
end
|
363
402
|
|
@@ -371,6 +410,10 @@ module Jekyll
|
|
371
410
|
|
372
411
|
private
|
373
412
|
|
413
|
+
def has_relative_page?
|
414
|
+
self.pages.any? { |page| page.uses_relative_permalinks }
|
415
|
+
end
|
416
|
+
|
374
417
|
def has_yaml_header?(file)
|
375
418
|
"---" == File.open(file) { |fd| fd.read(3) }
|
376
419
|
end
|
@@ -383,5 +426,11 @@ module Jekyll
|
|
383
426
|
def site_cleaner
|
384
427
|
@site_cleaner ||= Cleaner.new(self)
|
385
428
|
end
|
429
|
+
|
430
|
+
def sanitize_filename(name)
|
431
|
+
name = name.gsub(/[^\w\s_-]+/, '')
|
432
|
+
name = name.gsub(/(^|\b\s)\s+($|\s?\b)/, '\\1\\2')
|
433
|
+
name = name.gsub(/\s+/, '_')
|
434
|
+
end
|
386
435
|
end
|
387
436
|
end
|