kreek
kreek

Reputation: 8834

Refinements and namespaces

Trying to patch net/http and have it only apply to one service class. Refinements seem to be the way to go. The monkey patch below works but the refinement doesn't. Is this a namespace issue? The project is on ruby 2.3.0 but have tried with 2.4.1 as well and only the monkey patch seems to get applied.

With a monkey patch:

module Net
  class HTTPGenericRequest
    def write_header(sock, ver, path)
      puts "monkey patched!"
      # patch stuff...
    end
  end
end

Service.new.make_request
# monkey patched!

With a refinement:

module NetHttpPatch
  refine Net::HTTPGenericRequest do
    def write_header(sock, ver, path)
      puts "refined!"
      # patch stuff...
    end
  end
end

class Service
  using NetHttpPatch
end

Service.new.make_request
# :(

UPDATE:

This seems to be similar scope wise? Obviously more complex things are happening when net/http makes a request does it lose scope then?

module TimeExtension
  refine Fixnum do
    def hours
      self * 60
    end
  end
end

class Service
  using TimeExtension

  def one_hour
    puts 1.hours
  end
end

puts Service.new.one_hour
# 60

UPDATE UPDATE:

nvm, I see what's happening now :) have to keep your brain from mixing using up with how mixins work.

module TimeExtension
  refine Fixnum do
    def hours
      self * 60
    end
  end
end

class Foo
  def one_hour
    puts 1.hours
  end
end


class Service
  using TimeExtension

  def one_hour
    puts 1.hours
  end

  def another_hour
    Foo.new.one_hour
  end
end

puts Service.new.one_hour
# 60
puts Service.new.another_hour
# undefined method `hours' for 1:Fixnum (NoMethodError)

Upvotes: 0

Views: 185

Answers (1)

Jörg W Mittag
Jörg W Mittag

Reputation: 369430

Is this a namespace issue?

It is a scope issue. Refinements are lexically scoped:

class Service
  using NetHttpPatch
  # Refinement is in scope here
end

# different lexical scope, Refinement is not in scope here

class Service
  # another different lexical scope, Refinement is *not* in scope here!
end

Originally, there was only main::using, which was script-scoped, i.e. the Refinement was in scope for the entire remainder of the script. Module#using came later, it scopes the Refinement to the lexical class definition body.

Upvotes: 2

Related Questions