annealer annealer

gabriel gironda will have his revenge on seattle

Fast and easy method protocols in Ruby

Written on Friday, January 02, 2009

Squeak has a concept of “protocols”, in which you can group related methods. Jason wanted me to throw together a protocol implementation in Ruby. Here’s a fast and easy implementation that I’ll probably never use in production code.

    class Module
      @protocols = {}
  
      def protocols
        @protocols ||= ancestors[1] ? ancestors[1].protocols.dup : {}
      end
  
      # def add_to_protocol
      def protocol(protocol_name)
        raise "A block is required" unless block_given?
        protocols[protocol_name] ||= []
        original_method_added = method(:method_added).unbind
        (class << self; self; end).send(:define_method, :method_added) do |method_name|
          protocols[protocol_name] << method_name.to_s
        end
        yield
      ensure
        class << self; remove_method(:method_added); end
        original_method_added.bind(self)
      end
  
    end

    if $0 == $PROGRAM_NAME  
    require 'test/unit'

    class ProtocolTest < Test::Unit::TestCase
  
      class TestClass
    
        protocol "setters" do
          def bar=
          end
        end  
    
        protocol "getters" do
          def bar
          end
        end
   
        def foobarbaz
        end
    
        protocol "getters" do
          def foo
          end
        end
    
      end # TestClass
    
      class SonOfTestClass < TestClass; end
  
      def test_should_have_bar_equals_in_setters_protocol
        assert_equal ["bar="], TestClass.protocols["setters"].sort
      end
  
      def test_should_have_bar_and_foo_in_getters_protocol
        assert_equal ["bar", "foo"], TestClass.protocols["getters"].sort
      end
  
      def test_foobarbaz_shouldnt_be_any_any_protocols
        assert !TestClass.protocols.values.flatten.uniq.include?("foobarbaz")
      end
  
      def test_testclass_and_sonoftestclass_should_have_same_protocols_and_different_protocol_variables
        assert TestClass.protocols.object_id != SonOfTestClass.protocols.object_id
        assert_equal TestClass.protocols.values.flatten.sort, SonOfTestClass.protocols.values.flatten.sort
      end
  
    end

    end
blog comments powered by Disqus

Annealer, a Jekyll powered blog by Gabriel Gironda.