[Rubycocoa-devel 230] const_missing hook for function-style constants

Zurück zum Archiv-Index

Jonathan Paisley jp-ww****@dcs*****
Sun Mar 12 07:24:39 JST 2006


Hi,

People get caught out sometimes by the dynamic function-style  
constants on the OSX module (such as OSX.NSKeyValueChangeKindKey).  
Below is a sample implementation using const_missing that makes these  
available as normal constants (OSX::NS...).

Please let me know what you think.


tc_constants.rb
===============

require 'test/unit'
require 'osx/cocoa'

### This is the implementation (would go in one of the main ruby  
support files)

class Module
   old_const_missing = instance_method(:const_missing)
   define_method(:const_missing) do |c|
     # See if there's a function-style constant on OSX.
     if c.to_s =~ /^NS/ and OSX.respond_to?(c) then
       begin
         return OSX.const_set(c,OSX.send(c))
       rescue ArgumentError
         # Silently ignore real functions that need an argument
         # and fall through
       end
     end

     # Let the original implementation take care of creating
     # the exception.
     begin
       old_const_missing.bind(self).call(c)
     rescue NameError => e
       # Catch the exception and trim the backtrace to exclude
       # this hook.
       e.backtrace.slice!(0,3)
       raise e
     end
   end
end

### Tests below

module TestConstModule
   def self.const_missing(c)
     :module_success
   end
end

class TestConstClass
   def self.const_missing(c)
     :class_success
   end
end


class TC_Constants < Test::Unit::TestCase
   include OSX

   # Check that const lookup directly in OSX module
   # or via the include statement above works.
   def test_normal_constant
     assert_equal 10, OSX::NSKeyDown
     assert_equal 10, NSKeyDown
   end

   # Check that a function constant can be accessed directly
   def test_function_constant
     assert_equal "NSGlobalDomain", OSX.NSGlobalDomain.to_s
   end

   # Check that a function constant works when accessed as a real
   # constant using ::. Then check that it has now become
   # a real constant on OSX.
   def test_redirected_function_constant
     assert !OSX.const_defined?(:NSGlobalDomain)
     assert_equal "NSGlobalDomain", OSX::NSGlobalDomain.to_s
     assert OSX.const_defined?(:NSGlobalDomain)
     # This gets found in OSX directly since the previous
     # lookup defined it
     assert_equal "NSGlobalDomain", NSGlobalDomain.to_s
   end

   # Similar to above but without explicit reference to OSX module.
   def test_indirect_function_constant
     assert !OSX.const_defined?(:NSFileTypeRegular)
     assert !Object.const_defined?(:NSFileTypeRegular)
     assert_equal "NSFileTypeRegular", NSFileTypeRegular.to_s
     assert !Object.const_defined?(:NSFileTypeRegular)
     assert OSX.const_defined?(:NSFileTypeRegular)
   end

   def test_real_function
     assert_raises NameError do OSX::NSClassFromString end
   end

   # Check that a normal bad constant fails properly
   def test_nonexistent_constant
     assert_raises NameError do OSX::NonexistentConstant end
     assert_raises NameError do NonexistentConstant end
   end

   # Ensure that const_missing still works within a different module
   def test_module_const_missing
     assert_equal :module_success, TestConstModule::Foo
   end

   # Ensure that const_missing still works within a different class
   def test_module_const_missing
     assert_equal :class_success, TestConstClass::Foo
   end

   # Check that const_missing backtraces aren't affected
   def test_backtraces
     begin
       NonexistentConstant
     rescue NameError => e
     end
     assert_not_nil e
     assert_match /test_backtraces/, e.backtrace[0]
   end
end





More information about the Rubycocoa-devel mailing list
Zurück zum Archiv-Index