Search code examples
rubyruby-2.3refinementsruby-2.4

best way to organize a long piece of code in ruby refinement block


module Access
  def last
    self[-1]
  end

  def start_end
    self[0] + last
  end
end

module StringExt
  refine String do
    include Access
  end
end

using StringExt

puts 'abcd'.last       # => d
puts 'abcd'.start_end

When a class being refined with too many connected methods, I think it is better to extract them to a module. However, in above example which demos a problem when one method calls another(see the last statement), and it produces following error.

in 'start_end': undefined local variable or method 'last' for "abcd":String (NameError)

Similar issue was solved using a global variable, which also works for my example. But I'm seeking another better way to organize inter-called methods being refined and avoid a global thing.

How would advice a better way to organize those methods?


Solution

  • Here's a general pattern I ended up using. Basically I found no workaround for using global identifiers at some level. But this can be done fairly cleanly by making those globals classes/modules. This will be more clear as an example:

    module StringPatches
    
      def self.non_empty?(string)
        !string.empty?
      end
    
      def non_empty?
        StringPatches.non_empty?(self)
      end
    
      def non_non_empty?
        !StringPatches.non_empty?(self)
      end
    
      refine String do
        include StringPatches
      end
    
    end
    
    class Foo
      using StringPatches
      puts "asd".non_empty? # => true
      puts "asd".non_non_empty? # => false
    end
    

    The class methods on StringPatches don't get exported to using. But since classes/modules are constants (globals) they can be accessed from anywhere.