

Ruby Tips, Part 5 - timblair
http://globaldev.co.uk/2014/04/ruby-tips-part-5/

======
bstar77
My favorite Ruby resources:
[http://www.confreaks.com](http://www.confreaks.com) |
[http://www.rubytapas.com](http://www.rubytapas.com) |
[https://www.destroyallsoftware.com/screencasts](https://www.destroyallsoftware.com/screencasts)
| [http://railscasts.com](http://railscasts.com) (dated but still useful) |
[https://www.ruby-toolbox.com](https://www.ruby-toolbox.com)

------
ultimoo
I think growing a string with << is an expensive operation. Since once the
allocated memory is full, it will cost O(n) to copy the entire string to a new
location. If we are carrying out m insertions, this could take O(mn).

The way I have been building strings is to store the m strings in an Array and
then join() it at the end (which should be only O(n)). The m small objects
need to be allocated in both cases, the only difference being that we hold on
to those and join them later on.

Am I missing something? I have never measured the performance on either of the
these operations so I could be off the mark here.

~~~
matsadler
For the cases I have benchmarked concatenation with << has been faster than
join on an array.

Here's a simple benchmark:

    
    
        require "benchmark"
    
        n = 100_000
        Benchmark.bm(4) do |x|
          x.report("<<") do
            n.times do
              "aaaaa " << "bbbbbb " << "ccccc " << "ddddd " << "eeeee " << "fffff"
            end
          end
    
          x.report("join") do
            n.times do
              ["aaaaa", "bbbbbb", "ccccc", "ddddd", "eeeee", "fffff"].join(" ")
            end
          end
        end
    

The results I get for this are:

    
    
                   user     system      total        real
        <<     0.140000   0.000000   0.140000 (  0.143750)
        join   0.230000   0.000000   0.230000 (  0.228035)
    

I'd be interested to see if there were any use cases where the relative
performance was reversed.

~~~
xentronium

        require "benchmark"
        n = 100_000
    
        A = "foo " * 20
        B = "bar " * 20
        C = "baz " * 20
        D = "foobar " * 20
        STRINGS = [A, B, C, D]
    
        Benchmark.bm(4) do |x|
          x.report("<<") do
            n.times do
              "" << A << B << C << D
            end
          end
    
          x.report("join") do
            n.times do
              STRINGS.join
            end
          end
        end
    
    
                   user     system      total        real
        <<     0.090000   0.010000   0.100000 (  0.102411)
        join   0.070000   0.010000   0.080000 (  0.071142)

------
danso
Something in my computer-science-student background feels unsettled when I can
use a language for years and still learn new tricks (or often, that I've
forgotten that I've learned) about common functionality such as string
interpolation...but I do love a lot of Ruby's tricks.

    
    
        if path =~ %r{^/assets/mobile/img}
           ...
        end
    

I knew about `%r` but hadn't realized that it escapes forward-slashes while
leaving other regex symbols intact. But with the previous explanation of `%q`,
in which arbitrary delimiters can be used, it makes sense...curly braces
simply replace `/` as the delimiter, and `/` simply retains its non-
specialness in regex...saves me a lot of writing `Regex.escape("/path/to")`.

Another regex notation that I recently learned (elsewhere) and now love to
abuse:

    
    
         "He is 32-years old"[/\d+/] # => "32"
        

as opposed to:

    
    
         "He is 32-years old".match(/\d+/)[0]

~~~
petercooper
The extension of that can also be handy and is often overlooked. Contrived
example:

    
    
        "28 men are 32 years old"[/(\d+) (year|day|month)/, 1] # => "32"

------
rickdale
These are some good tips. If you are into the Ruby language and want more, I
recommend checking out Ruby Tapas
[http://www.rubytapas.com/](http://www.rubytapas.com/) . I have no affiliation
with it, just happy customer.

------
riffraff
notice that this is wrong

    
    
        x ||= y # equivalent to:
        x || x = y
    

it's actually something slightly different (or at least it was some years
ago).

~~~
matsadler
Here's an example that shows x ||= y behaves like x || x = y, rather than x =
x || y

    
    
        class Foo
          def foo=(object)
            puts "foo= called with #{object.inspect}"
            @foo = object
          end
    
          def foo
            puts "foo called"
            @foo
          end
        end
    
        puts "x || x = y"
    
        a = Foo.new
    
        a.foo || a.foo = 1
        a.foo || a.foo = 2
    
        puts "\nx = x || y"
    
        b = Foo.new
    
        b.foo = b.foo || 1
        b.foo = b.foo || 2
    
        puts "\nx ||= y"
    
        c = Foo.new
    
        c.foo ||= 1
        c.foo ||= 2
    

This outputs:

    
    
        x || x = y
        foo called
        foo= called with 1
        foo called
    
        x = x || y
        foo called
        foo= called with 1
        foo called
        foo= called with 1
    
        x ||= y
        foo called
        foo= called with 1
        foo called
    

and you can see that the output for ||= matches the output for x || x = y

~~~
riffraff
I didn't mean that it is "x = x || y", I meant "it's completely ad hoc".

For example, if you try "x || x = y" you will get an exception if x is not
defined, but "x ||= y" will work fine. The same is true of constants "X || X =
1" will explode while "X ||= 1" will work fine.

