Comments (9)
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.
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.
@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.
@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.
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.
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.
@saturnflyer yeah, I wrote that without double checking with the code 😬
from casting.
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.
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)
- Consider alternate method names
- Allow arguments to be passed into the `call` method
- Make Class-level casting (refinements) threadsafe HOT 6
- Mixin naming: Casting::Client vs. Castable HOT 3
- Report methods from delegates when querying a client HOT 1
- NoMethodError undefined method `cast_as` HOT 2
- how to cast_as rspec doubles? HOT 6
- optional Array/Set extension to allow casting all members of a collection HOT 9
- Examples HOT 1
- get super_delegate to work with more than one pass HOT 1
- Possibly incompatible with New Relic agent (or the other way around.) HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from casting.