Design Patterns in Ruby by Russ Olsen | Jason Meridth’s Blog

After nudging by Joe Ocampo and Scott Bellware, I finally sat down at finished “Design Patterns in Ruby” by Russ Olsen.
The format of most of the chapters made the book an interesting read:
1. A introduction to why you might need the pattern
2. A static language developer’s approach with the Ruby language
3. A seasoned Ruby developer’s approach to the design pattern
4. Using and Abusing
5. in the wild
6. Wrapping up
Some of the items that I learned [LosTechies is not a cult contrary to some of the examples you read below; some of the examples below are using LosTechies nomenclature but closely resemble what the author had in the book]
If any of the stuff below intrigues you: GO BUY THE BOOK. You won’t regret it. Even if you are trying to understand patterns in another language. Russ Olsen does an excellent job explaining the INTENT of the patterns.
FUN
When teaching the reader about “Truth, Lies, and nil”, the author even pokes fun at himself:

'russ' == 'smart'    # sadly, false

BOOLEAN
In Ruby, zero, being neither false nor nil, evaluates to true in Boolean expression.

if 0 puts('Zero is true!')end

will print out: Zero is true!

ARRAYS
Points for matrix reference in array examples

x = []y = Array.newa = ['neo', 'trinity', 'tank']

REGULAR EXPRESSIONS
The flow of the language when creating regular expressions:

/old/ =~ 'this old house'     # 5 - the index of 'old'/Russ|Russell/ =~ 'Fred'      # nil - Fred is not Russ nor Russell/.*/ =~ 'any old string'      # 0 - the RE will match anything

ARBITRARY PARAMETERS
Any author that uses DC comic character to explain arbitrary numbers of arguments, is a winner in my mind:

def describe_hero(name, *super_powers> puts("Name: #{name}") for power in super_powers     puts("Super power: #{power}") endenddescribe_hero("Batman")describe_hero("Flash", "speed")describe_hero("Superman", "can fly", "x-ray vision", "invulnerable")   # w00t!

DUCK TYPING AND UNIT TESTS
He mentions duck typing and the fact that “Unit Tests Are Not Optional” is a section heading when teaching the Template Method Pattern.

PROCS AND BLOCKS

# using the do/end notationhello = lambda do puts('Hello') puts('I am a follower of Pablo')end#you may use curly braces instead of do/endhello = lambda { puts('Hello, I am a follower of Pablo')}#the preferred way to use curly braceshello = lambda {puts('Hello, I am a follower of Pablo')}

STRATEGY PATTERN
using proc-based formatters to create a ruby-based strategy pattern

class Report attr_reader :title, :text attr_accessor :formatter def initialize(&formatter)   @title = 'Monthly Report'   @text = ['Things are going', 'really, really well.' ]   @formatter = formatter end def output_report   @formatter.call( self ) endendHTML_FORMATTER = lambda do |context|...code to output HTMLreport = Report.new &HTML_FORMATTERreport.output_report

You could create any type of formatter you want in a proc instead creating new classes.

OBSERVER PATTERN
Modules exist that encapsulate things that some of us static developers might already be used to:

require 'observer'class Employeeinclude Observableattr_reader :name, :addressattr_reader :salarydef initialize( name, title, salary) @name = name @title = title @salary = salaryenddef salary=(new_salary) @salary = new_salary changed notify_observers(self)endend

ITERATOR PATTERN
Internal Iterators versus External Iterators: (had never heard it put this way)
External iterator – client drives the iteration…you won’t call next until you are good and ready for the next element
Internal iterator – the aggregate relentlessly pushes the code block to accept item after item.

COMMAND PATTERN
The Command pattern translates very smoothly into code blocks. Here is a PabloForPresidentButton class reworked to use code blocks:

class PabloForPresidentButtonattr_accessor :commanddef initialize(&block) @command = blockend## Lots of button drawing and management# code omitted ...#def on_button_push @command.call if @commandendendnew_button = PabloForPresidentButton.new do## Make a developer stop looking so nerdy# by placing one over his pocket protector#end

The author does not diminish the needs for classes. For straightforward actions, use a Proc object. For complex object or objects that will carry around a lot of state, create a command class.

ADAPTER PATTERN
Instead of adhering to some interface and trying to create your adapter, why not just extend the original class.

# load original classrequire 'lostechies_text_object'# now add some methods to original classclass LosTechiesTextObjectdef sponsor return friend_of_pabloenddef blogger return follower_of_pabloendend

Before any of you Open-Closed people attack, please re-read the definition of OCP – Open for extension, closed for modification. Doesn’t this adhere to that? 🙂

PROXY PATTERN
There are three tyes of Proxies: The Protection Proxy, Remote Proxy, Virtual Proxy
These are mentioned in the Gang of Four book. He introduces a Ruby-esqe way to approach proxies: the method_missing Method

class AccountProxydef initialize(real_account) @subject = real_accountenddef method_missing(name, *args) puts("Delegating #{name} message to subject.") @subject.send(name, *args)endendap = AccountProxy.new( BankAccount.new(100) )ap.deposit(25)ap.withdraw(50)puts("account balance is now: #{ap.balance}")

Will output:
delegating deposit method to subject.
delegating withdraw method to subject.
delegating balance method to subject.
account balance is now: 75

DECORATOR PATTERN

module Decorator1def do_something(common_item_to_decorate) #codeendendmodule Decorator2def do_something(common_item_to_decorate) #codeendendd = SimpleItem.new()d.extend(Decorator1)d.extend(Decorator2)e.do_something('howdy')

SINGLETON PATTERN
The author admits the career of the singleton has been checkered, but still shows that you can use it in the Ruby world. An example he gives us to allow testing of singleton implementation code is to put the implementation code in a base class and have the child be the singleton:

require 'singleton'class SimpleLogger#  All of the logging functionality in this class...#  Test this codeendclass SingletonLogger < SimpleLogger

FACTORY/ABSTRACT FACTORY PATTERN
I never had it straight, exactly, what the difference between these patterns were (yes, besides name). I never bothered to look. According to the author, Factory returns back a single object while Abstract Factory is “an object dedicated to creating a compatible set of objects”. According to GoF book (which I have open in front of me), Abstract Factory “provides an interface for creating families of related or dependent objects without specifying their concreate classes”. Which one do you think would have turned the light bulb off in your head? 🙂
The other item was using “Convention Over Configuration” to generate abstract factories:

class IOFactorydef initialize(format) @reader_class = self.class.const_get("#{format}Reader") @writer_class = self.class.const_get("#{format}Writer")enddef new_reader @reader_class.newenddef new_writer @writer_class.newendendhtml_factory = IOFactory.new('HTML')html_reader = html_factory.new_readerpdf_factory = IOFactory.new('PDF')pdf_writer = pdf_factory.new_writer

Notice the correct classes (reader/writer) are generated dynamically with the help of the const_get Ruby method.

BUILDER PATTERN
Magic methods: “very easy to implement using the method_missing technique…you simply catch all unexpected method calls with method_missing and parse the method name to see if it matches the pattern of your magic method name”.

#example method passed into computer builder classbuilder.add_dvd_and_harddisk#orbuilder.add_turbo_and_dvd_dvd_and_harddiskdef method_missing(name, *args)words = name.to_s.split("_")return super(name, *args) unless words.shift == 'add'words.each do |word| #next is same as continue in for loop in C# next if word == 'and' #each of the following method calls are a part of the builder class add_cd if word == 'cd' add_dvd if word == 'dvd' add_hard_disk(100000) if word == 'harddisk' turbo if word == 'turbo'endend

Last 3 Chapters are the meat of the book:
INTERPRETER PATTERN (place where Bellware told me to start)
DOMAIN SPECIFIC LANGUAGE (DSL)
CONVENTION OVER CONFIGURATION
Those stay obscure so you can go read it. I think the book is worth a visit on Safari books if you have an account or worth the purchase for the bathroom reading.
I enjoyed it. Thanks Scott and Joe

Data mining with Ruby and Twitter

In October 2008, like many others, I created a Twitter account out of curiosity. Like most people, I connected with friends and did some random searching to better understand the service. Communicating at 140 characters didn’t seem like an idea that would be popular. An unrelated event helped me understand Twitter’s real value.

In early July 2009, my web-hosting provider went dark. After random web searching, I found information pointing to a fire in Seattle’s Fisher Plaza as the culprit. Information from traditional web-based sources was slow and gave no indication of when the service might return. However, after searching Twitter, I found personal accounts of the incident, including real-time information on what was happening at the scene. For example, shortly before my hosting service returned, there was a tweet indicating that diesel power generators were outside the building.

This was when I realized that the true power of Twitter is open and real-time communication of information among individuals and groups. Yet, under the surface, it is a treasure trove of information about behaviors of the users, and trends at the local and global levels. I explore this realization in the context of simple scripts using the Ruby language and the Twitter gem, an API wrapper for Twitter. I also demonstrate how to build simple mashups for data visualization using other web services and applications.

Ruby knowledge

If you do not have basic knowledge of the wonderful Ruby language, find references in the Resources section. These examples demonstrate the value of Ruby and its ability to encode a significant amount of power in a limited number of source lines of code.

Twitter and APIs

Although the early web was about human-machine interaction, today’s web is about machine-machine interaction, enabled using web services. These services exist for most popular websites—from various Google services to LinkedIn, Facebook, and Twitter. Web services create APIs through which external applications can query or manipulate content on websites.

Web services are implemented using a number of styles. Today, one of the most popular is Representational State Transfer, or REST. One implementation of REST is over the well-known HTTP protocol, allowing HTTP to exist as a medium for a RESTful architecture (using standard HTTP operations like GET, PUT, POST, and DELETE). The API for Twitter is developed as an abstraction over this medium. In this way, there’s no knowledge of REST, HTTP, or data formats like XML or JSON, but instead an object-based interface that integrates cleanly into the Ruby language.

Back to top

A quick tour of Ruby and Twitter

Let’s explore how you can use the Twitter API with Ruby. First, we need to get the necessary resources. If like me you’re using Ubuntu Linux®, you use the apt framework.

To get the latest full Ruby distribution (approximately a 13MB download), use this command line:

$ sudo apt-get install ruby1.9.1-full

Next, grab the Twitter gem using the gem utility:

$ sudo gem install twitter

You now have everything you need for this step, so let’s continue with a test of the Twitter wrapper. For this demonstration, use a shell called the Interactive Ruby Shell (IRB). This shell allows you to execute Ruby commands and experiment with the language in real time. IRB has a large number of capabilities, but we’ll use it for simple experimentation.

Listing 1 shows a session with IRB that has been broken into three sections to aid readability. The first section (lines 001 and 002) simply prepares the environment by importing the necessary run time elements (the require method loads and executes the named library). The next line (003) demonstrates the use of the Twitter gem to display the most recent tweet from IBM® developerWorks®. As shown, you use the user_timeline method of the Client::Timeline module to display a tweet. This first example demonstrates the “chain methods” capability of Ruby. The user_timeline method returns an array of 20 tweets that you chain into the first method. Doing so extracts the first tweet from the array (first is a method of the Array class). From this single tweet, you extract the text field emitted to output via puts.

The next section (line 004) uses the user-defined location field, a free-form field into which the user can provide both useful and non-useful location information. In this example, the User module grabs user information, constrained with the location field.

The final section (from line 005) explores the Twitter::Search module. The search module provides an extremely rich interface with which to search Twitter. In this example, you first create a search instance (line 005), then specify a search at line 006. You’re searching for the most recent tweets containing the word why that are directed to the LulzSec user. The resulting list has been reduced and edited. Searches are sticky in that the search instance maintains the defined filters. You can clear these filters by executing search.clear.

Listing 1. Experimenting with the Twitter API through IRB

$ irbirb(main):001:0> require "rubygems"=> trueirb(main):002:0> require "twitter"=> trueirb(main):003:0> puts Twitter.user_timeline("developerworks").first.textdW Twitter is saving #IBM over $600K per month: will #Google+ add to that? > http://t.co/HiRwir7 #Tech #webdesign #Socialmedia #webapp #app=> nilirb(main):004:0> puts Twitter.user("MTimJones").locationColorado, USA=> nilirb(main):005:0> search = Twitter::Search.new=> #<Twitter::Search:0xb7437e04 @oauth_token_secret=nil,     @endpoint="https://api.twitter.com/1/",     @user_agent="Twitter Ruby Gem 1.6.0",     @oauth_token=nil, @consumer_secret=nil,     @search_endpoint="https://search.twitter.com/",     @query={:tude=>[], :q=>[]}, @cache=nil, @gateway=nil, @consumer_key=nil,     @proxy=nil, @format=:json, @adapter=:net_http<irb(main):006:0> search.containing("why").to("LulzSec").result_type("recent").each do |r| puts r.text end@LulzSec why not stop posting <bleep> and get a full time job! MYSQLi isn't hacking you <bleep>....irb(main):007:0>

Next, let’s look at the schema for a user in Twitter. You can also do this through IRB, but I’ll reformat the result to illustrate more simply the anatomy of a Twitter user. Listing 2 shows the result of printing the user structure, which in Ruby is a Hashie::Mash. This structure is useful, because it permits an object to have method-like accessors for hash keys (an open object). As you can see from Listing 2, this object contains a wealth of information (user-specific and rendering information), including current user status (with geocode information). A tweet also contains a large amount of information, and you can easily visualize generating this information using the user_timeline class.

Listing 2. Anatomy of a Twitter user (Ruby perspective)

irb(main):007:0> puts Twitter.user("MTimJones")<#Hashie::Mash   contributors_enabled=false   created_at="Wed Oct 08 20:40:53 +0000 2008"   default_profile=false default_profile_image=false   description="Platform Architect and author (Linux, Embedded, Networking, AI)."  favourites_count=1   follow_request_sent=nil   followers_count=148   following=nil   friends_count=96   geo_enabled=true   id=16655901 id_str="16655901"   is_translator=false   lang="en"   listed_count=10   location="Colorado, USA"   name="M. Tim Jones"   notifications=nil   profile_background_color="1A1B1F"   profile_background_image_url="..."  profile_background_image_url_https="..."   profile_background_tile=false   profile_image_url="http://a0.twimg.com/profile_images/851508584/bio_mtjones_normal.JPG"   profile_image_url_https="..."   profile_link_color="2FC2EF"   profile_sidebar_border_color="181A1E" profile_sidebar_fill_color="252429"   profile_text_color="666666"   profile_use_background_image=true   protected=false   screen_name="MTimJones"   show_all_inline_media=false   status=<#Hashie::Mash     contributors=nil coordinates=nil     created_at="Sat Jul 02 02:03:24 +0000 2011"     favorited=false     geo=nil     id=86978247602094080 id_str="86978247602094080"     in_reply_to_screen_name="AnonymousIRC"     in_reply_to_status_id=nil in_reply_to_status_id_str=nil     in_reply_to_user_id=225663702 in_reply_to_user_id_str="225663702"     place=<#Hashie::Mash       attributes=<#Hashie::Mash>       bounding_box=<#Hashie::Mash         coordinates=[[[-105.178387, 40.12596],                       [-105.034397, 40.12596],                       [-105.034397, 40.203495],                       [-105.178387, 40.203495]]]         type="Polygon"      >       country="United States" country_code="US"       full_name="Longmont, CO"       id="2736a5db074e8201"       name="Longmont" place_type="city"       url="http://api.twitter.com/1/geo/id/2736a5db074e8201.json"    >     retweet_count=0     retweeted=false     source="web"     text="@AnonymousIRC @anonymouSabu @LulzSec @atopiary @Anonakomis Practical reading           for future reference... LULZ "Prison 101" http://t.co/sf8jIH9" truncated=false  >  statuses_count=79   time_zone="Mountain Time (US & Canada)"   url="http://www.mtjones.com"   utc_offset=-25200   verified=false>=> nilirb(main):008:0>

That’s it for the quick tour. Now, let’s explore some simple scripts that you can use to collect and visualize data using Ruby and the Twitter API. Along the way, you’ll get to know some of the concepts of Twitter, such as authentication and rate limiting.

Back to top

Mining Twitter data

The following sections present several scripts for collecting and presenting data available through the Twitter API. These scripts focus on simplicity, but you can extend and combine them to create new capabilities. Further, this section touches the surface of the Twitter gem API, where many more capabilities are available.

It’s important to note that the Twitter API only allows clients to make a limited number of calls in a given hour, that is, Twitter rate-limits requests (currently no more than 150 per hour), which means that after some amount of use, you’ll get an error message and be required to wait before submitting new requests.

User information

Recall from Listing 2 that a large amount of information is available about each Twitter user. This information is only accessible if the user isn’t protected. Let’s look at how you can extract a user’s data and present it in a more convenient way.

Listing 3 presents a simple Ruby script to retrieve a user’s information (based on his or her screen name), and then emit some of the more useful elements. You use the to_s Ruby method to convert the value to a string as needed. Note that you first ensure that the user isn’t protected; otherwise, this data wouldn’t be accessible.

Listing 3. Simple script to extract Twitter user data (user.rb)

#!/usr/bin/env rubyrequire "rubygems"require "twitter"screen_name = String.new ARGV[0]a_user = Twitter.user(screen_name)if a_user.protected != true  puts "Username   : " + a_user.screen_name.to_s  puts "Name       : " + a_user.name  puts "Id         : " + a_user.id_str  puts "Location   : " + a_user.location  puts "User since : " + a_user.created_at.to_s  puts "Bio        : " + a_user.description.to_s  puts "Followers  : " + a_user.followers_count.to_s  puts "Friends    : " + a_user.friends_count.to_s  puts "Listed Cnt : " + a_user.listed_count.to_s  puts "Tweet Cnt  : " + a_user.statuses_count.to_s  puts "Geocoded   : " + a_user.geo_enabled.to_s  puts "Language   : " + a_user.lang  if (a_user.url != nil)    puts "URL        : " + a_user.url.to_s  end  if (a_user.time_zone != nil)    puts "Time Zone  : " + a_user.time_zone  end  puts "Verified   : " + a_user.verified.to_s  puts  tweet = Twitter.user_timeline(screen_name).first  puts "Tweet time : " + tweet.created_at  puts "Tweet ID   : " + tweet.id.to_s  puts "Tweet text : " + tweet.textend

To invoke this script, ensuring that it’s executable (chmod +x user.rb), you invoke it with a user. The result is shown in Listing 4 for the developerworks user, showing the user information and current status (last tweet information). Note here that Twitter defines followers as people who follow you; but people that you follow are called friends.

Listing 4. Sample output from user.rb

$ ./user.rb developerworksUsername   : developerworksName       : developerworksId         : 16362921Location   : User since : Fri Sep 19 13:10:39 +0000 2008Bio        : IBM's premier Web site for Java, Android, Linux, Open Source, PHP, Social, Cloud Computing, Google, jQuery, and Web developer educational resourcesFollowers  : 48439Friends    : 46299Listed Cnt : 3801Tweet Cnt  : 9831Geocoded   : falseLanguage   : enURL        : http://bit.ly/EQ7teTime Zone  : Pacific Time (US & Canada)Verified   : falseTweet time : Sun Jul 17 01:04:46 +0000 2011Tweet ID   : 92399309022167040Tweet text : dW Twitter is saving #IBM over $600K per month: will #Google+ add to that? > http://t.co/HiRwir7 #Tech #webdesign #Socialmedia #webapp #app

Friends popularity

Look at your friends (people you follow), and gather data to understand their popularity. In this case, you gather your friends and sort them in the order of their followers count. This simple script is shown in Listing 5.

In this script, after you understand the user you want to analyze (based on their screen name), you create a user hash. A Ruby hash (or associative array) is a data structure that allows you to define the key for storage (instead of a simple numerical index). Your hash is then indexed by Twitter screen name, and the associated value is the user’s follower count. The process is simply to iterate your friends and hash their followers count. Sort your hash (in descending order), and emit it as output.

Listing 5. Friend’s popularity script (friends.rb)

#!/usr/bin/env rubyrequire "rubygems"require "twitter"require 'google_chart'name = String.new ARGV[0]user = Hash.new# Iterate friends, hash their followersfriends = Twitter.friend_ids(name)friends.ids.each do |fid|  f = Twitter.user(fid)  # Only iterate if we can see their followers  if (f.protected.to_s != "true")    user[f.screen_name.to_s] = f.followers_count  endenduser.sort_by {|k,v| -v}.each { |user, count| puts "#{user}, #{count}" }

Sample output from the friends script in Listing 5 is shown in Listing 6. I’ve clipped the output to conserve space, but as you can see, ReadWriteWeb (RWW) and Playstation are popular Twitter users in my direct network.

Listing 6. Screen output from the friends script in Listing 5

$ ./friends.rb MTimJonesRWW, 1096862PlayStation, 1026634HarvardBiz, 541139tedtalks, 526886lifehacker, 146162wandfc, 121683AnonymousIRC, 117896iTunesPodcasts, 82581adultswim, 76188forrester, 72945googleresearch, 66318Gartner_inc, 57468developerworks, 48518

Where are my followers?

Recall from Listing 2 that Twitter provides a wealth of location information. There’s a location field that is free form, user defined, and optional geocoding data. However, a user-defined time zone can also provide a hint as to the follower’s actual location.

In this example, you build a mash-up that extracts time zone data from your Twitter followers, and then visualize this data using Google Charts. Google Charts is an interesting project that allows you to build a variety of different chart types over the web; defining the chart type and data as an HTTP request, where the result is rendered directly in the browser as the response. To install the Ruby gem for Google Charts, use the following command line:

$ gem install gchartrb

Listing 7 provides the script for extracting time zone data, then building the Google Charts request. First, unlike previous scripts, this script requires that you be authenticated with Twitter. To do this, you need to register an application with Twitter, which provides you with a set of keys and tokens. Those tokens can be applied to the script in Listing 7 to successfully extract the data. See Resources for details on this easy process.

Following a similar pattern, this script accepts a screen name, and then iterates the followers of that user. The time zone is extracted for the current follower and stored in the tweetlocation hash. Note, you first test whether this key is in the hash and, if so, increment the counter for that key. You also keep a tab on the number of total time zones for the later construction of percentages.

The last portion of the script is the construction of the Google Pie Chart URL. You create a new PieChart and specify some options (size, title, and whether it’s 3D). Then, you iterate your time zone hash, emitting data for the chart for the time zone string (removing the & symbol) and the percentage of the time zone from the total.

Listing 7. Building a pie chart from Twitter followers’

time zones (followers-location.rb)

#!/usr/bin/env rubyrequire "rubygems"require "twitter"require 'google_chart'screen_name = String.new ARGV[0]tweetlocation = Hash.newtimezones = 0.0# AuthenticateTwitter.configure do |config|  config.consumer_key = '<consumer_key>'  config.consumer_secret = '<consumer_secret>'  config.oauth_token = '<oath_token>'  config.oauth_token_secret = '<oath_token_secret>'endcursor = "-1"# Loop through all pageswhile cursor != 0 do  # Iterate followers, hash their location  followers = Twitter.follower_ids(screen_name, :cursor=>cursor)  followers.ids.each do |fid|    f = Twitter.user(fid)    loc = f.time_zone.to_s    if (loc.length > 0)      if tweetlocation.has_key?(loc)        tweetlocation[loc] = tweetlocation[loc] + 1      else        tweetlocation[loc] = 1      end      timezones = timezones + 1.0    end  end  cursor = followers.next_cursorend# Create a pie chartGoogleChart::PieChart.new('650x350', "Time Zones", false ) do |pc|  tweetlocation.each do |loc,count|    pc.data loc.to_s.delete("&"), (count/timezones*100).round  end  puts pc.to_urlend

To execute the script from Listing 7, provide it with a Twitter screen name, and then copy and paste the resulting URL into a browser. Listing 8 shows this process with the resulting generated URL.

Listing 8. Invoking the followers-location script (result is a single line)

$ ./followers-location.rb MTimJoneshttp://chart.apis.google.com/chart?chl=Seoul|Santiago|Paris|Mountain+Time+(US++Canada)|Madrid|Central+Time+(US++Canada)|Warsaw|Kolkata|London|Pacific+Time+(US++Canada)|New+Delhi|Pretoria|Quito|Dublin|Moscow|Istanbul|Taipei|Casablanca|Hawaii|Mumbai|International+Date+Line+West|Tokyo|Ulaan+Bataar|Vienna|Osaka|Alaska|Chennai|Bern|Brasilia|Eastern+Time+(US++Canada)|Rome|Perth|La+Paz&chs=650x350&chtt=Time+Zones&chd=s:KDDyKcKDOcKDKDDDDDKDDKDDDDOKK9DDD&cht=p$

When you paste the URL from Listing 8 into a browser, you get the result shown in Figure 1.

Figure 1. Pie chart of Twitter followers’ locations
Pie chart shows the countries of followers organized by time zone

Twitter user behavior

Twitter contains a large amount of data that you can mine to understand some elements of user behavior. Two simple examples are to analyze when a Twitter user tweets and from what application the user tweets. You can use the following two simple scripts to extract and visualize this information.

Listing 9 presents a script that iterates the tweets from a particular user (using the user_timeline method), and then for each tweet, extracts the particular day on which the tweet originated. You use a simple hash again to accumulate your weekday counts, then generate a bar chart using Google Charts in a similar fashion to the previous time zone example. Note also the use of default for the

hash, which specifies the value to return for undefined hashes.

Listing 9. Building a bar chart of tweet days (tweet-days.rb)

#!/usr/bin/env rubyrequire "rubygems"require "twitter"require "google_chart"screen_name = String.new ARGV[0]dayhash = Hash.new# Initialize to avoid a nil error with GoogleCharts (undefined is zero)dayhash.default = 0timeline = Twitter.user_timeline(screen_name, :count => 200 )timeline.each do |t|  tweetday = t.created_at.to_s[0..2]  if dayhash.has_key?(tweetday)    dayhash[tweetday] = dayhash[tweetday] + 1  else    dayhash[tweetday] = 1  endendGoogleChart::BarChart.new('300x200', screen_name, :vertical, false) do |bc|  bc.data "Sunday", [dayhash["Sun"]], '00000f'  bc.data "Monday", [dayhash["Mon"]], '0000ff'  bc.data "Tuesday", [dayhash["Tue"]], '00ff00'  bc.data "Wednesday", [dayhash["Wed"]], '00ffff'  bc.data "Thursday", [dayhash["Thu"]], 'ff0000'  bc.data "Friday", [dayhash["Fri"]], 'ff00ff'  bc.data "Saturday", [dayhash["Sat"]], 'ffff00'  puts bc.to_urlend

Figure 2 provides the result of the execution of the tweet-days script in Listing 9 for the developerWorks account. As shown, Wednesday tends to be the most active tweet day, with Saturday and Sunday the least active.

Figure 2. Relative bar chart of per-day tweet activity
Bar chart shows activity for the days of the week

The next script determines from which source a particular user tweets. There are several ways you can tweet, and this script doesn’t encode them all. As shown in Listing 10, you use a similar pattern to extract the user timeline for a given user, and then attempt to decode the source of the tweet in a hash. You use the hash later to create a simple pie chart using Google Charts to visualize the data.

Listing 10. Building a pie chart of a user’s tweet sources

(tweet-source.rb)

#!/usr/bin/env rubyrequire "rubygems"require "twitter"require 'google_chart'screen_name = String.new ARGV[0]tweetsource = Hash.newtimeline = Twitter.user_timeline(screen_name, :count => 200 )timeline.each do |t|  if (t.source.rindex('blackberry')) then    src = 'Blackberry'  elsif (t.source.rindex('snaptu')) then    src = 'Snaptu'  elsif (t.source.rindex('tweetmeme')) then    src = 'Tweetmeme'  elsif (t.source.rindex('android')) then    src = 'Android'  elsif (t.source.rindex('LinkedIn')) then    src = 'LinkedIn'  elsif (t.source.rindex('twitterfeed')) then    src = 'Twitterfeed'  elsif (t.source.rindex('twitter.com')) then    src = 'Twitter.com'  else    src = t.source  end  if tweetsource.has_key?(src)    tweetsource[src] = tweetsource[src] + 1  else    tweetsource[src] = 1  endendGoogleChart::PieChart.new('320x200', "Tweet Source", false) do |pc|  tweetsource.each do|source,count|    pc.data source.to_s, count  end  puts "nPie Chart"  puts pc.to_urlend

Figure 3 provides a visualization of a user on Twitter who has an interesting set of tweet sources. The traditional Twitter website is used most often, along with a mobile phone application next.

Figure 3. Pie chart of a Twitter user’s tweet sources
Pie chart shows the tools used to generate tweets, such as twitter.com, web,LinkedIn, etc.

Followers graph

Twitter is a massive network of users that forms a graph. As you’ve seen from the scripts, it’s easy to iterate your contacts, and then iterate their contacts. Doing so forms the basis for a large graph, even at this level.

To visualize a graph, I’ve chosen to use the graph visualization software GraphViz. On Ubuntu, you can easily install this tool using the following command line:

$ sudo apt-get install graphviz

The script shown in Listing 11 iterates a user’s followers, and then iterates their followers. The only real difference in this pattern is the construction of a GraphViz dot-formatted file. GraphViz uses a simple script format to define graphs, which you’ll emit as part of your enumeration of the Twitter users. As shown, you define a graph simply by specifying the relationships of the nodes.

Listing 11. Visualizing a Twitter followers graph

(followers-graph.rb)

#!/usr/bin/env rubyrequire "rubygems"require "twitter"require 'google_chart'screen_name = String.new ARGV[0]tweetlocation = Hash.new# AuthenticateTwitter.configure do |config|  config.consumer_key = '<consumer_key>'  config.consumer_secret = '<consumer_secret>'  config.oauth_token = '<oath_token>'  config.oauth_token_secret = '<oath_token_secret>'endmy_file = File.new("graph.dot", "w")my_file.puts "graph followers {"my_file.puts "  node [ fontname=Arial, fontsize=6, penwidth=4 ];"# Get the first page of followersfollowers = Twitter.follower_ids(screen_name, :cursor=> -1 )# Iterate the followers returned in the Array (max 10).followers.ids[0..[5,followers.ids.length].min].each do |fid|  f = Twitter.user(fid)  # Only iterate if we can see their followers  if (f.protected.to_s != "true")    my_file.puts "  "" + screen_name + "" -- "" + f.screen_name.to_s + """    # Get the first page of their followers    followers2 = Twitter.follower_ids(f.screen_name, :cursor => -1 )    # Iterate the followers returned in the Array (max 10).    followers2.ids[0..[5,followers2.ids.length].min].each do |fid2|      f2 = Twitter.user(fid2)      my_file.puts "    "" + f.screen_name.to_s + "" -- "" +                    f2.screen_name.to_s + """    end  endendmy_file.puts "}"

Execute the script from Listing 11 on a user results in a dot file that you then generate an image from using GraphViz. First, invoke the Ruby script to gather the graph data (stored as graph.dot); then, use GraphViz to generate the graph image (here, using circo, which specifies a circular layout). The process of generating this image is defined as follows:

$ ./followers-graph.rb MTimJones$ circo graph.dot -Tpng -o graph.png

The resulting image is shown in Figure 4. Note that the Twitter graphs tend to be large, so I’ve constrained the graph by minimizing the number of users and their followers to enumerate (per the :count option in Listing 11).

Figure 4. Sample Twitter follower graph (extreme subset)
The follower graph shows followers as connected hubs like a networking diagram

Location information

When enabled, Twitter collects geolocation data about you and your tweets. This data consists of latitude and longitude information that can be used to pinpoint a user or from where a tweet originates. Further, searches can incorporate this information so that you can identify places or people based on a defined location or your location.

Not all users or tweets are geo-enabled (for privacy reasons), but this information serves as an interesting dimension to the overall Twitter experience. Let’s look at a script that allows you to visualize with geolocation data as well as another that allows you to search with this information.

In the first script (shown in Listing 12), you grab latitude and longitude data from a user (recall the bounding box from Listing 2). Although the bounding box is a polygon defining the area represented for the user, I simplify and use one point of this data. With this data, I generate a simple JavaScript function in a simple HTML file. This JavaScript code interfaces with Google Maps to present an overhead map of this location (given the latitude and longitude data extracted from the Twitter user).

Listing 12. Ruby script to construct a map of a user

(where-am-i.rb)

#!/usr/bin/env rubyrequire "rubygems"require "twitter"require 'google_chart'Twitter.configure do |config|  config.consumer_key = '<consumer_key>'  config.consumer_secret = '<consumer_secret>'  config.oauth_token = '<oauth_token>'  config.oauth_token_secret = '<oauth_token_secret>'endscreen_name = String.new ARGV[0]a_user = Twitter.user(screen_name)if a_user.geo_enabled == true  long = a_user.status.place.bounding_box.coordinates[0][0][0];  lat  = a_user.status.place.bounding_box.coordinates[0][0][1];  my_file = File.new("test.html", "w")  my_file.puts "<!DOCTYPE html>"  my_file.puts "<html><head>"  my_file.puts "<meta name="viewport" content="initial-scale=1.0, "  my_file.puts "user-scalable=no" />"  my_file.puts "<style type="text/css">"  my_file.puts "html { height: 100% }"  my_file.puts "body { height: 100%; margin: 0px; padding: 0px }"  my_file.puts "#map_canvas { height: 100% }"  my_file.puts "<style>"  my_file.puts "<script type="text/javascript""  my_file.puts "src="http://maps.google.com/maps/api/js?sensor=false">"  my_file.puts "<script>"  my_file.puts "<script type="text/javascript">"  my_file.puts "function initialize() {"  my_file.puts "var latlng = new google.maps.LatLng(" + lat.to_s + ", " + long.to_s + ");"  my_file.puts "var myOptions = {"  my_file.puts "zoom: 12,"  my_file.puts "center: latlng,"  my_file.puts "mapTypeId: google.maps.MapTypeId.HYBRID"  my_file.puts "};"  my_file.puts "var map = new google.maps.Map(document.getElementById("map_canvas"),"  my_file.puts "myOptions);"  my_file.puts "}"  my_file.puts "<script>"  my_file.puts "<head>"  my_file.puts "<body onload="initialize()">"  my_file.puts "<div id="map_canvas" style="width:100%; height:100%"<>/div>"  my_file.puts "<body>"  my_file.puts "<html>"else  puts "no geolocation data available."end

The script in Listing 12 is executed simply as:

$ ./where-am-i.rb MTimJones

The resulting HTML file is rendered through a browser, such as:

$ firefox test.html

This script can fail if no location information is available; but if it succeeds, an HTML file is generated that a browser can read to render the map. Figure 5 presents the resulting map image, which shows a portion of the Front Range of northern Colorado, USA.

Figure 5. Sample image rendered from the script in Listing 12
Google satelite map of the selected region with no special markers or tags

With the geolocation, you can also search Twitter to identify Twitter users and tweets related to a particular location. The Twitter Search API allows geocoding information to restrict its results. The following example shown in Listing 13 extracts latitude and longitude data for a user, then uses this data to fetch tweets within a radius of 5 miles of that location.

Listing 13. Search for local tweets with latitude and

longitude data (tweets-local.rb)

#!/usr/bin/env rubyrequire "rubygems"require "twitter"Twitter.configure do |config|  config.consumer_key = '<consumer_key>'  config.consumer_secret = '<consumer_secret>'  config.oauth_token = '<oauth_token>'  config.oauth_token_secret = '<oauth_token_secret>'endscreen_name = String.new ARGV[0]a_user = Twitter.user(screen_name)if a_user.geo_enabled == true  long = a_user.status.place.bounding_box.coordinates[0][0][0]  lat  = a_user.status.place.bounding_box.coordinates[0][0][1]  Array tweets = Twitter::Search.new.geocode(lat, long, "5mi").fetch  tweets.each do |t|    puts t.from_user + " | " + t.text  endend

The result of the script in Listing 13 is shown in Listing 14. This is a subset of the tweets given the frequency of tweeters out there.

Listing 14. Viewing local tweets within 5 miles of my location

$ ./tweets-local.rb MTimJonesBreesesummer | @DaltonOls did he answer uLongmontRadMon | 60 CPM, 0.4872 uSv/h, 0.6368 uSv/h, 2 time(s) over natural radiationgraelston | on every street there is a memory; a time and place we can never be again.Breesesummer | #I'minafight with @DaltonOls to see who will marry @TheCodySimpson I will marry him!!! :/_JennieJune_ | ok I'm done, goodnight everyone!Breesesummer | @DaltonOls same_JennieJune_ | @sylquejr sleep well!Breesesummer | @DaltonOls ok let's see what he saysLongmontRadMon | 90 CPM, 0.7308 uSv/h, 0.7864 uSv/h, 2 time(s) over natural radiationBreesesummer | @TheCodySimpson would u marry me or @DaltonOlsnatcapsolutions | RT hlovins: The scientific rebuttal to the silly Forbes release this morning: Misdiagnosis of Surface Temperatu... http://bit.ly/nRpLJl$

Back to top

Going further

This article presented a number of simple scripts for extracting data from Twitter using the Ruby language. The emphasis was on the development and presentation of simple scripts to illustrate the fundamental ideas, but much more is possible. For example, you can also use the API to explore your friends networks and identify the most popular Twitter users of interest to you. Another interesting area is the mining of tweets themselves, using geolocation data to understand location-based behaviors or events (such as flu outbreaks). This article only scratched the surface, but feel free to comment below with your own mash-ups. Ruby and the Twitter gem make it simple to develop useful mash-ups or dashboards for your data-mining needs.

Resources

Learn

  • Ruby’s official language website is the single source for Ruby news, information, releases, documentation, and community support for the Ruby language. Given Ruby’s growing use in web frameworks (such as Ruby on Rails), you can also learn the most recent security vulnerabilities and their solutions.

  • The Github social coding site provides the official source for the Twitter gem. At this site, you can get access to the source, documentation, and mailing list for the Ruby Twitter gem.

  • Registering a Twitter application is necessary to use certain elements of the Twitter API. The process is free and allows you to access some of the more useful elements of the API.

  • Google Maps JavaScript API tutorial shows how to use Google Maps to render maps of various types using user-provided geolocation data. The JavaScript used in this article was based on the “Hello World” example code provided within.

  • developerWorks Open source zone provides a wealth of information on open source tools and using open source technologies.

  • developerWorks on Twitter: Follow us and follow this author at M. Tim Jones.

  • developerWorks on-demand demos: Watch and learn demos ranging from product installation and setup demos for beginners to advanced functionality for experienced developers.

Get products and technologies

  • The Twitter Ruby gem, developed by John Nunemaker, provides a useful interface to the Twitter service that cleanly integrates into the Ruby language.

  • The Google Chart API is a useful service that provides the ability to construct complex and rich graphics using a variety of styles and options. This service provides an API through which a URL results that is rendered at the Google site.

  • The Google Chart API Ruby wrapper provides a Ruby interface to the Google Charts API for the construction of useful charts within Ruby.

  • Evaluate IBM products in the way that suits you best: Download a product trial, try a product online, use a product in a cloud environment, or spend a few hours in the SOA Sandbox learning how to implement service-oriented architecture efficiently.

Discuss

  • developerWorks community: Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis.

About the author

M. Tim Jones

M. Tim Jones is an embedded firmware architect and the author of Artificial Intelligence: A Systems Approach, GNU/Linux Application Programming (now in its second edition), AI Application Programming (in its second edition), and BSD Sockets Programming from a Multilanguage Perspective. His engineering background ranges from the development of kernels for geosynchronous spacecraft to embedded systems architecture and networking protocols development. Tim is a platform architect with Intel and author in Longmont, Colorado.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.

Report abuse help

Report abuse

Report abuse submission failed. Please try again later.

developerWorks: Sign in

If you don’t have an IBM ID and password, register here.

The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

All information submitted is secure.

Rate this article

Error: Submission failed. Please try again.

Average rating 5 stars based on 26 votes Average rating (26 votes)

Comments

Add comment:

Sign in or register to leave a comment.

Note: HTML elements are not supported within comments.

The code examples have been updated to the 1.7.2 version of the ruby twitter gem.

Posted by MTimJones on 13 October 2011

Report abuse

Hi Wolfgang. I chose not to incorporate other libraries, just to simplify things. Using puts is readable, as are concatenating strings. Sometimes simple is most readable.

Posted by MTimJones on 07 October 2011

Report abuse

Hi Dougie. It appears that the Twitter API has changed… I’ll submit some new scripts this weekend. Thanks for the note.

Posted by MTimJones on 07 October 2011

Report abuse

I’m getting

/var/lib/gems/1.9.1/gems/twitter-1.7.2/lib/twitter.rb:21:in `method_missing’: DEPRECATION #followers is deprecated as it will only return information about users who have Tweeted recently. It is not a functional way to retrieve all of a users followers. Instead of using this method use a combination of #follower_ids and #users.
/var/lib/gems/1.9.1/gems/twitter-1.7.2/lib/faraday/response/raise_http_4xx.rb:12:in `on_complete’: GET https://api.twitter.com/1/statuses/followers.json?cursor=-1: 401: Invalid / expired Token (Twitter::Unauthorized)

when I try your followers-graph program.

Posted by DougieLawson on 07 October 2011

Report abuse

I am not a typial Ruby guy but the code samples presented here do not look like ruby, but more like a beginner’s Java-to-ruby-rewrite. Ruby knows the feature of string interpolation which is never seen in the above examples. Instead, lots of ugly string concatenations have been used. Also, generating HTML via “puts” is a technology going back to 1997 and should never get done today. There are a couple of libraries out there for doing such a job that do their job right.
I personally do not think that code-samples like these should get published.

Posted by Wolfgang Kinkeldei on 07 October 2011

Report abuse

Back to top

Help: Update or add to My dW interests

What’s this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it’s not there already. You only need to be logged in to My developerWorks.

And what’s the point of adding your interests to your profile? That’s how you find other users with the same interests as yours, and see what they’re reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What’s this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you’ve indicated interest. In a future enhancement to My developerWorks, you’ll be able to see a record of that content.

View your My developerWorks profile

Return from help

Do you know how to write an internal DSL in Ruby?

Almost all Ruby programming newbies would love to get their hands wet writing a Ruby DSL. This article explains how you can write a simple Ruby DSL.

Introduction

A Domain-Specific Language (DSL) is a (usually small) programming or description language designed for a fairly narrow purpose. DSLs are targeted at end users or domain specialists who are not expert programmers. Martin Fowler classifies DSLs into two styles – external and internal. An external DSL is a language that is different from the main programming language for an application, but that is interpreted by or translated into a program in the main language. An internal DSL transforms the main programming language itself into the DSL (our simple DSL is tied to the Ruby programming language).

Ruby code blocks

Ruby’s support for blocks (i.e., closures) is useful in defining internal DSLs.

Ruby code blocks (called closures in other languages) are chunks of code between braces or between do- end that you can associate with method invocations, almost as if they were parameters. A Ruby block is a way of grouping statements, and may appear only in the source next to a method call; the block is written starting on the same line as the method call’s last parameter (or the closing parenthesis of the parameter list). The code in the block is not executed at the time it is encountered. Instead, Ruby remembers the context in which the block appears (the local variables, the current object, and so on) and then enters the method. Matz says that any method can be called with a block as an implicit argument. Inside the method, you can call the block using the yield keyword with a value. Blocks are not objects, but they can be converted into objects of class Proc. One way a block can be converted to a Proc object is by passing a block to a method whose last parameter is prefixed with an ampersand. That parameter will receive the block as a Proc object:

def my_method(p1, &block)  ...end

instance_eval

The class Object has an instance_eval public method which can be called from a specific object. It provides access to the instance variables of that object. It can be called either with a block or with a string:

class Rubyist  def initialize    @geek = "Matz"  endendobj = Rubyist.new# instance_eval can access obj's private methods# and instance variablesobj.instance_eval do  puts self  # => #<Rubyist:0x2ef83d0>  puts @geek # => Matzend

The block that you pass to instance_eval helps you dip inside an object to do something in there. You can wreak havoc on encapsulation! No data is private data anymore.

instance_eval can also be used to add class methods as shown below:

class RubyistendRubyist.instance_eval do  def who    "Geek"  endendputs Rubyist.who # => Geek

Deciding on a simple DSL

You are an expert Ruby programmer and your friends Victor, Michael and Satoshi (all 3 are novice chess players) have requested you to write a Ruby program for them, that could help them with a listing of the best black opening chess moves.

You tell your chess friends that if they need help they should individually send you a text file containing the white’s first move, as follows:

h4a3e4

h4, a3, e4 would be Ruby methods in your DSL program. Once we get the DSL to follow valid Ruby syntax, Ruby does all the work to parse the file and hold the data in a way that we can operate on it.

Victor is playing the black pieces and his opponent plays the opening white piece (say h4). Victor would like to know what’s the best strategy to counter white’s opening move of h4. He also would like to know, what if his opponent would have played a3.

Victor decides to send a text file to you.

The DSL program – chess_opener.rb

Being a Ruby expert, you dish out your first version of the DSL program – chess_opener.rb:

class ChessOpener  def initialize    @data = {}    load_data  end  def self.load(filename)    dsl = new    dsl.instance_eval(File.read(filename))  end  def h4    puts "=========="    puts @data.assoc("h4")    puts "=========="  end  def a3    puts "=========="    puts @data.assoc("a3")    puts "=========="  end  def method_missing(method_name, *args, &block)    msg = "You tried to call the method #{method_name}. There is no such method."    raise msg  end  private  def load_data    @data = {"a3" => ["Anderssen's Opening Polish Gambit: 1. a3 a5 2. b4",                      "Anderssen's Opening Creepy Crawly Formation: 1. a3 e5 2. h3 d5",                      "Anderssen's Opening Andersspike: 1. a3 g6 2. g4"],             "h4" => ["Koola-Koola continues 1.h4 a5",                      "Wulumulu continues 1.h4 e5 2. d4",                      "Crab Variation continues 1.h4 any 2. a4",                      "Borg Gambit continues 1.h4 g5.",                      "Symmetric Variation continues 1.h4 h5"]}  endend

Some explanation of code

The initialize method of your class ChessOpener creates a Hash object @data and populates it by calling the private method load_data. You have referred to the online list of chess openings to create the hash @data. The current program has the openings only for a3 and h4 moves, but you plan to add the other moves soon.

You want a simple and straightforward way to parse the DSL file. Something like:

my_dsl = ChessOpener.load(filename)

Also, you would like to accept the DSL file from the command line, something like:

my_dsl = ChessOpener.load(ARGV[0])

You write a class method load:

def self.load(filename)  dsl = new  dsl.instance_eval(File.read(filename))end

The class method load creates a ChessOpener object and calls instance_eval on the DSL file (chess_opener_test.txt above). If you feed instance_eval a string, instance_eval will evaluate the string as Ruby code. In fact, this Ruby code is nothing but calls to the methods h4 and a3 which are respectively called. The methods h4 and a3 make use of Ruby Hash’s assoc method to extract the information about the particular (say h4) move.

The program also provides a method_missing method, in case the program fails to find a method say h5 (assuming Victor has typed that by mistake in the file chess_opener_test.txt.)

Running the DSL program

You next write the program – chess_opener_test.rb, ensuring that the files chess_opener.rb, chess_opener_test.rb and chess_opener_test.txt are in the same folder on your computer.

You now run your Ruby code as follows:

ruby chess_opener_test.rb chess_opener_test.txt

Here’s the sample output:

==========h4Koola-Koola continues 1.h4 a5Wulumulu continues 1.h4 e5 2. d4Crab Variation continues 1.h4 any 2. a4Borg Gambit continues 1.h4 g5.Symmetric Variation continues 1.h4 h5====================a3Anderssen's Opening Polish Gambit: 1. a3 a5 2. b4Anderssen's Opening Creepy Crawly Formation: 1. a3 e5 2. h3 d5Anderssen's Opening Andersspike: 1. a3 g6 2. g4==========

In fact, in the next version of your DSL program, you plan to write the output to a file and send the same to Victor. Why don’t you fork this project and add-on some more functionality?

That’s it!

Feel free to ask questions and give feedback in the comments section of this post. Fellow Rubyists, if you would like to write a guest blog post for RubyLearning email me at satish [at] rubylearning.org

Technorati Tags: , , ,

Posted by Satish Talim

Tagged as:

programming,

Ruby,

Ruby DSL,

ruby programming

Buzz

Follow me on Twitter to communicate and stay connected

Send Email Notifications in Rails Development – WowKhmer

Send Email Notifications in Rails Development

Oct 02nd 2011

When there are features in your application that deal with sending email to users(eg. account activation, forget password). When you would like to test those features in your Rails development environment, then you probably go to running server’s log to copy generated links in the sending email, and how about when your managers would like to test them as well, then do they know how to go to the log to copy the links? And some other people use their real email addresses(eg. gmail) to receive the emails. Would you ever though any easier solution?

1. MailCatcher

MailCatcher is a Ruby gem from Samuel Cochran. MailCatcher runs a super simple SMTP server which catches any message sent to it to display in a web interface.

How

$ gem install mailcatcher$ mailcatcher -fStarting MailCatcher==> smtp://127.0.0.1:1025==> http://127.0.0.1:1080

Then set the delivery method in config/environments/development.rb

config.action_mailer.delivery_method = :smtpconfig.action_mailer.smtp_settings = { :host => "localhost", :port => 1025 }

2. Letter Opener

Letter Opener is a Ruby gem from Ryan Bates who run the awesome railscasts. Letter Opener previews emails in the browser instead of sending it.

How

First add the gem to your development environment and run the bundle command to install it.

gem "letter_opener", :group => :development

Then set the delivery method in config/environments/development.rb

config.action_mailer.delivery_method = :letter_opener

Note: While I write this article, there are some people face an issue with nothing happen when sending email out. I found the problem because of Launchy dependency gem version. Because @ryanb hasn’t specified version of Launchy dependency gem in gemspec file, then it won’t install the new Launchy version if there is any old versions in local gems. I already sent a pull request, and hope it will fix soon.

3. MockSMTP

The third option that I haven’t tried myself either is MockSMTP. MockSMTP is a native Mac application that embeds its own SMTP server.

HOW

There is a help page to tell you how to could configure your Rails applications to use with MockSMTP.

We Heart Code » Reading An Excel File With Ruby

Jump to Comments

This tutorial will cover how to read (or parse) an excel file with ruby. I had to write a script to unpivot some data for a co-worker that saved him hours of time, and I got to write a ruby script, so it was a win-win. Here’s how you can do the same thing.

Installing Parseexcel

Parseexcel is a ruby port of the perl parseexcel module.

It’s installable via a nice gem like so:

gem install parseexcel

That’s that, now remember since it’s a gem library we have to tell our script to use the gem libs when we run the script from the console using the -rubygems switch.

Using Parseexcel

Parseexcel is a very straight forward library, you can’t do too much with it, but it gets the job done.

Getting a Workbook

Spreadsheet::ParseExcel.parse(filenameandpath)

This returns the actual excel file’s workbook, from there we need to determine what worksheet we’re on.

Getting a Worksheet

worksheet = workbook.worksheet(0)

Will return the first worksheet, you could also use the each method on the workbook to iterate over all the worksheets.

Iterating over rows and columns

The worksheet object has a very nice each method that will allow us to iterate over the rows like so

worksheet.each { |row|
  j=0
  i=0
  if row != nil
  row.each { |cell|
    if cell != nil
      contents = cell.to_s(‘latin1’)
      puts “Row: #{j} Cell: #{i} #{contents}”
    end
    i = i+1
  }
  j = j +1
  end
}

Getting Cell Data

  • Getting a String: cell.to_s(‘latin1’)
  • Getting a Float: cell.to_s(‘latin1’)
  • Getting a Int: cell.to_i
  • Getting a Date: cell.date

Getting A Specific Cell

cell = row.at(3) #returns cell at column 3

A basic script for dumping an excel file

require ‘parseexcel’

#Open the excel file passed in from the commandline
workbook = Spreadsheet::ParseExcel.parse(ARGV[0])

#Get the first worksheet
worksheet = workbook.worksheet(0)

#cycle over every row
worksheet.each { |row|
  j=0
  i=0
  if row != nil
  #cycle over each cell in this row if it’s not an empty row
  row.each { |cell|
    if cell != nil
      #Get the contents of the cell as a string
      contents = cell.to_s(‘latin1’)
      puts “Row: #{j} Cell: #{i}> #{contents}”
    end
    i = i+1
  }
  end
}

To run the script, remember to use the -rubygems switch so that you can find the parsexcel library.

ruby -rubygems excelparse.rb myfile.xls
Bookmark to:
Add 'Reading An Excel File With Ruby' to Del.icio.us

Add 'Reading An Excel File With Ruby' to digg

Add 'Reading An Excel File With Ruby' to FURL

Add 'Reading An Excel File With Ruby' to reddit

Add 'Reading An Excel File With Ruby' to Technorati

Add 'Reading An Excel File With Ruby' to Google Bookmarks

43 Comments

Filed under ruby, tutorial

How to use ActiveRecord without Rails – Ruby – activerecord, standalone, rails, ruby

This is an example of how to use ActiveRecord without Rails:

1  require 'active_record'   2  require 'sqlite3'   3     4  ROOT = File.join(File.dirname(__FILE__), '..')   5     6  ['/lib', '/db'].each do |folder|   7     $:.unshift File.join(ROOT, folder)   8   end   9     10  ActiveRecord::Base.logger = Logger.new('log/debug.log')  11  ActiveRecord::Base.configurations = YAML::load(IO.read('config/database.yml'))  12  ActiveRecord::Base.establish_connection('development')  13    14  require 'db/schema'

The file db/schema.rb contains, for example:

1  ActiveRecord::Schema.define :version => 0 do   2    create_table :languages, :force => true do |t|   3      t.string :name   4    end   5  end

And config/database.yml contains:

1  development:   2    adapter: sqlite3   3    database: db/data.sqlite3   4    pool: 5   5    timeout: 5000