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.
Related
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'...
I'm making an application that involves booking appointments for users. I have a User model and an AvailableDate model. The user has_many availble_dates and the AvailableDate belongs_to user.
I want to present a form for the user so that they can mark a couple of dates in a calendar and each of the dates they mark will become an AvailableDate object tied to that user.
At the moment my solution is to do all the work that a form_for helper would normally do manually. This involves a lot of javascript and is generally just getting far too messy.
I can't figure out how I should make a form_for tag work when I need to create potentially infinitely many dates. In theory a user could keep marking off dates in the future as available. If I knew how many dates I needed to create for a user, I could do user.available_dates.build, N times. But this doesn't work here.
Can anyone help? It like this problem should be pretty common. Am I designing my application wrong?
One technique is to render the fields for your association once, outside the form.
When the user performs whatever interaction that should create a new set of inputs you use javascript to clone the initial set of fields and insert them into the form. The one thing you need to do is change the name of these inputs so that they are unique. Usually people use the current time in milliseconds for this unique identifier.
Been there & have found several resources to help: Tutorial & Cocoon
The bottom line is you need to ensure child_index is unique for each field. The tutorial I use has child_index: Time.now.to_i to create a truly unique id, consequently allowing you to add as many fields as you want
The best way to do this:
Render fields_for as a partial (passing your form builder object)
When you want to add new field, create ajax_field action
Make ajax_field view have its own form_builder
Both your original & ajax_field forms will call the partial
On front-end, you can use JS to GET new form action & append field to page
I can give you code if you want
So basically I have a Book model that contains information like
book title (string)
author (string)
description (text)
etc.
Now on the front end I added the capability of dynamically adding more author fields. In the form I named it book[coauthor], so if I add a second author, I'd have a field book[coauthor][name_0].
What I'd like to achieve is combine book[author] and all book[coauthor][name_i] to one big string and separate each name with ,. (i.e. so with book[author] being "Alice" and book[coauthor][name_0] being "Bob" I should get "Alice,Bob" saved in book[author] in DB)
First Question: At this point is it better to do it with Javascript or with Rails (in controller)?
Second Questiion: Currently I'm doing it in the Rails controller but got Can't mass-assign protected attribute 'coauthor'. However I don't want to make it a real attribute since all I need is some processing before saving all the information to the author field. What should I do?
Thanks guys.
The first question is a bit loaded, and you might get some heated disagreements but the way I look at it is this. Not everybody runs with Javascript enabled, so I make sure I have a way to do everything through Rails myself.
In this case, it's actually quite easy.
The first thing I recommend is to not send the coauthors as part of the params[:book]. Instead, send them separately as a params[:coauthors]. Then in your controller you can do this:
#book = Book.new(params[:book])
#book.author = ([#book.author] + [params[:coauthors]]).join(",")
#book.save
Actually, it may be better to update params[:book][:author] since that would work for both create and update. Either way, I hope that helps.
To the first question: I don't see any compelling reason to one over the other. JavaScript might provide a bit more ability to validate/fix formatting in this field in 'real time', but I'm not sure that's particularly important.
To the second question: You don't need to make something a real attribute in order to make it accessible. You've presumably created coauthor as a virtual attribute using attr_accessor, but this doesn't automatically add it to the mass-assignment whitelist. To do that, also add it to your attr_accessible list.
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
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.)