Archive → Author
Architecture the Lost Years by Robert Martin
This post by Greg Moreno is from Greg Moreno.
More Ruby tips and tricks
String to number conversion gotcha
>> Float('3.14159')
=> 3.14159
>> '3.14159'.to_f
=> 3.14159
# However, Float() method will return an exception if given
# a bad input while to_f() will ignore everything from the
# offending character.
>> Float('3.x14159')
ArgumentError: invalid value for Float(): "3.x14159"
from (irb):4:in 'Float'
from (irb):4
>> '3.x14159'.to_f
=> 3.0
# Similar case with to_i() and Integer().
>> Integer('19x69')
ArgumentError: invalid value for Integer(): "19x69"
from (irb):15:in 'Integer'
from (irb):15
from /Users/greg/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in '<main>'
>> '19x69'.to_i
=> 19
Case insensitive regular expression
# Regex is case sensitive by default. # Adding 'i' for insensitive match puts 'matches' if /AM/i =~ 'am'
Hash is ordered in 1.9
# new syntax in 1.9
h = {first: 'a', second: 'b', third: 'c'}
# hashes in 1.9 are ordered
h.each do |e|
pp e
end
Filter a list using several conditions
conditions = [
proc { |i| i > 5 },
proc { |i| (i % 2).zero? },
proc { |i| (i % 3).zero? }
]
matches = (1..100).select do |i|
conditions.all? { |c| c[i] }
end
Randomly pick an element from an array
>> [1,2,3,4,5].sample => 2 >> [1,2,3,4,5].sample => 1 # pick 2 random elements >> [1,2,3,4,5].sample(2) => [1, 5]
List methods unique to a class
# List all instance methods that starts with 're' # including those inherited by String. >> String.instance_methods.grep /^re/ => [:replace, :reverse, :reverse!, :respond_to?, :respond_to_missing?] # List methods unique to String, i.e. not include # those defined by its ancestors. >> String.instance_methods(false).grep /^re/ => [:replace, :reverse, :reverse!]
Globbing key-value pairs
>> h = Hash['a', 1, 'b', 2]
=> {"a"=>1, "b"=>2}
>> h = Hash[ [ ['a', 1], ['b', 2] ] ]
=> {"a"=>1, "b"=>2}
>> h = Hash[ 'a' => 1, 'b' => 2 ]
=> {"a"=>1, "b"=>2}
# The first form is very useful for globbing key-value pairs in Rails’ routes. For example, if you have the following:
# route definition in Rails 3
match 'items/*specs' => 'items#specs'
# sample url
http://localhost:3000/items/year/1969/month/7/day/21
# params[:specs] will be set
>> params[:specs]
=> "year/1969/month/7/day/21"
>> h = Hash[*params[:specs].split('/')]
=> {"year"=>"1969", "month"=>"7", "day"=>"21"}
This post by Greg Moreno is from Greg Moreno.
24 Ruby tips and tricks
Peter Cooper will share more tips in his book to be released later this year. Stay tune and don’t forget to leave your email address to get updates at http://rubyreloaded.com/trickshots/
Here are some of the tips in the video.
Generate random numbers within a given range
irb(main):019:0> rand(10..20) => 12 irb(main):020:0> rand(10...20) # works with exclusive range => 16
Dump your object using awesome_print
# Install the gem first
gem install awesome_print
irb(main):001:0> require 'ap'
=> true
irb(main):002:0> ap :a => 1, :b => 'greg', :c => [1,2,3]
{
:a => 1,
:b => "greg",
:c => [
[0] 1,
[1] 2,
[2] 3
]
}
=> {:a=>1, :b=>"greg", :c=>[1, 2, 3]}
Concatenating strings
irb(main):005:0> "abc" + "def"
=> "abcdef"
irb(main):006:0> "abc".concat("def")
=> "abcdef"
irb(main):007:0> x = "abc" "def"
=> "abcdef"
Include modules in a single line
class MyClass include Module1, Module2, Module3 # However, the modules are included in reverse order. Confusing eh! end
Instance variable interpolation
irb(main):008:0> @name = "greg"
=> "greg"
irb(main):009:0> "my name is #{@name}"
=> "my name is greg"
irb(main):010:0> "my name is #@name"
=> "my name is greg"
I still prefer the use curly braces.
Syntax checking
➜ ruby -c facu.rb facu.rb:12: syntax error, unexpected keyword_end, expecting $end
Zipping arrays
irb(main):027:0> names = %w(fred jess john) => ["fred", "jess", "john"] irb(main):028:0> ages = [38, 47,91] => [38, 47, 91] irb(main):029:0> locations = %w(spain france usa) => ["spain", "france", "usa"] irb(main):030:0> names.zip(ages) => [["fred", 38], ["jess", 47], ["john", 91]] irb(main):031:0> names.zip(ages, locations) => [["fred", 38, "spain"], ["jess", 47, "france"], ["john", 91, "usa"]]
Range into arrays
irb(main):034:0> (10..20).to_a # what I used to do => [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] irb(main):035:0> [*10..20] => [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Using parameter as default value
irb(main):047:0> def method(a, b=a); "#{a} #{b}"; end
=> nil
irb(main):048:0> method 1
=> "1 1"
irb(main):049:0> method 1, 2
=> "1 2"
Put regex match in a variable
irb(main):058:0> s = "Greg Moreno" => "Greg Moreno" irb(main):059:0> /(?<first>\w+) (?<second>\w+)/ =~ s => 0 irb(main):060:0> first => "Greg" irb(main):061:0> second => "Moreno"
This post by Greg Moreno is from Greg Moreno.
Ruby 101: method_missing gotchas
Forgetting ‘super’ with ‘method_missing’
method_missing is a hallmark of Ruby metaprogramming. It is one of those coding techniques that you need to master if you want to move from white belt to black belt. Aside from that, it is also fun to use.
class RadioActive
def initialize(path)
...
end
def to_format(format)
...
end
end
d = RadioActive.new('/path/to/uranium')
d.to_format('xml')
It works but you want explicit methods for each format. So you tap into your Ruby skills and implement your own method_missing.
class RadioActive
def method_missing(name, *args)
if name.to_s =~ /^to_(\w+)$/
to_format($1)
end
end
end
d = RadioActive.new('/path/to/uranium')
d.to_xml # WORKS
d.to_format('xml') # WORKS
d.undefined_method # FAILS
Unfortunately, the last call to ‘undefined_method’ fails. Actually, you would not know it fails because Ruby will not fire any exception. In case there is an undefined method, let us see how Ruby handles it.
>> s = 'uranium' => "uranium" >> s.to_xml NoMethodError: undefined method `to_xml' for "uranium":String
There you go. But there is no need to raise the ‘NoMethodError’ in your code. Instead, simply call ‘super’ if you are not handling the method. Whether you have your own class or inheriting from another, do not forget to call ‘super’ with your ‘method_missing’
class RadioActive
def method_missing(name, *args)
if name.to_s =~ /^to_(\w+)$/
to_format($1)
else
super
end
end
end
d = RadioActive.new('/path/to/uranium')
d.undefined_method
# => in `method_missing': undefined method `undefined_method' for # (NoMethodError)
Calling ‘super’ is not just for ‘missing_method’. You also need to do the same for the other hook methods like ‘const_missing’, ‘append_features’, ‘method_added’.
Forgetting respond_to?
If you modify ‘method_missing’, it will also affect the behavior of ‘respond_to?’ because what you are adding in as methods do not actually exist — they are ghost methods. If you check the list of instance methods for our class, it will only show 2.
>> RadioActive.instance_methods(false)
=> ["method_missing", "to_format"]
>> d.respond_to?('to_format')
=> true
>> d.respond_to?('to_xml')
=> false
Every time you modify ‘method_missing’, you also need to update ‘respond_to?’
class RadioActive
def respond_to?(name)
!!(name.to_s =~ /^to_/ || super)
end
end
This post by Greg Moreno is from Greg Moreno.
Preventing model explosion via Rails serialization
A great thing about ActiveRecord is you can easily add a new model to your application and play around with it as you progress. However, this power can easily be overused leading to unnecessary overhead in your code.
Consider the case where you have preferences for each user. For example, a user may opt to show or hide his email address, adjust his timezone, or language. One solution is to simply add new columns to the users table that correspond to each preference type. For example, you can have a ‘show_email’, ‘timezone’, ‘locale’ columns in the ‘users’ table, which can make your table become wide as you add more preferences options. Another option is to use a separate ‘preferences’ table.
class User < ActiveRecord::Base
has_many :preferences
end
class Preferences < ActiveRecord::Base
belongs_to :user
# name - preference name
# value - preference value
end
Note there is no user interface to add or remove ‘preferences’, i.e. the kinds of preferences are fixed. Of course, in the future you may add a new kind of preference but this kind of work is better done outside of the user interface. Since that is the case, there is no need to represent ‘preferences’ as a separate model.
One better alternative is to use Rails serialization to store the different kinds and user-specific values. The code would look like this:
class User < ActiveRecord::Base
serialize :preferences, Hash
end
u = User.new
u.preferences = {:show_email => true, :locale => :en }
u.save
# somewhere in your view using haml
- if @user.preferences[:show_email]
= @user.email
Using ‘serialize’ results in less code, fewer tables, and less overall complexity. However, with serialization you lose the ability to efficiently search the preferences data. The million-dollar question is do you need to query these preferences? Do you need a finder that returns all users who wants to show their email?
One issue I had with ‘serialize’ is that by using it, I expose the implementation details. In the display example above, it is obvious I had it stored as a Hash. I would rather hide this detail and present the preferences attributes as user attributes instead. I also want default values for every user.
For example:
u = User.new # automatically assigns the default preferences
u.preferences
=> {:show_email => false, :locale => :en}
u.show_email = true # I can change it like an attribute via @user.update_attributes(params[:user])
u.preferences
=> {:show_email => true, :locale => :en}
I have created a module to support this. It is not a unique problem so others may have probably released a gem or plugin to do this. (I actually never bothered to search for one.) Nevertheless, it was a good exercise in metaprogramming.
To use my implementation, simply call ‘serializeable’ with the column you want to serialize and the default values.
class User < ActiveRecord::Base
serializeable :preferences, :show_email => true, :locale => :en
end
Below is the implementation of ‘serializeable’. The convention is to save it under your ‘lib’ folder and include it in your ‘config/application.rb’ if you are using Rails 3.
module AttributeSerializer
module ActiveRecordExtensions
module ClassMethods
def serializeable(serialized, serialized_accessors={})
serialize serialized, serialized_accessors.class
serialized_attr_accessor serialized, serialized_accessors
default_serialized_attr serialized, serialized_accessors
end
# Creates the accessors
def serialized_attr_accessor(serialized, accessors)
accessors.keys.each do |k|
define_method("#{k}") do
self[serialized] && self[serialized][k]
end
define_method("#{k}=") do |value|
self[serialized][k] = value
end
end
end
# Sets the default value of the serialized field
def default_serialized_attr(serialized, accessors)
method_name = "set_default_#{serialized}"
after_initialize method_name
define_method(method_name) do
self[serialized] = accessors if self[serialized].nil?
end
end
end
end
end
class ActiveRecord::Base
extend AttributeSerializer::ActiveRecordExtensions::ClassMethods
end
ActiveRecord is both easy and powerful. It can also lead to misuse and abuse. Even though you are adding just one model, remember that it is not just the model class itself. You are also adding the database migrations, unit tests, factories, finders, and validations that go along with the model. Next time you have a new requirement, see if serialization can do a better job.
Update: Adam Cuppy converted this code into a Rails plugin while Jay added dynamic finder methods. I also moved this into a gem I called ‘fancy_serializer‘.
This post by Greg Moreno is from Greg Moreno.
Ruby 101: Improving your code by defining methods dynamically
Let’s say you have a user and you want to check its role.
class User
attr_accessor :role
end
u = User.new
u.role = 'admin'
# somewhere in your code you check the role
if u.role == 'admin'
puts 'admin'
elsif u.role == 'moderator'
puts 'moderator'
elsif u.role == 'guest'
puts 'guest'
end
Using a string value is bad code and you can improve this by using constants instead. But still, this is bad code becauses it exposes implementation details of your User class.
For our first improvement, we define methods that check the user’s role and hide the implementation of the role checking inside the User class.
class User
attr_accessor :role
def is_admin?
self.role == 'admin'
end
def is_moderator?
self.role == 'moderator'
end
def is_guest?
self.role == 'guest'
end
end
u = User.new
u.role = 'guest'
if u.is_admin?
puts 'admin'
elsif u.is_moderator?
puts 'moderator'
elsif u.is_guest?
puts 'guest'
end
Our first improvement is definitely better than the original but there are duplicate code in the role checking. You can eliminate the duplicate code by delegating the role checking to a single method.
class User
attr_accessor :role
def is_admin?
is_role? 'admin'
end
def is_moderator?
is_role? 'moderator'
end
def is_guest?
is_role? 'guest'
end
protected
def is_role?(name)
self.role == name
end
end
Our second improvement is a classic refactoring technique and common in any modern programming language. In other words, there is nothing “Ruby” about it. Before you get bored, I will now show the Ruby version.
The Ruby version uses ‘define_method()’ to further eliminate duplicate code.
class User
attr_accessor :role
def self.has_role(name)
define_method("is_#{name}?") do
self.role == "#{name}"
end
end
has_role :admin
has_role :moderator
has_role :guest
end
By using ‘define_method()’, we were able to add instance methods to our class User. You can check the new instance methods via irb.
ruby-1.9.2-p0 > User.instance_methods.grep /^is/ => [:is_admin?, :is_moderator?, :is_guest?, :is_a?]
Note that ‘has_role()’ is just another method and as such you can modify it to accept several parameters, an array, or other class. For example, we can make ‘has_role’ accept a list of roles.
class User
attr_accessor :role
def self.has_roles(*names)
names.each do |name|
define_method("is_#{name}?") do
self.role == "#{name}"
end
end
end
has_roles :admin, :moderator, :guest
end
This post by Greg Moreno is from Greg Moreno.
How to use OpenAmplify with Ruby
The OpenAmplify API reads text you supply and returns linguistic data explaining and classifying the content. What you do with that analysis is, in the fine tradition of APIs and mashups, up to you. Some possibilities might include pairing ads with articles, creating rich tag-clouds, or monitoring the tone of forum threads.
I created a ruby gem to simplify the use of the OpenAmplify API. It’s still in the early stages but should be enough to get you started.
Installation
gem install openamplify
Usage:
require 'openamplify'
API_KEY = "register to get a key"
client = OpenAmplify::Client.new(:api_key => API_KEY)
text = "After getting the MX1000 laser mouse and the Z-5500 speakers i fell in love with logitech"
response = client.analyze_text(text)
# List all the keys and values returned by OpenAmplify
response.each do |k, v|
pp k
pp v
end
# 'response' works like a Hash
puts response['Topics']
puts response['Demographics']
Output Format
In case you need a different format, OpenAmplify supports XML, JSON, RDF, CSV. It can also return the result as a fancy HTML page.
# assuming you use Nokogiri
doc = Nokogiri::XML(response.to_xml)
# or you want a JSON
json = JSON.parse(response.to_json)
# you should really try the pretty formats
puts response.to_pretty
# or
puts response.to_signals
The source code is available in github: http://github.com/gregmoreno/openamplify
This post is also available at Ruby On Cloud.
Related posts:
- Ruby 101: Make your class behave like a Ruby built-in I got re-acquianted with this scenario while working on the OpenAmplify gem – a wrapper for the OpenAmplify API. When you give the api a text like a blog comment,...
- How to read Google buzz updates in Ruby require 'rubygems' require 'nokogiri' require 'open-uri' require 'feedzirra' profile_name = 'dave.winer' page = Nokogiri::HTML(open("http://www.google.com/profiles/#{profile_name}")) feed_url = page.search('//head/link[@type="application/atom+xml"]').first['href'] feed = Feedzirra::Feed.fetch_and_parse(feed_url) puts feed.title puts feed.url puts feed.last_modified feed.entries.each do |entry| puts...
- Ruby 101: How to filter an Array using proc Over at the PhRUG, a Ruby developer community based in the Philippines, we conduct code review sessions via our mailing list. A code is posted and members share alternative implementations....
This post by Greg Moreno is from MetaGreg.
How to use OpenAmplify with Ruby
The OpenAmplify API reads text you supply and returns linguistic data explaining and classifying the content. What you do with that analysis is, in the fine tradition of APIs and mashups, up to you. Some possibilities might include pairing ads with articles, creating rich tag-clouds, or monitoring the tone of forum threads.
I created a ruby gem to simplify the use of the OpenAmplify API. It’s still in the early stages but should be enough to get you started.
Output Format
In case you need a different format, OpenAmplify supports XML, JSON, RDF, CSV. It can also return the result as a fancy HTML page.
The source code is available in github: http://github.com/gregmoreno/openamplify
This post by Greg Moreno is from Greg Moreno.
Using RabbitMQ and AMQP with Ruby
This post is also available at Ruby On Cloud
AMQP stands for Advanced Message Queuing Protocol. RabbitMQ is a server that implements the protocol. RabbitMQ is available on Linux, OSX, and Windows. Installation instructions for RabbitMQ are available at http://www.rabbitmq.com/install.html
I will be running RabbitMQ on Ubuntu 9.10. First, let’s install the server:
apt-get install rabbitmq-server
Note the server is installed as a daemon after the installation. Just to be sure, let’s stop and start the server.
greg@gohan:~$ sudo /etc/init.d/rabbitmq-server stop
Stopping rabbitmq-server: rabbitmq-server.
greg@gohan:~$ sudo /etc/init.d/rabbitmq-server start
Starting rabbitmq-server: SUCCESS
rabbitmq-server.
Next, we install the amqp gem by Aman Gupta. The gem can also be found at http://github.com/tmm1/amqp
greg@gohan:~$ gem install amqp
Building native extensions. This could take a while...
Successfully installed eventmachine-0.12.10
Successfully installed amqp-0.6.7
2 gems installed
Let’s now build a simple publisher code. Note the difference in the gem’s name and the file you need to use the gem.
require 'mq'
Thread.new { AMQP.start }
# All queues are created automatically the first time it is
# accessed. Make sure this is the same queue our consumers
# will use.
q = MQ.queue('my_queue')
i=0
while true
i += 1
q.publish("Hello, World #{i}")
sleep(1)
end
Next, the consumer code:
require 'mq'
AMQP.start do
q = MQ.queue('my_queue')
q.subscribe do |msg|
puts "#{Time.now}: #{msg}" if msg
end
end
The ‘subscribe’ method registers with the queue telling it to call the block when a message has arrived. Alternatively, you can use ‘pop’ but this would constantly poll the server for new messages making unnecessary calls even if the queue is empty.
AMQP.start do
q = MQ.queue('my_queue')
q.pop do |msg|
puts "#{Time.now}: #{msg}" if msg
q.pop
end
end
If you want to run AMQP on several machines, just specify the location of the broker in your publisher and consumer code:
AMQP.start(:host => '192.168.0.195') do
# code here
end
You only need to run 1 rabbitmq server, which in my case is on Ubuntu. When you try the code in other machines, you only need the amqp gem installed.
I highly recommend “Distributed Programming with Ruby” by Mark Bates if you’re interested in distributed programming.
Related posts:
- How to setup a Rails 3 app I finally decided to give Rails 3 a spin after beta was released 20 days ago. In geek time, that’s being a late adopter. But first, a warning. I’ve read...
- Deploy a Rails 3, Sqlite3 application in Tomcat using JRuby and have a Ruby version running side-by-side. A few months ago I got interested in JRuby while researching for text mining algorithms. I found some gems but they are either...
- Ruby 101: How to add methods to a Ruby class Let’s add a method that checks whether an Array has many elements. a = [1,2,3] a.many? # NoMethodError: undefined method `many?' Let’s fix this by adding a new method to...
This post by Greg Moreno is from MetaGreg.
Using RabbitMQ and AMQP with Ruby
AMQP stands for Advanced Message Queuing Protocol. RabbitMQ is a server that implements the protocol. RabbitMQ is available on Linux, OSX, and Windows. Installation instructions for RabbitMQ are available at http://www.rabbitmq.com/install.html
I will be running RabbitMQ on Ubuntu 9.10. First, let’s install the server:
Next, we install the amqp gem by Aman Gupta. The gem can also be found at http://github.com/tmm1/amqp
Let’s now build a simple publisher code. Note the difference in the gem’s name and the file you need to use the gem.
All queues are created automatically the first time it is accessed. Make sure this is the same queue our consumers will use.
Next, the consumer code:
The ‘subscribe’ method registers with the queue telling it to call the block when a message has arrived. Alternatively, you can use ‘pop’ but this would constantly poll the server for new messages making unnecessary calls even if the queue is empty.
If you want to run AMQP on several machines, just specify the location of the broker in your publisher and consumer code:
You only need to run 1 rabbitmq server, which in my case is on Ubuntu. When you try the code in other machines, you only need the amqp gem installed.
I highly recommend Distributed Programming with Ruby by Mark Bates if you’re interested in distributed programming.
This post by Greg Moreno is from Greg Moreno.


Recent Comments