I have the following two methods: def ProductFile::find_images(mode, prod_id) # convert to symbol in case it is not (most commonly it may be a String) mode = mode.to_sym case mode when :all, :first ProductFile::find(mode, :conditions => ["product_id = ? AND file_type LIKE ?", prod_id, "image%"]) end end def ProductFile::find_documents(mode, prod_id) mode = mode.to_sym case mode when :all, :first ProductFile::find(mode, :conditions => ["product_id = ? AND (file_type LIKE ? or file_type LIKE ?)", prod_id, "%pdf", "%msword"]) end end Is it possible to create one meta-method that contains the stub for these methods? I''m NOT looking for something like: def ProductFile::find_by_type(type, mode, prod_id) //code snip end Perhaps something using the define_method feature?
Try this out. The "make_find :documents" and "make_find :images" calls at the bottom will create the class methods you want. The sql used is a little different since I didn''t think you needed to use the parameters for the document types. If you want to extend it that way, feel free ;) It also could be a little shorter but I thought clarity was more important. # Begin here class ProductFile < ActiveRecord::Base # Makes a method named find_item, which will take a product_id and mode argument. The method # will use the file_types list given to generate SQL which matches against those types and the product_id # Usage example: # # make_find :documents, "pdf", "msword" # make_find :images, "image%" # # def self.make_find(item, *file_types) sql = "product_id = ? " if file_types.length > 0 sql << " AND (" sql << file_types.inject("") do |str, file_type| str << " OR " unless str.empty? str << "file_type LIKE ''#{file_type}''" end sql << ")" end # We want the method created to ''live'' in ProductFile as a class method, # so we must actually create the method in the meta-class of ProductFile. If # we used ProductFile directly, we would create an instance method. meta = class << self; self; end; # have to use send here because define_method is private meta.send(:define_method, "find_#{item}") do |mode, prod_id| # For debugging, outputs method definition when you run it puts "mode: #{mode}, prod_id #{prod_id}" puts "sql: #{sql}" mode = mode.to_sym case mode when :all, :first self.find(mode, :conditions => [sql, prod_id]) end end end # Create the two methods originally requested. make_find :documents, "%pdf", "%msword" make_find :images, "image%" end On 3/3/06, Nithin Reddy <jashugan@gmail.com> wrote:> > I have the following two methods: > > def ProductFile::find_images(mode, prod_id) > # convert to symbol in case it is not (most commonly it may be a > String) > mode = mode.to_sym > case mode > when :all, :first > ProductFile::find(mode, :conditions => ["product_id = ? AND > file_type LIKE ?", prod_id, "image%"]) > end > end > > def ProductFile::find_documents(mode, prod_id) > mode = mode.to_sym > case mode > when :all, :first > ProductFile::find(mode, :conditions => ["product_id = ? AND > (file_type LIKE ? or file_type LIKE ?)", prod_id, "%pdf", "%msword"]) > end > end > > Is it possible to create one meta-method that contains the stub for > these methods? I''m NOT looking for something like: > > def ProductFile::find_by_type(type, mode, prod_id) > //code snip > end > > Perhaps something using the define_method feature? > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060303/17011fc0/attachment.html
Thanks for the response. I''ve been struggling with the code for a little while, and couldn''t get it to work. When I go into the console and try this: => ProductFile.find_images(:first, 1) I get: => NoMethodError: undefined method `make_find'' for ProductFile:Class I think that this might be the issue:> make_find :documents, "%pdf", "%msword" > make_find :images, "image%"I wasn''t sure where to put these calls, so I put them in the ProductFile class. Is this where it goes? I would like to get some background into meta-programming. Are there any resources you can recommend? I would especially like to understand this:> meta = class << self; self; end;
Copy all the code I sent into your ProductFile class (except the "class ProductFile" bit, of course). That code should be able to be added to the class and work. Just make sure to remove your existing find_images and find_documents methods. As for the meta programming bit meta = class << self; self; end; That''s a little Ruby idiom that gets you the "class of the class." It''s a tough concept to explain, but it''s critical to know when adding methods to classes or doing other metaprogramming tricks. Check out Dwemthy''s Array at http://poignantguide.net/dwemthy/ and http://www.ruby-garden.org for more info (just search for "metaprogramming" or "singleton class"). Dwemthy''s Array is bizarre, to say the least, but if you spend some time with it it''s very rewarding. On 3/3/06, Nithin Reddy <jashugan@gmail.com> wrote:> > Thanks for the response. I''ve been struggling with the code for a > little while, and couldn''t get it to work. > > When I go into the console and try this: > => ProductFile.find_images(:first, 1) > > I get: > => NoMethodError: undefined method `make_find'' for ProductFile:Class > > I think that this might be the issue: > > make_find :documents, "%pdf", "%msword" > > make_find :images, "image%" > > I wasn''t sure where to put these calls, so I put them in the > ProductFile class. Is this where it goes? > > I would like to get some background into meta-programming. Are there > any resources you can recommend? I would especially like to understand > this: > > meta = class << self; self; end; > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060304/6061ad91/attachment.html
Ok. It works. I was under the (false) impression that I could put the make_find calls anywhere. It turns out that, as in your code, I needed to add them underneath the definition for make_find. However, in the end, I chose to go with a different implementation. In ProductFiles, I did the following: def ProductFile::find_by_file_type(f_type, mode, prod_id) f_type = f_type.to_sym case f_type when :images sql_where = ["product_id = ? AND file_type LIKE ''image%''", prod_id] when :documents sql_where = ["product_id = ? AND (file_type LIKE ''%pdf'' OR file_type LIKE ''%msword'')", prod_id] end unless sql_where.nil? mode = mode.to_sym case mode when :all, :first ProductFile::find(mode, :conditions => sql_where) end end end and in my Product model, I did this: def Product::make_find(name) define_method("find_#{name}") do |mode| mode.to_sym case mode when :all, :first ProductFile.find_by_file_type(mode, name, self.id) end end end make_find "images" make_find "documents" I am tempted to use the missing_method method, but I believe that it is used by ActiveRecord, which Product inherits from. I don''t want to override the ActiveRecord definition, so I''m leaving it alone for now. Is there a better way to do this? Thanks! - Nithin
> > I am tempted to use the missing_method method, but I believe that it > is used by ActiveRecord, which Product inherits from. I don''t want to > override the ActiveRecord definition, so I''m leaving it alone for now. > Is there a better way to do this? > >If you define your own method_missing, you can call into ActiveRecords just by using the "super" keyword. For example, def method_missing(sym, *args, &blk) if sym.to_s == "make_images" # your code ... else # Call parent class (i.e. ActiveRecord) super(sym, *args, &blk) end end I also think you may be over-engineering this a bit - why not just call into ProductFiles::find_by_file_type directly - but that''s just my opinion ;) -------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060308/12c02626/attachment.html
> def method_missing(sym, *args, &blk) > if sym.to_s == "make_images" > # your code ... > else > # Call parent class (i.e. ActiveRecord) > super(sym, *args, &blk) > end > endThanks!> I also think you may be over-engineering this a bit - why not just call into > ProductFiles::find_by_file_type directly - but that''s just my opinion ;) >You may be right, but unless I can do something like ProductFiles::find_by_file_type(/image/) or ProductFiles::find_by_file_type(/(msword|pdf)/), then it won''t work for me. - Nithin
I believe this is the case of a solution looking for a problem. I was looking at the code for the file_column plugin and was confused / awe-struck by all the meta-programming that was going on. So, in an effort to learn, I decided to implement some the magic into my project. Of course, I should probably KISS.