Rails Generators that generate generators

Generators?


rails new foo
  

Why might you write your own?

You wrote a gem


$> rails g
Usage: rails generate GENERATOR [args] [options]

Please choose a generator below.

Kaminari:
  kaminari:config
  kaminari:views

Mongoid:
  # ...

RailsAdmin:
  rails_admin:install
  rails_admin:uninstall
  

Why might you write your own?

You wrote a gem

A conversion

The mission

RoflDB -> pg

RoflDB is... a document database!


$> rails g
Usage: rails generate GENERATOR [args] [options]

General options:
  -h, [--help]     # Print generator's options and usage
  -p, [--pretend]  # Run but do not make any changes
  ...

Please choose a generator below.

Rails:
  assets
  controller
  generator <------------------------------ WAT?!?
  ...
  

$> rails g generator converter
      create  lib/generators/converter
      create  lib/generators/converter/converter_generator.rb
      create  lib/generators/converter/USAGE
      create  lib/generators/converter/templates
  

cat  lib/generators/converter/converter_generator.rb
  

class ConverterGenerator < Rails::Generators::NamedBase
  source_root File.expand_path('../templates', __FILE__)
end
  

require 'active_record'

class ConverterGenerator < Rails::Generators::NamedBase
  include Rails::Generators::Migration
  source_root File.expand_path('../templates', __FILE__)

  attr_accessor :attributes

  def initialize(args, *options)
    self.attributes = args[1..-1]
    super
  end

end
  

class ConverterGenerator < Rails::Generators::NamedBase
  # ...

  def copy_initializer_file
    template_locals = {
      class_name: class_name.split('::').join,
      table_name: table_name,
      attributes: attributes
    }
    number = next_migration_number
    template  "migration.rb",
              "db/migrate/#{number}_#{file_name}.rb",
              template_locals
  end
end
          

class ConverterGenerator < Rails::Generators::NamedBase
  # ...

  # required interface for Rails::Generators::Migration.
  def self.next_migration_number(dirname) #:nodoc:
    next_num = current_migration_number(dirname) + 1
    ::ActiveRecord::Migration.next_migration_number(next_num)
  end

  private
  def next_migration_number
    # ...
  end
          

class DatabaseMigrator
  TYPE_MAP = {
    # ...
  }

  def build_migration
    args = mapped_attributes.unshift(model_name)
    Rails::Generators.invoke('migration_writer', args, config)
  end

  private
  def mapped_attributes
    # ...
  end
end
  

cat  lib/generators/converter/templates/migrate.rb
  

class Create<%= config[:class_name] %> < ActiveRecord::Migration
  def change
    create_table :<%= config[:table_name] %> do |t|
      t.string :dbx_id
 <% config[:attributes].each do |attr| -%>
 <%= "t.#{attr.type} :#{attr.name}" %>
 <% if attr.name[/_id$/] %>
 <%= "t.string :dbx_#{attr.name}" %>
 <% end %>
 <% end %>

      t.timestamps
    end
  end
end
  

cat  lib/generators/converter/templates/migrate_sequel.rb
  

Sequel.migration do
  change do
    create_table :<%= config[:table_name] %> do
      primary_key :id
      String :dbx_id
   <% config[:attributes].each do |attr| -%>
   <%= "#{attr.type} :#{attr.name}" -%>
   <% if attr.name[/_id$/] %>
      <%= "String :dbx_#{attr.name}, index: true" -%>
   <% end %>
   <% end %>
    end
  end
end
  

What I want


rake db:migrate_schema
  

require 'rails/generators'

namespace :db do
  desc "Migrate RoflDB to pg"
  task migrate_schema: :environment do
    # explicitly load models to avoid lazy loading in development
    Dir[Rails.root.join("app/models/**/*.rb")].each do |model|
      require model
    end

    RoflDB::Base.subclasses.each do |model|
      DatabaseMigrator.migrate(model.schema_attrs, model.to_s)
    end
  end
end
  

rake db:migrate_schema
  

class CreateWidget < ActiveRecord::Migration
  def change
    create_table :widgets do |t|
      t.string :dbx_id
      t.string :name
      t.text   :description
      t.string :dbx_foo_id

      t.timestamps
    end
  end
end
  

Takeaway...

Repeatability is good!

Resources

Rails Guides

Thanks!

Jesse Cooke

github/jc00ke

@jc00ke

jesse [at] jc00ke.com

Reveal.js

HTML Presentations Made Easy

Created by Hakim El Hattab / @hakimel

Themes

Reveal.js comes with a few themes built in:
Solarized

* Theme demos are loaded after the presentation which leads to flicker. In production you should load your theme in the <head> using a <link>.

Pretty Code


function linkify( selector ) {
  if( supports3DTransforms ) {

    var nodes = document.querySelectorAll( selector );

    for( var i = 0, len = nodes.length; i < len; i++ ) {
      var node = nodes[i];

      if( !node.className ) {
        node.className += ' roll';
      }
    }
  }
}
					

Courtesy of highlight.js.