vimプラグインを作ってみた
はじめてvimプラグインというものを作ってみた。コメントを折りたためるようにするだけ。zm
, zr
などで表示/非表示を切り替えられる。NeoBundleでインストールすればそのまま使える。
金曜日にmrubyの勉強会にいって、build_config.rbの大量のコメント(デフォルト値のコメントアウト)を見てウッとなって、コメントだけ非表示にできないか調べてみたら意外に簡単にできた & 手頃なプラグインがなかったので作ってみた。
コメントって読む人が初心者の場合はとても助かるけど、分かってる人にとっては邪魔なだけだから、コメントを読む人が表示/非表示を切り替えられた方がいいと思った。分かってる人には邪魔だろうなと思ってコメント書かないと初心者は困ってしまうので、読む側がコメントを見るかどうかを判断すればいいと思う。そしたら、コメント書く側は初心者のことを考えてコメントを書くようになるんじゃないかと思う。
参考
vimの便利機能
入力補完
- インサートモードで
<C-n>
または<C-p>
と打つと、補完候補が出てきます。
バッファ
- 新しいファイルを開くと、バッファという領域にその中身が読み込まれます。過去に開いたファイルをまた開くときに便利です。
- 直前に開いたファイルに戻りたい場合によく使います。
- 個人的には、前後のバッファに移動したりバッファから履歴を削除するために以下のようなマッピングを設定しています。
nnoremap <Tab> :bnext<CR> nnoremap <S-Tab> :bprevious<CR> nnoremap <Leader>d :bdelete<CR>
- これでTabやShift+Tabで前後のバッファに移動できます。
タブ
- vimにもブラウザのようなタブがあります。同時に多くのファイルを開きたいときによく使います。
- デスクトップPCであまり画面が大きくない場合、分割して複数のファイルを開くよりタブの方が出番が多いような気がします。
- 個人的には、前後のタブに移動したり新しいタブを開くために以下のようなマッピングを設定しています。
nnoremap <Leader>t :tabnew<CR> nnoremap <Leader>n :tabnext<CR> nnoremap <Leader>p :tabprev<CR>
コマンド定義
- 文字コードを変換したりインデント量を変更する操作はけっこうやるので、自分でコマンドを定義して一発で操作できるようにするとラクですね。
command! Indent2 :setlocal tabstop=2 shiftwidth=2 command! Indent4 :setlocal tabstop=4 shiftwidth=4 command! ToSjis :e ++enc=sjis<CR>
- これで普通に
:Intent2
と打てばインデント量が2になります。 - 基本は
command! <Command name> <command>
です。コマンド名は大文字から始めなくちゃいけないようです。あとはいろいろオプションがあるので、詳しくは:help command-nargs
を見てください。
abbreviate
- abbreviateは長くてめんどくさい表記に略を設定できる機能です。
abbreviate #i #include
- 例えば上のように設定すると、"#i[space]"と入力すると勝手に"#import[space]"と変換してくれます。あとはコメントブロックを入力するのによく使われるみたいです。
abbreviate #b /****************************
- abbreviateは応用としてtypoを修正するのにも便利です。"abbreviate"っていう単語がもうtypoしそうですね。あと、個人的に"receive"を"recieve"と書いてしまうことが多いので以下のように設定します。
" abbreviate <誤> <正> abbreviate abbriviate abbreviate abbreviate recieve receive
" .vim/abbreviate.vim abbreviate abbriviate abbreviate abbreviate recieve receive " .vimrc source ~/.vim/abbreviate.vim
filetype毎の設定ファイル
- 言語によってインデント量を変えたいってケースはほとんどのvimmerにあると思うんですが、そういうときに僕はfiletype毎の設定ファイルを用意しています。
- インデント量だけなら
autocmd
を使うのもアリだと思うのですが、上のabbreviateで設定したコメントブロックのように言語によって細かく設定を変えたいケースが地味にあるので、設定ファイルを用意する方法を採っています。
.vim |- ftdetect |- filetype.vim |- ftplugin |- javascript.vim |- make.vim |- ruby.vim .vimrc
- 上のようなディレクトリ構造にしておくと、各filetypeごとに設定ファイルが読み込まれるようになります。詳しくは
:help filetype-plugin
らへんを見てください。 - 例えばMakefileを書く場合、インデントはspaceではなくtabしか使えないので、expandtabを無効にしたいところです。そこで、以下のようなファイルを用意します。
" .vim/ftplugin/make.vim setlocal noexpandtab setlocal tabstop=8 setlocal shiftwidth=8
filetypeの指定
- Gemfileなど拡張子では判別できないファイルのfiletypeを指定したい場合、ftdetectが便利です。
.vim |- ftdetect |- filetype.vim .vimrc
" .vim/ftdetect/filetype.vim autocmd BufRead,BufNewFile Gemfile setfiletype ruby autocmd BufRead,BufNewFile Guardfile setfiletype ruby autocmd BufRead,BufNewFile *.rabl setfiletype ruby autocmd BufRead,BufNewFile *.jbuilder setfiletype ruby autocmd BufRead,BufNewFile *.ru setfiletype ruby
- 上のように設定ファイルを用意すると、指定したファイルを自動的にrubyをfiletypeとして開いてくれます。
以上の設定はすべて僕のdotfilesに書いてあるので参考にしてみてください。
読み返してみると、その筋の方に怒られそうな気がしてきた…(´・ω・`)
退職のお知らせ
3月末をもって、アルバイトの頃から約2年ほど勤めていた会社を退職することになりました。アルバイトの頃はRailsでサーバーサイドの開発を担当し、大学卒業後は正社員としてAndroidアプリとiOSアプリの開発を担当しました。未経験の自分にアプリ開発を任せていただけて、いろいろな面で勉強させていただきました。
4月からは、これまでとは違うフィールドで働く予定です。偶然に偶然が重なって(このブログもきっかけの一つ)、以前から興味のあった領域に携われることになり、この度転職を決意しました。これまた未経験の領域に飛び込むことになるので、これから必死に勉強する必要がありそうです。ですが、この約2年間でずいぶんと未知の領域にチャレンジしてきたので、その経験と身につけた自信でこれからも頑張っていけそうです。
これまでお世話になった皆様、有難うございました。今後とも宜しくお願い致します。
人間の脳みその限界とツールについて
複数の設定があり、それらの組み合わせによって挙動が変わるアプリを書いていると、だんだんそれぞれの組み合わせについて頭がこんがらがってくる。コードを修正するたびにそれぞれの組み合わせについて、どのように影響が出るか考えなければならなくて、つらくなってくる。つらいだけでなく、確実にミスが生まれる。
こういう状況が続くと、人間の脳みその処理能力について限界を感じてくる。人間はこういうことに向いていないと思うようになる。人力によるチェックにも自信が持てなくなる(そして、それでいいのだと思うようになる)。人間の脳みそに限界を感じるようになると、ツールに頼りたくなってくる。限界を感じることで自分自身に対して謙虚になり、ツールの使い所を実感するんだと思う。
例えば、脳内で多数の組み合わせをシミュレーションするのをやめてプログラムにテストさせるとか、手でひとつひとつ丁寧にメソッド名を置換するのをやめて、Eclipseに任せるとか。何度も同じコマンドを手打ちするのをやめてシェルの補完を使うとか。あと、publicなメソッド・プロパティを必要最小限にすれば、そのクラスを使う人は考えることが減るんだから、public/privateという概念も脳みその負荷を減らす工夫なのかもしれない。
とにかく、脳に強いストレスを感じたら、たぶんその作業は人間には向いていないと思うので、ツールに任せられないか一回検討した方がいいと思う。もっと自分の脳みその能力を疑った方がいいと思う。
Arduinoと感圧センサーで圧力をサーバーに送信する
秋葉原の秋月電子に行って、慣れない雰囲気の中、なんとかこの感圧センサーをゲットしました。
オライリーのPrototyping Labという本を見ながら回路を接続して、上のようなプログラムを実行するとこんな感じになりました。
センサーに加わった圧力を0番のアナログピンから受け取り、シリアル通信でそのままモニタに出力しています。
この圧力をサーバーに送信するために、以下のものを買いました。
イーサネットシールド for Arduino (micro SD, Wiznet W5100)
- 出版社/メーカー: OEM
- メディア: エレクトロニクス
- この商品を含むブログを見る
ArduinoにぶっさすEthernetシールド。ここにLANケーブルをさしてインターネットに接続できます。
PLANEX 300Mbps 超小型ハイパワー無線LANマルチファンクションルータ/アクセスポイント/コンバータ MZK-MF300N
- 出版社/メーカー: プラネックス
- 発売日: 2010/07/16
- メディア: Personal Computers
- 購入: 11人 クリック: 216回
- この商品を含むブログ (14件) を見る
うちがE-mobileのpocket wifiを使っててLANケーブルをさすところがなかったので、有線を無線で使えるようにするコンバータも買いました。
PLANEX 無線LANルータ/アクセスポイント/コンバータ「MZK-MF300N」「FFP-PKR01」専用 USB給電ケーブル SSOP-USB02
- 出版社/メーカー: プラネックス
- 発売日: 2010/09/10
- メディア: Personal Computers
- 購入: 8人 クリック: 40回
- この商品を含むブログ (5件) を見る
USBから給電するためのケーブル。
これらをつないでHerokuのサーバーにPOSTリクエストを送ると、node.jsがwebsocketを使ってグラフを更新するようにしました。
arduinoからリクエストを受けてwebsocketでグラフを更新 vine.co/v/bJ0j6W3gvz1
— なおてぃー (@naoty_k) January 30, 2013
コードはこんなかんじ。
上2つのコードを組み合わせると、圧力をPOSTでサーバーに送信できます。で、node.jsで書いたサーバーに送ると以下のような感じになりました。
— なおてぃーさん (@naoty_k) 2013年2月6日
arduinoのコードとサーバーのコードはともにgithubで公開しています。
ハッカソンでgithub連携のnode.jsアプリ作った話
疲れたので手短に。
土日2日間ぶっ通しのハッカソンでnode.jsを使ったgithub連携アプリを作った。仕事はRailsで、まともなアプリをnode.jsで書いたことなかったし、せっかくだからnode.js使ってみた(っていうか、勝ちに行っても勝つ見込みないから、楽しむことに専念した)。
ソースコードはこちら。
https://github.com/naoty/arounds
Express 3.x, MongoDBでHerokuにデプロイしてます。
github認証
認証ライブラリはいろいろあるようだけど、Passportを使ってみた。github認証したい場合はpassport-githubというものがあるので、それを併用する。使い方は載ってるので割愛。
まだnode.jsでのセッションの取り扱いとかちゃんと理解してないから、passport.serializeUser
らへんがよくわかってない。
mongoose, MongoLAB
MongoDBのORMとしてmongooseを使った。Heroku上のMongoDBにはMongoLABを使った。ブラウザからコレクションの中身とか見れるのでよかった。
$ heroku addons:add mongolab:starter
環境ごとの設定
github APIのclient IDやDBのホストのために環境ごとに設定ファイルを用意した。
// app.js require config = process.env.NODE_ENV == 'production' ? require('./config/production') : require('./config/development');
// configs/production.js module.exports = { github: { clientID: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET, callbackURL: process.env.GITHUB_CALLBACK_URL }, mongodb: { path: process.env.MONGODB_URI || process.env.MONGOLAB_URI } };
本番環境ではAPIキーをHerokuの環境変数を経由して参照する。
// configs/development.js module.exports = { github: { clientID: 'GITHUB CLIENT ID', clientSecret: 'GITHUB CLIENT SECRET', callbackURL: 'http://127.0.0.1:3000/auth/github/callback' }, mongodb: { path: 'mongodb://localhost/arounds' } };
ソースコードを公開する場合は、APIキーを隠すためにconfig/development.jsを.gitignoreに追加しておく。
まだよくわかってないこと
とりあえずnode.jsでアプリを作ってみてわかんなかったところをメモ。
MVCな書き方
Expressは放っておくと、ルーティングやルーティングに対するアクション、モデルの定義などいろんなものをapp.jsに書くことができてしまう。viewは分かれてるけど。Rubyで言うと、RailsよりはSinatraが近い。簡単なアプリケーションなら1ファイルにまとめてしまった方がラクかもしれないけど、すぐにMVCが崩壊してしまう。
また、socket.ioを使ったコードを書くとき、view側のjavascriptにも複雑なロジックを書くことになる。
コールバック地獄
上のにも関連するけど、あっという間にコールバック内にコールバックを書いて、その中にコールバックを書くケースが出てくる。deferというものを教えてもらったので、それ使ってみたい。
ミドルウェア
app.use()
みたいなのがたくさんあるけど、あれらが何をやってるのかまだよくわかってない。express new
すると勝手にできてしまうから、あんまり意味を考えなくても動く。express new
に頼らずに書いて覚える。
Railsに組み込むgemを作るためのTips
params_inquirerというgemを作りました。何ができるかと言うと、文で説明するのがなかなか難しいので、下のコードを見てください。
# users_controller.rb def index if params[:status].accepted? # params[:status] == 'accepted' と同じ @users = User.accepted elsif params[:status].rejected? # params[:status] == 'rejected' と同じ @users = User.rejected else @users = User.all end end
params_inquirerを使うと上のaccepted?
のようなメソッドがparams
に対して呼べるようになります。すでにrubygemsで公開してるので、ちょっと試してみたい場合は、irbで試してもらうこともできます。
$ gem install params_inquirer $ irb irb > require 'params_inquirer' irb > params = ParamsInquirer::Parameters.new({ name: 'naoty' }) irb > params[:name].naoty? => true
params
の中身を文字列で比較するのがなんとなくダサいと感じていたので、作ってみました。あとは、Railsの中身について勉強してみたかったというのもあります。
Railsに組み込みgemを作るにあたって知っておいた方がいいポイントについてまとめてみます。
Bundlerでgemのひな形を作る
gemを作るとき、まず最初にBundlerを使ってgemのひな形を作ります。
$ gem install bundler $ bundle gem params_inquirer
これでgemのひな形ができます。作ったgemをローカル環境にインストールしたりrubygems.orgにリリースするためのRaketaskもここに含まれるので、かなり便利です。
Bundlerを使ったgemの開発についてはこの記事を参考にしました。
Railtie
Railtieは、Railsを起動するときにgemのコードをActionController::Base
にinclude
させるために使いました。これによって、自分のgemをRailsアプリケーションに組み込むことができます。
下のコードでは、Railsプロセスが起動するときにinitializer
ブロック内の処理が実行されて、自分で作ったParamsInquirer::ActionController::Base
がActionController::Base
にinclude
されるようになります。
# lib/params_inquirer/railtie.rb require 'params_inquirer/action_controller/base' module ParamsInquier class Railtie < ::Rails::Railtie initializer 'Initialize params_inquirer' do ::ActionController::Base.send :include, ParamsInquirer::ActionController::Base end end end
ただ、このファイルがRails起動時にrequire
されている必要があります。
インストールされたgemをrequire
するときlib/<gem_name>.rb
がrequire
されます。このgemであればlib/params_inquirer.rb
です。なので、ここでrailtieをrequire
しておく必要があります。
# lib/params_inquirer.rb if defined?(Rails) require 'lib/params_inquirer/railtie' else require 'lib/params_inquirer/parameters' end
require 'params_inquirer'
が実行されるとこのファイルが実行されます。もしRailsアプリケーション内であればrailtieをrequire
し、最初に見せたirbのような場合は必要なファイルだけrequire
するようにしています。
以上のようすることで、Rails起動時にrailtieをrequire
しrailtieから自分で作ったコードをRailsアプリケーション内にinclude
させることができました。
ActiveSupport::Concern
ここからは実際に使ったというよりは、actionpackやactivesupportなどのgemを読んでいくときに必要になったtipsです。
include
したモジュールを使ってクラスメソッドをmixinしたい場合、下のようにModule#.included
をオーバーライドしその中で内部モジュールをextend
するテクニックが定石みたいです。
module M def self.included(base) # extendによってクラスメソッドとしてmixinされる base.extend ClassMethods scope :disabled, where(disabled: true) end # クラスメソッドを定義する内部モジュール module ClassMethods ... end end
上のようなコードはActiveSupport::Concern
を使うと下のように書けます。
module M extend ActiveSupport::Concern included do scope :disabled, where(disabled: true) end module ClassMethods ... end end
一見すると、ClassMethods
モジュールがextend
されていないように見えますが、内部的にClassMethods
という名前のモジュールがextend
されます。「設定より規約」に従ってるんだと思います。
これを知らないと、クラスメソッドがextend
されていることに気づきにくいかもしれないです。また、ActiveSupport::Concern
はいろんなところに頻出するので、知っておいた方がいいと思いました。
ActiveSupport::Autoload
ActiveSupport::Autoload#autoload
はModule#autoload
の拡張で、Module#autoload
は必要なファイルを必要なタイミングでrequire
するメソッドです。
autoload(:Hoge, 'hoge') # 'hoge.rb'はこの時点ではrequireされていない p Hoge # ここで'hoge.rb'がrequireされる
ActiveSupport::Autoload#autoload
は、「Hoge
はhoge.rbにあるはず」という「設定より規約」に従って、Module#autoload
の第2引数を省略できるメソッドなので、上のコードは下のように書けます。
extend ActiveSupport::Autoload autoload :Hoge p Hoge # ここで'hoge.rb'がrequireされる
これもファイル名が省略されているということを知らないと、どのファイルをrequire
しているか見えづらいと思います。
最後に
あまりまとまらなくてすごい量になってしまいました。簡単なgemを作るのに知っておくべきことがいろいろあって大変でした。間違っていることがあれば修正しますので、コメントいただけると助かります。また、params_inquirerもまだ未完成なので、pull requestも待ってます。