I try to find how to best way implement chain of methods in Ruby( Rails application)
like this
class Foo
attr_accessor :errors
attr_accessor :shared_params
def initialize(..)
..
end
def call
check_params
calc_smthing
write_in_db
end
prviate
def check_params
..
end
def calc_smthing
..
end
def write_in_db
..
end
end
main ideas:
if something fail( or not fail but return false) in some step others steps doesnt call,
of course I dont want to add multiple ifs to check state and think about how implement it in one place
I need to save errors
It would be great to find way to share params between methods.
I can write in simple( ugly) way with ifs
but I try to find more elegant way or pattern because problem not so specific I think.
Thanks in advance.
Save errors and shared attributes to variables (how you do it now). Unite methods to chain of methods:
def call
check_params && calc_smthing && write_in_db
end
def valid?
errors.blank?
end
Related
Let's say I have the following method:
def run
#users.each do |u|
..
..
end
end
I have a lot of code in run so I am trying to refactor it and splitting it up into smaller methods. One of these methods is the following:
def finish_mutation
..
..
Rails.logger.info "Succesfully added #{u.name}"
end
This breaks because finish_mutation doesn't have access to the u variable. How can I create new methods that can access the u variable that I created in run?
You can simply create method taking parameter:
def finish_mutation(user)
# code
Rails.logger.info "Successfully added #{user.name}"
end
and call it, passing User instance:
finish_mutation(u)
it's easy to do you just add a parameter to your finish_mutation method like this :
def finish_mutation(param)
# .......
end
then you call your function like this :
def run
#users.each do |u|
..
..
finish_mutation(u) # <----- for example here you call your method
end
end
Sometimes passing your loop variable (as shown in the other answers) is the best answer. Sometimes you can DRY things up better by adding a method to whatever class 'u' is an instance of. So you might do
class User
def finish_mutation
# put your operation here
end
end
And then in your loop
u.finish_mutation
Obviously you need to think about which is the best way for a specific case.
Good day, i'm new to ruby. I want to know how to run a parent method from a method of a child class ?
in java it would be like
class Child
..
def something_else
super.something
end
end
and in php
parent::method_name();
And could you tell me how to do it in Ruby?
found only this, and it's kind of ugly using alias_method_chain
as Taiki suggested the comment in another thread stated
class B < A
alias :super_a :a
def a
b()
end
def b
super_a()
end
end
hope there are other ways...
UPDATE:
at long last, call super() instead of super_a(). not sure what it does completely though
I have a helper method which checks whether the collection of objects is empty? If not then it checks each one to make sure that the the existing event_id is not the #current_event.id.
Here is my crack at it:
def build_answers(question)
if question.answers.empty?
return question.answers.build
else
question.answers.each do |a|
if a.event_id != #current_event.id
return question.answers.build
end
end
end
end
Update: This helper method sets the form to build new children objects if the conditions pass. I've updated the example above. btw, it doesn't need to be a single line. I just wanted something cleaner than what I have above.
Without knowing what you're actually doing inside the blocks, it's difficult to give the best solution.
For the most part, all you could really do is select before executing the logic on the filtered collection, rather than testing the logic in the block.
e.g.
uncurrent_answers = questions.answers.select{|a| a.event_id != #current_event.id}
uncurrent_answers.each do |a|
#do whatever
end
IMHO it's a little bit neater, and perhaps more rubyish..
Well, I don't know why would you want to put conditions into a single line, but the else block could be rewritten into this:
question.answers.select {|answer| answer.event_id != #current_event.id }.each
{|ans| #.. logic with answer here }
I think you current method is responsible for too many things, my idea is to create a clase with a single responsibility of building answers. That would make your code more readable and also easy to test. A posible implementation would look something like :
def build_answers(question)
AnswerBuilder.build(question.answers, #current_event)
end
class AnswerBuilder
def initialize(answers, current_event)
#answers = answers
#current_event = current_event
end
def self.build(answers, current_event)
new(answers, current_event).build
end
def build
if answers.empty?
answers.build
else
create_allowed_answers
else
end
private
attr_reader :answers, :current_event
def create_allowed_answers
answers.each do |a|
if a.event_id != current_event.id
return answers.build
end
end
end
end
I'm trying total up all "amount" columns with a definition in the model like so:
def self.total
self.all.collect(&:amount).sum
end
With that, "Recipe.total" works as expected. However, I'm using a plugin that passes "Recipe.find(:all)", and I can't seem to pass that to the method to find the total. That is:
Recipe.find(:all).total # doesn't work
Is there a way to define the method in my model differently to make Recipe.find(:all).total work like Recipe.total?
You can write your method as:
def self.total
self.sum(:amount)
end
And then you can use it also with named scopes:
Recipe.total # without any scopes
Recipe.my_custom_named_scope.total # with your custom named scope
Another variant is to override find method for that model:
def self.find(*args)
result = super
if args[0] && args[0] == :all
def result.total
self.sum(&:amount)
end
end
result
end
Then you get exactly what you want, you'll be able to write Recipe.find(:all).total.
Check out the Calculation Module
It has methods for: sum,average,count, etc ...
Its baked into ActiveRecord.
So you would want to write:
Recipe.sum(:total)
Have fun!
I'm developing an online store, and the customer needs the ability to delete an order and have its products automatically restocked (e.g., for test orders). Here's my first try at implementing this:
class Order < ActiveRecord::Base
def destroy_and_restock
restock_products
destroy
end
protected
def restock_products
line_items.each do |li|
li.product.quantity_on_hand += li.quantity
li.product.save
end
end
end
But what if I need to create another destroy_and_x method later? Why not allow that X to be passed as a parameter to the destroy() method? So now I'm thinking of going with this:
alias :old_destroy :destroy
def destroy(options = {})
if options['restock'] == true
restock_products
end
old_destroy
end
protected
def restock_products
line_items.each do |li|
li.product.quantity_on_hand += li.quantity
li.product.save
end
This is more extensible, but makes me feel somewhat dirty. Am I wrong to feel dirty? Is there a better way of doing this?
I'd say "yes, this is dirty." Your intention isn't to modify the behavior of the 'destroy' method, but rather to do some domain-specific work, then run destroy. Your first approach is great -- define a method that does what you want, and invoke destroy as needed. I think that 'wrapping' or 'monkey-patching' a method, as you're considering, is a technique that's best applied when standard OO approaches can't be used -- eg, when you need to modify/augment behavior in a class that is defined and used outside of your realm of control.
Even if you are considering modifying the behavior of the destroy method itself, I'd suggest overriding the method here, rather than wrapping it:
def destroy(options = {})
restock_products if options['restock']
super() # I think parens are necessary here, to avoid passing options up the chain
end
How about just use a block? Then you dont have to pull your hair apart while designing this in the class and you can do more as and when you need to:
def destroy_after &block
yield if block
destroy
end
Then call it like this:
order.destroy_after { order.restock_products }
I can not think of a good name for this function... but I hope you get the idea.
Horace, I misunderstood your question. I think you are looking for this:
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Now you can keep your method protected and add as many before_destroy things as you like. Hope this works for you without overriding destroy.
Best of luck.
If monkey patching doesn't let you sleep at night, you can achieve the same thing by subclassing. When I'm in the need of a quick hack, or a quick debug hack, I monkey patch as well.