Will always return true and erase the entire array
<% users.delete_if do |user| %>
<% false %>
<% end %>
On the other hand
<%
users.delete_if do |user|
false
end
%>
works and does not delete the array.
Can i somehow use the delete_if statement in my view and still be able to insert html?
Thanks
You shouldn't be modifying data at all in your views -- do this in your controller or model instead. Use views only to reflect the current state of your database, not change it.
If you then use the second version of your code in either of those places, this problem will disappear.
Ignoring the side-effect-in-a-view aspect, here is why the block "returns" a value: there is a generated print statement as part of the ERB template generated code between the "%>" and the "<% end". Any value left behind by this generated code will be used as the final statement / expression in the block, and thus its value.
I think, and I would have to test it if you use "yield" in the first form, it will work.
<% users.delete_if do |user| %>
<% yield false %>
<% end %>
But, as the first poster says, you shouldn't change data from a view.
Related
I have an instance variable #tally_property, and if there are photos on that object I would like to cycle through the photos and show them.
So my code snippet looks like this:
<% if #tally_property.photos.present? %>
<% #tally_property.photos.each_with_index do |photo, index| %>
The issue is that based on the above, if #tally_property is nil, then the entire first line throws an error.
So is there a 'nil' check I can do that isn't bulky, i.e. I don't want to do if #tally_property.nil?, on both the primary object and the association, and is elegant and ruby & rails-esque?
I would use the safe navigation operator (&.) and write something like this:
<% #tally_property&.photos&.each_with_index do |photo, index| %>
...
<% end %>
In Ruby 2.3.0+ you can use the safe navigation operator:
#tally_property&.photos
ActiveSupport has a .try method that can be used to the same end in older versions of ruby:
#tally_property.try(:photos)
You can add a simple conditional to be able to safely iterate through the collection:
<% (#tally_property.try(:photos)||[]).each_with_index do |photo, index| %>
<% end %>
Rails 4 adds ActiveRecord::Relation#none and a change in behaviour so that associations always return a ActiveRecord::Relation. So its perfectly acceptable to write:
<% #tally_property.try(:photos).try(:each_with_index) do |photo, index| %>
<% end %>
After upgrading your app. Or you can use a partial and render:
<%= render partial: 'photos', collection: #tally_property.photos if #tally_property %>
Which removes the need for writing the iteration.
Use && (or and, they each have their sweetspot).
Taking it out of Erb for a moment, I would generally write something like this:
if #tally_property and #tally_property.photos.present?
Depending on photos I might use:
if #tally_property and #tally_property.photos
or perhaps:
if #tally_property and not #tally_property.photos.empty?
Sometimes I'll use a temporary variable:
if (photos = #tally_property && #tally_property.photos)
photos.each #…
That kind of thing.
I would recommend this episode of Ruby Tapas, And/Or for a longer (but still quick) look at it.
One more way, just select all photos connected to this tally_property:
example how it might be:
Photo.joins(:tally_property).each_with_index do |photo, index|
I want to implement a search functionality in my Rails app by using the pg_search gem. I've set up everything like it says in the documentation. Then I've set up a search controller with a show action:
def show
#pg_search_documents = PgSearch.multisearch(search_params)
end
The search itself works but I have a really annoying problem in my view. Whatever I do, it always outputs an array of PgSearch::Document objects. Even when I only write this in my view:
<%= #pg_search_documents.each do |document| %>
<% end %>
I get this (I've shortened it):
[#<PgSearch::Document id: 2, content: "…", searchable_id: 28, searchable_type: "Vessel">, #<PgSearch::Document id: 3, content: "…", searchable_id: 27, searchable_type: "Vessel">]
I know that pg_search sets up a polymorphic association which I've never dealt with before — could that be the problem?
Thanks in advance
<%= #pg_search_documents.each do |document| %>
<% end %>
This is a classic error, one I remember being puzzled over when I first started learning Rails. The mistake is using <%= %> with each. The return value of each is the array that you're iterating over (in this case, #pg_search_documents), and by using <%=, you're telling Rails to create a string from that array and insert it into your view. That generally isn't what you want: you want the view to be generated by the code inside the block you're passing to each.
Use <% #pg_search_documents.each do |document| %> instead (omitting the =) and you'll avoid the dump of the array's content.
You may also need to use .searchable as #blelump suggests, but I wanted to answer the other half of your question, as it's a common pitfall.
To get back to the original source model, searchable call is needed on these search result records, e.g:
<% #pg_search_documents.each do |document| %>
<%= document.searchable %>
<% end %>
You can also switch back to the source model within your controller, e.g:
#pg_search_documents = PgSearch.multisearch(search_params).collect(&:searchable)
Then, the #pg_search_documents will contain Vessel elements.
Can someone explain the logic behind this code?(This is the correct code btw)
<% if #request.query['first_name'] && !#request.query['first_name'].empty? %>
Welcome! <%= #request.query['first_name'] %>
<% else %>
Hi! What is your name?
<% end %>
My intuition is to write the following instead:
<% if #request.query.inspect['first_name'].empty? %>
Hi! What is your name?
<% else %>
Welcome! <%= #request.query.inspect['first_name'] %>
<% end %>
I am trying to have a user form where people can input their names, when there is no input yet the text above the form says "Hi! What is your name?" when there is an input it has a message saying "Welcome! *User_name*"
The first block of code is not intuitive to me, the second one would make more sense.. ANy advice on how to understand the code?
Your intuition is correct, though you need an alternative to empty?. Rails adds a few different methods you can use:
blank? returns true if the receiver is nil, an empty array, string, or hash, or a string with only whitespace.
present? returns true if blank? is false. So your condition could be:
<% if #request.query['first_name'].present? %>
Welcome...
(I find it's always more intuitive to start with the positive condition - it would work just as well to check blank?).
Edit: It's pretty likely you can skip the query method entirely if all you expect there is either a string or nil. Just use:
<% if #request.query['first_name'] %>
You need to check if it's nil before you can check if its empty, because you are checking a Hash#empty?
irb(main):001:0> nil.empty?
NoMethodError: undefined method `empty?' for nil:NilClass
from (irb):1
irb(main):002:0> {}.empty?
=> true
The code checks for hash key existence, then check if the value of the hash is present. This action can be done in one check using:
#request.query.try(:[], 'first_name').empty?
You can avoid the first condition inside the if statement by transforimng nil into an empty string. I don't know if that is what you meant to do but you almost had.
First, you shouldn't call inspect in the hash because it will transform the entire thing into a 'complex' string. What you want to do turn only the value inside the first_name option, because in that case if the name exists it will still be the same, and if it doesn't, it will be turned into "nil".
Secondly, the method inspect isn't the best choice here, because the returned string will never be empty, given that nil.inspect => "nil". What you should use is the method to_s, wich will behave like this when applied to nil: nil.to_s => "".
Finally, you could update your code to:
<% if #request.query['first_name'].to_s.empty? %>
Hi! What is your name?
<% else %>
Welcome! <%= #request.query['first_name'] %>
<% end %>
I have a pair of Rails helpers, one of which is meant to accept a block and the other of which just renders a button. Here are simplified versions the helper definitions:
def nav_wrapper(nav_at, id, css_class, &block)
"<ul class="complicated">\n #{yield} \n</ul>".html_safe
end
def nav_btn(nav_at, caption, id = caption.downcase.dasherize)
"Nav button codes goes here".html_safe
end
I'm trying to set things up such that I can do something like this:
<%= nav_wrapper(#nav_at, "Top Nav", "class") do %>
<%= nav_btn(#nav_at, "Foo", "id") %>
<%= nav_btn(#nav_at, "Bar", "id") %>
<%= nav_wrapper(#nav_at, "Sub Nav", "class") do %>
<%= nav_btn(#nav_at, "SubFoo", "id") %>
<%= nav_btn(#nav_at, "SubBar", "id") %>
<% end %>
<% end %>
But, the yield in the nav_wrapper method only picks up the last statement of each block. So in this example, I get the Top Nav wrapper, Foo and Bar are skipped, I get the Sub Nav wrapper (being the last statement in the outer nav_wrapper block), SubFoo is skipped, and I get SubBar (being the last statement in the inner nav_wrapper block).
I know that the reason for this behavior is that the block of code is implicitly returning the last evaluated value, but I know there are lots of template helpers that render all the interstitial lines (form_for, for example). Can someone help me figure out what the magic trick is here?
When a ERB template is compiled, it is converted to code which adds strings to a buffer. (Look at the source code for ERB to see what I mean.) Methods from Action View like form_for execute the block, and then retrieve the text in the ERB buffer.
Open up the lib/ruby/1.9.1/gems/1.9.1 folder, and look for actionpack. Open up whatever version of Action Pack you have, and go to lib/action_view/helpers/capture_helper.rb. There is a method in there called capture, which is used by form_for to execute a block and retrieve the text generated by ERB.
If you are writing a Rails helper, then presumably capture will be available to your code. If not, try include ActionView::Helpers::CaptureHelper.
I'm using Ruby on Rails and I want to display a drop down list in a view, but only if items in the list exist. What is a good way to check for the presence of at least one instance of a model, and then display in the view based on this condition?
I was thinking of using an if statement such as:
if firstmodel.secondmodels
. . html stuff
But I think the empty array doesn't stop evaluate to false.
You could use something like this in your view:
<% if #items.any? %>
<!-- drop down list here -->
<% else %>
<div>No items :-(</div>
<% end %>
Generally you should try to keep logic out of your view, but I think in this simple case there are not many alternatives (?)
If there are too many if else like that your view, well... you could maybe check for this in your controller and render another view altogether, like:
# render no_content.html.erb if there are no items,
# default template file otherwise
render "no_content" unless #items.any?
# or:
render "no_content" if #items.empty?
Using your example above, you can see if an array is empty by doing empty?.
if firstmodel.secondmodels.empty?
I believe you need something like that:
<% if firstmodel.secondmodels.empty? %>
<p>Stuff</p>
<% else %>
<p>Other Stuff</p>
<% end %>
Of if you are using HAML:
- if firstmodel.secondmodels.empty?
%p Stuff
- else %>
%p Other Stuff