Add Methods to Iterated Objects - ruby-on-rails

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 %>

Related

Rails 6 how to decorate application.html.erb

I've got an application.html.erb where I want move calculation of unread user messages into some decorator/helper, basically because it looks like this:
<% if current_user %>
<%= link_to 'Messages', conversations_path %>
<% counter = #conversations.map do |conversation| %>
<% unless conversation.unread_message_count(current_user).zero? %>
<% conversation.unread_message_count(current_user) %>
<% end %>
<% end %>
(<%= counter.sum %>)
I know the basic concept of decorators but I'm wondering if I have ConversationDecorator in app/decorators/conversation_decorator.rb with defined counter method with #conversations.map block there, how to use this decorator inside of application.html.erb ?
Example ConversationDecorator:
class ConversationDecorator < ApplicationDecorator
def unread_counter
#conversations.map do |conversation|
conversation.unread_message_count(current_user) unless conversation.unread_message_count(current_user).zero?
end.sum
end
end

How do I get this case statement to work?

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

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

Listing/linking to contents of directory in Rails

I have a set of static PDFs. I want to list them out on a rails page, with links to them.
What I need right now is how to trim the /public off the beginning of the path so the link will actually work.
Current code:
<h1>Listing letters</h1>
<table>
<ul>
<% #files = Dir.glob("public/files/*.pdf") %>
<% for file in #files %>
<% new_file = file.to_s %>
<% new_file = new_file.chomp("public/") %>
<li><%= link_to 'Letter', new_file %></li>
<% end %>
</ul>
</table>
However the links are still coming as
http://localhost:3000/public/files/document.pdf
when to work they need to be
http://localhost:3000/files/document.pdf
<% Dir["public/files/*.pdf"].each do |file| %>
<li><%= link_to 'Letter', file[/\/.*/] %></li>
<% end %>
The chomp method is used to remove someting at the end of the string ;) Use gsub instead.
new_file.gsub!('public', '')
or
new_file = new_file.gsub('public', '')

Resources