In my show view I have a few attributes that aren't required to be filled in by the user. In my show view I can easily create a conditional like so:
<% if #user.occupation.present? %>
<p><%= #user.occupation %></p>
<% end %>
But if I have 3 or more of these optional attributes, creating multiple conditionals for each of them can become very tedious. I thought of doing something like this but it turns out that the method attribute is private:
<% #user.attributes.map do |attribute, name| %>
<% if #user.attributes.present? %>
<p><b><%= name %>:</b> <%= #user.attribute %></p>
<% end %>
<% end %>
For security reasons I'm not necessarily trying to change the attribute method from being private but more so just looking for a way to list attributes of my model that have been filled in.
<% #user.attributes.each do |key, value| %>
<% if value.present? %>
<p><b><%= key %>:</b> <%= value %></p>
<% end %>
<% end %>
Related
This code causes ActionController to return undefined method `name' for # How do I print the category name
<h2>Categories</h2>
<%= #categories.group_by(&:parent_id).each do |category| %>
<%= category.name %>
<% end %>
I mean, that the :parent_id is an attribute of Category model and #categories is a collection of ::ActiveRecord_Relation class. Let you try:
<h2>Categories</h2>
<%= #categories.group(:parent_id).each do |category| %>
<%= category.name %>
<% end %>
Consider using <%= debug #categories.group_by(&:parent_id) %> so that you can inspect the data structure
I believe you want to pass two arguments to the each block, one for the parent and one for the categories associated with it
<h2>Categories</h2>
<% #categories.group_by(:parent).each do |parent, categories| %>
<%= parent.name %>
<ul>
<% categories.sort_by(&:name).each do |category| %>
<li><%= category.name %></li>
<% end %>
</ul>
<% end %>
Above is based on my assumptions about your data model which could be totally incorrect?
It is straightforward enough to display only the comments by people the user hasn't blocked:
<% post.comments.each do |comment| %>
<% unless user.blocking?(comment.user) %>
<%= render comment %>
<% end %>
<% end %>
It is also straightforward to display the comments in ascending order (the ascending scope is defined):
<% if post.comments.any? %>
<%= render post.comments.ascending %>
<% end %>
But how do you do both of these things at once in a succinct railsy way?
Just order the comments before iterating over them with each:
<% post.comments.ascending.each do |comment| %>
<% unless user.blocking?(comment.user) %>
<%= render comment %>
<% end %>
<% end %>
You can also combine the unless with your output:
<% post.comments.ascending.each do |comment| %>
<%= render comment unless user.blocking?(comment.user) %>
<% end %>
By the way, ascending isn't documented anywhere - in what context are you using it? Do you have a scope defined for it?
I have an app that has two models Category and Product
Products belong to categories.
I have created a navbar dropdown that requires an #category and #product object to be available across the entire app (the navbar is shown on every page of the application.)
What I can't work out is the best place to put these basic definitions without defining them multiple times in every page definition.
I need to define the following:
#category = Category.all
#products = #category.products.all
The navbar loop will then look something like this.
<% #category.each do |c| %>
<%= c.name %>
<% #products.each do |p| %>
<% link_to product_path(p) do %>
<%= p.name %>
<% end %>
<% end %>
<% end %>
I am a bit of a rails newbie so I am sure there are some errors in here but any help would be much appreciated!
If you need them in every single page of app, you can set them in ApplicationController's before_filter:
class ApplicationController
before_filter :get_categories
# ...
private
def get_categories
#categories = Category.includes(:products)
end
end
then, you can write in your view:
<% #categories.each do |category| %>
<%= category.name %>
<% category.products.each do |product| %>
<%= link_to p.name, p %>
<% end %>
<% end %>
I also fixed some other errors and convention incompatibilities.
The following code is incorrect.
#category = Category.all
#products = #category.products.all
This code assigns to #categories all the categories, then it attempts to fetch the products. It will not work, unless you have defined a products class method in the Category model. But I don't think so, otherwise you will just have to call Product.all.
Moreover, in the code below, you are trying to display the list of products per category, which definitely don't work with the two assignments before. According to what you are trying to achieve, you can't pre-assign the #products, because you want the products for a specific category.
Let's inline everything into the code.
<% Category.all.each do |category| %>
<%= category.name %>
<% category.products.each do |product| %>
<%= link_to product_path(product) do %>
<%= product.name %>
<% end %>
<% end %>
<% end %>
Next step is to make the code a little bit more performant, giving you need it everywhere.
<% Category.select(:id, :name).each do |category| %>
<%= category.name %>
<% category.products.select(:id, :name).each do |product| %>
<%= link_to product_path(product) do %>
<%= product.name %>
<% end %>
<% end %>
<% end %>
You could use pluck, but it will return an array and it will require a little bit more manipulation. However, it's way more performant.
<% Category.pluck(:id, :name).each do |category_id, category_name| %>
<%= category_name %>
<% Product.where(category_id: category_id).pluck(:id, :name).each do |product_id, product_name| %>
<%= link_to product_name, product_path(id: product_id) %>
<% end %>
<% end %>
It's not a good idea to chain all those methods inside a view, let's extract some code into the model.
class Category
def self.simple_listing
order(:name).pluck(:id, :name)
end
end
class Product
def self.simple_category_listing(category_id)
where(category_id: category_id).order(:name).pluck(:id, :name)
end
end
<% Category.simple_listing.each do |category_id, category_name| %>
<%= category_name %>
<% Product.simple_category_listing(category_id).each do |product_id, product_name| %>
<%= link_to product_name, product_path(id: product_id) %>
<% end %>
<% end %>
You can leave all this code into the view, or extract it into a partial. You don't even need to add a controller before filter, or make it "global". The code is self-contained, does not pollute the name space with instance variables, and it can easily be placed whenever you need it.
I created two scaffolds: announce_sections and announcements. The announce_sections are the types of announcements there are (i.e. games, tryouts, etc) and when I create an announcement I specify what type of announce_sections it is. I'm trying to display it so that each announce_section is viewed, with each announcement and its information under the announce_section. This is what I came up with:
<% #announce_sections.each do |announce_section| %>
<%= announce_section.name %>
<% #announcements.each do |announcement| %>
<%= announcement.announcement_title %>
<%= announcement.information %>
<%= announcement.additional_information %>
<%= announcement.type %>
<% end %>
<% end %>
However, this code only displays the announce_sections with the all announcements under it. The announcements don't get separated into their respective announce_sections. How do I change it so that it does?
<% #announce_sections.each do |announce_section| %>
<%= announce_section.name %>
<% #announcements.where(type: announce_section).each do |announcement| %>
<%= announcement.announcement_title %>
<%= announcement.information %>
<%= announcement.additional_information %>
<%= announcement.type %>
<% end %>
<% end %>
Use the name of the field you are using to assign the announcement type instead of 'type'
There are many ways to solve this, but one simple one is to build a hash where the key is the type of announcement_section and the value is an array (or Set) of the announcement. One way to build that hash is to use the Hash.new {|hash, key| ... } form of the constructor.
#hash = Hash.new {|hash, section| hash[section] = Array.new }
#announcements.each do |a|
# for each announcment append it to the array under the hash
#hash[a.section] << a
end
And then, in the view
<% #hash.keys.each do |section| %>
<%= section %>
<% #hash[section].each do |announcement| %>
<%= announcement.announcement_title %>
<%= announcement.information %>
<%= announcement.additional_information %>
<%= announcement.type %>
<% end %>
<% end %>
I have a model Post with :mark, :text
I have a list with my post
<% #posts.each do |p| %>
# todo
<% if p.mark? %>
<%= p.mark %> <%= sweet_thing(p.text) %>
<% else %>
<%= sweet_thing(p.text) %>
<% end %>
<% end %>
I need to show p.mark name instead #todo where p.mark first time appearance.
Final txt example:
Audi
Audi, text-text-text-text.
Audi, text-text-text-text.
Audi, text-text-text-text.
Ford
Ford, text-text-text-text.
Ford, text-text-text-text.
Ford, text-text-text-text.
Ford, text-text-text-text.
UPDATE
My txt render in controller
def txt_receiver
#posts = Post.where("created_at >= ?", 7.days.ago.utc).find(:all, order: "mark, LOWER(post)")
render "txt_for_newspapper", formats: ["text"]
end
An obvious solution is to keep track of seen marks.
<% seen_marks = {} %>
<% #posts.each do |p| %>
<% unless seen_marks[p.mark] %>
<%= p.mark %>
<% seen_marks[p.mark] = true %>
<% end %>
# rest of your code
<% end %>
A better solution (I think) involves grouping posts by their mark and then outputting in groups. But I'm not sure whether it will match your logic regarding missing marks.
<% #posts.group_by(&:mark).each do |mark, posts| %>
<%= mark %>
<% posts.each do |p| %>
<%= p.mark if mark %> <%= sweet_thing(p.text) %>
<% end %>
<% end %>