Update_attribute not working in rails 4 - ruby-on-rails

I have code in model level to update attribute to TeacherPayslip.
TeacherPayslip.rb (Model)
def net_salary
#teacher_id = self.id
#da = (self.basic * self.da)/100
#hra = (self.basic * self.hra)/100
#gs = #da + #hra + self.basic
#pf = (#gs * self.pf)/100
#netsalary = #gs - #pf + self.special_allowance + self.bonus
#a = TeacherPayslip.find(#teacher_id)
#raise #a.inspect
raise #a.update_attribute('net_salary',#netsalary).inspect
end
Here, when I raise #netsalary shows like 9789. But after updating(#a.update_attribute('net_salary',#netsalary) net_salary shows true value. instead of 9789.

update_attribute return a boolean (true or false). Beside, using update_attribute instead of update_attributes will cause the callbacks to not be fired.
You should use:
#a.update_attributes(net_salary: #netsalary)
#a.net_salary # will hold the new value

Related

how append an object to association relation in rails?

In a rails 4.1 application I need to add an object to an "AssociationRelation"
def index
employee = Employee.where(id_person: params[:id_person]).take
receipts_t = employee.receipts.where(:consent => true) #gives 3 results
receipts_n = employee.receipts.where(:consent => nil).limit(1) #gives 1 result
#I would need to add the null consent query result to the true consent results
#something similar to this and the result is still an association relation
#receipts = receipts_t + receipts_n
end
Is there a simple way to do this?
A way of solving this:
def index
employee_receipts = Employee.find_by(id_person: params[:id_person]).receipts
receipts_t = employee_receipts.where(consent: true)
receipts_n = employee_receipts.where(consent: nil).limit(1)
#receipts = Receipt.where(id: receipts_t.ids + receipts_n.ids)
end
Unfortunately .or() can't be used here because it's only available from Rails v5.0.0.1
you could do this way
receipts_t_ids = employee.receipts.where(:consent => true).pluck(:id)
receipts_n_ids = employee.receipts.where(:consent => nil).limit(1).pluck(:id)
#receipts = Receipt.where(id: receipts_t_ids + receipts_n_ids)
To avoid extra queries and keeping arrays in memory, you can use or
Like this:
def index
employee_receipts = Employee.find_by(id_person: params[:id_person]).receipts
#receipts =
employee_receipts.where(consent: true).or(
employee_receipts.where(consent: nil).limit(1)
)
end

How to call a class method in \lib from model in rails?

I'm trying to generate stats for a character created by a form. The user inputs the name, race, class, alignment, and whether or not the stats will be generated randomly, or prioritized (values being assigned from highest to lowest). The form works flawlessly, as I can see the output in a view.
What I am now trying to do is call a method from a class in /lib in the model that will generate the stats; however, I keep getting the following error (I can't post pictures):
NoMethodError in CharactersController#create
undefined method `[]' for nil:NilClass
Extracted source (around line #14):
12 before_save do
13 generate_stats
14 self.strength = #character_stats[:strength]
15 self.dexterity = #character_stats[:dexterity]
16 self.constitution = #character_stats[:constitution]
17 self.intelligence = #character_stats[:intelligence]
Here is a copy of some of my code:
In controllers\characters_controller.rb
class CharactersController < ApplicationController
def create
#character = Character.new(character_info_params)
#character.name = params[:character][:name].capitalize
#character.alignment = "#{params[:character][:alignment_lr]} #{params[:character][:alignment_ud]}"
if #character.save
redirect_to #character
else
render 'new'
end
end
private
def character_info_params
params.require(:character).permit(:name, :race, :class_, :alignment)
end
end
In models\character.rb
class Character < ActiveRecord::Base
require 'random_stats_generator'
attr_accessor :rand_stat_gen
def generate_stats
if #rand_stat_gen == true
#character_stats_inst = RandomStatGenerator.new
#character_stats = #character_stats_inst.generate
end
end
before_save do
generate_stats
self.strength = #character_stats[:strength]
self.dexterity = #character_stats[:dexterity]
self.constitution = #character_stats[:constitution]
self.intelligence = #character_stats[:intelligence]
self.wisdom = #character_stats[:wisdom]
self.charisma = #character_stats[:charisma]
end
#validation passed this point
end
In initializers\stat_builders.rb
require "./lib/random_stat_generator.rb"
In lib/random_stat_generator.rb
class RandomStatGenerator
def initialize
#strength = :strength
#dexterity = :dexterity
#constitution = :constitution
#intelligence = :intelligence
#wisdom = :wisdom
#charisma = :charisma
#character_stats = HashWithIndifferentAccess.new()
end
def self.generate
roll_stats
end
def roll(stat)
#roll_value_1 = (1 + (rand(6)))
#roll_value_2 = (1 + (rand(6)))
#roll_value_3 = (1 + (rand(6)))
#roll_value_4 = (1 + (rand(6)))
#roll_array = [#roll_value_1,#roll_value_2,#roll_value_3,#roll_value_4]
#roll_array = #roll_array.sort_by {|x| x }
#roll_array = #roll_array.reverse
stat = #roll_array[0] + #roll_array[1] + #roll_array[2]
end
def roll_stats
#strength = roll(#strength)
#dexterity = roll(#dexterity)
#constitution = roll(#constitution)
#intelligence = roll(#intelligence)
#wisdom = roll(#wisdom)
#charisma = roll(#charisma)
#character_stats[:strength] = #strength
#character_stats[:dexterity] = #dexterity
#character_stats[:constitution] = #constitution
#character_stats[:intelligence] = #intelligence
#character_stats[:wisdom] = #wisdom
#character_stats[:charisma] = #charisma
return #character_stats
end
end
To me, it looks like the method isn't returning anything, or isn't being called at all.
I've tried a lot of solutions that I've come across online, none of them working. There may be some things that don't really make sense that are left over from these solutions. I'm only just starting with rails, so I'm still trying to get used to everything.
Thanks a lot for your help.
Ruby has really powerful functions for manipulating both hashes and arrays.
Typing out duplicate assignments like:
self.strength = #character_stats[:strength]
self.dexterity = #character_stats[:dexterity]
self.constitution = #character_stats[:constitution]
Is pretty dull. So instead we can simply rewrite the methods to pass hashes around.
class RandomStatGenerator
# This is just a constant containing all the stats we want to generate.
STATS = [:strength, :dexterity, :constitution, :intelligence, :wisdom, :charisma]
# Create a hash with random roll values for each stat
def self.roll_stats
# This is kind of scary looking but actually just creates an
# hash from an array of keys
Hash[STATS.map {|k| [k, self.roll ] } ]
end
private
def self.roll
# Create an array with 4 elements (nil)
ary = Array.new(4)
# We then replace the nil value with a random value 1-6
ary = ary.map do
(1 + (rand(6)))
end
# sort it and drop the lowest roll. return the sum of all rolls.
ary.sort.drop(1).sum
# a ruby ninja writes it like this
Array.new(4).map { 1 + rand(6) }.sort.drop(1).sum
end
end
Output:
irb(main):032:0> RandomStatGenerator.roll_stats
=> {:strength=>14, :dexterity=>14, :constitution=>14, :intelligence=>13, :wisdom=>10, :charisma=>9}
But if you don't intend to actually create instances of a class, than you should use a module instead.
Rails models can either be created with a hash or you can replace its values with a hash:
Character.new(RandomStatGenerator.roll_stats)
#character.assign_attributes(RandomStatGenerator.roll_stats)
So we can use this in Character#generate_stats:
def generate_stats
assign_attributes(RandomStatGenerator.roll_stats)
end
You should use ActiveModel callbacks with extreme prejudice. It is often quite a challenge to regulate where in your application and when in the model lifetime. Since before_save runs after validations means that any validations like validates_presence_of :constitution will fail.
In your case it might be better to simply do it in the controller or use:
before_validation :generate_stats, if: -> { new_record? && #rand_stat_gen }
I would like to suggest the following organisation fo your library
# Use a module at top level
module RandomStatGenerator
STATS = [:strength, :dexterity, :constitution, :intelligence, :wisdom, :charisma]
# Use a class Stats if you need to but I don't see why...
class Stats
def initialize
RandomStatGenerator::STATS.each do |stat|
# Below line will do #stat = :stat
instance_variable_set("##{stat.to_s}", stat)
#character_stats = HashWithIndifferentAccess.new()
end
def roll_stats
#character_stats = RandomStatGenerator.roll_stats
end
end
module_function
# below lines will be considered as module functions
# => call RandomStatGenerator.function_name
def roll
roll_value_1 = (1 + (rand(6)))
roll_value_2 = (1 + (rand(6)))
roll_value_3 = (1 + (rand(6)))
roll_value_4 = (1 + (rand(6)))
roll_array = [roll_value_1,roll_value_2,roll_value_3,roll_value_4]
roll_array = roll_array.sort_by {|x| x }
roll_array = roll_array.reverse
roll_array[0] + roll_array[1] + roll_array[2]
end
def roll_stats
character_stats = {}
STATS.each do |stat|
character_stats[stat] = RandomStatGenerator.roll
end
return character_stats
end
end
Then in your character.rb
def generate_stats
#character_stats = RandomStatGenerator.roll_stats
end

How to get net salary value from database in rails 4

I am having problem getting net-salary value. I have teacher_payslip model. For calculating net-salary,I have written callback.
In TeacherPayslip.rb
#callbacks
after_create :net_salary
def net_salary
#teacher_id = self.id
#da = (self.basic * self.da)/100
#hra = (self.basic * self.hra)/100
#gs = #da + #hra + self.basic
#pf = (#gs * self.pf)/100
#netsalary = #gs - #pf + self.special_allowance + self.bonus
#raise #netsalary.inspect
#a = TeacherPayslip.find(#teacher_id)
#raise #a.inspect
#a.update_attributes(:net_salary => #netsalary)
end
The net_salary value was updated in TeacherPayslip Model.
In Rails console, I have tried some code
TeacherPayslip.last.net_salary
Shows true value instead of net_salary value
I don't know, Why this query shows true value.. Please Help Me...
It's a naming collision. You're overwriting the method net_salary.
The return value of true is the return value of update_attributes.
To fix this rename your method and the callback to set_net_salary.
user it
TeacherPayslip.last.net_salary

Choosing which form field to submit

I have a CommercialDocument model which have a discount_amount attribute and a discount_amount_with_tax virtual attribute.
Here is how I defined this in my model :
def discount_amount_with_tax
discount_amount * (1 + tax.rate / 100)
end
def discount_amount_with_tax=(amount)
self.discount_amount = amount.to_f / (1 + tax.rate / 100)
end
In my form, a user can fill in both discount_amount and discount_amount_tax :
= f.label :discount_amount
= f.text_field :discount_amount
= f.text_field :discount_amount_with_tax
I want to give the priority to the discount_amount_with_tax field, which means that discount_amount must not be taken into account unless the other field is empty.
My problem is that if I put nothing in the discount_amount_with_tax field, and let's say 10 in discount_amount, then discount_amount will be equal to 0, which is clearly not what I want.
How can I fix this ?
Any help would be greatly appreciated.
"".to_i
# => 0
A blank string converts to a zero integer. Therefore:
def discount_amount_with_tax=(amount)
self.discount_amount = amount.to_f / (1 + tax.rate / 100)
end
# same as...
def discount_amount_with_tax=(0)
self.discount_amount = 0 / (...)
end
# 0 / anything except zero = 0
# self.discount_amount = 0 no matter what
During mass-assignment, discount_amount_with_tax= is called. A blank form input is passed as an empty string, which Active Record then converts to an integer (zero). discount_amount_with_tax= sets discount_amount to zero regardless of discount_amount's previous value.
Easy way around this is to use a conditional:
def discount_amount_with_tax=(amount)
self.discount_amount = (amount.to_f / (1 + tax.rate / 100)) if amount > 0
end
Mind you, this is the easy way, not the ideal way. A better solution is to write custom setter logic in the controller in lieu of mass-assignment; basically to manually set these attributes in the controller.
I think you can use a before_validation callback here to set the discount_amount field.
before_validation :calculate_discount_amount
def discount_amount_with_tax
#discount_amount_with_tax ||= discount_amount * (1 + tax.rate / 100.0)
end
def discount_amount_with_tax=(amt)
#discount_amount_with_tax = amt
end
def calculate_discount_amount
self.discount_amount = discount_amount_with_tax / (1 + tax.rate / 100.0)
end
Just a reminder that you need to use 100.0 instead of 100 so that you'll be dividing by float and not by integer.

Ruby on Rails: Avoiding infinite loop when calling .save during an update

I have an order model that has_many :items. Each item has item.price for the cost of said item. I want to add up all of the item prices in the order for a order.total_price. Right now I'm doing that with
after_save :update_total_price, :if => "self.saved.nil? "
def update_total_price
self.total_price = Item.find(item_ids).inject(0){|sum,item| sum + (item.price * item.amount) } #amount is how many items there are
self.saved = 1
self.save if self.saved
end
This works just fine the first time that I put in the info, but if I try to edit the order, the total_price doesn't get updated because update_total_price doesn't get called because self.saved is not nil.
What can I do to make it so that updating the model will update it, but won't keep on doing an infinite loop of calling .save?
Why not have the update_total_price NOT save the data again.
just set the value in before_update:
before_save :update_total_price
def update_total_price
self.total_price = items.find(:all).inject(0){|sum,item| sum + (item.price * item.amount) }
end
after_save :update_total_price
def update_total_price
self.total_price = find_total_price
self.save_without_callbacks
end
def find_total_price
Item.find(item_ids).inject(0){|sum,item| sum + (item.price * item.amount)
end

Resources