first_or_initialize bug or what happened - ruby-on-rails

I try to insert record but Active Record do same magick that i don't understand !?!?!?
My test code:
UserToken.where(:user_id=>2).first_or_initialize.tap do |user|
user.token = 'token',
user.type_id = 0,
user.user_id = 2
user.save!
end
Result:
UserToken Load (56.9ms) SELECT `user_tokens`.* FROM `user_tokens` WHERE `user_tokens`.`user_id` = 2 LIMIT 1
(56.4ms) BEGIN
(56.4ms) UPDATE `user_tokens` SET `type_id` = 0, `token` = '---\n- token\n- 0\n- 2\n', `updated_at` = '2013-06-27 20:19:22' WHERE `user_tokens`.`id` = 19
(56.3ms) COMMIT
=> #<UserToken id: 19, user_id: 2, token: ["token", 0, 2], type_id: 0, created_at: "2013-06-27 20:14:11", updated_at: "2013-06-27 20:19:22">
Why update token token = '---\n- token\n- 0\n- 2\n', token: ["token", 0, 2] i just try to record 'token' not array ?!?!?!?

you shouldn't have those commas there
UserToken.where(:user_id=>2).first_or_initialize.tap do |user|
user.token = 'token'
user.type_id = 0
user.user_id = 2
user.save!
end
or with semicolon line enders:
UserToken.where(:user_id=>2).first_or_initialize.tap do |user|
user.token = 'token';
user.type_id = 0;
user.user_id = 2;
user.save!;
end
The way you have it you're passing the other two assignments to user.token. What you did ends up like this because ruby expression always have a return value and variables always return themselves:
user.token = 'token', 0, 2

Related

Callback not saving value

I am writing some tests for a relatively complicated action that I need to occur when a record is destroyed.
These are my CheckIn functions and a CheckIn has_one :weigh_in:
def weight_loss
prev_ci = CheckIn.joins(:weigh_in)
.where(client_id: self.client_id)
.where('week < ?', self.week)
.where('weigh_ins.current_weight IS NOT NULL')
.order('week desc').first()
return 0 if self.weigh_in.nil? or prev_ci.nil? or prev_ci.weigh_in.nil?
change = prev_ci.weigh_in.current_weight.to_f - self.weigh_in.current_weight.to_f
return change.round(2)
end
def prev_ci
prev_ci = CheckIn.joins(:weigh_in)
.where(client_id: self.client_id)
.where('week < ?', self.week)
.where('weigh_ins.current_weight IS NOT NULL')
.order('week desc').first()
return prev_ci
end
def post_ci
post_ci = CheckIn.joins(:weigh_in)
.where(client_id: self.client_id)
.where('week > ?', self.week)
.where('weigh_ins.current_weight IS NOT NULL')
.order('week desc').first()
return nil if post_ci.nil? or post_ci.weigh_in.nil?
return post_ci
end
In my WeighIn model I have the following callbacks:
class WeighIn < ActiveRecord::Base
belongs_to :check_in
before_save :add_weightloss
after_destroy :adjust_weightloss
def add_weightloss
self.weightloss = 0
self.weightloss = self.check_in.weight_loss
end
def adjust_weightloss
post_ci = self.check_in.post_ci
unless post_ci.nil?
post_ci.weigh_in.weightloss = 0
post_ci.weigh_in.weightloss = post_ci.weight_loss
# Prints the correct value to pass the test (39.7)
p 6, post_ci.weigh_in
post_ci.weigh_in.save!
end
end
end
But my final test (for deletion) still fails:
RSpec.describe WeighIn, type: :model do
it "before_save weigh_in" do
ci1 = CheckIn.create!(client_id: 1, program_id: 1, week: 1)
ci2 = CheckIn.create!(client_id: 1, program_id: 1, week: 2)
ci3 = CheckIn.create!(client_id: 1, program_id: 1, week: 3)
ci4 = CheckIn.create!(client_id: 1, program_id: 1, week: 4)
wi1 = WeighIn.create!(check_in_id: ci1.id, client_id: 1, date: Date.today, current_weight: 100)
wi2 = WeighIn.create!(check_in_id: ci2.id, client_id: 1, date: Date.today, current_weight: 90)
wi3 = WeighIn.create!(check_in_id: ci4.id, client_id: 1, date: Date.today, current_weight: 70.5)
# Verifies functionality of before save
expect(wi1.weightloss).to eq 0
expect(wi2.weightloss).to eq 10
expect(wi3.weightloss).to eq 19.5
# Verifies fucntionality of update
wi_params = { check_in_id: ci4.id, client_id: 1, date: Date.today, current_weight: 60.3 }
wi3.update(wi_params)
expect(wi3.weightloss).to eq 29.7
# Verifies functionality of destroy
wi2.destroy
# Prints incorrect, old value, 29.7
p 'in test', wi3
expect(wi3.weightloss).to eq 39.7
end
end
It seems a though the functions are working properly but the record reverts back or is never saved / updated?
Can you try this to reload the object to check for a change?
expect(wi3.reload.weightloss).to eq 39.7

wrong number of arguments (given 0, expected 1) when calling update_attributes

I have a method that is called every time the user signs in to my app using facebook or gplus, where I update all the user fields on each sign in.
user = where(:email => email).first_or_create do |user|
user.uid = uid
user.email = email
user.provider = provider
user.save!
end
user.first_name = auth["first_name"]
user.last_name = auth["last_name"]
user.nickname = auth["first_name"]
user.name = auth["name"]
user.gender = auth["gender"]
user.role = "user"
user.latitude = 0
user.longitude = 0
user.update_attributes(user.attributes)
user
but i get the following error everytime this method is called, in a similar note, I get this error even when I try updating a single attribute
user.upate_attributes(:token => token)
wrong number of arguments (given 0, expected 1)
can't seem to figure out why
Why don't you just save the user instance
# ...
user.longitude = 0
user.save
or try this way
user.update_attributes(
first_name: auth["first_name"],
last_name: auth["last_name"],
nickname: auth["first_name"],
name: auth["name"],
gender: auth["gender"],
role: "user",
latitude: 0,
longitude: 0
)
Also, In your second command, there is a typo
user.upate_attributes(:token => token)
# Change to
user.update_attributes(:token => token)

Ruby hash returning strange values

I am returning a response of user fields in JSON. I am creating JSON as below.
def user_response(users)
users_array = []
users.each do |user|
uhash = {}
uhash[:id] = user.id,
uhash[:nickname] = user.nickname,
uhash[:online_sharing] = user.online_sharing,
uhash[:offline_export] = user.offline_export,
uhash[:created_at] = user.created_at,
uhash[:app_opens_count] = user.app_opens_count,
uhash[:last_activity] = user.last_activity,
uhash[:activity_goal] = user.activity_goal,
uhash[:last_activity] = user.last_activity,
uhash[:region] = user.region
users_array << uhash
end
users_array
end
But the response is pretty weird. The :id key in hash has an array of all the fields don't know why.
{
"nickname": "adidas",
"online_sharing": null,
"offline_export": null,
"created_at": "2016-08-26T09:03:54.000Z",
"app_opens_count": 29,
"last_activity": "2016-08-26T09:13:01.000Z",
"activity_goal": 3,
"region": "US",
"id": [
9635,
"adidas",
null,
null,
"2016-08-26T09:03:54.000Z",
29,
"2016-08-26T09:13:01.000Z",
3,
"2016-08-26T09:13:01.000Z",
"US"
]
}
That's due to your , at the end of each line
The problem consists of two things:
An assignment evaluates as the value being assigned:
puts (foo = 42) # => prints 42
Multiple values, separated with comma on the right hand side of an assignment form an array:
bar = 1, 2, 3
bar # => [1, 2, 3]
The new lines don't change that, so you basically do something like this:
sonne = (foo = :eins), (bar = :zwei), (baz = :drei), (qux = :vier)
sonne # => [:eins, :zwei, :drei, :vier]
The fix is indeed to remove the commas.
You have comma , at the end of each line
uhash[:id] = user.id,
Also, You may change the above code to:
def user_response(users)
users.map do |user|
user.attributes.slice(:id, :nickname, :online_sharing, :offline_export, :created_at, :app_opens_count, :last_activity, :activity_goal, :last_activity, :region)
end
end

Group by part of attribute in hash

I have a model called coverage that looks like this
1.9.3p429 :005 > Coverage.new
=> #<Coverage id: nil, postcode: nil, name: nil, created_at: nil, updated_at: nil>
Here is an example record:
1.9.3p429 :006 > Coverage.find(10)
Coverage Load (7.3ms) SELECT "coverages".* FROM "coverages" WHERE "coverages"."id" = $1 LIMIT 1 [["id", 10]]
=> #<Coverage id: 10, postcode: "N10", name: "N10 - Muswell Hill", created_at: "2013-05-22 14:42:37", updated_at: "2013-05-22 14:42:37">
I've got over 300 postcodes and I want to group them by some values I have in this array
group = ['N','E','EC','LS','TS']
So I would like to do
#postcodes = Coverage.all
run it through something with the above array to get the following hash
#postcode_hash = { 'N' => [ '#Active Record for N1', '#Active Record for N2' ], 'E' => [ '#Active Record for E1', '#Active Record for E2' ] }
# note: not complete should contain all index from the above array
You can use the .group_by{} method:
#postcodes = Coverage.all
#postcodes_hash = #postcodes.group_by{ |c| c.postcode.gsub(/[0-9]/, '') }
Take a look at the group_by documentation:
http://apidock.com/rails/Enumerable/group_by
There is the explicit version of above:
#postcode_hash = {}
group = ['N','E','EC','LS','TS']
group.each{ |code| #postcode_hash[code] = [] }
#postcodes = Coverage.scoped # similar to .all but better
#postcodes.map do |coverage|
code = coverage.postcode.gsub(/[0-9]/, '') # takes off all the numbers of the postcode
#postcode_hash[code] << coverage if #postcode_hash[code]
end

semicolon as statement separator in Rails Console

The Rails console doesn't seem to like multiple ruby statements on the same line separated by a semicolon. Whenever I do this, the next line starts with ?> and I find that only the first statement was executed. Do you have to put each statement on a separate line?
>> user = User.new
user = User.new
=> #<User id: nil, username: "", hashed_password: "", first_name: "", last_name: "", email: "", display_name: "", user_level: 0, created_at: nil, updated_at: nil, posts_count: 0>
>> user.username = "John"; hashed_password = "John"; first_name = "John"; last_name = "coltrane"; email = "John#coltrane.com"; display_name = "Johndispay"; user_level = 9;
user.username = "John"; hashed_password = "John"; first_name = "John"; last_name = "coltrane"; email = "John#coltrane.com"; display_name = "Johndispay"; user_level = 9;
?> user.save
user.save
=> true
Everything except user.username = "John"; was ignored
You need to say "user." so Ruby knows you mean to call the attribute assignment methods of the instance of user. Otherwise, you are just setting local variables called "hashed_password", etc.
>> user.username = "John"; user.hashed_password = "John"; user.first_name = "John"; user.last_name = "coltrane"; user.email = "John#coltrane.com"; user.display_name = "Johndispay"; user.user_level = 9;
Although, you could just pass a hash of the attributes you want to set on the new instance, like so
>> user = User.new(:username => "John", :hashed_password => "John", ...
It's the trailing ; on your input. When you put a ';' on the end IRB will assume you want to add another statement. If you leave it off, it will evaluate all the statements and return the return value of the last one.
Sometimes if the method I'm calling is going to return a large array I will do something like this...
a = Account.entries; a.size
This will save the values I need and just output the size of the array instead of trying to dump it to the console, which can take a long time if it's large.
are you sure you didn't mean
user.username = "John"; user.hashed_password = "John";
i tried
>> a = 1; b= 2
=> 2
>> a
=> 1
>> b
=> 2
when something doesn't work, you can use one rule: always reduce it to the simplest case.

Resources