Debugging Specs

It is easy enough to debug specs on by one. For this you just run the ruby file that contains the spec with ruby-debug instead of ruby:

rdebug spec/rur_spec.rb

But when running via rake, you can’t do this. A simple solution is to add these lines in the spec file you want to debug (or in spec_helper.rb):

require_library_or_gem 'ruby-debug'
Debugger.start

And you are in business…

git branches, merges and remotes

After releasing the first version of git-rails, Ron Damen created a clone of the repository and started improving on it. And I liked what he was doing, so now was the time to figure out how to get some of his changes back in to the master repo.

Here is the list of steps (and what they mean) to get his changes into the master branch on Gitorious.

First, define a remote branch to track his cloned repository

git remote add ron git://gitorious.org/git-rails/rons-mainline-clone.git

This adds this to .git/config:

[remote "ron"]
        url = git://gitorious.org/git-rails/rons-mainline-clone.git
        fetch = +refs/heads/*:refs/remotes/ron/*

Before doing the actual checkout, make sure you have nothing to commit in the current branch, then checkout a tracking branch:

git checkout -b ron/master


Now get the content from thre remote (remote “ron”, branch “master”)

git pull ron master

Once you’ve reached this point, it is easy to switch back and forth between branches

git checkout master
git checkout ron/master

Next, I created a temporary branch to merge what I like from Ron (not needed really)

git branch merge-ron
git status # does not switch branch!
git checkout merge-ron

This could be used as a shortcut: checkout -b merge-ron

Merge only specific changes (obtained using git log in ron/master branch)

git cherry-pick dc3de57a073e24eeb398e0cacbe52340258e861b
git cherry-pick 61579f92e1bfc95e582728011fcd21c79f08c3ae

Merge from merge-ron branch into local master

git checkout master
git merge merge-ron

And finally, push to origin

git push origin

From now on, the only thing needed to get more changes from Ron is to pull changes into ron/master, cherry-pick changes and merge back into master. Sweet!

rebuilding git-rails

Rebuilding Git-Rails

Install Git

On linux, install git-core using your favorite package management tool should do it.

On Mac OS, using Mac Port:

sudo port install git-core

clone the repositoty

git clone git://gitorious.org/git-rails/mainline.git

Make sure all the specs pass:

spec spec

I’m not sure why rake does not see the task and I’ll fix this shortly

Then have at it!

And to do a local install and try it out:

rake install

And uninstall

rake uninstall

git-rails uses hoe, so you have a lot more commands available

Share

Then once you have something you think would benefit others, create a clone and share it!

Rails Undo Redo

What is RUR (Rails Undo Redo)

RUR (Rails-Undo-Redo) is a rails plugin (and soon a gem as well) to make it so easy to implement multi-level undo redo in your Rails application that you better start thinking of better excuses not to implement Undo/Redo (no, seriously, it is going to be a lot easier).

Try the demo!

To get a feel on how undo/redo can help make your app a lot more difficult for users to make mistake, test drive the RUR demo app

The full source code for that demo app is available here: rur_demo. Or via git:

git clone git://gitorious.org/rur_demo/mainline.git

Installing RUR

Using git-rails (this way, you’ll be able to stay up to date, assuming you use git):

git-rails install git://gitorious.org/rur/mainline.git rur

Using scrip/plugin:

script/plugin install http://svn.nanorails.com/plugins/rur

Then copy the migration from vendor/plugins/rur/migrations to db/migrations (renumber as needed), then run:

rake db:migrate

Using RUR

Define the undoable models

Once you have installed the plugin, for each model that you’d like to have its changes undone, just add act_as_undoable>

class Project < ActiveRecord::Base
  belongs_to :user
  has_many :tasks

  acts_as_undoable
end

Define the undoable controllers:

Add the undo/redo logic to all your controllers:

class TasksController < ApplicationController
  ...

  undoable_methods
  ...
end

Track changes

For all controller methods that make a change (and that are directly called by the user), you need to specify 3 things:

  1. an explanation of the change is
  2. what url to go back to when the user decides to undo the action
  3. what url to go back to when the user decides to redo the action

For this, you enclose the code that will change the records within a “change block”.

For example, here’s a create method:

def create
  @task = @project.tasks.new(params[:task])

  respond_to do |format|
    change("create task #{@task.title}", project_tasks_path(@project), project_tasks_path(@project)) do
      if @task.save
        flash[:notice] = 'Task was successfully created.'
        format.html { redirect_to(project_task_path(@project, @task)) }
        format.xml  { render :xml => @task, :status => :created, :location => @task }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @task.errors, :status => :unprocessable_entity }
      end
    end
  end
end

or for update:

def create
  @task = @project.tasks.new(params[:task])

  respond_to do |format|
    change("create task #{@task.title}", project_tasks_path(@project), project_tasks_path(@project)) do
      if @task.save
        flash[:notice] = 'Task was successfully created.'
        format.html { redirect_to(project_task_path(@project, @task)) }
        format.xml  { render :xml => @task, :status => :created, :location => @task }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @task.errors, :status => :unprocessable_entity }
      end
    end
  end
end

or for destroy:

def destroy
  @task = @project.tasks.find(params[:id])
  change("delete task #{@task.title}", project_task_path(@project, @task), project_tasks_path(@project)) do
    @task.destroy
  end

  respond_to do |format|
    format.html { redirect_to(project_tasks_url(@project)) }
    format.xml  { head :ok }
  end
end

Or even a non REST method:

def move_to
  @task = @project.tasks.find(params[:id])
  @new_project = Project.find(params[:task][:new_project_id])

  unless (@new_project == @project)
    change("move task #{@task.title} to #{@new_project.title}", project_task_path(@project, @task), project_task_path(@new_project, @task)) do
      @task.project.tasks.delete @task
      @new_project.tasks << @task
    end
  end
  flash[:notice] = "Task was successfully reassigned to #{@new_project.title}"
  redirect_to(project_task_path(@new_project, @task))
end

Any change within a change block to a model with an “act_as_undoable” attribute will be recorded and can then later on be undone and redone by calling undo or redo.

Let the user undo and redo

The last piece of the puzzle is to add the following to your views (the layout is probably a good place for it):

<% if undo_redo_links != "" %>
<p><%= undo_redo_links %></p>
<% end %>

Example

For a real life example, check out the rur_demo source code.

How does it work?

Coming up shortly… :)

Contributing

The ruby forge RUR project

Clone away the Git repository: git://gitorious.org/rur/mainline.git

git clone git://gitorious.org/rur/mainline.git

Or, better yet, create your own branch on gitorious.

ANN: git-rails

Git is quickly becoming one of the best option for using a DVCS (Distributed Version Control System). Git main goal is to be quick (most everything you do is local), and has already been battle tested by the Linux Kernel dev team.

Recently, I’ve started experimenting with git after Rick switched Mephisto to git.

Even though things are pretty straightforward, some things are easier than others, especially when you have to take it all in in one shot.

So, over the weekend, I created git-rails, completed with a rubyforge project, an installable gem (gem install git-rails), and a git repository hosted by Gitorious

Currently, git-rails provides 3 functions:

  1. init repository (hookup to remote git repository, rails aware .gitignore)
  2. install plugin managed by git (using git submodules)
  3. update plugin

For now, I’m happy to use giston to manage svn hosted plugins, but I’m considering whether to add that functionality as well. We’ll see how things go.

To get started with git-rails, head over to the new git-rails project page.

Inspired by piston, and its newest sibling: giston/braid

git-rails – Tools for using git with Ruby on Rails

Git-Rails

git-rails is a simple tool to help manage your rails app with git.

Currently, it provides 3 helpful commands:

Init

Initializes a git repository, and links to remote url if provided (for push/pull)
Also creates a .gitignore file with:

log/*.log
tmp/**/*
.DS_Store
public/cache/**/*
doc/api
doc/app

Example

git-rails init -m "message" -c git@gitorious.org:rur/mainline.git

Install

Installs a plugin (managed by git) from a remote git repository

Example

git-rails install git://gitorious.org/rur/mainline.git plugin-name

Update

Updates a plugin from original source

Example

git-rails update rur

Getting started

gem install git-rails

Helping out

The rubyforge git-rails project

The Gitorious git-rails repository

Rebuilding git-rails

If warranted, I’d be happy to create a mailing list, but for now, leave a comment on this page.

Git to svn (read-only)

As a follow up to last post about setting up git with gitorious

Here are the steps to mirror a git branch into svn.

First, I had to install git-svn

$ sudo port deactivate git-core
$ sudo port install git-core +svn

That did not got so well when trying to run git-svn:

error about Error.pm:
Can't locate Error.pm in @INC (@INC contains...

Something did not get copied correctly. Here’s the solution:

$ cp /opt/local/var/macports/software/git-core/1.5.2.4_1+doc/opt/local/lib/perl5/site_perl/5.8.8/Error.pm /opt/local/lib/perl5/site_perl/5.8.8/

Then create a place holder in svn so a location exist (it’s fine if it is empty):

$ svn mkdir svn+ssh://svn.nanorails.com/home/psq/svn/plugins/plugins/rur -m "creating rur"

Then add this to .git/config:

[svn-remote "nanorails"]
  url = svn+ssh://svn.nanorails.com/home/psq/svn/plugins/plugins/rur
  fetch = :refs/remotes/nanorails

nanorails will become the name of the branch

Then fetch the svn (it won’t fetch anything, but this starts the process).

$ git svn fetch nanorails

$ git branch -a
* master
  nanorails


And the new branch is here :)

Checkout that svn branch into a local branch:

$ git checkout -b local-svn nanorails
Switched to a new branch "local-svn"

$ git branch -a
* local-svn
  master
  nanorails

Now the fun part.

Using rebase will forward the local commits to the updated upstream head

$ git-svn rebase
Current branch local-svn is up to date.

Get all the goodies from the master branch

$ git merge master
Merge made by recursive.
 .gitignore                            |    1 +
 MIT-LICENSE                           |   21 ++
 README                                |   48 +++++
 Rakefile                              |   20 ++
 init.rb                               |    3 +
 lib/undo_action.rb                    |   24 +++
 lib/undo_manager.rb                   |   89 ++++++++
 lib/undo_record.rb                    |  224 ++++++++++++++++++++
 lib/undoable.rb                       |   40 ++++
 lib/undoable_helper.rb                |   23 ++
 migrations/001_create_undo_records.rb |   32 +++
 spec/rur_spec.rb                      |  132 ++++++++++++
 spec/spec_helper.rb                   |   26 +++
 spec/undo_action_spec.rb              |   46 ++++
 spec/undo_manager_spec.rb             |   10 +
 spec/undo_record_spec.rb              |  368 +++++++++++++++++++++++++++++++++
 spec/undoable_spec.rb                 |   46 ++++
 17 files changed, 1153 insertions(+), 0 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 MIT-LICENSE
 create mode 100644 README
 create mode 100644 Rakefile
 create mode 100644 init.rb
 create mode 100644 lib/undo_action.rb
 create mode 100644 lib/undo_manager.rb
 create mode 100644 lib/undo_record.rb
 create mode 100644 lib/undoable.rb
 create mode 100644 lib/undoable_helper.rb
 create mode 100644 migrations/001_create_undo_records.rb
 create mode 100644 spec/rur_spec.rb
 create mode 100644 spec/spec_helper.rb
 create mode 100644 spec/undo_action_spec.rb
 create mode 100644 spec/undo_manager_spec.rb
 create mode 100644 spec/undo_record_spec.rb
 create mode 100644 spec/undoable_spec.rb

And finally, send the changes to svn

$ git-svn dcommit
Committing to svn+ssh://svn.nanorails.com/home/psq/svn/plugins/plugins/rur ...
    A    .gitignore
    A    MIT-LICENSE
    A    README
    A    Rakefile
    A    init.rb
    A    lib/undo_action.rb
    A    lib/undo_manager.rb
    A    lib/undo_record.rb
    A    lib/undoable.rb
    A    lib/undoable_helper.rb
    A    migrations/001_create_undo_records.rb
    A    spec/rur_spec.rb
    A    spec/spec_helper.rb
    A    spec/undo_action_spec.rb
    A    spec/undo_manager_spec.rb
    A    spec/undo_record_spec.rb
    A    spec/undoable_spec.rb
Committed r66
    A    Rakefile
    A    .gitignore
    A    init.rb
    A    lib/undo_manager.rb
    A    lib/undo_action.rb
    A    lib/undo_record.rb
    A    lib/undoable_helper.rb
    A    lib/undoable.rb
    A    MIT-LICENSE
    A    spec/undo_record_spec.rb
    A    spec/rur_spec.rb
    A    spec/undoable_spec.rb
    A    spec/spec_helper.rb
    A    spec/undo_manager_spec.rb
    A    spec/undo_action_spec.rb
    A    migrations/001_create_undo_records.rb
    A    README
r66 = 98602d45907206a281f597f87445397f069cdc1d (nanorails)
No changes between current HEAD and refs/remotes/nanorails
Resetting to the latest refs/remotes/nanorails

And then there was much rejoicing across the land :)

Now, to update svn, the only things to do, assuming master is where the latest is, are:

$ git checkout local-svn
$ git merge master
$ git-svn dcommit

This setup could also bring changes back from svn, which git-svn is capable of doing, but I’ll be quite happy with the read-only part.

Resources:

  1. using-git-with-svn
  2. git-svn
  3. git tutorial
  4. gitorious

Update: in fact, it looks like “git-svn dcommit” from the master branch, without merging to the “local-svn” branch first, also commits to svn.

in git | 692 Words

Getting started with Git

I’ve been using git for a bit now since Rick switched Mephisto to git, but I’ve only been using an existing repository, and not using the full thing just yet.

So as I’m starting a new project, I have been taking a few notes to get started with a new project (I’m also testing gitorious.org in the process, a free git repository provided by Johan Sørensen)

First, I created a .gitignore file to filter out anything unwanted. On a Mac, for example, it is alway a good idea to add:

.DS_Store

Then to create the initial repository and commit locally

$ cd vendor/plugin/rur
$ git init
Initialized empty Git repository in .git/
$ git add .
$ git commit

Now the big push:

$ git push git@gitorious.org:rur/mainline.git
The authenticity of host 'gitorious.org (67.207.146.32)' can't be established.
RSA key fingerprint is 1d:0f:80:3b:95:c5:a8:08:85:f6:fc:0a:b2:54:52:bb.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'gitorious.org,67.207.146.32' (RSA) to the list of known hosts.
No refs in common and none specified; doing nothing.
fatal: The remote end hung up unexpectedly


Whoops! Not quite what I expected. Let’s try to pull first then?

$ git clone git@gitorious.org:rur/mainline.git
Initialized empty Git repository in /Users/psq/tmp/mainline/.git/
fatal: no matching remote head
fetch-pack from 'git@gitorious.org:rur/mainline.git' failed.


Hmm, not quite either.

Looking at the gitorious “faq”http://gitorious.org/about/faq provides the answer :) Add the remote and master definitions to .git/config:

[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
       url = git@gitorious.org:rur/mainline.git
       fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
       remote = origin
       merge = refs/heads/master

(Change to match your project).

Then, try to push again:

$ git push origin master
updating 'refs/heads/master'
  from 0000000000000000000000000000000000000000
  to   74bbfc838d4984ee81d5560daee3511fe044d36e
Generating pack...
Done counting 22 objects.
Deltifying 22 objects...
 100% (22/22) done
Writing 22 objects...
 100% (22/22) done
Total 22 (delta 0), reused 0 (delta 0)
refs/heads/master: 0000000000000000000000000000000000000000 -> 74bbfc838d4984ee81d5560daee3511fe044d36e

Finally! And checking the gitorious site, the code has been pushed!

(You can get your push and pull urls from the mainline page.

To get started locally with an existing project, just use the pull url

git clone git://gitorious.org/rur/mainline.git

Now, if I can figure out the best way to get an svn mirror from gitorious, I’ll be in business!

Source (and a good tutorial): A tutorial introduction to git (for version 1.5.1 or newer)

A final note: the project referenced here is not quite fully baked, so use at your own risk…

in git | 454 Words

MacHeist

If you have a Mac, or want one, then you should surely take a look at the $49 bundle MacHeist has put together. An amazing value, with lots of cool apps. Plus 25% of your purchase goes to charities.
It’s like getting 1password, CoverSutra (truly cool), and Snapz Pro for half the price, and getting all other 11 Mac Applications for free. Check it out (via Obie Fernandez)

Update: I had the link wrong (using https), and you could not see the apps. Fixed. Also forgot to mention you also get CSSEdit. And possibly VectorDesigner if they reach the $300,000 donation level.

Ruby-debug quick tips: init file and -n option

I use ruby-debug quite a bit (too much? perhaps). And every time I start it, I find myself setting a few options (always the same ones), and it finally got to the point where I thought there had to be a better way. And there was! (Thank you, Kent)

The solution is even documented in some recent release notes:

Create a .rdebugrc in your home directory (it also looks for one in the current directory first):

set autolist
set autoeval
set autoreload
set forcestep

And voila! Each time you start rdebug, it executes this script.

An other good tip: run with -n to avoid stopping on the first instruction, now that you don’t need to run all these commands.

Update: with version 0.10, -n does not seem to be recognized, but —no-stop still works.