Can't create belongs_to association in controller - ruby-on-rails-3

I have 2 models Users and Companies. (I'm using Devise for User)
User belongs to Company.
Company has many Users.
My User model includes an client_id column.
At the moment a User signs-up and is directed to the new_company_path where I'd like to create the relationship. (I'd prefer to keep this in 2 steps).
I know my code is wrong here in the companies_controller.rb — but it's where I'm at
def create
#user = current_user
#company = #user.Company.new(params[:company])
respond_to do |format|
if #company.save
format.html { redirect_to root_path, notice: 'Company was successfully created.' }
format.json { render json: #company, status: :created, location: #company }
else
format.html { render action: "new" }
format.json { render json: #company.errors, status: :unprocessable_entity }
end
end

Your problem lies within the line
#company = #user.Company.new(params[:company])
The association from user to company should not be accessed with a capital letter. To get the company associated with a user, you should call it like this:
#user.company
However, if there is no company associated then that method will return nil and you cannot call .new on nil so instead you need to call another method that Rails creates for you called build_company like this:
#company = #user.build_company(params[:company])
The last problem is that since it is the user that belongs to the company, the User instance needs to be updated with the newly created company_id and that will not happen if you only save the company. But when you use the build_company method, it will store the company instance in the association from User so if you call save on the user instead of the company it will create the company and link it to user, like this:
def create
#user = current_user
#user.build_company(params[:company])
respond_to do |format|
if #user.save
format.html { redirect_to root_path, notice: 'Company was successfully created.' }
format.json { render json: #user.company, status: :created, location: #user.company }
else
format.html { render action: "new" }
format.json { render json: #user.company.errors, status: :unprocessable_entity }
end
end
end

Your User model needs a company_id column. Then you can make a form to record that value wherever you like (i.e., on the new_company_path page).

Related

Rails: Nested resource and specifying the "nester"

A company has many properties. A property has one company.
In my routes file I got:
resources :companies do
resources :property_managers
end
In the property_manager_controller, my create action looks like this (default scaffold implementation slightly modified to accommodate the company):
def create
#property_manager = PropertyManager.new(params[:property_manager])
#property_manager.company_id = params[:company_id]
respond_to do |format|
if #property_manager.save
format.html { redirect_to company_property_managers_path, notice: 'Property manager was successfully created.' }
format.json { render json: #property_manager, status: :created, location: #property_manager }
else
format.html { render action: "new" }
format.json { render json: #property_manager.errors, status: :unprocessable_entity }
end
end
end
Is there a way in which I do not have to explicitly set the company_id, since it is known within the context of the URL/route?
I guess you could do something like the following, not sure if it's better or not:
class PropertyManagersController < ApplicationController
before_filter :find_company
def new
#property_manager = #company.property_managers.build
end
def create
#property_manager = #company.property_managers.build(params[:property_manager])
respond_to do |format|
...
end
end
private
def find_company
#company ||= Company.find(params[:company_id])
end
end

Rails 3 basic validation not working with :prompt or :include_blank?

I have the following code in my view:
<%= f.select :user_id, user_all_select_options, :include_blank => '--Select a name-----' %>
It displays a list of users with --Select a name----- at the top. When a user doesn't select a name from the list and leaves --Select a name----- selected I get errors because the user_id is blank.
For reference the helper method code looks like this:
def user_all_select_options
User.all.map{ |user| [user.name, user.id] }
end
My model is as follows:
class Parcel < ActiveRecord::Base
attr_accessible :parcel, :received_at, :received_by, :chilled, :courier, :location, :passed_to, :user_id, :user, :content, :localip
validates :user_id, :presence => true
belongs_to :user
end
For some reason the validation doesn't appear to be running, however if I select a user from the drop down and add a validation for an other field input the application correctly shows the user a message stating which field is incorrectly empty.
Interestingly if I leave the select drop down as --Select a name----- and keep the additional validation, an exception is thrown. It doesn't prompt for the missing fields it just errors.
Here is the record during an error (this record was from when I had a validates presence check on the location field:
{"utf8"=>"✓", "authenticity_token"=>"wM4KPtoswp3xdv8uU4UasdadNsZi9yFZmk=", "parcel"=>{"user_id"=>"", "received_by"=>"dan", "content"=>"", "chilled"=>"0", "courier"=>"", "location"=>"", "passed_to"=>"", "received_at(3i)"=>"9", "received_at(2i)"=>"2", "received_at(1i)"=>"2013", "received_at(4i)"=>"22", "received_at(5i)"=>"59"}, "commit"=>"Receive this Parcel", "action"=>"create", "controller"=>"parcels"}
Where should I start looking? The errors that show are when the controller does an unless check against the user.
The parcel controller create method looks like this:
def create
#parcel = Parcel.new(params[:parcel])
#parcel.localip = request.env['REMOTE_ADDR']
#parcel.received_by = #parcel.received_by.upcase
unless #parcel.user.mobilenumber.blank?
UserMailer.parcel_notification(#parcel).deliver
end
respond_to do |format|
if #parcel.save
format.html { redirect_to #parcel, notice: 'Parcel was successfully created.' }
format.json { render json: #parcel, status: :created, location: #parcel }
else
format.html { render action: "new" }
format.json { render json: #parcel.errors, status: :unprocessable_entity }
end
end
end
the reason why you're getting an exception when you don't select a user is this line
unless #parcel.user.mobilenumber.blank?
since the user_id is not set, #parcel.user is nil which causes the exception.
I suggest you move that inside the #parcel.save block.
def create
#parcel = Parcel.new(params[:parcel])
#parcel.localip = request.env['REMOTE_ADDR']
#parcel.received_by = #parcel.received_by.upcase
respond_to do |format|
if #parcel.save
unless #parcel.user.mobilenumber.blank?
UserMailer.parcel_notification(#parcel).deliver
end
format.html { redirect_to #parcel, notice: 'Parcel was successfully created.' }
format.json { render json: #parcel, status: :created, location: #parcel }
else
format.html { render action: "new" }
format.json { render json: #parcel.errors, status: :unprocessable_entity }
end
end
end

Avoid index url after failing create

When you create a User in rails through the create action, the url is changed to
http://myapplication.com/users with POST
before being redirected elsewhere. If validation fails, it appears that the above URL is retained. If you then refresh, you end up on the index page (as it's now a GET).
I would expect if validation was failed the url would remain as
http://myapplication.com/users/new
As i don't have an index page, this is causing me problems. Is there a way to resolve this please?
This depends on the logic in the respond_to block in your controller.
This is a typical example of the create action in users_controller.rb:
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render json: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
So if the save fails, the new action is rendered again.
In your UsersController, do like this:
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to root_url # save success will return to root page
else
render 'new'
end
end

Rails 3, inserting local IP address into record field

I have a Rails 3 application and I would like to add a field to one of my models, the field will be called something along the lines of localip.
How do I add the current local IP to the field?
The standard create method in the controller is like this:
# POST /prescriptions
# POST /prescriptions.json
def create
#prescription = Prescription.new(params[:prescription])
respond_to do |format|
if #prescription.save
format.html { redirect_to #prescription, notice: 'Prescription was successfully created.' }
format.json { render json: #prescription, status: :created, location: #prescription }
else
format.html { render action: "new" }
format.json { render json: #prescription.errors, status: :unprocessable_entity }
end
end
end
Is there some way of inserting it using the create method?
I assume that by "current local IP" you mean IP of the client.
If that's so then you can access this from env hash in your controller action -
current_local_ip = request.env['REMOTE_ADDR']
or
current_local_ip = request.env['REMOTE_HOST']
And if you mean server's ip by current local ip, just look up the value of HTTP_HOST key.

How can I manage public records and user specific records

I have a situation where a company is managed by a user. i.e.: A user can create, read, update and delete their own companies. But I'd also like that same user to access a list of all companies in the system, even when logged out.
e.g.:
user_a manages the following companies: company_a and company_b
user_b manages the following companies: company_c and company_d
user_a should be able to see a list of his own companies (a and b) as well as a list of all companies (a, b, c, and d)
What's the best way to handle this in the controllers?
Idealy, I'd like to have it setup under 2 separate routes as follows:
/companies
/users/1/companies
Should I have one controller for companies, or multiple? and how would that work?
I'm looking for best practices in this type of scenario.
In your situation approach can be:
Use Devise RubyGem to handle authentication. https://github.com/plataformatec/devise
Create or Scaffold simple CompaniesController with RESTful actions set: index, new, create, edit, udpate, destroy actions.
Add before_filter in CompaniesController to restrict access to action which require user authentication:
before_filter :authenticate_user!, :except => [:public_list]
You should have has_many assosiation between User and Company ActiveRecord models, to access companies collection of current_user.
Here goes example code:
Routing:
resources :users do
resources :companies
end
match '/companies' => 'companies#public_list', :as => :public_companies_list
Controller:
class CompaniesController < ApplicationController
before_filter :authenticate_user!, :except => [:public_list]
def index
#companies = current_user.companies
end
def show
#company = current_user.companies.find(params[:id])
end
def new
#company = current_user.companies.new
end
def edit
#company = current_user.companies.find(params[:id])
end
def create
#company = current_user.companies.new(params[:company])
respond_to do |format|
if #company.save
format.html { redirect_to #company, notice: 'Company was successfully created.' }
else
format.html { render action: "new" }
end
end
end
def update
#company = current_user.companies.find(params[:id])
respond_to do |format|
if #company.update_attributes(params[:company])
format.html { redirect_to #company, notice: 'Company was successfully updated.' }
else
format.html { render action: "edit" }
end
end
end
def destroy
#company = current_user.companies.find(params[:id])
#company.destroy
respond_to do |format|
format.html { redirect_to companies_url }
end
end
end
For public companies list add this method:
def public_list
#companies = Company.all
end
IMHO if all user can see all companies it's perfect to have one controller to get this job. Just in template you can check if current user is author of specified company and then add link to edit this company etc. if you want of course.