Why is user.save true but email shows as nil? - ruby-on-rails-3

I'm using a nested model form for sign-up and am working through the kinks as a beginner. One issue that popped up in particular though that I don't really get is user.email is returning as nil.
Before I started playing around with the nested model form, I could create records in the console wihtout a problem. Now, however I can't create records and some of the latest records created have nil as their email. (I'm not sure if it has anything to do with the nested model at all, but that's my reference point for when it started going haywire.)
If I go into rails console to create a new User/Profile, I follow this process:
user = User.new
user.email = ""
user.password = ""
user.profile = Profile.new
user.profile.first_name = ""
...
user.profile.save
user.save
Everything goes well until user.save, which gives me the NameError: undefined local variable or method 'params' for #<User:>. In rails console it pinpoints to user.rb:25 in create_profile
So here is my User model:
class User < ActiveRecord::Base
attr_accessor :password, :email
has_one :profile, :dependent => :destroy
accepts_nested_attributes_for :profile
validates :email, :uniqueness => true,
:length => { :within => 5..50 },
:format => { :with => /^[^#][\w.-]+#[\w.-]+[.][a-z]{2,4}$/i }
validates :password, :confirmation => true,
:length => { :within 4..20 },
:presence => true,
:if => :password_required?
before_save :encrypt_new_password
after_save :create_profile
def self.authenticate(email, password)
user = find_by_email(email)
return user if user && user.authenticated?(password)
end
def authenticated?(password)
self.hashed_password == encrypt(password
end
protected
def encrypt_new_password
return if password.blank?
self.hashed_password = encrypt(password)
end
def password_required?
hashed_password.blank? || password.present?
end
def encrypt(string)
Digest::SHA1.hexdigest(string)
end
end
Can anyone help me figure out what's going on?
UPDATE: I tried changing my regex but I'm still seeing nil for email. Though a prior SO post said not to blindly copy regex without testing, so maybe I just didn't test it correctly. Good news though: I no longer get the error.

attr_accessor simply defines a "property" on the object and has no relation to the attributes of a ActiveRecord model (attributes is a Hash of the fields and values obtained from a table).
ActiveRecord does not save such "properties" as defined by the attr_accessor. (Essentially, attr_accessor defines a attr_reader and attr_writer (i.e. "getter" and "setter") at the same time)

Related

Activerecord adds additional conditions in query

For example I am doing something like this:
PlatformInformer.where(1)
and result query via
PlatformInformer.where(1).to_sql
is
SELECT `platform_informers`.* FROM `platform_informers` WHERE `platform_informers`.`platform` = 'android' AND `platform_informers`.`email` = 'voldemar#klops.ru' AND (1)
I didn't ask to add email and platform fields in where clause!
This problem causes when I am executing code inside PlatforInformer model methods. Default scope is doesn't set. What is the root of evil?
Rails 3.2.13
UPDATE:
class PlatformInformer < ActiveRecord::Base
include HasUniqueGenerator
attr_accessible :email, :platform,:secret_code,:activated,:invitation_sent
before_create :init_secret_code
PLATFORMS = %w(windows macos android ios)
def self.PLATFORMS
PLATFORMS
end
validates :platform, :presence => true,:inclusion => { :in =>PLATFORMS }
validates :email, :presence => true, :email => true
validates_uniqueness_of :email, scope: :platform
scope :confirmed, Proc.new { where(:activated => true) }
def several_platforms?
PlatformInformer.confirmed.find_all_by_email(self.email).count > 0
end
def send_confirmation
if already_subscribed?
activate!
else
PlatformInformerMailer.inform_me(self.id).deliver
end
end
def activate!
PlatformInformer.where(:email=>self.email).update_all(:activated=>true)
end
private
def init_secret_code
gen_unique_code :secret_code, 16
end
def already_subscribed?
PlatformInformer.confirmed.where(email: self.email).any?
end
end
Problem was in using first_or_create method, that created virtual scope with email and platform attributes.

Rails 3.0 Skip validations for nested attributes

I need to be able to save a record without running validations on itself or its nested attributes. I'm stuck in Rails 3.0, and I cannot update to a newer version.
I have a report, each report has many responses (answers to questions). The responses are nested in the report form.
There are two ways the user should be able to save the report: Submit for review, where all validations are run, and Save And Finish Later, where no validations are run for the report or the nested responses. This needs to work for both create and update actions.
I am currently trying to use conditional validations. This works for update but not create. The problem is this line:
validate :has_answer_if_required, :if => Proc.new { |response| !response.report.finish_later? }
The report doesn't exist yet, so active record can't find this responses's report. That's where it crashes.
There are a lot some suggested solutions for this problem, but I couldn't get them working in Rails 3.0. update_attributes(attributes, :validate => false), for instance, is not available in Rails 3.0.
So, how do I skip the validations in the nested attributes?
class Report < ActiveRecord::Base
has_many :responses, :order => "created_at asc", :autosave => true
accepts_nested_attributes_for :responses
...
end
class Response < ActiveRecord::Base
belongs_to :report
validates_associated :report
validate :has_answer_if_required, :if => Proc.new { |response| !response.report.finish_later? }
validate :correct_answer_or_comment, :if => Proc.new { |response| !response.report.finish_later? }
end
class ReportsController < BaseController
def update
#report = Report.find(params[:id])
#report.attributes = params[:report]
if params[:finish_later].nil?
#report.update_attribute(:finish_later, false)
if #report.save!
redirect_to :action => :index
else
render :template => "reports/edit"
end
else
#report.finish_later = true
#report.save(:validate => false)
redirect_to :action => :index
end
end
def create
#report = Report.new(params[:report])
if params[:finish_later].nil?
#report.finish_later = false
if #report.save!
redirect_to :action => :index
else
render :template => "reports/edit"
end
else
#report.finish_later = true
#report.save!(:validate => false)
redirect_to :action => :index
end
end
end
Not sure if it will work with nested attributes, though I think it should... but give ValidationSkipper a try:
https://github.com/npearson72/validation_skipper
Just make sure you call skip_validation_for on the object you want to skip. Since nested attributes pass behavior to their children, you might be able to call this method directly on the parent object. Give it a try.

Omniauth Facebook auth + identity using the same model instead of two

I've setup Omniauth Facebook authentication according to this tutorial: http://net.tutsplus.com/tutorials/ruby/how-to-use-omniauth-to-authenticate-your-users/
And now I'm trying to combine it with omniauth-identity using the same User model instead of a separate Identity model as in this tutorial: http://railscasts.com/episodes/304-omniauth-identity?view=asciicast , but I cannot get it to work properly.
This is is my initializers/omniauth.rb file:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, 'xxxxx', 'xxxxx'
provider :identity, :fields => [:email], :model => User
end
I've added 'password_digest' column that is needed by omniauth-identity to my User model/table and changed the User model code
from
class User < ActiveRecord::Base
has_many :authorizations
#validates :name, :email, :presence => true
def add_provider(auth_hash)
# check if the provider already exists, so we don't add it twice
unless authorizations.find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"])
Authorization.create :user => self, :provider => auth_hash["provider"], :uid => auth_hash["uid"], :token => auth_hash["token"]
end
end
end
to
class User < OmniAuth::Identity::Models::ActiveRecord
...
end
but when I do that the code in the Authorization model that creates the User and the Authorization models does not work properly
When the User model extends from ActiveRecord::Base the records are created just fine but when I extend the user model from OmniAuth::Identity::Models::ActiveRecord the user model is not stored in the database when you create a new authorization.
This is the Authorization model code:
class Authorization < ActiveRecord::Base
belongs_to :user
validates :provider, :uid, :presence => true
def self.find_or_create(auth_hash)
unless auth = find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"])
user = User.create :name => auth_hash["info"]["name"], :email => auth_hash["info"]["email"]
auth = create :user => user, :provider => auth_hash["provider"], :uid => auth_hash["uid"], :token => auth_hash["credentials"]["token"]
end
auth
end
end
When I extend the User model from ActiveRecord::Base and try to create a new registration with Identity I get this error:
ActiveRecord::UnknownAttributeError
unknown attribute: password
Is there any way to get this working this way? I don't know what to do now.
not sure you're still having the problem, but maybe someone on the interwebz will.
I just posted a solution on by blog, should solve your problems:
http://bernardi.me/2012/09/using-multiple-omniauth-providers-with-omniauth-identity-on-the-main-user-model/
try to add attr_accessor :password and may be attr_accessor :email

Rails validation error email is too short, email is invalid

In attempting to seed my database I ran into a validation error on my User model's email attribute. The error:
Validation failed: Email is too short (minimum is 5 characters), Email is invalid
The thing is, my email is xxxxxxxx#gmail.com. I have five characters. Sorry for the beginner question but I don't know what is going on. I recently followed Railscasts to reset a User's password, and enable CanCan. I'm not sure if CanCan would affect anything, but prior to exploring that new functionality I've been able to fully seed my database without problems. I've pasted in some of my code below. I'm running Rails 3.0.5 and Ruby 1.9.2.
An example of how I create a User in my seed file:
me = User.create(:email => 'me#gmail.com', :password => 'test', :profile => my_profile)
User.rb model:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :password, :password_confirmation
before_save :encrypt_new_password
before_create { generate_token(:auth_token) }
before_validation :downcase_email
has_one :profile, :dependent => :destroy
accepts_nested_attributes_for :profile
validates :email, :uniqueness => true,
:length => { :within => 5..50 },
:format => { :with => /^[^#][\w.-]+#[\w.-]+[.][a-z]{2,4}$/i }
validates :password, :confirmation => true,
:length => { :within => 4..20 },
:presence => true,
:if => :password_required?
Add :email to attr_accessible to allow mass assignment on it. Without that the email field will not even be set so validation will fail.

Rails: awesome_nested_set issues

I am using the awesome_nested_set to do a simple drag and drop reordering of news items and the post happens but the position field in my DB is not updated...
Here is my model:
class NewsItem < ActiveRecord::Base
translates :title, :body, :external_url
attr_accessor :locale, :position # to hold temporarily
alias_attribute :content, :body
validates :title, :content, :publish_date, :presence => true
has_friendly_id :title, :use_slug => true
acts_as_indexed :fields => [:title, :body]
acts_as_nested_set
default_scope :order => "publish_date DESC"
# If you're using a named scope that includes a changing variable you need to wrap it in a lambda
# This avoids the query being cached thus becoming unaffected by changes (i.e. Time.now is constant)
scope :not_expired, lambda {
news_items = Arel::Table.new(NewsItem.table_name)
where(news_items[:expiration_date].eq(nil).or(news_items[:expiration_date].gt(Time.now)))
}
scope :published, lambda {
not_expired.where("publish_date < ?", Time.now)
}
scope :latest, lambda { |*l_params|
published.limit( l_params.first || 10)
}
# rejects any page that has not been translated to the current locale.
scope :translated, lambda {
pages = Arel::Table.new(NewsItem.table_name)
translations = Arel::Table.new(NewsItem.translations_table_name)
includes(:translations).where(
translations[:locale].eq(Globalize.locale)).where(pages[:id].eq(translations[:news_item_id]))
}
def not_published? # has the published date not yet arrived?
publish_date > Time.now
end
# for will_paginate
def self.per_page
20
end
end
Anyone know why this wouldn't work?