Rendering a view of hierarchical lists with unknown/unlimited nesting - ruby-on-rails

The following pattern is a set of lists nested within other list items.
<ul id="nestedlist">
<li> 1
<ul>
<li> 1.1
<ul>
<li> 1.1.1</li>
<li> 1.1.2</li>
<li> 1.1.3</li>
</ul>
</li>
<li class="connect"> 1.2
<ul>
<li> 1.2.1</li>
<li> 1.2.2</li>
<li> 1.2.3</li>
<li> 1.2.4</li>
<li> 1.2.5
<ul>
<li> 1.2.5.1</li>
<li> 1.2.5.2
<ul>
<li> 1.2.5.2.1</li>
<li> 1.2.5.2.2</li>
</ul>
</li>
<li> 1.2.5.3</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
The model Node
belongs_to :parentnode
has_many :children_nodes, foreign_key: :parentnode_id, class_name: 'Parentnode'
Thus an unlimited number of levels can be defined in a hierarchy.
The view rendering is essential a pattern where each node can become a parentnode
Parentnode.children_nodes.each_with_index do |node, index|
node.name
**parent_indicies.(index + 1)**
end
How can this be rendered in a rails view to represent each node level and its nesting level when the nesting levels are not known beforehand?

You can use rails helper methods to generate rails view.
Example:
content_tag(:ul) do
(1..10).each do |node|
concat content_tag(:li, node)
end
end
And the logic would be to use recursion:
def generate_list(node, level)
return if node == nil
#put logic of generating ul li here.
#add ul if node.children_nodes.present?
#else add li.
node.children_nodes.each_with_index |node, index|
generate_list(node, "#{level}.#{index+1}"
end
end
This is just the base logic. I hope you can do the rest of the part. If you still face difficulty just let me know.
Updated with below working code to generate html:
class Node
attr_accessor :val, :children_nodes
def initialize(value)
self.val = value
self.children_nodes = []
end
end
p1 = Node.new("P")
n1 = Node.new("1")
n2 = Node.new("2")
p1.children_nodes.push(n1)
p1.children_nodes.push(n2)
def generate_list(node, level, final_str)
return if node == nil
if node.children_nodes.size > 0
final_str << "<li> #{node.val}"
final_str << "<ul> "
else
final_str << "<li> #{node.val} </li>"
end
node.children_nodes.each_with_index do |n, index|
generate_list(n, "#{level}.#{index+1}", final_str)
end
if node.children_nodes.size > 0
final_str << "</ul> "
final_str << "</li>"
end
end
def generate(node)
final_str = ""
final_str << "<ul>"
generate_list(node, 1, final_str)
final_str << "</ul>"
final_str
end
puts generate(p1)

Related

How to Make a TODO list item when a database cell matches today's date

I'd like to create a todo list looking for any cell that matches today's date on either agreement, start or due date.
<% #project.where("project_date_agreement + project_date_start + project_date_due > ?", Date.today).each do |tasks_today| %>
<ul>
<li>Item Due Today</li>
</ul>
<% end %>
Any help getting me in right direction would be appreciated.
You want to use OR conditions in your where clause. Properly speaking this would be in your controller.
#projects = Project.where('date_agreement = ? OR date_start = ? or date_due = ?', Date.today, Date.today, Date.today)
In your Project Model you might want to create a field that say's what's due...
def due_because
due_array = []
due_array << 'Agreement date' if date_agreemnt == Date.today
due_array << 'Start date' if date_start == Date.today
due_array << 'Due date' if date_due == Date.today'
due_array.join(', ')
end
Then in your view you would iterate over the #projects
<ul>
<li>Items Due Today</li>
<% #projects.each do |project| %>
<li><%=project.name%> <%=project.due_because%></li>
<% end %>
</ul>
if you asking one row and different column, how about if you combine with table
<% #tasks = #projects.where("date_agreement = ? AND date_start = ? AND date_due = ?",Date.today ,Date.today, Date.today) %>
<table>
<tr>
<ul>
<% #tasks.each do |task| %>
<td>
<li><%= task.check_box :item_due %></li>
</td>
<% end %>
</ul>
</tr>
</table>

ruby on rails simplework calendar

good day,
i am rendering simple work calendar work days only and i wish indicate active day
source is on https://gist.github.com/netmoleCBA/c9fd3029a997fb50798774ec7200d080 but very shortly
controller
require 'date'
class HomeTouchController < ApplicationController
layout 'touch'
def index
session[:actualday]=Date.today()
#week=session[:actualday].at_beginning_of_week..session[:actualday].at_end_of_week).first(5)
#day=session[:actualday]
end
end
index template
<%= #week.each do |actualday| %>
<%= render partial: 'den', locals: { actualday: actualday, day: #day }%>
<%end%>
partial template den.html.erb
<%=if #actualday.wday=#day.wday%>
<li><a hreflang="cs-cz" class="menu-item active" ><i class="mdi mdi-numeric-5-box right-3"></i><%= l activeday%></a></li>
<%else%>
<li><a hreflang="cs-cz" class="menu-item" ><i class="mdi mdi-numeric-5-box right-3"></i><%= l activeday%></a></li>
<%end%>
thanks for any advices
Corrected code:
controller
require 'date'
class HomeTouchController < ApplicationController
layout 'touch'
def index
session[:actualday] = Date.today()
#week =(session[:actualday].at_beginning_of_week..session[:actualday].at_end_of_week).first(5) #MISSING (
#day = session[:actualday]
end
end
index.html.erb
<% #week.each do |actualday| %>
<% if actualday.wday == #day.wday %>
<li><a hreflang="cs-cz" class="menu-item active"><i class="mdi mdi-numeric-5-box right-3"></i><%= l activeday%></a></li>
<% else %>
<li><a hreflang="cs-cz" class="menu-item"><i class="mdi mdi-numeric-5-box right-3"></i><%= l activeday%></a></li>
<% end %>
<% end %>

Ruby (on Rails) nested if statements with or condition

I was looking for a way to improve this piece of code:
<% if A || B %>
<a></a>
<ul>
<% if A %>
<li>Action A</li>
<% end %>
<li>Action C</li>
<% if B %>
<li>Action B</li>
<% end %>
</ul>
<% end %>
this involves to evaluate A, eventually B and then A again and B again. Any idea on how to improve that code?
P.S.
Order of actions matters.
In your controller:
#action_list = []
if A || B
#action_list << "Action A" if A
#action_list << "Action C"
#action_list << "Action B" if B
end
In your view:
<% if #action_list.count > 0 %>
<a></a>
<ul>
<% #action_list.each do |action| %>
<li><%= action %></li>
<% end %>
</ul>
<% end %>
I'd go with the same approach as msergeant, but stick it in a class like so:
class ActionListItems
def initialize(a = nil, b = nil)
#a = a
#b = b
end
def render?
!!(#a || #b)
end
def each
yield "Action A" if #a
yield "Action C"
yield "Action B" if #b
end
end
action_list_items = ActionListItems.new "a", "b"
puts action_list_items.render?
#=> true
action_list_items.each do |item|
puts item
end
#=> Action A
#=> Action C
#=> Action B

What is the proper way to form this multi-dimensional array?

I am attempting to perform a series of requests that will organize the results in a given date span in a way where each location will live on the top level, and contain an another array with each set of results. Here is what I have so far:
locations = Location.all
#requests = []
locations.each do |location|
request = PurchaseRequest.where('created_at >= ? AND created_at <= ? AND location_id = ?', params[:start_date], params[:end_date], location.id).order(:location_id)
#requests.push(location => request)
end
My ideal (non-working) implementation in the view would look something like:
<ul>
<% #requests.each do |location| %>
<li>location[0].name</li>
<ul>
<% location[1].each do |request| %>
<li>request.name</li>
<% end %>
</ul>
<% end %>
</ul>
Try something like this:
#requests = {}
Location.all.each do |location|
#requests[location.name] = PurchaseRequest.where('created_at >= ? AND created_at <= ? AND location_id = ?', params[:start_date], params[:end_date], location.id).order(:location_id)
end
and:
<ul>
<% #requests.each do |location_name, request_list| %>
<li><%= location_name %></li>
<ul>
<% request_list.each do |request| %>
<li><%= request.name %></li>
<% end %>
</ul>
<% end %>
</ul>
#requests becomes a hash with the key being the location name and the value being the purchase requests.

How can I collapse this very repetitive Ruby/Rails code?

I've got two small structural issues that I'm not sure how to handle given my relative newbie-ness with RoR.
First issue: In one of my views, I have code that looks like this:
<ul style="list-style-type: circle">
<li><%= #apples.size %> apples</li>
<li><%= #oranges.size %> oranges</li>
<li><%= #bananas.size %> bananas</li>
<li><%= #grapefruits.size %> grapefruits</li>
</ul>
Is it possible to refactor this so that I only need to iterate once over some list of different kinds of fruit, and have the appropriate <li>'s be automatically generated? Edit: I forgot to add that #apples, #oranges, etc., might be nil. Is there an idiomatic way to handle that?
Second issue: In my controller, I have code that looks like this:
#apples = Apple.find(:all)
#apples.each { |apple| apple.do_stuff(:xyz) }
#bananas = Banana.find(:all)
#bananas.each = { |banana| banana.do_stuff(:xyz) }
# ... &c
As you can see, the same operation is invoked many times in exactly the same way. Is there a way to shorten this to something like [Apple.find(:all), ...].each { |fruit| ... } and have that work instead?
Thanks very much for your help!
I'd do this in a helper
def fruit_size(fruit)
list = #fruits[fruit]
return if list.empty?
content_tag(:li, "#{list.size} #{fruit}")
end
And this in the view:
<% ["apples", "oranges", "bananas", .....].each do |fruit| %>
<%= fruit_size(fruit)
<% end %>
In your controller:
#fruits = {}
["apples", "oranges", "bananas", ......].each do |fruit|
#fruits[fruit] = fruit.classify.constantize.find(:all).each {|record|
record.whatever_here
end
end
It makes sense to store all the items in a hash, #fruits, so that you don't have to use instance_variable_get and stuff.
Perhaps you also want to define that array somewhere, so that you don't have to repeat it in the controller and in the view. Let's pretend that you have a fruit model.
class Fruit < ActiveRecord::Base
FRUITS = ["apples", "oranges", "bananas", ....]
end
Then, use Fruit::FRUITS in the view and controller.
For the first part:
#li = ''
[#apples, #oranges, #bananas, #grapefruit].each{|fruit|
#li << "<li>#{fruit.size}</li>"}
<ul style="list-style-type: circle">
<%=#li%>
</ul>
You can actually do it pretty simply.
In your controller:
def whatever
#fruits = {
:apples => Apple.find(:all).each{ |a| a.do_stuff(:xyz) },
:bananas => Banana.find(:all).each{ |a| a.do_stuff(:xyz) } # ...
}
end
In your view:
<% #fruits.each |k, v| %>
<li><%= v.nil? ? 0 : v.size %> <%= k.to_s %></li>
<% end %>
Although you might want to consider whether do_stuff is something that could be done via a more complex finder, or by named scope.

Resources