Dr Nic

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

[ANN] Dr Nic’s Magic Models 0.8 – Validate Anything, Anytime, Anywhere

Ladies and Gentlemen, welcome one and all to the greatest Magic Show in Programming. If you bought free tickets to the last show, then you’ll be prepared to pay double for this show because its twice as good. Magic Models provide virtual validations on all database constraints!

In the first run of the sell-out Magic Models – “I can’t believe its an ActiveRecord” – Super Show – you were amazed as we stripped away the necessary essentials of an ActiveRecord – right in front of your eyes – the necessary became unnecessary.

We removed the validates_presence_of validations… and the Magic Model still validated.

We removed the has_many, belongs_to, and has_many :through associations… and the Magic Model still associated.

We removed the actual class definition itself – the very thing that provides the ActiveRecord CRUD layer… and the Magic Model still CRUDded.

You applauded loudly, and then walked out the front door surruptitiously ignoring the donation jar.

Today. Ladies and Gentlemen, I have removed the donation jar entirely so as to not distract your singular focus on the All New Magic Show!

Drums rolling in the background…

Let us start with a humble, unassuming database schema. Today’s schema syntax of choice? SQL.

CREATE TABLE `groups` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(50) NOT NULL UNIQUE,
  `description` varchar(50) default NULL,
  `some_int` integer default NULL,
  `some_float` float default NULL,
  PRIMARY KEY  (`id`)
) TYPE=InnoDB;

The first change to note, is that all Magic Models and their associations are now being generated at startup time, and not dynamically.

>> ActiveRecord::Base.send :subclasses
=> [CGI::Session::ActiveRecordStore::Session, Group]

This is the current solution to supporting the :include option on find calls. Debate the pros/cons of this on the forum. It may be helpful for the various individuals trying to get Magic Models working with Streamlined.

Now… the magic!!! Watch carefully…

>> g = Group.new
>> g.name = "x" * 51
=> "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
>> g.valid?
=> false
>> g.errors.full_messages
=> ["Name is too long (maximum is 50 characters)"]

Crikey! That’s a beauty! That name is just too big for the database and boy does the validation automatically tell you that. Fantastic isn’t it!?

Ok, that’s enough Steve Irwin impressions.

>> g = Group.new
>> g.name = "x" * 50
=> "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
>> g.valid?
=> true

Good.

Ok, more tricks… more drumrolls…

>> g.some_int = 99.9
=> 99.9
>> g.some_float = "string"
=> "string"
>> g.valid?
=> false
>> g.errors.full_messages
=> ["Some float is not a number", "Some int is not a number"]

Tada!!!

And now for the final Virtual Validation magic trick… I draw your attention to the SQL: the name field is UNIQUE. Could the magic models… could they… no… maybe? Oh maybe they could support validation over unique fields?!?!?!

100 dancing girls and guys in silver spandex appear on the stage. Of course, more drums are rolling…

>> g = Group.new
=> #<Group:0x3884eb8 @attributes={"name"=>"", ... >
>> g.name = "test"
=> "test"
>> g.save
=> true
>> g2 = Group.new
=> #<Group:0x3884eb8 @attributes={"name"=>"", ... >
 @new_record=true>
>> g2.name = "test"
=> "test"
>> g2.save
=> false
>> g2.errors.full_messages
=> ["Name has already been taken"]

Dr Nic takes a bow and is escorted off by his 100 dancers and 3 large elephants that turned up late.

Installation

gem install dr_nic_magic_models

The at the top of your Ruby script or at the bottom of your Rails’ environment.rb file, place the line:

require 'dr_nic_magic_models'

Epilogue

Matthew Painter made this possible. He is the author, producer, and cheer leader for this release. Whilst I was fart-arsing around writing Dr Nic’s GrandCivs, and he was doing something more important – making the Magic Models the ultimate magic show in town. And he has a funky website with a draggable background.

The original ideas for the extended Virtual Validations came from Red Hill Consulting: though again it was Matthew who did the work to integrate it into the Magic Models.

The whole of Magic Models architecture has been refactored. Matthew did that.

My 2+ gig of Gmail account filled with diff files. Matthew did that. You get the picture. Thanks Matt!

Anything, Anytime, Anywhere

“Anything, Anytime, Anywhere” was the slogan of the Goodies – a British TV show that was rerun over and over again after school in Australia. Their fame amongst school kids in the 70s and 80s, gives them Rock God status amongst midaged Australians today. They toured Australia in 2005 with 13 sell-out shows. I didn’t get tickets. If you did get tickets, I don’t want to know about it because I won’t talk to you.

[Trick] Natural language DSL in Ruby

Ever tried to explain DSLs (domain specific languages) to someone, so that they know how wonderful Ruby is, and just run out of useful examples? Try this piece of Street Magic on them…

The effect

See if your unsuspecting friend can figure out what this code will do. If they can, then say, “That’s because you are a domain expert in English. Good for you. Now use Ruby you Java boy.”

>> I.say "I love the Ruby language"
You said, 'I love the Ruby language'

Cool, yes? Yes.

How this trick works

To set this up for your unsuspecting Ruby noob, type the following into your console/irb:

>> class I; def self.say(text); puts "You said, '#{text}'"; end; end
=> nil

Now you have a constant I upon which you can call class methods.

In long hand, this is:

class I
  def self.say(text)
    puts "You said, '#{text}'"
  end
end

Create other pronoun classes, such as You, Everyone, etc and give them methods representing verbs, such as say, said, want_to.

For example,

class You
  def self.cannot(action, target)
    puts "Didn't your mother tell you never say never? Of course you can #{action} #{target}"
  end
end

Which delights us with:

>> You.cannot :learn, "Ruby"
Didn't your mother tell you never say never? Of course you can learn Ruby

“I still don’t know what a DSL is”

That’s unfortunate for you.

Exporting Netvibes bookmarks to Del.icio.us

For 12 months I’ve been using Netvibes to store bookmarks. A few weeks ago I started using Del.icio.us, which is also integrated into Netvibes. Now I had two sets of bookmarks. That’s one too many. Time to retire the Netvibes bookmark module, but how do I migrate/export the netvibes bookmarks, and how do I upload them to Del.icio.us?

“How to export Netvibes bookmarks?”

Exporting Netvibes bookmarks is a major request on their forum, so here is the answer.

  1. Login to Netvibes
  2. Go to address: http://www.netvibes.com/modules/bookmarks/getUserBookmarks.php?nocache=0
  3. Save the XML as netvibes_bm.xml

The XML should look something like:

<result>
  <link id="4401802" title=".htaccess generator" url="http://www.tools.dynamicdrive.com/password/" tags="unix"/>
  <link id="6444925" title="19 Rails Tricks Most Rails Coders Don't Know" url="http://www.rubyinside.com/19-rails-tricks-most-rails-coders-dont-know-131.html" tags="RoR"/>
  <link id="4436310" title="37signals - the tools we use ourselves" url="http://37signals.com/svn/archives2/the_tools_we_use_to_run_and_build_37signals.php" tags="business, ideas, RoR"/>
...
</result>

Note that your Netvibes tags are included. These will be uploaded into Del.icio.us too.

“How to upload Del.icio.us bookmarks?”

Now you are ready to upload these bookmarks into Del.icio.us (or modify it yourself to upload it wherever you want). I’ve written a script to do this using the Ruby programming language (dowload and install instructions here). Why? Because it took 10 minutes to do so. Feel free to rewrite it if you really want, but it’d be quicker to install Ruby, run the script, and move on with your life :)

  1. Install Ruby
  2. Install Hpricot [1]: type gem install hpricot (select the win32 version if you are on windows)
  3. Download the netvibes_delicious.rb script [2]
  4. Change the username and password values in the script to your del.icio.us username/password.
  5. Execute the script: ruby netvibes_delicious.rb netvibes_bm.xml
  6. The script will tell you how many bookmarks it will be uploading to Del.icio.us, and ask you to press return to continue. Press Ctrl-C to kill the script.

If you want to “test” the script first, create a copy of your netvibes_bm.xml file, and cut away all the <link ... > tags, except one or two. Check that the script works on these few bookmarks first, then rerun the script on the original file.

Now add the Del.icio.us module into Netvibes and remove the original Bookmarks module.

Tally ho.

[1] Hpricot is a brilliant library for parsing and processing HTML/XML data.

[2] The contents of the script:

['rubygems', 'hpricot', 'open-uri', 'net/https', 'cgi'].each {|lib| require lib}

# Netvibes bookmarks XML file
file = ARGV.shift
bookmarks = []

page = Hpricot(open(file))
page.search("//link").each do |link|
  bookmarks << {
    :title => link.get_attribute("title"),
    :description => link.get_attribute("title"),
    :url   => link.get_attribute("url"),
    :tags  => link.get_attribute("tags").split(', ').join(" "),
    :shared => "yes"
  }
end

puts "Transferring #{bookmarks.length} bookmarks to Del.icio.us"
puts "Do it?"; gets

# Del.icio.us target for bookmarks
username = "" # your del.icio.us username
password = "" # your del.icio.us password

resp = href = "";

bookmarks.each do |bookmark|
  begin
    http = Net::HTTP.new("api.del.icio.us", 443)
    http.use_ssl = true
    http.start do |http|
      pp bookmark
      req = Net::HTTP::Post.new("/v1/posts/add",
        {"User-Agent" => "drnicwilliams.com Netvibes to Delicious"})
      req.basic_auth(username, password)
      req.set_form_data bookmark
      response = http.request(req)
      resp = response.body
      puts "#{Hpricot(resp).search('/result').first.get_attribute('code').humanize} -> #{bookmark[:title]}"
    end     

  rescue SocketError
    raise "Host " + host + " not accessible"
  end
end