module Persistent💎::JRubyWorkaround

Constants

BROKEN_ENCODING
BROKEN_ENCODING_JRUBY_1_7

Public Class Methods

included(klass) click to toggle source
# File lib/persistent_dmnd/jruby_workaround.rb, line 42
def self.included(klass)
  # Make methods on this module also available inside classes (e.g. not just in instances)
  klass.extend(self)
end

Public Instance Methods

maybe_fix_broken_encoding(name, target = nil) click to toggle source

Workaround JRuby encoding bug See github.com/jruby/jruby/issues/4878 and gitlab.com/ivoanjo/persistent-dmnd/issues/5

The trick here is that on JRuby, methods with emojis in the name are defined, but the resulting name is mangled so instead of for instance to_💎 the result is to_?x00. (Or #💎ify will become #?ifyx00.) This behavior is consistent between def and calling a method, so def will register a method to_?x00, and a normal method call will do the same mangling and thus look for a method WITH the mangled name.

But there are still other places in the code that rely on the correct name, such as alias and public_send/send, so we additionally use this module to define the correct name as an alias for the broken name.

Thus, it all works on JRuby, albeit in a very roundabout way!

FOR JRUBY 1.7: On JRuby 1.7 it seems that the mangling is different, and the emoji just gets replaced with ?, so for instance to_💎 becomes to_?. But then, to make things interesting, on symbols created without quotes, it gets mangled with the BROKEN_ENCODING_JRUBY_1_7, e.g :to_💎 becomes :to_BROKEN_ENCODING_JRUBY_1_7 (but :“to_💎” is still correct). To support both of these, we add an extra alias on JRuby, so that everything else seems to work even if it is actually using the name with the mangled encoding.

# File lib/persistent_dmnd/jruby_workaround.rb, line 81
def maybe_fix_broken_encoding(name, target = nil)
  name_string = name.to_s

  if JRUBY_VERSION.start_with?('1.7.')
    unless name_string.match(/\?.+/) ||
           ['a?', 'h?', 's?', 'each?'].include?(name_string) ||
           ['_?', '_a?', '_h?', '_s?'].any? { |suffix| name_string.end_with?(suffix) }
      return
    end
  else
    return unless name_string.end_with?(BROKEN_ENCODING)
  end

  fixed_name = name_string.sub('?', '💎').delete(BROKEN_ENCODING).to_sym
  fixed_name_jruby_1_7 = name_string.sub('?', BROKEN_ENCODING_JRUBY_1_7).to_sym

  if target
    target.instance_eval { alias_method(fixed_name, name) }
    target.instance_eval { alias_method(fixed_name_jruby_1_7, name) } if JRUBY_VERSION.start_with?('1.7.')
  else
    alias_method(fixed_name, name)
    alias_method(fixed_name_jruby_1_7, name) if JRUBY_VERSION.start_with?('1.7.')
  end
end
method_added(name) click to toggle source
Calls superclass method
# File lib/persistent_dmnd/jruby_workaround.rb, line 47
def method_added(name)
  super
  maybe_fix_broken_encoding(name)
end
singleton_method_added(name) click to toggle source
Calls superclass method
# File lib/persistent_dmnd/jruby_workaround.rb, line 52
def singleton_method_added(name)
  super
  maybe_fix_broken_encoding(name, self.singleton_class)
end