I'm trying to display visitors' names in my rails admin show page.
I'm doing it this way:
field :visitors
But I get displayed the following:
Visitor #7 and Visitor #8
I've tried to do something like this with pretty value:
configure :visitors do
pretty_value do
bindings[:object].visitors.each do |visitor|
visitor.full_name
end
end
end
But it shows me objects' details:
[#<Visitor id: 7, first_name: "John", last_name: "Smith", created_at: "2016-03-03 10:23:34", updated_at: "2016-03-03 10:23:34", booking_id: 7>, #<Visitor id: 8, first_name: "Bob", last_name: "Smith", created_at: "2016-03-03 10:23:34", updated_at: "2016-03-03 10:23:34", booking_id: 7>]
I simply want to display something like this:
John Smith and Bob Smith
How can I do this?
Well, I've found the solution using map:
bindings[:object].visitors.all.map {|v| v.full_name}.join(', ')
Related
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)
I'm having issues trying to access my associated model in a one-to-many relationship during testing. I don't understand why the below doesn't return the association.
The reason I care about this problem is because I wanted to use Factory Girl to call my guardian through my_factory_girl_user.guardians.first which wasn't working. I tried to simplify by using just ActiveRecord and it still isn't behaving how I would expect.
I'm a true Rails beginner so any advice on this problem or how to better debug it would be much appreciated!
Question: Why doesn't #target_user.guardians return anything?
User Model
class User < ActiveRecord::Base
has_many :guardians, dependent: :destroy
...
end
Guardian Model
class Guardian < ActiveRecord::Base
belongs_to :user
...
end
Controller Spec
describe 'PUT #update' do
context 'when logged in and authorized' do
let(:new_attributes){
{level: 31}
}
it 'sets current guardian' do
#target_user = User.create!(profile_name: 'MyGuardian', system: 'Xbox One', region: 'North America', password: "password", password_confirmation: "password")
#target_guardian = Guardian.create!(level: 31, guardian_class: 'Titan', activity: 'Vault of Glass', user_id: #target_user.id)
puts "User: #{#target_user.inspect}"
puts "Guardian: #{#target_guardian.inspect}"
puts "user.guardians: #{#target_user.guardians}"
put :update, {id: #target_guardian.id, guardian: new_attributes}, {user_id: #target_user.id}
expect(assigns(:guardian)).to eq(#target_guardian)
end
end
Output
User: #<User id: 3147, email: nil, password_digest: "$2a$04$QPk3kCsKLp0IX.YmMPAPdO2gCe79mwVybHkFpcEsVkG...", created_at: "2014-12-16 02:55:57", updated_at: "2014-12-16 02:55:57", is_admin: nil, profile_name: "MyGuardian", system: "Xbox One", region: "North America">
Guaridan: #<Guardian id: 3284, created_at: "2014-12-16 02:55:57", updated_at: "2014-12-16 02:55:57", user_id: 3147, fireteam_id: nil, level: 31, guardian_class: "Titan", activity: "Vault of Glass", comment: nil>
user.guardians:
Answer:
Thanks Typpex for the help! My issue was that I was not refreshing the User after creating the Guardian, the solution I used was adding the below line after creating my Guardian. For more information read the answer below.
#target_user.reload
It is because you never actually added the guardian to the user's guardian list such as:
#target_user.guardians << #target_guardian
Only after that you will be able to see guardians in #target_user.guardians.
When testing with rspec you need to call the action in your controller that is actualling adding the guardian to the guardians collection in user and then test if the guardian was correctly added with something like
#target_user.guardians.size.should_be 1
I have this request to database
#show = Question.where(:question_status => params[:id])
Then in #show variable I have this: [#<Question id: 38, user_id: 1, question: "hi", question_status: 1, created_at: "2013-06-04 18:32:28", updated_at: "2013-06-04 18:32:28">, #<Question id: 40, user_id: 1, question: "urll", question_status: 1, created_at: "2013-06-04 18:34:57", updated_at: "2013-06-04 18:34:57">, #<Question id: 41, user_id: 1, question: "urll", question_status: 1, created_at: "2013-06-04 18:35:31", updated_at: "2013-06-04 18:35:31">]
How get , for example, question field ?
I trying #show.question but have error no defined method question.
#show = Question.find_by_question_status(params[:id])
and #show.question
If you us where statement then use:
#show.each do |show|
show.question
end
#show is an ActiveRecord::Relation object(Array). It does not belong to Question class.
#show = Question.where(:question_status => params([:id]).first
if you are expecting only one result. Otherwise you have to iterate the array for each element
You want to take an array of all question fields?
questions = Question.where(:question_status => params[:id]).map(&:question)
Then you can simply go like
questions.each{|question| puts question }
Try this code at your rails console
#show = Question.where(:question_status => params[:id])
#show.each_with_index do |ques, index|
p "#{index+1} :: #{ques.question}"
end
With the help of index, you will get the sequence number of rows.
Use #show.map(&:question) . This will give you all the questions in array.
#show.question gives you error no defined method question because you are trying to operate question on array #show.
If you do #show.first.question it will give you question of first object.
And
If you do #show.map(&:question) it will give you array of question.
To get all the information.
#show.each do |show|
puts show.question
puts show.question_status
puts show.user_id
end
You can optimize it as per you requirement.
I noticed that I can do a Model.find in a number of ways (assuming #user is an instance of the User model):
User.find(2)
=> #<User id: 2, name: "Mike Swift", email: "valid#email.com", ... etc ...
OR
User.find(#user)
=> #<User id: 2, name: "Mike Swift", email: "valid#email.com", ... etc ...
OR
User.find(#user[:id])
=> #<User id: 2, name: "Mike Swift", email: "valid#email.com", ... etc ...
OR
User.find(#user.id)
=> #<User id: 2, name: "Mike Swift", email: "valid#email.com", ... etc ...
Is there any real difference between the later three of these methods? (I already know User.find(n) would be the fastest) I would imagine they all work in about the same time, but perhaps I'm wrong.
In terms of sql they all do the same thing.
User.find(2)
This will be the fastest because there is no conversion needed.
Then User.find(#user.id) and User.find(#user[:id]).
And finally User.find(#user because rails needs convert the user to an ID.
User.find(2) should be faster as Rails doesn't have to do any work to figure out the id. The others require some level of message passing to get the id.
I doubt the difference is very significant though.
You could try all of them and look at your log to see how long it takes to get your response.