Rails 3 Serialization issue - ruby-on-rails

I have an application that was working fine with ror 2.3.x. I am having trouble upgrading to Rails 3 with serialization.
The code looks like this
class PaymentTransaction < ActiveRecord::Base
serialize :response
end
The response is supposed to contain the ActiveMerchant::Billing::Response. With rails 3 for some reason its being save as string.
=> #<PaymentTransaction id: 11, order_id: nil, amount: nil, mode: nil, payment_profile_id: nil, response: "#<ActiveMerchant::Billing::Response:0x1051aec98>", created_at: "2010-11-07 04:06:03", updated_at: "2010-11-07 04:24:58", result: "pending", payee: nil, login_id: nil, transaction_key: nil>
I didn't any notes on serialization in any other blogs talking about upgrade. Any thoughts?

The Rails 2 explanations for using serialization did not work in Rails 3 for me unless I also specified the type of the serialized object in the serialize call. For example:
serialize :response, Array
After specifying array the functionality worked as expected.
Further documentation here:
http://api.rubyonrails.org/classes/ActiveRecord/Base.html
under "Saving Arrays [...]"

There was a small change in rails 3 which have an effect: https://github.com/rails/rails/commit/c1d73270717f30498f8f4d55d6695509107c2834
There are two good blog posts about serialization here:
http://www.simonecarletti.com/blog/2010/04/inside-ruby-on-rails-serializing-ruby-objects-with-json/
http://www.skorks.com/2010/04/serializing-and-deserializing-objects-with-ruby/

Related

Rails: ActionText has_rich_text returns nil

I've added ActionText into my Rails 5.2 app, according to this tutorial. I performed installation, migration and added action_text_rich_texts column. I also updated my model:
class LiveEvent < ApplicationRecord
has_rich_text :description_long
end
However has_rich_text helper seems to not working. When I try to initialize new record this way:
#live_event = LiveEvent.new(live_event_params)
description_long attribute returns nil because of this helper. Which crashes my app due to the validation constrains.
Strong param permission for description_long it's also not a case since that attribute was permitted before. This error occurs even if I want to add new record directly through the Rails console:
le = LiveEvent.new(description_long: 'test')
le[:description_long] // returns nil
Maybe there is no established binding between action_text_rich_texts and my LiveEvent model? I'm not sure what it the possible cause of this error. How can I fix it?
ActionText is providing polymorphic association with Model we mention has_rich_text.
So when we define has_rich_text is actually we are defining an association, like we do has_one, 'has_many', belongs_to.
So when you write
#live_event = LiveEvent.new(description_long: 'test')
It will create a new instance of ActionText::RichText model and assign the "text" in the body column as instance of ActionText::Content. So what ever value we assigned to description_long as rich text will automatically wrapped into into a div tag <div class="trix-content">.
Here is the example.
pry(main)> e = Email.new(content: "Asd")
=> #<Email:0x00007fd612746018
id: nil,
user_id: nil,
subject: nil,
created_at: nil,
updated_at: nil>
pry(main)> e.content
=> #<ActionText::RichText:0x00007fd612745c80
id: nil,
name: "content",
body: #<ActionText::Content "<div class=\"trix-conte...">,
record_type: "Email",
record_id: nil,
created_at: nil,
updated_at: nil>
pry(main)> e[:content]
=> nil
pry(main)> e.content.body.to_s
=> "<div class=\"trix-content\">\n Asd\n</div>\n"
so content in this example is not actually a column but it's a association. same way description_long in your example is an association not a column.
Please fine the note below "Note: you don't need to add a content field to your messages table." here in this guide https://edgeguides.rubyonrails.org/action_text_overview.html

The `==` method in Ruby on Rails with `has_and_belongs_to_many` [duplicate]

In Ruby 1.9.2 on Rails 3.0.3, I'm attempting to test for object equality between two Friend (class inherits from ActiveRecord::Base) objects.
The objects are equal, but the test fails:
Failure/Error: Friend.new(name: 'Bob').should eql(Friend.new(name: 'Bob'))
expected #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
got #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
(compared using eql?)
Just for grins, I also test for object identity, which fails as I'd expect:
Failure/Error: Friend.new(name: 'Bob').should equal(Friend.new(name: 'Bob'))
expected #<Friend:2190028040> => #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
got #<Friend:2190195380> => #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
Compared using equal?, which compares object identity,
but expected and actual are not the same object. Use
'actual.should == expected' if you don't care about
object identity in this example.
Can someone explain to me why the first test for object equality fails, and how I can successfully assert those two objects are equal?
Rails deliberately delegates equality checks to the identity column. If you want to know if two AR objects contain the same stuff, compare the result of calling #attributes on both.
Take a look at the API docs on the == (alias eql?) operation for ActiveRecord::Base
Returns true if comparison_object is the same exact object, or comparison_object is of the same type and self has an ID and it is equal to comparison_object.id.
Note that new records are different from any other record by definition, unless the other record is the receiver itself. Besides, if you fetch existing records with select and leave the ID out, you’re on your own, this predicate will return false.
Note also that destroying a record preserves its ID in the model instance, so deleted models are still comparable.
If you want to compare two model instances based on their attributes, you will probably want to exclude certain irrelevant attributes from your comparison, such as: id, created_at, and updated_at. (I would consider those to be more metadata about the record than part of the record's data itself.)
This might not matter when you are comparing two new (unsaved) records (since id, created_at, and updated_at will all be nil until saved), but I sometimes find it necessary to compare a saved object with an unsaved one (in which case == would give you false since nil != 5). Or I want to compare two saved objects to find out if they contain the same data (so the ActiveRecord == operator doesn't work, because it returns false if they have different id's, even if they are otherwise identical).
My solution to this problem is to add something like this in the models that you want to be comparable using attributes:
def self.attributes_to_ignore_when_comparing
[:id, :created_at, :updated_at]
end
def identical?(other)
self. attributes.except(*self.class.attributes_to_ignore_when_comparing.map(&:to_s)) ==
other.attributes.except(*self.class.attributes_to_ignore_when_comparing.map(&:to_s))
end
Then in my specs I can write such readable and succinct things as this:
Address.last.should be_identical(Address.new({city: 'City', country: 'USA'}))
I'm planning on forking the active_record_attributes_equality gem and changing it to use this behavior so that this can be more easily reused.
Some questions I have, though, include:
Does such a gem already exist??
What should the method be called? I don't think overriding the existing == operator is a good idea, so for now I'm calling it identical?. But maybe something like practically_identical? or attributes_eql? would be more accurate, since it's not checking if they're strictly identical (some of the attributes are allowed to be different.)...
attributes_to_ignore_when_comparing is too verbose. Not that this will need to be explicitly added to each model if they want to use the gem's defaults. Maybe allow the default to be overridden with a class macro like ignore_for_attributes_eql :last_signed_in_at, :updated_at
Comments are welcome...
Update: Instead of forking the active_record_attributes_equality, I wrote a brand-new gem, active_record_ignored_attributes, available at http://github.com/TylerRick/active_record_ignored_attributes and http://rubygems.org/gems/active_record_ignored_attributes
META = [:id, :created_at, :updated_at, :interacted_at, :confirmed_at]
def eql_attributes?(original,new)
original = original.attributes.with_indifferent_access.except(*META)
new = new.attributes.symbolize_keys.with_indifferent_access.except(*META)
original == new
end
eql_attributes? attrs, attrs2
I created a matcher on RSpec just for this type of comparison, very simple, but effective.
Inside this file:
spec/support/matchers.rb
You can implement this matcher...
RSpec::Matchers.define :be_a_clone_of do |model1|
match do |model2|
ignored_columns = %w[id created_at updated_at]
model1.attributes.except(*ignored_columns) == model2.attributes.except(*ignored_columns)
end
end
After that, you can use it when writing a spec, by the following way...
item = create(:item) # FactoryBot gem
item2 = item.dup
expect(item).to be_a_clone_of(item2)
# True
Useful links:
https://relishapp.com/rspec/rspec-expectations/v/2-4/docs/custom-matchers/define-matcher
https://github.com/thoughtbot/factory_bot
If like me you're looking for a Minitest answer to this question then here's a custom method that asserts that the attributes of two objects are equal.
It assumes that you always want to exclude the id, created_at, and updated_at attributes, but you can override that behaviour if you wish.
I like to keep my test_helper.rb clean so created a test/shared/custom_assertions.rb file with the following content.
module CustomAssertions
def assert_attributes_equal(original, new, except: %i[id created_at updated_at])
extractor = proc { |record| record.attributes.with_indifferent_access.except(*except) }
assert_equal extractor.call(original), extractor.call(new)
end
end
Then alter your test_helper.rb to include it so you can access it within your tests.
require 'shared/custom_assertions'
class ActiveSupport::TestCase
include CustomAssertions
end
Basic usage:
test 'comments should be equal' do
assert_attributes_equal(Comment.first, Comment.second)
end
If you want to override the attributes it ignores then pass an array of strings or symbols with the except arg:
test 'comments should be equal' do
assert_attributes_equal(
Comment.first,
Comment.second,
except: %i[id created_at updated_at edited_at]
)
end

Rails 4 after_initialize not getting called on find

In a rails 4 (currently 4.0.10) application model I was having a problem with a method not defined for NilClass error in a view, and so I decided to try to solve by setting a sometime nil to an empty string in the model's after_initialize thusly:
def after_initialize
...
self.biography ||= ""
end
because, according to the docs here http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html , "after_initialize callback is triggered for each object that is found and instantiated by a finder"
yet, it appears that after_initialize never gets called and so the error persists, as tested in the rails console:
2.1.1 :001 > Presenter.find(2)
=> #<Presenter id: 2, mediahub_id: 2, name: "Craig", asset_id: nil, biography: nil, status: "active", created_at: "2015-08-12 23:00:34",
updated_at: "2015-08-12 23:00:34">
as you can see, biography is still nil after the find. What am I doing wrong? :)

How to test for (ActiveRecord) object equality

In Ruby 1.9.2 on Rails 3.0.3, I'm attempting to test for object equality between two Friend (class inherits from ActiveRecord::Base) objects.
The objects are equal, but the test fails:
Failure/Error: Friend.new(name: 'Bob').should eql(Friend.new(name: 'Bob'))
expected #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
got #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
(compared using eql?)
Just for grins, I also test for object identity, which fails as I'd expect:
Failure/Error: Friend.new(name: 'Bob').should equal(Friend.new(name: 'Bob'))
expected #<Friend:2190028040> => #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
got #<Friend:2190195380> => #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
Compared using equal?, which compares object identity,
but expected and actual are not the same object. Use
'actual.should == expected' if you don't care about
object identity in this example.
Can someone explain to me why the first test for object equality fails, and how I can successfully assert those two objects are equal?
Rails deliberately delegates equality checks to the identity column. If you want to know if two AR objects contain the same stuff, compare the result of calling #attributes on both.
Take a look at the API docs on the == (alias eql?) operation for ActiveRecord::Base
Returns true if comparison_object is the same exact object, or comparison_object is of the same type and self has an ID and it is equal to comparison_object.id.
Note that new records are different from any other record by definition, unless the other record is the receiver itself. Besides, if you fetch existing records with select and leave the ID out, you’re on your own, this predicate will return false.
Note also that destroying a record preserves its ID in the model instance, so deleted models are still comparable.
If you want to compare two model instances based on their attributes, you will probably want to exclude certain irrelevant attributes from your comparison, such as: id, created_at, and updated_at. (I would consider those to be more metadata about the record than part of the record's data itself.)
This might not matter when you are comparing two new (unsaved) records (since id, created_at, and updated_at will all be nil until saved), but I sometimes find it necessary to compare a saved object with an unsaved one (in which case == would give you false since nil != 5). Or I want to compare two saved objects to find out if they contain the same data (so the ActiveRecord == operator doesn't work, because it returns false if they have different id's, even if they are otherwise identical).
My solution to this problem is to add something like this in the models that you want to be comparable using attributes:
def self.attributes_to_ignore_when_comparing
[:id, :created_at, :updated_at]
end
def identical?(other)
self. attributes.except(*self.class.attributes_to_ignore_when_comparing.map(&:to_s)) ==
other.attributes.except(*self.class.attributes_to_ignore_when_comparing.map(&:to_s))
end
Then in my specs I can write such readable and succinct things as this:
Address.last.should be_identical(Address.new({city: 'City', country: 'USA'}))
I'm planning on forking the active_record_attributes_equality gem and changing it to use this behavior so that this can be more easily reused.
Some questions I have, though, include:
Does such a gem already exist??
What should the method be called? I don't think overriding the existing == operator is a good idea, so for now I'm calling it identical?. But maybe something like practically_identical? or attributes_eql? would be more accurate, since it's not checking if they're strictly identical (some of the attributes are allowed to be different.)...
attributes_to_ignore_when_comparing is too verbose. Not that this will need to be explicitly added to each model if they want to use the gem's defaults. Maybe allow the default to be overridden with a class macro like ignore_for_attributes_eql :last_signed_in_at, :updated_at
Comments are welcome...
Update: Instead of forking the active_record_attributes_equality, I wrote a brand-new gem, active_record_ignored_attributes, available at http://github.com/TylerRick/active_record_ignored_attributes and http://rubygems.org/gems/active_record_ignored_attributes
META = [:id, :created_at, :updated_at, :interacted_at, :confirmed_at]
def eql_attributes?(original,new)
original = original.attributes.with_indifferent_access.except(*META)
new = new.attributes.symbolize_keys.with_indifferent_access.except(*META)
original == new
end
eql_attributes? attrs, attrs2
I created a matcher on RSpec just for this type of comparison, very simple, but effective.
Inside this file:
spec/support/matchers.rb
You can implement this matcher...
RSpec::Matchers.define :be_a_clone_of do |model1|
match do |model2|
ignored_columns = %w[id created_at updated_at]
model1.attributes.except(*ignored_columns) == model2.attributes.except(*ignored_columns)
end
end
After that, you can use it when writing a spec, by the following way...
item = create(:item) # FactoryBot gem
item2 = item.dup
expect(item).to be_a_clone_of(item2)
# True
Useful links:
https://relishapp.com/rspec/rspec-expectations/v/2-4/docs/custom-matchers/define-matcher
https://github.com/thoughtbot/factory_bot
If like me you're looking for a Minitest answer to this question then here's a custom method that asserts that the attributes of two objects are equal.
It assumes that you always want to exclude the id, created_at, and updated_at attributes, but you can override that behaviour if you wish.
I like to keep my test_helper.rb clean so created a test/shared/custom_assertions.rb file with the following content.
module CustomAssertions
def assert_attributes_equal(original, new, except: %i[id created_at updated_at])
extractor = proc { |record| record.attributes.with_indifferent_access.except(*except) }
assert_equal extractor.call(original), extractor.call(new)
end
end
Then alter your test_helper.rb to include it so you can access it within your tests.
require 'shared/custom_assertions'
class ActiveSupport::TestCase
include CustomAssertions
end
Basic usage:
test 'comments should be equal' do
assert_attributes_equal(Comment.first, Comment.second)
end
If you want to override the attributes it ignores then pass an array of strings or symbols with the except arg:
test 'comments should be equal' do
assert_attributes_equal(
Comment.first,
Comment.second,
except: %i[id created_at updated_at edited_at]
)
end

Can I scope dynamic attribute-based finders to an object?

Don't mind me, I fricked up my attribute names :(
This is entirely possible, using the exact syntax I used - you just need to be able to spell!
I can't seem to get this to work, and it seems like a common enough scenario that there must be a solution, but I'm not having any luck with the correct terminology to get a helpful Google result.
I want to do this:
u = User.first
u.clients.find_or_create_by_email('example#example.com')
With the effect that a new Client is created with user_id = u.id.
Can I get the nice dynamic finders through a has_many relationship? If not, why?
Thanks :)
This
u = User.first
u.clients.find_or_create_by_email('example#example.com')
works if you have has_many relationship set. However, it won't raise validation error if you have any validations set on your Client object and it will silently fail if the validation fails.
You can check the output in your console when you do
u.clients.find_or_create_by_email('example#example.com') # => #<Client id: nil, email: 'example#example.com', name: nil, user_id: 1, another_attribute: nil, active: true, created_at: nil, updated_at: nil>
and the user_id will be set but not the id of client because the validation has failed and the client is not created
So this should create the client only if you pass all the required attributes of client object and the validation for client object has passed successfully.
So lets say your client model has validation on name as well apart from email then you should do
u.clients.find_or_create_by_email_and_name('example#example.com', 'my_name') #=> #<Client id: 1, email: 'example#example.com', name: 'my_name', user_id: 1, another_attribute: nil, active: true, created_at: "2009-12-14 11:08:23", updated_at: "2009-12-14 11:08:23">
This is entirely possible, using the exact syntax I used - you just need to be able to spell!

Resources