On Feb 26, 10:45 am, “Jones, Brian - McClatchy Interactive” [email protected] wrote:
I’d be at least a little interested in potentially offering developers
the chance to ‘lock’ their classes from monkey patches. This could be
useful to the ‘core’ library that comes with Ruby, and to at least make
developers look at extension points provided via an actual API instead
of just immediately jumping on monkey patching for solving all problems.
That’s ridiculous.
Look. What is the problem? That I might extend a class and you might
extend a class with the same method and thus we can’t use each others
code in the same program? Forget overriding core/standard methods –
anyone who does that knows they must do so with SUPER EXTREME caution.
But if someone is writing and end-user application, it doesn’t really
matter one way of the other --the code is not intended to be shared.
Extend to your hearts desire. One only needs to be aware of potential
conflicts with 3rd party libs they might use. And for those, in which
the developer is intending for their code to be reusable, then the
developer needs to tread more carefully and follow some simple rules.
Only extend core and standard classes when it’s a very clear and
general need. In which case it’s a good idea to checkout projects that
attempt to provide some general standardization around such methods
(eg. Facets). It’s likely your method is already available, and your
lib’s users can have a reasonable basis for knowing what to expect.
Beyond that, if you still really want to extend a core/standard class
in a way vunique to your particular program then give it an equally
unique name --generally putting you project’s name as the first part
of the method. For example, the YAML lib adds methods like #yaml_properties.
By following these rules, getting along with other programs is a
pretty safe bet. And we don’t need to add restrictions that will just
make life harder when we do have good reasons to MP.
I should point out one other rule of thumb often overlooked as a
consequence of MP: DO NOT use #respond_to? as a means of determining
if an object is extended by a module or is an instance of some class/
superclass. That’s much more likely to lead to unexpected issues with
MPs.
On Feb 26, 10:45 am, “Jones, Brian - McClatchy Interactive” [email protected] wrote:
I’d be at least a little interested in potentially offering developers
the chance to ‘lock’ their classes from monkey patches. This could be
useful to the ‘core’ library that comes with Ruby, and to at least make
developers look at extension points provided via an actual API instead
of just immediately jumping on monkey patching for solving all problems.
That’s ridiculous.
Fears about open classes sound very much like what people say about
dynamic typing.
“#{@core_feature} is unpredictable!”
“#{@core_feature} creates problems that cannot be found until run-time!”
“#{@core_feature} is unsafe!”
“#{@core_feature} should be locked down!”
It’s not that these claims are entirely untrue, it’s just that, in real
life, most people simply do not encounter the alleged problems.
Code that is poorly written or does not play well with others tends to
get discarded.
–
James B.
“Trying to port the desktop metaphor to the Web is like working
on how to fuel your car with hay because that is what horses eat.”
- Dare Obasanjo
data = case obj
when Array
CSV::from_a( obj )
when Hash
CSV::from_hash( obj )
else
raise "Sorry, I'm not duck-type friendly!"
end
end
Yuck.
Point taken. Monkey patching might be the best solution in this
situation.
Assuming you want to be able to convert any arbitrary object to a CSV,
this
might be best.
Then use CSV::From[obj]. The problem is that if you build a class later
that wants to work with CSV, you’d need to modify this CSV::From
“global”.
This “global” modification isn’t much better than monkey patching
“global”
modification.
I think John does illustrate why monkey patching can be useful with
duck-typing. In statically-typed languages (like C++), the above can be
solved by overloading a global function based on type (type should
prevent
collisions). Don’t take this to mean I want static-typing. I’m the
biggest
fan duck-typing.
Still, we should not use monkey patching as our first tool. We should
think
of it just like global variable modification, IMO.
The disadvantage of making Array#to_csv is that you are
modifying a global “variable” (the Array class).
I’ll take that disadvantage (in combination with good test coverage!)
over having to write verbose, type-checking code any day.
IMO, this is not an either/or case. Why not just add to_csv() only to
the instance who actually could need it?
How far in advance do you know that an instance needs this method?
If you wait too long, you end up with another case statement (again, on
the class of the object, yuck) to decide which #to_csv is appropriate
for the object.
If you add #to_csv eagerly, then the requirement that this object
respond to #to_csv becomes a strong coupling between the point of
creation and the #i_need_csv implementation.
It’s not that these claims are entirely untrue, it’s just that, in real
life, most people simply do not encounter the alleged problems.
Code that is poorly written or does not play well with others tends to
get discarded.
Except that I’m working in the real world, and I run into these
problems practically every day. Read through some Rails plugin code
sometime - nearly every significant, popular Rails plugin does what it
does by re-opening classes. Probably because this is the coding style
that Rails demonstrates and encourages. Now we can argue about this
being a Rails problem rather than a Ruby one, but again, that’s where
people are learning the language these days.
If you have the luxury of working on small projects with few
developers and the time to develop everything in-house rather than
relying on third-party gems and plugins, that’s great. But I and the
people I know are encountering these problems. And in the majority
of cases, they are completelyavoidable. And there is usually
nothing about the problem that makes a monkey-patch a desirable or
even an easier way to implement the feature - it’s simply done that
way because that’s how everyone else is doing it.
How far in advance do you know that an instance needs this method?
If you wait too long, you end up with another case statement (again, on
the class of the object, yuck) to decide which #to_csv is appropriate
for the object.
If you add #to_csv eagerly, then the requirement that this object
respond to #to_csv becomes a strong coupling between the point of
creation and the #i_need_csv implementation.
I don’t think it would matter in this case. You should be able to create
a dynamic implementation without case statements, since a CSV
implementation would be dependent on the #to_a method? (And #to_a for an
array object just returns self.)
Could you describe “the general thing” a little bit more? As I
understood it, he wants to limit a “monkey patch” to the scope of a
Module. Which is what I’ve done. I’ve only used the import-module
library and added a more user-friendly syntax. I’m not inspecting
caller at all, and import-module doesn’t either. What am I missing?
Well, I can’t find import-module-extended, but assuming it’s like
import-module… you’re missing:
performance
isolation of instance variables that the extensions might create
ability for an extended class to always appear extended when
called from anywhere in a class that knows about those extensions,
without having to constantly remember to add the extensions on each
call.
Is that enough to claim “no sensible way” of doing it?
[…]
My point rather is this should somehow be standardized, i.e. the standard
library should provide standard means to do this.
This is what I was attempting to say in my previous post on this
thread. You mentioned defadvice, which is a good example of a “long
reach” mechanism employed by top-level code in order to tweak lower-
level code. The reason this thread exists is because there is no
centralized mechanism in ruby to help programmers from stepping on
each others’ toes. Making ad hoc modifications to base-level classes
is not a scalable practice.
So I will ask my question again: Is (or was) there a serious plan for
something like selector namespaces in ruby 2.0? I found
Responses such as “What is the problem?” come from those who have been
lucky enough to evade these issues in their own work. But eventually
one will wish to use some code written by someone else which
introduces conflicts. It’s only a matter of time, unless you
personally write every line of code in your project. So the answer to
“What is the problem?” is that the technique doesn’t scale.
On Feb 26, 2008, at 9:45 AM, Jones, Brian - McClatchy Interactive wrote:
I’d be at least a little interested in potentially offering developers
the chance to ‘lock’ their classes from monkey patches.
There are probably ways around this, but:
Hmm I do not think that one can overrule freeze; I never found a way,
anyone else?
Cheers
Robert
– http://ruby-smalltalk.blogspot.com/
Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein
centralized mechanism in ruby to help programmers from stepping on
each others’ toes. Making ad hoc modifications to base-level classes
is not a scalable practice.
So I will ask my question again: Is (or was) there a serious plan for
something like selector namespaces in ruby 2.0? I foundhttp://rubygarden.org/ruby/page/show/Rite
which appears to be down; google cache:http://64.233.169.104/search?q=cache:ej4aPcNY41QJ:rubygarden.org/ruby…
Solutions to this tend to be pretty weighty. Ruby is already a rather
slow language. A while back I suggested this idea:
module MyModule
class String < ::String
def something; “something”; end
end
Austin thought it was the worst idea in the world. He may be right,
I’m not sure. In either case it would add another level of
consideration to the language.
Responses such as “What is the problem?” come from those who have been
lucky enough to evade these issues in their own work. But eventually
one will wish to use some code written by someone else which
introduces conflicts. It’s only a matter of time, unless you
personally write every line of code in your project. So the answer to
“What is the problem?” is that the technique doesn’t scale.
What technique is perfectly scalable? There is always the potential of
name clash no matter what technique is used. It’s an unfortunate fact
of life, but if the library you are trying to use is poorly written –
well, then life sucks. Fix the library or find another.
I think you may be looking foe a magic bullet that doesn’t exist.
Also, do you have examples of this issue?
Code that is poorly written or does not play well with others tends to
get discarded.
We still have Gems, which don’t play well at all with others (on a
technical basis) - witness the conflicts with the Debian team, and
what every other Unix package maintainer says about this. This proves
that this mechanism doesn’t work so well if there is a size/momentum
advantage.
Also, discussing what is appropriate and not is a good way to educate
people, and to help us all learn to think more clearly in the area.
I usually only do monkey patching in the
following cases which have no reuse: top-level script, testing, and
quick hacking.
I think it’s the ideal tool for adding compatibility methods.
Along these lines, I read a chapter in Design Patterns in Ruby last
night that suggests using Ruby’s open classes as one possible way to
implement the Adapter pattern. The book includes a pretty good
discussion of the plusses and minus to this approach and makes
recommendations about when it’s probably worth the trade-off. It’s a
good read.
Along these lines, I read a chapter in Design Patterns in Ruby last
night that suggests using Ruby’s open classes as one possible way to
implement the Adapter pattern. The book includes a pretty good
discussion of the plusses and minus to this approach and makes
recommendations about when it’s probably worth the trade-off. It’s a
good read.
James Edward G. II
I don’t have the book. I wouldn’t mind seeing the +/- list.
You are talking about this, right?
Using what this link above suggests (adapter “has-a” or “is-a” adaptee)
is
the better way to go, IMO. There are multiple ways to do this in Ruby.
Monkey-patching/open-classes makes the solution: adapter IS adaptee. I
don’t know if you can even call it the adapter pattern since you are
destroying the adaptee interface.
I don’t have the book. I wouldn’t mind seeing the +/- list.
I don’t think I would do it much justice condensing it down for this
email, but it basically amounted to how well you know the code you are
adapting and how simple the changes are. The author felt it was
probably OK if you knew the class involved well and you were just
aliasing a method or two. Again though, I’m grossly simplifying here.
The book also talked about adding the methods needed to the singleton
class of individual objects. I thought that was an awesome twist that
we probably don’t consider enough.
Code that is poorly written or does not play well with others tends to
get discarded.
We still have Gems, which don’t play well at all with others (on a
technical basis) - witness the conflicts with the Debian team, and
what every other Unix package maintainer says about this. This proves
that this mechanism doesn’t work so well if there is a size/momentum
advantage.
I would now suggest that it is the FHS that is holding us back, not
the other way around --talk about your size/momentum advantage.
Well, I can’t find import-module-extended, but assuming it’s like
import-module… you’re missing:
Clifford, thanks for listing your requirements.
performance
Comparable to other AOP frameworks in Ruby. That might be an obstacle
for you, but it’s not for me yet.
isolation of instance variables that the extensions might create
If you are really serious about this, there are ways to achieve this
goal.
ability for an extended class to always appear extended when
called from anywhere in a class that knows about those extensions,
without having to constantly remember to add the extensions on each
call.
Please look at my sample code again. It does exactly what you describe.
Is that enough to claim “no sensible way” of doing it?
Not for me, as I said above. But since there’s no formal definition of
“sensible ways”, there might be no sensible way for you. Maybe we
should take this to another thread or to private mail if you want to
discuss it further.
what every other Unix package maintainer says about this. This proves
Also, discussing what is appropriate and not is a good way to educate
people, and to help us all learn to think more clearly in the area.
In the case of Debian and Gems, I think it’s a mixture of a somewhat
draconian interpretation of the FHS, and a desire to make the gems
themselves somehow work like debian packages, which don’t envision
things like having multiple versions of a gem installed concurrently.