Git for Rubyforge accounts

First there was CVS, then came SVN to RubyForge. Actually, I’m guessing that CVS was the sole SCM initially, and SVN was added later. I just can’t imagine them starting RubyForge from scratch and explicitly saying “yeah we’ll offer CVS because …”. I don’t know how that sentence would have finished. It must have been added first. There is no valid “because …”.
But now, with each RubyForge project there is a Git repository. This is great for two very good reasons:
- the end is nigh for empty or unmaintained SVN repositories
- less centralised decentralised development
GitHub + Gitorious are the go-to-guys for somewhere to hang your proverbial hat: somewhere you push your local git repository to so other people can access it.
Yet when I’m investigate someone’s new RubyGem or other project, the first place I look for its source is RubyForge. I assume the project name is the same as the gem name, and goto http://rubyforge.org/projects/pastiepacker for example.
Then I click on the SCM tab. Normally this gives SVN instructions. In this modern era, many projects SVN repos can now be blank or unmaintained with Git repos being preferred by many.
So, the reason to push your git repository to RubyForge’s new git system is to make life easier for me. I mean, I don’t know who else clicks on the SCM link, but I do.
While this is all well and great for new projects - you can select “git” instead of “svn” as per the first image above - but I’m not yet sure how to migrate old projects [follow support ticket].
The other reason is to actually help your distributed repository be distributed. Push it to github. Push it to gitorious. And now you can push it to rubyforge. Sweet.
Adding another remote repository
Since your default remote repository is probably called “origin”, you may wonder how such a naming schema could ever expand to multiple remote repositories? Might I suggest “rubyforge” as the name of the rubyforge repository?
git remote add rubyforge gitosis@rubyforge.org:pastiepacker.git git push rubyforge master git push origin master
If you do this for all your projects, perhaps a bash script is in order:
function gpall() {
git push rubyforge master
git push origin master
}
One project equals one repository, but multiple packages
Using a Rubyforge project’s git repository might be problematic where you release multiple packages/rubygems (codeforpeople, seattlerb, mongrel, drnicutilities, etc). Here I’d prefer one repository per package, rather than one repository for the whole “project”.
Perhaps you could push the master branch from your local repository for each package into different branches of the rubyforge git repository. Thoughts?
Can’t push to your rubyforge git repo?
RubyForge Git repositories are managed via gitosis; gitosis does authentication via public keys. This means that in order to push to a RubyForge Git repository you’ll need to upload a public key to your account - see notes on that here.
Generally, Git support is something we’ve just introduced, so feedback about it on the forums would be quite welcome.
What is Pastie Packer?
The explicit Ruby metaclass you know you always wanted
When you define a “static” or “class” method on a Ruby class, it actually stores the method on that class’s metaclass/singleton class/eigenclass.
_why’s metaid gem gives you a metaclass method to explicit access this object:
require 'metaid'
class Person
def self.oldest
# find oldest person
end
end
Person.methods.grep(/oldest/) # => ['oldest']
Person.metaclass.instance_methods.grep(/oldest/) # => ['oldest']
So now here’s a new, fun way to access the metaclass of a class, look for a constant suffixed with ‘Metaclass’. For the Person class, look for PersonMetaclass. Yep, we can have explicit metaclass constants. Or try PersonClass or PersonEigen or PersonEigenclass. No one can agree on what they are called, so I made them all work.
$ gem install magic_metaclass $ irb
In irb try:
require 'rubygems' require 'magic_metaclass' class Person; end Person # => Person PersonMetaclass # => #<Class:Person> PersonClass # => #<Class:Person> PersonEigenclass # => #<Class:Person> PersonEigen # => #<Class:Person>
Neat.
Finally, the example from above:
class Person
def self.oldest
# find oldest person
end
end
PersonMetaclass.instance_methods.grep(/oldest/) # => ['oldest']
I wrote this gem with no known use cases. If you find any, let me know.
Writing C extensions in RubyGems using newgem generators (plus a free TextMate bundle)
Already know C extensions in RubyGems? Cool - then just run the following cmds and see what can be generated for you; plus check out the TextMate bundle at the bottom.
sudo gem install newgem newgem pickaxe cd pickaxe script/generate extconf my_test rake test
For everyone else…
Its 15000km from Brisbane AU to Prague CZ where Euruko2008 - the European Ruby Conf - was held. I came ready to talk, to met lots of cool multi-lingual Rubyists, and to learn. Ooh, I learnt something alright.
Tim Becker was introducing Native C Extensions for Ruby, and fortunately he said “now, everyone, follow along with this example”. I’d never done native C extensions, but I’d received lots of requests from RubyGem developers on how to do it. I had no idea.
So I was typing in everything Tim told me to type in. When Tim changed slides too quickly, I may have yelled at him to slow down. Perhaps I was the only one doing his tutorial out of 300 people, but I didn’t care. This was gold.
After he finished his session, I dragged him off into the corridor with Jonas Pfenniger (zimbatm), and the three of us mapped out a generic layout for how native C extensions work within RubyGems. I didn’t know any of this, but Tim and Jonas did, and we probably looked silly sitting at a small table in the middle of the narrow corridor.
But at the end, we had a working RubyGem with native C extensions that were built: when the tests were executed via rake, and when the gem was installed. The next day I figured out how to get the C extension built via autotest.
Thanks to Tim and Jonas I was able to then write a extconf generator for RubyGems so that its super-super easy to get started writing native C extensions within RubyGems.
Tutorial
This tutorial is for *nix, as I’m still investigating win32 extensions, and jruby + .net/ironruby extensions. So when I figure that out - hopefully with the help of other people currently at RubyFools conferences, I’ll get back to you.
The code comes from the Pickaxe book - p264, and we’ll insert it into a new RubyGem using newgem (version 0.20.1+):
sudo gem install newgem newgem pickaxe cd pickaxe script/generate extconf my_test
Create a test for a class MyTest that doesn’t exist yet:
# test/test_my_test_extn.rb
require "test/unit"
require 'pickaxe'
class TestMyTestExtn < Test::Unit::TestCase
def test_working
t = MyTest.new
assert_equal(Object, MyTest.superclass)
assert_equal(MyTest, t.class)
t.add(1)
t.add(2)
assert_equal([1,2], t.instance_eval(”@arr”))
end
end
Run rake to build the C extension and run the tests. You can also run autottest and it will automatically build the C extension before running the tests.
To create the MyTest class, using the code from p262 of the Pickaxe book:
#include "ruby.h"
static int id_push;
static VALUE t_init(VALUE self)
{
VALUE arr;
arr = rb_ary_new();
rb_iv_set(self, "@arr", arr);
return self;
}
static VALUE t_add(VALUE self, VALUE obj)
{
VALUE arr;
arr = rb_iv_get(self, "@arr");
rb_funcall(arr, id_push, 1, obj);
return arr;
}
VALUE cTest;
void Init_my_test() {
cTest = rb_define_class("MyTest", rb_cObject);
rb_define_method(cTest, "initialize", t_init, 0);
rb_define_method(cTest, "add", t_add, 1);
id_push = rb_intern("push");
}
To lib/pickaxe.rb:
require "my_test.so" # or require 'my_test' if its unique
The last line will import the generated shared library. If the RubyGem is tested or installed on Windows, then the .dll file will be automatically loaded instead. The “.so” notation is merely a placeholder to explicitly specify the shared C-extension, rather than any Ruby library of the same name.
Now run tests (rake), the C extension will be rebuilt and the tests will pass.
Build and install RubyGem
rake manifest:refresh rake install_gem irb -rubygems -rpickaxe > a = MyTest.new > a.add 3
You have successfully created a C-extension within RubyGems, using TDD.
TextMate bundle for Ruby C extensions
I’ve started a TextMate bundle to give syntax highlighting + some simple snippets for developing the C files for Ruby extensions.
To install:
cd ~/Library/Application Support/TextMate/Bundles git clone git://github.com/drnic/ruby-c-extensions-tmbundle.git "Ruby C Extensions.tmbundle" or wget http://github.com/drnic/ruby-c-extensions-tmbundle/tarball/master tar xfv drnic-ruby-c-extensions-tmbundle-master.tar.gz mv drnic-ruby-c-extensions-tmbundle-master "Ruby C Extensions.tmbundle"
Then restart TextMate or “Reload Bundles”.
You can clone/fork the source via http://github.com/drnic/ruby-c-extensions-tmbundle/tree/master

