winton / stasis

Static sites made powerful
http://stasis.me
MIT License
679 stars 56 forks source link

use rails content_for in templates? #62

Closed rkoberg closed 11 years ago

rkoberg commented 12 years ago

Hi, is there a way to enable rails features when running stasis? For example using content_for to set page and head titles. I want to generate static files for the rails app and use the same layout templates. Naively running stasis to generate a page gives:

$ stasis -o about.html.erb
Exception rendering view /Users/rkoberg/Documents/wegivebooks/wgb-static/about.html.erb
/Users/rkoberg/Documents/wegivebooks/wgb-static/about.html.erb:2:in `evaluate_source': undefined method `content_for' for #<Stasis::Action:0x007ff3f229a098> (NoMethodError)
    from /Users/rkoberg/.rvm/gems/ruby-1.9.3-p194/gems/tilt-1.3.3/lib/tilt/template.rb:209:in `instance_eval'
...
<article id="node-2" class="standalone-page clearfix">
<% content_for :head_title do %>
  <%= 'About' %>
<% end %>
<% content_for :page_header_title do %>
  <%= 'About' %>
<% end %>
    <p>Lorem ipsum dolor sit amet...</p>
<p><%= image_tag "/fred.jpg", "alt" => "The alt text", "title" => "The title text", "size" => "491x500" %></p>
<p>Lorem ipsum dolor sit amet...</p>
</article>
frank06 commented 11 years ago

+1

How can content_for be achieved with stasis?

winton commented 11 years ago

Hey guys, this is a HAML-only solution:

helpers do
  def content_for(key, &block)
    @content ||= {}
    @content[key] = capture_haml(&block) if block_given?
    @content && @content[key]
  end
end

For ERB, I would implement something similar to what they do in the sinatra-content-for project:

https://github.com/foca/sinatra-content-for/blob/master/lib/sinatra/content_for.rb

eric1234 commented 11 years ago

Was anybody able to get this to work for ERB? I have put together some code similar to the sinatra-content-for referenced. Here is my code:

module ContentFor
  def content_for key, value=nil, &block
    saved_content[key].push value || block if value || block_given?
  end

  def yield_content key, *args
    saved_content[key].collect do |content|
      if content.respond_to? :call
        content.call *args
      else
        content
      end
    end.join
  end

  def content? key
    !saved_content[key].empty?
  end

  private

  def saved_content
    @saved_content ||= Hash.new {|h, k| h[k] = []}
  end
end

The main differences between my version on the one in the Sinatra code are:

I just mix this module into the helpers by calling include ContentFor within the stasis helpers block. The end result is that when it executes the block I get:

[2013-07-26 10:40:04] Error: undefined method `concat' for nil:NilClass`
    /index.html.erb:45:in `block in evaluate_source'

I know ERB often outputs to an output buffer variable. My guess is that by delaying the execution of the block the variable it was writing to has been removed. I also tried executing the block at the time of calling content_for. While this didn't get an error it also didn't save my output since the output was written to that output buffer variable instead of just being returned. So it didn't have the intended behavior.

eric1234 commented 11 years ago

Figured this out. ERB's default output variable _erbout is likely gets set to nil when the template finishes rendering. The easiest way to fix is set the output variable to be something we can concat against again. Below is my new yield_content method. Everything else remains the same.

def yield_content key, *args
  saved_content[key].collect do |content|
    if content.respond_to? :call
      content.binding.eval '_erbout = ""'
      content.call *args
    else
      content
    end
  end.join
end