# See Bloggit::Generator module Bloggit # = Generator class Generator attr_reader :site, :cache_dir, :template_dir, :errors def initialize(site, verbose=false) @v = verbose @site = site @cache_dir = File.join(site.base_path, 'cache') @blog_root = site.blog_path # settings.fetch('blog_root', '') @site_home = site.home_page # settings.fetch('homepage', 'blog') Template.config { |t| t.site = @site } end def cleanup_cache(opts={}) Bloggit::fire_event(:before_cleanup) puts_if_verbose "Cleaning up files from cache folder..." FileUtils.rm_rf Dir.glob( File.join(@cache_dir, '*') ) puts_if_verbose "Done." rescue puts "Done with errors:\n\n#{$!}" ensure Bloggit::fire_event(:after_cleanup) end def generate_cache(opts={}) @errors = {} errs = Bloggit::fire_event(:before_generate, @site, @cache_dir) @errors.merge!(errs) if errs.is_a?( Hash ) puts_if_verbose "Blog generation..." gen_homepage(site.latest_posts) if @site.blog_path != '' create_folder site.blog_path gen_inline_archive(site.latest_posts, :path=>@site.build_post_path('index.html')) end post_yrs = site.posts.group_by &:post_year post_yrs.each do |year, posts_in_year| create_folder @site.blog_path, year.to_s gen_year_archive year, posts_in_year post_mths = posts_in_year.group_by &:post_month post_mths.each do |month, posts_in_month| create_folder @site.blog_path, year.to_s, month.to_s gen_month_archive year, month, posts_in_month posts_in_month.each do |post| gen_post(post) end end end if site.has_tags? create_folder 'tags' gen_tags_archive(site.tags) site.tags.each do |tag| gen_tag_archive(tag) end end puts_if_verbose "News feed..." gen_news_feed(site.posts) puts_if_verbose "Static page generation..." site.pages.each do |page| gen_page(page) end puts_if_verbose "Copying media files..." media_root = File.join(site.base_path, 'media') FileUtils.cp_r media_root, cache_dir, :verbose=>false if File.exists?(media_root) puts_if_verbose "Copying '#{site.theme}' theme files..." create_folder 'theme' theme_root = File.join(site.base_path, 'themes', site.theme) theme_dirs = [] %w(images scripts styles).each {|dir| theme_dirs << File.join(theme_root, dir) } FileUtils.cp_r theme_dirs, File.join(cache_dir, 'theme'), :verbose=>false errs = Bloggit::fire_event(:after_generate, @site, @cache_dir) @errors.merge!(errs) if errs.is_a?( Hash ) errs = Bloggit::fire_event(:before_finish, @site, @cache_dir) @errors.merge!(errs) if errs.is_a?( Hash ) puts_if_verbose "Generating cache checksum..." generate_checksums puts_if_verbose '' if @errors.keys.length == 0 puts "Done." true else puts "Done with errors:" @errors.each do |err, items| puts ">>> #{err}:" items.each do |item| puts " - #{item}" end end false end end def generate_checksums checksum_data = Bloggit::Checksum.generate_from(cache_dir) File.open( File.join(cache_dir, 'checksum.yml'), 'w' ) do |f| f.write checksum_data.to_yaml end rescue report_error('checksum.yml') end # Individual object generators... def gen_post(post) ctx = { 'content' => post, 'post' => post, 'site' => @site } render_template :post, post.permalink, ctx puts_if_verbose " - #{post.permalink}" rescue report_error(post.permalink || '??') end def gen_page(page) ctx = { 'content' => page, 'page' => page, 'site' => @site } render_template :page, "#{page.slug}.html", ctx puts_if_verbose " - #{page.permalink}" rescue report_error(page.permalink || '??') end def gen_homepage(posts, opts={}) opts.posts = posts opts.grouped_posts = opts.posts.group_by(&:publish_date) opts.site = @site path = opts.fetch(:path, 'index.html') render_template :home, path, opts puts_if_verbose " - #{path}" rescue report_error(path) end def gen_inline_archive(posts, opts={}) opts.posts = posts opts.grouped_posts = opts.posts.group_by(&:publish_date) opts.site = @site opts.mode = :inline path = opts.fetch(:path, 'index.html') render_template :archive, path, opts puts_if_verbose " - #{path}" rescue report_error(path) end def gen_year_archive(year, posts, opts={}) opts[:type] = :posts opts[:mode] = :list opts[:year] = year opts[:posts] = posts opts[:grouped_posts] = posts.group_by(&:post_month) opts[:path] = @site.build_post_path( "#{year}/index.html" ) gen_archives(opts) puts_if_verbose " - #{opts[:path]}" rescue report_error( opts[:path] ) end def gen_month_archive(year, month, posts, opts={}) opts[:type] = :posts opts[:mode] = :inline opts[:year] = year opts[:month] = month opts[:posts] = posts opts[:path] = @site.build_post_path( "#{year}/#{month}/index.html" ) gen_archives(opts) puts_if_verbose " - #{opts[:path]}" rescue report_error(opts[:path]) end def gen_tags_archive(tags, opts={}) opts.mode = :all_tags opts.tags = tags opts.site = @site render_template :tag_archive, 'tags/index.html', opts puts_if_verbose " - tags/index.html" rescue report_error("tags/index.html") end def gen_tag_archive(tag, opts={}) opts.mode = :single_tags opts.tag = tag opts.site = @site render_template :tag_archive, "tags/#{tag.slug}.html", opts puts_if_verbose " - tags/#{tag.slug}.html" rescue report_error("tags/#{tag.slug}.html") end def gen_archives(opts={}) opts.reverse_merge! :type=>:posts, :mode=>:inline, :path=>'index.html', :site=>@site render_template :archive, opts[:path], opts end def gen_news_feed(posts) feed_type = @site.settings.syndication.fetch('type', 'rss') limit = @site.settings.syndication.fetch('number_of_entries', 10) target_posts = posts[0..limit] File.open( File.join(cache_dir, @site.settings.syndication.filename), 'w') do |f| case feed_type when 'rss': gen_rss_feed(target_posts, f) when 'atom': gen_atom_feed(target_posts, f) end end puts_if_verbose " - #{site.settings.syndication.filename} (#{feed_type})" rescue report_error("#{site.settings.syndication.filename} (#{feed_type})") end def gen_atom_feed(posts, io_obj) tmpl = Template.from_text('') Template.folder_depth = 0 Template.force_absolute_path = true xml = Builder::XmlMarkup.new( :target=>io_obj, :indent=>2 ) #xml.instruct! 'xml-stylesheet', :href=>'/stylesheets/atom.css', :type=>'text/css' xml.feed :xmlns=>'http://www.w3.org/2005/Atom' do xml.div :xmlns=>'http://www.w3.org/1999/xhtml', :class=>'info' do xml << <<-EOF This is an Atom formatted XML site feed. It is intended to be viewed in a Newsreader or syndicated to another site. Please visit atomenabled.org for more info. EOF end xml.title @site.title xml.link :rel=>'self', :href=>@site.absolute_path_to(:feed) xml.link :href=>@site.absolute_path_to(:home) xml.id :href=>@site.absolute_path_to(:home) xml.updated posts.first.publish_date.strftime("%a, %d %b %Y %H:%M:%S %z") xml.author { xml.name @site.author } posts.each do |post| xml.entry do xml.title post.title xml.link :href=>@site.absolute_path_to(post) xml.id atom_id_for(post) xml.updated post.publish_date.strftime("%a, %d %b %Y %H:%M:%S %z") xml.author { xml.name @site.author } # xml.summary entry.summary xml.content post.render_content(tmpl.render_binding), :type=>'html' end end end Template.force_absolute_path = false end def gen_rss_feed(posts, io_obj) tmpl = Template.from_text('') Template.folder_depth = 0 Template.force_absolute_path = true xml = Builder::XmlMarkup.new( :target=>io_obj, :indent=>2 ) xml.instruct! :xml, :version=>"1.0" xml.rss(:version=>"2.0"){ xml.channel{ xml.title( @site.title() ) xml.link( @site.absolute_path_to(:home) ) xml.description(site.subtitle) xml.language('en-us') for post in posts xml.item do xml.title(post.title) xml.category(post.tags.join(', ')) xml.description(post.render_content(tmpl.render_binding)) xml.pubDate(post.publish_date.strftime("%a, %d %b %Y %H:%M:%S %z")) xml.link(site.absolute_path_to(post)) xml.guid(site.absolute_path_to(post)) end end } } Template.force_absolute_path = false end private def puts_if_verbose(msg='') puts(msg) if @v end def report_error(affected_item) err_msg = $!.to_s @errors[err_msg] ||= [] @errors[err_msg] << affected_item puts_if_verbose " x #{affected_item}" end def create_folder(*folder_names) folder_names.delete_if {|p| p=="" } full_path = [cache_dir, folder_names].flatten FileUtils.mkdir_p File.join(*full_path), :verbose=>false end def atom_id_for(post) base_url = @site.settings.syndication.fetch('base_url', 'no-name.com').gsub('http://', '') "tag:#{base_url},#{post.publish_date.strftime('%Y-%m-%d')}:#{post.permalink}" end def render_template(template, path, ctx={}) output = Template.render(template, File.dirname(path), ctx) File.open( File.join(cache_dir, path), 'w') do |f| f.write output end end end end