mattmatt-jekyll 0.4.0
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.
- data/History.txt +91 -0
 - data/README.textile +494 -0
 - data/Rakefile +76 -0
 - data/TODO +3 -0
 - data/VERSION.yml +4 -0
 - data/bin/jekyll +142 -0
 - data/lib/jekyll.rb +64 -0
 - data/lib/jekyll/albino.rb +116 -0
 - data/lib/jekyll/converters/csv.rb +26 -0
 - data/lib/jekyll/converters/mephisto.rb +79 -0
 - data/lib/jekyll/converters/mt.rb +59 -0
 - data/lib/jekyll/converters/textpattern.rb +50 -0
 - data/lib/jekyll/converters/typo.rb +49 -0
 - data/lib/jekyll/converters/wordpress.rb +54 -0
 - data/lib/jekyll/convertible.rb +71 -0
 - data/lib/jekyll/core_ext.rb +22 -0
 - data/lib/jekyll/filters.rb +39 -0
 - data/lib/jekyll/layout.rb +33 -0
 - data/lib/jekyll/page.rb +64 -0
 - data/lib/jekyll/post.rb +194 -0
 - data/lib/jekyll/site.rb +173 -0
 - data/lib/jekyll/tags/highlight.rb +53 -0
 - data/lib/jekyll/tags/include.rb +31 -0
 - data/test/helper.rb +14 -0
 - data/test/source/_includes/sig.markdown +3 -0
 - data/test/source/_layouts/default.html +27 -0
 - data/test/source/_layouts/simple.html +1 -0
 - data/test/source/_posts/2008-02-02-not-published.textile +8 -0
 - data/test/source/_posts/2008-02-02-published.textile +8 -0
 - data/test/source/_posts/2008-10-18-foo-bar.textile +8 -0
 - data/test/source/_posts/2008-11-21-complex.textile +8 -0
 - data/test/source/_posts/2008-12-03-permalinked-post.textile +9 -0
 - data/test/source/_posts/2008-12-13-include.markdown +8 -0
 - data/test/source/_posts/2009-01-27-categories.textile +7 -0
 - data/test/source/_posts/2009-01-27-category.textile +7 -0
 - data/test/source/category/_posts/2008-9-23-categories.textile +6 -0
 - data/test/source/css/screen.css +76 -0
 - data/test/source/foo/_posts/bar/2008-12-12-topical-post.textile +8 -0
 - data/test/source/index.html +22 -0
 - data/test/source/z_category/_posts/2008-9-23-categories.textile +6 -0
 - data/test/suite.rb +9 -0
 - data/test/test_filters.rb +37 -0
 - data/test/test_generated_site.rb +32 -0
 - data/test/test_jekyll.rb +0 -0
 - data/test/test_post.rb +144 -0
 - data/test/test_site.rb +36 -0
 - data/test/test_tags.rb +31 -0
 - metadata +230 -0
 
    
        data/lib/jekyll/post.rb
    ADDED
    
    | 
         @@ -0,0 +1,194 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Jekyll
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              class Post
         
     | 
| 
      
 4 
     | 
    
         
            +
                include Comparable
         
     | 
| 
      
 5 
     | 
    
         
            +
                include Convertible
         
     | 
| 
      
 6 
     | 
    
         
            +
                    
         
     | 
| 
      
 7 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 8 
     | 
    
         
            +
                  attr_accessor :lsi
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
                
         
     | 
| 
      
 11 
     | 
    
         
            +
                MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
         
     | 
| 
      
 12 
     | 
    
         
            +
                
         
     | 
| 
      
 13 
     | 
    
         
            +
                # Post name validator. Post filenames must be like:
         
     | 
| 
      
 14 
     | 
    
         
            +
                #   2008-11-05-my-awesome-post.textile
         
     | 
| 
      
 15 
     | 
    
         
            +
                #
         
     | 
| 
      
 16 
     | 
    
         
            +
                # Returns <Bool>
         
     | 
| 
      
 17 
     | 
    
         
            +
                def self.valid?(name)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  name =~ MATCHER
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
                attr_accessor :date, :slug, :ext, :categories, :topics, :published
         
     | 
| 
      
 22 
     | 
    
         
            +
                attr_accessor :data, :content, :output
         
     | 
| 
      
 23 
     | 
    
         
            +
                attr_accessor :previous, :next
         
     | 
| 
      
 24 
     | 
    
         
            +
                
         
     | 
| 
      
 25 
     | 
    
         
            +
                # Initialize this Post instance.
         
     | 
| 
      
 26 
     | 
    
         
            +
                #   +base+ is the String path to the dir containing the post file
         
     | 
| 
      
 27 
     | 
    
         
            +
                #   +name+ is the String filename of the post file
         
     | 
| 
      
 28 
     | 
    
         
            +
                #   +categories+ is an Array of Strings for the categories for this post
         
     | 
| 
      
 29 
     | 
    
         
            +
                #
         
     | 
| 
      
 30 
     | 
    
         
            +
                # Returns <Post>
         
     | 
| 
      
 31 
     | 
    
         
            +
                def initialize(source, dir, name)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  @base = File.join(source, dir, '_posts')
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @name = name
         
     | 
| 
      
 34 
     | 
    
         
            +
                  
         
     | 
| 
      
 35 
     | 
    
         
            +
                  self.categories = dir.split('/').reject { |x| x.empty? }
         
     | 
| 
      
 36 
     | 
    
         
            +
                  
         
     | 
| 
      
 37 
     | 
    
         
            +
                  parts = name.split('/')
         
     | 
| 
      
 38 
     | 
    
         
            +
                  self.topics = parts.size > 1 ? parts[0..-2] : []
         
     | 
| 
      
 39 
     | 
    
         
            +
                  
         
     | 
| 
      
 40 
     | 
    
         
            +
                  self.process(name)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  self.read_yaml(@base, name)
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            			if self.data.has_key?('published') && self.data['published'] == false
         
     | 
| 
      
 44 
     | 
    
         
            +
            				self.published = false
         
     | 
| 
      
 45 
     | 
    
         
            +
            			else
         
     | 
| 
      
 46 
     | 
    
         
            +
            				self.published = true
         
     | 
| 
      
 47 
     | 
    
         
            +
            			end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  
         
     | 
| 
      
 49 
     | 
    
         
            +
                  if self.categories.empty?
         
     | 
| 
      
 50 
     | 
    
         
            +
                    if self.data.has_key?('category')
         
     | 
| 
      
 51 
     | 
    
         
            +
                      self.categories << self.data['category']
         
     | 
| 
      
 52 
     | 
    
         
            +
                    elsif self.data.has_key?('categories')
         
     | 
| 
      
 53 
     | 
    
         
            +
                      self.categories = self.data['categories'].split
         
     | 
| 
      
 54 
     | 
    
         
            +
                    end
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
                
         
     | 
| 
      
 58 
     | 
    
         
            +
                # Spaceship is based on Post#date
         
     | 
| 
      
 59 
     | 
    
         
            +
                #
         
     | 
| 
      
 60 
     | 
    
         
            +
                # Returns -1, 0, 1
         
     | 
| 
      
 61 
     | 
    
         
            +
                def <=>(other)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  self.date <=> other.date
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
                
         
     | 
| 
      
 65 
     | 
    
         
            +
                # Extract information from the post filename
         
     | 
| 
      
 66 
     | 
    
         
            +
                #   +name+ is the String filename of the post file
         
     | 
| 
      
 67 
     | 
    
         
            +
                #
         
     | 
| 
      
 68 
     | 
    
         
            +
                # Returns nothing
         
     | 
| 
      
 69 
     | 
    
         
            +
                def process(name)
         
     | 
| 
      
 70 
     | 
    
         
            +
                  m, cats, date, slug, ext = *name.match(MATCHER)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  self.date = Time.parse(date)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  self.slug = slug
         
     | 
| 
      
 73 
     | 
    
         
            +
                  self.ext = ext
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
                
         
     | 
| 
      
 76 
     | 
    
         
            +
                # The generated directory into which the post will be placed
         
     | 
| 
      
 77 
     | 
    
         
            +
                # upon generation. This is derived from the permalink or, if
         
     | 
| 
      
 78 
     | 
    
         
            +
                # permalink is absent, set to the default date
         
     | 
| 
      
 79 
     | 
    
         
            +
                # e.g. "/2008/11/05/" if the permalink style is :date, otherwise nothing
         
     | 
| 
      
 80 
     | 
    
         
            +
                #
         
     | 
| 
      
 81 
     | 
    
         
            +
                # Returns <String>
         
     | 
| 
      
 82 
     | 
    
         
            +
                def dir
         
     | 
| 
      
 83 
     | 
    
         
            +
                  if permalink
         
     | 
| 
      
 84 
     | 
    
         
            +
                    permalink.to_s.split("/")[0..-2].join("/") + '/'
         
     | 
| 
      
 85 
     | 
    
         
            +
                  else
         
     | 
| 
      
 86 
     | 
    
         
            +
                    prefix = self.categories.empty? ? '' : '/' + self.categories.join('/')
         
     | 
| 
      
 87 
     | 
    
         
            +
                    case Jekyll.permalink_style
         
     | 
| 
      
 88 
     | 
    
         
            +
                    when :date
         
     | 
| 
      
 89 
     | 
    
         
            +
                      prefix + date.strftime("/%Y/%m/%d/")
         
     | 
| 
      
 90 
     | 
    
         
            +
                    when :shortdate
         
     | 
| 
      
 91 
     | 
    
         
            +
                      prefix + "/#{date.year}/#{date.month}/#{date.day}/"
         
     | 
| 
      
 92 
     | 
    
         
            +
                    else
         
     | 
| 
      
 93 
     | 
    
         
            +
                      prefix + '/'
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
                
         
     | 
| 
      
 98 
     | 
    
         
            +
                # The full path and filename of the post.
         
     | 
| 
      
 99 
     | 
    
         
            +
                # Defined in the YAML of the post body
         
     | 
| 
      
 100 
     | 
    
         
            +
                # (Optional)
         
     | 
| 
      
 101 
     | 
    
         
            +
                #
         
     | 
| 
      
 102 
     | 
    
         
            +
                # Returns <String>
         
     | 
| 
      
 103 
     | 
    
         
            +
                def permalink
         
     | 
| 
      
 104 
     | 
    
         
            +
                  self.data && self.data['permalink']
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
                
         
     | 
| 
      
 107 
     | 
    
         
            +
                # The generated relative url of this post
         
     | 
| 
      
 108 
     | 
    
         
            +
                # e.g. /2008/11/05/my-awesome-post.html
         
     | 
| 
      
 109 
     | 
    
         
            +
                #
         
     | 
| 
      
 110 
     | 
    
         
            +
                # Returns <String>
         
     | 
| 
      
 111 
     | 
    
         
            +
                def url
         
     | 
| 
      
 112 
     | 
    
         
            +
                  permalink || self.dir + self.slug + ".html"
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
                
         
     | 
| 
      
 115 
     | 
    
         
            +
                # The UID for this post (useful in feeds)
         
     | 
| 
      
 116 
     | 
    
         
            +
                # e.g. /2008/11/05/my-awesome-post
         
     | 
| 
      
 117 
     | 
    
         
            +
                #
         
     | 
| 
      
 118 
     | 
    
         
            +
                # Returns <String>
         
     | 
| 
      
 119 
     | 
    
         
            +
                def id
         
     | 
| 
      
 120 
     | 
    
         
            +
                  self.dir + self.slug
         
     | 
| 
      
 121 
     | 
    
         
            +
                end
         
     | 
| 
      
 122 
     | 
    
         
            +
                    
         
     | 
| 
      
 123 
     | 
    
         
            +
                # Calculate related posts.
         
     | 
| 
      
 124 
     | 
    
         
            +
                #
         
     | 
| 
      
 125 
     | 
    
         
            +
                # Returns [<Post>]
         
     | 
| 
      
 126 
     | 
    
         
            +
                def related_posts(posts)
         
     | 
| 
      
 127 
     | 
    
         
            +
                  return [] unless posts.size > 1
         
     | 
| 
      
 128 
     | 
    
         
            +
                  
         
     | 
| 
      
 129 
     | 
    
         
            +
                  if Jekyll.lsi
         
     | 
| 
      
 130 
     | 
    
         
            +
                    self.class.lsi ||= begin
         
     | 
| 
      
 131 
     | 
    
         
            +
                      puts "Running the classifier... this could take a while."
         
     | 
| 
      
 132 
     | 
    
         
            +
                      lsi = Classifier::LSI.new
         
     | 
| 
      
 133 
     | 
    
         
            +
                      posts.each { |x| $stdout.print(".");$stdout.flush;lsi.add_item(x) }
         
     | 
| 
      
 134 
     | 
    
         
            +
                      puts ""
         
     | 
| 
      
 135 
     | 
    
         
            +
                      lsi
         
     | 
| 
      
 136 
     | 
    
         
            +
                    end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                    related = self.class.lsi.find_related(self.content, 11)
         
     | 
| 
      
 139 
     | 
    
         
            +
                    related - [self]
         
     | 
| 
      
 140 
     | 
    
         
            +
                  else
         
     | 
| 
      
 141 
     | 
    
         
            +
                    (posts - [self])[0..9]
         
     | 
| 
      
 142 
     | 
    
         
            +
                  end
         
     | 
| 
      
 143 
     | 
    
         
            +
                end
         
     | 
| 
      
 144 
     | 
    
         
            +
                
         
     | 
| 
      
 145 
     | 
    
         
            +
                # Add any necessary layouts to this post
         
     | 
| 
      
 146 
     | 
    
         
            +
                #   +layouts+ is a Hash of {"name" => "layout"}
         
     | 
| 
      
 147 
     | 
    
         
            +
                #   +site_payload+ is the site payload hash
         
     | 
| 
      
 148 
     | 
    
         
            +
                #
         
     | 
| 
      
 149 
     | 
    
         
            +
                # Returns nothing
         
     | 
| 
      
 150 
     | 
    
         
            +
                def render(layouts, site_payload)
         
     | 
| 
      
 151 
     | 
    
         
            +
                  # construct payload
         
     | 
| 
      
 152 
     | 
    
         
            +
                  payload =
         
     | 
| 
      
 153 
     | 
    
         
            +
                  {
         
     | 
| 
      
 154 
     | 
    
         
            +
                    "site" => { "related_posts" => related_posts(site_payload["site"]["posts"]) },
         
     | 
| 
      
 155 
     | 
    
         
            +
                    "page" => self.to_liquid,
         
     | 
| 
      
 156 
     | 
    
         
            +
                    "previous" => self.previous,
         
     | 
| 
      
 157 
     | 
    
         
            +
                    "next" => self.next
         
     | 
| 
      
 158 
     | 
    
         
            +
                  }
         
     | 
| 
      
 159 
     | 
    
         
            +
                  payload = payload.deep_merge(site_payload)
         
     | 
| 
      
 160 
     | 
    
         
            +
                  
         
     | 
| 
      
 161 
     | 
    
         
            +
                  do_layout(payload, layouts)
         
     | 
| 
      
 162 
     | 
    
         
            +
                end
         
     | 
| 
      
 163 
     | 
    
         
            +
                
         
     | 
| 
      
 164 
     | 
    
         
            +
                # Write the generated post file to the destination directory.
         
     | 
| 
      
 165 
     | 
    
         
            +
                #   +dest+ is the String path to the destination dir
         
     | 
| 
      
 166 
     | 
    
         
            +
                #
         
     | 
| 
      
 167 
     | 
    
         
            +
                # Returns nothing
         
     | 
| 
      
 168 
     | 
    
         
            +
                def write(dest)
         
     | 
| 
      
 169 
     | 
    
         
            +
                  FileUtils.mkdir_p(File.join(dest, dir))
         
     | 
| 
      
 170 
     | 
    
         
            +
                  
         
     | 
| 
      
 171 
     | 
    
         
            +
                  path = File.join(dest, self.url)
         
     | 
| 
      
 172 
     | 
    
         
            +
                  File.open(path, 'w') do |f|
         
     | 
| 
      
 173 
     | 
    
         
            +
                    f.write(self.output)
         
     | 
| 
      
 174 
     | 
    
         
            +
                  end
         
     | 
| 
      
 175 
     | 
    
         
            +
                end
         
     | 
| 
      
 176 
     | 
    
         
            +
                
         
     | 
| 
      
 177 
     | 
    
         
            +
                # Convert this post into a Hash for use in Liquid templates.
         
     | 
| 
      
 178 
     | 
    
         
            +
                #
         
     | 
| 
      
 179 
     | 
    
         
            +
                # Returns <Hash>
         
     | 
| 
      
 180 
     | 
    
         
            +
                def to_liquid
         
     | 
| 
      
 181 
     | 
    
         
            +
                  { "title" => self.data["title"] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
         
     | 
| 
      
 182 
     | 
    
         
            +
                    "url" => self.url,
         
     | 
| 
      
 183 
     | 
    
         
            +
                    "date" => self.date,
         
     | 
| 
      
 184 
     | 
    
         
            +
                    "id" => self.id,
         
     | 
| 
      
 185 
     | 
    
         
            +
                    "topics" => self.topics,
         
     | 
| 
      
 186 
     | 
    
         
            +
                    "content" => self.content }.deep_merge(self.data)
         
     | 
| 
      
 187 
     | 
    
         
            +
                end
         
     | 
| 
      
 188 
     | 
    
         
            +
                
         
     | 
| 
      
 189 
     | 
    
         
            +
                def inspect
         
     | 
| 
      
 190 
     | 
    
         
            +
                  "<Post: #{self.id}>"
         
     | 
| 
      
 191 
     | 
    
         
            +
                end
         
     | 
| 
      
 192 
     | 
    
         
            +
              end
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/jekyll/site.rb
    ADDED
    
    | 
         @@ -0,0 +1,173 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Jekyll
         
     | 
| 
      
 2 
     | 
    
         
            +
              
         
     | 
| 
      
 3 
     | 
    
         
            +
              class Site
         
     | 
| 
      
 4 
     | 
    
         
            +
                attr_accessor :source, :dest
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor :layouts, :posts, :categories
         
     | 
| 
      
 6 
     | 
    
         
            +
                
         
     | 
| 
      
 7 
     | 
    
         
            +
                # Initialize the site
         
     | 
| 
      
 8 
     | 
    
         
            +
                #   +source+ is String path to the source directory containing
         
     | 
| 
      
 9 
     | 
    
         
            +
                #            the proto-site
         
     | 
| 
      
 10 
     | 
    
         
            +
                #   +dest+ is the String path to the directory where the generated
         
     | 
| 
      
 11 
     | 
    
         
            +
                #          site should be written
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # Returns <Site>
         
     | 
| 
      
 14 
     | 
    
         
            +
                def initialize(source, dest)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  self.source = source
         
     | 
| 
      
 16 
     | 
    
         
            +
                  self.dest = dest
         
     | 
| 
      
 17 
     | 
    
         
            +
                  self.layouts = {}
         
     | 
| 
      
 18 
     | 
    
         
            +
                  self.posts = []
         
     | 
| 
      
 19 
     | 
    
         
            +
                  self.categories = Hash.new { |hash, key| hash[key] = Array.new }
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
                
         
     | 
| 
      
 22 
     | 
    
         
            +
                # Do the actual work of processing the site and generating the
         
     | 
| 
      
 23 
     | 
    
         
            +
                # real deal.
         
     | 
| 
      
 24 
     | 
    
         
            +
                #
         
     | 
| 
      
 25 
     | 
    
         
            +
                # Returns nothing
         
     | 
| 
      
 26 
     | 
    
         
            +
                def process
         
     | 
| 
      
 27 
     | 
    
         
            +
                  self.read_layouts
         
     | 
| 
      
 28 
     | 
    
         
            +
                  self.transform_pages
         
     | 
| 
      
 29 
     | 
    
         
            +
                  self.write_posts
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
                
         
     | 
| 
      
 32 
     | 
    
         
            +
                # Read all the files in <source>/_layouts except backup files
         
     | 
| 
      
 33 
     | 
    
         
            +
                # (end with "~") into memory for later use.
         
     | 
| 
      
 34 
     | 
    
         
            +
                #
         
     | 
| 
      
 35 
     | 
    
         
            +
                # Returns nothing
         
     | 
| 
      
 36 
     | 
    
         
            +
                def read_layouts
         
     | 
| 
      
 37 
     | 
    
         
            +
                  base = File.join(self.source, "_layouts")
         
     | 
| 
      
 38 
     | 
    
         
            +
                  entries = Dir.entries(base)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  entries = entries.reject { |e| e[-1..-1] == '~' }
         
     | 
| 
      
 40 
     | 
    
         
            +
                  entries = entries.reject { |e| File.directory?(File.join(base, e)) }
         
     | 
| 
      
 41 
     | 
    
         
            +
                  
         
     | 
| 
      
 42 
     | 
    
         
            +
                  entries.each do |f|
         
     | 
| 
      
 43 
     | 
    
         
            +
                    name = f.split(".")[0..-2].join(".")
         
     | 
| 
      
 44 
     | 
    
         
            +
                    self.layouts[name] = Layout.new(base, f)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                rescue Errno::ENOENT => e
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # ignore missing layout dir
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
                
         
     | 
| 
      
 50 
     | 
    
         
            +
                # Read all the files in <base>/_posts except backup files (end with "~")
         
     | 
| 
      
 51 
     | 
    
         
            +
                # and create a new Post object with each one.
         
     | 
| 
      
 52 
     | 
    
         
            +
                #
         
     | 
| 
      
 53 
     | 
    
         
            +
                # Returns nothing
         
     | 
| 
      
 54 
     | 
    
         
            +
                def read_posts(dir)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  base = File.join(self.source, dir, '_posts')
         
     | 
| 
      
 56 
     | 
    
         
            +
                  
         
     | 
| 
      
 57 
     | 
    
         
            +
                  entries = []
         
     | 
| 
      
 58 
     | 
    
         
            +
                  Dir.chdir(base) { entries = Dir['**/*'] }
         
     | 
| 
      
 59 
     | 
    
         
            +
                  entries = entries.reject { |e| e[-1..-1] == '~' }
         
     | 
| 
      
 60 
     | 
    
         
            +
                  entries = entries.reject { |e| File.directory?(File.join(base, e)) }
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  # first pass processes, but does not yet render post content
         
     | 
| 
      
 63 
     | 
    
         
            +
                  entries.each do |f|
         
     | 
| 
      
 64 
     | 
    
         
            +
                    if Post.valid?(f)
         
     | 
| 
      
 65 
     | 
    
         
            +
                      post = Post.new(self.source, dir, f)
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                      if post.published
         
     | 
| 
      
 68 
     | 
    
         
            +
                        self.posts << post
         
     | 
| 
      
 69 
     | 
    
         
            +
                        post.categories.each { |c| self.categories[c] << post }
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
      
 71 
     | 
    
         
            +
                    end
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  self.posts.sort!
         
     | 
| 
      
 75 
     | 
    
         
            +
                  
         
     | 
| 
      
 76 
     | 
    
         
            +
                  # second pass renders each post now that full site payload is available
         
     | 
| 
      
 77 
     | 
    
         
            +
                  self.posts.each_with_index do |post, idx|
         
     | 
| 
      
 78 
     | 
    
         
            +
                    post.previous = posts[idx - 1] unless idx - 1 < 0
         
     | 
| 
      
 79 
     | 
    
         
            +
                    post.next = posts[idx + 1] unless idx + 1 >= posts.size
         
     | 
| 
      
 80 
     | 
    
         
            +
                    post.render(self.layouts, site_payload)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
                  
         
     | 
| 
      
 83 
     | 
    
         
            +
                  self.categories.values.map { |cats| cats.sort! { |a, b| b <=> a} }
         
     | 
| 
      
 84 
     | 
    
         
            +
                rescue Errno::ENOENT => e
         
     | 
| 
      
 85 
     | 
    
         
            +
                  # ignore missing layout dir
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
                
         
     | 
| 
      
 88 
     | 
    
         
            +
                # Write each post to <dest>/<year>/<month>/<day>/<slug>
         
     | 
| 
      
 89 
     | 
    
         
            +
                #
         
     | 
| 
      
 90 
     | 
    
         
            +
                # Returns nothing
         
     | 
| 
      
 91 
     | 
    
         
            +
                def write_posts
         
     | 
| 
      
 92 
     | 
    
         
            +
                  self.posts.each do |post|
         
     | 
| 
      
 93 
     | 
    
         
            +
                    post.write(self.dest)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
                
         
     | 
| 
      
 97 
     | 
    
         
            +
                # Copy all regular files from <source> to <dest>/ ignoring
         
     | 
| 
      
 98 
     | 
    
         
            +
                # any files/directories that are hidden or backup files (start
         
     | 
| 
      
 99 
     | 
    
         
            +
                # with "." or end with "~") or contain site content (start with "_")
         
     | 
| 
      
 100 
     | 
    
         
            +
                # unless they are "_posts" directories or web server files such as
         
     | 
| 
      
 101 
     | 
    
         
            +
                # '.htaccess'
         
     | 
| 
      
 102 
     | 
    
         
            +
                #   The +dir+ String is a relative path used to call this method
         
     | 
| 
      
 103 
     | 
    
         
            +
                #            recursively as it descends through directories
         
     | 
| 
      
 104 
     | 
    
         
            +
                #
         
     | 
| 
      
 105 
     | 
    
         
            +
                # Returns nothing
         
     | 
| 
      
 106 
     | 
    
         
            +
                def transform_pages(dir = '')
         
     | 
| 
      
 107 
     | 
    
         
            +
                  base = File.join(self.source, dir)
         
     | 
| 
      
 108 
     | 
    
         
            +
                  entries = Dir.entries(base)
         
     | 
| 
      
 109 
     | 
    
         
            +
                  entries = entries.reject { |e| e[-1..-1] == '~' }
         
     | 
| 
      
 110 
     | 
    
         
            +
                  entries = entries.reject do |e|
         
     | 
| 
      
 111 
     | 
    
         
            +
                    (e != '_posts') and ['.', '_'].include?(e[0..0]) unless ['.htaccess'].include?(e)
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
      
 113 
     | 
    
         
            +
                  directories = entries.select { |e| File.directory?(File.join(base, e)) }
         
     | 
| 
      
 114 
     | 
    
         
            +
                  files = entries.reject { |e| File.directory?(File.join(base, e)) }
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  # we need to make sure to process _posts *first* otherwise they 
         
     | 
| 
      
 117 
     | 
    
         
            +
                  # might not be available yet to other templates as {{ site.posts }}
         
     | 
| 
      
 118 
     | 
    
         
            +
                  if entries.include?('_posts')
         
     | 
| 
      
 119 
     | 
    
         
            +
                    entries.delete('_posts')
         
     | 
| 
      
 120 
     | 
    
         
            +
                    read_posts(dir)
         
     | 
| 
      
 121 
     | 
    
         
            +
                  end
         
     | 
| 
      
 122 
     | 
    
         
            +
                  [directories, files].each do |entries|
         
     | 
| 
      
 123 
     | 
    
         
            +
                    entries.each do |f|
         
     | 
| 
      
 124 
     | 
    
         
            +
                      if File.directory?(File.join(base, f))
         
     | 
| 
      
 125 
     | 
    
         
            +
                        next if self.dest.sub(/\/$/, '') == File.join(base, f)
         
     | 
| 
      
 126 
     | 
    
         
            +
                        transform_pages(File.join(dir, f))
         
     | 
| 
      
 127 
     | 
    
         
            +
                      else
         
     | 
| 
      
 128 
     | 
    
         
            +
                        first3 = File.open(File.join(self.source, dir, f)) { |fd| fd.read(3) }
         
     | 
| 
      
 129 
     | 
    
         
            +
                    
         
     | 
| 
      
 130 
     | 
    
         
            +
                        if first3 == "---"
         
     | 
| 
      
 131 
     | 
    
         
            +
                          # file appears to have a YAML header so process it as a page
         
     | 
| 
      
 132 
     | 
    
         
            +
                          page = Page.new(self.source, dir, f)
         
     | 
| 
      
 133 
     | 
    
         
            +
                          page.render(self.layouts, site_payload)
         
     | 
| 
      
 134 
     | 
    
         
            +
                          page.write(self.dest)
         
     | 
| 
      
 135 
     | 
    
         
            +
                        else
         
     | 
| 
      
 136 
     | 
    
         
            +
                          # otherwise copy the file without transforming it
         
     | 
| 
      
 137 
     | 
    
         
            +
                          FileUtils.mkdir_p(File.join(self.dest, dir))
         
     | 
| 
      
 138 
     | 
    
         
            +
                          FileUtils.cp(File.join(self.source, dir, f), File.join(self.dest, dir, f))
         
     | 
| 
      
 139 
     | 
    
         
            +
                        end
         
     | 
| 
      
 140 
     | 
    
         
            +
                      end
         
     | 
| 
      
 141 
     | 
    
         
            +
                    end
         
     | 
| 
      
 142 
     | 
    
         
            +
                  end
         
     | 
| 
      
 143 
     | 
    
         
            +
                end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                # Constructs a hash map of Posts indexed by the specified Post attribute
         
     | 
| 
      
 146 
     | 
    
         
            +
                #
         
     | 
| 
      
 147 
     | 
    
         
            +
                # Returns {post_attr => [<Post>]}
         
     | 
| 
      
 148 
     | 
    
         
            +
                def post_attr_hash(post_attr)
         
     | 
| 
      
 149 
     | 
    
         
            +
                  # Build a hash map based on the specified post attribute ( post attr => array of posts )
         
     | 
| 
      
 150 
     | 
    
         
            +
                  # then sort each array in reverse order
         
     | 
| 
      
 151 
     | 
    
         
            +
                  hash = Hash.new { |hash, key| hash[key] = Array.new }
         
     | 
| 
      
 152 
     | 
    
         
            +
                  self.posts.each { |p| p.send(post_attr.to_sym).each { |t| hash[t] << p } }
         
     | 
| 
      
 153 
     | 
    
         
            +
                  hash.values.map { |sortme| sortme.sort! { |a, b| b <=> a} }
         
     | 
| 
      
 154 
     | 
    
         
            +
                  return hash
         
     | 
| 
      
 155 
     | 
    
         
            +
                end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                # The Hash payload containing site-wide data
         
     | 
| 
      
 158 
     | 
    
         
            +
                #
         
     | 
| 
      
 159 
     | 
    
         
            +
                # Returns {"site" => {"time" => <Time>,
         
     | 
| 
      
 160 
     | 
    
         
            +
                #                     "posts" => [<Post>],
         
     | 
| 
      
 161 
     | 
    
         
            +
                #                     "categories" => [<Post>],
         
     | 
| 
      
 162 
     | 
    
         
            +
                #                     "topics" => [<Post>] }}
         
     | 
| 
      
 163 
     | 
    
         
            +
                def site_payload
         
     | 
| 
      
 164 
     | 
    
         
            +
                  {"site" => {
         
     | 
| 
      
 165 
     | 
    
         
            +
                    "time" => Time.now, 
         
     | 
| 
      
 166 
     | 
    
         
            +
                    "posts" => self.posts.sort { |a,b| b <=> a },
         
     | 
| 
      
 167 
     | 
    
         
            +
                    "categories" => post_attr_hash('categories'),
         
     | 
| 
      
 168 
     | 
    
         
            +
                    "topics" => post_attr_hash('topics')
         
     | 
| 
      
 169 
     | 
    
         
            +
                  }}
         
     | 
| 
      
 170 
     | 
    
         
            +
                end
         
     | 
| 
      
 171 
     | 
    
         
            +
              end
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Jekyll
         
     | 
| 
      
 2 
     | 
    
         
            +
              
         
     | 
| 
      
 3 
     | 
    
         
            +
              class HighlightBlock < Liquid::Block
         
     | 
| 
      
 4 
     | 
    
         
            +
                include Liquid::StandardFilters
         
     | 
| 
      
 5 
     | 
    
         
            +
                # we need a language, but the linenos argument is optional.
         
     | 
| 
      
 6 
     | 
    
         
            +
                SYNTAX = /(\w+)\s?(:?linenos)?\s?/
         
     | 
| 
      
 7 
     | 
    
         
            +
                
         
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(tag_name, markup, tokens)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  super
         
     | 
| 
      
 10 
     | 
    
         
            +
                  if markup =~ SYNTAX
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @lang = $1
         
     | 
| 
      
 12 
     | 
    
         
            +
                    if defined? $2
         
     | 
| 
      
 13 
     | 
    
         
            +
                      # additional options to pass to Albino.
         
     | 
| 
      
 14 
     | 
    
         
            +
                      @options = { 'O' => 'linenos=inline' }
         
     | 
| 
      
 15 
     | 
    
         
            +
                    else
         
     | 
| 
      
 16 
     | 
    
         
            +
                      @options = {}
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  else
         
     | 
| 
      
 19 
     | 
    
         
            +
                    raise SyntaxError.new("Syntax Error in 'highlight' - Valid syntax: highlight <lang> [linenos]")
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              
         
     | 
| 
      
 23 
     | 
    
         
            +
                def render(context)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  if Jekyll.pygments
         
     | 
| 
      
 25 
     | 
    
         
            +
                    render_pygments(context, super.to_s)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  else
         
     | 
| 
      
 27 
     | 
    
         
            +
                    render_codehighlighter(context, super.to_s)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
                
         
     | 
| 
      
 31 
     | 
    
         
            +
                def render_pygments(context, code)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  if Jekyll.content_type == :markdown
         
     | 
| 
      
 33 
     | 
    
         
            +
                    return "\n" + Albino.new(code, @lang).to_s(@options) + "\n"
         
     | 
| 
      
 34 
     | 
    
         
            +
                  else
         
     | 
| 
      
 35 
     | 
    
         
            +
                    "<notextile>" + Albino.new(code, @lang).to_s(@options) + "</notextile>"
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
                
         
     | 
| 
      
 39 
     | 
    
         
            +
                def render_codehighlighter(context, code)
         
     | 
| 
      
 40 
     | 
    
         
            +
                #The div is required because RDiscount blows ass
         
     | 
| 
      
 41 
     | 
    
         
            +
                  <<-HTML
         
     | 
| 
      
 42 
     | 
    
         
            +
            <div>
         
     | 
| 
      
 43 
     | 
    
         
            +
              <pre>
         
     | 
| 
      
 44 
     | 
    
         
            +
                <code class='#{@lang}'>#{h(code).strip}</code>
         
     | 
| 
      
 45 
     | 
    
         
            +
              </pre>
         
     | 
| 
      
 46 
     | 
    
         
            +
            </div>
         
     | 
| 
      
 47 
     | 
    
         
            +
                  HTML
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
              
         
     | 
| 
      
 51 
     | 
    
         
            +
            end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
            Liquid::Template.register_tag('highlight', Jekyll::HighlightBlock)
         
     | 
| 
         @@ -0,0 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Jekyll
         
     | 
| 
      
 2 
     | 
    
         
            +
              
         
     | 
| 
      
 3 
     | 
    
         
            +
              class IncludeTag < Liquid::Tag
         
     | 
| 
      
 4 
     | 
    
         
            +
                def initialize(tag_name, file, tokens)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  super
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @file = file.strip
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
                
         
     | 
| 
      
 9 
     | 
    
         
            +
                def render(context)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  if @file !~ /^[a-zA-Z0-9_\/\.-]+$/ || @file =~ /\.\// || @file =~ /\/\./
         
     | 
| 
      
 11 
     | 
    
         
            +
                    return "Include file '#{@file}' contains invalid characters or sequences"
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
                  
         
     | 
| 
      
 14 
     | 
    
         
            +
                  Dir.chdir(File.join(Jekyll.source, '_includes')) do
         
     | 
| 
      
 15 
     | 
    
         
            +
                    choices = Dir['**/*'].reject { |x| File.symlink?(x) }
         
     | 
| 
      
 16 
     | 
    
         
            +
                    if choices.include?(@file)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      source = File.read(@file)
         
     | 
| 
      
 18 
     | 
    
         
            +
                      partial = Liquid::Template.parse(source)
         
     | 
| 
      
 19 
     | 
    
         
            +
                      context.stack do
         
     | 
| 
      
 20 
     | 
    
         
            +
                        partial.render(context)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      end
         
     | 
| 
      
 22 
     | 
    
         
            +
                    else
         
     | 
| 
      
 23 
     | 
    
         
            +
                      "Included file '#{@file}' not found in _includes directory"
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
              
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            Liquid::Template.register_tag('include', Jekyll::IncludeTag)
         
     |