diff --git a/History.txt b/History.txt index 55aa6027..f9d359f1 100644 --- a/History.txt +++ b/History.txt @@ -1,3 +1,20 @@ +== HEAD + * Minor Enhancements + * Add wordpress.com importer (#207) + * Add --limit-posts cli option (#212) + * Add uri_escape filter (#234) + * Add --base-url cli option (#235) + * Bug Fixes + * Fixed filename basename generation (#208) + * Set mode to UTF8 on Sequel connections (#237) + +== 0.7.0 / 2010-08-24 + * Minor Enhancements + * Add support for rdiscount extensions (#173) + * Bug Fixes + * Highlight should not be able to render local files + * The site configuration may not always provide a 'time' setting (#184) + == 0.6.2 / 2010-06-25 * Bug Fixes * Fix Rakefile 'release' task (tag pushing was missing origin) diff --git a/bin/jekyll b/bin/jekyll index 94a5c090..62d88e1a 100755 --- a/bin/jekyll +++ b/bin/jekyll @@ -40,6 +40,10 @@ opts = OptionParser.new do |opts| options['server_port'] = port unless port.nil? end + opts.on("--base-url [BASE_URL]", "Serve website from a given base URL (default '/'") do |baseurl| + options['baseurl'] = baseurl + end + opts.on("--lsi", "Use LSI for better related posts") do options['lsi'] = true end @@ -78,6 +82,16 @@ opts = OptionParser.new do |opts| end end + opts.on("--limit_posts [MAX_POSTS]", "Limit the number of posts to publish") do |limit_posts| + begin + options['limit_posts'] = limit_posts.to_i + raise ArgumentError if options['limit_posts'] < 1 + rescue + puts 'you must specify a number of posts by page bigger than 0' + exit 0 + end + end + opts.on("--url [URL]", "Set custom site.url") do |url| options['url'] = url end @@ -166,9 +180,9 @@ if options['server'] s = HTTPServer.new( :Port => options['server_port'], - :DocumentRoot => destination, :MimeTypes => mime_types ) + s.mount(options['baseurl'], HTTPServlet::FileHandler, destination) t = Thread.new { s.start } diff --git a/features/create_sites.feature b/features/create_sites.feature index 8517e28d..a7b5d9b1 100644 --- a/features/create_sites.feature +++ b/features/create_sites.feature @@ -89,6 +89,6 @@ Feature: Create sites And I have an "_includes/about.textile" file that contains "Generated by {% include jekyll.textile %}" And I have an "_includes/jekyll.textile" file that contains "Jekyll" And I have an "index.html" page that contains "Basic Site with include tag: {% include about.textile %}" - When I run jekyll + When I debug jekyll Then the _site directory should exist - And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html" \ No newline at end of file + And I should see "Basic Site with include tag: Generated by Jekyll" in "_site/index.html" diff --git a/features/site_configuration.feature b/features/site_configuration.feature index 6f1c600e..38a315ae 100644 --- a/features/site_configuration.feature +++ b/features/site_configuration.feature @@ -101,3 +101,19 @@ Feature: Site configuration And I should see "Page Layout: 2 on 2010-01-01" in "_site/index.html" And I should see "Post Layout:

content for entry1.

" in "_site/2007/12/31/entry1.html" And I should see "Post Layout:

content for entry2.

" in "_site/2020/01/31/entry2.html" + + Scenario: Limit the number of posts generated by most recent date + Given I have a _posts directory + And I have a configuration file with: + | key | value | + | limit_posts | 2 | + And I have the following posts: + | title | date | content | + | Apples | 3/27/2009 | An article about apples | + | Oranges | 4/1/2009 | An article about oranges | + | Bananas | 4/5/2009 | An article about bananas | + When I run jekyll + Then the _site directory should exist + And the "_site/2009/04/05/bananas.html" file should exist + And the "_site/2009/04/01/oranges.html" file should exist + And the "_site/2009/03/27/apples.html" file should not exist diff --git a/jekyll.gemspec b/jekyll.gemspec index cab7dabd..b5cb0419 100644 --- a/jekyll.gemspec +++ b/jekyll.gemspec @@ -4,8 +4,8 @@ Gem::Specification.new do |s| s.rubygems_version = '1.3.5' s.name = 'jekyll' - s.version = '0.6.2' - s.date = '2010-06-25' + s.version = '0.7.0' + s.date = '2010-08-24' s.rubyforge_project = 'jekyll' s.summary = "A simple, blog aware, static site generator." @@ -123,6 +123,7 @@ Gem::Specification.new do |s| test/test_page.rb test/test_pager.rb test/test_post.rb + test/test_rdiscount.rb test/test_site.rb test/test_tags.rb ] diff --git a/lib/jekyll.rb b/lib/jekyll.rb index b44eb5f3..9d13f99f 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -45,7 +45,7 @@ require_all 'jekyll/generators' require_all 'jekyll/tags' module Jekyll - VERSION = '0.6.2' + VERSION = '0.7.0' # Default options. Overriden by values in _config.yml or command-line opts. # (Strings rather symbols used for compatability with YAML). @@ -71,6 +71,9 @@ module Jekyll 'png_engine' => 'blahtex', 'png_dir' => 'images/latex', 'png_url' => '/images/latex' + }, + 'rdiscount' => { + 'extensions' => [] } } diff --git a/lib/jekyll/albino.rb b/lib/jekyll/albino.rb index 10d0ffb9..f71e6382 100644 --- a/lib/jekyll/albino.rb +++ b/lib/jekyll/albino.rb @@ -54,7 +54,7 @@ class Albino end def initialize(target, lexer = :text, format = :html) - @target = File.exists?(target) ? File.read(target) : target rescue target + @target = target @options = { :l => lexer, :f => format, :O => 'encoding=utf-8' } end diff --git a/lib/jekyll/converters/markdown.rb b/lib/jekyll/converters/markdown.rb index b4bc2475..317b1487 100644 --- a/lib/jekyll/converters/markdown.rb +++ b/lib/jekyll/converters/markdown.rb @@ -13,6 +13,9 @@ module Jekyll when 'rdiscount' begin require 'rdiscount' + + # Load rdiscount extensions + @rdiscount_extensions = @config['rdiscount']['extensions'].map { |e| e.to_sym } rescue LoadError STDERR.puts 'You are missing a library required for Markdown. Please run:' STDERR.puts ' $ [sudo] gem install rdiscount' @@ -67,7 +70,7 @@ module Jekyll setup case @config['markdown'] when 'rdiscount' - RDiscount.new(content).to_html + RDiscount.new(content, *@rdiscount_extensions).to_html when 'maruku' Maruku.new(content).to_html end diff --git a/lib/jekyll/filters.rb b/lib/jekyll/filters.rb index 5bdebec8..3356eadf 100644 --- a/lib/jekyll/filters.rb +++ b/lib/jekyll/filters.rb @@ -1,3 +1,5 @@ +require 'uri' + module Jekyll module Filters @@ -25,6 +27,10 @@ module Jekyll CGI::escape(input) end + def uri_escape(input) + URI.escape(input) + end + def number_of_words(input) input.split.length end diff --git a/lib/jekyll/migrators/mephisto.rb b/lib/jekyll/migrators/mephisto.rb index aa985b5e..9a3e33ae 100644 --- a/lib/jekyll/migrators/mephisto.rb +++ b/lib/jekyll/migrators/mephisto.rb @@ -40,7 +40,7 @@ module Jekyll QUERY = "SELECT id, permalink, body, published_at, title FROM contents WHERE user_id = 1 AND type = 'Article' AND published_at IS NOT NULL ORDER BY published_at" def self.process(dbname, user, pass, host = 'localhost') - db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host) + db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8') FileUtils.mkdir_p "_posts" diff --git a/lib/jekyll/migrators/mt.rb b/lib/jekyll/migrators/mt.rb index 88cdb1a4..93cd4de6 100644 --- a/lib/jekyll/migrators/mt.rb +++ b/lib/jekyll/migrators/mt.rb @@ -21,7 +21,7 @@ module Jekyll QUERY = "SELECT entry_id, entry_basename, entry_text, entry_text_more, entry_authored_on, entry_title, entry_convert_breaks FROM mt_entry" def self.process(dbname, user, pass, host = 'localhost') - db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host) + db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8') FileUtils.mkdir_p "_posts" diff --git a/lib/jekyll/migrators/textpattern.rb b/lib/jekyll/migrators/textpattern.rb index a9a972db..4b371197 100644 --- a/lib/jekyll/migrators/textpattern.rb +++ b/lib/jekyll/migrators/textpattern.rb @@ -17,7 +17,7 @@ module Jekyll QUERY = "select Title, url_title, Posted, Body, Keywords from textpattern where Status = '4' or Status = '5'" def self.process(dbname, user, pass, host = 'localhost') - db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host) + db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8') FileUtils.mkdir_p "_posts" diff --git a/lib/jekyll/migrators/typo.rb b/lib/jekyll/migrators/typo.rb index 23775483..1795a6bb 100644 --- a/lib/jekyll/migrators/typo.rb +++ b/lib/jekyll/migrators/typo.rb @@ -22,7 +22,7 @@ module Jekyll def self.process dbname, user, pass, host='localhost' FileUtils.mkdir_p '_posts' - db = Sequel.mysql dbname, :user => user, :password => pass, :host => host + db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8') db[SQL].each do |post| next unless post[:state] =~ /Published/ diff --git a/lib/jekyll/migrators/wordpress.com.rb b/lib/jekyll/migrators/wordpress.com.rb new file mode 100644 index 00000000..5be1b42d --- /dev/null +++ b/lib/jekyll/migrators/wordpress.com.rb @@ -0,0 +1,38 @@ +require 'rubygems' +require 'hpricot' +require 'fileutils' + +# This importer takes a wordpress.xml file, +# which can be exported from your +# wordpress.com blog (/wp-admin/export.php) + +module Jekyll + module WordpressDotCom + def self.process(filename = "wordpress.xml") + FileUtils.mkdir_p "_posts" + posts = 0 + + doc = Hpricot::XML(File.read(filename)) + + (doc/:channel/:item).each do |item| + title = item.at(:title).inner_text + name = "#{Date.parse((doc/:channel/:item).first.at(:pubDate).inner_text).to_s("%Y-%m-%d")}-#{title.downcase.gsub('[^a-z0-9]', '-')}.html" + + File.open("_posts/#{name}", "w") do |f| + f.puts <<-HEADER +--- +layout: post +title: #{title} +--- + +HEADER + f.puts item.at('content:encoded').inner_text + end + + posts += 1 + end + + "Imported #{posts} posts" + end + end +end \ No newline at end of file diff --git a/lib/jekyll/migrators/wordpress.rb b/lib/jekyll/migrators/wordpress.rb index bd80daee..11b4f49a 100644 --- a/lib/jekyll/migrators/wordpress.rb +++ b/lib/jekyll/migrators/wordpress.rb @@ -19,7 +19,7 @@ module Jekyll QUERY = "select post_title, post_name, post_date, post_content, post_excerpt, ID, guid from wp_posts where post_status = 'publish' and post_type = 'post'" def self.process(dbname, user, pass, host = 'localhost') - db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host) + db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8') FileUtils.mkdir_p "_posts" diff --git a/lib/jekyll/page.rb b/lib/jekyll/page.rb index 3cdce5df..c4143424 100644 --- a/lib/jekyll/page.rb +++ b/lib/jekyll/page.rb @@ -71,7 +71,7 @@ module Jekyll # Returns nothing def process(name) self.ext = File.extname(name) - self.basename = name.split('.')[0..-2].first + self.basename = name[0 .. -self.ext.length-1] end # Add any necessary layouts to this post diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index 7570cb0b..331b6c07 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -3,7 +3,8 @@ module Jekyll class Site attr_accessor :config, :layouts, :posts, :pages, :static_files, :categories, :exclude, :source, :dest, :lsi, :pygments, - :permalink_style, :tags, :time, :future, :safe, :plugins + :permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts + attr_accessor :converters, :generators # Initialize the site @@ -22,19 +23,26 @@ module Jekyll self.permalink_style = config['permalink'].to_sym self.exclude = config['exclude'] || [] self.future = config['future'] + self.limit_posts = config['limit_posts'] || nil self.reset self.setup end def reset - self.time = Time.parse(self.config['time'].to_s) || Time.now + self.time = if self.config['time'] + Time.parse(self.config['time'].to_s) + else + Time.now + end self.layouts = {} self.posts = [] self.pages = [] self.static_files = [] self.categories = Hash.new { |hash, key| hash[key] = [] } self.tags = Hash.new { |hash, key| hash[key] = [] } + + raise ArgumentError, "Limit posts must be nil or >= 1" if !self.limit_posts.nil? && self.limit_posts < 1 end def setup @@ -118,6 +126,9 @@ module Jekyll end self.posts.sort! + + # limit the posts if :limit_posts option is set + self.posts = self.posts[-limit_posts, limit_posts] if limit_posts end def generate diff --git a/test/source/.htaccess b/test/source/.htaccess new file mode 100644 index 00000000..294fb14a --- /dev/null +++ b/test/source/.htaccess @@ -0,0 +1,8 @@ +--- +layout: nil +--- +ErrorDocument 404 /404.html +ErrorDocument 500 /500.html +{% for post in site.posts %} + # {{ post.url }} +{% endfor %} \ No newline at end of file diff --git a/test/source/deal.with.dots.html b/test/source/deal.with.dots.html new file mode 100644 index 00000000..a69dac8c --- /dev/null +++ b/test/source/deal.with.dots.html @@ -0,0 +1,7 @@ +--- +title: Deal with dots +permalink: /deal.with.dots/ +--- + +Let's test if jekyll deals properly with dots. + diff --git a/test/test_filters.rb b/test/test_filters.rb index bb2d8f82..0897fbc2 100644 --- a/test/test_filters.rb +++ b/test/test_filters.rb @@ -45,5 +45,9 @@ class TestFilters < Test::Unit::TestCase should "escape special characters" do assert_equal "hey%21", @filter.cgi_escape("hey!") end + + should "escape space as %20" do + assert_equal "my%20things", @filter.uri_escape("my things") + end end end diff --git a/test/test_generated_site.rb b/test/test_generated_site.rb index bfb86daa..cba75649 100644 --- a/test/test_generated_site.rb +++ b/test/test_generated_site.rb @@ -41,4 +41,32 @@ class TestGeneratedSite < Test::Unit::TestCase assert File.exists?(dest_dir('/contacts.html')) end end + + context "generating limited posts" do + setup do + clear_dest + stub(Jekyll).configuration do + Jekyll::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir, 'limit_posts' => 5}) + end + + @site = Site.new(Jekyll.configuration) + @site.process + @index = File.read(dest_dir('index.html')) + end + + should "generate only the specified number of posts" do + assert_equal 5, @site.posts.size + end + + should "ensure limit posts is 1 or more" do + assert_raise ArgumentError do + clear_dest + stub(Jekyll).configuration do + Jekyll::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir, 'limit_posts' => 0}) + end + + @site = Site.new(Jekyll.configuration) + end + end + end end diff --git a/test/test_page.rb b/test/test_page.rb index 6fbb003d..ad5c296f 100644 --- a/test/test_page.rb +++ b/test/test_page.rb @@ -16,13 +16,23 @@ class TestPage < Test::Unit::TestCase stub(Jekyll).configuration { Jekyll::DEFAULTS } @site = Site.new(Jekyll.configuration) end - + context "processing pages" do should "create url based on filename" do @page = setup_page('contacts.html') assert_equal "/contacts.html", @page.url end + should "deal properly with extensions" do + @page = setup_page('deal.with.dots.html') + assert_equal ".html", @page.ext + end + + should "deal properly with dots" do + @page = setup_page('deal.with.dots.html') + assert_equal "deal.with.dots", @page.basename + end + context "with pretty url style" do setup do @site.permalink_style = :pretty @@ -61,7 +71,7 @@ class TestPage < Test::Unit::TestCase setup do clear_dest end - + should "write properly" do page = setup_page('contacts.html') do_render(page) @@ -73,14 +83,14 @@ class TestPage < Test::Unit::TestCase should "write properly without html extension" do page = setup_page('contacts.html') - page.site.permalink_style = :pretty + page.site.permalink_style = :pretty do_render(page) page.write(dest_dir) assert File.directory?(dest_dir) assert File.exists?(File.join(dest_dir, 'contacts', 'index.html')) end - + should "write properly with extension different from html" do page = setup_page("sitemap.xml") page.site.permalink_style = :pretty @@ -92,7 +102,16 @@ class TestPage < Test::Unit::TestCase assert File.directory?(dest_dir) assert File.exists?(File.join(dest_dir,'sitemap.xml')) end + + should "write dotfiles properly" do + page = setup_page('.htaccess') + do_render(page) + page.write(dest_dir) + + assert File.directory?(dest_dir) + assert File.exists?(File.join(dest_dir, '.htaccess')) + end end - end + end end diff --git a/test/test_rdiscount.rb b/test/test_rdiscount.rb new file mode 100644 index 00000000..2cb40d6d --- /dev/null +++ b/test/test_rdiscount.rb @@ -0,0 +1,18 @@ +require File.dirname(__FILE__) + '/helper' + +class TestRdiscount < Test::Unit::TestCase + + context "rdiscount" do + setup do + config = { + 'rdiscount' => { 'extensions' => ['smart'] }, + 'markdown' => 'rdiscount' + } + @markdown = MarkdownConverter.new config + end + + should "pass rdiscount extensions" do + assert_equal "

“smart”

", @markdown.convert('"smart"').strip + end + end +end diff --git a/test/test_tags.rb b/test/test_tags.rb index ac92deda..d25aa705 100644 --- a/test/test_tags.rb +++ b/test/test_tags.rb @@ -24,9 +24,7 @@ title: This is a test This document results in a markdown error with maruku -{% highlight text %} -#{code} -{% endhighlight %} +{% highlight text %}#{code}{% endhighlight %} CONTENT create_post(content, override) end @@ -45,6 +43,16 @@ CONTENT end end + context "post content has highlight with file reference" do + setup do + fill_post("./jekyll.gemspec") + end + + should "not embed the file" do + assert_match %{
./jekyll.gemspec\n
}, @result + end + end + context "post content has highlight tag with UTF character" do setup do fill_post("Æ")