How to loop through a self referential model in Ruby on Rails - 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.

Related

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

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

Displaying has_many, :through association records

I am working on an application a deep association. A Story belongs to a Planning Level, the Planning Level belongs to one or many Programs.
class Program < ActiveRecord::Base
has_and_belongs_to_many :planning_levels
has_many :stories, :through => :planning_levels
end
class PlanningLevelsPrograms < ActiveRecord::Base
end
class PlanningLevel < ActiveRecord::Base
has_and_belongs_to_many :programs
has_many :stories
end
class Story < ActiveRecord::Base
belongs_to :planning_level
end
Within the Program show page I'd like to display the Program, each Planning Level and aggregate Story count for each Planning Level.
I'm not sure how to access the Story model from the Program show page.
The following works great for displaying each Planning Level belonging to the Program.
<% #program.planning_levels.each do |p| %>
<p><%= p.name %></p>
<% end %>
...but I have no idea how to make the following work for each displayed Planning Level. How do I access the Story model from with the Program? Is there something needed in the Program controller that I'm missing. Thanks in advance!
#program.planning_level.stories.count(:id)
Each Planning Level:
<% #program.planning_levels.each do |planning_level| %>
<p><%= planning_level.name %></p>
# Aggregate Story count for each planning_level
<p><%= planning_level.stories.count %></p>
<% end %>
Aggregate Story count for #program (if you want):
#program.stories.count
Hope this can help you.
In your view, you can simply use the model associations by name to do this. Try this code as a starting point for your display needs:
<% #program.planning_levels.each do |planning_level| %>
<p><%= planning_level.name %> with <%= planning_level.stories.count %> stories</p>
<% planning_level.stories.each do |story| %>
<p><%= story.name %></p>
<% end %>
<% end %>
You can output any details that you choose for the stories loop. You can add styling to get the presentation that you need.
For instance, you might consider formatting this as a nested list, like so:
<% #program.planning_levels.each do |planning_level| %>
<ul>
<li>Planning Level: <%= planning_level.name %> with <%= planning_level.stories.count %> stories
<ul>
<% planning_level.stories.each do |story| %>
<li>Story: <%= story.name %></li>
<% end %>
</ul>
</li>
</ul>
<% end %>
Adding CSS class and id attributes would give you the ability to add styling to the elements to give your UI some flair.

RoR View: List results grouped under categories

I am trying to group a set of products (obtained typing a query) based on taxonomy(on of the attributes of a product)
My desired output is
Taxonomy 1
prod1 prod2 prod3 prod4
prod 5 ...
Taxonomy 2
pod6 prod7
Taxonomy 3
prod8 prod9..
I am using the following code in the view:
<% taxonomies.each do |taxonomy|%> #"taxomonies" is a set of unique taxonomies for retrieved products
<h1><%= taxonomy%></h1>
<ul>
<% collection.each_with_index do |product,i| %> #"collection" is the list of products retrieved
<li>
<%#ptaxon = product.get_taxonomy%>
<%if #ptaxon == taxonomy%>
<%code for listing product%>
<%end%>
</li>
<%end%>
</ul>
<%end%>
This groups the products based on taxonomies but the format is not what I desire. Could someone please point out my mistake.
EDIT: also tried using < br > , but doesn't help!
This is the output I'm getting. I want the taxonomies earrings, bracelets and necklaces to start from a new line.
Thanks
If you have your associations setup correctly, you can do it like this:
<% taxonomies.each do |taxonomy| %>
<%= taxonomy.name %>
<% taxonomy.products.each do |product| %>
<%= product.name %>
<% end %>
<% end %>
Models should be something like:
class Taxonomy
has_many :products
end
class Product
belongs_to :taxonomy
end

Rails views based on HABTM Category

So I have this app where I'm using a HABTM association to determine "User Skills"; When a new user is created (via the new user view) the user can declare his/her skills via a group of HABTM Checkboxes available on that view with the form...
What I want to do is to have a view where I have Links based on the different skills, for example: "policemen", "doctors", "musicians" etc. And these links should point to other views where I can show to the visitor a list of only the users that belong to the specific category they clicked on.
My users/skills models (association part) look like this:
#User Model
class User < ActiveRecord::Base
has_and_belongs_to_many :skills
#Skill Model
class Skill < ActiveRecord::Base
has_and_belongs_to_many :users
And (if it's helpful) my HABTM checkboxes look like this:
<p> What Skills do you have?
<% for skill in Skill.find(:all) %>
<div>
<%= check_box_tag "user[skill_ids][]", skill.id, #user.skills.include?(skill) %>
<%= skill.name %>
</div>
<% end %>
</p>
Let's say the skills we have are: "policeman, doctor, musician" for example... How can I create links in a view wich point to the group of users that have X skill and then with what code could I render some views that display lists with only the users that belong to X skill category?
I bet the solution is really simple... But I'm missing something obvious, maybe. Could you point me in the right direction?
Thanks!
In config/routes.rb:
resources :skills
Generate a SkillsController with rails g controller skills and put there:
def index
#skills = Skill.all
end
def show
#skill = Skill.find(params[:id])
end
Then your views:
#app/views/skills/index.html.erb
<ul>
<% #skills.each do |skill| %>
<li><%= link_to skill.name, skill_path(skill) %></li>
<% end %>
</ul>
and
#app/views/skills/show.html.erb
<h1>Users who have the <%= #skill.name %> skill</h1>
<ul>
<% #skill.users.each do |user| %>
<li><%= user.full_name %></li>
<% end %>
</ul>
First of all dont use the has_and_belongs_to_many. Here is a link to RoR Guides showing how you are supposed to do the has_many :through assosiation. http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
Secondly if you want to show your user that have 'x' skill it just the skills#show action.
def show
#skill = Skill.find params[:id]
#users = #skill.users
end
And on your links to view this would be something like skill_path(skill)

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

Resources