Rails 3.1 - find_or_create_by Not Saving - ruby-on-rails

I have this code ...
recipient = User.find_or_create_by_email(params[:recipient_email],
{ :password => 'password',
:password_confirmation => 'password',
:first_name => 'First',
:last_name => 'Last',
:active => false })
which doesn't work. The recipient isn't saved in the database as it should be. However this ...
recipient = User.find_or_create_by_email(params[:recipient_email],
{ :password => 'password',
:password_confirmation => 'password',
:first_name => 'First',
:last_name => 'Last'})
does work in that it creates the recipient and saves it in the database, but the :active flag is now set to the default true.
In the User model I have ...
attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :active
and
validates :active, :presence => true
Any ideas as to what's going on here?

Your :active is either true or false (or nil unless you defined a default value in your migration file). Maybe false is interpreted as non-present, I don't really know. BUT this is how I would do this:
Put t.boolean :active, :default => false in your file (or if you need to create a migration, do change_column :table, :active, :boolean, :default => false) and then remove your validation.
Every user is now saved as false unless you provide a param with a different value. If most of your users should be true (so the whole thingm, but vice-versa), then change it so. This makes your validation needless.
Hope I got the point of your problem.. otherwise, ignore me.

Related

Postgres + Rails: How to validate while migrating to DB

So I have a userMigration file which creates the rows in the table manually. This called in the initialization stage. Also, on that particular table I have validations on how the user's details should be (length, uniqueness etc).
Migration.rb
Models::Persistence::User.create({:email => 'testuser#cs.com',
:username => 'testuser#cs.com',
:first => '',
:last => 'User',
:password => 'Test123!!',
:bio => 'User, student',
# :user_id => 3,
# :photoURL => "http://link",
:created_by => 1})
My models file for users:
module Persistence
# Models persistent User data
class User < ActiveRecord::Base
include Songkick::OAuth2::Model::ResourceOwner
validates :bio, length: {maximum: 500}
validates_uniqueness_of :email
validates :email, :presence => true, :allow_nil => false
validates_format_of :email,:with => /\A[^#\s]+#([^#\s]+\.)+[^#\s]+\z/
validates :first, :presence => true, :allow_nil => false, length: {maximum: 100}
validates_format_of :first, :with => /[a-zA-Z]/
When I change the userMigration data and put in values which do not meet the validation criteria, the rows with that data is still created. (The first name in nil in the migration file but the row is still created in the db.) Is there a way to make print out an error at this stage?
Try using the bang version: create! - it throws an error if the record is invalid.
http://guides.rubyonrails.org/active_record_validations.html#when-does-validation-happen-questionmark
Rails 3 - DB seed data validation

How to automatically update id and created_at columns for a model that wasn't originally an Active Record model?

I want to start persisting data that was (intentionally) not being persisted before.
The model originally looked like this:
class Inquiry
include ActiveAttr::Model
include ActiveAttr::MassAssignment
attribute :name
attribute :email
attribute :phone
attribute :company
attribute :content
attr_accessor :name, :email, :phone, :company, :content
validates :name, :presence => true
validates :email, :presence => true, :format => {:with => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i }
validates :phone, allow_blank: true, :format => {:with => /^\+?([\d]{10,13})?$/}
validates :content, :presence => true
end
I ran a migration to create an inquiries table and changed the model file to look like this:
class Inquiry < ActiveRecord::Base
attr_accessible :name, :email, :phone, :company, :content
validates :name, :presence => true
validates :email, :presence => true, :format => {:with => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i }
validates :phone, allow_blank: true, :format => {:with => /^\+?([\d]{10,13})?$/}
validates :content, :presence => true
end
I realized my models didn't have a created_at or updated_at field so I ran a migration and placed 'change_table(:inquiries) { |t| t.timestamps }' in the change method.
Now when I run my specs I get the following error:
Can't mass-assign protected attributes: id, created_at, updated_at
I would like to remedy this problem by modifying what already exists rather than deleting the model, dropping the table, and Rails generating a new model. I know I can pass id, created_at, and updated_at into attr_accessible but I would like to preserve Active Record's default behavior of handling their assignment automatically and preventing mass assignment.

Rails seed.rb as admin

I've rolled out my own authentication/authorization system based on Hartl's for my app. I wanted to allow admins to make other users admins, so I did this in my user.rb file:
attr_accessible :name, :email, :password, :password_confirmation, :order_id
attr_accessible :name, :email, :password, :password_confirmation, :order_id, :admin, :as => :administrator
and put this in my user update action:
def update
if current_user.admin?
if #user.update_attributes(params[:user], :as => :administrator)
This works great for me, but it's getting annoying to have to go into console and type
User.find(2).toggle!(:admin)
or whatever, whenever I want to make my first admin user after a db reset, or, for that matter, to have to use the console or individual edits to make other admins. I'd love it if I could seed ":as => administrator", so I tried this in my seed.rb file, but it doesn't work (mass-assign error):
admin = User.create(
:name => "My Name",
:email => "my email",
:password => "password",
:password_confirmation => "password",
:admin => true,
:as => :administrator
)
Any idea if there's a way to do this? It'd make my life a lot easier.
The simplest solution I found was to toggle admin in the seeds.rb file right after creating the user. This way, I avoid "mass" assignment without having to assign in the console. So:
admin = User.create(
:name => "My Name",
:email => "my email",
:password => "password",
:password_confirmation => "password"
)
admin.toggle!(:admin)
# I assume "admin.update_attribute(:admin, true)" would work as well.
Since you have a mass-assign error, I think you should only keep the second line of attr_accessible in User.rb and discard the first line, which is causing the error.
I was looking to perform the same thing and end up doing like this in seeds.rb:
# db/seeds.rb
users = User.create({email: 'email#admin.com', username: 'admin', password: 'sEcReT', password_confirmation: 'sEcReT', role: 'admin'},
:as => :admin)
# models/user.rb
attr_accessible :email, :username, :password, :password_confirmation, :role, :as => :admin

attr_accessor and password validation on update

I have this code in my user model:
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
attr_accessor :password
before_save :encrypt_password
validates :email, :presence => true,
:uniqueness => { :case_sensitive => false },
:format => { :with => /\A[^#]+#[^#]+\z/ },
:length => 7..128
validates :password, :presence => true,
:confirmation => true,
:length => 6..128
private
def encrypt_password
return unless password
self.encrypted_password = BCrypt::Password.create(password)
end
end
Now in my controller when I'm updating some user fields with
#user.update_attributes(params[:user])
the password field is always validated, even when it is not set in the params hash. I figured that this is happening because of the attr_accesor :password which always sets password = "" on update_attributes.
Now I could simply skip the validation of password if it is an empty string:
validates :password, :presence => true,
:confirmation => true,
:length => 6..128,
:if => "password.present?"
But this doesn't work because it allows a user to set an empty password.
Using update_attribute on the field I'd like to change is not a solution because i need validation on that attribute.
If I pass in the exact parameter with
#user.update_attributes(params[:user][:fieldname])
it doesn't solve the problem because it also triggers password validation.
Isn't there a way to prevent attr_accesor :password from always setting password = "" on update?
New answer
This works for me:
validates :password, :presence => true,
:confirmation => true,
:length => { :minimum => 6 },
:if => :password # only validate if password changed!
If I remember correctly it also took me some time to get this right (a lot of trial and error). I never had the time to find out exactly why this works (in contrast to :if => "password.present?").
Old answer - not really useful for your purpose (see comments)
I get around this problem by using a completely different action for password update (user#update_password). Now it is sufficient to only validate the password field
:on => [:create, :update_password]
(and also only make it accessible to those actions).
Here some more details:
in your routes:
resources :users do
member do
GET :edit_password # for the user#edit_password action
PUT :update_password # for the user#update_passwor action
end
end
in your UsersController:
def edit_password
# could be same content as #edit action, e.g.
#user = User.find(params[:id])
end
def update_password
# code to update password (and only password) here
end
In your edit_password view, you now have a form for only updating the password, very similar to your form in the edit view, but with :method => :put and :url => edit_password_user_path(#user)
The solution I have started using to get round this problem is this:
Start using ActiveModel's built in has_secure_password method.
At console
rails g migration add_password_digest_to_users password_digest:string
rake db:migrate
In your model:
class User < ActiveRecord::Base
has_secure_password
attr_accessible :login_name, :password, :password_confirmation
# secure_password.rb already checks for presence of :password_digest
# so we can assume that a password is present if that validation passes
# and thus, we don't need to explicitly check for presence of password
validates :password,
:length => { :minimum => 6 }, :if => :password_digest_changed?
# secure_password.rb also checks for confirmation of :password
# but we also have to check for presence of :password_confirmation
validates :password_confirmation,
:presence=>true, :if => :password_digest_changed?
end
And finally,
# In `config/locales/en.yml` make sure that errors on
# the password_digest field refer to "Password" as it's more human friendly
en:
hello: "Hello world"
activerecord:
attributes:
user:
password_digest: "Password"
Oh, one more thing: watch the railscast

How to avoid the validation, callbacks and 'attr_accessible' effects during the seeding process using Ruby on Rails 3?

I am using Ruby on Rails 3 and I am trying to seed data in my application database.
In 'RAILS_ROOT/models/user.rb' I have:
class User < ActiveRecord::Base
attr_accessible #none
validates :name,
:presence => true
validates :surname,
:presence => true
validates :email,
:presence => true
end
In 'RAILS_ROOT/db/seeds.rb' I have:
# Test 1
User.find_or_create_by_email (
:name => "Test1 name",
:surname => "Test1 surname",
:email => "test1#test1.test1"
)
# Test2
User.find_or_create_by_email (
:name => "",
:surname => "",
:email => "test2#test2.test2"
)
So, running in the Terminal
rake db:seed
of course the database will NOT populate with datas because 'attr_accessible' to nil (Case Test1) and validation not passed (Case Test2).
I would like to skip the validation and "attr-accessible effects" during the seeding process. Is it possible? If so, how to do that?
P.S.: I don't want to use in 'RAILS_ROOT/db/migrate/201....rb' code like this:
execute "INSERT INTO users ( name, surname, email ) VALUES ( "Test1 name", "Test1 surname", "test1#test1.test1")"
UPDATE
I need also to skip all callbacks. Is it possible? If so, how?
If you check ActiveRecord's documentation you'll see the attributes= method has a parameter to enable this:
attributes=(new_attributes, guard_protected_attributes = true)
Use it like this:
# Create a new user
#user = User.new
# Attributes for the user
#attrib = {
:name => "Test1 name",
:surname => "Test1 surname",
:email => "test1#test1.test1"
}
# Use 'send' to call the attributes= method on the object
#user.send :attributes=, #attrib, false
# Save the object
#user.save

Resources