I found that you can flush content_for looking at the Rails source. https://github.com/rails/rails/blob/master/actionpack/lib/action_view/helpers/capture_helper.rb
Rails Source:
def content_for(name, content = nil, options = {}, &block)
if content || block_given?
if block_given?
options = content if content
content = capture(&block)
end
if content
options[:flush] ? #view_flow.set(name, content) : #view_flow.append(name, content)
end
nil
else
#view_flow.get(name)
end
end
I am trying to set options[:flush] = true, but am having some trouble. The options[:flush] is not evaluating to true in my code below.
My code:
content_for(affiliate_link_pos.to_sym, {:flush=>true}) do
render page
end
Edit:
I have also tried passing a 3rd params (content), but I get wrong number of argument error (3 for 2).
content_for(affiliate_link_pos.to_sym, "true", {:flush=>true}) do
Try this:
content_for(affiliate_link_pos.to_sym, null, :flush=>true) do
render page
end
Looks like the source quoted in the OP's question is actually from Rails 4. Even in Rails 3.2.14, content_for does not accept any options.
content_for(name, content = nil, &block)
Calling #content_for stores a block of markup in an identifier for later use. You can make subsequent calls to the stored content in other templates, helper modules or the layout by passing the identifier as an argument to content_for.
Related
I have a Haml template with a bunch of “custom tags” scattered around, e.g. {{REPLACE_ME}}. Before rendering this template, I am going through the template and replacing all occurrences using Ruby’s String#gsub.
That has not been a problem—I have simply created a custom template handler, which essentially acts as a middleman by first modifying the template source and then passing it on to the Haml template handler.
For each occurrence of one of these “custom tags” in the view, it adds to a numerator that is stored in the background. The problem that I am facing, however, is that the numerator is being bumped up for content that will never be displayed to the user, e.g. <% if false %>{{SECTION_NO}}<% end %>.
Such content as above should be filtered out before I replace the content. I seem, however, to have hit a wall, as the Haml (and ERB) handlers return a content buffer and not the “end/finished” content.
Is there a straight-forward solution to this issue?
Here is an example of a view:
- if true
%h1 {{SECTION_NO}}
- if false
%h1 {{SECTION_NO}}
- if true
%h1 {{SECTION_NO}}
So basically, I need a way to filter out the middle (false-statement) of the view source before replacing and numerating occurrences of “{{SECTION_NO}}”.
For reference, here is the gist of the template handler I created for this purpose:
class ActionView::Template::Handlers::Caml
def initialize
#section = 0
#paragraph = 0
#references = {}
end
def call(template)
rendered_haml = ActionView::Template.registered_template_handler(:haml).call(template)
updated_source = render_sections(rendered_haml)
updated_source = render_paragraphs(updated_source)
updated_source = render_references(updated_source)
return ActionView::Template.new(
updated_source,
template.identifier,
template.handler,
{
locals: template.locals,
virtual_path: template.virtual_path,
updated_at: template.updated_at
}
)
end
private
def render_sections(source)
source.gsub(/{{section:([a-zA-Z0-9-_]+)}}/).each do |match|
fail 'Key already exists.' if #references.key?($1)
#section += 1
#references[$1] = "#{#section}"
"#{#section}"
end
end
def render_paragraphs(source)
# ...
end
def render_references(source)
source.gsub(/{{([a-zA-Z0-9-_]+)}}/).each do |match|
#references.fetch($1)
end
end
end
ActionView::Template.register_template_handler(
:caml,
ActionView::Template::Handlers::Caml.new
)
One could use yield with a :name in views in rails:
= yield :some_place
so then using then using content_for :some_place do ... to insert a code block only in there where yield :some_place is placed (http://guides.rubyonrails.org/layouts_and_rendering.html#using-the-content-for-method).
Also ruby allows passing parameters in the yiled (http://www.tutorialspoint.com/ruby/ruby_blocks.htm):
def test
yield 5
puts "You are in the method test"
yield 100
end
test {|i| puts "You are in the block #{i}"}
But I didn't find anything about using yield/content_for both with names and parameters in rails views:
= yield :some_place, 5, 6
...
= content_for :some_place do |a,b|
h3 = "Yield provided parameters: #{a} and #{b}"
Is it possible? Where is the official rails or ruby syntax for yield statements and passing blocks?
I heard something about the Proc.new() that could be somehow related to the problem.
content_for(:name) evaluates first, and stores a snip of HTML for later use. yield(:name) only fetches this content. Hence, you can't pass arguments into a method that was already called, and won't be called again.
You probably merely need to cut a partial HTML.erb file, and render it from your target location. Render takes named parameters as a hash.
I have a scenario where I'd like to pass back a long message with my JSON. Instead of writing it out with string concatenation I'd rather put together an erb template that I can render into my JSON. Below is the code I'm currently trying:
object #invitation
node(:phone_message) do |invitation|
begin
old_formats = formats
self.formats = [:text] # hack so partials resolve with html not json format
view_renderer.render( self, {:template => "invitation_mailer/rsvp_sms", :object => #invitation})
ensure
self.formats = old_formats
end
end
Everything works as expected the first time this code is run, however, I run into problems the second time I run it because it says there is a missing instance variable (which I assume was generated and cached during the first run).
undefined method
_app_views_invitation_mailer_rsvp_sms_text_erb___2510743827238765954_2192068340
for # (ActionView::Template::Error)
Is there a better way to render erb templates into rabl?
You could try using ERB as standalone, and not going through the view renderer, like so:
object #invitation
node(:phone_message) do |invitation|
begin
template = ERB.new(File.read("path/to/template.erb"))
template.result(binding)
end
end
binding is a method on Object (through the Kernel module) and it returns the binding which holds the current context, which also includes instance variables (#invitation in this case)
Update:
Don't really know if this will help you get any further (and I also realised it's been more than a year since you posted this), but here's another way to render ERB templates in a standalone fashion:
view = ActionView::Base.new(ActionController::Base.view_paths, {})
class << view
include ApplicationHelper
include Rails.application.routes.url_helpers
end
Rails.application.routes.default_url_options = ActionMailer::Base.default_url_options
view.render(:file => "path/to/template.html.erb", :locals => {:local_var => 'content'})
When I have time I should actually try this with Rabl.
In rails 3.0 with HAML (3.1.4) I have
some template-like partial, like _template.html.haml:
.panel.top
= yield :panel_top
.content
= yield
some another partial which will be displayed using prev template (all this stuff is rendered using AJAX, but this doesn't matter)
- content_for :panel_top do
.title.left
= title
content text
and this worked like a charm in Rails 3.0
But, after upgrade to 3.2 this fails! Yiels just yields "content text", so I have "content text" twice and no title at all
only changing = yield :panel_top to = content_for :panel_top works for 3.2
I am not sure that this solution is ok, and if it is stable or recommended, I cannot find any notes about changes in yield processing nor in Rails 3.1 release notes, nor in 3.2 ones.
Can you help what is the best way to organize yielding inside partials?
From Rails 3.0 to Rails 3.2 content_for was really changed:
3.0:
def content_for(name, content = nil, &block)
content = capture(&block) if block_given?
#_content_for[name] << content if content
#_content_for[name] unless content
end
3.2:
def content_for(name, content = nil, &block)
if content || block_given?
content = capture(&block) if block_given?
#view_flow.append(name, content) if content
nil
else
#view_flow.get(name)
end
end
This shows us, that from 3.2 content_for works for showing/inserting content too, not only store it for named section.
Also, if you make an attempt to debug yield logic you'll se that it yields before content_for is correctly initialized.
So, leaving fragment caching out of this discussion I can conclude that content_for is preferrable way to insert named sections anywhere except top-level layouts. In helpers and other situations yield should render wrong results.
Why does Rails create the path to the current page in the href atribute of the anchor element instead of raising an exception if I pass to the link_to method an instance variable which isn't associated with any resource (and equals nil)?
Here's an example:
Routes
# app/config/routes.rb
Example::Application.routes.draw do
resource :example
end
HAML
-# app/views/examples/show.html.haml
%div
= link_to 'Non-existent resource', #ne_resource
HTML
<!-- http://localhost/example -->
<div>
Non-existent resource
Thanks.
Debian GNU/Linux 6.0.1;
Ruby 1.9.2;
Ruby on Rails 3.0.6.
If you take a look at the link_to method it links to the url using the url_for method.
def link_to(*args, &block)
if block_given?
options = args.first || {}
html_options = args.second
link_to(capture(&block), options, html_options)
else
name = args[0]
options = args[1] || {}
html_options = args[2]
html_options = convert_options_to_data_attributes(options, html_options)
url = url_for(options) #THIS GETS CALLED
href = html_options['href']
tag_options = tag_options(html_options)
href_attr = "href=\"#{html_escape(url)}\"" unless href
"<a #{href_attr}#{tag_options}>#{html_escape(name || url)}</a>".html_safe
end
end
URL for
def url_for(options = {})
options ||= {}
url = case options
when String
options
when Hash #THIS CASE IS TRUE
options = options.symbolize_keys.reverse_merge!(:only_path => options[:host].nil?)
super
when :back
controller.request.env["HTTP_REFERER"] || 'javascript:history.back()'
else
polymorphic_path(options)
end
url
end
From the above, you can see that url_for is valid without options or with a nilClass, it is not designed to raise an exception. If you want errors when using link_to, then make sure to use the dynamic "path" helper methods, in the above case, new_example_path.
I asked DHH (the creator of Rails) a closely related question about link_to's behavior when passed nil, nil arguments. I was hoping it would just not render an tag at all, instead of making me check for nils before calling it. He graciously replied:
https://twitter.com/dhh/status/198487578352156672
The essence of this answer applies to your question. It needs to do something when it is handed a nil. Gazler points out what's technically happening, but DHH's response shows a bit of the higher level "why?" The url_for method is fine with taking a nil argument, with the current page a sensible default.