Dr Nic

map_by_method – the final announcement

I don’t really talk about my projects after I release them except to show off fancy new things, like newgem sporting the new RubiGen generator.

So I don’t know why I give updates here about one of the smallest projects – map_by_method.

Probably, its because it doesn’t deserve a Google Group or even a webpage really. It should probably be integrated into activesupport gem or something.

Except, it just never seemed to 100% work.

That is, it go more blog coverage than anything else, but wasn’t always useful. Yeah, a like Miss South Carolina.

BUT Version 0.8.2 is now out. It fixes – I believe – all known problems – I think.

gem install map_by_method

In your code (or config/environment.rb for rails):

gem 'map_by_method', '>=0.8.2'
require 'map_by_method'

I added a few more iterator methods too: sort_by, group_by, and index_by.

>> Conference.find(1).conference_sessions.group_by_from
=> returns all MyConfPlan conference sessions grouped by start time, for RailsConf2007

MyConfPlan…

Oh yes it sold. Announcement coming soon.

Plus, a link to the code – as it is being open sourced by its new owner. Sweet!

Well… sweet for you. I already had access to the code.

Next on the todo list

Add something similar to Ambition by Err “we-write-so-many-cool-projects-I-don’t-know-how-we-find-time-to-maintain-them-all” the Blog, Chris and PJ.

I’d kind link something like:

Conference.select { |c| c =~ "Rails").sort_by_name

Which would behave like:

>> Conference.select {|c| c.name =~ /Rails/}.sort_by(&:name).to_sql
=> "SELECT * FROM conferences WHERE conferences.\"name\" ~ 'Rails' ORDER BY conferences.name"

Oh well, perhaps we can talk about it during RailsConf Europe, when I get the microphone to talk about such tom-foolery and hi-jinx: Meta-Magic in Rails: Become a Master Magician

NewGem Generator – now with script/generate

The New Gem Generator (0.13.0)’s newgem command now behaves like the rails command thanks to RubiGen – a new project that is an extraction of the rails_generator.

Developing a RubyGem? You can now have a script/generate command, just like Rails. Other projects, say like Merb, can do this too.

Awesomeness with a Gold Star. (disclosure: Dr Nic awarded this Gold Star to himself)

Warning

This article can get confusing.

Especially around the heading “Generating Generators”.

Plus, this article talks about two projects – an update to New Gem Generator (0.13.0) and the new RubiGen project.

So, give yourself time to digest it all, and then perhaps come back and read it again.

And then perhaps re-write the article for me.

“A generator that can provide generators to your projects, so that you can write generators for your other projects.”

Re-write it – I dare you.

Background

One of the killer features of Rails is the Rails Generator.

It does three things:

  • generates an entire scaffold for your application, thus sharing with you its conventions (over configuration) methodology; and
  • it then allows you to generate more stuff, like models, controllers, plugins etc. Things that are relevant to a Rails app. Finally,
  • it allows you to write your own generators for Rails apps.

Now you can do all this with New Gem.

Killer Feature #1 – upgrade path

Never before have you been able to run ‘newgem’ on top of an existing RubyGem. Why? It used to blow away the entire folder, and then start writing new files… not very friendly.

Now, just like the rails command, you can go into your RubyGem development directory, and run newgem . and you will be asked which files to override.

NOTE: Copy your Rakefile to Rakefile.old, and after running newgem . copy the configuration information into the new file config/hoe.rb.

If you’re using newgem already, I dare you to upgrade as above. (Ok, perhaps make a backup copy of your work first…)

Killer Feature #2 – script/generate

Once you’ve upgraded, or create a new RubyGem (using newgem gemname), you’ll now have two scripts: script/generate and script/destroy, just like Rails has. (and matching generate.cmd and destroy.cmd for Windows users)

Try them out:

script/generate
...
Installed Generators
  Rubygems: application_generator, component_generator, executable, install_jruby, install_rspec, install_website
  Builtin: test_unit

Oh yeah, I’ve had some fun extracting things into generators.

  • Install the dubious-looking NewGem website – script/generate install_website.
  • Install RSpec support – script/generate install_rspec.
  • Make the RubyGem a JRuby gem – script/generate install_jruby (the generated gem will have -jruby.gem in its name)
  • Create an executable Ruby app – script/generate executable appname

These generators are also reused via the newgem commands various options (Run newgem to see them.)

Killer Feature #3 – generate generators

I’ll post more about RubiGen later, but you can create a new rails-like command-line app that generates a whole stack load of directories and files using script/generate application_generator appname.

There is a large USAGE rundown if you run script/generate application_generator.

Want to create your own generators for developing RubyGems? (similar to creating a generator for a Rails app, but for RubyGems)

script/generate component_generator foobar rubygems
    create  rubygems_generators/foobar/templates
    exists  test
    create  rubygems_generators/foobar/foobar_generator.rb
    create  test/test_foobar_generator.rb
    create  test/test_generator_helper.rb
    create  rubygems_generators/foobar/USAGE
    readme  readme

Firstly, note test/test_generatorname_generator.rb – that’s right, you get a test stub for your new generator. Start there, write tests, then write your generator. There’s inline help for useful assertions.

Secondly, note rubygems_generators folder. This folder is the “scope” of the generator. As it starts with “rubygems” the generator will only be available when you are developing rubygems. It will not show up in Rails, nor Merb or Camping or any other place that may support RubiGen one day.

Similarly, if you want to write a Rails generator using the component_generator then specify the scope as rails.

script/generate component_generator booya rails
     create  rails_generators/booya/templates
     exists  test
     create  rails_generators/booya/booya_generator.rb
     create  test/test_booya_generator.rb
  identical  test/test_generator_helper.rb
     create  rails_generators/booya/USAGE
     readme  readme

In Edge Rails (and any Rails version after 1.2.3), the script/generate command in Rails will search all RubyGems for /rails_generator/* folders in addition to the existing search paths (ticket).

Thanks goes to Rails Generator

I’ve long been in love with the Rails Generator for what it does, the beautiful syntax for specifying a generator, etc. I don’t know who wrote what bit, e.g. if DHH wrote all of it, or others wrote nice chunks, but its awesome. Thanks DHH and co. Thanks to Jeremy Kemper

RubiGen is 95% Rails Generator code, with extensions to support scoping. Setting Rails Generator free of its Rails constraints is a tribute to it.

Now RubyGems can use generators, and any other frameworks can integrate generators and their developers can write and distribute additional generators for those frameworks.

I award myself 2 gold stars, and a scratch-n-sniff

map_by_method now works with ActiveRecord associations

I was always annoyed that map_by_method was broken for ActiveRecord has_many associations. 6 mths later I finally fixed it.

That’s the magic of Open Source Software. [/end sarcasm]

So now, the following example works like it should:

$ gem install map_by_method
$ console
> require 'map_by_method'  # stick this in your environment.rb for Rails
> user = User.find_by_name "Dr Nic"
> user.companies.map_by_name
=> ['Dr Nic Academy', 'Dr Nic Institute of Being Silly']
> user.companies.map_by_id_and_name
=> [[1, 'Dr Nic Academy'], [9, 'Dr Nic Institute of Being Silly']]

Recap: why use map_by_method?

Try the following example:

> user.companies.map_by_employees.flatten
=> list of all employees of user

Versus:

> user.companies.map { |company| company.employees}.flatten
or
> user.companies.map(&:employees).flatten

Or compare:

> user.companies.map_by_id_and_name
=> [[1, 'Dr Nic Academy'], [9, 'Dr Nic Institute of Being Silly']]

Versus:

> user.companies.map { |company| [company.id, company.name]}

That is, it looks and feels just like ActiveRecord’s #find method, with its find_by_first_name_and_last_name magic.

Summary

No {, }, |, &, or : required. Just clean method names.

Bonus other gem

In the spirit of ActiveRecord hacks, there is to_activerecord:

$ gem install to_activerecord
$ console
> require 'to_activerecord'  # stick this in your environment.rb for Rails
> [1,2,3].to_user
=> [list of User with id's 1,2,3]

To me, this suffix operator reads cleaner than the traditional:

> User.find([1,2,3])

For example, if you want to perform an operation on the list of Users:

> ids = [1,2,3]
> ids.to_user.map_by_name
=> ['Dr Nic', 'Banjo', 'Nancy']

Versus:

> User.find(ids).map_by_name

Where art thou generator?

Summary: click to select

UPDATE: Rails Generators within RubyGems support is now in Rails Edge

Generators in Rails are awesome. No? So you don’t use the rails command to create a new rails app? Nor the model, migration, or plugin generators? Yeah, they are awesome. Hug the next one you meet.

On my part-time quest to replace the use of Plugins with RubyGems within Rails, it occurred to Geoffrey Grosenbach [1] and myself that there is no easy way to bundle rails generators within RubyGems. Patooey, we say!

Currently, there are several places you can store Rails generators:

  • Builtin ones – like model, scaffold etc – are stored in the rails gem.
  • ~/.rails/generators/ for your own personal generators.
  • Rubygems whose name ends with _generator, such as ajax_scaffold_generator
  • vendor/generators/
  • vendor/plugins/_some_plugin_/generators/ – the most common mechanism for deploying generators

But no way to bundle multiple generators in a RubyGem or have a gem without _generator in its name. And Rails has laid claim to the _generator namespace, which is a bit greedy.

This is outrageous!

Damn it, you’re right! Its now in Rails Edge!

rails newapp
cd newapp
rake rails:freeze:edge

So try out this new Rails Core patch. Never touched a raw patch before? Nevermind. To play with it, let’s create a new rails app:

cd vendor/rails/railties
wget http://dev.rubyonrails.org/attachment/ticket/8730/gem_generators.patch?format=txt -O gem_generators.patch
patch -p0 < gem_generators.patch
cd ../../..

The world’s first Generator-Enabled RubyGem is…

So you can play with this fabbo new feature and explore how to add generators to your RubyGems, Rick Olson’s restful_authentication plugin has been ported into a RubyGem. It is currently available for manual download and will soon be live on RubyForge.

wget http://drnicwilliams.com/wp-content/ruby/gems/restful_authentication-1.0.0.gem
gem install restful_authentication
ruby script/generate

And you’ll see the authenticated generator is discovered from the RubyGem. Neat. Note, we didn’t install the old plugin within the rails app.

So, now let’s play with it…

ruby script/generate authenticated user

It works exactly like it did from the original plugin. But now its a RubyGem, and you never ever need to reinstall this plugin for each new app, ever again. Ever.

Merge plugin generators into RubyGems

There exists a RubyGem daemons, and there exists a separate plugin, that makes it easy to use daemons via your rails app.

This separation will be no longer necessary!

Now the generator code can be moved into the daemons gem itself.

Have a look at the plugin, and note the generators folder. Simply copy this folder into the daemons gem. Then rename this folder to rails_generators. Repackage and release.

Shaazam!

PS. To the owner of daemons repository, come on dude! Upgrade from CVS to SVN:)

Want to write your own generators?

There is some useful info on writing generators on the tubes. Start there.

In all cases, (for an example “foo” generator) the generator file must be called foo_generator.rb, and it must contain a class called FooGenerator.

Does it work with “vendor everything?”

Normal people living normal lives won’t know there exists a Rails Core ticket to support “vendor everything”, created by Marcel Molina Jr [2] (and his backing singer Dave Thomas).

This allows you to store gem files in a /vendor/gems folder, and it will be super magically exploded into the /vendor/gems/home folder. That is, you can freeze raw gems into your rails source structure.

To test this, first uninstall the restful_authentication gem and then reload it into the /vendor/gems folder:

gem uninstall restful_authentication
cd vendor/rails/railties
wget http://dev.rubyonrails.org/attachment/ticket/8511/gems_loaded_from_vendor_like_plugins.diff?format=txt -O gems_loaded.patch
patch -p0 < gems_loaded.patch
cd ../../..
ruby script/generate

Do this just to confirm that the authenticated generate is no longer available.

cd vendor
ls .

Note that you now have a gems folder, and within it you have a home folder, which itself stores lots more new folders. This is Marcel’s patch in action!

cd gems
wget http://drnicwilliams.com/wp-content/ruby/gems/restful_authentication-1.0.0.gem
cd ../..
ruby script/generate

And the authenticated generator is back! Discovered auto-magically from within your own rails source! Investigate the vendor/gems/home folders at your leisure.

So this article is about two things…

Herein layeth a tale of two tickets (first one is now in Rails Edge). Sneaky, nay?

Please try them out and comment within each ticket about any praise/thoughts/fixes/outright-damnations so the nice Rails Core team (esp. to DHH who is not sure if “vendor everything” is something that should be in Rails Core [3]) know this is an important direction for Rails to go.


[1] Normally I’d reference/link
Nuby On Rails or Peepcode, but I just love the “half a head” self-photo on his home page.


[2] More info about Marcel on
Rails Core page.


[3] Based on a chat in irc #rails-contrib the other day, when that ticket was first created.

Smart people doing smart things in Netherlands – RubyEnRails 2007

Summary: click to select

rubyenrails2007-me-presenting-by-fngtps.jpg

Photo by Thijs van der Vossen,
Fingertips

For one million people in Amsterdam, time stood still as a parade of Rails gurus in Netherlands/ Belgium talked up a storm at RubyEnRails 2007. Only 240 were allow inside to the free, one day conference, so riots ensued at the doorsteps of the building. Police were called in. Helicopters. Tanks.

Or so I’m told. I was too busy inside meeting many very clever developers and sharp business people. The buzz of excitement for Rails in the foyer during the breaks was tremendous. The conference organisers recorded the audio of all presentations which will be great to listen to.

I was flown in my private jet, helicoptered to the conference building and shepherded in by 24 Dutch Special Forces men, all of equal height and build, identical in every fashion. Before my keynote, I was interviewed by all the major TV networks. I have no idea what drives me to make this all up.

But I was given a free RubyEnRails conference t-shirt. I was also able to give a couple of presentations:

Keynote

It was a wonderful opportunity to start the day, and I shared “What’s cool with Rails?” focusing on some hot potatoes like scaling and deployment, as well as RESTfulness. Slides here.

Ultimately the basic theme of the keynote was “Its all just text”, and choosing between Rails, Merb, Camping, Mongrel handlers, becomes a design/architectural decision to be made for each portion of each application you build.

I included a live demo of the Magic Model Generator, and encouraged people to write RubyGems instead of Rails plugins, as they provide better support for: dependencies, versioning, and reuse outside of Rails.

DIY syntax

The ability to extend my programming language tickles me pink. Often you write a block of code and you just think “That should be prettier and simpler”.

With Ruby meta-programming, blocks, method_missing, const_missing and optional parentheses you can craft nearly any syntactic sugar you like to replace lengthy, complicated code.

Slides here.

Dr Nic Academy

I also took the opportunity to introduce Dr Nic Academy – training courses in Ruby on Rails by me. The first course will be 7th and 8th of July in Amsterdam. If you are thinking of attending, hold off buying tickets!! The new website and a special bonus discount price will be coming soon. I’d hate you to miss out.