DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone >

Method Triggers: Instead, Before, After

Snippets Manager user avatar by
Snippets Manager
·
Mar. 03, 07 · · Code Snippet
Like (0)
Save
Tweet
494 Views

Join the DZone community and get the full member experience.

Join For Free
Redefine method calls, in manner similar to SQL triggers and PostgreSQL rules - i.e. execute code block instead, before or after call to original method. rules can be appended and removed.


class Object

  def self.__rules__
  # container for defined rules, each item is:
  #   [class, event_name, method_name, alias_for_original_method, caller, comment]
    @@rules ||= []
  end

  def self.__create_rule_instead( method, comment = '', &block) # creates and returns new rule
    b_id = "%04x" % block.object_id
    old_method_name = :"__previous_#{method}_#{b_id}"
    alias_method old_method_name, method
    define_method method, &block
    __rules__ << rule = [self, "INSTEAD", method, old_method_name,caller[0], comment ]
    rule
  end

  def self.__create_rule_before( method, comment = '', &block)
    args = instance_method(method).arity == 0 ? '' : '(*args)'
    b_id = "%04x" % block.object_id
    old_method_name = :"__previous_#{method}_#{b_id}"
    alias_method old_method_name, method
    define_method :"__before_#{method}_#{b_id}", &block
    class_eval <<-EOT
      def #{method}#{args}
        __before_#{method}_#{b_id}#{args}
        __previous_#{method}_#{b_id}#{args}
      end
    EOT
    __rules__ << rule = [self, "BEFORE", method, old_method_name, caller[0], comment]
    rule
  end

  def self.__create_rule_after( method, comment = '', &block)
    args = instance_method(method).arity == 0 ? '' : '(*args)'
    b_id = "%04x" % block.object_id
    old_method_name = :"__previous_#{method}_#{b_id}"
    alias_method old_method_name, method
    define_method :"__after_#{method}_#{b_id}", &block
    class_eval <<-EOT
      def #{method}#{args}
        res = __previous_#{method}_#{b_id}#{args}
        __after_#{method}_#{b_id}#{args}
        res
      end
    EOT
    __rules__ << rule = [self, "AFTER", method, old_method_name,caller[0], comment ]
    rule
  end

  def self.__remove_rule( rule ) # has some bugs when rules on subclasses are defined :(
    idx = __rules__.index(rule)
    if idx
      # look for next rule for the same method
      idx += 1
      while idx < __rules__.size
        break if  __rules__[idx][2] == rule[2] && __rules__[idx][0] == rule[0]
        idx+=1
      end
      if idx < __rules__.size
        next_rule = __rules__[idx]
        next_rule[0].send :remove_method, next_rule[3]
        next_rule[0].send :alias_method, next_rule[3], rule[3]
      else
        # that was last
        rule[0].send :remove_method, rule[2]
        rule[0].send :alias_method, rule[2], rule[3]
      end
      __rules__.delete(rule)
    end
  end

end


Example:

class Model
  def save
    puts "save"
  end
  def reload(flag)
    "reloaded"
  end
end

r1 = Model.__create_rule_instead(:reload) {|flag| flag ? "FRESH" : "STALE" }

obj = Model.new

puts "RELOAD:"+obj.reload(true)
puts "RELOAD:"+obj.reload(false)

Object.__remove_rule(r1)
puts "RELOAD:"+obj.reload(false)

Model.__create_rule_before(:save) { puts "BEFORE SAVE" }
r2 = Model.__create_rule_before(:save) { puts "YET BEFORE SAVE" }
Model.__create_rule_after(:save)  { puts "AFTER SAVE" }
r3 = Model.__create_rule_after(:save)  { puts "YET AFTER SAVE" }
obj.save
Object.__remove_rule(r2)
puts "----------"
obj.save
Object.__remove_rule(r3)
puts "----------"
obj.save

produces:

RELOAD:FRESH
RELOAD:STALE
RELOAD:reloaded
YET BEFORE SAVE
BEFORE SAVE
save
AFTER SAVE
YET AFTER SAVE
----------
BEFORE SAVE
save
AFTER SAVE
YET AFTER SAVE
----------
BEFORE SAVE
save
AFTER SAVE

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Choosing Between GraphQL Vs REST
  • The Right Way to Hybridize Your Product Development Technique
  • Memory Debugging and Watch Annotations
  • Modernize Legacy Code in Production: Rebuild Your Airplane Midflight Without Crashing

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo