4
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?

Community
  • 1
  • 1
  • 1
    And then there are people asking *"Why is noone using refinements? They are cool, yo!"*. FYI, you don't have to use global variable with the other answer, it was just the easiest way to show how you can delay the definition of the methods until you get to the refinement. In short, refinements don't work that well with `include`. – ndnenkov Mar 29 '17 at 11:27

1 Answers1

3

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.

max pleaner
  • 26,189
  • 9
  • 66
  • 118