Tbpgr Blog

Employee Experience Engineer tbpgr(てぃーびー) のブログ

Ruby | Sublime Text2向けのSnippetを生成するDSL

概要

Sublime Text2向けのSnippetを生成するDSL

詳細

Sublime Text2向けのSnippetを生成するDSLです。

仕様

DSL向けを想定しているため単純なメソッド名+引数名(複数)で構成されるクラスマクロのみを対象とします。
・ブロックは未対応。
DSL定義部、DSL利用部、DSL呼出部、Snippetテンプレートの4ファイルから構成され、全て同一フォルダに配置する
・出力結果はカレント配下にsnippetsフォルダを作成し、その配下にメソッド名+「.」+sublme-snippetで出力する。
DSL利用部は下記のようにメソッド名、引き数名をシンボルで指定する。引数は順序通りにタブ順が決まる。
DSL呼出部はDSL利用部のテンプレート生成機能を持つ。initを指定して呼び出すこと。
DSL呼出部をコンソールから引数無しで呼び出しするとSnippetを生成する。
・作成が成功したらSublime Text2のPackages\Userフォルダ配下にコピーすれば利用可能です。
RubyDSL向けに作ったのでテンプレートのscopeは「source.ruby」固定にしてあります。

DSL記述イメージ

add :method_name, :args_name1, :args_name1・・・
add :method_name

ソース

DSL定義部ソース

snippetter.rb

# encoding: utf-8
require "pp"
require "erb"

class TargetMethod
  attr_accessor :method_name, :args

  def initialize(&block)
    instance_eval do
      block.call(self)
    end
  end
end

module Kernel
  attr_accessor :_methods
  SNIPPETS_OUTPUT = "./snippets"

  def add(method_name, *args)
    return if method_name.nil?
    return if method_name.empty?
    return if args.each.include?(nil)
    return if args.each.include?("")

    @_methods ||= []
    @_methods << TargetMethod.new do |t|
      t.method_name = method_name
      t.args = args
    end
  end

  def generate
    base_template = File.open("template", "r") {|f|f.read}
    @_methods.each do |m|
      template = base_template
      output(m.method_name, get_snippet(template, m.method_name, get_args_names(m)))
    end if @_methods
  end

  private
  def get_args_names(_method)
    args = _method.args
    args_names = " "
    args.each_with_index do |a, i|
       args_names << "${#{i + 1}:#{a}}, "
    end
    args_names.chop!.chop! unless args.empty?
  end

  def get_snippet(template, method_name, args_names)
    erb = ERB.new(template)
    snippet = erb.result(binding)
    snippet
  end

  def output(method_name, snippet)
    Dir::mkdir(SNIPPETS_OUTPUT) unless FileTest.exist?(SNIPPETS_OUTPUT)
    file_name = "#{SNIPPETS_OUTPUT}/#{method_name}.sublime-snippet"
    File::open(file_name, "w") do |f|
      f.puts snippet
    end
    puts "create for #{method_name} method snippet => #{file_name}"
  end
end
DSL利用部設定サンプル

Snippet.rb

# encoding: utf-8
require_relative "./snippetter"

add :click, :action, :model, :tag
add :data, :key, :value
add :submit
DSL呼出部ソース

se.rb

# encoding: utf-8
if $*.size == 1 && $*[0] == "init"
  File::open("./Snippet.rb", "w") do |f|
    f.puts <<-EOS
    # encoding: utf-8
    require_relative "./snippetter"

    # add :method1, :args1, :args2, :args3
    # add :method2
    EOS
  end
  puts "create Snippet.rb template file"
  exit
end

unless FileTest.exist?("./Snippet.rb")
  puts "please create Snippet.rb"
  puts "you can use 'ruby se.rb init'. then you get Snippet.rb template."
  exit
end

require_relative "./snippetter"
require_relative "./Snippet"
generate
template
<snippet>
  <content><![CDATA[
<%= method_name %><%= args_names %>
]]></content>
  <tabTrigger><%= method_name %></tabTrigger>
  <scope>source.ruby</scope>
  <description><%= method_name %> method</description>
</snippet>

サンプル

実行コマンド
$ruby se.rb
create for click method snippet => ./snippets/click.sublime-snippet
create for data method snippet => ./snippets/data.sublime-snippet
create for submit method snippet => ./snippets/submit.sublime-snippet
出力例
<snippet>
  <content><![CDATA[
click ${1:action}, ${2:model}, ${3:tag}
]]></content>
  <tabTrigger>click</tabTrigger>
  <scope>source.ruby</scope>
  <description>click method</description>
</snippet>
出力も含めたファイル構成
│  se.rb
│  Snippet.rb
│  snippetter.rb
│  template
│
└─snippets
       click.sublime-snippet
       data.sublime-snippet
       submit.sublime-snippet
利用画面