Dynamic messages to refined objects

Hi,

I have a method which converts a given value using a configured
"converter", which may be a Proc or Symbol.

A simplified example:

def convert(val, converter)
  converter.to_proc.call(val)
end

convert(5, :to_s) # => "5"
convert(5, ->(v) { v * 2 } # => 10

In my scenario I also use refinements which leads to a problem of this code.

module CoreRefinements
  refine Integer do
    def to_guid
      # ...
    end
  end
end

class MyClass
  using CoreRefinements

  def convert(val, converter)
    converter.to_proc.call(val)
  end
end

convert(5, :to_guid) # => NoMethodError

I see the point why this fails, but I wonder what the best workaround
would be.

I would like to keep the change for the workaround as small as possible
and within #convert if possible. For example the workaround for
map(&:to_guid) would just be map { |v| v.to_guid }.

The best workaround for the issue above I came up with is this:

def convert(val, converter)
  case converter
  when Proc
    converter.to_proc.call(val)
  when Symbol
    pseudo_safe_accessor = converter.to_s.gsub(/[^[:word:]]/, '')
    eval("val.#{pseudo_safe_accessor}", binding, __FILE__, __LINE__)
  else
    fail TypeError, "Invalid converter #{converter}"
  end
end

Do you know a better or other clever workaround?

Cheers,
Christian

···

--
Christian Haase
:wavy_dash:️ Administration :wavy_dash:
Phone: +49 (0)40 480009-20
Fax: +49 (0)40 480009-22
c.haase@ifu.com

Max-Brauer-Allee 50 - 22765 Hamburg - Germany
Managing Director: Jan Hedemann - Commercial Register: Hamburg, HRB 52629

I think you want to rely on the object's (val's) methods rather than global functions. Use send instead:

if converter.respond_to? :call then
  converter.call val
else
  val.send converter
end

···

On Aug 30, 2016, at 01:42, Christian Haase <c.haase@ifu.com> wrote:

def convert(val, converter)
case converter
when Proc
   converter.to_proc.call(val)
when Symbol
   pseudo_safe_accessor = converter.to_s.gsub(/[^[:word:]]/, '')
   eval("val.#{pseudo_safe_accessor}", binding, __FILE__, __LINE__)
else
   fail TypeError, "Invalid converter #{converter}"
end
end

Hi Ryan,

if converter.respond_to? :call then
  converter.call val
else
  val.send converter
end

Unfortunately this does not work either, when the converter is :to_guid
from the refinement. See [1]

Cheers,
Christian

[1]
http://ruby-doc.org/core-2.1.1/doc/syntax/refinements_rdoc.html#label-Indirect+Method+Calls

···

--
Christian Haase
:wavy_dash: Administration :wavy_dash:
Phone: +49 (0)40 480009-20
Fax: +49 (0)40 480009-22
c.haase@ifu.com

Max-Brauer-Allee 50 - 22765 Hamburg - Germany
Managing Director: Jan Hedemann - Commercial Register: Hamburg, HRB 52629

Aren't you just proving that refinements do what they were supposed to
do, namely be only visible in a particular context? If you change
context, they should not be accessible. If I sorted out the details
correctly you are asking for a way to break a mechanism which would
render it useless - or at least less robust.

Kind regards

robert

···

On Wed, Aug 31, 2016 at 11:55 AM, Christian Haase <c.haase@ifu.com> wrote:

if converter.respond_to? :call then
  converter.call val
else
  val.send converter
end

Unfortunately this does not work either, when the converter is :to_guid
from the refinement. See [1]

[1]
http://ruby-doc.org/core-2.1.1/doc/syntax/refinements_rdoc.html#label-Indirect+Method+Calls

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/

Hi Robert,

Aren't you just proving that refinements do what they were supposed to
do, namely be only visible in a particular context? If you change

In my response I interpreted Ryan's example to replace the content of
MyClass#convert. The full example then would be this:

module CoreRefinements
  refine Integer do
    def to_guid
      # ...
    end
  end
end

class MyClass
  using CoreRefinements

  def convert(val, converter)
    if converter.respond_to? :call then
      converter.call val
    else
      val.send converter
    end
  end
end

MyClass.new.convert(5, :to_guid)
  # => NoMethodError: undefined method `to_guid' for 5:Fixnum

context, they should not be accessible. If I sorted out the details
correctly you are asking for a way to break a mechanism which would
render it useless - or at least less robust.

In fact, yes, kind of. The problem here is that if I use code like
map(&:to_guid), send(:to_guid), respond_to?(:to_guid) the symbol is
evaluated in another context which is not using the refinements, not
within MyClass#convert.

Cheers,
Christian

···

--
Christian Haase
:wavy_dash: Administration :wavy_dash:
Phone: +49 (0)40 480009-20
Fax: +49 (0)40 480009-22
c.haase@ifu.com

Max-Brauer-Allee 50 - 22765 Hamburg - Germany
Managing Director: Jan Hedemann - Commercial Register: Hamburg, HRB 52629

The link given does say "This behavior [indirect method access via send] may be changed in the future."

That said... With `send` not working, I don't see refinements as terribly useful. I never have and find it to be an antipattern. But knowing this? I see it as even less useful now.

···

On Aug 31, 2016, at 11:13, Robert Klemme <shortcutter@googlemail.com> wrote:

Aren't you just proving that refinements do what they were supposed to
do, namely be only visible in a particular context? If you change
context, they should not be accessible. If I sorted out the details
correctly you are asking for a way to break a mechanism which would
render it useless - or at least less robust.

Frankly, I haven't used them myself. I am also skeptical but willing
to assume that there are some use cases.

Kind regards

robert

···

On Thu, Sep 1, 2016 at 11:41 PM, Ryan Davis <ryand-ruby@zenspider.com> wrote:

On Aug 31, 2016, at 11:13, Robert Klemme <shortcutter@googlemail.com> wrote:

Aren't you just proving that refinements do what they were supposed to
do, namely be only visible in a particular context? If you change
context, they should not be accessible. If I sorted out the details
correctly you are asking for a way to break a mechanism which would
render it useless - or at least less robust.

The link given does say "This behavior [indirect method access via send] may be changed in the future."

That said... With `send` not working, I don't see refinements as terribly useful. I never have and find it to be an antipattern. But knowing this? I see it as even less useful now.

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/

I would love to see one. It currently feels like one of those solutions looking for a problem. It always seems to have one main use case (like logging for aspect oriented programming) and falls down everywhere else. This one doesn't even seem to have a main use case.

···

On Sep 2, 2016, at 00:22, Robert Klemme <shortcutter@googlemail.com> wrote:

Frankly, I haven't used them myself. I am also skeptical but willing
to assume that there are some use cases.