re: Flexible Ruby Value Object Initialisation VIEW POST

FULL DISCUSSION
 

If your OO approach means to be omnivorous,

class ISBN
  def initialize(value)
    @isbn_string = value.to_s.gsub /\D/, ''
  end
end

If you want to write idiomatic ruby code, use triple-equals instead of nasty elsifs:

class ISBN
  def initialize(value)
    @isbn_string =
      case value
      when String then value.gsub /\D/, ''
      when Integer, NilClass then value.to_s
      when ->(v) { v.respond_to?(:to_isbn) } then v.to_isbn
      else raise
      end
  end
end
 

Indeed, Ruby does conversions very well, as you show in the first code sample.

I do like case, but again it's a type check. I'd rather avoid them if I can. It's good code that, though.

 

I do like case, but again it's a type check.

Except the last clause is literally a duck check you wanted.

Yes, it is a duck check, but I don't want a type check or a duck check. Or any kind of check, really.

What I'm describing is how to achieve the aim of not doing those checks. If that is not an aim that someone shares then they of course are free to implement a different methodology.

You gotta be kidding. Your code explicitly depends on several classes refinements. If this is not a type check, I do not know what is.

I don't see it as anything other than enabling 100% duck typing in the application. I'm ensuring that the application depends only on the ability to respond, not on the underlying object type.

Negative. Proc clause in the case example I provided is depending only on the ability to respond.

Refinements are required to exist upfront: no 3rd party code that responds, but does not have a clue about them is accepted.

Hiding the type check in the module does not make it magically stop being a type check.

In itself, adding responses to a class clearly does not constitute type checking, it is simply "adding responses to a class".

So are you saying that needing to reference the module that adds the behaviour constitutes a form of type checking?

I am saying refinements depend on the type. 3 types in your example were explicitly checked and extended with some functionality. Other types, no matter whether they respond to / implement this functionality are rejected.

There is no way to supply my own type that mimics the functionality to the constructor, without monkey patching your code.

This is exactly how we do it in some Java, but this is not what I would call idiomatic ruby.

Refinements depend on the type

Everything in some way depends on the type, because the type defines the messages that can be received and the responses. That is not type checking, though, that is just code.

Other types, no matter whether they respond to / implement this functionality are rejected

If they respond to the correct message then they are not rejected by my ISBN class, because my ISBN class is not type checking. The refinements are just a way of adding the required responses that is safer than monkey patching, but ultimately they just define behaviour.

I would be keen on hearing other opinions, though.

code of conduct - report abuse