Custom SQL queries in Rails

Posted by Dan Sosedoff on August 17, 2010

Sometimes ORM just cant hand some complicated query, especially when the results of such query does not refer to any table.

For Rails ActiveRecord:

class Item < ActiveRecord::Base
   def self.some_heavy_stuff(param)
     sql = self.sanitize_sql(['.... SQL ....', param)
     self.connection.execute(sql)
   end
end
 
# usage
data = Item.some_heavy_stuff(1)
data.each do |row|
   puts row['id']
end

For DataMapper its almost the same (they`ve replaced query method with select not a long time ago):

class Item
  include DataMapper::Resource
 
  property :id, Serial
  property :foo, String
  property :bar, String
 
  def self.some_heavy_stuff(param)
     repository(:default).adapter.select('... SQL ...')
  end
end

Disable auto-incremental field in Rails Migrations

Posted by Dan Sosedoff on August 12, 2010

Since i`ve been using both DataMapper (Merb/Sinatra) and ActiveRecord (Rails) a lot i noticed that AR acts weight when i manually set PK key, particularly ID field, which you dont have to define by default. In DM you have to define it as ‘Serial’.

So, the task is to create/update records in your database which is supposes to represent data from primary database. In such cases all ID`s should be unique and equal to each other.

To disable autoincremental ID field in your AR models just use this option:

create_table :foo, :id => false do |t|
  t.integer   :id, :options => 'PRIMARY KEY'
  # .... rest of columns
end

Generate sitemaps with Ruby and XmlSitemap gem

Posted by Dan Sosedoff on June 18, 2010

Made a simple gem for website sitemap generation. Could be used in any Ruby/Rails/Merb/Sinatra application. It does not have any caching in that case if you want to use framework built-in cache methods.

Installation:

$ sudo gem install xml-sitemap

Example

pages = Page.all(:order => [:updated_at.desc] # DM model
map = XmlSitemap::Map.new('somedomain.com') do |m|
  m.add(:url => '/', :period => :daily, :priority => 1.0)
  m.add(:url => '/contractors', :period => :daily, :priority => 1.0)
  pages.each do |p|
    m.add(
      :url => url_for_page(p),
      :updated => p.updated_at,
      :priority => 0.5,
      :period => :never
    )
  end
end
# render the sitemap
puts map.render

Sinatra Example

# ... your code
 
get '/sitemap.xml' do
  map = XmlSitemap::Map.new('domain.com') do |m|
    m.add(:url => '/')
    m.add(:url => '/posts', :period => :weekly)
  end
 
  headers['Content-Type'] = 'text/xml'
  map.render
end
 
# ... more code

Options

:url – page path, relative to domain (ex.: /test), String.
:period – freqchange attribute, Symbol, :none, :never, :always, :hourly, :daily, :weekly, :monthly, :yearly
:priority – priority attribute, Float class,(0.0..1.0)
:updated – (optional) last_update attribute, Time class

Source Code

http://github.com/sosedoff/xml-sitemap

How to test mailers in Sinatra, Rails and Merb

Posted by Dan Sosedoff on May 03, 2010

Once you need to test mailers within your Sinara, Rails or Merb application you wish to see a real output. No need to setup delivery via SMTP. Just create an executable ruby script somewhere, for example: /usr/bin/fake-sendmail.sh with following content:

$ touch /usr/bin/fake-sendmail.sh 
$ chmod +x /usr/bin/fake-sendmail.sh  # make it executable (will require root priv.)
#!/usr/bin/ruby
path = "/tmp/fake-mailer"
Dir.mkdir(path) if !File.exists?(path)
File.open("#{path}/#{Time.now.to_i}.txt", "w") do |f|
    f.puts ARGV.inspect
    $stdin.each_line { |line| f.puts line }
end

And then configure your mailing system:

1. Sinatra

There is a plugin for Sinatra (ported from Merb, http://github.com/foca/sinatra-mailer),

Sinatra::Mailer.config = {:sendmail_path => "/usr/bin/fake-sendmail.sh"}
Sinatra::Mailer.delivery_method = :sendmail

2. Merb

Edit your development configuration file (config/environments/development.rb)

Merb::BootLoader.after_app_loads do
    Merb::Mailer.delivery_method = :sendmail
    Merb::Mailer.config = { :sendmail_path => '/usr/bin/fake-sendmail.sh' }
end

3. Rails.

Edit your development environment file with following options:

config.action_mailer.delivery_method = :sendmail 
config.action_mailer.sendmail_settings = {:location => "/usr/bin/fake-sendmail.sh"}