Rails 3 create default nested objects - ruby-on-rails

I have a form where the user signs up and creates an Account, an User and a Website.
def new
#account = Account.new
#account.users.build
#account.websites.build
...
end
def create
#account = Account.new(params[:account])
...
Everything works fine. Now, I want to create a default Page with Page.title = "homepage" and Page.body = "".
How can I do that? I tried different options and it doesn't work. For example, I do this #account.websites.pages.build and I get this undefined method pages for []:ActiveRecord::Relation.

The collection returned by #account.websites is an array, rails can't intuit which member of the collection you're trying to create an associated object on... You need to specify which website you want to build a page for, ie
#account.websites.first.pages.build

Related

Save form state for redirection

I'm working on a project in which I have a form, a part of this form is a button which authenticates with a 3rd party service, they click the button, are redirected to the authentication page, then redirected back to the form. When they return I'd like the form to be in the same state it was when they left the page, but I'm not sure of a good way to do this. It's a large form so users would be annoyed if they had to fill it all out again.
Any help with this would be greatly appreciated.
By default you define new method as an instance of object without any data:
def new
#post = Post.new
end
However, it is possible to instantiate object with default values:
def new_with_data
#post = Post.new(title: 'Some default title')
end
Therefore, when you want to store state of form and display it on next page, without storing it in database, you can store it in session / cookie / params (e.g. GET, if they are not sensitive or POST, if you just want to show it on next page; carry data to next view).
def confirm_form_data
#post = Post.new(params[:post])
end
Of course, you don't have to use any models here:
def confirm_data
#data = params[:post]
# use #data in your view
end

Show curent user's members based on user_id (Index View)

I have a user_id column. Instead of calling for all members how can I call up members based on current user's and user_id?
This is my controller, I tried changing .all to user_id or current_user.id plus many variations based on examples. Still can't get it. I also have no models (using authrocket). The create action also works and inserts the user_id, I have a def current_user at the bottom.
class Members::MainsController < ApplicationController
# Member Profile List
def index
#members_mains.user_id = current_user.id
#members_mains = Members::Main.all
end
private
# Common Callbacks
def set_members_main
#members_main = Members::Main.find(params[:id])
end
# White List
def members_main_params
params.require(:members_main).permit(:mfirstname, :mlastname, :mtitle, :memail, :mphone, :mnotes, :smfacebook, :smtwitter, :smlinkedin, :user_id)
end
end
If I got it right, your index action should be something like this:
# Member Profile List
def index
#current_member = Members::Main.find(current_user.id)
end
Do you intend to show a list of profiles for all members?
If not, your index action can simply be removed. If so, you wouldn't normally filter on a user_id at all for that action and you can remove that line.
To load a member profile for a specific user_id, try a show action something like this:
def show
#members_main = Members::Main.find_by(user_id: params[:id])
end
That will load a member profile based on the :id from the URL.
If you want to just show the current user's own profile instead, use current_user.id which will give you the AuthRocket user's ID.
def show
#members_main = Members::Main.find_by(user_id: current_user.id)
end
In either case, you may need to remove :show from the set_members_main callback.
Lastly, you probably want to remove :user_id from members_main_params so that users can't modify the AuthRocket user_id. You only want to control that directly (as you already are in the create action).
Hopefully that's clear enough to get you on your way, but I could be off a bit based on what functionality you actually intend for index and show.

using build in has_many relationship in create action results in mass assignment error

I have a mass assignment error I can only fix with UGLY code. I must be doing something wrong.
Let me explain:
I my rails application clients have many contacts. The Client page shows a list of contacts belonging to that client.
I want to be able to add a new contact to the list. So I pass the client to the contact controller using...
<%= link_to('new contact', new_contact_path(client_id: #client)) %>
In the ContactsController....
def new
client=Client.find(params[:client_id])
#contact=client.contacts.new
end
def create
#client = Client.find(params[:contact][:client_id])
#contact= #client.contacts.build(params[:contact])
if #contact.save
...
The 'save' results in an unsurprising error:
Can't mass-assign protected attributes: client_id
because the contact params include the client_id which is not (and should not be) attr_accessible in the contact model
The only way I know how to fix the problem is to set every parameter individually (excluding the client_id) as follows:
#contact= #client.contacts.build(first_name: params[:contact][:first_name], last_name: params[:contact][:first_name], email: params[:contact][:email])
This approach works but it just all seems wrong. Surely is some more elegant alternative.
Any help is appreciated.
(yes i am new to rails)
To skip the part to tell you how you should fix your error, I would like to tell you how you should do the coding in the first place :) Correct if I am wrong.
In Routes.rb you should have put(if not so already):
resources :client do
resources : contacts
end
Then, second in you view file you should put something like this:
<%= link_to('new contact', new_client_contact_path(#client)) %>
In that way, you don't have to do anything in your create action, rails will manage all other things.
That is the way it is supposed to be done
Edit:
just to make it more clear.
In new action in your contacts controller you should put:
user= user.find(params[:user_id])
#2nd you build a new one
#contact= user.contacts.build
And in your create action in contacts controller , you should put:
user = User.find(params[:user_id])
#2nd you create the contact with arguments in params[:contact ]
#contact = user.contact.create(params[:contact ])
response .....

How do I respect RESTful methods when using find_or_initialize_by in Rails 3.2?

I have a resource in my project that collects some information from a user. Basically it's a form that they fill out before they can access another area of the site. It then sets a cookie for a week, but if they come back it will look up their previous entry and keep their preferences tied to them (and will update any details as long as the email address matches).
Currently I have a Applicants controller that looks like this:
class ApplicantsController < ApplicationController
...
def create
#applicant = Applicant.find_or_initialize_by_email(params[:applicant])
if #applicant.new_record? ? #applicant.save : #applicant.update_attributes(params[:applicant])
set_cookie_and_redirect
else
render 'new'
end
end
def update
if #applicant.update_attributes(params[:applicant])
set_cookie_and_redirect
else
render 'new'
end
end
end
The set_cookie_and_redirect is a private method that just sets some cookies and redirects the user to a page. The code works, but it just feels dirty. It's essentially updating a record within the create method under the condition that it's not a new record. I'm also forced to have an update method in case an existing record comes back with a validation error--the form helper will then switch the form over to sending to the update method.
So to my point... is there a more appropriate way to push the update_attributes call in the create method to the update method? Or better put, is there a better way to respect the RESTful methods in isolating the create and update functionality?
UPDATE: I wanted to be a little more specific too. If the user has filled this form out before it will set a cookie so they don't have to fill it out again for seven days. However after seven days the cookie is expired and they see the form again. The controller doesn't know if the user is new or existing until they add user input into the form which is then compared based on the email address.
Thanks in advance! I definitely look forward to anyone's thoughts on this.
The create method should only create, and the update method should only update. Let Rails decide which is going to happen based on what is inside of #applicant when the form is rendered - It essentially does what you're doing: Checks if the record is new or not, and sends it to update/create accordingly. Example:
def applicant
#applicant = Applicant.find_or_initialize_by_email(cookies[:email])
# renders applicant.html.erb form
end
<%= form_for #applicant do |f| %>
# ... fields ...
<% end %>
def create
#applicant = Applicant.new(params[:applicant])
#applicant.save
# .. etc.
end
def update
#applicant = Applicant.find_by_email(cookies[:email])
#applicant.update_attributes(params[:applicant])
# ... etc.
end
Rails will send the request to the correct action based on the new_record? status of the Applicant object.

Multi-page record creation

puts 'newbie question'
I have an account sign-up that spans multiple pages, but I'm not exactly wrapping my head around creating a new instance that is tied to a database but only adds to the database if all pages are completed.
I have three actions:
def index
#ticket = Ticket.new
end
def signup_a
end
def signup_b
end
The index page only collects a single text field, then passes that to populate the field in signup_a, then to b, which is where the record is finally added to the database. How do I go from passing the variable from index to A to B in a Ticket object without actually adding it to the DB?
Edit---
I think I got tripped up that the line
if #order.save
actually saves the object...I thought it just performed a check.
You can keep it in the session and save it in the database once all the steps are complete .

Resources