Lots of code below, since it's usually the details that matter. Bit of a long one, but I think it's generic enough to be a good one for the archives.
Experimenting with how to extend ActiveRecord. So I picked a little example project of an input scrubber. The purpose for the code is to automatically sanitize attribute data prior to it being saved. Don't focus on the scrubber itself, it's just a arbitrary task for something to play with (obviously it's not a full scale sanitizer). I have the Peepcode plugins book, and I have checked several blogs. I have tinkered for hours now, trying simple Ruby-only steps to understand the various ruby dynamics with extend, include, included, etc. So I'm generally getting the concepts (and working simple examples), but getting lost in the details of how to get this particular idea to work. So let's start with an implementation hard coded inside one model which works just fine. class Example < ActiveRecord::Base @@scrub_attributes = [:author_name, :title, :content] def before_save self.attributes.each do |key,value| if @@scrub_attributes.include?(key.to_sym) scrub_value(value) if !value.nil? end end end def scrub_value(input_value) input_value.gsub!(/\"/,'"') input_value.gsub!(/`/,''') input_value.gsub!(/\'/,''') input_value.gsub!(/\(/,'(') input_value.gsub!(/\)/,')') input_value.gsub!(/</,'<') input_value.gsub!(/>/,'>') input_value.gsub!(/j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t\s*\:/i,'') end end #================================================================ But, of course, what I really want for the model code API is this: class Example < ActiveRecord::Base scrub_attributes :author_name, :title, :content end So, I started work on a plugin. Below is where I am at so far. I have boiled (or rather logged) the problem down to two details. I have marked a few points of reference to talk about. Have a gander, and I'll meet you at the end of the code... #================================================================ Plugin structure: /plugins init.rb /ar_extensions /lib /gw attribute_scrubber.rb init.rb contains ActiveRecord::Base.send(:include, GW::AttributeScrubber) module GW module AttributeScrubber def self.included(base_module) class << base_module @@attributes_to_scrub = [] end base_module.extend ClassMethods base_module.send(:include, InstanceMethods) # <---- (1) end module ClassMethods def scrub_attributes(*attr_names) @@attributes_to_scrub = attr_names end def attributes_to_scrub return @@attributes_to_scrub end end module InstanceMethods def scrub_value(input_value) # <---- (2) input_value.gsub!(/\"/,'"') input_value.gsub!(/`/,''') input_value.gsub!(/\'/,''') input_value.gsub!(/\(/,'(') input_value.gsub!(/\)/,')') input_value.gsub!(/</,'<') input_value.gsub!(/>/,'>') input_value.gsub!(/j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t\s*\:/i,'') end end def self.append_features(base_module) base_module.before_save do |model| model.attributes.each do |key,value| if @@attributes_to_scrub.include?(key.to_sym) #<--- (3) scrub_value(value) if !value.nil? # <---- (4) end end end end end end #================================================================ So, the problem I am having starts with (1). The PeepCode book says this code should be def self.included(base_module) base_module.extend ClassMethods base_module.include InstanceMethods end But that generates an error `included': private method `include' called Thus, I'm trying base_module.send(:include, InstanceMethods), but that doesn't seem to be working either because down at (4) I get errors that method scrub_value doesn't exist. Before we get to (4) though, it turns out that at (3) @@attributes_to_scrub, which I am expecting to be a class var for ActiveRecord doesn't exist for the before_save method. I discovered that even though the @@attributes_to_scrub contained in scrub_attributes does havethe values I expect, at (3) there are no values. Namespace problem? I hard coded an array in place of the class var at (3), and then I get the error that the method scrub_value doesn't exist, so I know the InstanceMethods are not being included, or at least not in the namespace I'm expecting. But I can get some simple Ruby-only code to work this way just fine. So, I'm confused about that. Other than running into the snags at (3) and (4), it all seems to run. Clues appreciated. Thanks. -- gw -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk@googlegroups.com To unsubscribe from this group, send email to rubyonrails-talk+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---