How to render partial in HAML that only contains a conditional block - ruby-on-rails

I'm new to Ruby and HAML and attempting to render the following partial inside of main HAML file. The partial consists of only an if conditional. I would like the if conditional to return the output of the partial's HAML code to the main template if the condition is met, and to render nothing if it isn't. The following code works if the array "attachments.each_file" is empty (it renders nothing as I would like), but if it isn't empty it throws an error when it tries to proceed into the if conditional's code. Here are the relevant code snippets:
Error Message:
LocalJumpError in Questions#show
Showing /questions/_attachments.html.haml where line #1 raised:
no block given (yield)
Main HAML template code:
= render "slashbias/questions/attachments", :attachments => #question.attachments, :editing => false
Partial HAML code:
- if !attachments.each_file.empty?
%dl#attachments_list
%dt.header Attached files:
%dd
-attachments.each_file do |key, file|
= link_to file.name, question_attachment_path(question.group, question, file, key)
-if editing
= link_to t("scaffold.destroy"), remove_attachment_question_path(question, :attach_id => key), :class => "remove_attachment_link"

I believe attachments.each_file on line 1 is expecting a block. It doesn't throw an error in the case of having 0 files, because it never attempts to yield anything. But in the case that there are files, each_file is trying to yield the files to a block and raising the error you're seeing because there is no block to yield to.
Is there another way for you to test if there are any files? Something like !attachments.files.empty? or attachments.files.count > 0?

Related

How can I re-use blocks of haml with arguments?

I've been using HAML for over 10 years, and this simple use case continues to elude me.
Say I have the following haml code
- links.each do |link|
= link_to link.url do
%i.fa.fa-map
= link.text
This block works while looping only, but imagine a situation where I want to reuse the block in multiple loops, or in multiple places in the HAML file. I do not currently know how to do that without creating a separate partial or helper. When the block is big enough, I often will create a partial, but often the block is small and a partial seems tedious overkill. Sometimes I have several blocks like this that I only want re-used in one HAML file.
I tried first to create this with content_for but it doesn't accept arguments and can't be cached.
My second attempt was with capture_haml, but I think that has to be called directly indide of HAML, not a ruby method. The following obviously fails for syntax reasons, but you see what I'm trying to do:
:ruby
def entry_link(url, text)
capture_haml(url, text) do |url, text|
= link_to url do
%i.fa.fa-map
text
end
end
Update:
I was able to get this far, which is functional but ugly AF.
:ruby
def entry_link(url, text)
capture_haml(url, text) do |url, text|
link_to url do
haml_tag :i, {class: 'fa fa-map'}
haml_concat(text)
end
end
end
= entry_link('/', 'My Link')
The following would be a more acceptable syntax, but I get the strange error undefined local variable or method _hamlout'`
- def entry_link(url, text)
- link_to url do
%i.fa.fa-map
text

"render :partial" can't find files when called on its own

I have a rails 2.2.2 app with the following odd problem.
There's a .js.rjs file, which is rendered to produce javascript as the response to a .js format query.
If i do this, it works fine:
page.replace_html "search-results-resources-paginated", :partial => "search/discover_results"
Ie, it renders out the app/views/search/_discover_results.html.erb partial (which in turn calls other partials). That's all working fine.
However, I want to break this down into a couple of steps, where i render the partial into a text string, and then use that string in the javascript (i just want to do this for a bit of extra debugging).
I'm trying to do this like so:
text = render(:partial => "search/discover_results")
page.replace_html "search-results-resources-paginated", :text => text
this is now starting to complain about not being able to find the partial:
ActionView::TemplateError (Missing template search/_discover_results.erb in view path
/app/views:
<project folder>/app/views/community:
<project folder>/vendor/plugins/savage_beast/app/views:
<project folder>/vendor/plugins/exception_notification/app/views:
<project folder>/vendor/plugins/query_reviewer/lib/query_reviewer/lib/query_reviewer/views:
) on line #49 of app/views/search/discover.js.rjs:
(i've formatted the above a little, for clarity)
If i tell render the actual file extension, like so:
text = render(:partial => "search/discover_results.html.erb")
then it finds that first partial, but i get the same problem with other partials this it references. It's like render doesn't know that it's supposed to be looking for html.erb files, rather than just .erb files.
Is there an extra option i need to pass to render to make this work?
thanks, Max

How to set #virtual_path true in refresh() in ActionView::Template?

We use render inline in our rails 3.2 app:
<%= render inline: #erb_code, locals: {f: f} %>
Here #erb_code returns a string of ERB code for rendering. The problem with this inline is that the #virtual_path is set to false in rendering which causes error in spec in refresh(view) of ActionView::Template. Here is the error:
ActionView::Template::Error:
A template needs to have a virtual path in order to be refreshed
Here is the line in definition of refresh which causes error:
raise "A template needs to have a virtual path in order to be refreshed" unless #virtual_path
The full definition of the refresh(view) is available at http://api.rubyonrails.org/classes/ActionView/Template.html.
How to set #virtual_path true in rspec to avoid the spec error? But the execution of the code seems having no error. Tried assign true to #virtual_path in spec and it did not work.
You can try to stub this refresh call in the beinning of your spec
ActionView::Template.any_instance.stub(:refresh)

Rails: Calling Render From Within a Generator

I've just started experimenting with generators. In one of my generated view templates, I want to call render like so:
index.js.slim
transition("#main", "<%= escape_javascript(render 'index') %>");
When I try using the generator, I get this error:
(erb):1:in `template': undefined method `render' for #<Slim::Generators::ScaffoldGenerator:0x000000041b2a20> (NoMethodError)
Is Rails really incapable of calling render from within a generator? Or am I doing something wrong?
Railscast 218 goes into more detail:
The first thing to note is that because we’re using the template
method, all of the erb tags in the code will be executed when the
generator runs. If we want to include any erb in the generated file
we’ll have to escape the percent sign at the beginning of each erb tag
and we’ve done that for most of the erb code above.
In this instance:
transition("#main", "<%= escape_javascript(render 'index') %>");
should become:
transition("#main", "<%%= escape_javascript(render 'index') %>");
All it needed was an extra % to escape the erb.

Dynamic rendering of partials in Rails 3

In the app I'm working on, I have defined a method task_types in the model:
task.rb
def self.task_types
["ad_hoc"]
end
I use task_types to populate a drop-down menu, but I would also like to use it to dynamically render a partial.
What I have in mind is, for each task type, there will be an associated comment box. I went ahead and made a helper that will call the correct partial depending upon which task type was selected:
#tasks_helper.rb
module TasksHelper
def completion_comment(task)
task_types = #task.task_type
render :partial => "#{Task.task_types}", :locals => {:task => task}
end
end
Unfortunately, when I called completion_comment, I received the error message "The partial name (["ad_hoc"]) is not a valid Ruby identifier."
What I'm looking for is a dynamic way to render the appropriate partial. I think I'm on the right track, but I'm not sure how to extract the array elements from my task_types method (as you can see in the error message, the brackets and quotation marks are getting pulled into the render action). Or, perhaps I need to utilize a different method for dynamically rendering the partial. Any help is appreciated.
Here's what worked:
module TasksHelper
def render_task_form(task)
render :partial => "/tasks/completed/#{task.task_type.downcase}"
end
end
I had tried this solution much earlier, and had received an error that "The partial name (/tasks/completed/) is not a valid Ruby identifier; make sure your partial name starts with a letter or underscore, and is followed by any combinations of letters, numbers, or underscores."
I tried it again, and to remedy this problem I deleted all tasks from the database. Now the partial renders dynamically. Hopefully this helps someone else!
You need a string instead of the entire task_types array. #task.task_type should return a key that a) matches an element in the task types array and b) matches a known partial.
The following is a bit more complicated that it needs to be but should do the trick:
tasks_helper.rb
module TasksHelper
def completion_comment(task)
if Task.task_types.include? task.task_type
render :partial => task.task_type,
:locals => {
:task => task
}
else
# render a generic comment box here
end
end
end
In Rails 4, the view or the partial name is supposed to respond to a valid Ruby identifier.
So, the entire view name must follow the same format as a ruby-identifier:
it should start with a _ or character
it cannot start with a number
it can have only _ and alphanumerics.
So, considering that task_type is a valid ruby identifier (which it should be), it will work. In generate this code will not work in Rails 4
render '/tasks/completed/some-task'
but this will work
render '/tasks/completed/some_task' # note the underscore.

Resources