Rails Script

Sometimes I want to write a ruby script that uses the models or other things in the Rails application. The simplest way to do it is to write a little script and executes it with Rails script runner. But what if the script is more than a few lines of code and needs to be able to handle multiple arguments? Let’s say I want to build a thrift server that uses Rails models and put it under Rails.root/script folder:

#!/usr/bin/env ruby
require 'optparse'

# Default options
app_config = { :environment => "development" }

# Parses options
OptionParser.new do |opts|
  opts.on("-e", "--environment=name", String,
          "Specifies the environment for the server to operate under (test/development/production).",
          "Default: development") { |v| app_config[:environment] = v }
  opts.on("-p", "--port=name", Integer,
          "Runs thrift server on the specific port.",
          "Default: 9090") { |v| app_config[:port] = v }
end.parse!

ENV["RAILS_ENV"] ||= app_config[:environment]

STDOUT.sync = true

# Loads Rails environment
require File.expand_path('../../config/boot',  __FILE__)
require File.expand_path("../../config/environment", __FILE__)

app_config = YAML::load(ERB.new(IO.read(File.join(Rails.root,
  'config/app.yml'))).result)[Rails.env].with_indifferent_access.merge(app_config)

port = app_config[:port] || 9090
thread_count = app_config[:thread] || 1

# Includes thrift-generated code
$:.push("#{Rails.root}/app/thrift")

class AppServiceHandler
  
  def initialize(options = {})
    ...
  end
  
  ...

end

handler = AppServiceHandler.new(app_config)
processor = AppService::Processor.new(handler)
transport = Thrift::ServerSocket.new(port)
transportFactory = Thrift::BufferedTransportFactory.new()
protocolFactory = Thrift::BinaryProtocolFactory.new()
server = Thrift::ThreadPoolServer.new(processor, transport, transportFactory, protocolFactory, thread_count)
server.serve()
Advertisements

Retrieves all friends information with Twitter gem

This might be trivial but since I do not find an example on the Github page, I would like to give a sample implementation.

# Public: Retrieves Twitter User IDs for the followers.
#
# Returns Array of Twitter User IDs.
#
def get_follower_ids
  follower_ids = []
  next_cursor = -1
  while next_cursor != 0
    cursor = client.follower_ids(:cursor => next_cursor)
    follower_ids.concat cursor.collection
    next_cursor = cursor.next_cursor
  end
  follower_ids
end

# Public: Retrieves Twitter Users the user is following.
#
# Returns Array of Twitter::User.
#
def get_friends
  friends = []
  get_friend_ids.each_slice(100) do |ids|
    friends.concat client.users(ids)
  end
  friends
end

This example shows you how to use Twitter::Cursor and with Twitter API, you can only get 100 user information at a time (that’s why I have each_slice(100) ).

Click here to find out more about Twitter gem.

Guard with OS X Lion Notification

Since Lion has notification center built-in, I do not want to install Growl again. It would be great if I don’t have to switch between editor and terminal to see if all of my tests passed whenever I saved a file. Turned out, with the latest guard with Terminal notification, I got what I wished.

In case you don’t know what guard is, you should check out Ryan Bate’s Railscast on Guard.

Update your guard with bundle update guard, and add the following in your Gemfile:

group :development do
  ...
  gem 'terminal-notifier'
end

And… MONEY!

 

Testing Resque background jobs with RSpec

Let’s say you have a model which involves a background job upon save, or you want to schedule a background job in the controller, how to do you make sure the background will run correctly? One way is to use expectation mock like Resque.should_receive(:enqueue).with(…), but a simpler/better way is just put the following in your before…end block:

Resque.inline = true

That will run your background job right away.

Remember me, devise, and omniauth

This is how you do remember_me with Devise and Omniauthable, this is the OmniauthCallbacksController in one of my apps:

class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    auth(:facebook)
  end

  def twitter
    auth(:twitter)
  end

protected
  def auth(provider)
    auth_hash = request.env['omniauth.auth']
    @user = User.find_for_omniauth(provider, auth_hash, current_user)

    if @user.persisted?
      flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => provider
      @user.remember_me = true
      sign_in_and_redirect @user, :event => :authentication
    else
      session['devise.omniauth'] = auth_hash.except('extra')
      redirect_to new_user_registration_url
    end
  end
end

Ruby and Mountain Lion

Image

This weekend I was trying to do a fresh install of Mountain Lion. This link provides you instructions on how to create a bootable Mountain Lion install disk. If you are going to burn a DVD, you need to make sure you use DVD-DL as normal Mountain Lion won’t fit in the 4.3G DVD.

I use RVM so I installed Xcode and the Command Line Tools. Installing Ruby 1.9.3 with RVM works fine but when I tried to install Ruby 1.8.7, I got:

The autodetected CC(/usr/bin/gcc-4.2) is LLVM based, it is not yet fully supported by ruby and gems, please read `rvm requirements`, and set CC=/path/to/gcc .

The problem is that we need a non-LLVM based gcc compiler. After spending half an hour googling, there are tons of solutions but I found using this homebrew package is easiest:

brew install https://raw.github.com/Homebrew/homebrew-dupes/master/apple-gcc42.rb

You need to install homebrew if you haven’t. I highly recommend you install homebrew as it just makes installing Unix tools so much easier.

The next thing you need to install is XQuartz as X11 is no longer shipped with Mac OS X, so download XQuartz, and run the following to install Ruby 1.8.7:

export CPPFLAGS=-I/opt/X11/include
rvm install 1.8.7

MONEY!

Bonus: MySQL gem 2.8.1

Installing MySQL 2.8.1 gem is just like what you did in Lion, after installing MySQL (I use the 64-bit DMG from MySQL download page), run:

export DYLD_LIBRARY_PATH="/usr/local/mysql/lib:$DYLD_LIBRARY_PATH"
env ARCHFLAGS="-arch x86_64" gem install mysql -v='2.8.1' -- --with-mysql-dir=/usr/local/mysql --with-mysql-lib=/usr/local/mysql/lib --with-mysql-include=/usr/local/mysql/include --with-mysql-config=/usr/local/mysql/bin/mysql_config