Altering nested messages helper from div to unordered list - ruby-on-rails-3

I am using a helper method from ryan bates railscasts on ancestry to display nested messages(code below works perfectly).
def nested_messages(messages)
messages.map do |message, sub_messages|
render(message) + content_tag(:div, nested_messages(sub_messages), :class => "nested_messages")
end.join.html_safe
end
The above bit of code nests the individual divs in a tree like structure. I would like to make this into an unordered list, so what i have done is this:
def nested_messages(messages)
messages.map do |message, sub_messages|
content_tag(:ul, :class => "") do
render(message)
content_tag(:li, :class => "nested_messages") do
nested_messages(sub_messages)
end
end
end.join.html_safe
end
The generated html looks fine, however the list items contain no values. Am i doing something wrong?
UPDATE
I would like the generated html to look like this:
<ul>
<li>Main Message</li> <!-- first message -->
<li>
<b>Message 1</b>
<ul>
<li>Message 1 subchild 1</li>
<li>Message 1 subchild 2</li>
</ul>
</li>
</ul>
UPDATE 2
I have changed it to this and it works, thanks to Dave:
def nested_messages(messages)
messages.map do |message, sub_messages|
#render(message) + content_tag(:div, sub_messages, :class => "nested_messages")
content_tag(:ul, :class => "") do
content_tag(:li, :class => "nested_messages") do
render(message) + nested_messages(sub_messages)
end
end
end.join.html_safe
end

You create a ul tag, then render the message. If you do that, what will your HTML look like?
Things inside a ul should be in a nested li: you just render the message.
You need to put it in an li tag so the unordered list has valid content.

Related

render partial view recursively in rails 5

I'm new to ruby on rails and I'm facing a problem rendering nested questions.
What I want to achieve is rendering the question and check if it have children question then render the children questions as well.
there is no limit on the nesting levels, so I have to use recursion method to achieve this and this is what I came up with.
# view file code
<% #questions.each do |q| %>
<%= render partial: "shared/question_block", locals: {q: q} %>
<% if have_children_questions?(q.id) == 'true' %>
<%= print_children_questions( get_children_ids(q.id) ) %>
<% end %>
<% end %>
and here is the helper functions I created
def have_children_questions?(id)
children = Question.get_children(id)
if !children.empty?
'true'
else
'false'
end
end
def get_children_ids(id)
ids = Question.where(parent: id).pluck(:id)
end
def print_children_questions(ids)
ids.each do |id|
q = Question.find(id)
render partial: "shared/question_block", locals: {q: q}
if have_children_questions?(id)
print_children_questions( get_children_ids(id) )
end
end
end
print_children_questions method returning the ids instead of the partial view, what I'm doing wrong?
is there is a better solution
Thanks in advance

Pass variables into rails partial

So basically in my partial I have the following line of code
...
<%= " active" if current_user."#{prop_row}" == "repeat-x" %>
...
So I tried to pass in the following variables "prop_id", "prop_row" using:
<%= render :partial => "users/image_props/repeat", :prop_id => "mbr", :prop_row => "main_background_repeat" %>
I get the error
/Users/codyjames408/rails/moz/app/views/users/image_props/_repeat.html.erb:4: syntax error, unexpected tSTRING_BEG
...= ( " active" if current_user."#{prop_row}" == "repeat-x" );...
...
^
I think the errors because its appending a string instead of the row method. But I am pulling my hair trying to figure how to work around this.
I would love to turn this into a big helper method or something! I just don't know how...
If prop_row is a string, containing the name of an attribute, you ucan do this:
<%= " active" if current_user.attributes[prop_row] == "repeat-x" %>
Or use this:
<%= " active" if current_user.send(prop_row.to_sym) == "repeat-x" %>

Rails 3 displaying count inside a loop

The following displays a list of documents grouped by subject and the name of each document is the name of the packet type. How do I display the the count for each packet type name? so for example if there are two documents for the first subject and their packet type names are 'class' how do I display 'class 1 of 2' and class 2 of 2' next to the packet type name?
controller:
class DevelopController < ApplicationController
def index
list
render('list')
end
def list
#subjects = Subject.includes(:documents => :packet_type)
end
end
view:
<ol>
<% #subjects.each do |subject| %>
<li><%= subject.subject_name %>
<ul>
<% subject.documents.each do |document| %>
<li><%= document.packet_type.name %></li>
</li>
<% end %>
</ul>
<% end %>
</ol>
You can get the total count with .size or .count. To get the current index, you can use each_with_index instead of each. And here on a silver plate : http://apidock.com/ruby/Enumerable/each_with_index :)

Rails HABTM fields_for – check if record with same name already exists

I have a HABTM-relation between the models "Snippets" and "Tags". Currently, when i save a snippet with a few tags, every tag is saved as a new record.
Now i want to check if a tag with the same name already exists and if that´s the case, i don´t want a new record, only an entry in snippets_tags to the existing record.
How can i do this?
snippet.rb:
class Snippet < ActiveRecord::Base
accepts_nested_attributes_for :tags, :allow_destroy => true, :reject_if => lambda { |a| a.values.all?(&:blank?) }
...
end
_snippet.html.erb:
<% f.fields_for :tags do |tag_form| %>
<span class="fields">
<%= tag_form.text_field :name, :class => 'tag' %>
<%= tag_form.hidden_field :_destroy %>
</span>
<% end %>
Ok, i´m impatient… after a while i found a solution that works for me. I don´t know if this is the best way, but i want to show it though.
I had to modify the solution of Ryan Bates Railscast "Auto-Complete Association", which handles a belongs_to-association to get it working with HABTM.
In my snippet-form is a new text field named tag_names, which expects a comma-separated list of tags.
Like Ryan, i use a virtual attribute to get and set the tags. I think the rest is self-explanatory, so here´s the code.
View "_snippet.html.erb"
<div class="float tags">
<%= f.label :tag_names, "Tags" %>
<%= f.text_field :tag_names %>
</div>
Model "snippet.rb":
def tag_names
# Get all related Tags as comma-separated list
tag_list = []
tags.each do |tag|
tag_list << tag.name
end
tag_list.join(', ')
end
def tag_names=(names)
# Delete tag-relations
self.tags.delete_all
# Split comma-separated list
names = names.split(', ')
# Run through each tag
names.each do |name|
tag = Tag.find_by_name(name)
if tag
# If the tag already exists, create only join-model
self.tags << tag
else
# New tag, save it and create join-model
tag = self.tags.new(:name => name)
if tag.save
self.tags << tag
end
end
end
end
This is just the basic code, not very well tested and in need of improvement, but it seemingly works and i´m happy to have a solution!

How to render all records from a nested set into a real html tree

I'm using the awesome_nested_set plugin in my Rails project. I have two models that look like this (simplified):
class Customer < ActiveRecord::Base
has_many :categories
end
class Category < ActiveRecord::Base
belongs_to :customer
# Columns in the categories table: lft, rgt and parent_id
acts_as_nested_set :scope => :customer_id
validates_presence_of :name
# Further validations...
end
The tree in the database is constructed as expected. All the values of parent_id, lft and rgt are correct. The tree has multiple root nodes (which is of course allowed in awesome_nested_set).
Now, I want to render all categories of a given customer in a correctly sorted tree like structure: for example nested <ul> tags. This wouldn't be too difficult but I need it to be efficient (the less sql queries the better).
Update: Figured out that it is possible to calculate the number of children for any given Node in the tree without further SQL queries: number_of_children = (node.rgt - node.lft - 1)/2. This doesn't solve the problem but it may prove to be helpful.
It would be nice if nested sets had better features out of the box wouldn't it.
The trick as you have discovered is to build the tree from a flat set:
start with a set of all node sorted by lft
the first node is a root add it as the root of the tree move to next node
if it is a child of the previous node (lft between prev.lft and prev.rht) add a child to the tree and move forward one node
otherwise move up the tree one level and repeat test
see below:
def tree_from_set(set) #set must be in order
buf = START_TAG(set[0])
stack = []
stack.push set[0]
set[1..-1].each do |node|
if stack.last.lft < node.lft < stack.last.rgt
if node.leaf? #(node.rgt - node.lft == 1)
buf << NODE_TAG(node)
else
buf << START_TAG(node)
stack.push(node)
end
else#
buf << END_TAG
stack.pop
retry
end
end
buf <<END_TAG
end
def START_TAG(node) #for example
"<li><p>#{node.name}</p><ul>"
end
def NODE_TAG(node)
"<li><p>#{node.name}</p></li>"
end
def END_TAG
"</li></ul>"
end
I answered a similar question for php recently (nested set == modified preorder tree traversal model).
The basic concept is to get the nodes already ordered and with a depth indicator by means of one SQL query. From there it's just a question of rendering the output via loop or recursion, so it should be easy to convert this to ruby.
I'm not familiar with the awesome_nested_set plug in, but it might already contain an option to get the depth annotated, ordered result, as it is a pretty standard operation/need when dealing with nested sets.
Since september 2009 awesome nested set includes a special method to do this:
https://github.com/collectiveidea/awesome_nested_set/commit/9fcaaff3d6b351b11c4b40dc1f3e37f33d0a8cbe
This method is much more efficent than calling level because it doesn't require any additional database queries.
Example: Category.each_with_level(Category.root.self_and_descendants) do |o, level|
You have to recursively render a partial that will call itself. Something like this:
# customers/show.html.erb
<p>Name: <%= #customer.name %></p>
<h3>Categories</h3>
<ul>
<%= render :partial => #customer.categories %>
</ul>
# categories/_category.html.erb
<li>
<%= link_to category.name, category %>
<ul>
<%= render :partial => category.children %>
</ul>
</li>
This is Rails 2.3 code. You'll have to call the routes and name the partial explicitely before that.
_tree.html.eb
#set = Category.root.self_and_descendants
<%= render :partial => 'item', :object => #set[0] %>
_item.html.erb
<% #set.shift %>
<li><%= item.name %>
<% unless item.leaf? %>
<ul>
<%= render :partial => 'item', :collection => #set.select{|i| i.parent_id == item.id} %>
</ul>
<% end %>
</li>
You can also sort their:
<%= render :partial => 'item', :collection => #set.select{|i| i.parent_id == item.id}.sort_by(&:name) %>
but in that case you should REMOVE this line:
<% #set.shift %>
Maybe a bit late but I'd like to share my solution for awesome_nested_set based on closure_tree gem nested hash_tree method:
def build_hash_tree(tree_scope)
tree = ActiveSupport::OrderedHash.new
id_to_hash = {}
tree_scope.each do |ea|
h = id_to_hash[ea.id] = ActiveSupport::OrderedHash.new
(id_to_hash[ea.parent_id] || tree)[ea] = h
end
tree
end
This will work with any scope ordered by lft
Than use helper to render it:
def render_hash_tree(tree)
content_tag :ul do
tree.each_pair do |node, children|
content = node.name
content += render_hash_tree(children) if children.any?
concat content_tag(:li, content.html_safe)
end
end
end
I couldn't get to work the accepted answer because of old version of ruby it was written for, I suppose. Here is the solution working for me:
def tree_from_set(set)
buf = ''
depth = -1
set.each do |node|
if node.depth > depth
buf << "<ul><li>#{node.title}"
else
buf << "</li></ul>" * (depth - node.depth)
buf << "</li><li>#{node.title}"
end
depth = node.depth
end
buf << "</li></ul>" * (depth + 1)
buf.html_safe
end
It's simplified by using the optional depth information.
(Advantage of this approach is that there is no need for the input set to be the whole structure to the leaves.)
More complex solution without depths can be found on github wiki of the gem:
https://github.com/collectiveidea/awesome_nested_set/wiki/How-to-generate-nested-unordered-list-tags-with-one-DB-hit