Is It Acceptable Practice to Patch Ruby's Base Classes, Such as Fixnum

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:

  1. 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 your Point#* is passed an argument it doesn't recognize, you would ask that argument to coerce itself to a Point by calling arg.coerce(self).

  2. Yes, it has to be an Array of 2 elements, such that b_equiv, a_equiv = a.coerce(b)

  3. 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
    end
  4. The idea is that you shouldn't modify Fixnum#*. If it doesn't know what to do, for example because the argument is a Point, then it will ask you by calling Point#coerce.

  5. 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



Leave a reply



Submit