You are currently browsing the category archive for the 'Uncategorized' category.

In studying the inheritance of methods I came across what I consider a surprising behavior of modules included into classes.

First, when you include a module into a class, the module methods are available in the class and they also propagate down to subclasses. This is reflected in the fact that the module is added to the ancestors of both the including class and all subclasses (be the defined before or after the include).

class LateIntoClassTest < Test::Unit::TestCase

  class A
  end

  class B < A
  end

  module LateInClass
  end

  class A
    include LateInClass
  end

  class C < A
  end

  def test_including_a_module_into_a_superclass_adds_to_ancestors
    # LateInClass is added to A
    assert_equal [A, LateInClass, Object, Kernel], A.ancestors

    # LateInClass is added to B
    assert_equal [B, A, LateInClass, Object, Kernel], B.ancestors

    # LateInClass is added to C
    assert_equal [C, A, LateInClass, Object, Kernel], C.ancestors
  end
end

What is surprising (at least to me), is that the same is not true when you include a module into an included module. I thought modules were kind of like superclasses; if you add to a module then you add to everything that uses the module. Not so.

Here you can see LateInModule is not added to classes that already include A. By contrast, classes defined after the ‘late’ include will add LateInModule to their ancestors.

class LateIntoModuleTest < Test::Unit::TestCase

  module A
  end

  class B
    include A
  end

  module LateInModule
  end

  module A
    include LateInModule
  end

  class C
    include A
  end

  def test_including_into_an_included_module_DOES_NOT_add_to_ancestors
    # LateInModule is added to A
    assert_equal [A, LateInModule], A.ancestors

    # LateInModule is missing from B
    assert_equal [B, A, Object, Kernel], B.ancestors

    # LateInModule is added to C
    assert_equal [C, A, LateInModule, Object, Kernel], C.ancestors
  end
end

You might take from this the lesson that modules are not superclasses, they are collections of methods poured into a class by include. But that isn’t the full story either. After all, you can modify a module and still have those changes propagate into an including class.

class LateModuleModificationTest < Test::Unit::TestCase

  module A
  end

  class B
    include A
  end

  module A
    def late_method; true; end
  end

  def test_included_modules_MAY_be_modified
    assert_equal true, B.new.late_method
  end
end

I find it tricky to express this behavior descriptively, even though the cause is clear; modules only add to ancestors when first included in a class.

Update: for those who are interested, Redmine has a feature request regarding how modules get added to ancestors.

I’ve been trying to speed up the command line response of tap by judiciously loading only what needs be loaded up front.  This script has proven quite helpful… it’s a profiler for require/load. Simply add the requires you want to profile at the end of the script and run it from the command line:

  % ruby profile_load_time.rb
  ================================================================================
  Require/Load Profile (time in ms)
  * Load times > 0.5 ms
  - duplicate requires
  ================================================================================
  * 21.6: yaml
  *   0.5: stringio
      0.2: yaml/error
  *   2.1: yaml/syck
  *     0.7: syck
  *     1.2: yaml/basenode
          0.3: yaml/ypath
      0.3: yaml/tag
      0.3: yaml/stream
      0.3: yaml/constants
  *   15.9: yaml/rubytypes
  *     11.8: date
  *       1.2: rational
  *       4.0: date/format
  -         0.1: rational
  *   0.9: yaml/types

The output flags requires that take longer than 0.5 ms, and requires that occur multiple times. Long requires are often good candidates for autoload… if you want to have YAML available but feel 22 ms is too long to wait up front:

  autoload(:YAML, 'yaml')

Then the file will be required the first time YAML gets used, if at all.