Giter Site home page Giter Site logo

Comments (9)

cupakromer avatar cupakromer commented on May 30, 2024

I'll let Jim speak to his desired behavior, but yes this is expected due to Ruby.

Casting adds the module to the ancestor chain. This means it appears after the methods defined on the class.

Consider if you had just done this:

class Request
  include RequestPresenter
end

You would see the same behavior. The methods defined on the class have precedence.

Now in Ruby 2.0, it is possible to Module::prepend. This will make the most recently defined module have higher method lookup precedence.

from casting.

krisleech avatar krisleech commented on May 30, 2024

I think I see that super does not work in the familiar way (because I am unfamilar with true delegation versus consultation).

Here is an example:

class Request
  def basic_type
    'delivery'
  end

   def delivery?
     basic_type == 'delivery'
   end
end

module RequestPresenter
  def basic_type
    super.capitalize
  end
end

request = Request.new.cast_as(RequestPresenter)
request.delivery? # => false (incorrect)
request.basic_type == 'Delivery' 

This is because the module methods are merged in to the object so there is only one self, not two, like with SimpleDelegator. The object and module become one.

from casting.

krisleech avatar krisleech commented on May 30, 2024

@cupakromer Thanks for your reply and clarification. Previously I did not have the inheritence chain problem because I used SimpleDelegator, like this:

class Request
  def basic_type
    'delivery'
  end

   def delivery?
     basic_type == 'delivery'
   end
end

module RequestPresenter < SimpleDelegator
  def basic_type
    super.capitalize
  end
end

request = RequestPresenter.new(Request.new)
request.delivery? # => true (correct result)
request.basic_type == 'Delivery' 

from casting.

cupakromer avatar cupakromer commented on May 30, 2024

@krisleech yep, that will do it. I'm inclined to agree that if possible Casting in Ruby 2.0 should use prepend by default. Or at least make it an option in the cast. But then the behavior would be fairly different from 1.9.3.

@saturnflyer I'd be willing to help update things based on how you feel regarding this.

from casting.

saturnflyer avatar saturnflyer commented on May 30, 2024

Not quite. Casting relies on method_missing and not prepend nor include/extend so it fires after any misses on the same method names. If your object already has a singleton method called scheduled_time_from the one from your module used in cast_as will never be hit, because method_missing isn't used.

Choosing to use casting is a design decision that allows you to use the self keyword and any instance variables form your object of concern (the main object). SimpleDelegator does not allow you to do that and you must explicitly work with 2 objects. This can lead to subtle bugs later on.

The upside of SimpleDelegator is that methods there are run first. Because it is a wrapper, you send a message to the wrapper and it first looks for it's own methods and then forwards them to the wrapped object if it doesn't respond to the given message itself. This means that your new behavior comes first and allows you to override existing behavior on your wrapped object but pass the message along if you need to. But any calls to super look up through the ancestry of your wrapper, which is entirely different from the ancestry of your main object, and then fall to method_missing to get back to the wrapped object.

Casting, however, keeps you focused on the object you care about so you send messages to that object, but the messages first go through any methods defined on your main object and only then go to any of the delegates provided with Casting. This means that super is essentially dead there, because your new methods come last, not first. So this means that defining methods of the same name as existing ones will mean they'll never be run.

There's another approach that I provide in https://github.com/saturnflyer/surrounded/tree/master/lib/surrounded/README.md which I call an interface or negotiator (haven't settled on it and checked the historical baggage for either term yet) that attempts to run the module methods first and then run methods on your object. But it is a lightweight wrapper approach. With that approach in Surrounded, you should be able to use super, but you can't because Ruby doesn't allow it (yet?).

I originally reported the behavior related to the use of super as a bug, but it was changed to a feature http://bugs.ruby-lang.org/issues/8546.

from casting.

saturnflyer avatar saturnflyer commented on May 30, 2024

Just wanted to add another note that I would opt to use names that imply their purpose so it'll be less surprising when you dive in to see what's happening or try to fix a bug. If you have a scheduled_time_from method and want one to be formatted in a different way, then I would alter the name of the method in your module or wrapper regardless. You might have 2 or more places in your code where you use scheduled_time_from but in once place it looks like X and in another it looks like Y... wtf!?
A more descriptive name will prevent confusion.

from casting.

cupakromer avatar cupakromer commented on May 30, 2024

@saturnflyer yeah, I wrote that without double checking with the code 😬

from casting.

krisleech avatar krisleech commented on May 30, 2024

I take your point about naming methods to match there purpose. I'm undecided if having a new method name for a lightweight presenter which just calls strftime is overkill. I could stick to SimpleDelegator for presenters and continue to use casting for applying roles to data objects. I'll use both for a while and see which is most comfortable.

I'm really enjoying using casting.

from casting.

saturnflyer avatar saturnflyer commented on May 30, 2024

Thanks @krisleech. I'm glad you like it. I was building a presenter the other day with SimpleDelegator and outputting a bio field, then realized that the bio was actually better named as bio_html because that's what the view was expecting.

from casting.

Related Issues (12)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.