Sublime TextとRailsを連携させて開発を楽にする

  • 5
    いいね
  • 0
    コメント

Happy Elements株式会社 カカリアスタジオ Advent Calendar 2016の9日目の記事です。
担当は @ryooo です。よろしくお願いします。

要旨

Sublime Textのプラグインからrailsの開発用APIを叩くことで快適な開発環境ができる。

どういうことができるか

○○に応じた××を調べられる

ID1〜3のアイテム名を調べる

g1.gif

スキルID38, 39それぞれのモンスターIDを調べ、その後スキルID38のモンスター名を調べる

g2.gif

開発中にRubyのコードを動かす

g3_2.gif

説明

Sublime側のコード(Python)

概要

GetDictionaryCommand

マルチカーソルで選択されている行の末尾に、サーバーから受け取った辞書をもとに値を追記する実装です。

ExecRailsCommand

マルチカーソルで選択されているrubyコードの末尾に、サーバーから受け取った実行結果を追記する実装です。

実装(紹介用の例)

import sublime
import sublime_plugin
import json
import urllib.request
import urllib.parse

# 設定例(Setting User)
# "App.host" : "localhost",

# binding例
# { "keys": ["ctrl+m"], "command": "get_dictionary" },
# { "keys": ["ctrl+e"], "command": "exec_rails" },

class GetDictionaryCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.view.window().show_input_panel('Dictionary Type', '', lambda s: self.view.window().run_command("get_dictionary_and_replace",{"arg":s}), None, None)

class GetDictionaryAndReplace(sublime_plugin.TextCommand):
    def run(self, edit, arg):
        # 接続先サーバーをステータスバーに出力
        settings = sublime.load_settings('Preferences.sublime-settings')
        host = settings.get('App.host')
        if not host:
            host = "localhost"
        sublime.status_message("host is " + host + ".")

        with urllib.request.urlopen("https://" + host + "/辞書データをreturnするAPI/?type=" + urllib.parse.quote(arg)) as res:
            body = res.read().decode("utf-8")
            jsonData = json.loads(body)
            for selection in self.view.sel():
                if self.view.substr(selection) in jsonData:
                    row = self.view.line(selection)
                    self.view.replace(edit, row, self.view.substr(row) + " # " + jsonData[self.view.substr(selection)])
            self.view.sel().clear()

class ExecRailsCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        host = "localhost"
        sublime.status_message("host is " + host + ".")
        for selection in self.view.sel():
            code = self.view.substr(selection)
            try:
                with urllib.request.urlopen("https://" + host + "/コードを実行して結果を返すAPI/?q=" + urllib.parse.quote(code)) as res:
                    body = res.read().decode("utf-8")
                    row = self.view.line(selection)
                    self.view.replace(edit, row, self.view.substr(row) + " # " + body)
            except urllib.error.HTTPError as e:
                row = self.view.line(selection)
                self.view.replace(edit, row, self.view.substr(row) + " # err " + e.reason)
        self.view.sel().clear()

Rails側のコード

概要

typeに応じた辞書jsonを返すAPIです。
リターンした辞書を元に、Sublime側でコメントを追記するので、このAPIの実装次第でやりたいようにカスタムできます。

実装(紹介用の例)

class XXXController < ActionController::Base
  def dictionary
    ret = case params[:type]
    when 'item'
      # アイテムID => アイテム名
      Hash[*::Item.all_with_cache.map{|r|[
        r.id,
        r.name,
      ]}.flatten]
    when 'monster'
      # モンスターID => モンスター名
      Hash[*::Monster.all_with_cache.map{|r|[
        r.id,
        r.name,
      ]}.flatten]
    when 'monster skill_id'
      # モンスタースキルID => モンスターID(カンマ区切り)
      val = Hash.new {|h, k| h[k] = []}
      ::Monster.all_with_cache.each do |m|
        val[m.skill_id] << m.id
      end
      Hash[*val.map{|k, v|[k, "#{v.join(',')}"]}.flatten]
    else
      raise 'unknown type'
    end

    return render :json => ret
  end
end

コードを実行して結果を返すAPI

実装は割愛しますが、受け取ったコードを実行して結果をreturnするようなコードなので、認証された環境や外部からアクセスできない環境でしか絶対に受け付けないこと、また必ず検証を行うことが前提となります。

おまけ

gifの中で使っている連番入力を簡単にするプラグインの紹介

※ 昔ここで投稿したものの簡易版です。

概要

マルチカーソルの先頭の数字を初期値として、連番に変換してくれるプラグインです。
ni.gif

実装(紹介用の例)

# binding例
# { "keys": ["ctrl+n"], "command": "increment_numbers" },

class IncrementNumbersCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        start_value = int(self.view.substr(self.view.sel()[0]))
        counter = 0
        for selection in self.view.sel():
            self.view.insert(edit, selection.begin(), str(start_value + counter))
            counter = counter + 1
        for selection in self.view.sel():
            self.view.erase(edit, selection)

最後に

Happy Elements株式会社 カカリアスタジオでは、一緒にものづくりをする仲間を募集しています!

ものづくりが大好きな方は是非お気軽にご応募下さい。

カカリアスタジオ 採用情報

ひとりひとりのチカラを最大限に引き出せるこのうえない環境と体制、文化があればこそ、最高のものづくりをつながると私たちは信じています。カカリアスタジオのメンバーとして、私たちと一緒に働きたいと思う仲間を募集しています。