Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically include module into Model at runtime?

I have a "transaction" (extends ActiveRecord::Base). I have two different types of transactions, a "Purchase" or a "Donation". There's enough overlap between the two that there's no need to create two separate database tables, so I just have one table for the transactions with an "item_type" column.

However, there are different methods and validations for a Purchase and Donation, so it makes sense to break them up into two different controllers and models. Instead of creating ActiveBase models (minus the tables), I'm trying to use Modules for each of these.

Here is what the Purchase module looks like.

module Purchase
  def self.included(base)
    base.validates :amount, 
      :presence => true
  end

  def testing
    "testing"
  end
end

Here is how one is created: (this code is in the create action of the Purchases controller)

@purchase = Transaction.new(params[:purchase]).extend(Purchase)

I am able to call @purchase.testing and it returns "testing". However, that validation is not being run.

If I include the module in the traditional fashion on the Transaction model, with "include Purchase", then they work.

Any ideas how I can make this workflow happen? I followed this a bit: http://paulsturgess.co.uk/articles/84-how-to-include-class-and-validation-methods-using-a-module-in-ruby-on-rails

like image 945
Brian Avatar asked Jul 06 '12 20:07

Brian


2 Answers

If you only want to work with a single table and multiple columns, I think the preferred approach is single table inheritance. You can read about it here if you search for single table inheritance. With regards to your question, though, about how to include the module, I think you'll want to include the module in the metaclass for @purchase so it isn't included in all subsequent Transactions. Kind of like this (can definitely be shortened):

@purchase = Transaction.new(params[:purchase])
class << @purchase
  include Purchase
end

Then, your validation should work.

like image 66
Adam Avatar answered Nov 13 '22 09:11

Adam


Your included hook will not be run when you do .extend(Purchase). You need an extended hook for that. Inside the extended hook, you can call the Rails validation helpers on the eigenclass of the new Transaction object (similar to what @Adam demonstrated).

like image 3
Alex D Avatar answered Nov 13 '22 09:11

Alex D