Dr Nic

Dr Nic’s Magic Show at RejectConf2007

Update: there is a patch available for edge rails to support merging by generators; add comments to the ticket.


The original Dr Nic’s Magic Models were named as such because I entertained the idea of showing them off as a live magic show. So, given 3 minutes in front of some of the Ruby community’s hottest hackers, I got my chance! Not the Original Magic Models, but the never-before-released Magic Model Generator.

How to install and use the magic_model_generator follows the video from RejectConf:

The Magic Show

How to use it

Create a rails application, and point it to your database.

$ rails magic_show -d [mysql|sqlite|postgresql|oracle|etc|etc]
$ cd magic_show
$ cp database.yml.sample database.yml
and point it to your database.yml to your legacy database

I use the database created for the ActiveRecord test cases – activerecord_unittest. If you’ve never downloaded the activerecord gem, run rake build_[mysql|sqlite|postgresql|oracle|etc|etc]_databases, and then rake test_[mysql|sqlite|postgresql|oracle|etc|etc], then you’ve probably got more free time than I do as a result and I appreciate that. And so does my wife.

Now install the magic_model_generator gem:

$ sudo gem install magic_model_generator

Nonetheless, you’re done. That’s all the preparation I did for the video.

Next I recreated the schema.rb file and the schema_info database table via rake db:migrate.

Next I ran the generator:

$ ./script/generate magic_model

And we’re done.

Coming soon

The MMG is awesome.

The one major drawback of the MMG is the same drawback of all rails generators: if you want to regenerate your models (say you update your schema via migrations) then you cannot regenerate your model associations and validations without completely recreating the file, thus destroying anything else you wrote. Which is useless.

The world of version control (subversion, cvs, etc etc) already solved this problem: merging. So I’m investigating adding merging to the rails_generator. That should be neat.

New Gem Generate to get merging too?

Currently the newgem command doesn’t use the rails_generator for creating files. So adding merging to rails_generator won’t help newgem. That is, unless I rewrite newgem. So, I’ll look into that too.

Related posts:

  1. NewGem Generator – now with script/generate The New Gem Generator (0.13.0)’s newgem command now behaves like...
  2. Magic Wiggly Lines => GuessMethod, by Chris Shea If you ever make time to code just for pleasure,...
  3. New Gem Generator gets RSpec [video] One day you will be able to write a...
  4. “Reads -> slaves, writes -> master” plugin Another solution to multiple connections in Rails has been put...
  5. Magic Multi-Connections: A “facility in Rails to talk to more than one database at a time” At this point in time there’s no facility in Rails...

14 Responses to “Dr Nic’s Magic Show at RejectConf2007”

  1. Jack Nutting says:

    Nice work on MMG! Great to see you advancing this concept further.

    I have one idea for you, regarding the model-regeneration drawback you mention above: Instead of try to merge a newly generated version of a model with an old one that may have been modified, how about splitting each model class into two files, one to contain machine-generated code, and one to contain hand-coded additions, with one of them requiring the other. For instance, for a Thing class, you’d generate these:

    #-----  Thing.rb  -----
    require '_' + File.basename(__FILE__)
    class Thing < ActiveRecord::Base
        # hand-coded methods go here
    end
    
    #---- _Thing.rb ----
    # created by the magic model generator.  don't edit this file!
    class Thing < ActiveRecord::Base
        @has_many :froobs
        @belongs_to :loogy
    end
    

    Then, when it's time to regenerate, you recreate _Thing.rb entirely,
    and if Thing.rb already exists, you leave it alone (maybe spitting
    out a warning if the 'require' line is missing or something).

    This idea is already implemented for CoreData (think of it as
    ActiveRecord for Cocoa programmers) in a third-party tool called
    mogenerator, so that you can recreate model classes after changing
    the model schema without wiping out hand-coded parts; That's where I
    got the idea from.

  2. Dr Nic says:

    I’ve created a ticket to track my thoughts and patches for the merge: http://dev.rubyonrails.org/ticket/8439

    I agree with the multiple files approach, though I’m thinking of using the subversion merge with conflict approach – 3 files: original, new, and merged.

    Feel encouraged to add any thoughts about this into the ticket (and add yourself to the cc list to get email updates)

  3. Mat Schaffer says:

    I don’t know how possible this is, but it could be really nice if your 3 file approach somehow matched the way SVN outputs its files. Then people would have a shot at resolving generator conflicts with the same tools they use to resolve regular code conflicts.

    As always, Great work Dr. Nic!

  4. Dr Nic says:

    @Mat – there are TOOLS to resolve conflicts? I need to get my hands on them. I haven’t found a nice way to use TextMate to find and resolve conflicts yet. But it never occurred to me til now that there might be such tools. Certainly navigating between conflict regions in a project sounds handy at the very least.

  5. Dr Nic says:

    There is now a patch available for edge rails to support merging for generators if you wish to test drive it.

    To patch your edge rails:

    cd /vendor
    curl http://dev.rubyonrails.org/attachment/ticket/8439/generator_merging.patch?format=txt > generator_merging.patch
    patch -p 0 < generator_merging.patch
    

    And go.

    Windows users: you'll need to install cygwin and the diff3 application, and add it to your path.

  6. AkitaOnRails says:

    Hey, this is nice. Jack’s multiple file approach reminds me of an old Eclipse plugin for Hibernate called Hibernate Synchronizer. It did something similar: if the model is called Trick, it would create a class Trick that extends from a BaseTrick. If regeneration was needed only the Base* classes were overwritten leaving all the user logic secure.

    I feel a little bit awkward about the revision based original/new/old approach. In a normal situation I would see dozens of models and several dozens .new and .old files spread out. And what happens when you regenerate again before fixing the conflicts? Maybe a multiple file or multiple class approach would be easier to handle?

  7. Dr Nic says:

    @jack + akita – I think support for a generated base model and a “free to edit” subclass model is a nice solution; and I’ve also seen it implemented elsewhere.

    Ultimately the two solutions can co-exists, because “m”erge is an option to be selected when a conflict is found;

    Where as support for generated base classes is specific to generated files that include a single Ruby class, such as models/controllers, but not other types of generated content.

    That is, it will be more complicated to implement, but certainly can be implemented in addition to offering merging as an option for conflict resolution.

  8. I found a (very old?) ruby project http://rubyforge.org/projects/merge3/ with the description “Merge3 is a 3 way merge for text/binary files. It works similar to diff3 but uses move/insert instructions to merge cases diff3 can’t.”

    Maybe this can be used without requiring a seperate diff/merge tool?

  9. Matt Scilipoti says:

    Looking for a tool to resolve conflicts? Check out TortoiseSVN (http://tortoisesvn.tigris.org/TortoiseMerge.html#merge). When a conflict is encountered during a svn update, the ‘resolve conflict’ menu choice opens a tool displaying three versions of the file: the original, the repo head, and the working copy. It also indicates which merges can be handled automatically and which require manual intervention. You can use either the included TortoiseMerge or an external tool. Until recently, I would have recommended KDiff3. Recent updates have made the internal merge tool almost as slick and it now includes a ‘mark as resolved’ action. Sweet. I highly recommend you review TortoiseSVN. Disclaimer: no amphibians (or furry, bipedal mammals) were harmed, or compensated, during the making of this commercial.

  10. Craig Buchek says:

    I was going to suggest something very similar to Jack Nutting’s idea. I don’t think I would use MMG without it. Would you be willing to accept a patch? I’ll probably use Jack’s method of opening the class in both files, instead of using a base class, as it requires the least changes to existing model code.

  11. Dr Nic says:

    @craig – yep a patch is fine, assuming its a parameter or something to select which of the two generate methods are activated.

  12. [...] Metaprogramming – Fresh off his RejectConf performance in Portland, Dr. Nic discussed some great metaprogramming techniques in Ruby and demo’ed his Magic Model Generator plugin as well as some controversial uses of metaprogramming. [...]

  13. 5cents says:

    Hi Nic,

    I have a suggestion for a future version: It would be great, when such things like the class name or the foreign key are only explicitly defined in the model, if they diverge from the rails conventions.

    This would reduce the code in the models and it would help us to see what actually differs from the defaults. It’s not really crucial, but it would make the code clearer. I think people would really like that.

    Anyway, great job!

  14. Dr Nic says:

    @5cents – I haven’t used the generator for a while but I thought it did do that. If you’re using it and its not doing this, email me some samples + some sql and we’ll have a look-see.