views:

93

answers:

3

The interaction of the site is based on the input of user. For example, if the user selected "VISA" as payment method, he will be redirected to another controller/actions asking for credit card number. If the user selected "Paypal", then he/she will be redirected to an external website.

Instead of putting all logic in the action itself, I'm thinking of abstracting it in an object, for the sake of extensibility. Example.

class Payment < ActiveRecord::Base; end
class VisaPayment < Payment
  def process
    ... 
  end
end
class PaypalPayment < Payment
  def process(controller)
    ...
    controller.redirect_to "http://paypal.com"
  end
end


class OrdersController < ApplicationController
  def accept
    params[:select].constantize.new.process(self)
  end
end

This doesn't work because methods like "redirect_to" are protected. Is there any idiom or common pattern for this kind of delegation?

p.s. the code above is truly imagination, not excerpt of any actual coding

A: 

I would put that into a module and execute the right method via a case-switch:

case credit_card_system:
when "VISA" then: VisaToolbox::ExecuteTransaction(params)
when "MasterCard" then: MasterCardToolbox::ExecuteTransaction(params)
else
...
end

I would reside the other things in ONE controller...

Lichtamberg
A: 

I don't like the idea of having ActiveRecord code that invokes controller code. If you have to go that way, though, I would create a class method in payment that I will define your "process part" and then instance_eval it in the controller, like this:

class Foo
  def self.define_process(&block)
    @process = block
  end

  def self.apply_to(controller)
    controller.instance_eval(&@process)
  end
end

class SpecificFoo < Foo
  define_process do
    puts self.bar
  end
end

class Something
  attr_accessor :bar
end

something = Something.new
something.bar = 42

SpecificFoo.apply_to(something)

Notice that code in define_process' block/iterator/coroutine is executed with self pointing to something (the controller in your case) and you'll have access both to protected and private stuff.

Stefan Kanev
i decided not to invoke controller code from a model. but the design of your code is very interesting to me.. thanks!
alanho
+1  A: 

Keep your controller and model logic separate. You'll thank me later on.

class OrdersController < ApplicationController
  def accept
    payment_path = case params[:select]
      when 'visa': credit_card_controller_path(payment_type)
      when 'paypal': 'http://www.paypal.com'
    end

    redirect_to payment_path
  end
end

What I like about this approach is that it is readable and expressive. You know exactly what is happening. The code isn't being more clever than necessary.

BigCanOfTuna