

Evil Ruby - wlll
http://caiustheory.com/evil-rb

======
wub
I can't help but feel that even the "good" example makes things unnecessarily
complicated for the sake of saving one line of code.

    
    
        # original
        start_date, end_date = ["24 Dec 2011", "23 Jan 2013"].map {|d| Date.parse(d) }
    
        # better
        start = Date.parse("24 Dec 2011")
        end = Date.parse("23 Jan 2013")

~~~
Argorak
This really depends. If your data already comes in tupels (which often happens
in this case), deconstructing assignment can express very well what you are
doing.

    
    
        # original
        start_date, end_date = timeframe.map { |d| Date.parse(d) }
    
        # better?
        start_date = Date.parse(timeframe[0])
        end_date = Date.parse(timeframe[1])
    

For decent Rubyists, reading a map is very easy. On the other hand, both
versions are acceptable - I wouldn't bother discussing about either of the
options.

------
judofyr
FYI, evil.rb was also a script that hooked you into Ruby's internals, so you
could e.g. change the class of an object, unfreeze objects and do all sorts of
funky things: <https://github.com/yugui/evil-ruby/blob/master/lib/evil.rb>

------
Xylakant
I actually like and use the last one. I don't see why it falls into the "don't
do this" category of tricks.

I consider

    
    
      def foo(bar=(default_given = true; nil)
        puts "default value given" if default_given
      end
    

a quite readable solution to the problem. Actually, it's the only solution if
you need to know if an explicit value was passed and you don't have any
restriction on what values are acceptable.

~~~
damncabbage
I'm still trying to think of a situation where I _want_ to know if someone has
passed the default value or not.

The method itself shouldn't care; it returns the same result regardless
whether an argument was passed explicitly or not.

What am I missing? :(

~~~
philh
You might want a lookup method to usually throw an exception if it doesn't
find anything, but sometimes return a user-specified default.

It's problematic though, because writing a thin wrapper becomes a pain in the
ass.

~~~
damncabbage
I'd argue that it's really two functions:

* One to do the lookup and potentially explode; this always takes an argument.

* A second to provide the default.

Have the caller call the appropriate one. User provides params[:search] (or
some other "explicitly trying to search"), then call the first. If not, call
the latter.

------
SeoxyS
The first example's use case has benefits beyond just shorter code. When
writing ruby, I try to code as functionally as possible, and so I built myself
a Ruby version of a construct stolen from Lisp, which I use for this exact
purpose:

    
    
        def let; yield; end
    
        let do |b = ["24 Dec 2011", "23 Jan 2013"].map {|d| Date.parse(d) }|
          puts "#{b.first} to #{b.last} is #{(b.last - b.first).to_i} days"
        end
    

\---

Unfortunately Ruby's parser doesn't understand multiple assignment and default
values in block argument declaration when used simultaneously, so you can't
do:

    
    
        let do |(first, last) = ["24 Dec 2011", "23 Jan 2013"].map {|d| Date.parse(d) }|
    

Though you can do this:

    
    
        let do |both = ["24 Dec 2011", "23 Jan 2013"].map {|d| Date.parse(d) },
                first = both.first, last = both.last|
          puts "#{first} to #{last} is #{(last - first).to_i} days"

------
jayferd
You forgot this one, using point-free style:

    
    
        ["24 Dec 2011", "23 Jan 2013"].map(&Date.method(:parse))

------
glenntzke
If you find these sort of ruby (ab)uses interesting, take a look through the
ruby Koans solutions at <http://rubykoans.com/>

Playing around these examples led to a fun discovery: I'm 5 days away from
begin 10000 days old. Maybe I'll take Monday off!

~~~
caiusdurling
Good suggestion. I love the koans.

------
xentronium
Why not #tap with some one-letter variable for the first example?

~~~
caiusdurling
OS X default ruby is 1.8 and therefore didn't have Object#tap till the last
few versions. I still forget that I can use it in one-liners there without it
throwing an error. :-)

------
ballard

        # Add method foo only to instance x.
        (x = Object.new).instance_eval { def foo ; puts 'bar' end }

~~~
caiusdurling
I find a better way of doing this is to use a module.

    
    
        x = Object.new
        x.extend Module.new { def foo; puts 'bar'; end }
        x.foo
        # >> bar

------
quarterto
I've used similar to the last one in LiveScript:

    
    
      a = (b ? throw TypeError "buuut b!")-> b

