You See the Invisible Block?

require "rubygems";require "thin";require"markaby"; class Invisible
HTTP_METHODS =[:get,:post,:head,:put,:delete];attr_reader :request,
:response, :params; def initialize(&block); @actions =[]; @with=[];
@layouts={};@views={};@helpers=Module.new;@app=self; instance_eval(
&block) if block end; def action(method, route, &block); @actions<<
[method.to_s, build_route(@with*"/"+route),block] end;HTTP_METHODS.
each{|m|class_eval "def #{m}(r='/',&b); action('#{m}', r, &b) end"}
def with(route); @with.push(route);yield;@with.pop end; def render(
*args,&block);options=args.last.is_a?(Hash)?args.pop: {};@response.
status=options.delete(:status)||200;layout=@layouts[options.delete(
:layout)||:default];assigns={:request=>request,:response=>response,
:params=>params,:session=>session};content=args.last.is_a?(String)?
args.last : Markaby::Builder.new(assigns,@helpers, &(block||@views[
args.last] )).to_s ; content = Markaby::Builder.new( assigns.merge(
:content => content), @helpers, &layout).to_s if layout; @response.
headers.merge!(options);@response.body=content end;def layout(name=
:default, &block); @layouts[name]=block end; def view(name,&block);
@views[name]=block end; def helpers(&block);@helpers.instance_eval(
&block ) ; instance_eval(&block)  end;  def session;  @request.env[
"rack.session"]end; def use(middleware, *args);@app=middleware.new(
@app,*args) end; def run(*args);Thin::Server.start(@app, *args) end
def call(env); @request = Rack::Request.new(env); @response =Rack::
Response.new; @params = @request.params; if action = recognize(env[
"PATH_INFO"], @params["_method"] ||env["REQUEST_METHOD"]); @params.
merge!(@path_params);action.last.call;@response.finish; else; [404,
{}, "Not found"]; end; end; def self.run(*args,&block);new(&block).
run(*args) end; def self.app;@app||=self.new end;def self.call(env)
@app.call(env) end; private; def build_route(route);pattern= route.
split("/").inject('\/*') { |r, s| r << (s[0] == ?: ? '(\w+)' : s) +
'\/*' } + '\/*';[/^#{pattern}$/i,route.scan(/\:(\w+)/).flatten] end
def recognize(url, method); method =method.to_s.downcase; @actions.
detect do |m,(pattern,keys),_| method==m&&@path_params=match_route(
pattern,keys,url)end;end;def match_route(pattern,keys,url);matches,
params=(url.match(pattern)||return)[1..-1],{};keys.each_with_index{
|key,i| params[key]=matches[i]};params;end;end; def method_missing(
method, *args,  &block);  ; if Invisible.app .respond_to?(method) ;
Invisible.app. send( method, *args, &block);  else; super; end; end

No!

6 Comments

Filed under ruby

RubyFringe == Awesome

RubyFringeNick Sieger though us about Jazz, Giles Bowkett got a standing ovation, Zed Shaw sang “Matz can’t patch” and “Don’t fuck Chad’s community”, Geoffrey Grosenbach was wearing a kilt, Hampton Catlin presented Haml for Javascript, Damien Katz made me cry and Tom Preston-Werner is my new hero.

Better reviews

2 Comments

Filed under conference, ruby

MeshU slides and code

Code is at http://github.com/macournoyer/meshu

5 Comments

Filed under conference, ruby, thin

Speaking at meshU

meshUOn May 20th, I’ll be in Toronto presenting at meshU. Probably about climate change or Thin, haven’t decided yet.

From meshU site:

meshU is a one-day event of 12 focused workshops in three streams (design, development, management) given by those who have earned their stripes in the startup game; people who can talk knowledgeably about everything from interface design to using Amazon’s S3 distributed server network. We hope this addition to mesh will meet a growing need in Toronto: the need for practical, down-to-earth information about tools, knowledge and expertise for startups, web designers and developers of all kinds.

There will be some awesome and famous speakers: Daniel Burka and Leah Culver of Pounce, John Resign, creator of jQuery, Kevin Hale of Wufoo, Avi Bryant of Dabble DB, Jon Lax, Ryan Carson, Allistair Croll and Reg Braithwaite, it’s gonna be crazy-sexy-cool!

I hope to see you there!

5 Comments

Filed under Misc

Ruby to C, a couple snippets

As you might already know, the Ruby interpreter you’re (probably) using (the one at ruby-lang.org) know as MRI, for Matz Ruby Interpreter, is written in C. The VM is entirely written in C and most of the standard libraries too. So it seems that the way to make code run faster is really to dive into C.

Now I hear you yell with your ears all red, eyes full of blood and a big vein pumping on your forehead: But it will be unmaintainable!

If everything seems under control, you’re not going fast enough

- Mario Andretti

The hard part is that the C API of MRI lacks documentation. You either have to search through the mailinglist, look at other people’s code or try to guess from Ruby’s code. I recently ported a bit of code from Ruby to C and here are a couple of Ruby to C mapping that might help you.

If you’ve never written or looked at a Ruby extension before, I suggestion reading Peter Cooper excellent and very simple tutorial on how to set things up.

Objects are VALUEs

my_var = nil
this_is_true = true
some_fixnum = 1

VALUE my_var = Qnil;
VALUE this_is_true = Qtrue;
VALUE some_fixnum = INT2FIX(1);

Strings

string = "hi"

VALUE string = rb_str_new2("hi");

/* Ruby string to C string */
char *s = RSTRING_PTR(str);
int len = RSTRING_LEN(str);

Object to C struct

In C you'll probably want to store your data in a struct. Ruby provide some things to wrap a struct inside a Ruby object. Also, since Ruby is not aware of the stuff created in the C world we have to be pretty explicit about everything.

module MyModule
  class MyClass
    def close
      @closed = true
    end
  end
end

In C

/* This is called by Ruby GC when the object is freed
 * so free all resources used here. We hook it up
 * in the Data_Wrap_Struct call down there.  */
void my_obj_free(my_struct_t *s)
{
  free(s);
}

/* Called by Ruby then and instance of your class is created
 * hooked by rb_define_alloc_func. */
VALUE my_obj_alloc(VALUE klass)
{
  my_struct_t *s = ALLOC_N(my_struct_t, 1);
  /* This stores the struct inside the Ruby object, so you
   * can get the struct back on each method call. */
  return Data_Wrap_Struct(klass, NULL, my_obj_free, s);
}

/* The actual MyClass#close method, first argument is the
 * instance object. It's hook into our class by the rb_define_method
 * call in Init_ */
VALUE my_obj_close(VALUE self)
{
  my_struct_t *s;
  /* This is where we get the struct back from the Ruby object */
  Data_Get_Struct(self, my_struct_t, s);
  
  s->closed = 1;
}

/* Init_* is magically called by Ruby to bootstrap your extension,
 * it's like a main function if you will. */
void Init_name_of_your_extension()
{
  VALUE mMyModule = rb_define_module("MyModule");
  VALUE cMyClass = rb_define_class_under(mMyModule, "MyClass", rb_cObject);  

  rb_define_alloc_func(cMyClass, my_obj_alloc);
  rb_define_method(cMyClass, "close", my_obj_close, 0);
}

Calling methods

obj.method_name

result = begin
  obj.method_name(arg)
rescue
  #
end

rb_funcall(obj, rb_intern("method_name"), 0);
/* if an error is raised, result will be set to Qundef */
VALUE result = rb_funcall_rescue(obj, rb_intern("method_name"), 1, arg);

Blocks

my_ary.each do |i|
  puts i
end

VALUE i_each(VALUE elem, VALUE *obj)
{
  /* elem is the yielded object and obj is a pointer to obj down there v */
  printf("%d\n", FIX2INT(elem));
  return Qnil;
}
rb_iterate(rb_each, my_ary, i_each, (VALUE) obj);

Error handling

raise ArgumentError, "..."

rb_raise(rb_eArgError, "...");
/* To raise an error corresponding to the one in errno */
rb_sys_fail("oops");

Garbage Collection

When creating Ruby objects in the C world, Ruby is not aware of the scope of each, it's impossible to track when a variable is used and when it's not, so it's impossible for the garbage collector to know when do leave or sweep objects. If you create a long live object, that will persist for several method calls you have to tell Ruby's garbage collector when it's in use and when you're finished with it. If you don't, Ruby GC will free your variable in the worst possible time, ending up with unthinkable tragedies around the world, leaving you homeless and poor for all eternity.

VALUE var = rb_str_new2("prout");

/* Tell Ruby GC this object is in use */
rb_gc_register_address(&var);
/* when you're finished you can unregister it and let the GC free it */
rb_gc_unregister_address(&var);

Note that you don't need to do this if you only use the variable in one method since the control is not given back to Ruby during the method call. Also if the variable is still referenced in the Ruby world it's already registered.

More, more, more!

This is far from a complete guide to MRI, you'll need to dig the Ruby doxygen doc if you need more info.

Now lets port Rails to C!

20 Comments

Filed under ruby, tips, tutorial

How many applications can you fit in your VM ?

SanboxSo there I was last night, unable to sleep after Simone finally did went to sleep. I started to play with Why’s Freaky Freaky Sandbox. I knew some people thought about using this to run multiple Rails applications inside the same VM but nobody actually did.

Installing the Sandbox

Installing the Sandbox is kinda scary cause you have to patch Ruby and install it using setup.rb which is not the typical gem install.

Patch Ruby 1.8.6:

cd /path/to/src/ruby-1.8.6
wget http://code.whytheluckystiff.net/svn/sandbox/trunk/patch/ruby-1.8.6-sandbox_needs.patch
patch -p1 < ruby-1.8.6-sandbox_needs.patch
make
sudo make install

Install the Extension:

svn co http://code.whytheluckystiff.net/svn/sandbox/trunk sandbox
cd sandbox
sudo ruby setup.rb

See InstallingTheSandbox for the official guide.

What the Sandbox allows?

It clones the Ruby environment and keeps all the objects seperated in their own freaky freaky sandbox! It’s a bit like running a VM inside another VM!

Rails + Multiple VMs, yeaaaah baby you guessed it!

Rack Sandbox

So I’m hacking on this thing: http://github.com/macournoyer/rack_sandbox that runs a Rack application inside a Sandbox. I was able to run 2 very-very bare bone Rails applications (no db, no session, no plugins) inside the same VM, all served by one single Thin instance!

map 'http://myfirstapp.com' do
  run Rack::Adapter::SandboxedRails.new(:root => '/path/to/a/rails_app')
end
map 'http://mysecondapp.com' do
  run Rack::Adapter::SandboxedRails.new(:root => '/path/to/another/rails_app')
end

I don't fully understand how the Sandbox works, I had to do some crazy hacking to make it work and haven't tested it with several Rails applications. A lot more work needs to be done before this is even usable.

But this allows to save all the memory overhead of running a separate ruby process which is about 60 MB on my machine. So instead of costing you 120 MB to run 2 simple Rails app it's only around 63 MB.

git clone git://github.com/macournoyer/rack_sandbox.git

Feel free to clone, pull and fork!

3 Comments

Filed under Misc

Simone Cournoyer

SimoneSince a few days now, I have the pleasure to be the father of the most beautiful baby girl in the world. You might say that all parents think their child is the most beautiful, but that’s because you haven’t seen mine. I’m totally objective, ok?

I’ve already started to teach her some basic Ruby.

18 Comments

Filed under Misc