Module: JrubyScala::CoreExt::Module::Traits

Included in:
Module
Defined in:
lib/jruby_scala/core_ext/module/traits.rb

Overview

Enables the inclusion of Scala traits into Ruby modules.

TODO: Currently, JRuby throws an exception when attempting to load many of the top-level traits: List, Iterator, Equiv, Ordered, etc. Oddly enough, scala.FunctionN works fine. We need to investigate why this happens.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



15
16
17
18
19
20
# File 'lib/jruby_scala/core_ext/module/traits.rb', line 15

def self.included(base)
  base.module_eval do
    alias_method :include_without_scala_trait_support, :include
    alias_method :include, :include_with_scala_trait_support
  end
end

Instance Method Details

#include_with_scala_trait_support(*modules) ⇒ Object

Mixes the given modules into the current module, including Scala Traits.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/jruby_scala/core_ext/module/traits.rb', line 24

def include_with_scala_trait_support(*modules)
  modules.each do |m|
    if m.respond_to?(:java_class) and m.java_class.interface?
      loader = m.java_class.class_loader
      unless loader.nil?
        mixin_methods_from_trait(loader.load_class(m.java_class.to_s), loader)
        unless self < JrubyScala::CoreExt::OperatorTranslations
          include_without_scala_trait_support(JrubyScala::CoreExt::OperatorTranslations)
        end
      end
    end

    if defined?(@@trait_methods)
      define_method(:scala_reflective_trait_methods) {@@trait_methods}
    end

    include_without_scala_trait_support(m)
  end
end

#mixin_methods_from_trait(trait_class, loader, done = Set.new) ⇒ Object

Mixes the methods from a Scala trait into the module.

Parameters:

  • trait_class

    The trait to mixin

  • loader (java.lang.ClassLoader)

    The Java ClassLoader for the trait

  • done (Set) (defaults to: Set.new)

    The set of traits that have already been mixed in, for memoization purposes



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/jruby_scala/core_ext/module/traits.rb', line 50

def mixin_methods_from_trait(trait_class, loader, done = Set.new)
  return if done.include?(trait_class)
  done << trait_class

  begin
    klass = loader.load_class("#{trait_class.name}$class")
  rescue java.lang.ClassNotFoundException
    # Unable to load the <TraitName>$class, probably because this is
    # a standard Java interface, not a Scala trait, so just return.
    return
  end

  # TODO: Should this happen before attempting to load the special
  # <TraitName>$class?
  trait_class.interfaces.each do |iface| 
    mixin_methods_from_trait(iface, loader, done)
  end

  klass.declared_methods.each do |meth|
    mods = meth.modifiers
    
    if java.lang.reflect.Modifier.static?(mods) and java.lang.reflect.Modifier.public?(mods)
      @@trait_methods ||= []

      unless meth.name.include?('$')
        module_eval <<-CODE
          def #{meth.name}(*args, &block)
            args.insert(0, self)
            args << block unless block.nil?
            args.map! {|a| defined?(a.java_object) ? a.java_object : a}
            scala_reflective_trait_methods[#{@@trait_methods.size}].invoke(nil, args.to_java)
          end
        CODE

        @@trait_methods << meth
      else
        # Method name contains special characters, so use a fallback
        # implementation with define_method.
        #
        # Caveat: This impl can't deal with methods with function
        # arguments (blocks)
        define_method(meth.name) do |*args|
          args.insert(0, self)
          meth.invoke(nil, args.to_java)
        end
      end
    end
  end
end