Notes posted to Ruby

RSS feed
January 19, 2010
2 thanks

Argument Accepted

Accepts a single argument sep_string

January 11, 2010
0 thanks

Includes all ancestors

May be helpful to know that this returns true if B is any ancestor of A, not just a direct one. As an example:

class Foo; end
class Bar < Foo; end
class Baz < Bar; end

Foo >= Bar #=> true
Foo >= Baz #=> true
January 11, 2010
0 thanks

Includes all ancestors

May be helpful to know that this returns true if B is any ancestor of A, not just a direct one. As an example:

class Foo; end
class Bar < Foo; end
class Baz < Bar; end

Foo > Bar #=> true
Foo > Baz #=> true
January 11, 2010
0 thanks

Includes descendants

May be helpful to know that this returns true if A is any descendant of B, not just a direct one. As an example:

class Foo; end
class Bar < Foo; end
class Baz < Bar; end

Bar <= Foo #=> true
Baz <= Foo #=> true
January 11, 2010
0 thanks

Includes descendants

May be helpful to know that this returns true if A is any descendant of B, not just a direct one. As an example:

class Foo; end
class Bar < Foo; end
class Baz < Bar; end

Bar < Foo #=> true
Baz < Foo #=> true

If you want direct descendance try Class#superclass:

Bar.superclass == Foo #=> true
Baz.superclass == Foo #=> false
December 31, 2009
2 thanks

Replacing with "\" and match — a simple solution

A somewhat different approach to the same problem:

v.gsub(/(?=\W)/, '\\') #=> Foo\ Bar\!

But from what you are trying to achieve I suspect you might be interested in Regexp.escape method :)

December 3, 2009
2 thanks

Replacing with "\" and match

If you’re trying to place a “" in front of your matches, you’ll quickly see that it is a pain in the ass to add the quoting in the replacement string.

Here’s an example:

v = "Foo Bar!"  # Target: Foo\ Bar\!
# Resulting strings will not be quoted to decrease
# the amount of backslashes. Compare \\! to "\\\\!"

v.gsub(/\W/, '\0') #=> Foo Bar!

# \\ escapes to a literal \, which next to the 0 becomes \0
v.gsub(/\W/, '\\0') #=> Foo Bar!

# \\\0, means "\ \0", or "escaped \0"
v.gsub(/\W/, '\\\0') #=> Foo\0Bar\0

# Same mechanism as before. \\ → \
v.gsub(/\W/, '\\\\0') #=> Foo\0Bar\0

# Finally! We have now an escaped \ before \0 and
# we get the results we want.
v.gsub(/\W/, '\\\\\0') #=> Foo\ Bar\!

# It's very tempting to just write it like this now, right?
v.gsub(/\W/) { |m| "\\#{m}" } #=> Foo\ Bar\!
# It might not be shorter, but anyone can understand it.

Surely, there must be an easier way to do this. I haven’t found it, though. Hopefully, this makes it easier for you to understand why it behaves the way it does. :-)

December 3, 2009 - (v1_8_6_287 - v1_8_7_72)
0 thanks

Testing Regular Expressions?

Writing regular expressions is never easy. You may use http://rubular.com to test and optimize your regular expressions.

November 23, 2009
3 thanks

Make directory if not exists

If the directory already exists, mkdir raises exception. To prevent this:

Dir.mkdir(dir) unless File.exists?(dir)
November 18, 2009 - (>= v1_8_6_287)
5 thanks

Example

code:

class Klass
  def set(string)
    var_name = "@#{string}"  # the '@' is required
    self.instance_variable_set(var_name, 'bar')
  end
  def puts_foo
    puts @foo
  end
end
k = Klass.new
k.puts_foo  # nil
k.set('foo')
k.puts_foo  # 'bar'
November 10, 2009
1 thank

lower case am/pm

With DateTime#strftime you can also use %P to get a lowercase am/pm. Not so with Time#strftime though!

November 9, 2009
0 thanks

overwrite

Replacing old value with new one

>> Module.const_set('MY_CONSTANT', 'value')
=> "value"
>> Module::MY_CONSTANT
=> "value"
>> Module.const_set('MY_CONSTANT', 'new value')
(irb):3: warning: already initialized constant MY_CONSTANT
=> "new value"
>> Module::MY_CONSTANT
=> "new value"

or

>> Kernel.const_set('MY_CONSTANT', 'value')
=> "value"
>> MY_CONSTANT
=> "value"
>> Kernel.const_set('MY_CONSTANT', 'new value')
(irb):3: warning: already initialized constant MY_CONSTANT
=> "new value"
>> MY_CONSTANT
=> "new value"
November 5, 2009
10 thanks

define_method with parameters

Just to be clear, you can do this:

define_method(:my_method) do |foo, bar| # or even |*args|
  # do something
end

This means same as:

def my_method(foo, bar)
  # do something
end

If you want to define method with parameters that have default values, you need to get a bit more creative and do something like this:

define_method(:my_method) do |foo, bar|
  bar ||= {}
  # do something
end
October 31, 2009
2 thanks

Don't forget to require 'tmpdir'

If you simply say Dir.tmpdir you might get a nice surprise:

irb> Dir.tmpdir
NoMethodError: undefined method `tmpdir' for Dir:Class

from (irb):1

Strangely, this method seems to be stored in a file that Ruby doesn’t require by default. Just require 'tmpdir' and all should be well.

irb> require 'tmpdir'
=> true
irb> Dir.tmpdir
=> "/var/folders/An/AnwlXPZFH2aRLCERERQDKE+++TI/-Tmp-"
October 30, 2009
3 thanks

Map-like Manipulation of Hash Values

Let’s say you want to multiply all values of the following hash by 2:

hash = { :a => 1, :b => 2, :c => 3 }

You can’t use map to do so:

hash.map {|k, v| v*2 }   # => [6, 2, 4]

However, with merge you can:

hash.merge(hash) {|k,v| v*2 }   => {:c=>6, :a=>2, :b=>4}

(The above is Ruby 1.8, in Ruby 1.9 the order is preserved.)

October 20, 2009
3 thanks

See Dir#glob

See glob for more usage information and comments.

October 20, 2009
2 thanks

Getting relative path from absolute globbing

Say you want to scan for files in directory base_dir and you want to use the relative path from this base dir, you could do it like this:

base_dir = '/path/to/dir'
files = Dir[File.join(base_dir, '**', '*.yml')]

# files now contain absolute paths:
files.first # => "/path/to/dir/foo/bar.yml"

# let's make them relative
base_pathname = Pathname.new(base_dir)
files = files.collect do |file|
  Pathname.new(file).relative_path_from(base_pathname)
end

files.first # => "foo/bar.yml"

Of course, a more common use-case could be the following:

def scan_for_documents!
  base_path = Pathname.new(self.base_path)
  self.contained_files = []
  Dir[File.join(self.base_path, '**', '*.pdf')].each do |full_path|
    path = Pathname.new(full_path).relative_path_from(base_path)
    self.contained_files << path
  end
end
October 19, 2009
1 thank

Get all inner texts

Extend REXML::Element so that it can get the first text and following inner texts (child texts included) of the current element as array and as string:

class REXML::Element

 def inner_texts
  REXML::XPath.match(self,'.//text()')
 end

 def inner_text
  REXML::XPath.match(self,'.//text()').join 
 end

end
October 18, 2009
1 thank

Receiving data over UDP

It’s perfectly normal to receive ‘X’ strings with Ruby’s UDP sockets before the actual content.

Consider the following example:

require 'socket'

PORT = 5500

socket = UDPSocket.new
socket.bind('', PORT)

for i in 1..10
  IO.select([socket])
  p socket.recvfrom_nonblock(4096)
end

Now, sending data with netcat:

echo "Hello APIdock" | nc -vv -u 127.0.0.1 5500

The application would output:

["X", ["AF_INET", 61755, "localhost", "127.0.0.1"]]
["X", ["AF_INET", 61755, "localhost", "127.0.0.1"]]
["X", ["AF_INET", 61755, "localhost", "127.0.0.1"]]
["X", ["AF_INET", 61755, "localhost", "127.0.0.1"]]
["Hello APIdock\n", ["AF_INET", 61755, "localhost", "127.0.0.1"]]
October 10, 2009
2 thanks

Example

[5,6,7].each_with_index do |x,i|

puts "#{i} -> #{x}"

end

Outputs: 0 -> 5 1 -> 6 2 -> 7

October 9, 2009
0 thanks

Case-insensitive comparison

For a case-insensitive comparison, use String#casecmp

October 7, 2009
4 thanks

Hash#without

Here’s a small helper for doing the “opposite” of this method:

class Hash
  def without(*keys)
    cpy = self.dup
    keys.each { |key| cpy.delete(key) }
    cpy
  end
end

h = { :a => 1, :b => 2, :c => 3 }
h.without(:a)      #=> { :b => 2, :c => 3 }
h.without(:a, :c)  #=> { :b => 2 }
October 5, 2009
1 thank

Using argument version in ruby < 1.8.7

The argument to this method was added in Ruby 1.8.7. If you want to use this form in an earlier version, you must instead use the slice! method.

It is mentioned up in the docs, but here it is again for reference:

# Ruby >= 1.8.7
p = list.pop(n)

# Ruby < 1.8.7
p = list.slice!(-n, n)
October 5, 2009
3 thanks

collect/map

If you’d like to use this method for something like Enumerable#collect, you are looking at the wrong place. This method will return the initial integer, not the values from the block.

a = 20.times { |n| n * 2 } #=> 20

Instead, use Range#collect:

a = (0...20).collect { n * 2 }
October 5, 2009
0 thanks

Shortcut with %

@tordans for multiple args wrap the args in an array

October 3, 2009
0 thanks

Shortcut with %

Thanks iamcata, that works :). And I finally found the right place to put this comment: http://apidock.com/ruby/String/%25#726-Use-it-with-HAML

October 3, 2009
1 thank

Use it with HAML

Like Henrik pointed out <a href=“http://henrik.nyh.se/2008/01/surround-helper-alternative-in-haml”>in his blogpost, this method is particulary useful when using HAML (http://haml-lang.com/) in Rails.

Instead of usind the HAML-Helper ‘surround’ (etc) you can just write

= "(%s)" % link_to("Edit", ...)

Or with two Arguments:

= "(%s, %s)" % ["Edit", "Delete"]

Thanks very much, Henrik!

October 2, 2009
1 thank

Shortcut

You can try:

“(%s %s)” % [“foo”, “bar”]

September 28, 2009
1 thank

Shortcut

According to http://henrik.nyh.se/2008/01/surround-helper-alternative-in-haml there is a short version for sprintf:

“(%s)” % “foo” is the same as sprintf(“(%s)”, “foo”)

Can someone who knows write more about this here? How do I work with multiple strings? Is this even possible? “(%s %t)” % “foo”, “bar” does not work.

September 9, 2009
1 thank

extend adds class methods too

Because classes are objects. So for example:

module Ispeak
  def says
    "greetings aliens!"
  end
end

module Ieat
  def eats
    "spinach"
  end
end

module Inhabitant
  def says
    "I'm strong to the finish"
  end
end

class Human
  extend Ispeak # add class methods from Ispeak
  include Inhabitant # add instance methods from Inhabitant
end

Human.extend Ieat # add class methods from Ieat

puts Human.says # -> greetings aliens!
puts Human.eats # -> spinach

popeye = Human.new

puts popeye.says # -> I'm strong to the finish