lib/shinmun.rb in shinmun-0.1 vs lib/shinmun.rb in shinmun-0.2
- old
+ new
@@ -1,420 +1,23 @@
require 'rubygems'
require 'fileutils'
require 'erb'
require 'yaml'
-require 'uuid'
+require 'time'
+
require 'bluecloth'
require 'rubypants'
-require 'rexml/document'
-# A small and beautiful blog engine.
-module Shinmun
+begin; require 'redcloth'; rescue LoadError; end
+begin; require 'packr'; rescue LoadError; end
- # strip html tags from string
- def self.strip_tags(html)
- REXML::Document.new(html).each_element( './/text()' ).join
- end
+require 'shinmun/cache'
+require 'shinmun/post'
+require 'shinmun/comment'
+require 'shinmun/template'
+require 'shinmun/helpers'
+require 'shinmun/blog'
- def self.urlify(string)
- string.downcase.gsub(/[ -]+/, '-').gsub(/[^-a-z0-9_]+/, '')
- end
-
-
- # This class represents an article or page.
- # A post has a header and body text.
- # Example:
- # ---
- # category: Ruby
- # date: 2008-09-05
- # guid: 7ad04f10-5dd6-012b-b53c-001a92975b89
- #
- # BlueCloth, a Markdown library
- # =============================
- #
- # This is the summary, which is by definition the first paragraph of the
- # article. The summary shows up in list views and rss feeds.
- class Post
-
- attr_reader :blog, :title, :path, :head, :src
-
- # Split up the source text into header and body.
- # Load the header as yaml document.
- def initialize(blog, path)
- @src = File.read(path)
- @blog = blog
- @path = path.chomp('.md')
-
- case @src
- when /---.*?\n(.*?)\n\n(.*?)\n.*?\n(.*)/m
- @head = YAML.load($1)
- @title = $2
- @body = $3
-
- when /(.*?)\n.*?\n(.*)/m
- @head = {}
- @title = $1
- @body = $2
- else
- raise "This file is empty!"
- end
- end
-
- # Generates the body from source text.
- def body
- @__body__ ||= RubyPants.new(BlueCloth.new(@body).to_html).to_html
- end
-
- # Generate an unique id.
- def generate_guid
- @head['guid'] = UUID.new.generate
- end
-
- def date ; @head['date'] end
- def tags ; @head['tags'] end
- def languages; @head['languages'] end
- def category ; @head['category'] end
- def guid ; @head['guid'] end
- def year ; date.year end
- def month ; date.month end
-
- # Return the first paragraph of rendered html.
- def summary
- body.split("\n\n")[0]
- end
-
- # Return doc root as relative path.
- def root
- slashes = path.count('/')
- if slashes > 0
- Array.new(slashes, '..').join('/') + '/'
- else
- ''
- end
- end
-
- # Return a hash of post attributes.
- def variables
- head.merge(:root => root,
- :header => category || title,
- :title => title,
- :body => body,
- :link => link)
- end
-
- # Return absolute link to this post.
- def link
- "#{blog.meta['blog_url']}/#{path}.html"
- end
-
- end
-
-
- # This class renders an ERB template for a set of attributes, which
- # are accessible as instance variables.
- class Template
-
- attr_reader :root
-
- # Initialize this template with an ERB instance.
- def initialize(erb)
- @erb = erb
- end
-
- # Set instance variable for this template.
- def set_variables(vars)
- for name, value in vars
- instance_variable_set("@#{name}", value)
- end
- end
-
- # Render this template.
- def render
- @erb.result(binding)
- end
-
- # Render a hash as attributes for a HTML tag.
- def attributes(attributes)
- attributes.map { |k, v| %Q{#{k}="#{v}"} }.join(' ')
- end
-
- # Render a HTML tag with given name.
- # The last argument specifies the attributes of the tag.
- # The second argument may be the content of the tag.
- def tag(name, *args)
- text, attributes = args.first.is_a?(Hash) ? [nil, args.first] : args
- "<#{name} #{attributes(attributes)}>#{text}</#{name}>"
- end
-
- # Render stylesheet link tags with fixed url.
- def stylesheet_link_tag(*names)
- names.map { |name|
- mtime = File.mtime("public/stylesheets/#{name}.css").to_i
- path = "#{root}stylesheets/#{name}.css?#{mtime}"
- tag :link, :href => path, :rel => 'stylesheet', :media => 'screen'
- }.join("\n")
- end
-
- # Render javascript tags with fixed url.
- def javascript_tag(*names)
- names.map { |name|
- mtime = File.mtime("public/javascripts/#{name}.js").to_i
- path = "#{root}javascripts/#{name}.js?#{mtime}"
- tag :script, :src => path, :type => 'text/javascript'
- }.join("\n")
- end
-
- # Render an image tag with fixed url.
- def image_tag(src, options = {})
- tag :img, options.merge(:src => root + 'images/' + src)
- end
-
- # Render a link with fixed url.
- def link_to(text, path, options = {})
- tag :a, text, options.merge(:href => root + path + '.html')
- end
-
- # Render a link for the navigation bar. If the text of the link
- # matches the @header variable, the css class will be set to acitve.
- def navi_link(text, path)
- link_to text, path, :class => (text == @header) ? 'active' : nil
- end
-
- # Render a link to a post with fixed url.
- def post_link(post)
- link_to post.title, post.path
- end
-
- # Render a link to an archive page.
- def month_link(year, month)
- link_to "#{Date::MONTHNAMES[month]} #{year}", "#{year}/#{month}/index"
- end
-
- # Render a date or time in a nice human readable format.
- def date(time)
- "%s %d, %d" % [Date::MONTHNAMES[time.month], time.day, time.year]
- end
-
- # Render a date or time in rfc822 format. This will be used for rss rendering.
- def rfc822(time)
- time.strftime("%a, %d %b %Y %H:%M:%S %z")
- end
-
- def strip_tags(html)
- Shinmun.strip_tags(html)
- end
-
- end
-
-
- # This class represents a blog. You need to provide a source
- # directory and the meta file `blog.yml` which defines some variables.
- # Example for `blog.yml`:
- # blog_title: Matthias Georgi
- # blog_description: Webdev, Gamedev, Interaction Design
- # blog_language: en
- # blog_author: Matthias Georgi
- # blog_url: http://www.matthias-georgi.de
- # categories:
- # - Ruby
- # - Emacs
- class Blog
-
- attr_reader :meta, :posts, :pages
-
- # Read all posts from disk.
- def initialize
- @uuid = UUID.new
-
- Dir.chdir('posts') do
- @meta = YAML.load(File.read('blog.yml'))
-
- @posts, @pages = Dir['**/*.md'].
- map { |path| Post.new(self, path) }.
- partition {|p| p.date }
-
- @posts = @posts.sort_by { |post| post.date }.reverse
- end
-
- @templates = {}
- end
-
- def categories
- meta['categories']
- end
-
- def create_post(title)
- date = Date.today
- name = Shinmun.urlify(title)
- file = "posts/#{date.year}/#{date.month}/#{name}.md"
-
- if File.exist?(file)
- raise "#{file} exists"
- else
- puts "creating #{file}"
- head = {
- 'date' => date,
- 'guid' => @uuid.generate,
- 'category' => ''
- }
- FileUtils.mkdir_p(File.dirname(file))
- File.open(file, "w") do |io|
- io.puts head.to_yaml
- io.puts
- io.puts title
- io.puts "=" * title.size
- io.puts
- end
- end
- end
-
- # Return template variables as hash.
- def variables
- meta.merge(:posts => posts,
- :months => months,
- :categories => categories)
- end
-
- # Read and cache template file.
- def template(name)
- @templates[name] ||= ERB.new(File.read("templates/#{name}"))
- end
-
- # Render template with given variables.
- def render_template(name, vars)
- template = Template.new(template(name))
- template.set_variables(vars)
- template.render
- end
-
- # Render template and insert into layout with given variables.
- def render(name, vars)
- vars = variables.merge(vars)
- content = render_template(name, vars)
- if name =~ /\.rxml$/
- content
- else
- render_template("layout.rhtml", vars.merge(:content => content))
- end
- end
-
- # Write a file to output directory.
- def write_file(path, data)
- FileUtils.mkdir_p('public/' + File.dirname(path))
- open('public/' + path, 'wb') do |file|
- file << data
- end
- end
-
- # Render a template and write to file.
- def render_file(path, name, vars)
- puts path
- write_file(path, render(name, vars))
- end
-
- # Return all posts for a given month.
- def posts_for_month(year, month)
- posts.select { |p| p.year == year and p.month == month }
- end
-
- # Return all posts for given tag.
- def posts_for_tag(tag)
- tag = tag.downcase
- posts.select { |p| p.tags.to_s.match(tag) }
- end
-
- # Return all posts in given category.
- def posts_for_category(category)
- posts.select { |p| p.category == category }
- end
-
- # Return all months as tuples of [year, month].
- def months
- posts.map { |p| [p.year, p.month] }.uniq.sort
- end
-
- # Write all posts.
- def write_posts
- for post in posts
- render_file("#{post.path}.html",
- "post.rhtml",
- post.variables)
- end
- end
-
- # Write all pages.
- def write_pages
- for page in pages
- render_file("#{page.path}.html",
- "page.rhtml",
- page.variables)
- end
- end
-
- # Write archive summaries.
- def write_archives
- for year, month in months
- path = "#{year}/#{month}"
- month_name = Date::MONTHNAMES[month]
- posts = posts_for_month(year, month)
-
- render_file("#{path}/index.html",
- "posts.rhtml",
- :header => "#{month_name} #{year}",
- :year => year,
- :month => month_name,
- :posts => posts,
- :root => '../../')
- end
- end
-
- # Write category summaries.
- def write_categories
- for category in categories
- posts = posts_for_category(category)
- render_file("categories/#{category.downcase}.html",
- "posts.rhtml",
- :header => category,
- :category => category,
- :posts => posts,
- :root => '../')
- end
- end
-
- # Write index page.
- def write_index
- render_file("index.html",
- "posts.rhtml",
- :header => 'Home',
- :posts => posts[0, 10],
- :root => '')
- end
-
- # Write rss feeds for index page and categories.
- def write_feeds
- render_file("index.rss",
- "feed.rxml",
- :posts => posts[0, 10],
- :root => '')
-
- for category in categories
- posts = posts_for_category(category)
- render_file("categories/#{category.downcase}.rss",
- "feed.rxml",
- :category => category,
- :posts => posts,
- :root => '../')
- end
- end
-
- def write_all
- write_posts
- write_pages
- write_archives
- write_categories
- write_index
- write_feeds
- end
-
- end
-
-end
+require 'shinmun/aggregations/audioscrobbler'
+require 'shinmun/aggregations/delicious'
+require 'shinmun/aggregations/flickr'
+require 'shinmun/aggregations/fortythree'