One to one in rails aplication - sql

i have question about proper way to do one to one association in rails.
I am using Devise GEM as user module and all users can have only one company so i made
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :company
end
and
class Company < ActiveRecord::Base
belongs_to :user
end
What is proper way to create company?? normally i made it like this:
i check if company exist if exist display message if not proceed to create. Is that good method?
def new
if Company.exists?(:user_id => current_user.id )
flash[:notice] = "Only One company is allowed."
redirect_to(:action => 'index')
end
end
def create
# Instantiate a new object using form parameters
#company = Company.new(company_params)
##company = Company.new(params[:page].merge(:user_id => 1))
# Save the object
if #company.save
# If save succeeds, redirect to the index action
flash[:notice] = "Company created successfully."
redirect_to(:action => 'index')
else
# If save fails, redisplay the form so user can fix problems
render('new')
end
end

If your form is properly created, it should be OK.
in your User form, you should use fields_for, and it should look like something like this :
<%= f.fields_for :company do |company_field| %>
<%= company_field.text_field :name %>
<% end %>
Edit : Don't forget to add nested attibutes : accepts_nested_attributes_for :address on the model user, below the has_one :company.
Nested attributes allow you to save attributes on associated records
through the parent.
Beside that, be carefull with the validation you wanted to do in the new method, the proper way to handle validation is to use ActiveRecord Validations.

Related

Devise - mass assignment error when changing other users passwords in specific password change page

In my RoR application I'm using devise and the client requires a bit of customisation - basically he requires that the administrator be able to change passwords of other users and that the password change page be different than the page to edit profile details. I've set up custom actions to handle this namely my own change_password action in users controller.
Users Controller Actions
def change_password
#user = User.find(params[:id])
end
def update_password # I post to this
#user = User.find(params[:id])
if #user.update_attributes!(params[:user])
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
Heres the routes.rb entries
devise_for :users, :skip => [:registrations]
as :user do
get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
put 'users' => 'devise/registrations#update', :as => 'user_registration'
end
resources :users
...
match "/users/:id/change_password" =>"users#change_password", :as=>:change_password_user, :via=>:get
match "/users/:id/update_password" => "users#update_password", :as=>:update_password_user, :via=>:post
And this is my users model
class User < ActiveRecord::Base
rolify
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable, :registerable,
devise :database_authenticatable, #:registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :role_ids, :as => :admin
attr_protected :username, :name, :email, :password, :password_confirmation, :remember_me
validates_uniqueness_of :username
validates_presence_of :username, :email
validates_uniqueness_of :email
end
however I keep getting this mass attributes assignment error
Can't mass-assign protected attributes: password, password_confirmation
the weird thing is that I've set all these attributes to accessible_protected. I can edit other users details but can't edit their passwords. Whats going on here?
There are many ways you can fix this problem. I'll try to explain a few.
I think the key to your problem is that you are mixing up the MassAssignmentSecurity roles. You've defined a Whitelist for the admin role and a Blacklist for the default role. The error says that you tried to assign something that was on the Blacklist for the default role.
Since you are defining different roles, I assume you probably want to fix it this way:
Change your admin Whitelist
attr_accessible :role_ids, :password, :password_confirmation, as: :admin
Then assign as the admin:
if #user.update_attributes!(params[:user], as: :admin)
(If your controller action includes fields other than the password fields, this may cause new violations.)
A different option is to stick to the default role. You can bypass security a couple ways.
The first option which I don't recommend is to not pass the password and password confirmation as part of the User params, and send them separately in your view. You can then manually set those fields like so:
#user.assign_attributes(params[:user])
#user.password = params[:password]
#user.password_confirmation = params[:password_confirmation]
if #user.save!
However, it's even easier to do the following to just skip protection:
#user.assign_attributes(params[:user], without_protection: true)
if #user.save!
For more information, this guide is fairly good:
http://guides.rubyonrails.org/security.html#mass-assignment
I hope that helps.

Devise's current_user only works for admins

I've got a standard User model, it has in it an admin boolean. All's well and good for users that have that set to true, but for normal users I get this error:
undefined local variable or method `current_user'
app/models/doc.rb:18:in `mine'
app/controllers/docs_controller.rb:9:in `index'
The Doc model on line 18 reads like this:
def self.mine
where(:user_id => current_user.name, :retired => "active").order('created_at DESC')
end
My User model looks like this:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
attr_accessor :current_password
attr_accessible :name, :password, :password_confirmation, :current_password, :email, :remember_me, :admin
end
class Ability
include CanCan::Ability
def initialize(user)
can :manage, :all if user.admin
end
end
And in my application controller I have the following:
class ApplicationController < ActionController::Base
protect_from_forgery
after_filter :user_activity
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_path
end
def admin?
self.admin == true
end
def authenticate_admin
redirect_to :new_user_session_path unless current_user && current_user.admin?
end
private
def user_activity
current_user.try :touch
end
end
I think that's everything relevant. I can't for the life of me figure this out.
the current_user helper is a controller method that is not accessible from a model. You should pass current user in as a parameter from the controller to the model.
def self.mine(current_user)
where(:user_id => current_user.name, :retired => "active").order('created_at DESC')
end
EDIT: Side note
It looks like user_id is a string in your logic. If this is what you are doing, you should reconsider. Setting up a belongs_to and a has_many with rails using identifiers in the database is far more maintainable. Using string ids is unconventional, and its a rabbit hole that ends in very bad places.

Not able to update my foreign key in the new method,using Devise for Users Table

I have Two models Users and Profiles ( Users is from devise ). A user can have many profiles , so i created a migration to add user_id to Profiles.
class Profile < ActiveRecord::Base
belongs_to :user
attr_accessible :description, :name, :product, :image, :price , :user_id
mount_uploader :image, ImageUploader
validates_presence_of :name, :product,:image, :price
end
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
# attr_accessible :title, :body
has_many :profiles
validates_associated :profiles
end
In my profiles controller , i have a new method to create a new profile. I want that when user logs in, he is able to see only his profiles not all.
def new
#profile = current_user.profiles.build
##profile = #user.profile.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #profile }
end
end
Ideally, my user_id column should be updated with build, but my user_id does not get updated and that is why profiles are not displayed . i am using postgresql with pg gem and when i manually add the user_id for that profile , i am able to see the profiles.
Please help to solve this, i am able to figure this out since a long time.Let me know if you want more information to solve this.
Maybe try something like this in your controller:
if current_user.profiles.count == 0
profile = Profile.create
current_user.profiles << profile
end

Trouble with Mass-Assignment Error for Admin User

I was trying to follow the railscasts tutorial that explains how to handle mass-assignment errors and attr_accessible for admins, but since that was a little outdated, I'm trying to follow what's in the rails API dock for 3.2.6 here.
All I want to do is allow the admin user the ability to access the "winning" attribute for the Proposal Model on the Update action.
Here's my Proposal Model showing the current attr_accessible.
class Proposal < ActiveRecord::Base
attr_accessible :email, :email_confirmation, :link, :name, :references, :short_description
belongs_to :idea
Here's my code for the Proposal Controller's Update action.
class ProposalsController < ApplicationController
include ActiveModel::MassAssignmentSecurity
attr_accessible :email, :email_confirmation, :link, :name, :references, :short_description
attr_accessible :email, :email_confirmation, :link, :name, :references, :short_description, :winning, :as => :admin
def update
#idea = Idea.find(params[:idea_id])
#proposal = #idea.proposals.find(params[:id])
if #proposal.update_attributes(proposal_params)
redirect_to idea_proposals_url(#idea)
else
render 'edit'
end
end
protected
def proposal_params
role = current_user.admin ? :admin : :default
sanitize_for_mass_assignment(params[:proposal], role)
end
Check out this Railscast. I had a similar issue with an Admin field Boolean and didn't want any user to circumvent the security by sending a curl post. If the user is an admin then it gave them the ability to access the field, otherwise Mass Assignment would protect the field from being modified.
http://railscasts.com/episodes/237-dynamic-attr-accessible?view=asciicast

Rails/Heroku: undefined method `events' for nil:NilClass

I have a method that works locally on my machine but it fails in Heroku. Heroku logs say:
NoMethodError (undefined method `events' for nil:NilClass)
I have used heroku console for an equivalent of this method and it works so there is data that supports it. The method is:
def index
#events = current_user.school.events
end
I am using Devise which I believe gives me the current_user method. The equivalent, a = User.first.school.events yields the true instance value with data. The User.first yields correct data.
Here are my models:
class School < ActiveRecord::Base
#validates_presence_of :name
has_many :events, :dependent => :destroy
has_many :users, :dependent => :destroy
belongs_to :user
belongs_to :event
def self.fetch_for_name(_name)
school = self.new(:name => _name)
end
end
class User < ActiveRecord::Base
belongs_to :school
rolify
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
end
class Event < ActiveRecord::Base
belongs_to :school
end
It would be like me if I overlooked some simple, basic thing but if I can do this correctly in Heroku console, why would this method break. Another unrelated page works correctly on Heroku.
current_user is the user that is logged in. It doesn't necessarily mean User.first. This should be the same locally as on Heroku. If you're having trouble figuring out what user is logged in you can add this to your application controller
before_filter :debug
def debug
Rails.logger.info("Current User is: #{current_user.inspect}")
end
And then view the output with $ heroku logs --tail It should show you the current value of the current_user. At the end of the day what #thesis said is correct, you have a user that does not have a school associated with it.