Rails Model Attribute - ruby-on-rails

Going a little crazy here. I am doing a very simple lookup of a user by its email and then if I have a user checking its role. quick example.
user = User.find_by_email(params[:email])
if user
if user.role == 'admin'
abort("Yay were an admin")
end
end
The user has a role like in the example but the if statement is not returning true.
If I inspect the user ie,
if user
raise user.to_yaml
end
I get valid attributes ie.
--- !ruby/object:User
attributes:
id: 1
email: user#test.com
created_at: 2011-12-01 08:37:45.000000000Z
updated_at: 2011-12-01 08:37:45.000000000Z
role: admin
Why would the if statement not return true? I know the user has a roel as if I do if user.role it returns true.
Doing the same in rails console gives me the user role.
ruby-1.9.2-p180 :001 > u = User.find_by_email('user#test.com')
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`email` = 'user#test.com' LIMIT 1
=> #<User id: 1, email: "user#test.com", created_at: "2011-12-01 08:37:45", updated_at: "2011-12-01 08:37:45", role: "admin">
ruby-1.9.2-p180 :002 >
But then trying a if statement fails as well
ruby-1.9.2-p180 :008 > if u.role == 'admin'
ruby-1.9.2-p180 :009?> puts 'Yay'
ruby-1.9.2-p180 :010?> end
=> nil
ruby-1.9.2-p180 :011 >
Any ideas ??

According to your comments, the user's role attribute returns a symbol. Therefore, your test should be
if user.role == :admin
abort("Yay were an admin")
end

Possibly the column name role is reserved, depending on what database you are using, which can cause all kinds of strange conflicts.
I had the same problem with my first Rails project using a column called error in sqlite3 - try renaming the role column to project_role or similar and see if that helps.
There was a great resource for checking reserved names at yup.com which has since gone down, here is the only mirror I can find.

Related

Migrate models between different environments in Rails

I am writing a script that will be in charge of transferring data from production db to a new production db.
There is one Rails app connecting to each database. In the new app we have made some migrations that change the schema (remove columns, etc)
I tried to do this:
rails console old
2.0.0p247 :004 > tag = Tag.last.dup
=> #<Tag id: nil, description: " views", account_id: 46, screenshotBase64: "", user_id: 1, created_at: nil, updated_at: nil>
2.0.0p247 :005 > ActiveRecord::Base.establish_connection(:development)
2.0.0p247 :006 > tag.save
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'screenshotBase64' in 'field list
As we removed the screenshotBase64 in the new db, it does not work.
Is there a way to do that with Rails ? Delete attributes from Rails model before save ?
Is there a better way to transfer all data from a user linked through its associations between two databases (changing ids in the new db) ?
Please have a try with this code
tag_attributes = Tag.last.attributes.dup
%w(id screenshotBase64).map{|method| tag_attributes.delete method}
ActiveRecord::Base.establish_connection(:development)
tag = Tag.create(tag_attributes)

Active Record has_many generates sql with foreign key IS NULL

I don't expect a model with NULL as foreign key to belong to anything!
I have the following rails app, modelling ants and ant hills (inspired by Jozef).
$ rails -v
Rails 3.2.8
$ rails new ant_hill
$ cd ant_hill
Create the ant hill and ant models. An ant can belong to an ant hill and an ant hill can have many ants.
$ rails generate model AntHill name:string
$ rails generate model Ant name:string ant_hill_id:integer
$ vim app/models/ant.rb
$ cat app/models/ant.rb
class Ant < ActiveRecord::Base
belongs_to :ant_hill
end
$ vim app/models/ant_hill.rb
$ cat app/models/ant_hill.rb
class AntHill < ActiveRecord::Base
has_many :ants
end
$ rake db:migrate
== CreateAntHills: migrating =================================================
-- create_table(:ant_hills)
-> 0.0013s
== CreateAntHills: migrated (0.0016s) ========================================
== CreateAnts: migrating =====================================================
-- create_table(:ants)
-> 0.0035s
== CreateAnts: migrated (0.0037s) ============================================
Run the following code in a console.
$ rails c
Loading development environment (Rails 3.2.8)
Create a couple of ants, persisted, that don't belong to any ant hill.
1.9.2-p290 :001 > Ant.create! name: "August"
=> #<Ant id: 1, name: "August", ant_hill_id: nil, created_at: "2012-09-27 12:01:06", updated_at: "2012-09-27 12:01:06">
1.9.2-p290 :002 > Ant.create! name: "Bertil"
=> #<Ant id: 2, name: "Bertil", ant_hill_id: nil, created_at: "2012-09-27 12:01:13", updated_at: "2012-09-27 12:01:13">
Now instantiate an ant hill, but don't save it just yet.
1.9.2-p290 :003 > ant_hill = AntHill.new name: "Storkullen"
=> #<AntHill id: nil, name: "Storkullen", created_at: nil, updated_at: nil>
I expect this ant hill to not have any ants and it doesn't.
1.9.2-p290 :004 > ant_hill.ants
=> []
I still expect the ant hill to not have any ants but now it has two.
1.9.2-p290 :005 > ant_hill.ants.count
(0.1ms) SELECT COUNT(*) FROM "ants" WHERE "ants"."ant_hill_id" IS NULL
=> 2
Same here, it should never generate a query containing "IS NULL" when dealing with foreign keys. I mean "belongs_to NULL" can't belong to anything, right?
1.9.2-p290 :006 > ant_hill.ants.all
Ant Load (0.4ms) SELECT "ants".* FROM "ants" WHERE "ants"."ant_hill_id" IS NULL
=> [#<Ant id: 1, name: "August", ant_hill_id: nil, created_at: "2012-09-27 12:01:06", updated_at: "2012-09-27 12:01:06">, #<Ant id: 2, name: "Bertil", ant_hill_id: nil, created_at: "2012-09-27 12:01:13", updated_at: "2012-09-27 12:01:13">]
After it is persisted it behaves as expected.
1.9.2-p290 :007 > ant_hill.save!
=> true
1.9.2-p290 :008 > ant_hill.ants.count
(0.4ms) SELECT COUNT(*) FROM "ants" WHERE "ants"."ant_hill_id" = 1
=> 0
1.9.2-p290 :009 > ant_hill.ants.all
Ant Load (0.4ms) SELECT "ants".* FROM "ants" WHERE "ants"."ant_hill_id" = 1
=> []
Any insight? Is this the expected behavior?
While it seems counterintuitive, I think this behavior makes sense given your examples. Take ant_hill.ants.count for example. Count is an ActiveRecord query method that hits the database, and you're essentially asking ActiveRecord to give you all the ants that do not belong to an ant hill. Rails is simply letting you do something you should not be able to do, and not complaining about it. Should this be raising an exception instead? Possibly.
If you really want to know how many ants belong to this ant_hill object, you should be using size. It queries the object when not persisted or when the association is already loaded, and queries the database otherwise.
ant_hill.ants.size
One way you can get around this oddity is to make ant_hill_id a required field by validating its presence.
TL;DR Avoid using ActiveRecord query interface if parent object is not persisted to database.

Rails boolean values and callbacks to default to false

UPDATE:
If the after_validation callback is used, it works as desired (e.g. the false value is persistent). Still would like to know why that is, but I guess this is solved for my purposes :)
For a boolean field, I would like a callback in the model to set the default value to false instead of nil.
Currently when I create a new record, it initially shows the value as false, but then shows it as nil.
Wondering what's going on here and if the desired behavior is possible w/ a callback.
This is in the model:
after_save :default_is_forsale
def default_is_forsale
self.not_for_sale = false if self.not_for_sale.nil?
end
Here is the rails console output (irrelevant bits omitted):
1.9.3p125 :001 > Item.create(name: "thing 4")
(0.1ms) begin transaction
SQL (6.4ms) INSERT INTO items [...]
(190.8ms) commit transaction
=> #<Item id: 20, name: "thing 4", not_for_sale: false>
Cool, created the new record with a default value of false. But when I check again:
1.9.3p125 :002 > Item.last
Item Load (0.3ms) SELECT [...]
=> #<Item id: 20, name: "thing 4", not_for_sale: nil>
Weird, now the value is nil.
But if I create a new record and explicitly set the value to false, it acts as I'd expect:
1.9.3p125 :003 > Item.create(name: "more thing", not_for_sale: false)
(0.1ms) begin transaction
SQL (0.7ms) INSERT INTO items [...]
(225.2ms) commit transaction
=> #<Item id: 21, name: "more thing", not_for_sale: false>
When retrieved, the record still shows its boolean value of false
1.9.3p125 :004 > Item.last
Item Load (0.3ms) SELECT [...]
=> #<Item id: 21, name: "more thing", not_for_sale: false>
BTW, I read elsewhere that the desired result is achievable via db migrations, but I am new to rails and would like to accomplish it through the model.
Thanks
Change your migration to set this boolean to false, as default. if there was code i'd show you.
I just read you were 'new to rails' but that doesn't matter. You don't need to do it in the model, unless you want that record to be true.
You want this in a before_save callback. The after_save callback is called, unsurprisingly, after the record has already been saved.

password_digest, has_secret_password and form validations in RoR 3.1

If you haven't seen it yet, have a look at the latest railscast authentication in rails 3.1. He uses password_digest:string when generating the model and adds has_secret_password to the User model. He also adds some accessible_attributes, :password and :password_confirmation.
By using validates_confirmation_of :password we can make sure the password is confirmed before creating an instance. If I leave the password fields empty in my form I get this error message:
ruby-1.9.2-p180 :024 > u = User.new
=> #<User id: nil, name: nil, email: nil, password_digest: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p180 :027 > u.save
(0.4ms) SELECT 1 FROM "users" WHERE "users"."email" = '' LIMIT 1
=> false
ruby-1.9.2-p180 :028 > u.errors.full_messages
=> ["Password digest can't be blank"]
Of course we don't want to call the password field "Password digest" when communicating with our users. How do I change this error message?
Bonus question
When using validates_confirmation_of and using mismatching passwords I get two error messages telling me about it, and only the :password label and input tags are surrounded with fields_with_errors divs. If the passwords don't match I want to also highlight the password_confirmation input, if possible remove it altogether from the password part.
Should I write my own method for checking password confirmation? If so, could you provide some small guidelines?
The official way to solve this is to add the following to your locale file:
en:
activerecord:
attributes:
user:
password_digest: 'Password'
The English locale file is located at config/locales/en.yml. You may have to restart your server for the changes to be loaded in your environment.
To get rid of password_digest message, you can modify the view:
<% #user.errors.messages.delete(:password_digest) -%>
You could override the human_attribute_name method and set your own humanized version of the password_digest attribute. Try something like this:
HUMANIZED_ATTRIBUTES = {
:password_digest => "Password"
}
def self.human_attribute_name(attr, options={})
HUMANIZED_ATTRIBUTES[attr.to_sym] || super
end
Then your error should look like this: "Password can't be blank"

Rails: Some seeds don't appear to be saving

It appears some of my seeds are not saving. For starters, I will show a console session so you can see that the 'instructor_id' is indeed being set in the console, but not when I seed.
ruby-1.9.2-p180 :015 > c = Course.find 2
Course Load (1.6ms) SELECT "courses".* FROM "courses" WHERE "courses"."id" = $1 LIMIT 1 [["id", 2]]
=> #<Course id: 2, name: "Microcomputers II Lab", course_code: "CE-420L", instructor_id: nil, school_id: nil, created_at: "2011-06-04 19:40:32", updated_at: "2011-06-04 19:40:32">
ruby-1.9.2-p180 :016 > c.instructor = Instructor.first
Instructor Load (0.6ms) SELECT "instructors".* FROM "instructors" LIMIT 1
=> #<Instructor id: 1, name: "Instructor Name", created_at: "2011-06-04 19:40:32", updated_at: "2011-06-04 19:40:32">
ruby-1.9.2-p180 :017 > c
=> #<Course id: 2, name: "Microcomputers II Lab", course_code: "CE-420L", instructor_id: 1, school_id: nil, created_at: "2011-06-04 19:40:32", updated_at: "2011-06-04 19:40:32">
From looking at the console, you can see that when I call c.instructor = Instructor.first, it is correctly setting my instructor_id.
Now, in the seeds file I have variables. (This is just a snippet)
### Instructors ###
puts "Creating Instructors"
instructor_1 = Instructor.find_or_create_by_name("Instructor Name")
### Courses ###
puts "Creating Courses"
ce420L = Course.find_or_create_by_name("Microcomputers II Lab", :course_code => "CE-420L")
### Add the Instructor to the Course ###
puts "Adding an Instructor to the Courses"
ce420L.instructor = instructor_1
But when I run the seeds using 'rake db:seed', it is correctly creating all of my models, and most of my relationships. But it is not setting the instructor properly.
Thoughts?
EDIT:
Just tried:
ce420 = Course.find_or_initialize_by_name("Microcomputers II")
ce420.instructor_id = instructor_1.id
ce420.save!
And it did not save my instructor.
Here are my models.
class Instructor < ActiveRecord::Base
### ASSOCIATIONS ###
has_many :courses
end
class Course < ActiveRecord::Base
belongs_to :instructor
end
Did you run...
ce420L.save!
... after assigning the instructor?
Much faster to do this:
### Courses ###
puts "Creating Courses belonging to Instructor 1"
ce420L = Course.find_or_initialize_by_name("Microcomputers II Lab") :course_code => "CE-420L")
ce420L.instructor_id = instructor_1.id
ce420L.save
Note the following:
You had an errant comma after your find_or_create on ce420L.
Doing the assignment together with the course creation prevents the system from validating and saving ce420L twice.
You can try assigning the specific ID as I did, i.e. ce420L.instructor_id = ...
If this doesn't work, check your Instructor Model to ensure you don't have any callbacks getting in the way.

Resources