Ruby on Rails Polymorphic - Controller and View solution - ruby-on-rails

I have been trying to wrap my mind on a polymorphic structure for my app for a while now, but I can't get it, and I don't know how to solve it.
I have a Supplier model, and a supplier's profile can by a Company OR a Person as listed below.
Supplier Model
class Supplier < ActiveRecord::Base
belongs_to :profile, :polymorphic => true
accepts_nested_attributes_for :personable, :allow_destroy => true, :reject_if => :all_blank
end
Company Model
class Company < ActiveRecord::Base
has_one :supplier, as: :profile
end
Person Model
class Person < ActiveRecord::Base
has_one :supplier, as: :profile
end
On the Model level (on console) my associations works fine, but I have to those between the two model up front (supplier.profile = Company.new, for example).
What I want is, when a user get to the NEW Supplier action he can select on the form if the Supplier is a Person or a Company, and then something must be done to show the correct fields for. And back on the Create action, handle everything.
I have read many stackoverflow questions and watch Ryan Bates cast #197 and #394.
I'm a Rails enthusiastic, not a programmer.
If someone can point me out to the right direction, I would be thankful.
Regards,
David

This largely depends on the variety of fields you need, and how much data is shared. But if they are very different, I would actually have two separate divs or section on the page that a radio button toggles showing one & hiding the other with javascript. Can do this pretty easily with jquery at the bottom of the view, given it's a default in rails:
<input type="radio" name="supplier_type" value="person" />
<input type="radio" name="supplier_type" value="company" />
<script>
$('input[name="supplier_type"]').on('change', function () {
if ($(this).val() === "person") {
$('.person-form').show();
$('.company-form').hide();
}
else {
$('.person-form').hide();
$('.company-form').show();
}
});
</script>
Then inside each of those divs have an entirely separate form that post to different actions. If the data is different enough to require two forms, then it's worth having two different controllers for this.
Now if it's just a couple fields different, or an additional field for one or something. I would have the radio button same as above inside the form_for. Again attach some javascript to show or hide the values. In the controller, use a check against the radio button value to get the params you need:
supplier_params = params.slice :list, :of, :generic, :params
if params[:supplier_type] == "company"
supplier_params.merge! params.slice :company_name
elsif params[:supplier_type] == "person"
supplier_params.merge! params.slice :person_name
end
Supplier.new supplier_params

Related

Choosing a specific row in a table in rails

I have a migration and model with a table called medications. I need to pick a specific row from the medications table. I also am trying to filter out all medications that don't have the current user's id.
Here is the current code I have.
Medication.find(:name, :conditions => { :user_id => current_user.id }, :order => "Medication.name")
I know this isn't complete, but any help would be greatly appreciated.
You can load the first medication for a specific user_id like this (assuming that your medications table has an user_id):
Medication.where(user_id: current_user.id).order(:name).first
When our User model has a belongs_to :medications it can be simplified to:
current_user.medications.order(:name).first
When you want to load the e.g. 5th medication just add an offset of 4:
current_user.medications.order(:name).offest(4).first
Or load all medications and iterate through them:
current_user.medications.limit(10).each do |medication|
puts medication.name
end
When you want to output the first ten medications on a website you would do something like this:
# in the controller
#medications = current_user.medications.order(:name).limit(10)
# in the view
<ul>
<% #medications.each do |medication| %>
<li><%= medication.name %></li>
< end %>
</ul>
The finder syntax you use is deprecated and was replaced in Rails 4. See Rails Guide about querying the database.
This is a perfect use case for a has_many :through association if you don't already have it set up.
class User < ActiveRecord::Base
has_many :prescriptions # or whatever
has_many :medications, :through => :prescriptions
end
class Prescription < ActiveRecord::Base
belongs_to :user
belongs_to :medication
end
class Medication < ActiveRecord::Base
has_many :prescriptions
has_many :users, :through => :prescriptions
end
Now you can do stuff like #user.medications to retrieve only that user's medications, #user.medications.find(params[:medication_id] to find a specific one within a user's assigned medications, and #user.medications << Medication.find_by(name: 'Aspirin') to add a medication to a user, and so on.
This is a basic overview of this technique, but it's a basic Rails concept so there's plenty of information on use cases close to whatever you may be trying to do.
I fixed the problem and I have decided to post the answer in case anybody else seems to have a similar problem.
I ended up not putting anything in my controller or adding anything new to my models. I just used this line of code in the view.
<%= Medication.offset(0).where(:user_id => current_user.id).pluck(:name).first %>
I couldn't have done it without the support of everyone who posted, Thank you!

How to have conditional views in Rails?

I saw a feature in an app that I'd like to be able to implement. The app has several resources - photos, articles etc.. In the nav bar next to the photos and articles tabs there were two buttons - organization and personal. When one clicks on the organization button if they then click on the photos or articles, they get a list of all photos and articles that belong to the members of their organization. If they clicked on the personal button and after that they click on photos or articles, they get lists of only their personal photos and articles, omitting the resources that belong to the other members of their organization. So I wonder how this state is kept between requests.
I imagine that one way would be to constantly pass a variable between the views and the controller and based on that variable to list a particular resource. Or maybe save the state in the session (though I suppose this should be done as a last resort). Another way would be to use a decorator like draper, but I am kind of confused about the specifics of implementing this. I would be very grateful if somebody points me to an article or to a tutorial that shows how to implement such a feature or just provides an overview of the steps.
To be clear, one again: there are links to index different resources, but based on a parameter the index action of the respective controller returns different results. Something like:
def index
if params[:type] == 'organization'
#photos = Organization.find_by(id: params[:organization][:id]).photos
else
#photos = User.find_by(id: params[:user][:id]).photos
end
end
The question is - how do I pass the type parameter - hard code it in the path helpers and have different views with different values for that parameter or is there a better way?
This is the relationship of my models:
class User < ActiveRecord::Base
belongs_to :organization,
inverse_of: :members
has_many :photos,
inverse_of: :owner,
foreign_key: :owner_id,
dependent: :destroy
...
end
class Photo < ActiveRecord::Base
belongs_to :owner,
class_name: User,
inverse_of: :photos
belongs_to :organization,
inverse_of: :photos
...
end
class Organization < ActiveRecord::Base
has_many :members,
class_name: User,
inverse_of: :organization,
dependent: :destroy
has_many :photos,
inverse_of: :organization,
dependent: :destroy
...
end
By Reloading the Page with URL Parameters
I had a similar issue and there's a couple different ways to pass parameters depending on what you want. I actually just went with passing them through the url. If you append ?type=organization to your url, you will get an additional key-value pair in your params, 'type' => 'organization' and you can just use that. If you want to go to this URL through a link, you can just do link_to photos_path + "?type=organization". I ended up using javascript since I wanted something other than a link. Whatever your html element is, give it the attribute onclick=changeParam() and then define that function in your javascript.
function changeParam() {
window.location.search = "?type=organization"
}
That will automatically reload the same page with that parameter. If you want more than one, append &param2=value2 to what you have so far.
Through AJAX
You can also use AJAX to pass parameters if you don't want a full refresh of the page. If you know how to use AJAX already, just use the data option:
$.ajax({
type: "GET",
url: "/photos",
data: { type : organization, param2 : value2 }
})
In this case, your params are sent though data. If you want to know more about AJAX and how to use that, let me know and I will update my answer with more details. It's pretty confusing in Rails and there isn't good documentation, so I'll add what I know if you want.
it can be solved on many different ways, but for example, if I understood you, one of them is that the resources photos, articles etc.. have one field which can be 'organization' or 'personal'. After that by clicking on this in application, you can filter resources (articles, photos,...) by that field.
Everything depends on situation, maybe another solution will be to create totally separated module where you gonna store things like organization, personal etc. This is better if you want to extend latter that and next to organization and personal add something else.
As I said, everything depends on situation and project.
additional:
ok, from your example I can see that by clicking on, for example, user link, you will have user id. Therefore, you can easily show all photos of that user:
# params[id] - user id which is got by clicking
user = User.find(params[id])
#photos = user.photos

Interacting with 5 models in a rails form

I have a form that needs to be able to interact with 5 (and possibly more) models. It is the following:
Patient - Needs to create the patient record and details (Name, address, etc.).
Form - Keeps track of form name and version.
Questions - Keeps track of questions attached to each form.
Answers - The answers to the questions that the patient gives.
Form Response - Keeps track of the patient that filled out the form, and what form it was.
I've created a layout of the models and what I'm thinking the data would look like:
Google Doc - DB Layout
On the Form Response, the completed_by poly relationship is taking into account that a user, third party, or patient can fill out a form
I am stuck on the best way to go about doing all of this through one form. Right now I am going through the patient controller and patient/new view because I picture this all being related to a patient. I'm just not exactly sure how to do all of this nesting inside the form itself (form_for, form_tag, fields_or?) and then dealing with that in the create action of the controller. Even after going over a bunch of railscasts etc, I'm still very stuck. Any insight would be much appreciated.
Well here is what I can understand from the question. There will be five models
Patient, Form, Question, Answer, Formresponse
Now a Form will have many Questions. A Question will have many Answers. A Formresponse will have many Answers.
The model structure should be
class Patient < ActiveRecord::Base
has_many :formresponses
end
class Form < ActiveRecord::Base
has_many :questions
has_many :formresponses
end
class Question < ActiveRecord::Base
belongs_to :form
has_many :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
end
class Formresponse < ActiveRecord::Base
belongs_to :form
belongs_to :patient
end
Now you will have one form for creating/editing patient information, which should be straightforward as no nesting is there.
The creation of Forms will be tricky. Storing attributes of the class Form will be straightforward. To store attributes of the class Question, you would need to handle them in the controller. create form elements such that the name is binded to "questions"
eg,
<input type="text" name="questions[0][name]" value="some name" />
<input type="text" name="questions[0][help_text]" value="some help text" />
note - name and help_text are attributes of your model Question
Now in the controller you can iterate through each value of params[:questions] to get the induvidual form value.
so, there it will be something like this
params[:questions].each do |question|
q = Question.new(question)
#form.questions << q #This will append the question to your #form instance
end
...
#form.save
Similarly you can do this for storing responses. I hope its a bit clear to you now

Creation of object which doesn't have model, while creation need to create several entries in other tables

In application user can enter new post which contain title, content of the post and category of post. So creating new post will be through some simple html form with few fields. Now i don't know where to put logic for creating new post for following reasons:
Post(or posts collection) is object which is constructed from different tables, for example.
#posts = User.joins(entries: [{storage: :vote}, :category])
.where("votes.count > ?", 0)
.select("users.username AS username,
storages.id AS storage_id,
storages.title AS title,
storages.content AS content,
votes.count AS votes,
categories.category_name AS category_name")
.order("votes.count DESC")
So when user create new post application must create new entries in different tables:
1.Create new entry in entries table. (id, user_id, category_id)
2. Create new entry in storages table.(id, title, content, entry_id)
3. Create new entry in vote table.(id, count, storage_id)
In situation where post is model i can use something like resources: posts then in posts controller through new and create i can create new post, but what in situation like this where i don't need posts controller nor post model? So, question is: which place is more appropriate to put logic for creating new post? Q1
My solution is to craete Storages controller with resource: storages, :only => [:new, :create] then through new and create of this controller to populate different tables in db? I'm forcing here only because i dont see any point of other CRUD actions here(like showing all or one storage), because i will not use storages individually but in conjunction with other tables. So from views/storages through new.html.erb and create.html.erb i can construct new post? Q2
Another solution is to create Post controller which doesn't have "corresponding" post model as i stated before. Here i'm guessing i can't use restful routes(CRUD) because there is not :id of post? I only can make manually non-restful routes like:
post 'posts/create/:title/:content/:category' => 'posts#create', :as => 'create_post' then from params[:title], params[:content] and params[:category] to populate other tables. Q3
Im new to rails so dont yell please :D
This sound like a call for nested forms, its covered in a screen cast
here.
You use the resources of the top model, in this case Entry.
and drill down to the 3rd model.
Simple sample of what to do is bellow.
Model should look like so,
entry.rb
class Entry < ActiveRecord::Base
has_many :storages, :dependent => :destroy
accepts_nested_attributes_for :storages, :allow_destroy => true
end
storage.rb
class Storage < ActiveRecord::Base
belongs_to :entry
has_many :votes, :dependent => :destroy
accepts_nested_attributes_for :votes, :allow_destroy => true
end
vote.rb
class Vote < ActiveRecord::Base
belongs_to :storage
end
and the form should look like so, in simple_form style
<%= simple_form_for #entry do |f| %>
<%= f.simple_fields_for :storages do |storage_fields| %>
<%= storage_fields_for :votes do |vote_fields| %>
<% end %>
<% end %>
<% end %>
and if you have all the models set up, you shouldn't have to do anything to the controller.
This approach is also nice because you can add multiple storages and votes ajax style(without reloading the page) if needed, which is always nice.
I'd use a form class instead of nested attributes any day, see http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ for an example of this pattern (Chapter "3. Extract Form Objects")
I've used the pattern often enough to gemify it https://github.com/bbozo/simple_form_class and it's used roughly in this way: https://gist.github.com/bbozo/5036937, if you're interested to use it I'll push some docs
EDIT: reason why I tend to go the form class route most of the time is because nested attributes never failed to bite me in the end, either because strong parameter handling got cumbersome, or validators get too complicated (if persisted? else ...), or persistence logic needs to be extended to support some little knack that resolves into callback hell or recursive saves in the model (before/after save spaghetti troubles)

How to make a custom collection select input with Simple Form

I would like to build a custom collection select input that takes a collection of text fields as input rather than a collection of check boxes. I have three basic models:
class Signature
has_many :signature_terms
has_many :signatures, :through => :signature_terms
...
class SignatureTerm
attr_accessible :initial # user provides initial as verification
belongs_to :signature
belongs_to :term
...
class Term
has_many :signature_terms
has_many :signatures, :through => :signature_terms
...
I need to present every term for the user to initial. With radio buttons or check boxes, this is fairly easy to accomplish:
= simple_form_for #signature do |f|
...
= f.collection_check_boxes :policy_term_signature_ids, #signature.terms, :id, :name
Which allows me to establish the relationship. However, I would like to present the user with every term for them to initial/not initial.
I've read the documentation on customizing simple form field inputs and have had success following the examples out of the code. What I haven't been able to do is implement a custom collection_select input. If someone could point me to an tutorial or functioning example, I'd sure appreciate it.
I do not know if it is the best approach but I would use the checkbox solution you currently have and then show a list of the choosen terms and textfields besides them for the initials.
Creating the list and textfields with JS.

Resources