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
Rails 2.0 TextMate bundle - Tasty Tidbit - respond_to and view navigation
The new release of the Rails TextMate bundle is coming soon. Its guaranteed to be shiny, sparkly and will fit in with any home or office decor. More importantly, it will be upgraded for Rails 2.0.
Today is the first Tasty Tidbit - a demonstration of one of the snazzy new features coming to your Macintosh soon.
In this Tasty Tidbit, we look at respond_to and the ability to create and navigate to view templates based on the selected format block, such as wants.js -> .js.rjs.
Cannot see the embedded video? Want the Hi-Def version? Download the video (5 Mb).
Contribute to the Bundle
To clone the git repository and start sharing your own personal goodness, see previous article.
One-stop JavaScript Unit Testing for Rails 2.0
Previously, I mentioned a multi-step/multi-project solution to doing JavaScript Unit Testing for Rails, including an autotest script to make TDD life easier for the autotest-addicted.
It was too many steps, too many different projects, and too much dependence on prototypejs. So let’s fix this via the spanky new newjs (introduced here):
Instead of several steps, there is now two steps:
cd myrailsapp # rails 2.0 app
sudo gem install newjs # v1.3.1+
script/generate javascript_test application
create vendor/plugins/javascript_unittest/lib
create vendor/plugins/javascript_unittest/tasks
create test/javascript/assets/jsunittest.js
create test/javascript/assets/unittest.css
create config/javascript_test_autotest.yml.sample
create vendor/plugins/javascript_unittest/lib/jstest.rb
create vendor/plugins/javascript_unittest/tasks/runner.rake
create vendor/plugins/javascript_unittest/tasks/autotest.rake
create vendor/plugins/javascript_unittest/README
create test/javascript/application_test.html
create script/rstakeout
create script/js_autotest
It installs all assets, gives you a rake test:javascripts task, gives you script/js_autotest, and finally (and most importantly), creates the application_test.html test stub.
As always, you can also run a single test file by loading it into a browser.
Prototype independence
Previously, the test suite - unittest.js - had a dependency on prototypejs. This was wonderful if you’re developing in prototypejs, but could cause some grief if you weren’t.
newjs now comes packed with an identical test suite - jsunittest - but it is built without a dependency on prototypejs.
Merb et al support
I wrote a rails generator because I used Rails. If a merb/other framework (Ruby or non-Ruby) wants a similar generator, just ask. Its pretty simple - I copy over the rails_generators folder into a merb_generators folder in newjs and change some things based on your frameworks’ structure.
Alternate test suite support
If you like another test suite, and want it included in the generators, this might require some additional effort on your part. It probably has different mechanisms for launching runners etc; whereas I was able to steal the rake tests + support libraries from prototypejs. But, it can be done.
Peepcode
These tools will be discussed in animated detail in the forth coming Peepcode “JavaScript Unit Testing”.
Get ready for the TextMate “Trundle to Rails 2.0 Bundle”
Have you noticed lately that the Rails TextMate git pull origin two_point_oohbundle (also installed with e - the Text Editor for Windows) isn’t quite as helpful as it once was - guiding and amplifying your rails app development.
Your resourceful/restful rails apps are now filled with named routes, but none of the helpers (link_to, redirect_to, etc) use them. The default view template was still rhtml for a long time (latest svn trunk fixes this to be html.erb, though Marc Jeanson is having a crack at supporting both through a per-project config file).
Or perhaps you were hoping for some snippets for new features of Rails 2.0: assert_difference (asd), assert_select (ass), respond_to (rst).
Or just maybe, you’ve been updating your local bundle to Rails 2.0 already? Please say yes, please say yes. If you are saying “yes”, read at the bottom for how/where to send me the patches. If you are saying “no”, read at the bottom for how/where to start creating patches and sending them to me.
Firstly, DHH didn’t write the Rails bundle
I didn’t know this - I just assumed the guy who created Rails and helped promote TextMate also created the Ruby on Rails TextMate bundle. Instead if was WWF’s “The Rock” himself Duane Johnson (figurines sold separately, and may look different to the real Duane Johnson who writes Rails code instead of prancing around in his underpants with other adult males in confined spaces).
The TextMate SVN logs say that it the bundle was first committed in Jan 2005. Its now Jan 2008. That’s three years of maintaining a suite of code that thousands of people take for granted every day writing Rails code. What a champion.
Like the Australian Cricket team’s wicketkeeper, Adam Gilchrist, he’s also retiring at the top of his game.
New Maintainer… me
Instead of maintaining my own projects, I’ve decided to maintain this one. I love the TextMate bundles. Before I had a Mac, I ported them across to RadRails and many RadRailers entered a golden era of snippet happiness.
My bundle-fu may not be the equivalent of Bruce Lee, but I’m keen to see my favourite Rails tool live on strong.
Bleeding edge bundle
Whilst the TextMate SVN administrators process the maintainer change request (Allan is on holidays somewhere and he took the house keys with him), you might think all is lost!
Never.
The straight-off-the-cow bleeding edge Rails bundle can be accessed via Git. Specifically the branch two_point_ooh.
So, let’s all do away with our pre-existing Rails bundle and live on the edge.
cd ~/"Library/Application Support/TextMate/Bundles/" mv "Ruby on Rails.tmbundle" "RoR.tmbundle.orig" git clone git://github.com/drnic/ruby-on-rails-tmbundle.git "Ruby on Rails.tmbundle" cd "Ruby on Rails.tmbundle" git checkout --track -b two_point_ooh origin/two_point_ooh git pull
Now, back in TextMate, go to menu “Bundles” > “Bundle Editor” > “Reload Bundles”.
You, my friend, are sitting pretty.
Upgrading to when newer blood is on the bleeding edge
Coming back for more?
cd ~/"Library/Application Support/TextMate/Bundles/Ruby on Rails.tmbundle" git checkout two_point_ooh # optional; only if you changed to another branch etc git pull
Now, back in TextMate, go to menu “Bundles” > “Bundle Editor” > “Reload Bundles”.
Bathe yourself in glory. Splash it all over yourself.
Submitting patches
If you go into TextMate, launch the Bundle Editor (Cmd+Alt+Ctrl+B) and change something in the Ruby on Rails bundle, then files will added/changed within your local git clone.
What I mean is, you can very simply submit patches now. [When I say "simply", I mean, "I think this is the best approach but I could be completely wrong here..."] Git is so cool that I don’t know what its doing most of the time; but let’s hang in there as a team.
cd ~/"Library/Application Support/TextMate/Bundles/Ruby on Rails.tmbundle" git add . # to add any new files/snippets/commands you created; TextMate uses one file per snippet git commit -a -m "Useful message about this patch goes here" git diff -p HEAD^ HEAD
[Theoretically] this creates a patch based on your most recent commit. So you can easily create patches for changes even if your repository and the remote/central/bleeding edge repository look different.
If your patch was created over several commits, then replace HEAD^ with HEAD~4, if there were 4 commits.
I think. God I could be wrong about this though.
Just try your best and if it works, let me know and I’ll remove all this “I don’t know what I’m talking about” text and future readers will think I know everything. Web 2.0 rocks.
Send patches to…
Me drnicwilliams@gmail.com [update] or via the new Google Group.
Want to fork the git repo? Use a free account at Gitorious or GitHub.
|
|
| Ruby on Rails: TextMate |
| Visit this group |
Trundle to Rails 2.0 Bundle
Arguably, we just keep patching the bundle forever and bleeding edges will continue to live happily within the blood and muck of said existence.
But, lots of other people want nicely zipped up bundles. No doubt “e - the Text Editor” will want a “released” version so they can clean it of all the ugly things they can’t yet replicate like Macros. And eventually I’ll push it all down to the SVN repo and it will be released with future TextMate upgrades.
So, there will be a “Rails 2.0 compatible” grand opening one day soon. Probably before 14th of March 2008, when I’m giving a “What’s new in Rails 2.0?” talk at QCON London. Showing off the new bundle will be sweet.
That gives us 6 wks to refactor this bundle to make it a shining example of all that is wonderful in Rails 2.0. Every patch helps.
[Please consider Rails 1.2.6 and before developers when conceiving of patches, and please consult your doctor if pain persists.]
