Accessing a class's variables from an included module

I created a mixin module called BaseProduct. This module has an initialize method in it. I am trying to use some meta-programming to set variables in initialize from the class that the module is in included in.. I pass a CSV row into the initialize method and it needs to set all the variables. Its a bit confusing trying to explain it, but here is what it looks like:

Note: the real world scenario is a bit more complicated using different objects, but I simplified it here to use a "product" scenario.

There's a lot of recommendation against overloading initialize for AR::Base classes: http://www.google.co.uk/search?sourceid=chrome&ie=UTF-8&q=rails+overload+initialize

Regardless, I think you're "solving" your problem the wrong way - you're *never* going to be able to instanciate an object without a CSV row; is that really what you want? How will the rest of your application work when you just want to query the DB for ShirtProduct.first?

And have you *really* designed to have a different table for different types of product? I can only imagine lots of pain that will be caused by that further down the line.

I'd suggest a look at polymorphism.

def initialize(attributes = nil,row)    super(attributes)    <table_fields_from_class>.each_with_index do |field,i|      #NOTE: row is an array of values (csv row to array)      eval("self.#{field} = row[#{i}]")    end end end

Don't use eval for this - send("#{field}=", value) will do the trick

I dont want to have to code an initialize method for every type of product and I thought it would be much easier to use metaprogramming and just pass an array of fields to a base initialize method. I may be way off here and there may be a completely different design pattern that would support this use case better, but this is what I originally came up with. I have tried several things with attr_accessor, CONSTANTS, etc.. but I have not been able to get the initialize method to see variables I set in the class. I am probably missing something simple as I don't fully understand the module's scope of access to the class... seems pretty straight forward vice-versa (class from module), but the other way around is confusing me a bit. let me know if you got any ideas. THANKS!

If you choose to go down the constant route, remember that constants are scoped lexically. Try something like self.class::SOME_CONSTANT

Michael's advice is also sensible - writing a create_from_csv_row method is probably sounder

Fred

Thanks guys for the quick responses.. looks like I will go back to the drawing board. I was trying to force it in the initialize method to save a line of code, but creating create_from_csv_row is a much better solution giving the problems you guys outlined here. Looks like polymorphism may solve some of my problems if I can rework my legacy db schema a bit.

Regardless, I think you're "solving" your problem the wrong way - you're *never* going to be able to instanciate an object without a CSV row; is that really what you want? How will the rest of your application work when you just want to query the DB for ShirtProduct.first?

I actually included a check which I left out of the simplified code, which will allow regular use of initialize method if I leave out the row:

def initialize(attributes = nil,row=nil)     super(attributes)     unless row.blank?       #ugly code goes here     end end

I was messing around with this some more and I figured out I can use class variables to achieve what I was trying to do. Set the class var in first line of the module: @@table_fields and then reset it in the class. The module has access to it in the class var in the initialize method after this. Although I am scrapping this design in favor of what was suggested.. figured I would still post what I came up with.

Michael Pavling wrote:

class ShirtProduct < ActiveRecord::Base end

class PantsProduct < ActiveRecord::Base end

And have you *really* designed to have a different table for different types of product? I can only imagine lots of pain that will be caused by that further down the line.

I'd suggest a look at polymorphism.

My original example is polymorphism using mixin modules/duck typing. Are you referring to STI? I read a while back that STI was a bad choice unless your fields are almost the same for every subclass. I am using sort of a hybrid form of STI as you can see in my current design below. Can you explain which implementation of polymorphism you were referring to? In my use case the fields can vary a good bit from type to type, and the types share about 20% of the fields with the parent class. I am more concerned about sharing methods than I am fields in these associations, but I do need to use a parent table to retrieve all records from every subclass, so I don't have to do a bunch of crazy joins. My real use case is as follows. If you have 5 minutes to read my lengthy use case, can you explain which design you would choose and why?

USE CASE: I am rewriting some software I use to track affiliate network transactions and I am going to release it open source, so I want to make sure the design is sound. The original software was basically a quick hack written in a day to get some info in my tables. But as I expand my use of these tables I realize the limitations and I need something that is designed well and maintainable.

An affiliate network = website that tracks clicks from a publisher website over to a merchant website and records if a purchase is made. The affiliate network then records transactions from the merchant website and makes them available to the publisher so they can see how much commission they earned from each sale. There are 8 major affiliate networks that I pull transactions from right now and in the next 5 years, maybe that will grow to 20 or 30... and maybe up to 100 if someone adds smaller networks to my open source branch.

In the most basic form a transaction (which will be a single instance/database row of the class) includes a date, a sale amount, and a commission amount. Each network has a different way of representing this data and they all include additional fields that are different.

I want to preserve the integrity of the original data from each network in my database in case I need it later on.. otherwise I would just manipulate the data when I pull it and insert it into one table with the common fields (date,sale,commission,network) and drop the rest of the data. I also need to get the transactions in various formats (csv,xml) from various sources using REST & SOAP, so each subclass (affiliate network) will have some custom functionality in grabbing the data. For these 2 reasons, I was planning on having a separate model and table for each subclass.

So here is my current design. I created a mixin module called BaseTransaction for all the shared functionality. Then I have a class called GeneralTransaction that is an activerecord model. I have a seperate transaction subclass for each affiliate network... AffiliateOneTransaction, etc. Each subclass uses the BaseTransaction mixin module for shared functionality. I am trying to remember, but I think I chose the mixin module method because I could not use Ruby inheritence "<" with activerecord and have a seperate table for each subclass. A GeneralTransaction is inserted every time a subclass record is inserted and GeneralTransaction has subclass & subclass_id fields to reference the records in the subclass table. Seems a little bit hairy to do it this way, but it will work. I am just trying to figure out if there is a more "correct" way to do this design. Any thoughts and suggestions are much appreciated. Thanks if you took the time to read my lengthy post!