‘belongs_to’ and ‘has_one’ Differentiated

One of the more common confusions with ActiveRecord associations is the difference between the `has_one` and `belongs_to` relationships.

However, they are not interchangeable, and there are some serious differences. A `belongs_to` can only go in the class that holds the foreign key whereas `has_one` means that there is a foreign key in another table that references this class. So `has_one` can only go in a class that is referenced by a column in another table.

So this is wrong:

class Transaction < ActiveRecord::Base
  # The transactions table has a order_id  
  has_one :order                
end

class Order < ActiveRecord::Base
  # The orders table has a transaction_id
  has_one :transaction          
end

So is this:

class Transaction < ActiveRecord::Base
  # The transactions table has a order_id
  belongs_to :order             
end

class Order < ActiveRecord::Base
  # The orders table has a transaction_id
  belongs_to :transaction     
end

For a two-way association, you need one of each, and they have to go in the right class. Even for a one-way association, it matters which one you use, and which direction you use it:

class Transaction < ActiveRecord::Base
  # This table has no foreign keys
  has_one :order             
end

class Order < ActiveRecord::Base
  # The orders table has a transaction_id
  belongs_to :transaction     
end

Ruby Script to Import Google Contact Photos From Gravatar

Google Contact photos are a much neglected feature of the Google Stack. It really adds to the user experience when you see each of your contact photos when you make or receive a call. However, it can be a real pain (especially if you have hundreds of contacts).

But I had an idea recently, to try and match my Google Contact emails with Gravatar and try to auto-populate some of the dozens of contacts that didn’t already have a photo (after all a Gravatar is better than nothing).

So I wrote a Ruby script to find my contacts missing a photo and try to update it with a Gravatar (wherever possible). NB: You may need to first install the GData (Google Data) gem by opening a Terminal window and issuing: sudo gem install gdata.

#!/usr/bin/env ruby

# Google Contact Photos - Gravatar Importer
# Written by Ashley Angell
# http://ashleyangell.com
# Licenced under Creative Commons with Attribution

require "rubygems"
require "gdata"
require "rexml/document"
require "digest/md5"
require "net/http"
include REXML

none = 'd5fe5cbcc31cff5f8ac010db72eb000c'
user = ARGV[0]
pass = ARGV[1]

client = GData::Client::Contacts.new
client.clientlogin(user, pass)
data = client.get("https://www.google.com/m8/feeds/contacts/#{user}/full?max-results=10000")
myxml = Document.new data.body
p "contacts"
puts "-"*70
i = 0
myxml.each_element("feed/entry") do |e|
  begin
    gd = e.elements['gd:email']
    if !gd.nil?
      email = gd.attributes['address'].downcase
      hash = Digest::MD5.hexdigest(email)
      image_src = "http://www.gravatar.com/avatar/#{hash}"
      nil_image = false
      image_element = e.get_elements("link[@rel='http://schemas.google.com/contacts/2008/rel#photo']")[0]
      if !image_element.nil? and image_element.attributes['gd:etag'].nil?
        data = nil
        md5 = nil
        Net::HTTP.start(URI.parse(image_src).host) do |http|
          resp = http.get(URI.parse(image_src).path)
          data = resp.body
          md5 = Digest::MD5.hexdigest(data)
          File.open("#{email}.png", 'w') do |f|
            f.puts data if md5 != none
          end
        end
        md5 = Digest::MD5.hexdigest(data)
        if md5 != none
          puts "#{email} > #{image_src}"
          client.put_file(image_element.attributes['href'], "#{email}.png", 'image/png')
          i = i + 1
        else
          puts "#{email} > no match"
        end
      else
        puts "#{email} > skipped (already has photo)"
      end
      File.delete("#{email}.png") if File.exists?("#{email}.png")
    end
  rescue Exception => ex
    puts ex
  end
end
puts "Updated #{i} contact photos"

To execute it, simply copy and paste this into a text editor (or download it and unzip) and from Terminal (command) window and execute the following commands:

sudo chmod +x googlegravatarimporter.rb [Enter]
./googlegravatarer.rb your.address@gmail.com your_password [Enter]

It will cycle through your Google Contacts and indicate what action was taken. For me, surprisingly updated a few dozen contacts (even more than I expected).

I’ve posted this here for others that might want to do the same thing but cannot be bothered writing the script for it. Consider it posted here under Creative Commons with Attribution.

‘File not found: lib’ Error installing Rails Gem

I recently had a problem trying to install Rails 3 on my MacBook with a fresh OSX Snow Leopard:

sudo gem install rails
Password: {entered}
Successfully installed rails-3.0.7
1 gem installed
Installing ri documentation for rails-3.0.7...
File not found: lib

Turns out this is a somewhat common problem.  But it seems that the solution is easy, just manually reinstall RDoc. To do this run these 3 commands:

sudo gem install rdoc-data
sudo rdoc-data --install
sudo gem rdoc --all --overwrite

The last line in particular will re-generate all the documentation for your installed gems (including Rails) and can take a while, but you should be able to confirm the fix by reissuing the Rails gem install command:

sudo gem install rails

shows that rails now installs properly and says that it has installed both ri and RDoc documentation without issue.

Adding Wiki Style Functionality to Your Rails Site Using 'acts_as_versioned'

If you need to add basic wiki style functionality to your Ruby on Rails models, there is a really easy way to get similar model versioning without having to resort to cutting the code yourself.

The acts_as_versioned ‘plugin’ has been available for quite some time, but its been made far better by it now becoming a gem instead of an old-school plugin. The authors have gone to considerable effort to make it as painless as possible to use.

This post, is designed to give you a brief over-view into how to get up and running with with models which ‘acts_as_versioned’.  Because its the current version (at time of posting) and because its awesome, this walk-though assumes that you are using Rails 3, not 2.  The instructions for Rails 2 sites are similar, but you’ll need to tweak this for it to work.

First, you need to grab the gem:

sudo gem install acts_as_versioned

Next, add the dependency to the ‘Gemfile’, it doesn’t matter too much where it goes, I stuck it somewhere in the middle:

gem 'acts_as_versioned', '0.6.0'

Next, just under the ‘ActiveRecord::Base’ line in the model’s class file, instruct the class that its to act as a versioned model.

class Article < ActiveRecord::Base
  acts_as_versioned
end

Then, in the migration file you need to execute the model's method to create the version table.  This is key because the acts_as_versioned gem actually creates an additional database table to house all the previous versions of a given record.  Obviously, you need to delete the table is the schema is taken down.  My migration now looks like:

class CreateArticles < ActiveRecord::Migration
  def self.up
    create_table :article do |t|
      t.string :title
      t.string :body
      t.integer :user_id

      t.timestamps
    end
    Article.create_versioned_table
  end

  def self.down
    drop_table :articles
    Article.drop_versioned_table
  end
end

The key method is the

Article.create_versioned_table

which creates the version table of the model. Now, get rake to create the database:

 rake db:migrate

Thats it!  Its done.  Using acts_as_versioned is simple. I'll provide some examples, where '@article' represents an instance of a model setup 'acts_as_versioned'. To find the current version of an article you can use the version property:

@article.version 

But just performing a normal ActiveRecord lookup returns the most current version anyway, so to revert to a previous version use the revert_to method on an article instance:

@article.revert_to(version_number)

You can save (just like you've done a hundred times before) a previous version as the current on by using the save method. The save on a reverted articles will just create a new version.

To get the number of versions:

@article.versions.size

Since '@article.versions' returns an array of versions, you can do neat things like this:

History

<% for version in @article.versions.reverse %> Version <%= version.version %> <%= link_to '(revert to this version)', :action => 'revert_to_version', :version => version.id, :id => @article %>
<% end %>

Obviously for this to work, you'd need to create a 'revert_to_version' action in the appropriate controller, but you get the idea.

acts_as_versioned is an amazing piece of work, and aside from the wiki-like functionality it gives you for very little effort, I can imagine scenarios such as audit and trace logs and "undo" features which could really benefit from this little gem.

Ruby Code to Scrape Images from TwitPic URLs

Twitpic logo

I’ve been trying to find an easy way to re-grab my TwitPic images back off TwitPic to be re-syndicated on a communal family website I am trying to create.  I found a great site which had some Rails code that scraped the image, so I took the code and modified it to make it compatible for pure Ruby (too be honest it didn’t need much modifying at all) .

Here’s my modified snippet of code that I’ve been using to grab the image from Twitpic with the Hpricot gem:

require 'rubygems'
require 'net/http'
require 'hpricot'

def rip_twitpic(url)
  begin
    code=url.match(/[w]+$/).to_s
    unless code.empty?
      uri=URI.parse(url)
      resp=Net::HTTP.get_response(uri)
      html=Hpricot(resp.body)
      html.at("#photo-display")['src']
    end
  rescue Exception => e
    puts "Error extracting twitpic: #{e}"
    url
  end
end

Just as it appears, this method will return the URL of the image embedded on the page that the TwitPic URL points too.

This will form the core part of a little project which will allow you to scrape TwitPic images and send them to a server of your choosing. I’ll try to release this ASAP but in the meantime, I thought a few of you might find this useful. I know I do.

Project Euler Problem 52 Solution

It can be seen that the number, 125874, and its double, 251748, contain exactly the same digits, but in a different order.

Find the smallest positive integer, x, such that 2x, 3x, 4x, 5x, and 6x, contain the same digits.

My solution in Ruby:

i = 1;
answer = nil;

while answer == nil
  i += 1
  x = i * 2
  base = Array.new(x.to_s.scan(/./))
  matchbase = true
  for t in 3..6
    break if matchbase == false
    x = i * t
    digits = Array.new(x.to_s.scan(/./))
    digits.each { |d| matchbase = false if base.index(d) == nil }
  end
  answer = i if matchbase == true
end
puts "Answer: #{answer}"

Project Euler Problem 48 Solution

The series, 1^(1) + 2^(2) + 3^(3) + … + 10^(10) = 10405071317.

Find the last ten digits of the series, 1^(1) + 2^(2) + 3^(3) + … + 1000^(1000).

My solution in Ruby:

sum = 0
for i in 1..1000 do
  sum += i**i
end
str = sum.to_s

puts str[str.length - 10,str.length]

UPDATE

s = 0
(1..1000).inject { |s, x| s + x ** x } % (10 ** 10)
str = s.to_s
puts str[str.length - 10,str.length]

Project Euler Problem 28 Solution

Starting with the number 1 and moving to the right in a clockwise direction a 5 by 5 spiral is formed as follows:

21   22   23   24   25
20    7    8    9   10
19    6    1    2   11
18    5    4    3   12
17   16   15   14   13

It can be verified that the sum of the numbers on the diagonals is 101.

What is the sum of the numbers on the diagonals in a 1001 by 1001 spiral formed in the same way?

My solution in Ruby:

corners = [1]
num = 1
add = 2
500.times do
  4.times do
    num += add
    corners << num
  end
  add += 2
end
sum = 0
corners.each { |c| sum += c }
puts sum

Project Euler Problem 25 Solution

The Fibonacci sequence is defined by the recurrence relation:

F_(n) = F_(n−1) + F_(n−2), where F_(1) = 1 and F_(2) = 1.

Hence the first 12 terms will be:

F_(1) = 1
F_(2) = 1
F_(3) = 2
F_(4) = 3
F_(5) = 5
F_(6) = 8
F_(7) = 13
F_(8) = 21
F_(9) = 34
F_(10) = 55
F_(11) = 89
F_(12) = 144

The 12th term, F_(12), is the first term to contain three digits.

What is the first term in the Fibonacci sequence to contain 1000 digits?

My solution in Ruby:

t1, t2, term = 1, 2, 3
loop do
  temp = t1 + t2
  t1 = t2
  t2 = temp
  term += 1
  temp_s = temp.to_s
  break if temp_s.length >= 1000
end
puts term
Enhanced by Zemanta

Project Euler Problem 20 Solution

n! means n x (n – 1) x … x 3 x 2 x 1

Find the sum of the digits in the number 100!

My solution in Ruby:

sum = 0
factorial = 1
for i in 1..100
  factorial = factorial * i
end
str = factorial.to_s
y = str.scan(/./)
y.each do |c|
  sum += c.to_i
end
puts sum