What would be the best way of conditionally including an attribute in a create (or any method(attr, attr, attr) style method)?
#user = User.find_by_name("Sam") # <- might not exist
Store.create(
name: "Some Store",
email: "store#example.com",
user_id: #user.id if #user.present? # <- CONCEPT ONLY - Should be added if condition is true to prevent error
)
the line user_id: #user.id throws an error if #user wasn't found and is therefore nil.
You forgot that .create()'s argument is a shortcut for a hash. Make the hash explicit:
hash = {
name: "Some Store",
email: "store#example.com"
}
hash[:user_id] = #user.id if #user.present?
Store.create(hash)
Another option is to pass .create a block:
Store.create do |s|
s.name = "Some Store"
s.email = "store#example.com"
s.user_id = #user.id if #user
end
The most minimal change I could make for your code to work is:
Store.create(
name: "Some Store",
email: "store#example.com",
user_id: #user&.id
)
This is using the safe navigation operator (&.) to "ignore method calls" on nil values.
However, why are you fetching a possibly-nil object and then trying to call a method on it? Given the context of your original post, you could instead just do:
Store.create(
name: "Some Store",
email: "store#example.com",
user: #user
)
This will work fine, regardless of whether or not the #user exists.
...Or perhaps even, what you really intend to do is something like:
Store.create(
name: "Some Store",
email: "store#example.com",
user: User.find_or_create_by(name: 'Sam')
)
Related
I am trying to update a hash that is being made when a csv is uploaded by a user, so that it saves the added key/value pair to the db.
How can I update the hash being made in the create_by_location method with the method check_enable_recordings
user model
before_save :check_enable_recordings
def check_enable_recordings
x = tenant.enable_recording_extensions
Rails.logger.debug("check if true #{x}" )
if x
user = User.new(
recorded: "1"
)
end
end
def self.create_by_location(location,hash)
user = User.new(
first_name: hash[:firstname],
last_name: hash[:lastname],
email: hash[:email],
)
end
Perhaps you're looking for something like:
before_save :check_enable_recordings
def check_enable_recordings
self.recorded = 1 if tenant.enable_recording_extensions
end
def self.create_by_location(location,hash)
user = User.new(
first_name: hash[:firstname],
last_name: hash[:lastname],
email: hash[:email],
)
end
BTW, you don't seem to use the location argument anywhere. Maybe you're not showing us all the code.
Also, if you have control over the construction of the hash argument, you should probably change firstname to first_name and lastname to last_name so you can just do:
def self.create_by_location(location,hash)
user = User.new(hash)
end
Trying to write rspec test for code below. The spec suppose to test that the right key is passed. Do I make a hash let variable and expect the key to equal the variable key. I am not really sure how to set it up.
def something_att
Hash[
%i(name age).map do |k|
[k, send(k)]
end
]
end
first version something_att without parameters
it "test something_att" do
hash_result = something_att
expect(hash_result).to eq(
name: 'john doe',
age: 40
)
end
second version something_att with parameters
(but you have to change how something_att methods)
I prefer to use this since it's more flexible to use with params you can set
it "test something_att" do
hash_data = { name: 'john doe', age: 20 }
hash_result = something_att(hash_data)
expect(hash_result).to eq(
name: 'john doe',
age: 40
)
end
As you said, The spec supposes to test that the right key is passed.
describe "#something_att" do
it "should have right keys" do
expect(something_att.keys.sort).to eq ['name', 'age'].sort
end
end
I'm struggling to understand the relationship that owner = create(:user, device_token: device_token) has to owner: {device_token: device_token}, I usually use user_id for this association.
2. What is the device_token method in the controller is doing.
describe 'POST /v1/events' do
it 'saves the address, lat, lon, name, and started_at date' do
date = Time.zone.now
device_token = '123abcd456xyz'
owner = create(:user, device_token: device_token)
post '/v1/events', {
address: '123 Example St.',
ended_at: date,
lat: 1.0,
lon: 1.0,
name: 'Fun Place!!',
started_at: date,
owner: {
device_token: device_token
}
}.to_json, { 'Content-Type' => 'application/json' }
event = Event.last
expect(response_json).to eq({ 'id' => event.id })
expect(event.address).to eq '123 Example St.'
expect(event.ended_at.to_i).to eq date.to_i
expect(event.lat).to eq 1.0
expect(event.lon).to eq 1.0
expect(event.name).to eq 'Fun Place!!'
expect(event.started_at.to_i).to eq date.to_i
expect(event.owner).to eq owner
end
end
Controller Code:
def create
#event = Event.new(event_params)
if #event.save
render
end
end
private
def event_params
{
address: params[:address],
ended_at: params[:ended_at],
lat: params[:lat],
lon: params[:lon],
name: params[:name],
started_at: params[:started_at],
owner: user
}
end
def user
User.find_or_create_by(device_token: device_token)
end
def device_token
params[:owner].try(:[], :device_token)
end
end
There's a number of ways you can identify uniquely identify a record in a database. Using the id field is the most common - but if you've got another way to uniquely identify a user, then you can use that, too. Normally, you don't show a user what their ID number is in the database. But, if you want to give them a way to uniquely identify themselves, you could create another field which is unique for each user - such as a membership_number or similar. It seems like in your case, device_token is a field that uniquely identifies a user.
So, your database cares about the user_id field - that's what it uses to tie an Event to a specific User (aka, the owner). If your users knew their id, then they could pass in that, rather than their device_token, and everything would be fine. But they don't know it.
So, they pass in their devise_token. You use that to fetch the user from the database, and then you know that user's id. Then, you can store that user's id in the user_id field of your Event model.
def user
User.find_or_create_by(device_token: device_token)
end
This method is the one that gets a user based on a devise_token. And then this method:
def event_params
{
address: params[:address],
ended_at: params[:ended_at],
lat: params[:lat],
lon: params[:lon],
name: params[:name],
started_at: params[:started_at],
owner: user
}
end
In particular, the line: owner: user calls that method above. From that point, Rails handles it under the hood and makes sure your user_id is set correctly.
UPDATE AFTER YOUR COMMENT:
device_token is being passed in as a parameter. It is also the name of a field in the User model. So, a single row in the user table might look like this:
id: 24, first_name: fred, last_name: flintstone, device_token: 123abc, address: bedrock, etc.
the method:
def user
User.find_or_create_by(device_token: device_token)
end
is saying: go to the User's table in the database, try to find a User which has a device_token that has the value that was passed in as a parameter, and if we can't find one, then create one.
So in this line: User.find_or_create_by(device_token: device_token), the first reference to device_token is the key of a hash, and it refers to the field called device_token in your User model.
The second reference to device_token is a call to this method:
def device_token
params[:owner].try(:[], :device_token)
end
which fetches the device_token from the parameters passed in. This method basically says: Look in the params hash at the value inside the owner key. See if the owner key contains a device_token. If it does, return that device_token, and if it doesn't return nil. It does this using the try method, which you can read more about here: http://apidock.com/rails/Object/try
I've been trying to do this for the past 10 hours, but it's been useless.
For example:
Event.where(login_screen: Time.now-8.days ..Time.now)
I have an Event table and login_screen is one of the column names. I'm listing them in a drop-down menu and I'd like to take the event names as a variable. It's in the request params like this: params[:segmentation][:first_event]. When I tried to give it like:
Event.where(params[:segmentation][:first_event] Time.now-8.days ..Time.now)
...it didn't work. I tried to use to_sym but that didn't help either.
How can I use a variable as a symbol?
Another question:
What's the difference between :hello and hello: ?
It's alternative syntax for ruby hashes with symbols as keys
Event.where(login_screen: Time.now-8.days ..Time.now)
is the same as
Event.where(:login_screen => Time.now-8.days ..Time.now)
So, if you store key in variable you need use 'hash rocket' syntax:
Event.where(params[:segmentation][:first_event] => Time.now-8.days ..Time.now)
these are the different ways to pass arguments in where clause:--
User.where(["name = ? and email = ?", "Joe", "joe#example.com"])
User.where(["name = :name and email = :email", { name: "Joe", email: "joe#example.com" }])
User.where("name = :name and email = :email", { name: "Joe", email: "joe#example.com" })
using hash:-
User.where({ created_at: (Time.now.midnight - 1.day)..Time.now.midnight })
User.where({ name: ["Alice", "Bob"]})
User.where({ name: "Joe", email: "joe#example.com" })
I want to use this function from mongoid:
person.update_attributes(first_name: "Jean", last_name: "Zorg")
But I want to pass in all the attributes from another variable. How do I do that?
Edit: Thanks everyone for your reply. I'm new to ruby so at first I thought I just made a silly mistake with this. The bug was in a completely different place, the correct code, for your enjoyment:
def twitter
# Scenarios:
# 1. Player is already signed in with his fb account:
# we link the accounts and update the information.
# 2. Player is new: we create the account.
# 3. Player is old: we update the player's information.
# login with a safe write.
puts "twitter"
twitter_details = {
twitter_name: env["omniauth.auth"]['user_info']['name'],
twitter_nick: env["omniauth.auth"]['user_info']['nickname'],
twitter_uid: env["omniauth.auth"]['uid']
}
if player_signed_in?
#player = Player.find(current_player['_id'])
else
#player = Player.first(conditions: {twitter_uid: env['omniauth.auth']['uid']})
end
if #player.nil?
#player = Player.create!(twitter_details)
else
#player.update_attributes(twitter_details)
end
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Twitter"
sign_in_and_redirect #player, :event => :authentication
end
The update_attributes method takes a Hash argument so if you have a Hash, h, with just :first_name and :last_name keys then:
person.update_attributes(h)
If your Hash has more keys then you can use slice to pull out just the ones you want:
person.update_attributes(h.slice(:first_name, :last_name))
if you look at the source code of Mongoid, you'll see the definition of update_attributes in the file
.rvm/gems/ruby-1.9.2-p0/gems/mongoid-2.3.1/lib/mongoid/persistence.rb
# Update the document attributes in the datbase.
#
# #example Update the document's attributes
# document.update_attributes(:title => "Sir")
#
# #param [ Hash ] attributes The attributes to update.
#
# #return [ true, false ] True if validation passed, false if not.
def update_attributes(attributes = {})
write_attributes(attributes); save
end
It takes a Hash -- that means you can use a Hash as the variable that's passed in.
e.g.
my_attrs = {first_name: "Jean", last_name: "Zorg"}
person.update_attributes( my_attrs )
What's happening in the update_attributes method and, indeed, across the Rails platform is variables get put into a hash internally, when necessary.
So the following are equivalent:
person.update_attributes(first_name: "Jean", last_name: "Zorg")
person.update_attributes({first_name: "Jean", last_name: "Zorg"})
person.update_attributes(name_hash)
Where name_hash is:
name_hash = {first_name: "Jean", last_name: "Zorg"}