Iterating in ruby using an if else statement ...(along w. ancestry gem) - ruby-on-rails

Recently started withrails along with ancestry gem for the first time.
I am attempting to iterate through an array of Category objects which are stored in a tree structure via the ancestry gem, in order to display buttons, wired to link_to to other category objects, a given category is related to.
The first 3 categories in the database are the roots, and so I will eventually not have enough space in the view to display buttons for all descendants. So, if the category.parent == 'nil', I would like to display only the children of given category.
However, for all other category objects, I would like to display all descendants of a given category.
I was thinking something along these lines, as to iterate through
<%= current_user.folders.each do |folder| %>
<% if folder.is_root? %>
<% folder.children.each do |child| %>
<%= link_to child, folder_path(child), class: 'btn' %>
<% end %>
<% else %>
<% folder.descendants.each do |desc| %>
<%= link_to desc, folder_path(desc), class: 'btn' %>
<% end %>
<% end %>
Would anyone be able to advise?

How are your child categories related to their parents? I'll assume the categories table has a parent_id column.
Given these categories:
parent_category = Category.create!
child_category1 = Category.create! parent_id: parent_category.id
child_category2 = Category.create! parent_id: parent_category.id
Now you can check if the parent_id is nil to see if it is a parent category or not, we'll add a self-referential association and scope to the Category model:
class Category < ActiveRecord::Base
scope :parents, -> { where(parent_id: nil).joins :children }
has_many :children, class_name: "Category", foreign_key: :parent_id
end
Now you can iterate over the parents and iterate over each parent's children:
Category.parents.each do |parent|
parent.children.each do |child|
# code
end
end

Related

How do I implement associations in searchkick

First, the example I read in the docs shows to declare the associated model as singular, :address, but if I do I get the error Association named 'address' was not found on User; If I change it to plural :addresses, then the next problem I have is the association doesn't work in views undefined method `country' for ...
Why am I declaring the association as plural and how can I make the association available in the view
User.rb:
class User < ActiveRecord::Base
searchkick word_middle: ['full_name', 'description', 'interests']
has_many :addresses
scope :search_import, -> { includes(:addresses) }
search.html.erb:
<% #users.each do |u| %>
<li>
<%= link_to "#{u.first_name} #{u.middle_name} #{u.last_name}", page_path(name: u.name) %>
<% #ua=u.addresses.where("current=?", true) %>
<% if #ua.country=="US" %>
<%= #ua.city %>, <%= #ua.state %> <%= ISO3166::Country.find_country_by_alpha2(#ua.country) %>
<% else %>
<%= #ua.city %>, <%= ISO3166::Country.find_country_by_alpha2(#ua.country) %>
<% end %>
</li>
<% end %>
</ul>
In controller, do this: #user = User.includes(:addresses).where(your_query) to make the association readily available in view.
And yes has_many associations are bound to be plural: "User has_one
:life" and "User has_many :passions"; does it make sense?
And finally your error: where returns an array because you queried: "bring me all records which fulfill this condition". find, on the other hand, will bring back 1 specific record as it expects a unique attribute or brings back first record that matches that attribute.
What you need to do:
You should either do this (if you are dead-sure that you will get 1
such record or you need only one of that type):
<% #ua=u.addresses.where("current=?", true).first %>
OR
If you need to go through all the resultant array then:
<% #ua=u.addresses.where("current=?", true) %>
<% #ua.each do |ua| %>
# Your code for each ua instead of #ua.
<% end %>
Happy Learning :)

How to loop through a self referential model in Ruby on Rails

I'm new to ruby on rails and I'm building a wiki app where the navigation is to be sorted by categories. Each article, or page, can belong to a category, but a category can also be a sub-category of another category. An administrator will be able to create new categories or sub-categories calling for a dynamic approach to generating a list of categories for the menu. I'm trying to figure out how to display a list of all parent categories and all of their children and grandchildren categories where the menu would look something like this:
1. Parent1
1.a Child1
1.b Child2
2. Parent2
2.a Child1
2.a.1 Grandchild1
I currently have some nested loops in my view which kind of work, but it's not dynamic since it will only show the first two generations, and I would have to repeat the code to show more.
Model:
class Category < ApplicationRecord
has_many :sub_categories, class_name: "Category", foreign_key: "category_id"
belongs_to :category, class_name: "Category", optional: true
end
Controller:
class CategoriesController < ApplicationController
def index
#sorted_categories = Category.order(:sort_number).where("category_id IS NULL")
#sub_categories = Category.order(:sort_number).where("category_id IS NOT NULL")
end
end
View:
<% if #categories.nil? %>
<h3>There are currently no categories.</h3>
<% else %>
<ul>
<% #sorted_categories.each do |c| %>
<li><%= c.name %><%= link_to 'Move Up', categories_move_up_path(c) %> Sort:<%= c.sort_number %></li>
<% #sub_categories.each do |s| %>
<% if s.category_id == c.id %>
<ul>
<li>
<%= s.name %><%= link_to 'Move Up', categories_move_up_path(s) %> Sort:<%= s.sort_number %>
</li>
</ul>
<% end %>
<% end %>
<% end %>
</ul>
<% end %>
Any advice would be greatly appreciated, thanks!
Have a look at the acts_as_list gem, it does exactly what you want.
It will define a parent_id column, and each object will be a child of a parent, so that you can create infinite tree of categories ans sub-categories.
It also provides the methods to move objects up and down.

simple_form many to many association as tree?

I am trying to build a UI for updating a model ("Profile") that has a many to many relationship with another model ("Category"). The "Category" model has a self referential relationship with itself for "Sub Categories".
In my simple_form I want to display the categories as checkboxes with their sub categories nested below them as check boxes.
In my current code all I have is this for the association field:
= f.association :categories, as: :check_boxes, collection: #categories
I am just retrieving the top level categories into the variable #categories.
I'm not sure where to go from here. What is the best way to do this?
Ancestry
We've done this before using the ancestry gem:
The problem you have is your self-referential association won't give you the scope required to create a "real" nested dropdown; it has to be able to consider all the nested data.
Instead, what we did was to firstly employee the ancestry gem, and then use a partial and helper to get the nested dropdown affect:
#app/models/category.rb
Class Category < ActiveRecord::Base
has_ancestry
end
Display
If you store the dependent data like that, it allows you to create a partial-based nested effect:
#app/views/admin/categories/index.html.erb
<%= render partial: "category", locals: { collection: collection } %>
#app/views/categories/_category.html.erb
<!-- Categories -->
<ol class="categories">
<% collection.arrange.each do |category, sub_item| %>
<li>
<!-- Category -->
<div class="category">
<%= link_to category.title, edit_admin_category_path(category) %>
<%= link_to "+", admin_category_new_path(category), title: "New Categorgy", data: {placement: "bottom"} %>
</div>
<!-- Children -->
<% if category.has_children? %>
<%= render partial: "category", locals: { collection: category.children } %>
<% end %>
</li>
<% end %>
</ol>
Dropdown
#app/helpers/application_helper.rb
Class ApplicationHelper
def nested_dropdown(items)
result = []
items.map do |item, sub_items|
result << [('- ' * item.depth) + item.name, item.id]
result += nested_dropdown(sub_items) unless sub_items.blank?
end
result
end
end
This will allow you to call:
= f.input :categories, as: :select, collection: nested_dropdown(#categories)

ActiveAdmin -- How to display category taxonomy? (in tree type hierarchy)

I have a model category which contains a field called category.parent_id which is used to create a taxonomy (top level categories with sub categories).
At the moment my index page shows the categories as:
Top level category
Sub category
Sub category
Top category
How could I order them in a proper taxonomy (i.e. first all top level categories are taken from db then sub categories, etc) and shown as the following:
Top level category
-- Sub category
-- Sub category
Top category
After hours of banging my head against the wall, I came up with this solution:
ActiveAdmin can not render a partial for the entire index, however you can render one for a single column!
So the work around was to create a column for sub categories like this:
column "Sub Categories" do |category|
children = Category.children(category.id)
if children.present?
render :partial => "children", :locals => { :children => children }
end
end
And then in _children.html.erb view partial you can print out the list like this:
<% children.each do |child| %>
<p><%= link_to child.name, edit_admin_category_path(child) %></p>
<% end %>
You could create a self referencing Model like this in you case (In app/model/category.rb):
belongs_to :parent, :class_name => 'Category', :foreign_key => 'parent_id'
has_many :children, :class_name => 'Category', :foreign_key => 'parent_id'
Then you can create a scope for the parents
scope :parents, where("parent_id IS NULL")
And in view you can use iteration like this
<ul>
<% Category.all.parents.each do |parent| %>
<li class="parent"><%= link_to parent.name, parent %></li>
<% parent.children.each do |child| %>
<li class="sub"><%= link_to child.name, child %></li>
<% end %>
<% end %>
</ul>
Hope this helps you!
//I read what I wanted to read,... I didn't see it's about active admin. Thought it's about ActiveRecord -.-

Rails - Problem calling column from join table

I have an index of users where I display the user name, location of user and a field called categories, which can range from zero to many items. I'm having trouble getting the categories to display properly. Here are my details:
User
has_many :user_categories
has_many :categories, :through => :user_categories
Categories
has_many :user_categories
has_many :users, :through => :user_categories
User_categories
belongs_to :user
belongs_to :category
In my user_categories table, there are three columns: id, user_id, category_id.
In my categories table, there are two columns: id and name.
In my user/index view, I have the following:
<% #users.each do |user| %>
<%= link_to user.name, user %>
<%=h user.state %>
<%=h user.categories %> ## This line is the problem
<% end %>
Where I've indicated the problem with my comment directly above, I've tried multiple variations, but somehow I'm missing it. Here are the variations that I've tried, and the results that they produce. Note that for testing, user #2 is the only one with a category that should display.
<%=h user.categories %>
displays: #<Category:0x3f40634>
Log shows:
UserCategory Load (0.4ms) SELECT * FROM "user_categories" WHERE ("user_categories".user_id = 2)
Category Load (0.4ms) SELECT "categories".* FROM "categories" INNER JOIN "user_categories" ON "categories".id = "user_categories".category_id WHERE (("user_categories".user_id = 2))
<%=h user.categories.name %> <-- Here I added .name at the end of the call
displays: category
log shows: App does not do a category load.
<%=h user.user_categories %>
displays: #<UserCategory:0x3e4d1a0>
log shows: UserCategory Load (0.4ms) SELECT * FROM "user_categories" WHERE ("user_categories"."user_id" = 2) AND ("user_categories".user_id = 2) LIMIT 1
Does not do a category load.
<%=h user.user_categories.find_by_user_id(user.id) %>
produces same results as above.
Based on this, I think that <%=h user.categories %> is the one that makes the most sense, but I've tried every variation that I can think of and they all throw exceptions.
The model definitions look correct. You may want to check your database to ensure you've used proper conventions for your ids. I was a concerned that you tried to display all categories in the "problem line". Try looping over them:
<% #users.each do |user| %>
<%= link_to user.name, user %>
<%=h user.state %>
<%=user.categories.each do |c| %>
<%h c.name%>
<%end%>
<% end %>
The problem you were having is that in all three cases, you were attempting to display or operate on a collection of ActiveRecord objects. <%= h user.categories %> and <%= h user.user_categories %> will display the results of calling to_s on the collection, which gives the output (#<Category:0x3f40634> or #<UserCategory:0x3e4d1a0>) you noted. <%= h user.categories.name %> will display the result of calling name on the categories association.
jrhicks' answer works because he iterated over the categories and printed out the name of each one. See http://guides.rubyonrails.org/getting_started.html#hooking-comments-to-posts for a similar example - in that case, they iterate over a post's comments.

Resources