How to properly assign value to model column? - ruby-on-rails

In my console I have one thing that is working and I need to make a method for it.
#sale.weight = #sale.fisherman.fish.sum(:weight)
Here I assign right part to the column weight on model Sale.
The question is how to write a method for this?
The following approach didn't work out for me. In this case the column remains nil after calculation.
class Sale < ActiveRecord::Base
def calculate_total_weight
weight = fisherman.fish.sum(:weight)
end
end

Ok, need to use self, otherwise weight will be treated as local variable. Like :
class Sale < ActiveRecord::Base
def calculate_total_weight
self.weight = fisherman.fish.sum(:weight)
end
end
Follow Why does attr_accessor in module require 'self.'? to know the reason of using self explicitly.

Related

Rails how to find id by another attr in a callback model?

I'm looking to create a callback where update a object if find the id attribute of another model.
in this case if find update Odata model if find the order_id.
someone know how to find the object based on another model id attribute?
class Order < ActiveRecord::Base
after_update :update_odata
def update_odata
order = Order.find_by_id(attributes['id'])
od = Odata.find_by_id(attributes['order_id'])
od.shipping_cost = order.shipping_cost
od.shipping_method = order.shipping_method
od.status = order.status
od.feedback_id = order.feedback_id
od.track_number = order.track_number
od.seller_name = order.seller_name
od.buyer_name = order.buyer_name
od.save
end
end
In general you should check the docs and at least make an effort to learn the tools you're using before resorting to asking for someone to help explain it to you on StackOverflow.
To answer your question, find(1) is effectively a shortcut method for find_by(id: 1). Thusly, if you want to find an order by customer_id you could do this: Order.find_by(customer_id: 42).
Or, if you're trying to make this contingent on order (making some assumptions based on how Rails apps are built vs this unusual attributes stuff you have in your example):
order = Order.find(params[:id])
od = Odata.find_by(order_id: order.id)
In which case, you should probably just use relations:
class Order < ApplicationRecord
has_one :odata
end
class Odata < ApplicationRecord
belongs_to :order
end
# controller:
order = Order.find params[:id]
od = order.odata
If you wanted to do exactly what you are above, which is probably a bad path to go down, you would probably want to do something like this:
class Order < ApplicationRecord
has_one :odata
def attributes_for_odata
%w{ shipping_cost shipping_method status feedback_id track_number seller_name buyer_name }
end
def update_order_data
odata.update attributes.slice(*attributes_for_odata)
end
end

Ruby on rails associations parents

I would like to know, whatever the association is (simple belongs_to, polymorphic ...), when I make an association like :
class Toto < ActiveRecord::Base
belongs_to :test_one
belongs_to :test_two
end
class TestOne < ActiveRecord::Base
has_many :totos
end
class TestTwo < ActiveRecord::Base
has_many :totos
end
and then
test_one = TestOne.create
test_two = TestTwo.create
test1 = test_one.totos.create
test2 = test_two.totos.create
I would like to know into a callback of Toto what object instantiate me. In this case, it's obviously test_one and then test_two. I know I could check ids for example but the problem is when i do :
test3 = test_one.totos.create(test_two: test_two)
I can't know if test3 was created through test_one or test_two.
Thank you.
According to your example, I understand that you want to identify the type of object which is associated to your totos object (has_many :totos).
Since there are multiple different objects that might be associated to your totos object through the has_many and belongs_to associations, you might want to perform some kind of verification first to identify the type of the associated object.
First Answer:
This will only work if you know beforehand all the object types that has_many :totos
if test3.respond_to?(:test_one)
test = test3.test_one
elsif test3.respond_to?(:test_two)
test = test3.test_two
end
Second Answer:
I found this on Stackoverflow, and it somehow answeres your question. So if I rephrase the answer to:
def get_belongs_to(object)
associated = []
object.class.reflect_on_all_associations(:belongs_to).map do |reflection|
associated << object.try(reflection.name)
end
associated.compact
end
This method will return an array of all objects associated to your totos object. This will also work when totos belongs to multiple objects say test_one and test_two at the same time. So the following:
associated_objects = get_belongs_to(test3)
and in your case associated_objects[0] will yield the object you desire.
Hope this helps.
Rails does not persist the data you're looking for, so you'll have to store it yourself if you want it. This means you'll need a migration for the new field:
rails generate migration AddOriginalParentTypeToTotos original_parent_type:string
rake db:migrate
You can then override the assignment methods so that the first parent assigned will assign the original_parent_type attribute (and it will remain the same once assigned):
class Toto < ActiveRecord::Base
def test_one=(val)
self[:original_parent_type] ||= 'test_one'
super
end
def test_one_id=(val)
self[:original_parent_type] ||= 'test_one'
super
end
def test_two=(val)
self[:original_parent_type] ||= 'test_two'
super
end
def test_two_id=(val)
self[:original_parent_type] ||= 'test_two'
super
end
end
You can then use send to add an original_parent method:
class Toto < ActiveRecord::Base
def original_parent
send(original_parent_type) if original_parent_type
end
end

How to display this instance method from rails model?

I have two rails models, Usage & Price. So far they look like this:
class Usage < ActiveRecord::Base
has_one :price
def spend
usage.amount * price.amount
end
end
and
class Price < ActiveRecord::Base
belongs_to :usage
end
I'm trying to call the "spend" by doing this in the console:
Usage.create(amount:100)
Price.create(amount:0.1)
usage=Usage.find(1)
puts usage.price
Where am I going wrong??
You need to create the price through the usage model to have the association work.
usage = Usage.create(amount:100)
usage.create_price(amount:0.1)
As the price belongs to usage, you should first create the usage object, and then using that you can create the price object.
usage = Usage.create(amount:100)
price = usage.price.create(amount:0.1)
Then you will get that price related to the usage model.
Then in the usage model you can write,
class Usage < ActiveRecord::Base
has_one :price
def spend
self.amount * (self.price.amount)
end
end
You can call the association like above, self.price.amount where "self" is the Usage object.
The problem lay in two things I was doing. Thanks to blamattina for pointing out this is how you create new associated objects after they've been defined in the model:
u = Usage.create(amount:100)
u.create_price(amount:0.1)
Also, in the model itself, when referring to the parent class within a model's instance method, the class should be referred to as self:
def spend
self.amount * price.amount
end
That last bit was where I was going wrong, and spend can be easily called with u.spend!

same active record dataset across many instances

i have a model say modela with a calculate field. like below
class modelA < ActiveRecord::Base
attr_accessor : calc_field
def calc_field
#stuff = modelb.all
//do fancy things with stuff
end
end
now my problem is if i have 100 records of modelA. the modelb.all gets called a 100times. but its the same dataset each time. but the query gets sent out a 100 times.
is there anyway/anywhere i can declare #stuff globally so its gets shared across all instances of modelA. so it gets called only once.
There are numerous ways on how to tackle this problem.
Solution A:
class modelA < ActiveRecord::Base
attr_accessor : calc_field
def calc_field(modelb_info)
#stuff = modelb_info
//do fancy things with stuff
end
end
And then in your code work flow
model_info = ModelB.all
model_a_array.collect{|model_a| model_a model_info}
Solution B
class modelA < ActiveRecord::Base
##stuff ||= ModelB.all
end
You could use a class variable.
def calc_field
##stuff ||= modelb.all
//do fancy things with stuff
end
If you can modify the way of getting the ModelA instances, I would suggest this:
modelas = ModelA.includes(:modelbs)
From then on, the database will not be hit anymore when you call (e.g.)
modelas.first.calc_field
I hope that helps

ActiveRecord Always Run Some Code After Retrieval

I'm trying to always run some code after .find or .where or whatever is used to retrieve objects.
For example, the following describes what I want, but does not work
Class Person < ActiveRecord::BA
#mortality=true
end
I want #mortality=true to run whenever a Person object is created
And based on my current understanding of ORM/ActiveRecord, a new object is created whenever retrieval is done. Hopefully that is correct.
You want to do this in the after_initialize method:
class Person < ActiveRecord::Base
def after_initialize
#mortality = true
end
end
Note that this is something you should avoid doing if possible because it happens on every object, even when you retrieve enormous result sets.
In this (albeit simple) case, you can do the assignment lazily by overriding the getter:
class Person < ActiveRecord::Base
def mortality
#mortality.nil? ? true : #mortality
end
end
(you can't use the nil gate ||= here because it filters false values as well)
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Look for after_find and after_initialize callbacks.

Resources