Access parent through child in view using rails - ruby-on-rails

I have a model Field with children Topics. Topics are given to posts and I want to display the Fields which topics are children of (e.g If a post has the topics 'rofl', 'lol' and 'lmao' and all of these topics belonged to the parent 'Internet abbreviations').
I want to display something like this:
INTERNET ABBREVIATION rofl lol lmao
Here is the code from the view.
<div class="fields">
<% reading.topics.each do |topic| %>
<span class="indivfields">
<%= link_to topic.field.name, field_path(topic.field) %>
</span>
</div>
<div class="subjects">
<% reading.topics.each do |topic| %>
<span class="indivsubjects"> <%= link_to topic.name,field_topic_path(topic.field,topic) %></span>
<% end %>
</div>
<% end %>
The problem is because the first reading.topic.each do is topic and not field so, continuing the example above, the output I end up with is this:
INTERNET ABBREVIATION rofl lol lmao
INTERNET ABBREVIATION rofl lol lmao
INTERNET ABBREVIATION rofl lol lmao
It prints based on the number of children and I want this to display once. Thanks!

Reading has_many field has_many topics it seems. I would structure the models like this:
class Reading
has_many :fields
has_many :topics
end
class Field
belongs_to :reading
has_many :topics
end
class Topic
belongs_to :reading
belongs_to :field
end
That way you can do reading.fields.each:
<%= reading.fields.each do |field| %>
<h1><%= field %>
<%= field.topics.each do |topic| %>
<p><%= topic %></p>
Why? Because you want to have your software to map the domain model (or how things are in the real world) as much as possible. It just seems intuitive, even from an outsider perspective, that a post have the option to be categorized by Field and Topic.
For our application we do the same thing. We have Questions, which have Subjects and Topics, and Topics belong to Subject. Same database schema as the one I have above (just different names).
However if that didn't make sense (hehe) the other ways to solve this are to do a group to move up the models from Topic to Field. Involves SQL (check this out: http://guides.rubyonrails.org/active_record_querying.html#group), and grouping via a hash (Group Hash by values in ruby). Sorry if I am not as helpful as I can, I am a bit tired now.
By the way I suggest don't do random abbreviations like indiv. I know it is in your best intention, and it sounds intuitive to abbreviate individual to indiv, but it's just best coding practice to not have random abbreviations, maybe someone else will join you in the coding and they might think about what indiv is. (I don't have the article to Clean Code but check this out: http://agileinaflash.blogspot.com/2009/07/abbreviations.html)

So I have an answer for you all. One of my classes is still poorly named using the 'indiv' abbreviation so please forgive me for that. I needed to use the group_by method. Here is the code:
<% reading.topics.group_by(&:field).each do |fields, topics| %>
<%= link_to fields.name, field_path(fields), class: "individualfields" %>
<div class="subjects">
<%= topics.collect { |t| link_to t.name, field_topic_path(fields, t), class: "indivsubjects" }.join.html_safe %>
</div>
<% end %>
And thats it. Thanks for getting to this answer must go to various people on the Rails IRC channel. It worked out very nicely because this code is actually smaller than my previous code for rending topics alone, and this renders fields and topics.

Related

Rails 5.2 each statement without duplicates values

<% Schedule.all.each do |schedule| %>
<%= link_to schedule.county, events_path(County: schedule.county) %>
Displays duplicates in the Schedule table
I've tried all below without luck in displaying no duplicates.
<% Schedule.all.each.uniq do |schedule| %>
<% Schedule.distinct.pluck(:county) do |schedule| %>
<% Schedule.distinct.each.pluck(:county) do |schedule| %>
They display nothing for some reason.
Edit:
Associations --
Event has_many :schedules, inverse_of: :event
Schedule belongs_to :event
So the original each block does bring in what I want but grabs the county for each schedule in the database. This results in duplicate county listings. I'm trying to remove these duplicates which I presumed was simple enough by adding a uniq method to the statement but looks like it requires joins. Thanks.
It depends on the association between schedule and country model. Assuming Schedule belongs to country
<% Schedule.joins(country).select(:country).distinct.each do |schedule| %>
<%= link_to schedule.county, events_path(County: schedule.county) %>
<% end %>
<% Schedule.select(:county).distinct.each do |schedule| %>
So looks like I didn't need a join. It was more just a select and then use distinct method which works. Thanks #Tacyons for putting me in the right direction.

Accepting nested attributes in within a model, that contains other models not associated to itself

Okay I have a small issue which I'm not of. I know I'm close to solving this issue but I need some help. Okay so basically what I'm creating is a website that allows users to submit surveys, and then allow people to answer those surveys. So I created a survey model, which uses questions, and those questions use answers. When a user creates a survey he only creates the survey and the questions, the answers are later created through another model called results. What I'm having trouble doing is submitting those answers as part of the questions through the results form. So here is my results controller:
class ResultsController < ApplicationController
before_action :set_result, only: [:show]
def show
#survey = Survey.find(params[:survey_id])
end
def new
#survey = Survey.find(params[:survey_id])
#survey.questions.all.each do |question|
question.build_answer
end
#result = #survey.results.build
end
def create
#survey = Survey.find(params[:survey_id])
#result = #survey.results.build
respond_to do |format|
if #result.save
format.html { redirect_to survey_result_path(#result), notice: 'Survey was successfully answered.' }
else
format.html { render :new }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_result
#result = Result.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def result_params
params.require(:result).permit(:survey_id)
end
end
So in my form I'm attempting to submit the answers as part of the questions and not the results.
Here's how that looks:
<%= form_for([#survey, #result]) do |f| %>
<% if #result.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#result.errors.count, "error") %> prohibited this result from being saved:</h2>
<ul>
<% #result.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<% for question in #survey.questions %>
<div class="field">
<label><%= question.question_content %></label>
<%= fields_for :answer, question.build_answer do |builder| %>
<%= builder.text_field :answer_content, placeholder: "Answer"%>
<% end %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Anyway, this is the relevant part:
<% for question in #survey.questions %>
<div class="field">
<label><%= question.question_content %></label>
<%= fields_for :answer, question.build_answer do |builder| %>
<%= builder.text_field :answer_content, placeholder: "Answer"%>
<% end %>
</div>
I should also mention that, questions and answers have no controllers, they're only models, I don't need any views or controllers for them, there simply isn't any reason to have those components.
I don't know if it is an issue that the answers and questions are in no way related to the results. If it is better to change that then I'll do it, but so far this is the only issue I've come across with. If anyone can help me with this, I'd appreciate, this is the last step to a long difficult project.
I'm taking your word for it that you have tried #max's suggestion of looking how this has already been done - I agree with #max that seeing how people have already solved it will give you a better answer than what I've got for you. But, if you've tried that, then maybe this can help.
This is not an answer to your question, but it might be a solution to your problem. I agree with #max's first comment also - it seems like you're trying to do too much with that controller. If it were me, I would do some refactoring and take a look at your overall design rather than working through this specific problem.
Here's my suggestion. (Disclaimer: I haven't re-created your project to test it out, or provided enough detail for it to be a step-by-step instruction.) It is just one idea of another approach to the problem. I think it would make your issue go away by simplifying what each controller has to do.
Suggestions
Split up your controllers - SurveysController and ResultsController like #max suggested
Associate the Result class with the Survey class (probably survey has_many :results, if one survey can be completed more than once)
From a routing perspective, use nested resources to have Results as a child of Survey (so your url might look like http://yoursite.com/survey/12/result/2 to view the second set of answers to survey #12)
From the Survey view, link to your new Result route as your "respond to the survey" link. In other words, answer a survey by creating a new Result that belongs_to the survey in question. This new result will be created, edited, and saved from a different controller than you use for the survey creation and management
Key part: from the Result model, when you create a new result, use a callback to create one answer for every question in the associated survey (but leave the text of the answers empty for now) [See below for result.rb snippet]
from the view, allow the user to provide text for each empty answer [See below for views/results/edit.html.erb snippet]
when displaying the results, match the survey question ids to the result answer
ids
in result.rb
after_initialize :create_answers
def create_answers
return unless self.new_record? # don't create them if it isn't a new record
self.survey.questions.each_with_index do |q,i|
self.answers.create(question_index: i)
end
end
in views/results/edit.html.erb
<% #result.answers.each_with_index do |a,i| %>
<!-- label the answer with: -->
<%= #result.survey.questions[i] %>
<!-- render the form field for: -->
<%= a.text %>
<% end %>
from views/results/show.html.erb accessed from http://yoursite.com/survey/12/result/2
<dl>
<% #result.survey.questions.each_with_index do |q,i| %>
<dt class="question"><%= q.text %></dt>
<dd class="answer"><%= #result.answers[i] %></dd>
<% end %>
</dl>
Summary
This lets you use the same basic controller logic for creating a survey that you would when saving the results. You let the model take care of matching questions to answers, and then the controller only has to deal with the result and it's child answers, or the survey and it's child questions.
Again, not a full solution, but hopefully some ideas to help you keep moving.
Further Improvements
This is still a bit ugly - #result.survey.questions[index] isn't very clean. If it were me, I would get everything working, and then refactor the #result.survey.questions[index] stuff into the Result model somehow - maybe something like: Result#question_answer_pairs that returns a 2D array (but that's still not very clean). Or, use ActiveRecord to associate each answer with a corresponding question, so you can use answer.text and answer.question_text side by side.

Access object in the join table

I have a #minisets model and a #miniatures model. They both have_many of each other through the #contents model.
As well as the foreign keys, the #contents model also has a quantity column.
From my #minisets show view I can show the associated #miniatures with the following:
<% #miniset.miniatures.each do |miniature| %>
<%= link_to miniature.name, miniature %>
<% end %>
I want to be able to show the quantity entered for those miniatures but can't work out how to call information from the join table rather than the table it is joining.
Something like <%= miniature.content.quantity %> except that won't work. I assume the joining model must be in play for it to be supplying the joined info but how do I interact with it itself in that instance?
Figured it out.
I need to be working with the join object in the instance variable rather than the joined object.
Find the #contents which belong to this #miniset and then get the #miniature info from there. Makes much more sense.
<% #miniset.contents.where(miniset_id: #miniset).each do |content| %>
<%= link_to content.miniature.name, content.miniature %>
x<%= content.quantity %>
<% end %>
Found some very complicated answers to similar questions but this is dead simple. Hope it helps someone.

Stuck trying to list associated model

I think I am deeply misunderstanding how to write instances.
Miniatures have_many Manufacturers through the Productions table.
On the miniatures show page I am trying to list all the manufacturers for the current miniature and have them link the Manufacturer show page. Like so:
<% #miniature.manufacturers.each do |manufacturer| %>
<%= link_to #miniature.manufacturer.name, manufacturer_path %>
<% end %>
Needless to say it does not work. It gives "undefined method `manufacturer'".
I have tried A LOT of different combinations to no avail. The following version puts all the manufacturers, rolled into one link, once for each manufacturer a miniature has, and links to /manufacturers. A big mess.
<% #miniature.manufacturers.each do |manufacturer| %>
<%= link_to #miniature.manufacturers.map(&:name).join(', '), manufacturer_path %>
<% end %>
I have been working on other things and hoping I would get the hang of this but I'm pretty sure it's something pretty fundamental about how I set up the instance.
If it's more likely something I need to add to the controller then I can add my controller code here. Any help much appreciated.
Does this work:
<% #miniature.manufacturers.each do |manufacturer| %>
<%= link_to manufacturer.name, manufacturer_path(manufacturer) %>
<% end %>

Syntax for form_for when building an array from checkboxes

I'm making a form for an Order object, and the order has many Products, via a join table called OrderProducts. So, we've got something like this:
<% #order = Order.new %>
<% form_for #order do |f| %>
<% #products.each do |product| %>
... want to iterate over products here to build up "order[product_ids][]", with one checkbox per product
<% end %>
<% end %>
Usually for each product i would have a check_box_tag, saying
<%= check_box_tag "order[product_ids][]", product.id, #order.product_ids.include?(product.id) %>
But this, while working fine, always feels like a bit of a cop out. Is there a way i can do it with the f.check_box syntax? Important note - on the project in question I'm working in Rails 2.2.2, so a solution that works in rails 2 would be ideal.
Rails <= 2.x (original)
<% #products.each do |product| -%>
<% fields_for 'product[]' , product do |product_fields| -%>
[...]
<%= product_fields.check_box :id %>
<% end -%>
<% end -%>
Rails >= 3.x (updated)
<% #products.each do |product| -%>
<%= fields_for 'product[]' , product do |product_fields| -%>
[...]
<%= product_fields.check_box :id %>
<% end -%>
<% end -%>
I know the author was looking for version 2 answers, but this is the top hit for google and I though I would update:
One can do this ( I'm using 4.0, don't know how far back it goes ):
<%= form_for #order do |form| %>
<%= form.collection_check_boxes(:product_ids, Product.all, :id, :labeling_method ) %>
<% end %>
For more info: http://edgeapi.rubyonrails.org...
I've done a number of multi checkbox forms over the years and different Rails version. Rails has never provided any really clean way to do it, but the "cop out" solution you came up with is pretty good isn't it? It's one line, it's explicit, and as long as the list of products is reasonably short it will perform adequately.
To answer your specific question, f.check_box will never work for this. It's just short hand for the check_box_tag, but none of the semantics apply. If you want to go Rails native, the only possibility I think is to use nested attributes. Part of the problem is that there is not one obvious way for this type of thing to work. Rails core went through a lot of planning and feedback to come up with nested attributes as they exist, and though they seem a bit obtuse, they capture the most common use cases quite elegantly. But nested attributes were introduced in Rails 2.3, and besides they will introduce quite a bit of conceptual overhead for something which sounds like it doesn't need the complexity.
There are also some plugins that provide helpers for this, although I haven't used any in a long time (since Rails 2 era actually). My impression is that they too are overkill unless you have many forms that make use of this pattern.
In short, I think you should go ahead with your existing solution.
formastic gem
check_boxes option is very good to implement multiple checkboxes
like
f.input :yourcolumn, :as => :check_boxes, :collection => your_collection

Resources