Rails boolean values and callbacks to default to false - ruby-on-rails

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.

Related

Could not find_by an object from database in Rails job

I have created a Ruby on Rails job (extending from ActiveJob::Base) that parses a CSV file and inserts its rows as records (Students) in a database. That is working as expected.
However, when I try to fetch another object from the DB (the Career for each Student, which is part of each CSV row as a pair (career_code, career_name)), I'm getting this error:
undefined method 'find_by' for Career:Class
(I have also tried using Career.where instead).
I find this quite strange, since I'm already saving my Student, which is also an ActiveRecord::Base child class.
Here's the relevant part of my job:
ActiveRecord::Base.transaction do
student_hash.keys.each do |k|
some_student = student_hash[k]
student = Student.new
student.csv_id = some_student.id
student.first_name = some_student.first_name
student.last_name = some_student.last_name
student.file_number = some_student.file_number
# student.career = Career.where(code: some_student.career_code)
student.career = Career.find_by code: some_student.career_code
puts student.save! # Why this works, and the above line doesn't?
end
end
And also, the desired output, as I can see it in the Rails console:
Loading development environment (Rails 4.2.4.rc1)
2.1.3 :001 > Career.where(code: 11)
Career Load (0.5ms) SELECT "careers".* FROM "careers" WHERE "careers"."code" = ? [["code", 11]]
=> #<ActiveRecord::Relation [#<Career id: 4, name: "Informática", created_at: "2015-09-30 22:05:07", updated_at: "2015-09-30 22:05:07", code: 11>]>
2.1.3 :002 > Career.where code: 11
Career Load (0.2ms) SELECT "careers".* FROM "careers" WHERE "careers"."code" = ? [["code", 11]]
=> #<ActiveRecord::Relation [#<Career id: 4, name: "Informática", created_at: "2015-09-30 22:05:07", updated_at: "2015-09-30 22:05:07", code: 11>]>
2.1.3 :003 > Career.find_by code: 11
Career Load (0.4ms) SELECT "careers".* FROM "careers" WHERE "careers"."code" = ? LIMIT 1 [["code", 11]]
=> #<Career id: 4, name: "Informática", created_at: "2015-09-30 22:05:07", updated_at: "2015-09-30 22:05:07", code: 11>
2.1.3 :004 >
Probably this is a really stupid question, but I'm quite a beginner using Rails. Do I need to import some sort of "Context" (this seems unlikely, since Student seems to be properly resolved)? I'm using Rails 4.2.4.rc1 by the way.
Thanks in advance
Any chance at that point in the code Career isn't an AR, but some other class? That would at least explain why the methods don't exist.

Thinking sphinx search doesnt return any result

i'm trying to implement simple search function with thinking sphinx, basing on this. i managed to set sphinx up, but when i try it in a console, 0 results are found every time. i'm wondering what is wrong here...
indices/news_post_index.rb:
ThinkingSphinx::Index.define :news_post, :with => :active_record do
indexes title
indexes content
end
and the rails console output:
2.0.0p0 :013 > NewsPost.create!(title: "awesome post", content: "this post is awesome and you know it", published_at: "2001-01-01")
(0.3ms) BEGIN
NewsPost Exists (60.7ms) SELECT 1 AS one FROM `news_posts` WHERE `news_posts`.`slug` = 'awesome-post' LIMIT 1
SQL (48.3ms) INSERT INTO `news_posts` (`content`, `created_at`, `published_at`, `slug`, `title`, `updated_at`) VALUES ('this post is awesome and you know it', '2013-09-10 08:18:35', '2001-01-01', 'awesome-post', 'awesome post', '2013-09-10 08:18:35')
(40.1ms) COMMIT
=> #<NewsPost id: 2, published_at: "2001-01-01", content: "this post is awesome and you know it", title: "awesome post", created_at: "2013-09-10 08:18:35", updated_at: "2013-09-10 08:18:35", slug: "awesome-post">
2.0.0p0 :014 > NewsPost.search "awesome post"
Sphinx Query (0.9ms) SELECT * FROM `news_post_core` WHERE MATCH('awesome post #sphinx_internal_class_name (NewsPost)') AND `sphinx_deleted` = 0 LIMIT 0, 20
Sphinx Found 0 results
=> []
From what i understand title is indexed, and so I should be able to search for it. but no matter what my key is, results are always empty array, no results at all...
Run rake ts:index after every update, then search.

Why does rails change the type of the attribute _was value after it is set?

I have an attribute in my model that is stored as text but interpreted as a rational. I have this method to handle that:
def start
read_attribute(:start).to_r
end
When I set the start attribute to a new value, the start_was helper method returns a string, instead of a rational, but before I do so, it returns the correct value. Why?
Loading development environment (Rails 3.2.8)
1.9.3p194 :001 > d = Day.find(55)
Day Load (8.7ms) SELECT "days".* FROM "days" WHERE "days"."id" = ? LIMIT 1 [["id", 55]]
=> #<Day id: 55, date: "2012-03-30", start: "1/2", finish: "2/2", created_at: "2012-09-18 15:16:42", updated_at: "2012-09-19 08:20:41", day_year_id: 1>
1.9.3p194 :002 > d.start_was
=> (1/2)
1.9.3p194 :003 > d.start=0
=> 0
1.9.3p194 :004 > d.start_was
=> "1/2"
I think the reason is this method in ActiveModel (activemodel-3.2.8\lib\active_model\dirty.rb)
# Handle <tt>*_was</tt> for +method_missing+.
def attribute_was(attr)
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
end
As you see, if attribute was not actually changed it just calls its own getter, in your case hitting your start method which does the transformation. But if the attribute is actually changed, it reads its raw value from the changed_attributes storage.

Nested objects and collection size

I run into a weird issue since I upgraded to Rails 3.2.
My application have some objects managed with nested_forms, but when i try to reach a collection's size after having built a new nested object, the nested object is taken in count.
For ex.:
1.9.3p0 :004 > e = Expense.last
Expense Load (22.6ms) (...)
=> #<Expense id: 1, (...)
1.9.3p0 :005 > e.comments.size
(0.3ms) SELECT COUNT(*) (...)
=> 0
1.9.3p0 :006 > e.comments.build
=> #<Comment id: nil, content: nil, commentable_id: 1, commentable_type: "Expense", created_at: nil, updated_at: nil, creator_id: nil>
1.9.3p0 :007 > e.comments.size
=> 1
In this case, I expect the e.expense.size to return 0 since the Commentobject has not been saved yet.
Building the comment creates ... 1 comment. It doesn't exist in the database (yet) so e.commment.count would return 0, but it exists in the application, so size returns 1. So that sort of makes sense.
But I do see there's a little potentially unexpected result here. If a comment doesn't exist in memory, it looks like Rails checks with the DB (the SELECT COUNT(*)...), whereas if it finds one in memory it does not.
I wonder what would happen if you had several existing comments already, then checked e.comments.size in that case?
Which version of Rails were you coming from that worked differently?

Rails Model Attribute

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.

Resources