Notes posted to Ruby

RSS feed
February 28, 2009
1 thank

rescue

Store exceptions using ‘rescue => var’

begin                              
  x = factorial(-1)             
rescue => ex                       
  puts "#{ex.class}: #{ex.message}"
end                                
February 25, 2009
0 thanks

Always a String

Remember that even if you ask for one field that is a number, a String will be returned:

Time.now.strftime("%j") #=> "055"
February 24, 2009
2 thanks

Test if one array includes the elements of another v2

Maybe a bit more readable way to write the previous snippet would’ve been

puts "yay" if [1, 2, 3].all? { |i| (1..9).include?(i) }
# => "yay"

puts "nope" if [1, 2, 3, 'A'].any? { |i| not (1..9).include?(i) }
# => "nope"
February 24, 2009
0 thanks

Test if one array includes the elements of another

Recently I’ve written this little snippet:

puts "yay" if [1, 2, 3].each {|i| break unless (1..9).to_a.include?(i)}
# => "yay"

puts "nope" unless [1, 2, 3, 'A'].each {|i| break unless (1..9).to_a.include?(i)}
# => "nope"
February 16, 2009
1 thank

See max

See max for comments and more usage examples.

February 16, 2009
2 thanks

Capping values

This method is very useful when you want to cap values:

# minimum ≤ value 
value = [input.to_i, minimum].max

# value ≤ maximum
value = [input.to_i, maximum].min

# minimum ≤ value ≤ maximum
value = [ [input.to_i, minimum].max, maximum ].min

# Practical example: Make sure destination is within container
destination.x = [ [current.x + current.velocity.x, 0].max, container.width  ].min
destination.y = [ [current.y + current.velocity.y, 0].max, container.height ].min
February 16, 2009
6 thanks

Usage example

Some examples:

# Remove even numbers
(1..30).reject { |n| n % 2 == 0 }
# => [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]

# Remove years dividable with 4 (this is *not* the full leap years rule)
(1950..2000).reject { |y| y % 4 != 0 }
# => [1952, 1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000]

# Remove users with karma below arithmetic mean
total = users.inject(0) { |total, user| total += user.karma }
mean = total / users.size
good_users = users.reject { |u| u.karma < mean }
February 12, 2009
4 thanks

Real life use

If you’re wondering what the base64 format is used for, here are some examples:

Just note that the encoded (character) data is about 30% larger than un-encoded (binary) data.

February 12, 2009
4 thanks

Binary files

Another real important flag is b when dealing with binary files. For example to download an mp3 from the internet you need to pass the b flag or the data will be screwed up:

# Downloads a binary file from the internet
require 'open-uri'
url = "http://fubar/song.mp3"
open(url, 'rb') do |mp3|
  File.open("local.mp3", 'wb') do |file|
    file.write(mp3.read)
  end
end

Don’t say you haven’t been warned. :)

February 12, 2009
3 thanks

Other regular-expression modifiers

Likewise you can set Regexp::IGNORECASE directly on the regexp with the literal syntax:

/first/i
# This will match "first", "First" and even "fiRSt"

Even more modifiers

  • o – Perform #{} interpolations only once, the first time the regexp literal is evaluated.

  • x – Ignores whitespace and allows comments in * regular expressions

  • u, e, s, n – Interpret the regexp as Unicode (UTF-8), EUC, SJIS, or ASCII. If none of these modifiers is specified, the regular expression is assumed to use the source encoding.

Literal to the rescue

Like string literals delimited with %Q, Ruby allows you to begin your regular expressions with %r followed by a delimiter of your choice.

This is useful when the pattern you are describing contains a lot of forward slash characters that you don’t want to escape:

%Q(http://)
# This will match "http://"
February 12, 2009
4 thanks

Literal syntax

As you propably know you can create an Array either with the constructor or the literal syntax:

Array.new == []
# => true

But there is also another nice and concise literal syntax for creating Arrays of Strings:

["one", "two", "three"] == %w[one two three]
# => true

You can use any kind of parenthesis you like after the %w, either (), [] or {}. I prefer the square brackets because it looks more like an array.

February 12, 2009
2 thanks

Use this!

You should raise your own ArgumentError in methods to notify users of your class, if you think certain kinds of arguments aren’t acceptable.

def transfer_money(amount)
  unless amount.is_a?(Number)
    raise ArgumentError.new("Only numbers are allowed")
  end
  # ... Do the actual work
end
February 12, 2009
4 thanks

Useful scenario

This can be quite useful, for example when writing a command line script which takes a number of options.

Example

Let’s say you want to make a script that can make the basic CRUD operations. So want to be able to call it like this from the command line:

> my_script create
> my_script delete

The following script allows you to use any abbreviated command as long as it is unambiguous.

# my_script.rb
require 'abbrev'

command = ARGV.first
actions = %w[create read update delete]
mappings = Abbrev::abbrev(actions)
puts mappings[command]

That means you can call it like this:

> my_script cr
> my_script d

And it will print:

create
delete
February 10, 2009
0 thanks

Cheat Sheet

I have written a short introduction and a colorful cheat sheet for Perl Compatible Regular Expressions (PCRE) as used by Ruby’s Regexp class:

http://www.bitcetera.com/en/techblog/2008/04/01/regex-in-a-nutshell/

February 10, 2009
0 thanks

Cheat Sheet

I have written a short introduction and a colorful cheat sheet for Perl Compatible Regular Expressions (PCRE) as used by Ruby’s Regexp class:

http://www.bitcetera.com/en/techblog/2008/04/01/regex-in-a-nutshell/

February 10, 2009
3 thanks

Cheat Sheet

I have written a short introduction and a colorful cheat sheet for Perl Compatible Regular Expressions (PCRE) as used by Ruby’s Regexp class:

http://www.bitcetera.com/en/techblog/2008/04/01/regex-in-a-nutshell/

February 10, 2009
0 thanks

Output format

This outputs date & time in format of yyyy-MM-ddThh-mm-ssZZZ. All values are preceded with 0 if less than 10. Hours are in 0..23 range. Timezone is sticked at the end. Watch out for capital T in the middle :)

February 9, 2009
2 thanks

Calculating on an enumerable

Inject can easily be used to sum an enumerable or to get the product of it

[100, 200, 1000].inject(0) { |sum, value| sum += value } # => 1300
[100, 200, 1000].inject(1) { |sum, value| sum *= value } # => 20000000

# You can access members and move down in the data structures, too
points.inject(0) { |sum, point| sum += point.y }

In the case of the first two examples, an easier way to do it in Ruby 1.9 is to use reduce:

[100, 200, 1000].reduce :+ # => 1300
[100, 200, 1000].reduce :* # => 20000000

Look at reduce for more examples on how to use this.

February 8, 2009
0 thanks

Can be used on classes, too

For example:

class C
  protected
    def foo
    end 
end
p C.protected_instance_methods(false)

outputs:

["foo"]
February 5, 2009
1 thank

Weird method...

Takes three params:

- a Class constant (has to be an existing class)
- a "tag" param that, if set to "tag:yaml.org,2002:binary" will call unpack("m") on the third parameter, val; any other values for tag are ignored
- val can be a Hash or a String; if it's a string it is wrapped in a hash {'str' => val}, other wise...
  1. an instance of klass is allocated

  2. an instance of string is initialized, with the val parameter, and bound to the class context

  3. for each key in val (if any!), set instance variables in the instantiated class

So, what does this method do, a part from making my eyes hurt? It loads a yaml file and instantiates a class and sets the ivars found in the yaml file. Sorta.

It is probably the worst code I have ever seen in the ruby standard libs. WTF!!

:)

February 4, 2009
1 thank

RE: Careful when building gems

I found out a way to combine using RDoc::usage when you are building a gem. By ripping out the code from the usage method you can create your own version which opens the correct file.

In my case, I skipped the file reading and just put the usage information in a string and used that directly.

Here is my code:

$usage = %Q{
 = Synopsis
 ...
 = Usage
 ...

 == Options
 ...
 }

 # Shamefully stolen from RDoc#usage. We cannot use RDoc::usage because
 # RubyGems will call this from a wrapper, and #usage is hardcoded to look
 # at the top-level file instead of the current one. I have changed this
 # code to instead just parse a string.
 def usage_and_exit!
   markup = SM::SimpleMarkup.new
   flow_convertor = SM::ToFlow.new

   flow = markup.convert($usage, flow_convertor)

   options = RI::Options.instance
   formatter = options.formatter.new(options, "")
   formatter.display_flow(flow)

   exit(0)
 end

Look in usage_no_exit if you want to rip stuff out. It’s up to you how and if you implement this at all, but it’s a shortcut to use the RDoc code and formatting. Do also consider doing the formatting yourself by hand and just output that string instead.

February 4, 2009
3 thanks

Multiline regexps

A shortcut for multiline regular expressions is

/First line.*Other line/m

(notice the trailing /m)

For example:

text = <<-END
  Hello world!
  This is a test.
END

text.match(/world.*test/m).nil?  #=> false
text.match(/world.*test/).nil?   #=> true
February 4, 2009
0 thanks

Careful when building gems

If you are building a gem with executables and you think about using RDoc::Usage for facilitating the --help message, think again.

usage and usage_no_exit is hardcoded to look for the initial comment in the top level file being executed, which is normally fine, but when installing a gem, RubyGems will generate a wrapper executable for you, and usage will therefore look in that wrapper file for the comment instead of your own file.

The methods contain too much code to be easily monkey patched too. Hopefully, this will be fixed in a later version.

January 22, 2009
2 thanks

Correct way to quote for the shell

A followup for my previous comment;

I actually found a good way to handle quoting for the shell in Ruby 1.9 in case you really need to (for example to pipe the output from the examples I gave below).

require 'shellwords'

escape = lambda { |str| Shellwords.shellescape(str) }
file = %(Peter "Smiley" McGraw.txt)
puts "grep foobar #{escape[file]} > foobars"
  # => grep foobar "Peter \"Smiley\" McGraw.txt" > foobars

Do note that I have not tested this since I do not have Ruby 1.9 installed, but from what I have read, this should work properly there.

I still do not know a good way to do this in Ruby 1.8.

January 15, 2009
4 thanks

Convert String to Class in Rails

ncancelliere gave us a very useful tip below, and I just want to make an addendum for it:

If you are using Rails, there is a CoreExtension for this called String#constantize.

"Foo::BarKeeper".constantize #=> Foo::BarKeeper

You can use it with String#camelize if you have to convert the name too

"foo/bar_keeper".camelize             #=> "Foo::BarKeeper"
"foo/bar_keeper".camelize.constantize #=> Foo::BarKeeper

Don’t forget to rescue NameError in case there was an invalid class name. :-)

January 15, 2009 - (v1_8_6_287)
1 thank

Convert String to Class

this example shows how you can take a string and make it a class and then send it a method

Kernel.const_get(my_string.capitalize).select_options
January 14, 2009
6 thanks

File open permissions

Usage: File.open path, flags, [permissions]

Flags (bitmasks)

Access:

File::RDONLY

Read-only

File::WRONLY

Write-only

File::RDWR

Read and write

If the file exists:

File::TRUNC

Truncate

File::APPEND

Append

File::EXCL

Fail

If the file doesn’t exist:

File::CREAT

Create

Flags (strings)

r

File::RDONLY

r+

File::RDWR

w

File::WRONLY|File::TRUNC|File::CREAT

a

File::WRONLY|File::APPEND|File::CREAT

Examples

File.open path, File::RDONLY
File.open path, 'w'
File.open path, File::WRONLY|File::TRUNC|File::CREAT
File.open path, File::WRONLY|File::TRUNC|File::CREAT, '0666'
January 12, 2009
2 thanks

A word of warning

This method returns the supplied string with double quotes around it if it has a space in it, otherwise leaves it alone.

This is useful when sending paths to the shell or any similar operations. Here is a small demonstration of how it works and why it would be useful:

def cat(term)
  puts "cat #{term}"
end

def cat_q(term)
  puts "cat #{term.quote}"
end

# No difference without whitespace
cat   "hello.txt"       #=> cat hello.txt    -- OK!
cat_q "hello.txt"       #=> cat hello.txt    -- OK!

# With whitespace
cat   "hello world.txt" #=> cat hello world.txt    -- Error
                        #   trying to find "hello" or "world.txt"

cat_q "hello world.txt" #=> cat "hello world.txt"   -- OK!

Watch out if you want to do things securely, since this method does not escape quotes that are already there!

cat_q 'Peter "Smiley" McGraw.txt' #=> cat "Peter "Smiley" McGraw.txt" -- ERROR!

It is possible to insert fork bombs and other dangerous stuff by taking advantage of this, and that might take down the whole machine, steal data or delete data depending on the rights of the user executing the script.

The insecurity might also transfer into other areas in case you are not using this method for the shell, but for something else.

Please do note that most of these problems can be avoided if you just split the arguments in Ruby when doing system calls, which is easier to do most of the time.

quoted_files = file_list.collect { |f| f.quote }  
system("grep #{search.quote} #{quoted_files.join(' ')}")

system("grep", search, *file_list) # Much easier!

So, in summary: You should only use this method if you know what you’re doing! It’s neither secure nor needed in most (but not all) cases.

December 23, 2008
0 thanks

Entries are not standalone!

The entry Pathnames consist solely of leaf filenames, so they’re not directly useable in filesystem operations like “open” or “directory?”. To create a useable Pathname, append the entry onto the directory you’re iterating over.

Also keep in mind that the iteration includes the magic entries “.” and “..”, which you probably want to skip. (Is this true on Windows too?)

The #children method doesn’t have either of these issues, although it’s slightly less efficient since it creates an Array of Pathnames up-front instead of yielding one at a time.

December 6, 2008
5 thanks

Array expansion in blocks

The syntax can be improved as changing the second parameter of the block (values) and using an array of two variables instead, which will be used by Ruby as the key and value of “array”.

array = [['A', 'a'], ['B', 'b'], ['C', 'c']]

hash = array.inject({}) do |memo, (key, value)|
  memo[key] = value
  memo
end

hash
# => {'A' => 'a', 'B' => 'b', 'C' => 'c'}