How do I get this case statement to work? - ruby-on-rails

In my view, I am doing this:
<% case #post
when #post.has_children? %>
<% #post.children.each do |child| %>
<li><%= link_to child.title, post_path(child)%></li>
<% end %>
<% when #post.has_siblings? %>
<% #post.siblings.where.not(id: #post.id).each do |sibling| %>
<li><%= link_to sibling.title, post_path(sibling)%></li>
<% end %>
<% when !#post.parent.nil? %>
<li><%= link_to #post.parent.title, post_path(#post.parent) %></li>
<% else %>
</ul>
</p>
<p>
There are no related posts.
</p>
<% end %>
Basically what I want to do is I want to check #post for a variety of conditions. If it has_children?, if it has_siblings?, etc.
I don't want the statement to exit if any of the above is true or false.
Once the view loads, it should automatically check for all of these statements. If it finds any of the above true, it should execute the command right below the check.
The issue is when I do this, it always defaults to the else. i.e. the case statement doesn't work.
I know I could simply just do a bunch of disjointed if statements, but then the HTML around it gets a bit weird.
Is there a way I can do this with a CASE statement?
Edit 1
The reason the if statement doesn't work properly, is if I have 3 if statements back to back - none of which that interact with each other (that's the only way to cycle through all of the conditions properly), is that the else doesn't trigger properly.
E.g. if the first two conditions are true, but the third is not...it will print out "there are no related posts"...when that's not the case. It is the case that there are no parent posts.
Basically I just want to have a catch-all related posts, so I am simply iterating through all of the various options and checking to see if those relations exist. If they do, I am pulling them out and if they don't then they move on. If none exist, then I don't print "there are no related posts".

The fact that the view is already looking looking complex is a sign that it may be a good idea to refactor the logic out of the view and place it into the Post model where it belongs. Ideally the view(s) should end up looking like this:
<%# posts/show.html.erb %>
<% if #post.has_related_posts? %>
<%= render partial: 'children', collection: #post.children, as: :child %>
<%= render partial: 'siblings', collection: #post.other_siblings, as: :sibling %>
<%= render partial: 'parent', locals: {parent: #post.parent}%>
<% else %>
<p>There are no related posts</p>
<% end %>
The paritals:
<%# posts/_children.html.erb %>
<li><%= link_to child.title, post_path(child)%></li>
<%# posts/_sibling.html.erb %>
<li><%= link_to sibling.title, post_path(sibling)%></li>
<%# posts/_parent.html.erb %>
<% unless parent.nil? %>
<li><%= link_to parent.title, post_path(parent) %></li>
<% end %>
Then the Post model can organize the logic:
class Post < ActiveRecord::Base
def has_related_posts?
!children.to_a.empty? || !other_siblings.to_a.empty? || !parent.nil?
end
def children
self.children || [] # Rails does this automatically, but just for the example
end
def other_siblings
self.siblings.where.not(id: self.id)
end
#...
end
I know this doesn't directly answer your question, however IMHO I think it's a better solution.

You have two options here.
Use IF ELSIF
<% if #post.has_children? %>
<% #post.children.each do |child| %>
<li><%= link_to child.title, post_path(child)%></li>
<% end %>
<% elsif #post.has_siblings? %>
<% #post.siblings.where.not(id: #post.id).each do |sibling| %>
<li><%= link_to sibling.title, post_path(sibling)%></li>
<% end %>
<% elsif !#post.parent.nil? %>
<li><%= link_to #post.parent.title, post_path(#post.parent) %></li>
<% else %>
</ul>
</p>
<p>
There are no related posts.
</p>
<% end %>
Use only case as doz mentioned
<% case
when #post.has_children? %>
<% #post.children.each do |child| %>
<li><%= link_to child.title, post_path(child)%></li>
<% end %>
<% when #post.has_siblings? %>
<% #post.siblings.where.not(id: #post.id).each do |sibling| %>
<li><%= link_to sibling.title, post_path(sibling)%></li>
<% end %>
<% when !#post.parent.nil? %>
<li><%= link_to #post.parent.title, post_path(#post.parent) %></li>
<% else %>
</ul>
</p>
<p>
There are no related posts.
</p>
<% end %>

You can do it with a case statement. Just give case no parameter. Eg instead of case #post just use case
This is the equivalent of an if statement. And should work for what your trying to achieve
Check out ruby case statements for some examples

Related

Add Methods to Iterated Objects

I have a block that looks similar to this one:
<% #github_tmp_files.files.each do |file| %>
<li><%= link_to #github_tmp_files.filename(file.key), #github_tmp_files.download_url(file.key) %></li>
<% end %>
As you can see in the loop I call two methods with file as the argument:
#github_tmp_files.filename(file.key)
#github_tmp_files.download_url(file.key)
I would prefer to call this two methods like that:
file.filename (should return) #github_tmp_files.filename(file.key)
file.download_url (should return) #github_tmp_files.download_url(file.key)
So that at the end I can write the loop like this:
<% #github_tmp_files.files.each do |file| %>
<li><%= link_to file.filename, file.download_url %></li>
<% end %>
How do I have to change the files method in #github_tmp_files, so that it allows this behaviour? Thanks
#in #github_tmp_files -> Class
def files
github_bucket.objects(prefix: #folder)
end
Just out of curiosity:
<% #github_tmp_files.files.map(&:key).each do |file| %>
<li>
<%= link_to *[:filename, :download_url].each do |m|
#github_tmp_files.public_send(m, file)
end %>
</li>
<% end %>

when #user is blank, how to make this if statement work?

I have a header partial linked to my application.html.erb that looks like this:
<header class="unselectable">
<h2 class="float_left">
<% if #user.try(:errors).present? %>
<%= render 'shared/error_messages' %>
<% else %>
<%= #title %>
<% end %>
</h2>
<nav class="round">
<ul>
<% if logged_in? %>
<li><%= link_to "Home", current_user %></li>
<li><%= link_to "Settings", edit_user_path %></li>
<li><%= link_to "Log out", logout_path %></li>
<% else %>
<li><%= link_to "Log in", login_path %></li>
<% end %>
</ul>
</nav>
</header>
This is all well and good unless the page that loads doesn't have an #user variable (such as an about or logout page) in which case i get this:
undefined method `errors' for nil:NilClass
How can I make this work? I tried changing the logic to render the title unless #user.errors.any?but that didn't work either. I'm sure this is a simple fix but I can't figure it out!
EDIT added the fixes suggested (updated in the header partial above) and now get this error:
No route matches {:action=>"edit", :controller=>"users"} which seems to be coming from the edit_user_path
You can use the method .try(:something):
<% if #user.try(:errors).present? %>
<%= render 'shared/error_messages' %>
<% else %>
<%= #title %>
<% end %>
If #user is nil, the .try(:errors) will not raise an error.
The .present? method works for nil too:
.
>> nil.present?
#=> false
>> false.present?
#=> false
>> [].present?
#=> false
>> ''.present?
#=> false
>> 'bonjour'.present?
#=> true
>> ['bonjour'].present?
#=> true
.present? is a combination of .nil? AND .empty?
.present? is actually the opposite result of .blank?
I highly question the need for #user in your partial which is rendered in your application layout, hence its need in every page of your application. I argue that this is not good design at all because now you're relying on a global variable in all views of your application.
I think what you really mean to use is the flash. In which case you want something like this in application.html.erb.
<% flash.each do |key, value| %>
<%= content_tag :div, value, class: key %>
<% end %>
This should be set in the appropriate controller action before it's view is rendered so that the error message displys according to the request that was just made.
If your error messages come from your models, then this should be part of what actually generates these error messages. Typically this is a call to either create or update actions in the controller. In which case you should have the error_messages partial rendered with the form when your validations do not pass and the form is rendered again with the model object.
<%= form_for #user do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<!-- and so on -->
<% end %>
This way you can be confident that the #user object is always available for the partial to render without any errors since we're explicitly passing the object to the partial itself, and the partial is being rendered with the correct context. Using #users in your partial itself is the equivalent of using a global variable, hence the entire application relying on that global variable to exist.
The #user object is now accessed with a local variable in the partial as object (or whatever your decide to end up naming it).
<% object.errors.full_messages.each do |message| %>
<li>* <%= message %></li>
<% end %>
You can reformulate to like this:
<header>
<h2 class="float_left">
<% if #user.try(:errors).try(:any?) %>
<%= render 'shared/error_messages' %>
<% else %>
<%= #title %>
<% end %>
</h2>
...
</header>
Or add errors_any? to model:
class User
def errors_any?
self.try(:errors).try(:any?)
end
end
And to this:
<header>
<h2 class="float_left">
<% if #user.try(:errors_any?) %>
<%= render 'shared/error_messages' %>
<% else %>
<%= #title %>
<% end %>
</h2>
...
</header>

How can I display hash data?

<% #user.dids.each_with_index{ |did, i| %>
<li><%=h #user.dids %></li>
<% } %>
result to display
#<Did:0x7fc4c55f4bf0>#<Did:0x7fc4c55f4ba0>#
<Did:0x7fc4c55f4b50>#<Did:0x7fc4c55f4b00>#
<Did:0x7fc4c55f4a88>#<Did:0x7fc4c55f4a10>#
<Did:0x7fc4c55f49c0>#<Did:0x7fc4c55f4970>
I almost sleep and did not think, tell me somebody how to display the hash data
You could just use the debug function in rails
<%= debug #user %>
First off, your loop is a bit strange. It seems to me that you should have
<% #user.dids.each_with_index{ |did, i| %>
<li><%=h did %></li>
<% } %>
In your version, you are printing the entire #user.dids with each iteration.
Also, how is the class Did defined? If you are in Rails and Did is an ActiveRecord model, you should have the .to_json method available to you (options and info here):
<% #user.dids.each_with_index{ |did, i| %>
<li><%=h did.to_json %></li>
<% } %>
OK I sleep so correctly
<% #user.dids.each_with_index{ |did, i| %>
<li><%= did.did %></li>
<% } %>
Thank you for helping colleagues

get specific element from ruby block, rails app

When performing a block like:
<% #user.favoured_user.each do |user| %>
<li><%= user.name %></li>
<% end %>
With the favoured_user method returning a limit of 5 users, how would I manipulate the block so that even when there are only 3 users available, I could still return 5 li elements?
I'm guessing a helper would come in to play, and maybe the 'first, second, third, etc.' array methods, but I can't think how to write it.
Any help?
You can try this,
<% 5.times do |i| %>
<li> <%= #user.favoured_user[i].try(:name) %> </li>
<% end %>
You can use in_groups_of
Like:
<% #user.favoured_user.in_groups_of(5).each do |favored_user| %>
<% favored_user.each do |user| %>
<li><%= user.try(:name) %></li>
<% end %>
<% end %>
The first 3 users will come through, and the last two entries will be nil

For the first x in array?

I imagine this has a rather simple answer
<% for user in #users %>
<li>
<%= link_to user.username, user %>
</li>
<% end %>
Taking my simple example above, how would I apply a class to my <li> to the first three instances returned?
Secondly, how could I just have the the second two items have a different class from the first one? as in 1..2
Either you could count manually (which is kinda ugly):
<% i = 0 %>
<% for user in #users %>
<li class=<%= (i < 3 ? "foo" : "bar") %>>
<%= link_to user.username, user %>
</li>
<% i = i.next %>
<% end %>
or use each_with_index
<% #users.each_with_index do |user, i| %>
<li class=<%= (i < 3 ? "foo" : "bar") %>>
<%= link_to user.username, user %>
</li>
<% end %>
Once you get to more complex things than i < 3 (like your 1..2 issue) you should think about a helper_method (in helpers) like class_by_position(pos) so that you can write
<li class=<%= class_by_position(i) %>>
The :first-child pseudoselector might be a better way to go, but you'll need to have a counter variable that keeps track of the iterations to do it your way.
Your question is a little vague. I can't tell if you want to stop processing the array after the first x, or not.
If you're just looking to stop after the first x items, or just looking for the 2nd and 3rd items the solution is to use a slice.
Example: just the first 3:
#user[0,3].each do |user|
... # only executed for user = #user[0],user = #user[1] and user = #user[3]
end
Example: just the second and third:
#user[1,2].each do |user|
... #only only executed for user = #user[1] and user = #user[3]
end
And here's a more specific answer to your question using these new concepts and the content_tag to programatically decide the class of the list item. If you're going to be doing this often, makes a great candidate for a function.
<% first = true %>
<% #user[0,2].each do |user| %>
<% content_tag :li,
:class => first ? "class-for-first-item" : "class-for-2nd-&-3rd" do %>
<%= link_to user.username, user %>
<% end %>
<% first = false %>
<% end %>
<!-- Now to do the rest of them:-->
<% #user[3,-1].each do |user| %>
<% content_tag :li, :class => "class-for-rest" do %>
<%= link_to user.username, user %>
<% end %>
<% end %>

Resources