meetteem: tech addict

software development, etc.

Viewing posts tagged 'ruby'

Using Capistrano to Create A Remote Git Repository

Last night, I thought about buying a GitHub micro plan to host my private projects. The free account in GitHub gives you a 100MB storage and unlimited public repositories, but no private repositories. It has a nice web interface, which makes things like creating repositories easy.

Anyway, after further thinking, I decided not to buy and use my VPS instead. I have a VPS which has several gigs more of storage space, and that's more than enough for Git hosting. And I only want it for my private/personal projects. For open source, GitHub is always there.

One of the things that I seem to keep repeating is setting up a remote git repository. So to address this repetitive task, I created a tiny Capistrano script to do just that: create a remote repository in my VPS.

Here's the capfile.

set :root_path, "/var/git/"
set :box, "server.com"

namespace :git do

  desc "Create a bare git repository"
  task :create, :hosts => box do
    get_repository_name()
    path = "#{root_path}#{gitdirname}"
    create_bare_repository(path)
  end

  desc "View clone urls of existing git repos in #{box}:#{root_path}"
  task :view_clone_urls, :hosts => box do
    sudo "ls #{root_path}" do |ch, stream, data|
      data.split(/\s+/).each do |r|
        puts "#{r.ljust(20)} -> git@#{box}:#{root_path}#{r}"
      end
    end
  end

  def get_repository_name
    set(:gitdirname) { Capistrano::CLI.ui.ask "Enter project name: " }
    set(:gitdirname, "#{gitdirname}.git") if gitdirname[-4..-1] != ".git"
  end

  def create_bare_repository(repo_path)
    sudo "mkdir #{repo_path}"
    run "cd #{repo_path} && #{sudo} git --bare init"
    sudo "chown -R git:git #{repo_path}"

    puts "\n\nClone url: git@#{box}:#{repo_path}\n"
    puts "To add the new remote to your existing git, run: "
    puts "  git remote add origin git@#{box}:#{repo_path}"
    puts "After committing the changes, push the with the following command"
    puts "  git push origin master\n\n"
  end

end

How to use?

Get it and check out the cap tasks.

~/dev/tools$ cap -T
cap git:create          # Create a bare git repository
cap git:view_clone_urls # View clone urls of existing git repos in server.com...
...

Edit the variables root_path and box near the top of the capfile. root_path is the path in the remote machine where the repo will be stored, and box is the server address.

Let's create a remote repository using cap git:create.

~/dev/tools$ cap git:create
  * executing `git:create'
Enter project name: hello-world
  * executing "sudo -p 'sudo password: ' mkdir /var/git/hello-world.git"
    servers: ["server.com"]
    [server.com] executing command
    command finished
  * executing "cd /var/git/hello-world.git && sudo -p 'sudo password: ' git --bare init"
    servers: ["server.com"]
    [server.com] executing command
 ** [out :: server.com] Initialized empty Git repository in /var/git/hello-world.git/
    command finished
  * executing "sudo -p 'sudo password: ' chown -R git:git /var/git/hello-world.git"
    servers: ["server.com"]
    [server.com] executing command
    command finished


    Clone url: git@server.com:/var/git/hello-world.git
    To add the new remote to your existing git, run: 
      git remote add origin git@server.com:/var/git/hello-world.git
    After committing the changes, push the with the following command
      git push origin master

And bind our project to the remote repository using git-remote add and push it to our newly created remote.

  ~/dev/tools/hello-world$ git remote add origin git@server.com:/var/git/hello-world.git
  ~/dev/tools/hello-world$ git push origin master
  Counting objects: 3, done.
  Writing objects: 100% (3/3), 215 bytes, done.
  Total 3 (delta 0), reused 0 (delta 0)
  To git@server.com:/var/git/hello-world.git
   * [new branch]      master -> master

So, there.

Using ActiveRecord Migrations in Django Apps?

In Episode 40 of the Rails Envy podcast, using Rails ActiveRecord migrations in Django apps was mentioned.

I'm comfortable with AR, and AR migrations are more readable and convenient than using plain SQL to migrate the models, so I decided to give it a try. (I use plain SQL when I migrate the database schemas in my Django projects and I haven't used Django Evolution yet.)

Here's what I did. (If you wanna see the AR migration script now, click here.)

Test Bed

I created a Django project, set up my database, and updated settings.py.

$ django-admin.py startproject mysite
$ cd mysite
$ mysqladmin create mysite
$ mate .                   # open the project in TextMate and update the db connection info in setting.py

Next, I created a Django app called blog. Yeeeehaw! Blog!

$ ./manage.py startapp blog

And added the Post model in blog/models.py

# blog/models.py
from django.db import models

# Create your models here.
class Post(models.Model):

    title = models.CharField(max_length=60)
    body = models.TextField()

    date_created = models.DateTimeField()
    date_published = models.DateTimeField()

Then I added the blog application in the INSTALLED_APPS tuple in settings.py, validate, and created the blog tables.

$ ./manage.py validate
$ ./manage.py syncdb

Check out the CREATE TABLE statements for the blog app.

$ ./manage.py sql blog
BEGIN;
CREATE TABLE `blog_post` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `title` varchar(60) NOT NULL,
    `body` longtext NOT NULL,
    `date_created` datetime NOT NULL,
    `date_published` datetime NOT NULL
)
;
COMMIT;

A New Attribute

Uh. Okay. Now, for the fun part. I added date_updated to the Post model. Kept it simple.

class Post(models.Model):
    .
    .
    .

    date_updated = models.DateTimeField()

And checked the newer CREATE TABLE statements for the blog app.

$ ./manage.py sqlall blog
BEGIN;
CREATE TABLE `blog_post` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `title` varchar(60) NOT NULL,
    `body` longtext NOT NULL,
    `date_created` datetime NOT NULL,
    `date_published` datetime NOT NULL,
    `date_updated` datetime NOT NULL
)
;
COMMIT;

Hmm.. A new NOT NULL column date_updated.

The ActiveRecord Migration Script

Now for the standalone ActiveRecord migration script.

# File: add_date_updated.rb
require 'rubygems'
gem 'activerecord'
require 'activerecord'

# Setup database connection
ActiveRecord::Base.establish_connection(
  :adapter => "mysql",
  :database => "mysite",
  :username => "teem",
  :password => "passw0rd",
  :host => "localhost"
)

# Migration script
class AddDateUpdated < ActiveRecord::Migration
  def self.up
    add_column :blog_post, :date_updated, :datetime, :null => false
  end

  def self.down
    remove_column :blog_post, :date_updated
  end
end

# Migrate
AddDateUpdated.migrate(:up)

The script is pretty straight-forward. It sets up the database connection, defines the migration script, and finally runs the migration. You can always migrate down by replacing the :up with down, but that's a little obvious, right? Also, note that the table name is blog_post, and also the :null => false condition.

Notes, Etc.

  1. It's not really DRY, since the db connection information is in the script itself. Maybe creating a database.yml and putting db connection info there could make things DRYer. The Ruby script and the settings.py can simply load the yaml instead.
  2. I didn't bother with the schema information, versioning stuff. Should I?
  3. If you wanna try it out, the ActiveRecord documentation is here and the ActiveRecord Migration documentation here.

GottaDash with Shoes

In my previous job, I regularly used the (10+2)*5 procrastination hack to boost my productivity. I was pretty comfortable with the system. It kept me focused in what I was doing. It allowed me to conserve my energy for my tasks. Sticking to the system sort of developed my discipline. Because I was aware that I was working and I had progress in my tasks, I felt encouraged and motivated to even more productive. My boss and colleague suggested that we implement a tool to facilitate that (10+2)*5 hack, and we did. The company named it GottaDash. Mikong made a JavaScript version, while I made a Flex version. In making that Flex version, I had to spend some time learning the basics of ActionScript, MXML, and the SDK because I wasn't familiar with the platform and the languages. My learning of Flex had a very good motivation -- RIA using Flex and Ruby on Rails. GottaDash in Flex was the tool I used at work.

But then my MacBook crashed, and along with its hard drive, all my programs and files were obliterated, including the source code of the Flex version. Sigh. I didn't have the time to re-implement the Flex version, and it already took a lot of my work hours, so I decided not to re-implement it.

Last Saturday, I felt that my body was missing its productivity juices; my mind, its focus. So I quickly cooked up GottaDash using Shoes, a tiny UI toolkit for Ruby.

Coding in Shoes was a little bit challenging. Why? Because I wasn't able to find a comprehensive documentation (in RDoc). I switched back and forth between my code and the Shoes manual. There was one time that I wanted to know the api of the Shoes::Every, because I wanted to subclass it. Nope. I failed to find it. It isn't in the internets? That's weird. I ended up getting the Shoes source code and going through it. I wasn't able to find the documentation for the :styles options for elements.

Another thing I had a problem with was the video command (I only needed to play the alarm sound). You can't play hidden videos. They have to be visible in order to play them. I ended up creating a stack element with a size of 1x1 pixel just to hold the alarm mp3.

So here it is. Grab it, open it with your Shoes installation, and feel the joy of being productive!

P.S. In the spirit of open source, I've uploaded the GottaDash source code in github, if you want to take a look at it. It's released under MIT license.

P.P.S. A friend recommended to me that I should set up a way to accept donations, possibly through PayPal. I haven't done so and I'm still wondering if people would actually want to donate money to me. But I sure hope that this will be useful for many. It's useful for me.

Update There are times when Shoes would crash. I couldn't trace the bug atm, but when I have the time, I'll update the thing, maybe refactor the code a little bit to make it smaller and simpler.

Factory Girl!

Last month, factory_girl was featured in the RailsEnvy podcast. factory_girl is one of the best things that happened to my Rails TDD/BDD life. It's the only other thing I use with rspec and rspec-rails, though I'm pretty open, so suggestions to make making tests easier are very much welcome. The one con is that factory_girl is tied to ActiveRecord, but it's not really an issue for me because all my non-personal projects use AR. If you are still using fixtures, take a look at factory_girl. It's really nice. Anyway...

factory_girl has a nice feature that lets you define a sequence which you can use to generate unique things. For example, to generate an email address, you can do this (taken from factory_girl's rdoc):

# define a sequence
Factory.sequence :email {|n| "user#{n}@example.com" }

# calling the `next` method
Factory.next :email
# => "user1@example.com"

Factory.next :email
# => "user2@example.com"

However, one of the behaviors in my projects that is not so easy to do with factory_girl is generating unique strings that cannot contain numbers.

I added this behavior in my projects using the following monkey patch I made.

class Factory

  def self.sequence(name, initial_value = 1, &block)
    self.sequences[name] = Sequence.new(initial_value, &block)
  end

  class Sequence

    def initialize(initial_value = 1, &proc)
      @proc = proc
      @value = initial_value
    end

    def next
      prev, @value = @value, @value.next
      @proc.call(prev)
    end

  end

end

This would make the sequence more generic such that anything that has a next method can be passed. A string object has next method so we can use that to generate unique and sequential strings, with no numeric characters. For example,

Factory.sequence :email, 'aaaa' {|s| "user#{s}@example.com"}

# calling the `next` method
Factory.next :email
# => "useraaaa@example.com"

Factory.next :email
# => "useraaab@example.com"

We can even implement a PrimeSequence class and pass it to Factory.sequence to generate a prime number sequence. ;)

Last night, I forked factory_girl in GitHub and applied this monkey patch to the fork. The clone url is git://github.com/teem/factory_girl.git. You can check it out here. I've included specs as well, which has the prime number sequence I mentioned earlier.

Hope this helps you in making the world a better place.

P.S. I recently found Rick's model_stubbing. I haven't looked at it so atm, I have no idea how it works, but the features look promising.

Getting My Feet Wet With Rubinius

Recently, Rubinius hit some major milestones. Merb runs on it; Rails runs on it. With these two Ruby frameworks running on it, I decided to get my feet wet.

I installed Rubinius using the instructions here. I had most of the prerequisites; I just had to grab libtool and bison using MacPorts.

But when I ran rake build, I encountered an error.

Unable to send initialize on #<FFI::NotFoundError> (NoMethodError)

I logged on to IRC (#rubinius on irc.freenode.net) then asked for help there.

After less than two hours, changes were pushed to the git repository, and after pulling those changes, I was able to build Rubinius. Very nice!

The first thing I did was to check if the rubinius binary actually ran. I just checked the version.

$ ./shotgun/rubinius -v
rubinius 0.8.0 (ruby 1.8.6 compatible) (3b48d1b7b) (05/20/2008) [i686-apple-darwin8.11.1]

I launched irb using Rubinius and just to check if it's really Rubinius, I sent the methods.sort message to irb and compared it with MRI's irb methods.sort. They were different. I guess it works.

I then tested out other methods in irb. One thing that I immediately noticed was the local_variables in irb duplicates things. (This happens only in irb but not in Ruby scripts running on Rubinius. MRI gets this right.)

irb(main):004:0> local_variables
=> ["_", "_"]

One of the features I really wanted to check out was bytecode compilation.

I quickly made up a simple (and useless) Ruby script, dum.rb.

module Fields
  def self.included(base)
    base.extend(Fields::ClassMethods)
  end

  module ClassMethods
    def field(sym)
      attr_accessor sym
      puts "Added the field '#{sym}' to the class '#{self}'"
    end
  end
end

class TestClass
  include Fields

  field :name
end

t = TestClass.new
t.name = "Tim"
puts t.name

I ran this script using Rubinius.

$ ./shotgun/rubinius ~/dev/lab/rubinius/dum.rb 
Added the field 'name' to the class 'TestClass'
Tim

It created another file, dum.rbc. I ran this compiled code and it worked.

$ ./shotgun/rubinius ~/dev/lab/rubinius/dum.rbc
Added the field 'name' to the class 'TestClass'
Tim

I then moved the Fields module to another file, fields.rb, and required it. Running this new script created a separate fields.rbc. I required fields.rbc instead of the fields.rb and it still works.

This means that, as the Rubinius main page says,

... you can distribute in easy-to-install packages similar to jar files. Other measures to protect intellectual property can be easily added.

That's a big plus for those who like to develop in Ruby in a closed source environment.

When I have the time, I plan to check out Rubygems in Rubinius next.

<< Older Posts