retrieving Polymorphic association correct model and id - ruby-on-rails

I am new to polymorphic associations and am a bit lost at the moment.
I am trying to add a functionality to my website where users can ask quotes to different types of professionnals. Each professionnal own a specific type of quote such as :
model photographer own photographerquotes
I did this because each type of quotes is very dependable to the professional model it belongs to, and database model is specific for each.
Though one thing is constant with those quotes models : they can be commented either by the user or the professionnal
I have then made the different quotes models commentable but I am a bit lost when I need to create the commentable variable inside the comments controller #create action.
this tutorial says we can make modules https://gorails.com/episodes/comments-with-polymorphic-associations (at 15:00)
And the railscast here: https://www.youtube.com/watch?v=WOFAcbxdWjY grabs the correct quote model directly from the address bar with his following trick :
def load_commentable
klass = [Article, Photo, Event].detect { |c| params["#{c.name.underscore}_id"] }
#commentable = klass.find(params["#{klass.name.underscore}_id"])
end
actually both seem counterintuitive to me as I am no Ruby expert, nor even Rails expert yet.
Is one of the two solutions a desired solution ? Are there some more possibilities in 2017 developping under rails 5 ?
EDIT
Actually there would be a third possibility :
it is to pass the quote model type and id inside the form as hidden fields. But it the gorails tutorial the person says that anybody can pass these data for us and this is not really safe ...
I am no HTPP expert but I thought the CREATE method couldnt be replicated inside the address bar. I thought only the GET method was 'public'...

Related

Extracting data from an Activerecord with joined tables

Project.joins(:project_status).where(id: 1).first
I want to use a string such as "Project.ProjectStatus.name" and it will return the name of the joined project status, and simply something like Project.name.
.read_attribute() doesn't seem to go deeper to joined/included records. .send works when I do .send('ProjectStatus').send('id') but this does not seem ideal and could be dangerous, these variables come from users.
It's for a templating engine so I might have something like..
"{{Project.name}} status was changed to {{ProjectStatus.name}}"
Are there any gems I can pick up? I'm really exhausting what I've been searching for in Google now. In CakePHP I have used Hash::extract before which is a little like xpath.
What is the association between Project and ProjectStatus here? I assume it's has_one: :project_status?
Anyway, if you want to be able to call #project.name ( where #project is an instance of the class Project; .name should be specific to each individual project, so you should define it as an instance method rather than the class method Project.name), you can create a method in Project model:
def name
project_status.name
end
Assuming that you previously defined the association in model as
has_one: :project_status
Now the name can be retrieved simply by using .name

Showing attributes from a different rails model

I know this is probably an extremely basic level question, but I'm new to rails and can't seem to locate a clear answer in the Ruby Guides on my own; it's likely that I just don't know the term for this and can't figure it out.
I've got two models, documents and companies (companies is a table built by devise). Companies has_many :documents and documents belongs_to :companies. On my form there is a place for the company's name, address, etc., and I would like to populate the associated company on both on the _form.html.erb and the show.html.erb so that it's not necessary to input this information every time you fill out a form. It's not absolutely necessary that the information be present on the _form.html.erb, but it would be nice to go ahead and present this information so as to not confuse the user.
When I try calling #companies.company_name in my documents show view, I hit a nil class error. I've tried adding #companies = companies.all to my controller, but that doesn't work either. Like I said, I'm sure this is a simple problem, but I don't have much hair left and would like to preserve it for another problem.
I was able to find a different method that seems to be working well at the moment. Instead of adding #company = Company.find (params[:id]), I was able to call #document.company.company_name within the show action. I'll forgo to the new and edit for the moment since show was all that mattered.

Rails beginner help with making a custom order form

So what I basically would like to do is make an order form. In this order form, users will submit information about their company members, and there will also be a part at the end of the order form where the user can select option services that are added by my client from a backend. I am new to rails so I would just like someone to help me make sure that I am going to do this using the best possible practice. Here is what I think I need to do:
Make an Order model
Make a Member model
Make a Field model
In my order model make
has_many :members
has_many :fields
In my member model make belongs_to :order and in my field model make belongs_to :order. Then what I need to do in my orders controller is #fields = Field.all and extract it in a #fields.each block.
P.S. there is one other thing I have to do and that is to make the order form displays 3 types of headers: Corporation, LLC, and Non-profit. What I thought would be smart is if in the url I made it like type=1 and type=2 and type=3 and in my model use an if statement like:
def order_type(type)
if type === "1"
"corporation"
elsif type === "2"
"llc"
else
"nonprofit"
end
end
I think using an if statement is kinda sloppy so for that so if someone could please explain to me the best practice that would be great. Please don't try and answer with a railscasts episode because I have checked out a lot of them already. What I would like is an explanation if possible
Thanks guys it means a lot
Are your fields really so complicated that they require their own model? If your "fields" are a one column list just include them as a column in the model they are applied too.
The problem with using the URL method that you suggested is as follows. Say a user creates a model object and assigns it as a "LLC.". Then your URL would have type=1 somewhere in it as you suggested. Now a user bookmarks this URL and afterwards someone realized it was a typo and instead of LLC the heading should be something else. Now you have a unRESTful situation where you are creating dead links on the Internet or you have urls that are encoded. URL encoding should be used for querying data not dceciding what the data should be.
I would not mess with the urls for the heading question. Instead just add a heading field to the main model that will be displayed and have that field either be LLC etc. Then in the view just have a variable that loads in the value for the object being displayed.

What kind of information can the Model access in Rails?

In Ryan Bates's first episode on complex forms, he adds the following to a model:
# models/project.rb
has_many :tasks
def task_attributes=(task_attributes)
task_attributes.each do |attributes|
tasks.build(attributes)
end
end
I've never thought about this before, but how does the Project model know what "tasks" of which Project instance? Does that come from the has_many association? Is it like, when the project is running and I'm viewing a Project, that's the "active" object so project.rb knows which Project object we're referring to, so it knows that tasks is really some_current_project.tasks? (I'm obviously grasping at straws here.)
Also, if someone would point me to some reference that explains other questions like this one, I'd really appreciate it.
I hope my question is clear. Please ask for more clarification in comments if needed.
Please note: I know that Active Record handles CRUD actions and that objects correspond to rows in tables, etc. Those are just descriptions of what Active Record is. I'm looking for how it works when the project is running. I also now the constructs MVC, but I can't seem to find a detailed explanation of what information is sent where with respect to Rails.
(Not sure I fully understood your question, feel free to let me know if that's the case.)
A rails model is basically a ruby class that is persisted to a database. So it acts like a normal ruby object for the most part, with some database magic mixed in.
You tell rails which project instance to load (e.g. by providing an id), and it loads the data from the database.
Then, when you call project.tasks is when the magic happens: the Project model has no tasks method, so it will trigger ruby's method_missing method. This will then load the associated records into model instances and provide access to them via a rails object.
Since a project has many tasks, rails knows it should look into the tasks database and load the rows where project_id is equal to the project model's id attribute.
In short, ruby meta-programming and monkey patching possibilities make much of rails' magic possible.
(Edit for question on routing.)
When you want to edit project number 13, you go to a URL that looks something like www.mysite.com/projects/13/edit. If you look at routes.rb in your config directory, you'll see (in Rails3) resources :projects what Rails does is set up all sorts of paths for you. Behind the magic, the edit path looks like
get '/projects/:id/edit' => 'projects#edit'
This basically says "when a user wants to see www.mysite.com/projects/13/edit, send him to the edit action in the projects controller and set the id parameter to the value that's in that place.
Then in your controller, you'll load the appropriate project with
#project = Project.find(params[:id])
In a similar way, you could do this (this is an dumb example):
In routes.rb, put
get '/projects/:id/edit_name/:name' => 'projects#edit'
And then in you controller
#project = Project.find(params[:id])
#project.name = params[:name]
So rails basically uses magic to assign values in the URL to params you can work with in your controller. You can read more about routing here: http://guides.rubyonrails.org/routing.html

Passing parameters as path variables in ruby on rails

I'm still new to ROR, so pardon the simplicity of the question...
So http://www.example.com/controller/:id displays a record in my table, with :id being a number (1,2,3 etc.).
Is there a way I can have :id in the URL be the value of a field in the displayed record? Such that I can have http://www.example.com/controller/record_field? I want to have a human-friendly reference to specific records in my table. I'm sure this must be possible. Do I change something in routes.rb?
Thanks for the help!
The cleanest way is to add a new find method in your model (or simply use the find_by_fieldname Rails gives you in your control). Then you'll have your controller use that method instead of the regular find(params[:id]) to pull your model record.
Check out Ryan B's screencast on this here. It's pretty easy, and he's a good teacher, so you shouldn't have any problems.
I use the excellent rails plugin named friendly_id.
http://github.com/norman/friendly_id/tree/master
That should sort you out nicely. It is well documented too.
Take care around fields that might have modern Greek characters—might need to figure a work around for those.
Jon Smock's solution will work, too. I tend to prefer the following.
class Hamburger << ActiveRecord::Base
#this normally defaults to id
def to_param
name
end
end
class SomeModelController << ApplicationController
def show
#hamburger = Hamburger.find(params[:id]) #still default code
end
end
#goes in some view
This is the <%= link_to "tastiest hamburger ever", url_for(#hamburger) %>.
This is, loosely speaking, an SEO technique (beautiful URLs are also user-friendly and I suggest them to absolutely everyone even if you don't care about SEO, for example on pages behind a login). I have a more extended discussion of Rails SEO, which includes other tips like this, here.
Important tip: You should consider, at design-time, what you are going to do if the param should change. For example, in my hamburger scenario, it is entirely possible that I might rename "Sinfully Delicious Cheeseburger" to "Triple Bypass". If that changes URLs, there are some possible implications, such as breakage of customer links to my website. Accordingly, for production use I usually give these models an immutable permalink attribute which I initialize to be human-meaningful exactly once. If the object later changes, oh well, the URL stays the same. (There are other solutions -- that is just the easiest one.)

Resources