I do the following in the console:
1.9.3p194 :062 > #user = [name: "Joe", age: "17"]
=> [{:name=>"Joe", :age=>"17"}]
1.9.3p194 :063 > #user.slice(:name)
TypeError: can't convert Symbol into Integer
Why isn't slice returning [name: "Joe"]?
You're embedding the hash in an array!
Try like this :
#user = {name: "Joe", age: "17"}
#user.slice(:name)
To get an array of only name and id on User.all :
array = User.all.map { |u| u.attributes.slice(:name, :id) }
map executes what you provide in the code block on each element u and builds an array with it, that is returned and put in the variable array in the above example.
u.attributes gives a Hash containing all attributes of the User model for an instance of User ... everything's that's saved in the DB.
Based on the limited information provided, I'm not sure why you would want the overhead of array operations. Consider using OStruct instead.
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/ostruct/rdoc/OpenStruct.html
The example you provided would look like this in OStruct:
require 'ostruct'
#user = OpenStruct.new
#user.name = "John Smith"
#user.age = 17
puts #user.name # -> "Joe"
puts #user.age # -> 17
No slicing and clean, fast-executing Ruby code.
Related
I have a model, Message that belongs to the model User and the User model has an attribute name.
Message:
user_id
message_body
1
"hello world"
User:
user_id
name.
1
"johndoe"
The result I want is a complete list of all the messages and the respective user name that created each of those messages.
the api controller endpoint looks something like:
def index
#messages = Message.all
render json: { messages: #messages }
end
The issue is that when I return #messages it only contains the user_id that each message belongs to. What I really want is the user name
I could loop through every message and construct an entirely new object that looks something like:
#object = [
{
name: #messages[0].user.name,
message_body: #messages[0].body
},
{
name: #messages[1].user.name,
message_body: #messages[1].body
},
etc.
]
and then call render json: { messages: #object }
This would probably work fine, but it seems inefficient. Is there a better method for joining these tables for this result?
name
message body
"johndoe"
"hello world"
I was hoping the above example would be enough to get the answer I'm looking for. This is a simplified version of my architecture. In reality it's a bit more complicated:
LeagueChatMessage belongs_to LeagueChat
LeagueChatMessage belongs_to User
LeagueChat belongs_to League
League has_one LeagueChat
so this is really what the controller looks like
def index
#league = League.find_by(id: 1)
render json: { messages: #league.league_chat.league_chat_messages }
end
it works fine. It returns all the league chat messages for the league with the id: 1 but it returns the user_id for each message instead of the user name
Use following logic
#data = Message.includes(:user)
Now you can use like below
#data.each do |msg|
puts "Message #{msg.body}"
puts "User #{msg.user.name}"
end
I used puts for understanding but you can use this object in views as you want. And your approach leads to an n+1 query problem, so I used the includes, which helps remove the n+1 query. Try this and let me know if you have any queries.
I use raw SQL to build an SQL editor in my project. But when I call simple select queries, I get this result:
[{"id"=>1, "name"=>"Ukraine", "phone_code"=>"+38", "created_at"=>"2015-11-23 21:52:49.415748", "updated_at"=>"2015-11-23 21:52:49.415748", 0=>1, 1=>"Ukraine", 2=>"+38", 3=>"2015-11-23 21:52:49.415748", 4=>"2015-11-23 21:52:49.415748"}]
The query is simple:
SELECT * FROM countries
As you can see, the same fields are displayed firstly with titles of attributes, and then second time with integer indexes.
I use this code two output only elements with attribute titles:
#headers = #result.first.keys
#count = #headers.size / 2
After that I iterate over headers:
<tr>
<% (0...#count).each do |i| %>
<th><%= #headers[i] %></th>
<% end %>
</tr>
But I think that it is not the best solution. So, what is the best way to output only elements with proper titles, not with indexes?
Try select_all
$ bin/rails c
Loading development environment (Rails 4.2.4)
>> result = ActiveRecord::Base.connection.select_all "SELECT * FROM wingnuts"
(0.4ms) SELECT * FROM wingnuts
=> #<ActiveRecord::Result:0x007fb92fbcdb18 #columns=["id", "size"], #rows=[[1, 10], [2, 11], [3, 12]], #hash_rows=nil, #column_types={}>
>> result.each { |row| puts row.inspect }
{"id"=>1, "size"=>10}
{"id"=>2, "size"=>11}
{"id"=>3, "size"=>12}
=> [{"id"=>1, "size"=>10}, {"id"=>2, "size"=>11}, {"id"=>3, "size"=>12}]
If you already have the known keys that you want to extract from the hash, then you can just use Hash#select method as following:
a = [{"id"=>1, "name"=>"Ukraine", "phone_code"=>"+38", "created_at"=>"2015-11-23 21:52:49.415748", "updated_at"=>"2015-11-23 21:52:49.415748", 0=>1, 1=>"Ukraine", 2=>"+38", 3=>"2015-11-23 21:52:49.415748", 4=>"2015-11-23 21:52:49.415748"}]
known_keys = %w(id name phone_code created_at updated_at)
a[0].select { |key, value| known_keys.include? key }
# => {"id"=>1, "name"=>"Ukraine", "phone_code"=>"+38", "created_at"=>"2015-11-23 21:52:49.415748", "updated_at"=>"2015-11-23 21:52:49.415748"}
I am not sure why you are returning in this manner, but in answer to your question "what is the best way to output only elements with proper titles, not with indexes?":
# The new, properly formatted object
hash_of_proper_keys = Hash.new
# Assuming country is in the form of a hash of values
country.each do |key, value|
hash_of_proper_keys[key] = value unless key.is_a? integer
end
I recently upgraded my rails to Rails 4.1.6.
This query used to work :
#user = User.find(:all, :conditions => { :name => 'batman' })
Now I get this error message:
Couldn't find all Users with 'id': (all, {:conditions=>{:name=>"batman"}}) (found 0 results, but was looking for 2)
When I check the logs I can see that rails is trying to do a completely different query :
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" IN ('all', '---
:conditions:
:name: batman
')
It looks like, it's trying to get all the users with the id "all" and "{:conditions=>{:name=>"batman"}}". Please help.
UPDATE:
My real question behind that was I want to get a specific user and add to it his cars, only the cars that are blue. For example this is my query, the user id is 20.
#user = User.joins(:cars).find(20, :cars => {:color => "blue"})
But I get this error:
Couldn't find all Users with 'id': (20, {:cars=>{:color=>"blue"}})
(found 41 results, but was looking for 2)
You should definitely read this ActiveRecord Query Interface quide
User.where(name: "batman")
Some others already pointed out: The query syntax changed. Try this:
#user = User.joins(:cars).where(:cars => { :color => "blue" }).find(20)
Note that this will raise an exception if that record is not found, to return an array empty instead call:
#user = User.joins(:cars).where(:id => 20, :cars => { :color => "blue" })
I suggest to read: http://guides.rubyonrails.org/active_record_querying.html
If you want to load the user even if he does not have any cars and than display only his blue cars, I would do it like this:
#user = User.find(20) # returns the user
#user.cars.where(:color => 'blue') # returns the user's blue cars (or an empty array)
The find method is deprecated in this version of Rails (see the reference).
Instead, you must use the where method.
In your case, you should write #user = User(:name => 'batman') or #user = User(name: 'batman')
I have a User model and a List model in my app.
pages_controller.rb
class PagesController < ApplicationController
def home
if user_signed_in?
#lists = current_user.lists
# raise #lists.inspect
#new_list = current_user.lists.build
end
end
end
pages/home.html.erb
<%= raise #lists.inspect %>
Now, my current user has no lists associated with him .
When I uncomment the 3rd line in "Pages#home" raise #lists.inspect I get the output like so : []
But, when I comment that line out, then the exception inside home.html.erb is raised , and its output is like so : [#<List id: nil, name: nil, description: nil, user_id: 1, created_at: nil, updated_at: nil>]
Why is there a difference in output for the same #lists.inspect line ?
EDIT : When I use #lists = current_user.lists.all instead of #lists = current_user.lists then I get an empty array at both places . Why the difference in behavior between the 2 codes ?
Because you build lists in the controller after the first raise:
#new_list = current_user.lists.build
It's the same code, but the data is different, because you did something to it.
I want to use FactoryGirl.attributes_for in controller testing, as in:
it "raise error creating a new PremiseGroup for this user" do
expect {
post :create, {:premise_group => FactoryGirl.attributes_for(:premise_group)}
}.to raise_error(CanCan::AccessDenied)
end
... but this doesn't work because #attributes_for omits the :user_id attribute. Here is the difference between #create and #attributes_for:
>> FactoryGirl.create(:premise_group)
=> #<PremiseGroup id: 3, name: "PremiseGroup_4", user_id: 6, is_visible: false, is_open: false)
>> FactoryGirl.attributes_for(:premise_group)
=> {:name=>"PremiseGroup_5", :is_visible=>false, :is_open=>false}
Note that the :user_id is absent from #attributes_for. Is this the expected behavior?
FWIW, my factories file includes definitions for :premise_group and for :user:
FactoryGirl.define do
...
factory :premise_group do
sequence(:name) {|n| "PremiseGroup_#{n}"}
user
is_visible false
is_open false
end
factory :user do
...
end
end
Short Answer:
By design, FactoryGirl's attribues_for intentionally omits things that would trigger a database transaction so tests will run fast. But you can can write a build_attributes method (below) to model all the attributes, if you're willing to take the time hit.
Original answer
Digging deep into the FactoryGirl documentation, e.g. this wiki page, you will find mentions that attributes_for ignores associations -- see update below. As a workaround, I've wrapped a helper method around FactoryGirl.build(...).attributes that strips id, created_at, and updated_at:
def build_attributes(*args)
FactoryGirl.build(*args).attributes.delete_if do |k, v|
["id", "created_at", "updated_at"].member?(k)
end
end
So now:
>> build_attributes(:premise_group)
=> {"name"=>"PremiseGroup_21", "user_id"=>29, "is_visible"=>false, "is_open"=>false}
... which is exactly what's expected.
update
Having absorbed the comments from the creators of FactoryGirl, I understand why attributes_for ignores associations: referencing an association generates a call to the db which can greatly slow down tests in some cases. But if you need associations, the build_attributes approach shown above should work.
I think this is a slight improvement over fearless_fool's answer, although it depends on your desired result.
Easiest to explain with an example. Say you have lat and long attributes in your model. On your form, you don't have lat and long fields, but rather lat degree, lat minute, lat second, etc. These later can converted to the decimal lat long form.
Say your factory is like so:
factory :something
lat_d 12
lat_m 32
..
long_d 23
long_m 23.2
end
fearless's build_attributes would return { lat: nil, long: nil}. While the build_attributes below will return { lat_d: 12, lat_m: 32..., lat: nil...}
def build_attributes
ba = FactoryGirl.build(*args).attributes.delete_if do |k, v|
["id", "created_at", "updated_at"].member?(k)
end
af = FactoryGirl.attributes_for(*args)
ba.symbolize_keys.merge(af)
end
To further elaborate on the given build_attributes solution, I modified it to only add the accessible associations:
def build_attributes(*args)
obj = FactoryGirl.build(*args)
associations = obj.class.reflect_on_all_associations(:belongs_to).map { |a| "#{a.name}_id" }
accessible = obj.class.accessible_attributes
accessible_associations = obj.attributes.delete_if do |k, v|
!associations.member?(k) or !accessible.include?(k)
end
FactoryGirl.attributes_for(*args).merge(accessible_associations.symbolize_keys)
end
Here is another way:
FactoryGirl.build(:car).attributes.except('id', 'created_at', 'updated_at').symbolize_keys
Limitations:
It does not generate attributes for HMT and HABTM associations (as these associations are stored in a join table, not an actual attribute).
Association strategy in the factory must be create, as in association :user, strategy: :create. This strategy can make your factory very slow if you don't use it wisely.
The accepted answer seems outdated as it did not work for me, after digging through the web & especially this Github issue, I present you:
A clean version for the most basic functionality for Rails 5+
This creates :belongs_to associations and adds their id (and type if :polymorphic) to the attributes. It also includes the code through FactoryBot::Syntax::Methods instead of an own module limited to controllers.
spec/support/factory_bot_macros.rb
module FactoryBot::Syntax::Methods
def nested_attributes_for(*args)
attributes = attributes_for(*args)
klass = args.first.to_s.camelize.constantize
klass.reflect_on_all_associations(:belongs_to).each do |r|
association = FactoryBot.create(r.class_name.underscore)
attributes["#{r.name}_id"] = association.id
attributes["#{r.name}_type"] = association.class.name if r.options[:polymorphic]
end
attributes
end
end
this is an adapted version of jamesst20 on the github issue - kudos to him 👏