Is it acceptable practice to patch Ruby's base classes, such as Fixnum?
My personal answer, in a nutshell: the core-class patching hammer should be at the bottom of your toolbox. There are a lot of other techniques available to you, and in almost all cases they are sufficient, cleaner, and more sustainable.
It really depends on the environment in which you are coding, though. If it's a personal project - sure, patch to your heart's content! The problems begin to arise when you are working on a large codebase over a long period of time with a large group of programmers. In the organization I work for, which has Ruby codebases of over 100KLOC and and twenty or so developers, we have started to crack down pretty hard on monkey patching, because we've seen it lead to head-scratching, man-hour wasting behavior far too often. At this point we pretty much only tolerate it for temporarily patching third-party code which either hasn't yet incorporated or won't incorporate our source patches.
Is a bad practice to monkey patch a base ruby class?
Monkey patching isn't considered to be a bad practice unless you are writing odd methods that do not have PatchedClass-related behavior (for example, String.monkeyPatchForMakingJpegFromString
is rather bad, but Jpeg.fromString
is good enough.)
But if your project is rather large, the libraries that you use in it may happen to have colliding patches, so you may have one more problem with all these patching stuffs. In Ruby 2.0, refinements come to an aid. They work as follows: you define a module
, refine
your (even core) class in it, and then use that module where it's necessary. So, in your code it works as:
YourClass.new.refinedMethodFromCoreClass #=> some result
But
CoreClass.refinedMethodFromCoreClass
produces undefined method
exception.
That's all monkey patching stuff: monkey patching is useful and convenient, but refinements add some features, that make your code more secure, maintainable and neat.
Is there a better way to monkey-patch Ruby's base classes?
Take a look at Ruby's coerce feature.
Alternatives to monkey patching core classes
Any other way of doing this would just be a more awkward syntax for monkey patching. There are ways involving send
and eval
and all sorts of things, but why? Go ahead and do it the obvious way.
You want to be careful of monkey patching in big projects or when you have dependencies, because you can wind up with conflicts when several hands are all messing around in the same place. This doesn't mean look for an alternative syntax that accomplishes the same thing — it means be careful when you're making changes that could affect code that's not yours. This probably isn't a concern in your particular case. It's just something that might need to be addressed in larger projects.
One alternative in Ruby is that you can add methods to a single object.
a = "Hello"
b = "Goodbye"
class <<a
def to_slang
"yo"
end
end
a.to_slang # => "yo"
b.to_slang # NoMethodError: undefined method `to_slang' for "Goodbye":String
How to use Ruby Module?
Why not use just argument on the round
call?
13.6657.round(2) # => 13.67
But if you are sure you need module (to possibly adjust the format for all Floats out there, I'd propose you just define format
method as such:
module MyModule
def format
("%.2f" % self).to_f
end
end
And mix this in to Float.
And later in code you call the format
method instead of round:
13.6657.format # => 13.67
This way it does not hurt the core functionality (as your initial code dropped the argument from the round
definition).
Even better - if you want (can) pinpoint the monkey-patching, simply extend specific instance:
a = 13.6657
a.extend MyModule
a.format # => 13.67
This way it wont mess with other Floats, but you can still adjust the format without finding all calls to a.round(2)
in your code.
XOR-ing two objects
How about this?
a = Object.new
b = Object.new
c = nil
!a ^ !b # => false
!a ^ !c # => true
Is It Proper Ruby Style to Extend Built-in Classes?
Yes.
In general, everyone "monkey patches" everything in Ruby freely, from classes you wrote, to classes someone more important than you wrote, to library classes.
However, the general computing style guideline is if your class does everything, it does nothing. Your example accesses each()
and length()
efficiently, but your new Grades
class now exposes every Array
method, including ones you might not want called ~ and including ones that some cretin might someday go and monkey-patch! So if your Grades
class were very public (used by your entire program), you might want to consider delegation.
Another guideline—that applies more in some languages than others—is you should never inherit unless you then override a method, to achieve polymorphism. Yet another rule which the entire Ruby community, including me, enjoys breaking freely.
Ruby (Rails 4.0.2): (1 == true) = false?
My understanding is that in the Ruby programming language, only nil and false are considered false, and EVERYTHING else is considered true
This is true.
However,
"Thus, 1 == true SHOULD return true."
is false.
Explanation: in Ruby everything except false/nil is considered true. However when testing for equality, like x == true
you're actually not testing "is this value considered true" but "is this value equal to true".
And of course 1
isn't equal to true
, 1
is a number and true
is a boolean value. You can read a very good blog post about equality in Ruby at "Ruby Equality And Object Comparison".
If you really, really want to test it that way, you need to convert the expression like 1
to its boolean value, which you can do by double negating it. So !!1 # => true
.
So, your examples will look like this:
!!1 == true # => true
!!0 == true # => true
!!"test" == true # => true
!!"" == true # => true
However I believe it isn't necessary in most cases.
When working with MSSQL you should let Active Record do all the conversions for you. Declare the field in a migration, with type boolean, and when you do your where
search in the database, do it like this:
Model.where(['attribute = ?', true])
Model.where(:attribute=> true)
This way ActiveRecord will do all the work related to converting the type to a database compatible one for you.
In Ruby, how does coerce() actually work?
Short answer: check out how Matrix
is doing it.
The idea is that coerce
returns [equivalent_something, equivalent_self]
, where equivalent_something
is an object basically equivalent to something
but that knows how to do operations on your Point
class. In the Matrix
lib, we construct a Matrix::Scalar
from any Numeric
object, and that class knows how to perform operations on Matrix
and Vector
.
To address your points:
Yes, it is Ruby directly (check calls to
rb_num_coerce_bin
in the source), although your own types should do too if you want your code to be extensible by others. For example if yourPoint#*
is passed an argument it doesn't recognize, you would ask that argument tocoerce
itself to aPoint
by callingarg.coerce(self)
.Yes, it has to be an Array of 2 elements, such that
b_equiv, a_equiv = a.coerce(b)
Yes. Ruby does it for builtin types, and you should too on your own custom types if you want to be extensible:
def *(arg)
if (arg is not recognized)
self_equiv, arg_equiv = arg.coerce(self)
self_equiv * arg_equiv
end
endThe idea is that you shouldn't modify
Fixnum#*
. If it doesn't know what to do, for example because the argument is aPoint
, then it will ask you by callingPoint#coerce
.Transitivity (or actually commutativity) is not necessary, because the operator is always called in the right order. It's only the call to
coerce
which temporarily reverts the received and the argument. There is no builtin mechanism that insures commutativity of operators like+
,==
, etc...
If someone can come up with a terse, precise and clear description to improve the official documentation, leave a comment!
Is this use of case statement a bad practice?
Ah, such a seemingly simple queston. But is it?
Here Product
is some class, say:
Product = Class.new
Since
Product.new.class
#=> Product
your case statement can be simplified to
case Product
when Module
'this condition will always be true'
when Product
'i need this to be true, so it never happens'
end
Recall that the case
statement uses the method ===
to determine which object to return, meaning that your case
statement is equivalent to
if Module === Product
'this condition will always be true'
elsif Product === Product
'i need this to be true, so it never happens'
end
Let's see how the two logical expressions evaluate:
Module === Product #=> true
Product === Product #=> false
Note this is syntactic sugar for
Module.===(Product) #=> true
Product.===(Product) #=> false
Examine the docs for the method Module#=== to see how it works: it returns true
if Product
is an instance of Module
or of one of Module
's descendents. Well, is it?
Product.class #=> Class
Class.ancestors #=> [Class, Module, Object, Kernel, BasicObject]
It is! Now what about:
Product === Product
Does Product
have a method ===
?:
Product.methods.include?(:===)
#=> true
Where did it come from (we didn't define it, after all)? Let's first look at:
Product.ancestors
#=> [Product, Object, Kernel, BasicObject]
Does Object
have a method ===
? Checking the docs we see that it does: Object#===.1
So Object#===
is invoked. Right? Let's just confirm that:
Product.method(:===).owner
#=> Module
Whoops! It comes from Module
(which is both a module and a class), not from Object
. As we saw above, Product
is an instance of Class
and Class
is a subclass of Module
. Note also that here ===
is an instance method of Class
(and of Module
)2:
Class.instance_method(:===).owner
#=> Module
So Product
is in a quandary. Should it use Module#===
, an instance method supplied by it's parent (Class
), who inherited it from Module
, or should it go with Object#===
, that it inherits from its superclass, Object
? The answer is that precedence is with the former.
This is at the heart of Ruby's "object model". I will say no more about that, but I hope I have provided readers with some tools they can use to figure out what's going on (e.g., Object#method and Method#owner.
Since Product === Product
uses Module#===
just as Module == Product
does, we determine if the former returns true
by answering the question, " is Product
an instance of Product
or of one of Product
's decendents?". Product has no descendents and
Product.class #=> Class,
so the answer is "no", meaning Product === Product
returns false
.
Edit: I see I forgot to actually answer the question posed in the title. This calls for an opinion I suppose (a SO no-no), but I think case
statements are the greatest thing since sliced bread. They are particularly useful when one needs to compare various values to a reference value (e.g., the contents of a variable or to a value returned by a method) using ===
or ==
. For example (see Fixnum#===, where ===
is equivalent to ==
--note the typo in the docs, Regexp#=== and Range#===):
str =
case x
when 1 then 'cat'
when 2,3 then 'dog'
when (5..Float#INFINITY) then 'cow'
else 'pig'
end
result =
case obj
when String
...
when Array
...
end
case str
when /\d/
...
when /[a-z]/
...
end
Beyond that, however, I often use a case statement
in place of if..elsif..else..end
just because I think it`s tidier and more esthetically pleasing:
case
when time == 5pm
feed the dog
when day == Saturday
mow the lawn
...
end
1 In fact, this method is available to all objects, but is not generally
invoked because the method is also defined for a descendant.
2 To be thoroughly confusing, Class also has a triple-equals class method:Class.method(:===).owner #=> Module
.
Related Topics
Missing a Template for This Request Format and Variant
Rails Before_Filter for Specific Actions in Controller
How Does Pack() and Unpack() Work in Ruby
Get Title, Content via Link in Rails
Stream Multiple Body Using Async Sinatra
How to Output Names of Ruby Unit Tests
Sinatra - Response.Set_Cookie Doesn't Work
How to Find a Model's Relationships
Using the Value of a Variable as Another Variables Name in Ruby
Heroku: How to Push Seeds.Rb to Existing Rails App
Spork 0.9.2 and Rspec 3.0.0 = Uninitialized Constant Rspec::Core::Commandline (Nameerror)
Vi Input Mode in Command Line Matlab
Check If a File Exists Using a Wildcard
How to Run My Ruby Code After Rails Server Start
Are Ruby 1.9 Regular Expressions Equally Powerful to a Context Free Grammar
Mongoid: Find Through Array of Ids
Error Running 'Requirements_Osx_Brew_Libs_Install...' on MAC 10.7