Dr Nic

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

Another use for const_missing – generating unicode characters in strings

I just spotted something very sexy on the RubyForge News-vine (subscribe to all RubyForge news and new releases): a new use for Ruby’s const_missing method – charesc.

If the unicode character for ü is #00FC, then you can insert that into a string using the global constant U00FC. Where is U00FC defined? Its not. You ask for it and a unicode string is returned. Oohlala.

For the demo we’ll use a Rails app as it has built-in unicode support to make the output look nicer:

Using any old irb:

$ sudo gem install charesc
$ irb
>> $KCODE = "u"  # thanks Paul Battley
>> require 'rubygems'
>> require 'charesc'
>> "charesc is made by Martin D#{U00FC}rst"
=> "charesc is made by Martin Dürst"
>> U00FC
=> "ü"

If you have any more fun uses for const_missing please let me know. Feed my fetish.

Spring Collection – the Modular Magic Models

Dr Nic’s Magic Models are like cheating on your taxes but without jail time. Its like filling out your tax return, and the tax office saying, “No, no, you keep your money – this year’s on us.”

Its like coming home and dinner is already cooked for you.

Its like being good looking and funny.

But this is Ruby, so we call it Magic!

With the release of 0.9.1, you can configure how much cheating, lying and thievery – err, magic – you want.

Already got model classes, but you haven’t gotten around to writing validations for some of them?

class Person < ActiveRecord::Base
  generate_validations
end

As always, if you don't have a Person class, you'll get it for free plus validations.

As always, with automatically created classes AND pre-existing classes, if your schema supports an association and you ask for it it will be there. If you don't want a certain association to exist in your application... don't ask for it. The Magic Models are no place for enforcing law and order.

The other publicly announced, above board, for-sale to the general public feature you might like to know about is Magic Modules.

You can now use modules to specify common features of magic models. Currently supported is table name prefixes. If you need anything else, ask.

module Admin
  magic_module :prefix_table_name => 'admin_'
end

Admin::Permission.table_name # => 'admin_permissions'

The upcoming Magic Multi-Connections use this same concept to allow modules to specify different database connections instead of using superclasses. All this and more in the Magic Models: Spring collection series.

The end.

Ok, there's more. Secret stuff. Backdoor hooks into a Harry Potter world of mystery. Ways to execute code, import modules, and what-not upon newly created magic models. Awesome magicalness.

But you'll need to read the code to find them, and have some imagination as to what you'd want to dynamically do to your generated classes.

But as an example to prod your brain. Our schema at work supports date-ranging on many tables. The table will have a primary id key, plus another primary key - effective_start_date. All these tables end with the suffix '_history'. So when a new model class is created, I use the hooks to test the table name suffix, and if table_name =~ /_history$/ then I include a module that adds more methods etc to these date-ranged classes.

Meta-Magic in Ruby: Dr Nic Unplugged in Stockholm

Last nights’ Ruby meeting in Stockholm had a great turn out and starred Ola Bini sharing the latest and greatest about JRuby, and myself giving an overview on the wonders of Meta-Magic in Ruby.

I’ll write a separate post on Ola’s presentation shortly. It was awesome and I videoed it. Hehehe.

But first and foremost, lets talk about me. Or rather, let’s talk about my talk, which was also videoed.

Meta-magic in a programming language is as important to programmers as changeable ring tones are to teenagers. Authors of programming languages cannot provide every feature to everyone, so it is so wonderful to be able to add new language features and extensions that you want. Everyone knows you can add Jessica Simpson as your mobile ring tone, but not all programmers know that you can add new features to their programming world.

So here is an overview to a new world of happiness. It also overviews how the Magic Models work, and introduces a new gem I’m working on – the Magic Wiggly Lines – described as “genius or insane

I love “map by pluralisation” [now: map_by_method]

Update: this is a gem called map_by_method.

The other day I introduced a new syntax idea that I call “map by pluralisation”. Everyday I use it in code and in the console/irb I fall more in love with its simplicity – both to type and to read.

The following are all equivalent:

>> BankTransaction.columns.names  # map by pluralisation
=> ["id", "amount", "date", "description", "balance"]
>> BankTransaction.columns.name   # singular works too
=> ["id", "amount", "date", "description", "balance"]
>> BankTransaction.columns.map_name  # merge collector (map) + operation (name is method of BankTransaction object)
=> ["id", "amount", "date", "description", "balance"]
>> BankTransaction.columns.collect_name  # merge collector (collect) + operation
=> ["id", "amount", "date", "description", "balance"]

All of which are easier to read and quicker to type than the current equivalents:

>> BankTransaction.columns.map {|p| p.name}  # standard map
=> ["id", "amount", "date", "description", "balance"]
>> BankTransaction.columns.map &:name  # Symbol.to_proc
=> ["id", "amount", "date", "description", "balance"]

You can now type:

>> BankTransaction.columns.select_primary  # merge collector (select) and operator (primary returns true/false on a Column object)
=> [#<...MysqlColumn:0x3c8a4f0 @limit=11, @sql_type="int(11)", @primary=true, @type=:integer, @number=true, @name="id">]

Instead of:

>> BankTransaction.columns.select {|c| c.primary}
>> BankTransaction.columns.select &:primary

Use with ActiveRecords

“Map by pluralisation” is truly wonderful in the console for exploring and collecting data models.

Without knowing a thing about the data model, I bet you can understand either of the following:

@transactions = BankTransaction.find :all, :conditions => ['date = ?', Date.today], :include => [:accounts => [:owner]]
return @transactions.collect_accounts.select_overdrawn?.collect_owner.full_names

Answer: the full name of each owner of an overdrawn bank accounts for all today’s bank transactions.

It reads well as there is a minimum of {, }, |, &, : characters.

Download

Code available here

Tip for use with ActiveRecord Associations

You may need to cast associations to Arrays.

@account.transactions.to_a.map_dates

The result of the transactions assocation on the Account class is not an Array. My initial attempts to provide the “map by pluralisation” code (a method_missing) didn’t work, and casting it to an explicit Array using to_a seemed simple and harmless.

Discussion of Syntax Ideas

Ruby Forum