I'm creating an active record object, and I want to test that I'm doing it correctly.
Here's my test code:
describe '#transaction process' do
let(:penalty_purchase) { penalty.penalty_purchase }
describe '#transaction' do
it 'a transaction between the seller and the admin, includes the penalty purchase and the sellers sale' do
penalty.purchase= penalty_purchase
args = {sale: sale, purchase: penalty_purchase}
wanted = penalty.transaction_factory.new(args).transaction
expect(penalty.transaction).to eql wanted
end
end
end
Heres the code my test is testing:
def penalty_transaction
args = {sale: sale, purchase: purchase}
#transaction = transaction_factory.new(args).transaction
end
The transaction factory object above takes in 2 other active record objects, a sale and a purchase.
For some reason the test is failing with this:
1) LockedSalePenalty purchase process #transaction process #transaction a transaction between the seller and the admin, includes the penalty purchase and the sellers sale
Failure/Error: expect(penalty.transaction).to eql wanted
expected: #<Transaction id: nil, purchase_id: nil, sale_id: 1, created_at: "2014-08-03 00:23:45", updated_at: nil, seller_id: 1, buyer_id: 2, amount: 20.0, reduced_asset_id: nil>
got: #<Transaction id: nil, purchase_id: nil, sale_id: 1, created_at: "2014-08-03 00:23:45", updated_at: nil, seller_id: 1, buyer_id: 2, amount: 20.0, reduced_asset_id: nil>
(compared using eql?)
I know that for objects you create you need implement your own equality methods for objects you create, so in my active record transaction class:
def eql?(obj)
self.class == obj.class &&
self.attributes == obj.attributes
end
When I compare the attributes of penalty.transaction and wanted, they are not equal, even though they look the exact same. Any ideas?
Edit:
The problem was that the created at attribute was different by a small amount.
Related
Using Rails 4 with GraphQL API.
I'm getting some inputs via an object, based on which I'm finding or initializing new ActiveRecord objects that I want to save later.
Sample input is:
[
{:id=>"192", :internalId=>128, :title=>"Editing"},
{:internalId=>130, :title=>"New"}
]
As you can notice, some of the records already exist and have an ID, we need to update those. And the rest we need to save as new records.
Then I have a method that goes through those post values:
def posts=(value)
#posts = value.map do |post|
init_post(post)
end
end
def init_post(post)
Post.find_or_initialize_by(
id: post[:id],
title: post[:title],
internal_id: post[:internalId],
)
end
That will return two instances of the Post model:
[#<Post id: 192, title: "Editing", internal_id: 128, created_at: nil, updated_at: nil>, #<Post id: nil, title: "New", internal_id: 130, created_at: nil, updated_at: nil>]
Finally, I want to save both records:
def save_posts
posts.each(&:save)
end
Which will return:
"#<ActiveRecord::RecordNotUnique: Mysql2::Error: Duplicate entry '192' for key 'PRIMARY': INSERT INTO `posts` ..."
So how do I make sure the instances with ID just update the existing record, and the rest just save as new ones?
You can find, change/create and save it at once
Post.find_or_initialize_by(id: post[:id]).tap do |record|
record.title = post[:title]
record.internal_id = post[:internalId]
record.save
end
I have three models, related with has_many :through associations:
class Account < ApplicationRecord
has_many :account_owners
has_many :employees, through: account_owners
def is_owned_or_belongs_to_team_of_employees(employee)
employee.team.any? { |m| employees.include?(m) }
end
end
class AccountOwner < ApplicationRecord
belongs_to :account
belongs_to :employee
end
class Employee < ApplicationRecord
has_many :account_owners
has_many :accounts, through: :account_owners
def team
self.class.where(
'id IN (?)',
self. class.find_by_sql(['WITH RECURSIVE search_tree(id, path) AS (
SELECT id, ARRAY[id]
FROM employees
WHERE id = ?
UNION ALL
SELECT employees.id, path || employees.id
FROM search_tree
JOIN employees ON employees.manager_id = search_tree.id
WHERE NOT employees.id = ANY(path)
)
SELECT id FROM search_tree ORDER BY path',
self.id])
).order(:id)
end
end
I'm manually testing, in the Rails console in my development environment (using some fixtures that I first loaded on the database), the Account#is_owned_or_belongs_to_team_of_employees method.
When I run the method in the console this is what happens:
> a = Account.first
=> #<Account id: 534788375, name: "Sales Rep 1 (elena)-owned account", code: "EEE", created_at: "2018-07-15 09:41:55", updated_at: "2018-07-15 09:41:55">
> e = Employee.find_by(first_name: 'Elena')
=> #<Employee id: 701979064, first_name: "Elena", last_name: "López", manager_id: 1069403509, created_at: "2018-07-15 09:41:55", updated_at: "2018-07-15 09:41:55", mobile: nil, work: nil>
> e.team
=> #<ActiveRecord::Relation [#<Employee id: 701979064, first_name: "Elena", last_name: "López", manager_id: 1069403509, created_at: "2018-07-15 09:41:55", updated_at: "2018-07-15 09:41:55", mobile: nil, work: nil>]>
> a.is_owned_or_belongs_to_team_of e
=> nil
> a.is_owned_or_belongs_to_team_of e
=> true
As you can see, the method returns nil (wrong!) the first time, and returns true (correct!) the following times.
The amazing thing is that I can correct the problem if I define the method like this:
def is_owned_or_belongs_to_team_of employee
puts "employees are #{employees.inspect}"
employee.team.any? { |m| employees.include?(m) }
end
Now the execution is correct, and the method returns consistently the same result (true in my example):
> a = Account.first
=> #<Account id: 534788375, name: "Sales Rep 1 (elena)-owned account", code: "EEE", created_at: "2018-07-15 09:41:55", updated_at: "2018-07-15 09:41:55">
> e = Employee.find_by(first_name: 'Elena')
=> #<Employee id: 701979064, first_name: "Elena", last_name: "López", manager_id: 1069403509, created_at: "2018-07-15 09:41:55", updated_at: "2018-07-15 09:41:55", mobile: nil, work: nil>
> e.team
=> #<ActiveRecord::Relation [#<Employee id: 701979064, first_name: "Elena", last_name: "López", manager_id: 1069403509, created_at: "2018-07-15 09:41:55", updated_at: "2018-07-15 09:41:55", mobile: nil, work: nil>]>
> a.is_owned_or_belongs_to_team_of e
=> true
> a.is_owned_or_belongs_to_team_of e
=> true
If I remove the puts statement, we are back to square one: the method returns nil the first time, and true the following times.
And, amazingly, if I keep the puts statement but remove the inspect (that is, I just do puts "employees are #{employees}" we are also back to square one: nil the first time, and true the following times.
Any idea? What is going on here?
By the way, I'm running Ruby 2.5.1 y Rails 5.2.0.
I'm glad I stumbled upon this Unicorn of a bug!
After debugging this for hours, I found out the following:
any? had new changes in rails 5.2 release that was supposed to delegate it to Enumerable
the surprising thing, that if you put a binding.pry in the implementation of any? and call super it returns true even the first time and then the method returns nil. ~/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.0/lib/active_record/relation.rb # line 228 ActiveRecord::Relation#any?:
if you add to employee.team .to_a everything works consistently.
if you put any? { |_| true } it returns true.
If you check for the value inside the block for include? it returns true but any? still returns nil!!!
If you avoid resolving the has_may through association (by calling .to_a before the block) or even using a different association inside the any? block everything works as expected.
using any other ruby version fixes the problem.
Summary
The problem was introduced in ruby 2.5.1 rails v5.2.0 when ActiveRecord::Relation started to include Enumerable.It happens with %w(none? any? one? many?) while trying to resolve a has many through association in its block.
I'm using Rails 5 and minitest. In minitest, how do I lookup an object from the database by one of its attributes (e.g. not the fixtures designation for the object). In my test I have
item = items(:one)
,,, do some db manipulation to this object ...
Item.all.each do |i|
puts "#{i.inspect}"
end
updated_item = Item.find(id: item.id)
But the line
updated_Item = Item.find(id: item.id)
dies with the error
ActiveRecord::RecordNotFound: Couldn't find Item with 'id'={:id=>980190962}
What is odd is taht in the lines above, where I print out the records in my database, I can see the object in question with the ID that Rails claims not to find ...
#<Item id: 298486374, name: "MyString", rating: 0, score: 0, created_at: "2018-01-19 20:25:05", updated_at: "2018-01-19 20:25:05">
#<Item id: 980190962, name: "Item1", rating: 1, score: 5, created_at: "2018-01-19 20:25:05", updated_at: "2018-01-19 20:25:05">
What am I doing wrong? How do I lookup the updated object?
find() looks by id , no need to tell, so:
updated_Item = Item.find(item.id)
This is driving me insane. I've stripped this down to the bare minimum without losing context (I think!)
All I am trying to do is check that when I update a value and save it to the database, that the value was saved. I want to do this because I need to write some other code that conditionally prevents this in the before_save callback, and I can't test that until I'm sure this is working!
The factories and the spec are below, I'm sure its something really stupid but I just can't figure it out...
FactoryGirl.define do
factory :programme do
name 'Trainee Programme'
end
factory :membership do
programme
end
factory :specialty do
sequence(:name) { |n| "Specialty #{n}" }
end
factory :user do
sequence(:email) { |n| "factorygirl-user-#{n}#remailer.org" }
password 'password'
password_confirmation 'password'
factory :trainee, class: User do
sequence(:email) { |n| "factorygirl-trainee-#{n}#remailer.org" }
name 'Factory Girl Trainee'
after(:create) do |user|
FactoryGirl.create(:membership, user: user, start_date: 1.day.ago)
end
end
end
end
describe Membership do
let(:trainee) { FactoryGirl.create(:trainee) }
it 'sets specialty' do
puts trainee.current_membership.inspect
trainee.current_membership.specialty = specialty
puts trainee.current_membership.inspect
trainee.current_membership.save!
puts trainee.current_membership.inspect
expect(trainee.current_membership.specialty).to eq(specialty)
end
end
The spec is failing because the expect sees a nil value. When I run the code the debug output I get is:
#<Membership id: 11, user_id: 11, programme_id: 11, start_date: "2015-03-10", end_date: nil, created_at: "2015-03-11 22:02:51", updated_at: "2015-03-11 22:02:51", options: {}, specialty_id: nil, membership_type_id: nil>
#<Membership id: 11, user_id: 11, programme_id: 11, start_date: "2015-03-10", end_date: nil, created_at: "2015-03-11 22:02:51", updated_at: "2015-03-11 22:02:51", options: {}, specialty_id: nil, membership_type_id: nil>
#<Membership id: 11, user_id: 11, programme_id: 11, start_date: "2015-03-10", end_date: nil, created_at: "2015-03-11 22:02:51", updated_at: "2015-03-11 22:02:51", options: {}, specialty_id: nil, membership_type_id: nil>
So its as if the assignment of specialty never happens??
Try reloading trainee, e.g.
expect(trainee.reload.current_membership.specialty).to eq(specialty)
Thanks to BroiState and Mori giving me some pointers I was able to establish that it was related to persistence (in particular one of my object methods not respecting it!)
The code for trainee.current_membership is as follows:
def current_membership
return unless memberships.current.any?
memberships.current.first
end
which uses these related scopes in Membership...
scope :started, -> { self.where("#{table_name}.#{_start_field}::TIMESTAMP < '#{Time.now}'") }
scope :not_ended, -> { self.where("#{table_name}.#{_end_field} IS NULL OR #{table_name}.#{_end_field}::TIMESTAMP > '#{Time.now}'") }
scope :current, -> { self.started.not_ended }
So each call to trainee.current_membership was giving me a new instance of the 'current' membership record
by explicitly using the same object the spec passed fine, i.e.:
it 'sets specialty' do
membership = trainee.current_membership
membership.specialty = specialty
membership.save!
expect(membership.specialty).to eq(specialty.reload)
end
The only reason I could think of is that speciality is a new, non-persisted record. Since membership belongs_to :speciality and membership is already persisted, it will not save associated object neither on assignment nor on save. In short, make sure that speciality is saved before creating association.
Like the title said, I'm trying to get the ID of the record I just created, I tried to get it in the console but the ID is nil.
Here is the print I got of self in the console at the beginning of after_create
<Unit id: nil, uuid: "9f11be13-8d07-4471-a3bb-f87943966bbd", organization_id: 33, property_id: 430, door_number: "3", created_at: "2014-12-05 13:27:57", updated_at: "2014-12-05 13:27:57", deleted_at: nil, size: "5 1/2", door_number_int: 3, leases_count: 0, tasks_count: 0, notes: nil, state_id: 68, state_tasks_count: 2, current_lease_id: nil, next_lease_id: nil, state_tasks_serie: 1, state_tasks_serie_count: 2, price_asked: #<BigDecimal:7fc79162cb80,'0.123E3',9(18)>, availability_date: nil, comments_count: 0>
Is there a way to get access to the record ID?
This is what I tried so far
after_create
self.save
end
before_save
if self.id.present?
# do stuff
end
end
It is not very pretty but it work
To answer #MarekLipka : It doesn't cause an infinite loop because the record is created only once.
I also tried to use :
after_create
reload
# do stuff if self.id
end
but that doesn't seem to work.
Like #PauloFidalgo said, I can't get the id except in the after_save method. Although it is strange to me that I can get it in the second before_save (triggered by the save in after_create).
The id is only assigned on save, on create you are just creating the skeleton object and giving some values.
To get the id of the object just call object.id
If for some reason you want to get the id in the after_* methods, you need to use the after_save:
after_save {id = self.id}
You could also allocate an ID in create, but you need to query the database and set the value in the variable. To achieve this you need to use a sequence in database to set the id's.