jekyll 0.11.2 → 0.12.0

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.

@@ -27,7 +27,23 @@ module Jekyll
27
27
 
28
28
  def convert(content)
29
29
  setup
30
- RedCloth.new(content).to_html
30
+
31
+ # Shortcut if config doesn't contain RedCloth section
32
+ return RedCloth.new(content).to_html if @config['redcloth'].nil?
33
+
34
+ # List of attributes defined on RedCloth
35
+ # (from http://redcloth.rubyforge.org/classes/RedCloth/TextileDoc.html)
36
+ attrs = ['filter_classes', 'filter_html', 'filter_ids', 'filter_styles',
37
+ 'hard_breaks', 'lite_mode', 'no_span_caps', 'sanitize_html']
38
+
39
+ r = RedCloth.new(content)
40
+
41
+ # Set attributes in r if they are NOT nil in the config
42
+ attrs.each do |attr|
43
+ r.instance_variable_set("@#{attr}".to_sym, @config['redcloth'][attr]) unless @config['redcloth'][attr].nil?
44
+ end
45
+
46
+ r.to_html
31
47
  end
32
48
  end
33
49
 
@@ -10,6 +10,7 @@ require 'set'
10
10
  # self.data=
11
11
  # self.ext=
12
12
  # self.output=
13
+ # self.name
13
14
  module Jekyll
14
15
  module Convertible
15
16
  # Returns the contents as a String.
@@ -26,14 +27,13 @@ module Jekyll
26
27
  def read_yaml(base, name)
27
28
  self.content = File.read(File.join(base, name))
28
29
 
29
- if self.content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
30
- self.content = $POSTMATCH
31
-
32
- begin
30
+ begin
31
+ if self.content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
32
+ self.content = $POSTMATCH
33
33
  self.data = YAML.load($1)
34
- rescue => e
35
- puts "YAML Exception reading #{name}: #{e.message}"
36
34
  end
35
+ rescue => e
36
+ puts "YAML Exception reading #{name}: #{e.message}"
37
37
  end
38
38
 
39
39
  self.data ||= {}
@@ -76,9 +76,13 @@ module Jekyll
76
76
  payload["pygments_suffix"] = converter.pygments_suffix
77
77
 
78
78
  begin
79
- self.content = Liquid::Template.parse(self.content).render(payload, info)
79
+ self.content = Liquid::Template.parse(self.content).render!(payload, info)
80
80
  rescue => e
81
81
  puts "Liquid Exception: #{e.message} in #{self.name}"
82
+ e.backtrace.each do |backtrace|
83
+ puts backtrace
84
+ end
85
+ abort("Build Failed")
82
86
  end
83
87
 
84
88
  self.transform
@@ -94,9 +98,13 @@ module Jekyll
94
98
  payload = payload.deep_merge({"content" => self.output, "page" => layout.data})
95
99
 
96
100
  begin
97
- self.output = Liquid::Template.parse(layout.content).render(payload, info)
101
+ self.output = Liquid::Template.parse(layout.content).render!(payload, info)
98
102
  rescue => e
99
103
  puts "Liquid Exception: #{e.message} in #{self.data["layout"]}"
104
+ e.backtrace.each do |backtrace|
105
+ puts backtrace
106
+ end
107
+ abort("Build Failed")
100
108
  end
101
109
 
102
110
  if layout = layouts[layout.data["layout"]]
@@ -37,13 +37,19 @@ module Jekyll
37
37
  if num_page > 1
38
38
  newpage = Page.new(site, site.source, page.dir, page.name)
39
39
  newpage.pager = pager
40
- newpage.dir = File.join(page.dir, "page#{num_page}")
40
+ newpage.dir = File.join(page.dir, paginate_path(site, num_page))
41
41
  site.pages << newpage
42
42
  else
43
43
  page.pager = pager
44
44
  end
45
45
  end
46
46
  end
47
+
48
+ private
49
+ def paginate_path(site, num_page)
50
+ format = site.config['paginate_path']
51
+ format.sub(':num', num_page.to_s)
52
+ end
47
53
  end
48
54
 
49
55
  class Pager
@@ -14,19 +14,24 @@ module Jekyll
14
14
  # Reads a MySQL database via Sequel and creates a post file for each post
15
15
  # in wp_posts that has post_status = 'publish'. This restriction is made
16
16
  # because 'draft' posts are not guaranteed to have valid dates.
17
- QUERY = "SELECT node.nid, \
18
- node.title, \
19
- node_revisions.body, \
20
- node.created, \
21
- node.status \
22
- FROM node, \
23
- node_revisions \
24
- WHERE (node.type = 'blog' OR node.type = 'story') \
25
- AND node.vid = node_revisions.vid"
17
+ QUERY = "SELECT n.nid, \
18
+ n.title, \
19
+ nr.body, \
20
+ n.created, \
21
+ n.status \
22
+ FROM node AS n, \
23
+ node_revisions AS nr \
24
+ WHERE (n.type = 'blog' OR n.type = 'story') \
25
+ AND n.vid = nr.vid"
26
26
 
27
- def self.process(dbname, user, pass, host = 'localhost')
27
+ def self.process(dbname, user, pass, host = 'localhost', prefix = '')
28
28
  db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
29
29
 
30
+ if prefix != ''
31
+ QUERY[" node "] = " " + prefix + "node "
32
+ QUERY[" node_revisions "] = " " + prefix + "node_revisions "
33
+ end
34
+
30
35
  FileUtils.mkdir_p "_posts"
31
36
  FileUtils.mkdir_p "_drafts"
32
37
 
@@ -73,12 +78,18 @@ EOF
73
78
 
74
79
  # Make a file to redirect from the old Drupal URL
75
80
  if is_published
76
- FileUtils.mkdir_p "node/#{node_id}"
77
- File.open("node/#{node_id}/index.md", "w") do |f|
78
- f.puts "---"
79
- f.puts "layout: refresh"
80
- f.puts "refresh_to_post_id: /#{time.strftime("%Y/%m/%d/") + slug}"
81
- f.puts "---"
81
+ aliases = db["SELECT dst FROM #{prefix}url_alias WHERE src = ?", "node/#{node_id}"].all
82
+
83
+ aliases.push(:dst => "node/#{node_id}")
84
+
85
+ aliases.each do |url_alias|
86
+ FileUtils.mkdir_p url_alias[:dst]
87
+ File.open("#{url_alias[:dst]}/index.md", "w") do |f|
88
+ f.puts "---"
89
+ f.puts "layout: refresh"
90
+ f.puts "refresh_to_post_id: /#{time.strftime("%Y/%m/%d/") + slug}"
91
+ f.puts "---"
92
+ end
82
93
  end
83
94
  end
84
95
  end
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'sequel'
3
+ require 'fileutils'
4
+ require 'yaml'
5
+
6
+ # NOTE: This migrator is made for Joomla 1.5 databases.
7
+ # NOTE: This converter requires Sequel and the MySQL gems.
8
+ # The MySQL gem can be difficult to install on OS X. Once you have MySQL
9
+ # installed, running the following commands should work:
10
+ # $ sudo gem install sequel
11
+ # $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
12
+
13
+ module Jekyll
14
+ module Joomla
15
+ def self.process(dbname, user, pass, host = 'localhost', table_prefix = 'jos_', section = '1')
16
+ db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
17
+
18
+ FileUtils.mkdir_p("_posts")
19
+
20
+ # Reads a MySQL database via Sequel and creates a post file for each
21
+ # post in wp_posts that has post_status = 'publish'. This restriction is
22
+ # made because 'draft' posts are not guaranteed to have valid dates.
23
+ query = "SELECT `title`, `alias`, CONCAT(`introtext`,`fulltext`) as content, `created`, `id` FROM #{table_prefix}content WHERE state = '0' OR state = '1' AND sectionid = '#{section}'"
24
+
25
+ db[query].each do |post|
26
+ # Get required fields and construct Jekyll compatible name.
27
+ title = post[:title]
28
+ slug = post[:alias]
29
+ date = post[:created]
30
+ content = post[:content]
31
+ name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month, date.day,
32
+ slug]
33
+
34
+ # Get the relevant fields as a hash, delete empty fields and convert
35
+ # to YAML for the header.
36
+ data = {
37
+ 'layout' => 'post',
38
+ 'title' => title.to_s,
39
+ 'joomla_id' => post[:id],
40
+ 'joomla_url' => post[:alias],
41
+ 'date' => date
42
+ }.delete_if { |k,v| v.nil? || v == '' }.to_yaml
43
+
44
+ # Write out the data and content to file
45
+ File.open("_posts/#{name}", "w") do |f|
46
+ f.puts data
47
+ f.puts "---"
48
+ f.puts content
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -5,7 +5,7 @@ require 'net/http'
5
5
  require 'uri'
6
6
  require "json"
7
7
 
8
- # ruby -r './lib/jekyll/migrators/posterous.rb' -e 'Jekyll::Posterous.process(email, pass, blog)'
8
+ # ruby -r './lib/jekyll/migrators/posterous.rb' -e 'Jekyll::Posterous.process(email, pass, api_key, blog)'
9
9
 
10
10
  module Jekyll
11
11
  module Posterous
@@ -27,9 +27,8 @@ module Jekyll
27
27
  end
28
28
  end
29
29
 
30
- def self.process(email, pass, blog = 'primary')
31
- @email, @pass = email, pass
32
- @api_token = JSON.parse(self.fetch("/api/2/auth/token").body)['api_token']
30
+ def self.process(email, pass, api_token, blog = 'primary')
31
+ @email, @pass, @api_token = email, pass, api_token
33
32
  FileUtils.mkdir_p "_posts"
34
33
 
35
34
  posts = JSON.parse(self.fetch("/api/v2/users/me/sites/#{blog}/posts?api_token=#{@api_token}").body)
@@ -0,0 +1,47 @@
1
+ # Created by Kendall Buchanan (https://github.com/kendagriff) on 2011-12-22.
2
+ # Use at your own risk. The end.
3
+ #
4
+ # Usage:
5
+ # (URL)
6
+ # ruby -r '_import/rss.rb' -e "Jekyll::MigrateRSS.process('http://yourdomain.com/your-favorite-feed.xml')"
7
+ #
8
+ # (Local file)
9
+ # ruby -r '_import/rss.rb' -e "Jekyll::MigrateRSS.process('./somefile/on/your/computer.xml')"
10
+
11
+ require 'rubygems'
12
+ require 'rss/1.0'
13
+ require 'rss/2.0'
14
+ require 'open-uri'
15
+ require 'fileutils'
16
+ require 'yaml'
17
+
18
+ module Jekyll
19
+ module MigrateRSS
20
+
21
+ # The `source` argument may be a URL or a local file.
22
+ def self.process(source)
23
+ content = ""
24
+ open(source) { |s| content = s.read }
25
+ rss = RSS::Parser.parse(content, false)
26
+
27
+ raise "There doesn't appear to be any RSS items at the source (#{source}) provided." unless rss
28
+
29
+ rss.items.each do |item|
30
+ formatted_date = item.date.strftime('%Y-%m-%d')
31
+ post_name = item.title.split(%r{ |!|/|:|&|-|$|,}).map { |i| i.downcase if i != '' }.compact.join('-')
32
+ name = "#{formatted_date}-#{post_name}"
33
+
34
+ header = {
35
+ 'layout' => 'post',
36
+ 'title' => item.title
37
+ }
38
+
39
+ File.open("_posts/#{name}.html", "w") do |f|
40
+ f.puts header.to_yaml
41
+ f.puts "---\n"
42
+ f.puts item.description
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'sequel'
3
3
  require 'fileutils'
4
+ require 'yaml'
4
5
 
5
6
  # NOTE: This converter requires Sequel and the MySQL gems.
6
7
  # The MySQL gem can be difficult to install on OS X. Once you have MySQL
@@ -1,119 +1,195 @@
1
1
  require 'rubygems'
2
- require 'nokogiri'
3
2
  require 'open-uri'
4
3
  require 'fileutils'
5
- require 'CGI'
6
- require 'iconv'
4
+ require 'nokogiri'
7
5
  require 'date'
6
+ require 'json'
7
+ require 'uri'
8
+ require 'jekyll'
8
9
 
9
10
  module Jekyll
10
11
  module Tumblr
11
- def self.process(url, grab_images = false)
12
- current_page = 0
13
-
14
- while true
15
- f = open(url + "/api/read?num=50&start=#{current_page * 50}")
16
- doc = Nokogiri::HTML(Iconv.conv("utf-8", f.charset, f.readlines.join("\n")))
17
-
18
- puts "Page: #{current_page + 1} - Posts: #{(doc/:tumblr/:posts/:post).size}"
19
-
20
- FileUtils.mkdir_p "_posts/tumblr"
21
-
22
- (doc/:tumblr/:posts/:post).each do |post|
23
- title = ""
24
- content = nil
25
- name = nil
26
-
27
- if post['type'] == "regular"
28
- title_element = post.at("regular-title")
29
- title = title_element.inner_text unless title_element == nil
30
- content = CGI::unescapeHTML post.at("regular-body").inner_html unless post.at("regular-body") == nil
31
- elsif post['type'] == "link"
32
- title = post.at("link-text").inner_html unless post.at("link-text") == nil
33
-
34
- if post.at("link-text") != nil
35
- content = "<a href=\"#{post.at("link-url").inner_html}\">#{post.at("link-text").inner_html}</a>"
36
- else
37
- content = "<a href=\"#{post.at("link-url").inner_html}\">#{post.at("link-url").inner_html}</a>"
38
- end
39
-
40
- content << "<br/>" + CGI::unescapeHTML(post.at("link-description").inner_html) unless post.at("link-description") == nil
41
- elsif post['type'] == "photo"
42
- content = ""
43
-
44
- if post.at("photo-link-url") != nil
45
- content = "<a href=\"#{post.at("photo-link-url").inner_html}\"><img src=\"#{save_file((post/"photo-url")[1].inner_html, grab_images)}\"/></a>"
46
- else
47
- content = "<img src=\"#{save_file((post/"photo-url")[1].inner_html, grab_images)}\"/>"
48
- end
49
-
50
- if post.at("photo-caption") != nil
51
- content << "<br/>" unless content == nil
52
- content << CGI::unescapeHTML(post.at("photo-caption").inner_html)
53
- end
54
- elsif post['type'] == "audio"
55
- content = CGI::unescapeHTML(post.at("audio-player").inner_html)
56
- content << CGI::unescapeHTML(post.at("audio-caption").inner_html) unless post.at("audio-caption") == nil
57
- elsif post['type'] == "quote"
58
- content = "<blockquote>" + CGI::unescapeHTML(post.at("quote-text").inner_html) + "</blockquote>"
59
- content << "&#8212;" + CGI::unescapeHTML(post.at("quote-source").inner_html) unless post.at("quote-source") == nil
60
- elsif post['type'] == "conversation"
61
- title = post.at("conversation-title").inner_html unless post.at("conversation-title") == nil
62
- content = "<section><dialog>"
63
-
64
- (post/:conversation/:line).each do |line|
65
- content << "<dt>" + line['label'] + "</dt><dd>" + line.inner_html + "</dd>" unless line['label'] == nil || line == nil
66
- end
67
-
68
- content << "</section></dialog>"
69
- elsif post['type'] == "video"
70
- title = post.at("video-title").inner_html unless post.at("video-title") == nil
71
- content = CGI::unescapeHTML(post.at("video-player").inner_html)
72
- content << CGI::unescapeHTML(post.at("video-caption").inner_html) unless post.at("video-caption") == nil
73
- end # End post types
74
-
75
- name = "#{Date.parse(post['date']).to_s}-#{post['id'].downcase.gsub(/[^a-z0-9]/, '-')}.html"
76
-
77
- if title != nil || content != nil && name != nil
78
- File.open("_posts/tumblr/#{name}", "w") do |f|
79
-
80
- f.puts <<-HEADER
81
- ---
82
- layout: post
83
- title: #{title}
84
- ---
12
+ def self.process(url, format = "html", grab_images = false,
13
+ add_highlights = false, rewrite_urls = true)
14
+ @grab_images = grab_images
15
+ FileUtils.mkdir_p "_posts/tumblr"
16
+ url += "/api/read/json/"
17
+ per_page = 50
18
+ posts = []
19
+ # Two passes are required so that we can rewrite URLs.
20
+ # First pass builds up an array of each post as a hash.
21
+ begin
22
+ current_page = (current_page || -1) + 1
23
+ feed = open(url + "?num=#{per_page}&start=#{current_page * per_page}")
24
+ json = feed.readlines.join("\n")[21...-2] # Strip Tumblr's JSONP chars.
25
+ blog = JSON.parse(json)
26
+ puts "Page: #{current_page + 1} - Posts: #{blog["posts"].size}"
27
+ posts += blog["posts"].map { |post| post_to_hash(post, format) }
28
+ end until blog["posts"].size < per_page
29
+ # Rewrite URLs and create redirects.
30
+ posts = rewrite_urls_and_redirects posts if rewrite_urls
31
+ # Second pass for writing post files.
32
+ posts.each do |post|
33
+ if format == "md"
34
+ post[:content] = html_to_markdown post[:content]
35
+ post[:content] = add_syntax_highlights post[:content] if add_highlights
36
+ end
37
+ File.open("_posts/tumblr/#{post[:name]}", "w") do |f|
38
+ f.puts post[:header].to_yaml + "---\n" + post[:content]
39
+ end
40
+ end
41
+ end
85
42
 
86
- HEADER
43
+ private
87
44
 
88
- f.puts content
89
- end # End file
45
+ # Converts each type of Tumblr post to a hash with all required
46
+ # data for Jekyll.
47
+ def self.post_to_hash(post, format)
48
+ case post['type']
49
+ when "regular"
50
+ title = post["regular-title"]
51
+ content = post["regular-body"]
52
+ when "link"
53
+ title = post["link-text"] || post["link-url"]
54
+ content = "<a href=\"#{post["link-url"]}\">#{title}</a>"
55
+ unless post["link-description"].nil?
56
+ content << "<br/>" + post["link-description"]
90
57
  end
58
+ when "photo"
59
+ title = post["photo-caption"]
60
+ max_size = post.keys.map{ |k| k.gsub("photo-url-", "").to_i }.max
61
+ url = post["photo-url"] || post["photo-url-#{max_size}"]
62
+ ext = "." + post[post.keys.select { |k|
63
+ k =~ /^photo-url-/ && post[k].split("/").last =~ /\./
64
+ }.first].split(".").last
65
+ content = "<img src=\"#{save_file(url, ext)}\"/>"
66
+ unless post["photo-link-url"].nil?
67
+ content = "<a href=\"#{post["photo-link-url"]}\">#{content}</a>"
68
+ end
69
+ when "audio"
70
+ if !post["id3-title"].nil?
71
+ title = post["id3-title"]
72
+ content = post.at["audio-player"] + "<br/>" + post["audio-caption"]
73
+ else
74
+ title = post["audio-caption"]
75
+ content = post.at["audio-player"]
76
+ end
77
+ when "quote"
78
+ title = post["quote-text"]
79
+ content = "<blockquote>#{post["quote-text"]}</blockquote>"
80
+ unless post["quote-source"].nil?
81
+ content << "&#8212;" + post["quote-source"]
82
+ end
83
+ when "conversation"
84
+ title = post["conversation-title"]
85
+ content = "<section><dialog>"
86
+ post["conversation"]["line"].each do |line|
87
+ content << "<dt>#{line['label']}</dt><dd>#{line}</dd>"
88
+ end
89
+ content << "</section></dialog>"
90
+ when "video"
91
+ title = post["video-title"]
92
+ content = post["video-player"]
93
+ unless post["video-caption"].nil?
94
+ content << "<br/>" + post["video-caption"]
95
+ end
96
+ end
97
+ date = Date.parse(post['date']).to_s
98
+ title = Nokogiri::HTML(title).text
99
+ slug = title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
100
+ {
101
+ :name => "#{date}-#{slug}.#{format}",
102
+ :header => {
103
+ "layout" => "post",
104
+ "title" => title,
105
+ "tags" => post["tags"],
106
+ },
107
+ :content => content,
108
+ :url => post["url"],
109
+ :slug => post["url-with-slug"],
110
+ }
111
+ end
91
112
 
92
- end # End post XML
93
-
94
- if (doc/:tumblr/:posts/:post).size < 50
95
- break
96
- else
97
- current_page = current_page + 1
113
+ # Create a Hash of old urls => new urls, for rewriting and
114
+ # redirects, and replace urls in each post. Instantiate Jekyll
115
+ # site/posts to get the correct permalink format.
116
+ def self.rewrite_urls_and_redirects(posts)
117
+ site = Jekyll::Site.new(Jekyll.configuration({}))
118
+ dir = File.join(File.dirname(__FILE__), "..")
119
+ urls = Hash[posts.map { |post|
120
+ # Create an initial empty file for the post so that
121
+ # we can instantiate a post object.
122
+ File.open("_posts/tumblr/#{post[:name]}", "w")
123
+ tumblr_url = URI.parse(post[:slug]).path
124
+ jekyll_url = Jekyll::Post.new(site, dir, "", "tumblr/" + post[:name]).url
125
+ redirect_dir = tumblr_url.sub(/\//, "") + "/"
126
+ FileUtils.mkdir_p redirect_dir
127
+ File.open(redirect_dir + "index.html", "w") do |f|
128
+ f.puts "<html><head><meta http-equiv='Refresh' content='0; " +
129
+ "url=#{jekyll_url}'></head><body></body></html>"
98
130
  end
131
+ [tumblr_url, jekyll_url]
132
+ }]
133
+ posts.map { |post|
134
+ urls.each do |tumblr_url, jekyll_url|
135
+ post[:content].gsub!(/#{tumblr_url}/i, jekyll_url)
136
+ end
137
+ post
138
+ }
139
+ end
99
140
 
100
- end # End while loop
101
- end # End method
102
-
103
- private
104
-
105
- def self.save_file(url, grab_image = false)
106
- unless grab_image == false
107
- FileUtils.mkdir_p "tumblr_files"
141
+ # Uses Python's html2text to convert a post's content to
142
+ # markdown. Preserve HTML tables as per the markdown docs.
143
+ def self.html_to_markdown(content)
144
+ preserve = ["table", "tr", "th", "td"]
145
+ preserve.each do |tag|
146
+ content.gsub!(/<#{tag}/i, "$$" + tag)
147
+ content.gsub!(/<\/#{tag}/i, "||" + tag)
148
+ end
149
+ content = %x[echo '#{content.gsub("'", "''")}' | html2text]
150
+ preserve.each do |tag|
151
+ content.gsub!("$$" + tag, "<" + tag)
152
+ content.gsub!("||" + tag, "</" + tag)
153
+ end
154
+ content
155
+ end
108
156
 
109
- File.open("tumblr_files/#{url.split('/').last}", "w") do |f|
110
- f.write(open(url).read)
157
+ # Adds pygments highlight tags to code blocks in posts that use
158
+ # markdown format. This doesn't guess the language of the code
159
+ # block, so you should modify this to suit your own content.
160
+ # For example, my code block only contain Python and JavaScript,
161
+ # so I can assume the block is JavaScript if it contains a
162
+ # semi-colon.
163
+ def self.add_syntax_highlights(content)
164
+ lines = content.split("\n")
165
+ block, indent, lang, start = false, /^ /, nil, nil
166
+ lines.each_with_index do |line, i|
167
+ if !block && line =~ indent
168
+ block = true
169
+ lang = "python"
170
+ start = i
171
+ elsif block
172
+ lang = "javascript" if line =~ /;$/
173
+ block = line =~ indent && i < lines.size - 1 # Also handle EOF
174
+ if !block
175
+ lines[start] = "{% highlight #{lang} %}"
176
+ lines[i - 1] = "{% endhighlight %}"
177
+ end
178
+ lines[i] = lines[i].sub(indent, "")
111
179
  end
180
+ end
181
+ lines.join("\n")
182
+ end
112
183
 
113
- return "/tumblr_files/#{url.split('/').last}"
114
- else
115
- return url
184
+ def self.save_file(url, ext)
185
+ if @grab_images
186
+ path = "tumblr_files/#{url.split('/').last}"
187
+ path += ext unless path =~ /#{ext}$/
188
+ FileUtils.mkdir_p "tumblr_files"
189
+ File.open(path, "w") { |f| f.write(open(url).read) }
190
+ url = "/" + path
116
191
  end
192
+ url
117
193
  end
118
194
  end
119
195
  end