Using Authlogic with Rails 3

Posted by Dan Sosedoff on November 15, 2010

Authlogic is a good authentication library for Rails, supports a lot of different methods. But its not fully compatible with Rails 3 which is solvable. This solution works for latest gem version (2.1.6):

Basically, default setup is the same, nothing specific;

class UserSession < Authlogic::Session::Base ; end

And you’ll probably get error message like this:

undefined method `to_key’ for <UserSession: no credentials provided>

In this case all you have to do is to define additional method in your UserSession model:

class UserSession < Authlogic::Session::Base
  def to_key
    new_record? ? nil : [ self.send(self.class.primary_key) ]
  end
end

And everything should work just fine.

Debugging email output in Rails

Posted by Dan Sosedoff on November 05, 2010

Need to troubleshoot email output? No problem. Just need to configure your development environment with the following lines:

# RAILS_ROOT/config/environments/development.rb
config.action_mailer.delivery_method = :sendmail 
config.action_mailer.sendmail_settings = {:location => "/usr/bin/fake-sendmail.sh"}
config.action_mailer.default_url_options = { :host => "YOUR HOST" }

And here is the fake-sendmail utility i wrote: (save as “/usr/bin/fake-sendmail.sh”)

#!/usr/bin/ruby
 
dir = "/tmp/fake-sendmail"
headers = ''; $stdin.each_line { |l| headers << l ; break if l.strip.length == 0 }
body = '' ; $stdin.each_line { |l| body << l }
format = body.match(/html/i) ? "html" : "txt"
filename = Time.now.to_i
 
Dir.mkdir(dir) unless File.exists?(dir)
File.open("#{dir}/#{filename}_headers.txt", "w") { |f| f.write(headers) }
File.open("#{dir}/#{filename}.#{format}", "w") { |f| f.write(body) }

Paste: http://pastie.org/private/er61gafxb6fzowjo4chjvq

As the result you’ll see 2 files created for each outgoing email. One has all email headers and another one has the body.

Also, it works for Sinatra and Merb. Woot!

Some fun with Pastie

Posted by Dan Sosedoff on September 30, 2010

I really like public paste service – pastie.org. Too bad it does not have public API.
The most common user case for me – drop some files. 1 or 2 usually. So i have to copy and paste file contents into the form, select private and then send link to someone. Too many actions i think.

I decided to spend some time and create console tool and simplified api based on html extractions.

Source: http://github.com/sosedoff/pastie

Just install it:

$ sudo gem install pastie-api

And use it (multiple files are supported):

$ pastie file
$ pastie file1 file2 ... fileN

Also, API access:

require 'rubygems'
require 'pastie-api'
 
# Create a new private paste
p = Pastie.create('Test string')
 
# Create a new public paste
p = Pastie.create('Hello!', false)
 
# View paste details
puts "Paste ID: #{p.id}"
puts "Paste Key: #{p.key}"
puts "URL: #{p.link}"
puts "Raw link: #{p.raw_link}"
 
# Find existing paste
p = Pastie.get(1234567) # find by paste's ID
p = Pastie.get('abcdefabcdef') # find by paste's private code

Planning to add local pastes history and auto-paste from clipboard.

Maintenance page for Rails applications with nginx

Posted by Dan Sosedoff on September 22, 2010

Here is a small tooltip how to setup maintenance page for Rails applications under nginx. I usually put offline.txt into public directory while making any changes or testing.
Solution works with any type of backend server (passenger/php-fpm/upstream)

Sample vhost config:

server {
  listen 80;
  server_name HOSTNAME;

  location / {
    root /path/to/your/public/dir;
    passenger_enabled on;
    rails_env production;

    if (-f $document_root/offline.txt) {
      return 503;
    }
  }

  location /maintenance.html {
    root /path/to/your/public/dir;
  }

  error_page 503 /maintenance.html;
}

Pastie: http://pastie.org/private/pgnhvnkj4uxe4mkgnhvqw

Why rapid development is the best way to make web apps

Posted by Dan Sosedoff on September 17, 2010

What do you know about rapid development?

I guess something. Almost every developer experienced moments when you or someone have really interesting idea and wanted to jump on it right away. One of the obstacles – tools you use to achieve it. And the web framework is the first of them. Why? Because if you want to create something cool without being stuck on technology level you should use things that you dont really notice while making your app. Of course it varies from person-to-person, but just imagine yourself in traffic jam driving stick-shift car, switching gears like crazy all the time.

Back to webdev, with my experience i can only say that the best tool for rapid development is Sinatra. Its so small and powerful that i got hooked on it since first day i found it. Almost all my personal/work projects on early stages have been developed with Sinatra. Another big plus – small and usefull extensions, big players like AR or DataMapper. It was extracted from Merb project, another small and powerful web framework i like to work with. Its like a one-file web application. Perfect for super-fast development. Wanna do simple music downloads page? No problem. API – again, no problem. Deployment is simple and similar to most of frameworks, as it built on top of Rack: thin, passenger (nginx/apache).

What do you know about rapid development?
I say – i love it.

Custom field aggregations in Sphinx using SphinxQL

Posted by Dan Sosedoff on September 06, 2010

Sphinx is a really powerful tool for a full-text database search. It is the perfect option as a search engine on your website’s data.
In default mode it works as a regular tcp server and has multiple native language bindings for php, ruby, c, etc. But its another outstanding feature is MySQL Protocol Connectoin and SphinxQL, which is similar to native mysql query language.

So, ok. Lets say we have N documents with M attributes. Attributes could be different: string, integer, double, boolean. Out objective is to perform attribute aggregation based on specified search term (user-defined, etc). That will give us full information on data selected only by search term. Its only use-case when you really need to get these aggregate fields. Next part is tricky and not really efficient.

First of all, you have to setup Sphinx search daemon instance using different configuration file (it could not run both). Another problem – you have to setup another data sources and index files, Sphinx puts a lock on all used-right-now files.

Lets assume we have a database of books. We need to build a form with sliders which could be used as user-friendly search filter. All we need is to get a list of min and max attributes values. But there is a problem: sometimes, while working with sphinx you might find yourself trying to use it like you usually do with regular RDMS. Unfortunately, sphinx has a different design. Basically, sphinx has one primary field which presents in each search request – DocumentID. Its an unique id that represents your data ID, which makes it harder to product aggregate data. And there is no way to get rid of that field.
The whole idea of our aggregation – using boolean match mode with no weighting performed at all. In that case all results will have weight field = 1. That will give us ability to group all the results by weight field, rejecting the DocumentID field.

Here is the sample query:

SELECT
  MIN(reviews) AS min_reviews, MAX(reviews) AS max_reviews,
  MIN(pages) AS min_pages, MAX(pages) AS max_pages,
  MIN(pub_year) AS min_date, MAX(pub_year) AS max_date,
  @weight AS w
FROM 
  INDEX_NAME
WHERE
  MATCH('SEARCH_TERM') AND pages > 30
GROUP BY w OPTION ranker = none

The result of this query will be one row with field alias names. Thats’s it.

All statements are fully customizable. Just check full SphinxQL reference for details.

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

Debugging PHP applications in terminal

Posted by Dan Sosedoff on July 03, 2010

Most of Ruby web frameworks have terminal logging in development environments. It makes application debugging process much easier than using file logging. Especially for AJAX requests. Of course there is simple solution – use Firebug and javascript console. Unfortunately it is not that convenient.

So, one day i came up with idea to make the same ruby-like style of application logging, even with colorized output.
I decided to use EventMachine library as a server platform. And API is based on JSON packets, via json/pure. Simple, isnt it?

And i called it DebugServer. Pretty understandable :)

Installation

To install just type

$ sudo gem install debugserver

And for information type:

$ debugserver -i
Usage: debugserver [options]
    -h, --host HOSTNAME              Server hostname
    -p, --port PORT                  Server port
    -i, --info                       Get usage information

By default it will start on localhost:9000.

Usage in PHP

First, download library from GitHub repository: http://github.com/sosedoff/debugclient-php

And then:

// place it into app initialization file
$debug = DebugClient::instance();
$debug->connect();
 
// .... and write it to terminal output
$obj = array(
  'id' => rand(0, 0xFFFF),
  'name' => 'Sample name',
  'time' => strftime('%m-%d-%Y', time())
);
 
// these functions are globally defined
debug_clear(); // clear terminal
debug('This is a plain text message.');
debug_info('This is an informational message.');
debug_warning('This is a warning message.');
debug_error('This is an error message.');
debug_dump($obj);
 
// optional
$debug->close();

Results:
Output

Sources

DebugServer: http://github.com/sosedoff/debugserver
PHP Client: http://github.com/sosedoff/debugclient-php

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