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!(/\"/,'&#34;')
    input_value.gsub!(/`/,'&#39;')
    input_value.gsub!(/\'/,'&#39;')
    input_value.gsub!(/\(/,'&#40;')
    input_value.gsub!(/\)/,'&#41;')
    input_value.gsub!(/</,'&lt;')
    input_value.gsub!(/>/,'&gt;')
    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!(/\"/,'&#34;')
        input_value.gsub!(/`/,'&#39;')
        input_value.gsub!(/\'/,'&#39;')
        input_value.gsub!(/\(/,'&#40;')
        input_value.gsub!(/\)/,'&#41;')
        input_value.gsub!(/</,'&lt;')
        input_value.gsub!(/>/,'&gt;')
        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
-~----------~----~----~----~------~----~------~--~---

Reply via email to