A new Ruby and Rails blog is born
I haven’t been blogging much lately. I’ve been too busy with some vacation time and, of course, work.
This is going to change, but it’s not all going to happen on Programblings.
It’s been a long time coming, but giraffesoft finally has a blog. We’re going to kickstart our blog with a week of open source releases.
At giraffesoft we like DRY code. We all know that creating Rails plugins is barely more work than actually implementing the functionality inside of a specific application. For that reason, we create plugins all the time when working on projects.
So this week, we’re going to polish up a few of them – big and small – and officially introduce them to the world.
For now, please let me direct you to the brand-spanking new giraffesoft blog.
If you’re too lazy to read the introductory post, here’s the punchlines:
I guess we’ve all heard last week’s sad news about Engine Yard diminishing the awesome support they’ve given the Rubinius project. That’s personally how I see it: they’ve put the project on steroids for roughly a year, rather than “they’re now cutting back” x people.
As Brian Ford pointed out, Rubinius is a community project. And Rubinius is not going away. A lot of people can’t wait to have a Ruby written more in Ruby than in C or C++. Koichi Sasada (lead developer on Ruby 1.9) even recently projected that Rubinius would eventually be the Ruby implementation of choice.
Rubinius’ future is promising. I’d like to go from that positive note, and have YOU try Rubinius now. I promise that in 20 minutes, you’ll be running Rubinius and enjoying it. The longest parts of the process will be waiting (cloning, compiling), so it won’t even be difficult. Note: the article may look long, but it’s not. Half of it is code samples and the results returned.
So it’s been aeons since the last article of the RFTL series. But yes, this article is part of a series. You can look at the first 2 parts if you want. Some details1 won’t be up to date anymore, but it’ll help you get the gist of Rubinius if you’re not familiar with the project yet.
- Rubinius for the Layman, Part 1: Rubies All the Way Down
- Rubinius for the Layman, Part 2: How Rubinius is Friendly
- Rubinius for the Layman, Part 3 – Try Rubinius in 20 minutes
The 20 minutes timer starts now, for those who went ahead and read the past articles.
Install Rubinius
Prerequisites
Prerequisites are probably already taken care of on most Ruby developers’ machines:
- ruby 1.8, rubygems, rake and ParseTree (
sudo gem install rake ParseTree) - git (no need to understand it, really, just having it installed)
- general C++ building tools
Dependencies for Mac users
Leopard users should have all they need when Xcode is installed. Insert your Leopard upgrade DVD and from your terminal:
open /Volumes/Mac\ OS\ X\ Upgrade\ DVD/Optional\ Installs/Xcode\ Tools/XcodeTools.mpkg
Dependencies for Linux users
Linux users have to make sure the following packages are installed. Use your the equivalent command for your distro:
sudo apt-get install gcc bison make pkg-config libtool git
(where make == GNU make)
Get the Rubinius code
Right now the GitHub feature to download a tarball is disabled for big projects like Rubinius. They say the feature on the radar as one of the things to fix in the next few weeks. Which will be awesome.
For now we have have to clone the repo, which in this case takes a few minutes. Find yourself a comfortable directory and:
git clone git://github.com/evanphx/rubinius.git
# go make coffee
cd rubinius
Build Rubinius
Assuming you’ve installed all the dependencies, the following should work right off the bat. If it’s not the case, check out the notes on the subject.
rake build
# go get a http://www.brawndo.com/
Later, when you want to recompile from a clean slate, just run rake distclean.
If you have not gotten a BRAWNDO, please skip over the next paragraph.
RUBINIUS, like BRAWNDO, is one of the CRAZIEST ideas of the LAST DECADE! Can you realize you’re ABOUT to try the Ruby IMPLEMENTATION that’s got the BEST Ruby code ratio AMONG THEM ALL! Isn’t that FREAKING AWESOME? You can also do some of the CRAZIEST INTROSPECTION with MethodContext, StaticScope and other classes like THAT!
Run Rubinius
The Rubinius executable is rbx in the ‘bin’ subdirectory. It’s a bit different from MRI in that rbx starts an irb session if you don’t specify a file to run. So let’s do that:
bin/rbx
# in irb
puts "Don't you spring a hello world on me"
#=> Don't you spring a hello world on me
Ok then. Well, technically you’ve now tried Rubinius in less than 20 minutes. Now if you keep reading, you’ll really taste some true Rubinius awesomeness.
Kick-ass introspection
Let’s start slow by patching Object to help us quickly grok the new kinds of objects we may encounter:
class Object
# Return only the methods not present on basic objects
def interesting_methods
(self.methods - Object.new.methods).sort
end
end
#=> #<CompiledMethod interesting_methods file=(irb)>
And now let’s create a basic little class that will help us start our exploration of a MethodContext instance.
class C
def initialize
@inst = 42
end
def get_mc
local_var = 'value'
MethodContext.current
end
end
#=> #<CompiledMethod get_mc file=(irb)>
So with the help of an instance of the class C, let’s start poking gently at a MethodContext.
c = C.new
ctx = c.get_mc
#=> #<MethodContext:0xcc #<C:0xca>#get_mc (irb):7>
ctx.interesting_methods
#=> ["__add_method__", "_get_field", "_set_field", "activate",
"active_path", "alias_method", "back_ref", "block", "class_variable_defined?",
"const_defined?", "const_path_defined?", "context_from_proc", "context_stack",
"copy", "current_scope", "describe", "disable_long_return!", "dynamic_locals",
"file", "fp", "from_eval?", "get_eval_local", "ip", "ip=", "last_match",
"last_match=", "line", "lines", "locals", "locals=", "location",
"make_independent", "method=", "method_module", "method_scope",
"method_scope=", "name", "normalized_name", "nth_ref", "position_info",
"receiver", "receiver=", "reload_method", "script_object", "send_private?",
"sender", "set_eval_local", "set_iseq", "sp", "stack_trace_starting_at"]
ctx.name
#=> :get_mc
ctx.describe
#=> "C\#get_mc"
ctx.method_module
#=> C
Interesting, that reminds me of the monkey-patching discussion that often comes up in the Ruby community. Let’s try something else:
module ModuleMC
def module_mc
MethodContext.current
end
end
#=> #<CompiledMethod module_mc file=(irb)>
C.include ModuleMC
#=> [ModuleMC]
c.module_mc.method_module
#=> #<IncludedModule:0xd6>
c.module_mc.method_module.name
#=> "ModuleMC"
Wouldn’t it be nice if we had that kind of introspection, when comes time to debug some mixin magic?
Now let’s go back our MethodContext object. Or rather, its method accessor, which gives us a CompiledMethod instance:
m = ctx.method
#=> #<CompiledMethod get_mc file=(irb)>
m.interesting_methods
#=> ["__ivars__", "__ivars__=", "activate", "activate_as_script",
"as_script", "child_methods", "compile", "decode", "describe",
"exceptions", "exceptions=", "file", "file=", "first_ip_on_line",
"first_line", "from_string", "hints", "hints=", "inherit_scope",
"is_block?", "iseq", "iseq=", "line_from_ip", "lines", "lines=",
"literals", "literals=", "local_count", "local_count=", "local_names",
"local_names=", "locate_line", "min_stack_size", "name", "name=",
"primitive", "primitive=", "private?", "protected?", "public?",
"required_args", "required_args=", "scope", "scope=", "send_sites",
"serial", "serial=", "splat", "splat=", "stack_size", "stack_size=",
"total_args", "total_args="]
m.describe
#=> "method get_mc: 0 arg(s), 0 required"
m.local_names
#=> #<Tuple: :local_var>
m.literals
#=> #<Tuple: "value", :MethodContext, #<SendSite:0xda
# name=current hits=0 misses=0>>
m.file
#=> :"(irb)"
The local_names method sounds extremely promising, but unfortunately for now, there’s no primitive for actually getting the local variable’s value, but it’s perfectly possible3. It’s just not been done yet.
By the way, what is that? #describe summarizes the arguments? Let’s try something more interesting with it:
def method_with_args(arg1, arg2='default', *args)
MethodContext.current
end
#=> #<CompiledMethod method_with_args file=(irb)>
ctx2 = method_with_args(42, 'towel', "don't panic")
#=> #<MethodContext:0x16e main#method_with_args (irb):4>
m2 = ctx2.method
#=> #<CompiledMethod method_with_args file=(irb)>
m2.describe
#=> "method method_with_args: 2 arg(s), 1 required, splatted."
m2.local_names
#=> #<Tuple: :arg1, :arg2, :args>
m2.literals
#=> #<Tuple: "default", :MethodContext, #<SendSite:0x16c
# name=current hits=1 misses=0>>
Nice! Ok, now let’s come back to our CompiledMethod instance and check out it’s scope accessor.
ss = m.scope
#=> #<StaticScope:0xea parent=#<StaticScope:0xe8 parent=nil
# module=Object> module=C>
ss.interesting_methods
#=> ["initialize", "module", "parent", "script", "script="]
ss.parent
#=> #<StaticScope:0xe8 parent=nil module=Object>
So now we’ve essentially poked 2 levels deep: ctx.method.scope. Let’s rewind again and look at a our context’s receiver and sender accessors. To better understand both, let’s come back to our OO roots of 30 years back and start calling ‘method calls’ ‘messages’ instead.
sender (sends message ‘get_mc’) => receiver
sender is then the caller of the method, and receiver is, the object receiving the message. Also known as self, during the execution of the method.
Let’s see that in action:
r = ctx.receiver
#=> #<C:0xca @inst=42>
r == c
#=> true
So ctx.receiver is a reference to the instance we’d put in the variable c. From there of course we can do Ruby’s regular meta-poking around:
r.instance_variable_get '@inst'
#=> 42
Now let’s look at the sender:
s = ctx.sender
#=> #<BlockContext:0xf0 main#irb_binding (irb):1>
s.class.ancestors
#=> [BlockContext, MethodContext, Object, PP::ObjectMixin, Kernel]
s.interesting_methods - ctx.interesting_methods
#=> ["env", "home"]
s.home
#=> #<MethodContext:0xf6 main#irb_binding
# /Users/mat/dev/_rubies/rubinius/rubinius/lib/irb/workspace.rb:1>
s.env
#=> #<BlockEnvironment:0xf8 @initial_ip=0 @last_ip=268435456
# @post_send=0 @bonus=#<Tuple: true>>
When calling a method from IRB, we’re in a BlockContext instead of a MethodContext, but it’s still in the family.
s = ctx.sender
#=> #<BlockContext:0xfe main#irb_binding (irb):1>
s.class.ancestors
#=> [BlockContext, MethodContext, Object, Kernel]
Anything new we need to know about?
s.interesting_methods - ctx.interesting_methods
#=> ["env", "home"]
s.home
#=> #<MethodContext:0x102 main#irb_binding
# /Users/mat/dev/_rubies/rubinius/rubinius/lib/irb/workspace.rb:1>
s.env
#=> #<BlockEnvironment:0xf8 @initial_ip=0 @last_ip=268435456
# @post_send=0 @bonus=#<Tuple: true>>
All of this is strangely reminiscent of a stack trace. Before you go collecting all senders to explore the execution stack, let me point you to the convenient context_stack:
ctx.context_stack.length
#=> 27
puts *ctx.context_stack
# Too noisy to output here
puts *ctx.context_stack.map{ |s| s.describe }
#=>
# C#get_mc
# Object#irb_binding {}
# Kernel(IRB::WorkSpace)#eval
# IRB::WorkSpace#evaluate
# IRB::Context#evaluate
# IRB::IrbRubinius#process_statements {}
# IRB::Irb(IRB::IrbRubinius)#signal_status
# IRB::IrbRubinius#process_statements {}
# RubyLex#each_top_level_statement {}
# Kernel(RubyLex)#catch {}
# ThrownValue.register
# Kernel(RubyLex)#catch
# RubyLex#each_top_level_statement
# IRB::IrbRubinius#process_statements
# IRB::Irb(IRB::IrbRubinius)#eval_input
# IRB.start {}
# Kernel(Module)#catch {}
# ThrownValue.register
# Kernel(Module)#catch
# IRB.start
# main.__script__
# CompiledMethod#activate_as_script
# CompiledMethod#as_script
# Compile.single_load
# Compile.unified_load
# Kernel(Object)#require
# Object#__script__
# #=> nil
I’m pretty sure there’s other areas specific to Rubinius that can be explored like that. Please share any insight in the comments.
S-Expressions
Rubinius groks s-expressions out of the box (similar to standard Ruby with ParseTree or ruby_parser. An example).
require 'pp'
pp sx = "
class C
def meth(arg)
arg * 2
end
end".to_sexp
#=>
# s(:class,
# :C,
# nil,
# s(:scope,
# s(:defn,
# :meth,
# s(:args, :arg),
# s(:scope,
# s(:block, s(:call, s(:lvar, :arg), :*, s(:arglist, s(:fixnum, 2))))))))
sx[0]
#=> :class
sx[3][1][1]
#=> :meth
With something that reminiscent to Lisp, it’s probably better to explore recursively, though.
S-expressions are used by a lot of the Ruby code inspection tools to understand your ugly Ruby code.
Gems
I won’t touch trying out gems for today. There seems to be little issues as the moment. They do install, but I’ve been having problems running them. Please leave a comment if you’ve had success with specific gems.
If you’re curious and want to try playing with gems, Rubygems is already installed.
rbx gem install rails --no-rdoc --no-ri
Pro tip: always skip the documentation when playing with gems Rubinius. The doc takes unusually long to compile.
Run the famous test suite
The spec suite that’s been keeping all Ruby implementations honest was born from the Rubinius project. It’s been split into a separate project a while ago, since it’s now such an important and central piece of the Ruby ecosystem.
Update the specs
Since they are now in a different project, we first have to get the most recent version. Easy stuff:
rake rubyspec:update
rbx in your PATH
Before you run the specs, you need to do one little thing. One of the specs expects a shell call to rbx to start Rubinius (as in, the executable must be in your path).
The simplest way to do that for now is just to temporarily add the directory to your path (right in your console, not in your .bash_profile).
pwd
#=> /path/to/project/rubinius
export PATH=$PATH:/path/to/project/rubinius/bin
rbx -v
Run the specs
rake spec
# Time for another BRAWNDO!
Or rather, time to actually look at some of the specs you’re currently running. If you look in the spec directory, you’ll see that it’s pretty extensive, to say the least. To start with something familiar, navigate to spec/ruby/1.8, in subdirectories core or library. Open up a few of the specs in there and stare at them for a few minutes. Or better, improve a few of them and try them out on MRI, JRuby and of course, Rubinius.
Conclusion
Well, now I’ve tricked you into putting the Rubinius project on your hard drive. And you’re a Ruby developer. What are you waiting for?
I think Rubinius will be an awesome runtime for our Ruby programs. It probably won’t be only Ruby in the close future, but the kernel of Ruby (base classes & stuff) is mostly implemented in Ruby, and the compiler is also implemented in Ruby. This is awesome to help understand the workings of the language and to lower the barrier to contribution. Which is already pretty low.
Rubinius is here to stay and it’s gonna keep rocking.
Footnotes
- Examples of details not up to date in the old articles are: the LOC numbers and the base language of the VM (used to be C, now C++).
-
If the build craps out with a message you understand, cool. Try to install the dependency through apt-get or macports/fink, or search your hard drive to see if the tool’s just not in your PATH. Otherwise, here are a few related pointers.
- Getting Started
- Installation
- Common Problems
- If you can’t find an answer in the doc, don’t hesitate and ask on IRC in #rubinius (freenode.org). The crew’s very friendly.
- For a quick discussion about getting local variable’s values out of a CompiledMethod, check the [IRC logs around 19:20][9].
git-config has autocomplete?
Seen on a git 1.6.0.4 installation (dunno about previous versions), installed through MacPorts +bash_completion option.
$ git config #tab
apply.whitespace core.compression ...
branch. core.fileMode
clean.requireForce core.gitProxy
color.branch core.ignoreStat
color.branch.current core.logAllRefUpdates
...
With sub-options too, on words ending with a dot:
$ git config remote.origin. #tab
remote.origin.fetch remote.origin.receivepack ...
remote.origin.push remote.origin.skipDefaultUpdate
And as usual, calling git-config with a setting name without specifying a new value displays the current value.
$ git config remote.origin.url
git://github.com/evanphx/rubinius.git
Git’s getting easier by the day. Awesome!
1. A recent change from a previous position!
Tonight I’m trying conciseness.
Editor’s note: I failed.
I recently decided to test my git_remote_branch gem with Ruby 1.9, for the heck of it. Well, I was making sure it ran on a bunch of platforms: Windows, Ruby 1.8.7 and with the most recent Git version (1.6.0.2, get it). So it seemed fitting to check it out under Ruby 1.9.
On Leopard, the only missing dependency to Ruby 1.9 is readline 5.2. This article will present the installation of both. And help heat up your apartment.
Linux too
These instructions will mostly work on Linux as well (tried on Ubuntu). There will be a few minor differences though.
- Make sure you download the original readline from the gnu site and patch it yourself;
- Uninstall older versions of Ruby1.9;
- Make sure you have the basic dev tools installed, like gcc and make;
- Skip the part about installing Xcode :-)
Prerequisites
You first need to have the Apple developer tools installed on your mac. They’re available on your installation CD. Put it in, run the installer. It’s pretty straightforward.
# in your terminal
open /Volumes/Mac\ OS\ X\ Upgrade\ DVD/Optional\ Installs/Xcode\ Tools/XcodeTools.mpkg
If you don’t compile your own stuff often, you may have to set up your PATH variable in your ~/.bash_profile.
# file ~/.bash_profile
export PATH="/usr/local/bin:$PATH"
Now, prepare a working directory to keep the source close to the corresponding executables.
# in your terminal
sudo mkdir -p /usr/local/src
sudo chgrp admin /usr/local/src
sudo chmod -R 775 /usr/local/src
cd /usr/local/src
Once you’ve set yourself up, if you don’t care about the details, you can skip to the end for the Cliff’s notes.
Installing readline
This one’s not as straightforward as it could have been. The gzipped readline library available on the gnu site is 12 patches behind. It so happens that the 12th patch fixes a problem with compilation under OS X. So I applied all 12 to the code and repackaged it. The example uses that file, compiled by me.
You can also do do the patching by yourself if you so wish. Here’s where you can download the readline code:
So let’s get on with the instructions to install readline from my patched package:
# in your terminal
curl -O http://s3.amazonaws.com/webmat-public/readline-5.2-patch012.tar.gz
md5 readline-5.2-patch012.tar.gz
# should be a9f37d2a22d181f8c23c6a320907917d
tar xzf readline-5.2-patch012.tar.gz
cd readline-5.2-patch012
./configure --prefix=/usr/local
make
sudo make install
cd ..
Build Ruby 1.9
Build options
Note that here you have a few options as to how you want to distinguish your 1.9 stack from your main 1.8 one.
In the following example, I build Ruby with the ‘1.9’ suffix, which means all executables will be suffixed with 1.9: ruby1.9, gem1.9, irb1.9, rake1.9 and so on. This approach is ideal for casual use of two versions side by side. If you don’t care about the details, skip right over the next paragraph.
The industrial approach would be to put ruby in a non standard directory and only add it to your path when you want to use that version (or use the explicit path to invoke executables). To go industrial, you can simply use the --prefix=/usr/local/ruby1.9 option and then drop the --program-suffix argument when you run configure for Ruby. This setup is ideal if you really want to have a bunch of versions living side by side (e.g. all 1.9 versions as well as 1.8.7 in addition to the current 1.8.6).
Actual installation of Ruby 1.9
Pick the most recent version or Ruby 1.9 on the ftp server. At the time of writing, 1.9.1-preview1 is the most recent.
So, still from /usr/local/src:
# in your terminal
curl -O ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-preview1.tar.gz
tar xzvf ruby-1.9.1-preview1.tar.gz
cd ruby-1.9.1-preview1
./configure --prefix=/usr/local --program-suffix=1.9 --enable-pthread --with-readline-dir=/usr/local --enable-shared
make
sudo make install
Now you’re about to see some of the funniest looking progress indicators around. Ruby’s about making the programmer happy, and it delivers even in the details!
Note: the recent source packages of Ruby1.9 automatically include the documentation, as the end of the make install attests.
Try Ruby 1.9
# in your terminal
ruby1.9 --version
gem1.9 --version
irb1.9
Once inside the Ruby interactive shell,
# in irb1.9
RUBY_VERSION
#=> "1.9.1"
stabby = ->(msg='inside the stabby lambda') { puts msg }
stabby.call
# => "inside the stabby lambda"
stabby.call 'hello world'
# => "hello world"
Yep, Ruby 1.9 introduces the very cool stabby lambda syntax. Ruby 1.8’s lambdas couldn’t have optional parameters (unless you fiddled with *args). 1.9’s stabby lambdas can, with a syntax as clean as a simple method definition, as you just experimented.
Now install the gems you use everyday (or kindly make available to your peers) and help make them 1.9 compatible.
For the sake of the stabby lambda!
That’s it! (Except for those who skipped to the Cliff’s Notes).
Cliff’s Notes
# Install patched readline
cd /usr/local/src
curl -O http://s3.amazonaws.com/webmat-public/readline-5.2-patch012.tar.gz
md5 readline-5.2-patch012.tar.gz
# should be a9f37d2a22d181f8c23c6a320907917d
tar xzf readline-5.2-patch012.tar.gz
cd readline-5.2-patch012
./configure --prefix=/usr/local
make
sudo make install
cd ..
# Install Ruby1.9
curl -O ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-preview1.tar.gz
tar xzvf ruby-1.9.1-preview1.tar.gz
cd ruby-1.9.1-preview1
./configure --prefix=/usr/local --program-suffix=1.9 --enable-pthread --with-readline-dir=/usr/local --enable-shared
make
sudo make install
# Try Ruby 1.9
ruby1.9 --version
gem1.9 --version
irb1.9
# in irb1.9
RUBY_VERSION
stabby = ->(msg='inside the stabby lambda') { puts msg }
stabby.call
stabby.call 'hello world'
To install any ruby gem which has a command-line interface on Ubuntu 8.10, you have to add a path to your PATH environment variable. In your .bashrc file, add the following line:
export PATH=$PATH:/var/lib/gems/1.8/bin
Also worth noting is the fact that the default ruby interpreter on 8.10 is back to the 1.8 branch: it’s 1.8.7 (1.9 was the default on 8.04 iirc). 1.9 also be installed right besides 1.8.
$ ruby --version ruby 1.8.7 (2008-08-11 patchlevel 72) [i486-linux] $ ruby1.9 --version ruby 1.9.0 (2008-06-20 revision 17482) [i486-linux]
Neither comes installed by default, however. You must install them explicitly.
sudo aptitude install ruby irb rubygems
While I’m at it, why not mention that rubygems 1.2.0 is installed by default. It doesn’t want to update to 1.3.0 with the usual “gem update –system” command. Since it’s not my main machine I didn’t investigate further, but the suggestion is to use apt-get or aptitude. The repos don’t seem to be up to date with 1.3.0, but rather with a version named something like 1.3.0really1.2.0.