Rails: undefined method `primary_key' for ActiveSupport::HashWithIndifferentAccess:Class - ruby-on-rails

I have a polymorphic relationship for my User model. I am using Devise.
When I try to edit the user's details, I get the following error:
undefined method `primary_key' for ActiveSupport::HashWithIndifferentAccess:Class
The data submitted through the form is:
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"Ap0JP2bs/w9J6iI9rZahiKR1K8UEIi7rp33a4OutMbo=",
"user"=>{"email"=>"some_email#yahoo.com",
"rolable"=>{"first_name"=>"Christopher",
"last_name"=>"Columbus",
"city"=>"16"}},
"commit"=>"Update"}
The controller method is:
def update
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
redirect_to edit_user_registration_path, notice: 'Your profile was successfully updated.'
else
redirect_to edit_user_registration_path, error: "Something went wrong, couldn't update your profile!."
end
end
The models:
1. User
class User < ActiveRecord::Base
belongs_to :rolable, :polymorphic => true
# Devise business
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :remote_avatar_url, :avatar, :login, :username, :email, :password, :password_confirmation, :remember_me
# For authenticating with facebook
attr_accessible :provider, :uid, :rolable
devise :omniauthable, :omniauth_providers => [:facebook]
# For authenticating with both username or email
attr_accessor :login
# Upload avatar
mount_uploader :avatar, AvatarUploader
# Makes username necessary
validates :username, :presence => true
end
2. Customer
class Customer < ActiveRecord::Base
has_one :user, :as => :rolable
has_one :preferences, :class_name => "CustomerPreferences"
belongs_to :city
attr_accessible :first_name, :last_name
end
What's the problem?

Based on your request hash, you are passing the rolable attribute as:
"rolable"=>{"first_name"=>"Cristian",
"last_name"=>"Gavrila",
"city"=>"16"}
You can't do this unless you specify that the User model accepts nested attributes for rolable. However, you have setup your User as belonging to a Rolable rather than the other way around. Nested attributes aren't designed to handle this inverse relationship - you may want to reconsider what you are trying to accomplish here, and modify the relationship accordingly.
For instance, you may want to turn your hash inside out and pass the rolable attributes with the user attributes embedded within. Or you may want to turn rolable into a has_one association.

Related

profile model for devise users on separate form

I have my devise users linked to a profile model with has_one :profile I would like to keep the initial user form very simple, with the standard username email and password. I would like then the users to be prompted the profile edit form at the first login, and I would like them to be forced to fill in some data.
at the moment my profile model is :
class Profile < ActiveRecord::Base
attr_accessible :time_zone, :telephone, :country, :opt_out,
:first_name, :last_name, :address, :city, :postcode, :birthdate,
:currency_id
belongs_to :currency
validates_presence_of :telephone, :country, :first_name, :last_name,
:address, :city, :postcode, :birthdate, :currency
belongs_to :user
end
my User model is:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
before_create :initialize_user
before_destroy :destroy_profile
has_one :profile
has_one :subscription
attr_accessible :email, :password, :password_confirmation, :remember_me,
:username, :terms
validates_acceptance_of :terms
validates_presence_of :username
private
def initialize_user
generate_profile
generate_free_subscription
end
def generate_free_subscription
subscription = Subscription.new() do |s|
s.expiration_date = nil
s.plan = :free
s.billing_name = username
s.billing_street = "unknown"
s.billing_zip = "unknown"
s.billing_city = "unknown"
s.billing_country = "unknown"
s.billing_email = email
end
if subscription.save
self.subscription = subscription
self.roles = [:free]
else
msg = "Error generating free subscription for user, #{subscription.errors.to_yaml}"
logger.error msg
raise msg
end
end
def generate_profile
p = Profile.new() do |p|
p.daily_capital_exposure = 50
p.risk_per_day = 60
p.risk_per_trade = 30
p.risk_per_week = 90
p.user_id = self.id
p.time_zone = "Rome"
end
if p.save
self.profile = p
else
msg = "Error generating profile for user #{p.errors}"
logger.error msg
raise msg
end
end
def destroy_profile
p = self.profile
t = self.trades
p.destroy
t.destroy_all
end
end
My problem is that when I create a User, the callback also creates its profile, which is missing some data and so fails creation of profile.
I wouldn't like to insert in profile temporary data just to make the profile validate correctly, because I would really like to have a nice way to force users to insert such information.
I guess my error is that I shouldn't be creating the profile at the time I create the User, but I'm not sure how else to make sure the Profile is created.
Try something like this to create a default profile in the beginning :
class User < ActiveRecord::Base
rolify
searchkick autocomplete: [:fullname]
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile
before_create :build_default_profile
private
def build_default_profile
# build default profile instance. Will use default params.
# The foreign key to the owning User model is set automatically
build_profile
true # Always return true in callbacks as the normal 'continue' state
# Assumes that the default_profile can **always** be created.
# or
# Check the validation of the profile. If it is not valid, then
# return false from the callback. Best to use a before_validation
# if doing this. View code should check the errors of the child.
# Or add the child's errors to the User model's error array of the :base
# error item
end
end
This will create a profile when you create the user.
Also if you want to take the fullname during the registration itself, I do something like this :
#application_controller
before_action :configure_permitted_parameters, if: :devise_controller?
private
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:fullname, :email, :password, :password_confirmation) }
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:avatar, :fullname, :email, :password, :password_confirmation, :current_password) }
end
This way, you should be able to take the firstname during the registration and the then create the profile and then after the user logs in you can redirect it to the profile creation page where the user can be asked to fill in the other details.
Hope I could help.
You could just use the on: option in your Profile validations:
#app/models/profile.rb
Class Profile < ActiveRecord::Base
validates_presence_of :telephone, :country, :first_name, :last_name,
:address, :city, :postcode, :birthdate, :currency, on: :update #-> means this will not fire on create
end
--
In terms of building your Profile model on creation of a User, we use the following setup:
#app/models/user.rb
Class User < ActiveRecord::Base
before_create :build_profile
end
This creates a profile for the User model upon creation of that parent model

Getting NoMethodError in Statuses#show

i'm building a social media website with Ruby on Rails and i'm following instructions from teamtreehouse.com . Every time i make a new status or go to the index page i get this message
undefined method `full_name' for nil:NilClass
<p>
<strong>Name:</strong>
<%= #status.user.full_name %>
</p>
I coded my pages to show the full name with this
<%= #status.user.full_name %>
and
<%= status.user.full_name %>
for the index page.
I have included all of these snippets of code into my project as well
status.rb
class Status < ActiveRecord::Base
attr_accessible :content, :user_id
belongs_to :user
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :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,
:first_name, :last_name, :profile_name
# attr_accessible :title, :body
def full_name
first_name + " " + last_name
end
end
I've tried using rake db:reset and rake db:migrate but nothing seems to solve the error problem. Thank you for your help!
As per the error,
undefined method `full_name' for nil:NilClass
which means that
#status.user = nil
i.e., the particular status instance that you are looking at(#status) doesn't have an associated user record.
You can verify it by going to rails console
Status.find(pass_the_id) ## pass the id of #status instance
You will notice that user_id is nil for that record.
Set the value to an existing user_id and try again.
Note: you might need to take a deeper look at how you are storing a status in your application. I suppose you are missing to store user_id attribute there
Looks like you'd need to include has_many :statuses in the User model
You may also wish to use the .delegate() method:
class Status < ActiveRecord::Base
attr_accessible :content, :user_id
belongs_to :user
delegate :full_name, to :user, prefix: :author
end
This should allow you to call #status.author_full_name

Creating an empty profile on devise registration

I have used devise for authentication in a rails app and I want a user to be able to sign up and once signed up be able to edit their own profile. I've followed along with other answers on stack but when I try and register using the default devise registration form I'm getting this error.
NoMethodError in Devise::RegistrationsController#create
undefined method `create' for nil:NilClass
app/models/user.rb:17:in `create_profile'
My User.rb is as follows
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile
attr_accessible :login, :username, :email, :password, :password_confirmation, :remember_me, :profile_attributes
attr_accessor :login
accepts_nested_attributes_for :profile
validates :username, :uniqueness => { :case_sensitive => false }, :presence => true
after_create :create_profile
def create_profile
self.profile.create
end
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["username = :value OR lower(email) = lower(:value)", { :value => login }]).first
else
where(conditions).first
end
end
end
Profile.rb is
class Profile < ActiveRecord::Base
attr_accessible :fax, :phone_1, :phone_2, :url
belongs_to :user
end
profiles controller is the norm apart from the edit action
def edit
#profile = current_user.profile
end
You need to use self.create_profile instead of self.profile.create for has_one association.
Example
An Account class declares has_one :beneficiary, which will add:
Account#beneficiary (similar to Beneficiary.where(account_id: id).first)
Account#beneficiary=(beneficiary) (similar to beneficiary.account_id = account.id; beneficiary.save)
Account#build_beneficiary (similar to Beneficiary.new("account_id" => id))
Account#create_beneficiary (similar to b = Beneficiary.new("account_id" => id); b.save; b)
Account#create_beneficiary! (similar to b = Beneficiary.new("account_id" => id); b.save!; b)
More docs here

Not able to use new model with devise

I have 2 models in my application ( Using rails 3.2.5)
1) User(From devise)
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
end
2) Profiles.
class Profile < ActiveRecord::Base
belongs_to :user
attr_accessible :description, :name, :product, :image, :price
mount_uploader :image, ImageUploader
validates_presence_of :name, :product, :image, :price
end
There are many profiles of one user .
GOAL : I want to display only the profiles related to each user , when they login.
By reading rails document we can create a new profile for a user by (Code below), But this gives an error
"undefined method `Profile' for nil:NilClass"
Can somebody help me to define my controller's index,show,new,edit,create,update ,destroy or show me one and i can do it for all.
def new
#profile = #user.Profile.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #profile }
end
end
You are receiving this error because the #user is not initialized and is equal to nil so it has no method named profile, you should be using current_user helper of devise to get the current user #user = current_user.

Using a model in the controller for a different model?

I have a User model (generated by devise) and a Submission model in a Rails project. I've added a field called 'full_name' to the user model. Also, there is a field for 'user_id' in the submission model.
I want to show the the full_name of the user associated with the submission on the submission show page. Right now it shows the user_id from the submission model just fine.
The User model I have this:
class User < ActiveRecord::Base
has_many :submissions
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
attr_accessible :email, :password, :password_confirmation, :remember_me, :full_name, :role_id
validates_presence_of :full_name
end
This is the model for submissions:
class Submission < ActiveRecord::Base
belongs_to :user
attr_accessible :description, :title, :user_id
end
This is in the controller:
def show
#submission = Submission.find(params[:id])
#user = User.find(params[#submission.user_id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #submission }
end
end
What I get when I try to use this line in the view:
<%= #user.full_name %>
I get this error:
Couldn't find User without an ID
I've tried several variations and can't quite figure out what should be in the place of:
#user = User.find(params[#submission.user_id])
Assuming that you're populating the data correctly, you only need the following once you have looked up the submission.
#submission.user
If you think that the data is OK, then try the following in the Rails console.
> Submission.first.user
What do you see there?
The specific error that you are seeing is because this:
params[#submission.user_id]
is unlikely to ever have anything in it. If the user ID is "1" (for example) then you're asking for the value in the params hash that corresponds to the key "1".

Resources