<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Talk Like A Duck: Tag textmate</title>
    <link>http://talklikeaduck.denhaven2.com/articles/tag/textmate</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>In Ruby, it's not the dog, it's the tricks!</description>
    <item>
      <title>My First Serious TextMate Automation</title>
      <description>&lt;div style='width:240; float:left; text-align:right; font-size:xx-small; border-width:1px; border-color:#444444; border-style:solid;margin-bottom:30px; margin-right:30px;' class='tease-image'&gt;
&lt;img width='240' height='144' alt='A permanent fixture.' src='http://farm1.static.flickr.com/21/34024841_8e548f4569_m.jpg'&gt;
&lt;br/&gt;
&lt;a href='http://flickr.com/photos/henryfaber/34024841/'&gt;A permanent fixture.&lt;/a&gt;
&lt;br/&gt;&amp;copy;
&lt;a href='http://flickr.com/people/henryfaber'&gt;Henry Faber&lt;/a&gt;
&lt;br/&gt;&lt;a href='http://creativecommons.org/licenses/by-nc/2.0/'&gt;&lt;img src='http://i.creativecommons.org/l/by-nc/2.0/80x15.png' title='used under a Creative Commons Attribution-NonCommercial License' width='80' height='15' border='0'/&gt;&lt;/a&gt;
&lt;/div&gt;

I recently got assimilated by the Mac/TextMate borg.  I'm slowly teaching my fingers to dance the TextMate tango and unlearning old vim habits.
&lt;p&gt;One resource has been James Edward Gray II's book on TextMate published by the Pragmatic Programmers. I finally sat down and got serious about writing some automations of my own.&lt;/p&gt;
&lt;p&gt;I've got to say that I'm pretty impressed by TextMate.  At the last Raleigh.rb hack night I was talking to another vim user.  I'd mentioned to him that you can extend TextMate easily in Ruby, without really having experienced it. Today, I wrote a neat little TextMate command to help in building Rails database test fixtures.&lt;/p&gt;
&lt;p&gt;It acts like a tab triggered snippet, but it's a smart little critter.  If I'm editing a fixture file, say 'test/fixtures/users.yml', I can type item then tab and it will produce the skeleton yaml for a new record, with:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;A dummy name selected for overtyping.&lt;/li&gt;
  &lt;li&gt;The id set to the next available primary key&lt;/li&gt;
  &lt;/li&gt;Each column name as a yaml key ...&lt;/li&gt;
  &lt;li&gt;... A tabstop on a value which shows the column type&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example:&lt;/p&gt;



&lt;p&gt;Suppose I've got a fixture file for a model called Item:&lt;/p&gt;
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;&lt;notextile&gt;	# == Schema Information
	# Schema version: 14
	#
	# Table name: items
	#
	#  id          :integer(11)   not null, primary key
	#  description :string(255)   
	#  name        :string(255)   
	#  price       :integer(11)   
	#

	# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
	lotr1:
	  id: 1
	  name: LOTR1
	  description: The Road Goes Ever On
	lotr2:
	  id: 2
	  name: LOTR2
	  description: Introduction to Elvish&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If I type a new line at the end of this file with the snippet command item and hit tab I see this:&lt;/p&gt;                                                    
&lt;br/&gt;lotr2:
&lt;br/&gt;&amp;nbsp;&amp;nbsp;id: 2
&lt;br/&gt;&amp;nbsp;&amp;nbsp;name: LOTR2
&lt;br/&gt;&amp;nbsp;&amp;nbsp; description: Introduction to Elvish
&lt;br/&gt;
&lt;br/&gt;&amp;nbsp;&amp;nbsp;&lt;b&gt;NameMe&lt;/b&gt;:
&lt;br/&gt;&amp;nbsp;&amp;nbsp;  id: 3
&lt;br/&gt;&amp;nbsp;&amp;nbsp;  description: string
&lt;br/&gt;&amp;nbsp;&amp;nbsp;  name: string
&lt;br/&gt;&amp;nbsp;&amp;nbsp;  price: integer    
&lt;p&gt;With &lt;b&gt;NameMe&lt;/b&gt; selected so that I can easily replace it by typing. Note that the id was set to 3 since rows with ids 1 and 2 already exist.&lt;/p&gt;
&lt;p&gt;Hitting tab again selects the first instance of string, and subsequent tabs step through the columns&lt;/p&gt;
&lt;p&gt;Another feature which I won't show is that it looks at the table and if it doesn't have a primary key (e.g. for an association table in a has_and_belongs_to_many association), it won't generate the id: or a key.&lt;/p&gt;
&lt;p&gt;You might be impressed, but I'm pretty happy with the results of a couple of hours of playing with TextMata and Ruby.&lt;/p&gt;
&lt;h2&gt;How it works.&lt;/h2&gt;
&lt;p&gt;The whole thing is done with a TextMate command implemented in Ruby.  A few notes.  First, it's just a coincidence that the snippet trigger is item and my example table is called items.  Second, I'm not using the comments at the top of the fixture file which were generated when the model was created.  The command works off of db/schema.db.&lt;/p&gt;
&lt;p&gt;Here's the code:&lt;/p&gt;
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;&lt;notextile&gt;	#!/usr/bin/env ruby -w  
	#
	#  Created by Rick DeNatale on 2007-10-09.
	#  Copyright (c) 2007. You may use this under the ruby license.                    

	$LOAD_PATH &amp;lt;&amp;lt; &amp;quot;#{ENV['TM_SUPPORT_PATH']}/lib&amp;quot;
	require 'exit_codes.rb'


	class FixtureSnipGen
	  attr_reader :rails_root, :table_name
	  def initialize(fixture_file)
	    match  = %r{(.*)test/fixtures/(.*).yml}.match(ENV[&amp;quot;TM_FILEPATH&amp;quot;])
	    @rails_root = match[1]
	    @table_name = match[2]
	    @no_id = false  
	  end

	  def next_id
	    max_id = 0     
	    while line = gets
	      claimed = %r{\s+id:\s+(\d+)}.match(line) 
	      max_id = [max_id, claimed[1].to_i].max if claimed
	    end
	    max_id + 1
	  end

	  def gen_snippet
	    cols = gen_columns
	    if cols
	      puts &amp;quot;${1:NameMe}:&amp;quot;
	      puts &amp;quot;  id: #{next_id}&amp;quot; unless @no_id
	      puts cols.join(&amp;quot;\n&amp;quot;)
	    else 
	      puts &amp;quot;Couldn't find #{table_name} in schema.rb&amp;quot;
	      TextMate.exit_show_tool_tip
	    end
	    puts &amp;quot;$0&amp;quot;
	  end

	  def schema_file_name  
	    &amp;quot;#{rails_root}db/schema.rb&amp;quot;
	  end 

	  def value_tab(text)
	    @tab_stop += 1
	    (@tab_stop &amp;lt; 10) ? &amp;quot;${#{@tab_stop}:#{text}}&amp;quot; : &amp;quot; # #{text}&amp;quot;
	  end


	  def gen_columns
	    #    raise Exception.new(&amp;quot;No schema file&amp;quot;) unless File.exist?(schema_file_name)
	    started = false
	    @tab_stop = @no_id ? 0 : 1
	    result = []
	    File.new(schema_file_name).each do | line |
	      if started
	        break if %r{^(\s*)create_table\s+}.match(line)
	        col_match = %r{\.column\s+\&amp;quot;(.*)\&amp;quot;\s*,\s*:(\w+)}.match(line)
	        result &amp;lt;&amp;lt; &amp;quot;  #{col_match[1]}: #{value_tab(col_match[2])}&amp;quot; if col_match
	      else
	        started = %r{^(\s*)create_table\s+[&amp;quot;']#{table_name}['&amp;quot;]}.match(line)
	        @no_id = %r{:id\s+=&amp;gt;\s+false}.match(line) if started
	      end
	    end
	    return result.empty? ? nil : result    
	  end
	end

	begin
	  FixtureSnipGen.new(ENV[&amp;quot;TM_FILEPATH&amp;quot;]).gen_snippet
	rescue Exception =&amp;gt; ex
	  puts ex
	  TextMate.exit_show_tool_tip
	end&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the TextMate bundle editor, I just added a new command with this code, and set the following options:&lt;/p&gt;
&lt;dl&gt;
	&lt;dt&gt;Save:&lt;/dt&gt;&lt;dd&gt;Nothing&lt;/dd&gt;
	&lt;dt&gt;Input:&lt;/dt&gt;&lt;dd&gt;Entire Document - which pipes the entire textmate buffer for the file into stdin.&lt;/dd&gt;
	&lt;dt&gt;Output:&lt;/dt&gt;&lt;dd&gt;Insert as Snippet - which triggers TextMate to interpret the output as a snippet once the script has finished.&lt;/dd&gt;
	&lt;dt&gt;Activation:&lt;/dt&gt;&lt;dd&gt;Tab Trigger = item&lt;/dd&gt;
	&lt;dt&gt;Scope Selector&lt;/dt&gt;source.yaml - this only works within yml files.&lt;/&gt;
&lt;/dl&gt;         
&lt;p&gt;Finally note that although my example uses a table called items, it's just a coincidence that the tab trigger is item. It would be item in any fixture file.&lt;/p&gt;</description>
      <pubDate>Tue, 09 Oct 2007 08:15:00 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:ad8ffd07-f3e1-4083-abee-ec5301b9c1ba</guid>
      <author>Rick DeNatale</author>
      <link>http://talklikeaduck.denhaven2.com/articles/2007/10/09/my-first-serious-textmate-automation</link>
      <category>rails</category>
      <category>textmate</category>
      <category>automation</category>
      <category>testing</category>
      <category>fixtures</category>
      <trackback:ping>http://talklikeaduck.denhaven2.com/articles/trackback/469</trackback:ping>
    </item>
  </channel>
</rss>
