lib/shinmun/blog.rb in shinmun-0.2 vs lib/shinmun/blog.rb in shinmun-0.5

- old
+ new

@@ -1,319 +1,181 @@ module Shinmun + ROOT = File.expand_path(File.dirname(__FILE__) + '/../..') - class Blog + class Blog < Kontrol::Application + include Helpers - # Define reader methods for configuration. - def self.config_reader(file, *names) - names.each do |name| - name = name.to_s - define_method(name) { @config[file][name] } - end + attr_accessor :config, :store, :posts, :pages + + %w[ base_path title description language author categories ].each do |name| + define_method(name) { @config[name.to_sym] } end - attr_reader :root, :posts, :pages, :aggregations - - config_reader 'config/assets.yml', :javascript_files, :stylesheet_files, :base_path, :images_path, :javascripts_path, :stylesheets_path - config_reader 'config/blog.yml', :title, :description, :language, :author, :url, :repository - config_reader 'config/categories.yml', :categories + # Initialize the blog + def initialize(path) + super - # Initialize the blog, load the config file and write the index files. - def initialize - @config = Cache.new do |file| - YAML.load(File.read(file)) - end - - @stylesheets = Cache.new - - @templates = Cache.new do |file| - ERB.new(File.read(file)) - end - - @javascripts = Cache.new do |file| - script = File.read(file) - script = Packr.pack(script) if defined?(Packr) - "/* #{file} */\n #{script}\n" - end - - @posts_cache = Cache.new do |file| - Post.new(:filename => file).load - end - - @pages_cache = Cache.new do |file| - Post.new(:filename => file).load - end - - Dir['pages/**/*'].each do |file| - @pages_cache.load(file) if File.file?(file) and file[-1, 1] != '~' - end - - Dir['posts/**/*'].each do |file| - @posts_cache.load(file) if File.file?(file) and file[-1, 1] != '~' - end - - @config.load('config/aggregations.yml') - @config.load('config/assets.yml') - @config.load('config/blog.yml') - @config.load('config/categories.yml') - - @aggregations = {} - - load_aggregations - - Thread.start do - sleep 300 - load_aggregations - end + @config = {} + @store = GitStore.new(path) + @store.handler['md'] = PostHandler.new + @store.handler['rhtml'] = ERBHandler.new + @store.handler['rxml'] = ERBHandler.new end - def load_aggregations - @config['config/aggregations.yml'].to_a.each do |c| - @aggregations[c['name']] = Object.const_get(c['class']).new(c['url']) - end - end + def self.init(path) + path = File.expand_path(path) + Dir.mkdir(path) - # Reload config, assets and posts. - def reload - if @config.dirty? || @templates.dirty? - @config.reload! - @templates.reload! - @posts_cache.reload! - @pages_cache.reload! - else - @config.reload_dirty! - @templates.reload_dirty! - @posts_cache.reload_dirty! - @pages_cache.reload_dirty! - end + FileUtils.cp_r "#{ROOT}/assets", path + FileUtils.cp_r "#{ROOT}/templates", path + FileUtils.cp "#{ROOT}/config.ru", path - @posts = @posts_cache.values.sort_by { |p| p.date }.reverse - @pages = @pages_cache.values + Dir.mkdir("#{path}/posts") + Dir.mkdir("#{path}/pages") + Dir.mkdir("#{path}/comments") + Dir.mkdir("#{path}/public") - pack_javascripts if @javascripts.dirty? or @javascripts.empty? - pack_stylesheets if @stylesheets.dirty? or @stylesheets.empty? + FileUtils.ln_s("../assets", "#{path}/public/assets") - load 'templates/helpers.rb' - end - - # Use rsync to synchronize the rendered blog to web server. - def push - system "rsync -avz public/ #{repository}" - end - - def urlify(string) - string.downcase.gsub(/[ -]+/, '-').gsub(/[^-a-z0-9_]+/, '') - end - - # Compress the javascripts using PackR and write them to one file called 'all.js'. - def pack_javascripts - @javascripts.reload_dirty! - File.open("assets/#{javascripts_path}/all.js", "wb") do |io| - for file in javascript_files - io << @javascripts["assets/#{javascripts_path}/#{file.strip}.js"] << "\n\n" - end + Dir.chdir(path) do + `git init` + `git add .` + `git commit -m 'init'` end end - # Pack the stylesheets and write them to one file called 'all.css'. - def pack_stylesheets - @stylesheets.reload_dirty! - File.open("assets/#{stylesheets_path}/all.css", "wb") do |io| - for file in stylesheet_files - io << @stylesheets["assets/#{stylesheets_path}/#{file.strip}.css"] << "\n\n" - end - end + def load_template(file) + store['templates/' + file] end - # Write a file to output directory. - def write_file(path, data) - FileUtils.mkdir_p("public/#{base_path}/#{File.dirname path}") - - open("public/#{base_path}/#{path}", 'wb') do |file| - file << data - end + def render(name, vars = {}) + super(name, vars.merge(:blog => self)) end - # Return all posts for a given month. - def posts_for_month(year, month) - posts.select { |p| p.year == year and p.month == month } + def pages + store.tree('pages').values.select { |v| Post === v } end - # Return all posts in given category. - def posts_for_category(category) - name = category['name'] - posts.select { |p| p.category == name } + def posts + store.tree('posts').values.select { |v| Post === v }.sort_by { |p| p.date.to_s }.reverse end - # Return all posts with any of given tags. - def posts_with_tags(tags) - return [] if tags.nil? - tags = tags.split(',').map { |t| t.strip } if tags.is_a?(String) - posts.select do |post| - tags.any? do |tag| - post.tags.to_s.include?(tag) - end + def call(env) + if ENV['RACK_ENV'] == 'production' + store.load if store.changed? + else + store.load(true) end + + super end - # Return all months as tuples of [year, month]. - def months - posts.map { |p| [p.year, p.month] }.uniq.sort + def url + "http://#{request.host}" end - # Create a new post with given title. - def create_post(title) - date = Date.today - name = urlify(title) - path = "#{date.year}/#{date.month}/#{name}.md" - - if File.exist?(file) - raise "#{file} exists" - else - Post.new(:path => path, - :title => title, - :date => date).save + def symbolize_keys(hash) + hash.inject({}) do |h, (k, v)| + h[k.to_sym] = v + h end end - def find_page(path) - pages.find { |p| p.path == path } + def transaction(message, &block) + store.transaction(message, &block) end - def find_post(path) - posts.find { |p| p.path == path } + def post_file(post) + 'posts' + post_path(post) + '.' + post.type end - def find_category(category) - category = urlify(category) - categories.find { |c| urlify(c['name']) == category } + def page_file(post) + 'pages' + page_path(post) + '.' + post.type end - # Render template with given variables. - def render_template(name, vars) - template = Template.new(@templates["templates/#{name}"], name) - template.set_variables(vars) - template.render + def comment_file(post) + 'comments/' + post_path(post) + '.yml' end - def render_layout(vars) - render_template("layout.rhtml", vars.merge(:blog => self)) - end + def create_post(attr) + post = Post.new(attr) + path = post_file(post) - # Render named template and insert into layout with given variables. - def render(name, vars) - render_layout(vars.merge(:content => render_template(name, vars.merge(:blog => self)))) - end + transaction "create post `#{post.title}'" do + store[path] = post + end - # Render given post using the post template and the layout template. - def render_post(post) - render('post.rhtml', post.variables.merge(:header => post.category)) + post end - # Render given page using only the layout template. - def render_page(page) - render_layout(page.variables.merge(:content => page.body_html)) - end + def create_page(attr) + post = Post.new(attr) + path = page_file(post) - # Render comments. - def render_comments(comments) - render_template('comments.rhtml', :comments => comments) - end + transaction "create page `#{post.title}'" do + store[path] = post + end - # Render index page using the index and the layout template. - def render_index_page - render('index.rhtml', - :header => 'Home', - :posts => posts) + post end - # Render the category summary for given category. - def render_category(category) - posts = posts_for_category(category) - render("category.rhtml", - :header => category['name'], - :category => category, - :posts => posts) + def comments_for(post) + store[comment_file post] || [] end - # Render the archive summary for given month. - def render_month(year, month) - path = "#{year}/#{month}" - month_name = Date::MONTHNAMES[month] - posts = posts_for_month(year, month) - render("month.rhtml", - :header => "#{month_name} #{year}", - :year => year, - :month => month_name, - :posts => posts) + def create_comment(post, params) + path = comment_file(post) + comments = comments_for(post) + comment = Comment.new(params) + + transaction "new comment for `#{post.title}'" do + store[path] = comments + [comment] + end + + comment end - # Render index feed using the feed template. - def render_index_feed - render_template("index.rxml", - :blog => self, - :posts => posts) + def find_page(name) + pages.find { |p| p.name == name } end - # Render category feed for given category using the feed template . - def render_category_feed(category) - render_template("category.rxml", - :blog => self, - :category => category, - :posts => posts_for_category(category)) + def find_post(year, month, name) + posts.find { |p| p.year == year and p.month == month and p.name == name } end - def write_index_page - write_file("index.html", render_index_page) + def find_category(permalink) + name = categories.find { |name| urlify(name) == permalink } + + { :name => name, + :posts => posts.select { |p| p.category == name }, + :permalink => permalink } end - # Write all pages. - def write_pages - for page in pages - write_file("#{page.path}.html", render_page(page)) - end + def recent_posts + posts[0, 20] end - # Write all posts. - def write_posts - for post in posts - write_file("#{post.path}.html", render_post(post)) - 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 - # Write archive summaries. - def write_archives - for year, month in months - write_file("#{year}/#{month}/index.html", render_month(year, month)) + # Return all posts with any of given tags. + def posts_with_tags(tags) + return [] if tags.nil? or tags.empty? + tags = tags.split(',').map { |t| t.strip } if tags.is_a?(String) + + posts.select do |post| + tags.any? do |tag| + post.tag_list.include?(tag) + end end end - # Write category summaries. - def write_categories - for category in categories - write_file("categories/#{urlify category['name']}.html", render_category(category)) - end + # Return all archives as tuples of [year, month]. + def archives + posts.map { |p| [p.year, p.month] }.uniq.sort end - # Write rss feeds for index page and categories. - def write_feeds - write_file("index.rss", render_index_feed) - for category in categories - write_file("categories/#{urlify category['name']}.rss", render_category_feed(category)) - end - end - - # Render everything to public folder. - def write_all - load_aggregations - reload - - write_index_page - write_pages - write_posts - write_archives - write_categories - write_feeds - end - end - + end