Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

About this user

« Newer Snippets
Older Snippets »
Showing 1-8 of 8 total  RSS 

annotate models with associations

slightly modified http://repo.pragprog.com/svn/Public/plugins/annotate_models/lib/annotate_models.rb to include short declarations for model associations.

   1  
   2  require "config/environment"
   3  
   4  MODEL_DIR   = File.join(RAILS_ROOT, "app/models")
   5  FIXTURE_DIR = File.join(RAILS_ROOT, "test/fixtures")
   6  
   7  module AnnotateModels
   8  
   9    PREFIX = "== Schema Information"
  10    
  11    # Simple quoting for the default column value
  12    def self.quote(value)
  13      case value
  14        when NilClass                 then "NULL"
  15        when TrueClass                then "TRUE"
  16        when FalseClass               then "FALSE"
  17        when Float, Fixnum, Bignum    then value.to_s
  18        # BigDecimals need to be output in a non-normalized form and quoted.
  19        when BigDecimal               then value.to_s('F')
  20        else
  21          value.inspect
  22      end
  23    end
  24  
  25    # Use the column information in an ActiveRecord class
  26    # to create a comment block containing a line for
  27    # each column. The line contains the column name,
  28    # the type (and length), and any optional attributes
  29    def self.get_schema_info(klass, header)
  30      info = "# == Model #{klass}\n# #{header}\n#\n"
  31      info << "# Table name: #{klass.table_name}\n#\n"
  32      
  33      max_size = klass.column_names.collect{|name| name.size}.max + 1
  34      klass.columns.each do |col|
  35        attrs = []
  36        attrs << "default(#{quote(col.default)})" if col.default
  37        attrs << "not null" unless col.null
  38        attrs << "primary key" if col.name == klass.primary_key
  39  
  40        col_type = col.type.to_s
  41        if col_type == "decimal"
  42          col_type << "(#{col.precision}, #{col.scale})"
  43        else
  44          col_type << "(#{col.limit})" if col.limit
  45        end 
  46        info << sprintf("#  %-#{max_size}.#{max_size}s:%-15.15s %s\n", col.name, col_type, attrs.join(", "))
  47      end
  48      assoc_list = klass.reflect_on_all_associations.sort {|x,y| d = x.class_name.to_s <=> y.class_name.to_s; c = d == 0 ? x.macro.to_s <=> y.macro.to_s : d; c == 0 ? x.name.to_s <=> y.name.to_s : c }
  49      unless assoc_list.empty?
  50        info << "#\n# == Associations\n" 
  51        assoc_list.each do |assoc|
  52          ao = assoc.options.dup
  53          ao.delete(:class_name)
  54          line = "# * " +sprintf("<tt>%-25s :%-25s (%s) %s</tt>", assoc.macro, assoc.name, assoc.class_name, ao.empty? ? '' : ao.inspect)
  55          info << line << "\n"
  56        end 
  57      end
  58      info << "#\n\n"
  59    end
  60  
  61    # Add a schema block to a file. If the file already contains
  62    # a schema info block (a comment starting
  63    # with "Schema as of ..."), remove it first.
  64  
  65    def self.annotate_one_file(file_name, info_block)
  66      if File.exist?(file_name)
  67        content = File.read(file_name)
  68  
  69        # Remove old schema info
  70        content.sub!(/^# #{PREFIX}.*?\n(#.*\n)*\n/, '')
  71  
  72        # Write it back
  73        File.open(file_name, "w") { |f| f.puts info_block + content }
  74      end
  75    end
  76    
  77    # Given the name of an ActiveRecord class, create a schema
  78    # info block (basically a comment containing information
  79    # on the columns and their types) and put it at the front
  80    # of the model and fixture source files.
  81  
  82    def self.annotate(klass, header)
  83      info = get_schema_info(klass, header)
  84      
  85      model_file_name = File.join(MODEL_DIR, klass.name.underscore + ".rb")
  86      annotate_one_file(model_file_name, info)
  87  
  88      fixture_file_name = File.join(FIXTURE_DIR, klass.table_name + ".yml")
  89      annotate_one_file(fixture_file_name, info)
  90    end
  91  
  92    # Return a list of the model files to annotate. If we have 
  93    # command line arguments, they're assumed to be either
  94    # the underscore or CamelCase versions of model names.
  95    # Otherwise we take all the model files in the 
  96    # app/models directory.
  97    def self.get_model_names
  98      models = ARGV.dup
  99      models.shift
 100      
 101      if models.empty?
 102        Dir.chdir(MODEL_DIR) do 
 103          models = Dir["**/*.rb"]
 104        end
 105      end
 106      models
 107    end
 108  
 109    # We're passed a name of things that might be 
 110    # ActiveRecord models. If we can find the class, and
 111    # if its a subclass of ActiveRecord::Base,
 112    # then pas it to the associated block
 113  
 114    def self.do_annotations
 115      header = PREFIX.dup
 116      version = ActiveRecord::Migrator.current_version rescue 0
 117      if version > 0
 118        header << "\n# Schema version: #{version}"
 119      end
 120      
 121      self.get_model_names.each do |m|
 122        class_name = m.sub(/\.rb$/,'').camelize
 123        begin
 124          klass = class_name.split('::').inject(Object){ |klass,part| klass.const_get(part) }
 125          if klass < ActiveRecord::Base && !klass.abstract_class?
 126            puts "Annotating #{class_name}"
 127            self.annotate(klass, header)
 128          else
 129            puts "Skipping #{class_name}"
 130          end
 131        rescue Exception => e
 132          puts "Unable to annotate #{class_name}: #{e.message}"
 133        end
 134        
 135      end
 136    end
 137  end
 138  

Creating PayPal recurring payments profile with activemerchant



   1  
   2  # simple extension to ActiveMerchant for basic support of recurring payments with Express Checkout API
   3  # 
   4  # NOTE: set pem_file when loading
   5  module ActiveMerchant #:nodoc:
   6    module Billing #:nodoc:
   7      class PaypalExpressRecurringGateway < Gateway
   8        include PaypalCommonAPI
   9  
  10        LIVE_REDIRECT_URL = 'https://www.paypal.com/cgibin/webscr?cmd=_customer-billing-agreement&token='
  11        TEST_REDIRECT_URL = 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_customer-billing-agreement&token='
  12  
  13        def redirect_url
  14          test? ? TEST_REDIRECT_URL : LIVE_REDIRECT_URL
  15        end
  16  
  17        def redirect_url_for(token)
  18          "#{redirect_url}#{token}"
  19        end
  20  
  21        def setup_agreement(description, return_url, cancel_url)
  22          commit 'SetCustomerBillingAgreement', build_setup_request(description, return_url, cancel_url)
  23        end
  24  
  25        def create_profile(token, description, period, cycles, amount)
  26          commit 'CreateRecurringPaymentsProfile', build_create_profile_request(token, description, period, cycles, amount)
  27        end
  28  
  29        def get_profile_details(profile_id)
  30          commit 'GetRecurringPaymentsProfileDetails', build_get_profile_details_request(profile_id)
  31        end
  32  
  33      private
  34        def build_setup_request(description, return_url, cancel_url)
  35          xml = Builder::XmlMarkup.new :indent => 2
  36          xml.tag! 'SetCustomerBillingAgreementReq', 'xmlns' => PAYPAL_NAMESPACE do
  37            xml.tag! 'SetCustomerBillingAgreementRequest', 'xmlns:n2' => EBAY_NAMESPACE do
  38              xml.tag! 'n2:Version', 50
  39              xml.tag! 'n2:SetCustomerBillingAgreementRequestDetails' do
  40                xml.tag! 'n2:BillingAgreementDetails' do
  41                  xml.tag! 'n2:BillingType', 'RecurringPayments'
  42                  xml.tag! 'n2:BillingAgreementDescription', description
  43                end
  44                xml.tag! 'n2:ReturnURL', return_url
  45                xml.tag! 'n2:CancelURL', cancel_url
  46              end
  47            end
  48          end
  49          xml.target!
  50        end
  51  
  52        def build_create_profile_request(token, description, period, cycles, money)
  53          xml = Builder::XmlMarkup.new :indent => 2
  54          xml.tag! 'CreateRecurringPaymentsProfileReq', 'xmlns' => PAYPAL_NAMESPACE do
  55            xml.tag! 'CreateRecurringPaymentsProfileRequest', 'xmlns:n2' => EBAY_NAMESPACE do
  56              xml.tag! 'n2:Version', 50
  57              xml.tag! 'n2:CreateRecurringPaymentsProfileRequestDetails' do
  58                xml.tag! 'Token', token
  59                xml.tag! 'n2:RecurringPaymentsProfileDetails' do
  60                  xml.tag! 'n2:BillingStartDate', Time.now.utc.iso8601
  61                end
  62                xml.tag! 'n2:ScheduleDetails' do
  63                  xml.tag! 'n2:Description', description
  64                  xml.tag! 'n2:PaymentPeriod' do
  65                    xml.tag! 'n2:BillingPeriod', 'Day'
  66                    xml.tag! 'n2:BillingFrequency', period
  67                    xml.tag! 'n2:TotalBillingCycles', cycles
  68                    xml.tag! 'n2:Amount', amount(money), 'currencyID' => currency(money)
  69                  end
  70                end
  71              end
  72            end
  73          end
  74  
  75          xml.target!
  76        end
  77  
  78        def build_get_profile_details_request(profile_id)
  79          xml = Builder::XmlMarkup.new :indent => 2
  80          xml.tag! 'GetRecurringPaymentsProfileDetailsReq', 'xmlns' => PAYPAL_NAMESPACE do
  81            xml.tag! 'GetRecurringPaymentsProfileDetailsRequest', 'xmlns:n2' => EBAY_NAMESPACE do
  82              xml.tag! 'n2:Version', 50
  83              xml.tag! 'ProfileID', profile_id
  84            end
  85          end
  86  
  87          xml.target!
  88        end
  89  
  90        def build_response(success, message, response, options = {})
  91          PaypalExpressResponse.new(success, message, response, options)
  92        end
  93  
  94      end
  95    end
  96  end
  97  

PostgreSQL: generate DDL to alter views with dependencies

Determine dependencies on given table/view or their columns, types (base/domain/composite), functions, rules and show DROP/ALTER/CREATE series to update their definitions.

   1  
   2  #!/usr/bin/ruby -W0
   3  #
   4  # This script tries to determine all dependencies on given table/view or their columns, 
   5  # types (base/domain/composite), functions, rules.
   6  #
   7  # Usage:
   8  #       ruby show_obj_deps conn_string object_id alter_stmt 
   9  # e.g:
  10  #       ruby show_obj_deps dbname=db1 "VIEW public.base_stats" "DROP VIEW public.base_stats; CREATE VIEW public.base_stats AS SELECT ..."
  11  #
  12  # Result will be series of DDL DROP statements for dependent objects, then alter_stmt, and
  13  # then series of DDL CREATE statements for dropped objects.
  14  #
  15  # Format of object_id:
  16  #   TABLE schema_name.table_name
  17  #   VIEW schema_name.view_name
  18  #   TABLE schema_name.table_name COLUMN column_name
  19  #   VIEW schema_name.view_name COLUMN column_name
  20  #   FUNCTION schema_name.func_name(type_1, type2, ...)
  21  #   RULE rule_name ON schema_name.obj_name
  22  #
  23  # This script is somewhat rewritten version of http://snippets.dzone.com/posts/show/2105
  24  #
  25  # Developed using PostgreSQL v8.0.3, v8.1 with ruby-postgres libpq binding
  26  # 
  27  
  28  require 'postgres'
  29  require 'tsort'
  30  require 'pp'
  31  PGconn.translate_results = true
  32  
  33  $PG_CLASSES = {}
  34  class PgDependencyGraph            
  35  class DBObject
  36    attr_accessor :row, :o_type, :nsp
  37    def initialize(conn, class_id, obj_id, sub_id)
  38      @o_type   = $PG_CLASSES[class_id]
  39      @row   = conn.query(sql_for(@o_type, obj_id, sub_id)).first
  40      @nsp = row['nsp']
  41      if @o_type == 'pg_proc'
  42        arg_types = row.last.split(" ")
  43        unless arg_types.empty?
  44          arg_type_names = arg_types.map {|oid| "format_type(#{oid}, -1)"}.join(", ")
  45          row[-1] = "("+conn.query("SELECT #{arg_type_names}").first.join(", ") +")"
  46        else 
  47  	     row[-1] = "()"
  48        end
  49      end
  50    end
  51    def sql_for(pg_class, obj_id, sub_id)
  52    base_sql = case pg_class 
  53      when 'pg_type' 
  54        "select       (case 
  55        	when typtype = 'b' then 'BASE '
  56  	when typtype = 'c' then 'COMPOSITE '
  57  	when typtype = 'd' then 'DOMAIN '
  58  	when typtype = 'p' then 'PSEUDO '
  59        end) || coalesce( 
  60        (select (CASE WHEN relkind = 'r' THEN 'TABLE'
  61              WHEN relkind = 'v' THEN 'VIEW'
  62              WHEN relkind = 'i' THEN 'INDEX'
  63              WHEN relkind = 'S' THEN 'SEQUENCE'
  64              WHEN relkind = 's' THEN 'SPECIAL'
  65              WHEN relkind = 't' THEN 'TOAST'
  66  	    WHEN relkind = 'c' THEN ' '
  67          END) from pg_class c where c.oid = typrelid), ' '), 
  68  (select nspname from pg_namespace n where n.oid = typnamespace) as nsp,       typname as obj_name,
  69        #{sub_id} from pg_type  "
  70      when 'pg_proc' 
  71        "select (select nspname from pg_namespace n where n.oid = pronamespace) as nsp, proname as obj_name, proargtypes from pg_proc "
  72      when 'pg_class'
  73        "select 
  74    (CASE WHEN relkind = 'r' THEN 'TABLE'
  75              WHEN relkind = 'v' THEN 'VIEW'
  76              WHEN relkind = 'i' THEN 'INDEX'
  77              WHEN relkind = 'S' THEN 'SEQUENCE'
  78              WHEN relkind = 's' THEN 'SPECIAL'
  79              WHEN relkind = 't' THEN 'TOAST'
  80          END) , (select nspname from pg_namespace n where n.oid = relnamespace) as nsp, 
  81         relname as obj_name,  
  82        (select attname from pg_attribute where attrelid = #{obj_id} and attnum = #{sub_id}) from pg_class"
  83      when 'pg_rewrite'
  84        "select (select nspname from pg_namespace n where n.oid = (select relnamespace from pg_class c where c.oid = ev_class) ) as nsp, rulename, (select relname from pg_class c where c.oid = ev_class)  from pg_rewrite"
  85      else
  86          puts "IGNORE: #{pg_class}, #{obj_id}, #{sub_id}"
  87          nil
  88    end
  89    base_sql = base_sql ? base_sql << " WHERE oid = #{obj_id} ": nil
  90    
  91  end
  92   
  93    def to_s
  94      case @o_type
  95      	when 'pg_proc' : "FUNCTION #{@row[0]}.#{@row[1]}#{@row[2]}"
  96        when 'pg_type': "TYPE: #{@row[0]} #{@row[1]}.#{@row[2]}"
  97        when 'pg_class' : "#{@row[0]} #{@row[1]}.#{@row[2]}" + (row[3] ? " COLUMN #{@row[3]}" : "")
  98        when 'pg_rewrite' :  @row[1] == '_RETURN' ? "VIEW #{@row[0]}.#{@row[2]}" : "RULE #{@row[1]} ON #{@row[0]}.#{@row[2]}"
  99      end 
 100    end
 101  end
 102    attr_accessor :depend_graph
 103      ACCEPTED = ['pg_type', 'pg_proc', 'pg_class', 'pg_rewrite']
 104    def initialize(conn)
 105      conn.query("select distinct classid, relname from pg_class c join pg_depend d on (c.oid = d.classid)").each do |row|
 106        $PG_CLASSES[row['classid']] = row['relname']
 107      end
 108      conn.query("select distinct refclassid, relname from pg_class c join pg_depend d on (c.oid = d.refclassid)").each do |row|
 109        $PG_CLASSES[row['refclassid']] = row['relname']
 110      end
 111      dep_graphs = {}
 112      conn.query("SELECT * FROM pg_catalog.pg_depend   where  true or ( deptype<> 'i' and deptype <> 'p')").each do |row|
 113      if ACCEPTED.include?($PG_CLASSES[row['classid']]) && ACCEPTED.include?($PG_CLASSES[row['refclassid']])
 114        this_obj = DBObject.new(conn, row['classid'], row['objid'], row['objsubid'])
 115        that_obj = DBObject.new(conn, row['refclassid'], row['refobjid'], row['refobjsubid'])
 116        #dep_string = this_obj.nsp !~ /^pg_toast$/ ? "\"#{this_obj.to_s}\" -> \"#{that_obj.to_s}\"; \n" : nil
 117        dep_string = this_obj.nsp !~ /^(information_schema|pg_catalog|pg_toast)$/  ? this_obj.to_s : nil
 118      
 119        if dep_string && this_obj.to_s !~ /^INDEX /
 120          dep_graphs[that_obj.to_s] ||=[]
 121          dep_graphs[that_obj.to_s] << dep_string
 122          if that_obj.to_s =~ /^(VIEW|TABLE) (.+?) COLUMN /m
 123            ds2 = that_obj.to_s.sub(/ COLUMN.*/m, '')
 124            dep_graphs[ds2] ||=[]
 125            dep_graphs[ds2] << that_obj.to_s unless that_obj.to_s == ds2
 126          elsif that_obj.to_s =~ /^TYPE: COMPOSITE (TABLE|VIEW) /
 127            ds2 = that_obj.to_s.sub(/^TYPE: COMPOSITE (TABLE|VIEW) /, '\1 ')
 128            dep_graphs[ds2] ||=[]
 129            dep_graphs[ds2] << that_obj.to_s unless that_obj.to_s == ds2
 130          end
 131        end
 132      end
 133    end
 134    dep_graphs.each do |that, values|
 135      values.uniq!
 136      values.reject! {|item| item == that}
 137    end
 138    @depend_graph = dep_graphs
 139   end
 140   def list_dependencies(obj)
 141      dep_list = []
 142      if @depend_graph[obj]
 143        @depend_graph[obj].each do |v|
 144          k = list_dependencies(v)
 145          k.empty? ? dep_list << v : dep_list << [v, k]
 146        end
 147      end
 148      dep_list
 149   end
 150  end
 151  
 152  class DG
 153   include TSort
 154   def initialize(dep_graph, node_list)
 155     @nodes = node_list
 156     @dg = dep_graph
 157   end
 158   def tsort_each_node(&block)
 159     @nodes.each {|x| yield x}
 160   end
 161   def tsort_each_child(node, &block)
 162     (@dg[node]||[]).each(&block)
 163   end
 164  end    
 165  
 166  class Function
 167    attr_reader :typed_head
 168    def initialize(conn, tuple)
 169      @name = tuple['namespace'] + "." + tuple['function_name']
 170      @language = tuple['language_name']
 171      @src = tuple['source_code']
 172      @returns_set = tuple['returns_set']
 173      @return_type = format_type(conn, tuple['return_type'])
 174      @tipes = tuple['function_args'].split(" ")
 175      if tuple['function_arg_names'] && tuple['function_arg_names'] =~ /^\{(.*)\}$/
 176        @arnames = $1.split(',')
 177      elsif tuple['function_arg_names'].is_a? Array
 178        @arnames = tuple['function_arg_names']
 179      else
 180        @arnames = [""] * @tipes.length
 181      end
 182      alist = []
 183      atypelist = [] 
 184      @tipes.each_with_index do |typ,idx|
 185        ft = format_type(conn, typ)
 186        alist << (@arnames[idx] +" " + ft)
 187        atypelist << ft
 188      end
 189      @arglist = alist.join(" , ")
 190      @strict = tuple['proisstrict'] ? ' STRICT' : ''
 191      @secdef = tuple['prosecdef'] ? ' SECURITY DEFINER' : ''
 192      @volatile = case tuple['provolatile']
 193        when 'i' then ' IMMUTABLE'
 194        when 's' then ' STABLE'
 195        else ''
 196      end
 197      @typed_head = @name+"("+atypelist.join(", ")+")"
 198    end
 199    def signature
 200      "#{@name}(#{@arglist})"
 201    end
 202    def definition
 203      <<-EOT
 204  CREATE OR REPLACE FUNCTION #{@name} (#{@arglist}) RETURNS #{@returns_set ?  'SETOF' : ''} #{@return_type} AS $_$#{@src}$_$ LANGUAGE '#{@language}' #{@volatile}#{@strict}#{@secdef};
 205  EOT
 206    end
 207    def == (other)
 208      definition == other.definition
 209    end
 210    def format_type(conn, oid)
 211      t_query = <<-EOT
 212      SELECT pg_catalog.format_type(pg_type.oid, typtypmod) AS type_name
 213       FROM pg_catalog.pg_type
 214       JOIN pg_catalog.pg_namespace ON (pg_namespace.oid = typnamespace)
 215       WHERE pg_type.oid = 
 216      EOT
 217      return conn.query(t_query + oid.to_s)[0][0]
 218    end
 219    def self.find(conn, schema, name, args)
 220      func_query = <<-EOT
 221       SELECT proname AS function_name
 222       , nspname AS namespace
 223       , lanname AS language_name
 224       , pg_catalog.obj_description(pg_proc.oid, 'pg_proc') AS comment
 225       , proargtypes AS function_args
 226       , proargnames AS function_arg_names
 227       , prosrc AS source_code
 228       , proretset AS returns_set
 229       , prorettype AS return_type,
 230       provolatile, proisstrict, prosecdef
 231       FROM pg_catalog.pg_proc
 232       JOIN pg_catalog.pg_language ON (pg_language.oid = prolang)
 233       JOIN pg_catalog.pg_namespace ON (pronamespace = pg_namespace.oid)
 234       JOIN pg_catalog.pg_type ON (prorettype = pg_type.oid)
 235       WHERE pg_namespace.nspname !~ 'pg_catalog|information_schema|pg_temp_'
 236       AND nspname = $1
 237       AND proname = $2
 238       AND oidvectortypes(proargtypes) = $3
 239      EOT
 240  
 241      Function.new(conn, conn.query(func_query, schema, name, args).first)
 242    end
 243  end
 244  
 245  conn = PGconn.new(ARGV[0])
 246  graph = PgDependencyGraph.new(conn)
 247  #pp graph.list_dependencies(ARGV[1])
 248  dep_list = []
 249  ARGV[1].split(/\|/).each do |obj_id|
 250    dep_list += graph.list_dependencies(obj_id).flatten.uniq
 251  end
 252  dep_list.uniq!
 253  
 254  top_sorted = DG.new(graph.depend_graph, dep_list).tsort
 255  top_sorted.each do |line|
 256    case line
 257          when /^TYPE:/, /^VIEW (\S+) COLUMN/; 
 258          when /^VIEW (.+)/ then puts "DROP VIEW #$1;"
 259          when /^FUNCTION (.+)/ then puts "DROP FUNCTION #$1;"
 260          when /^RULE / then puts "DROP "+line+";"
 261          else 
 262          puts "-- SKIP #{line}"
 263    end
 264  end
 265  
 266  puts "", "--- ", "--- ALTER: ", "---"
 267  puts ARGV[2]
 268  puts "---", "---", ""
 269  def view_def(conn, name)
 270    conn.select_value("SELECT pg_catalog.pg_get_viewdef('#{name}'::regclass, true)")
 271  end
 272  def rule_def(conn, tablename, rule_name)
 273   conn.select_value("select definition from pg_rules  where schemaname || '.' ||  tablename = $1  and rulename = $2", tablename, rule_name)
 274  end
 275  
 276  def func_def(conn, *args)
 277   Function.find(conn, *args).definition
 278  end
 279  
 280  
 281  top_sorted.reverse.each do |line|
 282    case line
 283          when /^TYPE:/, /^VIEW (\S+) COLUMN/ then puts "-- SKIP #{line}"
 284          when /^VIEW (.+)/ then puts "CREATE VIEW #$1 AS "+view_def(conn, $1)
 285          when /^FUNCTION (\w+)\.(\w+)\((.*)\)/ then puts func_def(conn, $1, $2, $3)
 286          when /^RULE (.+) ON (.+)/ then puts "CREATE "+rule_def(conn, $2, $1)
 287          else
 288            puts "-- SKIP #{line}"
 289    end
 290    puts 
 291  end
 292  
 293  
 294  

method triggers: instead, before, after

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.

   1  
   2  class Object
   3  
   4    def self.__rules__
   5    # container for defined rules, each item is:
   6    #   [class, event_name, method_name, alias_for_original_method, caller, comment]
   7      @@rules ||= []
   8    end
   9  
  10    def self.__create_rule_instead( method, comment = '', &block) # creates and returns new rule
  11      b_id = "%04x" % block.object_id
  12      old_method_name = :"__previous_#{method}_#{b_id}"
  13      alias_method old_method_name, method
  14      define_method method, &block
  15      __rules__ << rule = [self, "INSTEAD", method, old_method_name,caller[0], comment ]
  16      rule
  17    end
  18  
  19    def self.__create_rule_before( method, comment = '', &block)
  20      args = instance_method(method).arity == 0 ? '' : '(*args)'
  21      b_id = "%04x" % block.object_id
  22      old_method_name = :"__previous_#{method}_#{b_id}"
  23      alias_method old_method_name, method
  24      define_method :"__before_#{method}_#{b_id}", &block
  25      class_eval <<-EOT
  26        def #{method}#{args}
  27          __before_#{method}_#{b_id}#{args}
  28          __previous_#{method}_#{b_id}#{args}
  29        end
  30      EOT
  31      __rules__ << rule = [self, "BEFORE", method, old_method_name, caller[0], comment]
  32      rule
  33    end
  34  
  35    def self.__create_rule_after( method, comment = '', &block)
  36      args = instance_method(method).arity == 0 ? '' : '(*args)'
  37      b_id = "%04x" % block.object_id
  38      old_method_name = :"__previous_#{method}_#{b_id}"
  39      alias_method old_method_name, method
  40      define_method :"__after_#{method}_#{b_id}", &block
  41      class_eval <<-EOT
  42        def #{method}#{args}
  43          res = __previous_#{method}_