a day in the pit my view from inside

30Aug/110

Resque me with a custom Redis host

If you're working outside of Ruby on Rails and want to have Redis hosted non-locally than this is the command you're looking for:

Resque.redis = '<non_localhost_address>:<port>'

To paint the full picture: You've got Redis running on Machine1.Hostname and your Resque workers on Machine2.Hostname and you need those two wired up. In your Rake file for Resque workers you should add the line of code from above.

One little caveat, if you're told Resque is an uninitialized constant than you just simply need to get Resque into your environment. Here's an example IRB session where I'm using Bundler/Gemfile:

ree-1.8.7-2011.03 :001 > require 'rubygems'
 => false 
ree-1.8.7-2011.03 :002 > require 'bundler/setup'
 => true 
ree-1.8.7-2011.03 :003 > Bundler.require
=> [<Bundler::Dependency type=:runtime name="SystemTimer" requirements=">= 0">, <Bundler::Dependency type=:runtime name="rake" requirements=">= 0">, <Bundler::Dependency type=:runtime name="redis" requirements=">= 0">, <Bundler::Dependency type=:runtime name="redis-namespace" requirements=">= 0">, <Bundler::Dependency type=:runtime name="yajl-ruby" requirements=">= 0">, <Bundler::Dependency type=:runtime name="resque" requirements=">= 0">, <Bundler::Dependency type=:runtime name="ruby-prof" requirements=">= 0">, <Bundler::Dependency type=:runtime name="json" requirements=">= 0">, <Bundler::Dependency type=:runtime name="nokogiri" requirements=">= 0">, <Bundler::Dependency type=:runtime name="fastercsv" requirements=">= 0">, <Bundler::Dependency type=:runtime name="yieldmanager" requirements=">= 0">]

Cool, now we've got our environment and can do what we want. It's not necessary to use a Gemfile, you can manually install your gems and then require them in your Ruby file. But since I have multiple files doing many different things it makes sense to have one common section for inclusion of my packages. I also encourage creating a ruby include file with your Redis setups; this will prevent you from having that Resque.redis scattered in all of your files that access Redis.

Hope this saves someone a minute. More information is in Defunkt's Resque package on Github.

Filed under: Code, Redis, Resque, Ruby No Comments
1Aug/110

Under the hood – Ruby methods Array#map, Array#inject

In the spirit or writing some Ruby methods I'm going to add three in this post. We'll begin with mapping elements of an Array.

class Array
    def rb_map
        n_array = []
        self.each { |elm| n_array << yield(elm) }
        n_array
    end
end

Then we do Inject,

class Array
    def rb_inject initial = nil
        value = initial
        self.each { |elm| value = yield(value,elm) }
        value
    end
end

And finally, let's revisit map using our inject:

class Array
    def new_map_using_inject
        rb_inject([]) { |set, elm| set << elm }
    end
end

You'll notice I did not do any error handling. That's not the point of the exercise; we want to know what the methods we call daily do behind the scenes.

Filed under: Code, Learning, Ruby No Comments
28Jul/110

Rails 3, Webrick, Mac OS X, Error: missing :action (ArgumentError)

I was making some changes in a routes.rb file and fat fingered the wrong line:

match '/public_html', "domains#public_html", :as => 'public'

Ran rails server, hit localhost:3000 and quickly saw a huge stack trace with this little gem of an error at the top:

/Users/miker/projects/projects/logthingy/vendor/bundle/ruby/1.8/gems/actionpack-3.0.7/lib/action_dispatch/routing/mapper.rb:171:in `default_controller_and_action': missing :action (ArgumentError)

To me, that error message is not the most helpful. Because the only change I had made was in routes.rb I knew the issue was there. By the way, this is where I feel a little bit sorry for newcomers to Ruby and Rails... The error messages are not always entirely clear. For those of us who have been in Ruby long enough to know the "breadcrumb logic" we can get by, but for those who don't it can be a nightmare.

The fix was simple and subtle... was missing some punctuation:

match '/public_html' => "domains#public_html", :as => 'public'
27Jul/115

Two variations on Ruby array#flatten

Implement Ruby's flatten method for an Array (without taking any peaks at the source!). Probably not the hardest thing you'll ever have to do, but there are many ways to do it... Jon and I each take a blind stab at it. Here's our effort:

Mike's variation

def mikes_flatten arr, level = nil
  result = []
  arr.each do |elem|
    recursive_flatten(result, elem,level,0)
  end
  result
end
 
def recursive_flatten(result, elem,level,current_level)
  if elem.is_a?(Array)
    elem.each do |el|
      if level.nil? || current_level &lt; level
        recursive_flatten(result, el,level,current_level+1)
      else
        result &lt;&lt; el
      end
    end
  else
    result &lt;&lt; elem
  end
  result
end

Jon's variation

def flatten values, level=-1
  flat = []
  values.each do |value|
    if level != 0 &amp;&amp; value.kind_of?(Array)
      flat.concat(flatten(value, level-1))
    else
      flat &lt;&lt; value
    end
  end
  flat
end

Perf different can be seen by Jon's benchmark code (flatten.rb):

#!/usr/bin/ruby
require 'benchmark'
 
# Insert flatten methods here
 
def random_value
  if rand(3) &lt; 1
    (rand(5)+1).times.map { |i| random_value }
  else
    rand(10000)
  end
end
 
VALUE = 100.times.map { |i| random_value}
ITERATIONS = 1000
 
Benchmark.bm do |b|
  puts "Iterations: #{ITERATIONS}"
  b.report("Mikes version") do
    ITERATIONS.times { |i| mikes_flatten(VALUE)}
  end
  b.report("Jons version") do
    ITERATIONS.times { |i| flatten(VALUE)}
  end
end

Jon's is pretty succinct. When I first went about writing things out I was going for the "get it to work." Either way, I don't see a huge performance difference... They toggle back and forth by about a half a second.

[13:57:11 miker@laughwhat-lm ~/Downloads] $ ruby flatten.rb
      user     system      total        real
Iterations: 10000
Mikes version  2.920000   0.010000   2.930000 (  2.933274)
Jons version  2.990000   0.000000   2.990000 (  3.017929)
 
[13:57:25 miker@laughwhat-lm ~/Downloads] $ ruby flatten.rb
      user     system      total        real
Iterations: 10000
Mikes version  3.010000   0.010000   3.020000 (  3.030987)
Jons version  2.980000   0.010000   2.990000 (  2.999335)
 
[13:57:34 miker@laughwhat-lm ~/Downloads] $ ruby flatten.rb
      user     system      total        real
Iterations: 100000
Mikes version 26.860000   0.050000  26.910000 ( 26.973855)
Jons version 24.050000   0.050000  24.100000 ( 24.126557)
 
[13:58:34 miker@laughwhat-lm ~/Downloads] $ ruby flatten.rb
      user     system      total        real
Iterations: 100000
Mikes version 24.120000   0.050000  24.170000 ( 24.201227)
Jons version 25.080000   0.050000  25.130000 ( 25.196811)

Write your own and submit as a comment. Let's see some other ways to do this :)

Filed under: Benchmark, Code, Ruby 5 Comments
19Jul/111

Hanging in the Treetops

Parsing in Ruby is pretty easy is you know what you're doing. Fortunately, for people like me, who don't, we have handy gems that do a lot of work for us.

I wanted to find a way to parse short urls without making any database calls. Since I have a pattern for short urls I figured, for a bit of fun, a parser would make this possible.

Enter Treetop: "Treetop is a language for describing languages. Combining the elegance of Ruby with cutting-edge parsing expression grammars, it helps you analyze syntax with revolutionary ease." Treetop

The grammar defined is straight forward: (FILE: message_grammar.treetop)

grammar MessageGrammar
 rule message
   [0-9] / 'X' message / ('Y' / 'Z') message message
 end
end

So here are some valid codes: 0, X0, XY00, XX0, XY09
Invalid codes: T0, P, PPPPP0, X0X0X00

Make sure you've got treetop installed. Drop into terminal in the directory of the grammar file and run
tt message_grammar.treetop
This will result in a file called message_grammar.rb which you can include in another file to use as your parser...

File: message_parser.rb

require "rubygems"
require "treetop"
require "polyglot"
require "message_grammar"
 
# MessgeGrammarParser is a generated Parsing class based on the grammar
# defined in message_grammar.treetop
parser = MessageGrammarParser.new
 
STDIN.each do |string|
 
 # for each string, split on whitespace
 string.split(" ").each do |message|
   # print status of whether the message could be parsed or not
   puts "#{message} #{parser.parse(message) ? 'VALID' : 'INVALID'}"
 end
end

And we're Done-zo Washington. Parse away.

Filed under: Code, Learning, Ruby, Software 1 Comment