Toyama.rb に参加してきた

タイミングよくちょっと近くへ行く用事があったの Toyama.rb に参加してきました。
勉強会の内容はみんなで雑談しながらもくもくして最後にやったことを各自で発表するような会です。

やったこと

  • Ruby 2.5 の導入
  • Ruby 本体をビルド
  • ファイルローカルなトップレベルメソッドを定義する gem の作成
    • もの自体は30分ぐらいでできたんですがちょっと問題があったので修正してから公開する予定

どんな場面で必要なのか?

成果発表の時に「どういう場面で必要なのか?」と質問されたので補足 わたし自身は以下のような場面で使いたいと思うことが多いですかね。

  • 長いメソッドから処理を切り出したい時
  • DRY を行う際に重複している処理を別メソッド定義したい時


まあこの辺りは Ruby に限らずどの言語でも言えるかと思います。

private メソッドやモジュール関数ではダメ?

private メソッドもダメではないんですが、Ruby の private は簡単に外部から呼び出すことができるので完全に隠蔽できないという問題があります(そもそも Ruby の private はそういう目的の機能ではないというのは置いておいて。
また、個人的には DRY を行うという目的でそのクラスに「余計なメソッド」を定義したくないというのもありますね。
モジュール関数も同様に「トップレベルで定数(モジュール)を定義すること」になってしまうので隠蔽化が不完全であったり、そのファイル以外でも影響してしまうという問題があるのでそれを避けたいです。

以上の理由から「ファイルローカルメソッドを定義する」専用の機構がほしいなーという感じです。
あと口頭で解答していた時は「Rails みたいなフレームワークではなくてライブラリとか小さい範囲のプロジェクトで使うことが多いかなー」みたいな話もあったと思うんですが、これ自体は別に大小は関係ないと思います(Rails でも上記で挙げたようなケースでは利用することはあると思いすし…多分…。

参加してみて

と、いうことで Toyama.rb に参加してきましたー。
都内だと地域.rb は結構頻繁に行われていますが、こういう地方の Ruby の勉強会は少ないので貴重ですね。
みんなでワイワイ言いながら作業していたので割といい刺激になりました。
いい意味で人数も多くなく(物理的な意味でも)他の参加者と距離が近くて話しやすかったです。
そういう意味では何かコードとかをスクリーンに映して、それを見ながらみんなで意見を言い合ったりして議論をしてみるのもいいんじゃないかなーと思いました。

Ruby の勉強会と言いつつ内容は Ruby に限らない(実際参加した今回も Ruby 以外の作業を行っている人は何人かいました)ので近くに住んでいてきになる方は参加してみるとよいのではないでしょうか。
さすがに毎回参加することは難しいですが、機会があればまた参加してみたいと思います。
運営の方々ありがとうございましたー。

あと、これは今回の勉強会に限ったことはないんですが、もくもく会でみんな作業しているとなかなかわからないことなんかを相談するのが難しいので「つらいときにあげる札」なんかがほしいなーと思いましたまる。
そしてもうちょっとしゃべりが上手くなりたい…

C++ で each_split

C++ で split というと『戻り値型とかどうするの』みたいな問題があるんですが、『それなら戻り値ではなくて each みたいに関数オブジェクトを渡せばいいじゃん』みたいな感じでやってみた。

#include <iostream>
#include <sstream>
#include <string>

auto each_split = [](auto src, char del, auto f){
    std::stringstream input(src);
    std::string output;
    while(std::getline(input, output, del)){
        f(output);
    }
};

int
main(){
    each_split("homu,mami,mado", ',', [](auto str){
        std::cout << str << std::endl;
    });

    return 0;
}
/*
output:
homu
mami
mado
*/

関数テンプレートを定義するのがめんどくさかったのでラムダ式を使っていますが、実装には関係ないです。

rbenv をインストールしたので覚書

今まで めんどくさかったので rbenv を使っていなかったのですが Ruby 2.5 をインストールする為に入れたのでその覚書。
rbenv とは?みたいなことは以下の記事を参照してください。

事前準備

すでに system の gem で bundle などをインストールしている場合は削除しておくと混乱が少ない。

$ gem uninstall bundler
$ gem uninstall bundle

あと ~/.bundle なども削除しておく。

rbenv のダウンロード

rbenv 本体は ~/.rbenv に保存

$ git clone git://github.com/sstephenson/rbenv.git ~/.rbenv

ruby-build のダウンロード

rbenv 以外に Ruby 本体をインストールするための ruby-build が必要なのでそれの保存。

$ mkdir -p ~/.rbenv/plugins
$ cd ~/.rbenv/plugins
$ git clone git://github.com/sstephenson/ruby-build.git

.bashrc に rbenv を読み込む設定を追記

rbenv をダウンロードしただけでは PATH などが設定されていないので .bashrc で設定を初期化するように追記。

export RBENV_ROOT="${HOME}/.rbenv"
if [ -d "${RBENV_ROOT}" ]; then
  export PATH="${RBENV_ROOT}/bin:${PATH}"
  eval "$(rbenv init -)"
fi

Ruby のインストール

rbenv installRuby のインストールし、rbenv global で使用する Ruby を設定する。

$ rbenv install 2.5.0
# rbenv intall や gem install した後に呼び出す
$ rbenv rehash
# global で使用する Ruby の設定
$ rbevn global 2.5.0
$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]

rbevn rehash を忘れないように。

ruby -v で system の Ruby を参照してる場合に試すこと

rbenv versions の確認

rbenv versions で現在インストールしてる Ruby と設定されている Ruby を確認する。

$ rbenv versions
  system
* 2.5.0 (set by /home/worker/.rbenv/version)

rbenv local をリセット

可能性は低いんですが rbenv local --unset でローカルのバージョンを削除してみる。

$ rbenv local --unset
$ rbenv rehash

bundle install でハマった

bundle install した際に rbenv ではなくて system 側の gem を参照しておりはまった。

bundle の PATH を確認

bundle コマンドが rbenv 側のものを参照しているか確認

$ which bundle
~/.rbenv/shims/bundle

~/.bundle を削除

手元だとこれを削除したら改善しました。

参照

2017年を振り返ってみて

残り25分しかないんですが雑に書いてみます。

C++

相変わらず Twitter とかでてきとーに言ってる事が多かったです。
C++ 界隈のニュースで言えば、つい先日やっと C++17 がリリースされましたね。
これでやっと <variant><optional> が標準ライブラリに追加されたり、構造化束縛や constexpr lambda みたいな機能も実装されました。
いやー C++20 もたのしみですねー。
個人的な話でいえば、Ruby の Enumerable っぽいライブラリを夏頃に書きました。
コードはクソですが、実装手段としては結構気に入っているのでどこかでまとめ記事を書こうかなーと思っていたら年を越してしまいそうなのでぐぬぬ…。
そのうち書きます…。

Ruby

今年も C++ より Ruby ばっかり書いていたような気がします。
個人的なニュースでいえば Ruby に投げたパッチが取り込まれた奴ですかねー。
内容自体はしょぼいんですが、こういうのを投げて取り込まれるのははじめてなので素直によかった。
これを期に来年は Ruby 本体のほうにもちょっとずつ関心を向けていきたい。

あと今年つくった gem は以下のような感じ。


相変わらず役に立たない gem ばかりつくっているので来年ももっと役に立たない gem をつくっていきたい。

Vim

無です。
来年はなんかしたい。

Electron + Vue.js + JavaScript

あんまりアウトプットしてなかったんですが、Electron + Vue.js + JavaScript で簡単なアプリとかつくってました。
今までフロントエンドはちょろちょろーっと触ったことはあるんですが、Vue.js や JavaScript をガッツリと触ったのはこれがはじめてかなー?(正確にいえば ES2015 を。
async/await なんかもはじめて触ったんですがめちゃくちゃ便利でした。
もう全部のプログラミング言語にほしいぐらい。
JavaScript は書いてて思ったよりもストレスがなかったので来年も書いてみたいですね。
フロントエンドって意味ならもうちょい開発環境(ビルドシステム)とかもどうにかしたい…。

Advent Calendar

一人 Advent Calendar やりました。


1人で2つも25日間記事を書き続けるとかあたまおかC。
でも来年もやりたい。

勉強会

今年は後半にちょいちょい勉強会に参加してみました。
来年も勉強会にちょいちょい参加して LT なんかをしていきたいですね。

来年の抱負

まあ今まで通りてきとーにプログラミング書いていきたいなーという感じ。
Ruby はそろそろ Rails やらなと人権がないので今年こそはやりたいですね…。
もしくは Hanami をやってみたい。
時代はうぇぶぷろぐらみんぐ。
あと Electron + Vue.js の技術を忘れないうちにまた何かつくりたいですね…。

それは良いお年をー。
そして書いている間に年が開けてしまったのであけおめー。

【一人 vimrc advent calendar 2017】vimrc を拡張するコツ【25日目】

一人 vimrc advent calendar 2017 25日目の記事になります。
今日で終わりっ!! と、言うことで vimrc を拡張するコツみたいなものを。

vimrc を設定する目的

vimrc を書こう!って言っても最初はどういう風に書けばいいのかわからないと思います。
Vim はかなり特殊なエディタなのでまずは

  • Vim らしい使い方


よりも

*自分の使い勝手のよいエディタ


を目指すとよいともいます。
Vim を使い始めた人がよく『カーソルキーを封印する』みたいな事を行っている人がいると思うんですが、そういう矯正をしてストレスを溜めるよりも『ストレスがない設定』を行っているほうがよいかなーと個人的には思います。
別に自分が使いやすければカーソルキーを使ってもええんやで…。

:help を読む

まずは基本中の基本ですが :help を読みましょう。
Vim:help がかなり充実しているので、わからないオプションやコマンドなんかが出てきたらすぐに調べる癖を付けておきましょう。

まず何から設定するべきか

vimrc ではまず、オプションを使って自分が使いやすいように挙動を制御していくとよいと思います。
どんなオプションがあるのかは :help option-list に簡単にまとまっているのでざっと眺めてみるとよいでしょう。
ちなみにオプション数は約300個ぐらいあります。
オプションの次は


みたいな順で設定を拡張していくとわかりやすいかなーと思います。

Vim script を知る

vimrc を書くということはつまり Vim script を書くことになります。
もちろん :set コマンドでオプションを設定するだけでも Vim はカスタマイズすることは出来るんですが、基本的な制御構文や組み込み関数、変数の扱いなんかを覚えるとより設定の幅が広がります。
Vim script については :help vim-script-intro なんかを読んでみるとよいでしょう。

キーマッピングを行う際の注意点

これは注意点というかわたしの持論なんですが『コマンドを呼び出すようなキーマッピングは極力しない』方がいいです。
例えば、以下のようにコマンドを呼び出すようなキーマッピングを行っていることは多いと思います。

nnoremap <Space>udm :Unite directory_mru<CR>
nnoremap <Space>urm :UniteResume<CR>
nnoremap <Space>uff :Unite file file/new -hide-source-names<CR>
nnoremap <Space>uol :Unite outline -no-quit -buffer-name=outline -winheight=10<CR>
nnoremap <Space>unb :Unite neobundle<CR>
nnoremap <Space>url :Unite reanimate:default_category -default-action=reanimate_switch<CR>
nnoremap <Space>urs :Unite reanimate:default_category -default-action=reanimate_save<CR>

一見すると便利そうなんですが、この手のキーマッピングは設定しても『まず覚えきれない』ので『無駄』です。
このようなコマンドを呼び出すキーマッピングは、


というのではなくて

  • 1日1000回ぐらい呼び出すようなコマンドはキーマッピングしておく


というぐらいのほうが無駄なキーマッピングを省くことができるので便利です。
また、長いコマンドはキーマッピングではなくてユーザ定義コマンド化しておくのもよいでしょう。

command! UniteFile Unite file file/new -hide-source-names

ただし、以下のように『デフォルトの操作を変更する』ようなキーマッピングは覚える必要がないのでどんどんしていくとよいと思います。

" https://sites.google.com/site/fudist/Home/vim-nihongo-ban/tips#TOC--3
" カーソルを表示行で移動する。物理行移動は<C-n>,<C-p>
nnoremap j gj
nnoremap k gk
vnoremap j gj
vnoremap k gk

nnoremap <Down> gj
nnoremap <Up>   gk

" カーソルキーで行末/行頭の移動可能に設定。  
set whichwrap=b,s,[,],<,>
nnoremap h <Left>
nnoremap l <Right>

頻繁に使うようなコマンドは予めユーザ定義しておく

先ほどのキーマッピングの話でもあったのですが、以下のようによく使うコマンドなどはユーザ定義しておくとよいです。

" ハードタブをソフトタブに
command! TabToSoft %s/\t/    /ge
" 行末の空白文字を削除
command! RemoveLastSpace %s/\s\+$//ge

こうすることで入力する手間を省くことが出来ます。
また、コマンド化しておけば autocmd なんかでも呼び出しやすくなりますね。

他人の vimrc を読む

他人の vimrc はパンツです。
そしてパンツは見たくなるものですよね。
vimrc を公開している人は結構いるので気になる人や憧れている人のパンツ vimrc をみてみると興奮して新しい発見があって面白いです。
とはいえ、そんなに他人の vimrc を読むことはあまりないと思うのでvimrc 読書会などに参加してみるとよいと思います。
他人の便利な設定はどんどんパクっていきましょう。

定期的に vimrc を見直す

これが一番重要です。
vimrc を設定していくとどんどん肥大化していきます。
肥大化していくと管理が難しくなったり Vim の起動時間などにも影響してきます。
vimrc はどうしても『その場』で『雑に』設定を追加してしまうことが多いので、定期的に整理していく必要があります。
特に利用していないキーマッピングプラグインがあればどんどん削除してしまいましょう。
逆にユーザ定義コマンドなどはそれほど副作用が大きくないのであまり使っていなくてもそのまま残しておいてもよいと思います。

変数のスコープに注意する

vimrc を書くと言うことは Vim script を書くことになります。
ただ、Vim script は結構癖が強い言語なので注意して書く必要があります。
例えば、変数のスコープが結構特殊で変数名に prefix を付けることでそのスコープを明示化します。

" g: をつけるとグローバル変数
" どこからでもアクセスできる
let g:value = 42

" s: をつけるとスクリプトローカル変数
" そのスクリプトファイル上でのみ参照できる
let s:value = 42

上記のようにスコープごとに別々に変数を定義します。
また、prefix を付けなかった場合は『グローバル変数』として定義されるので注意する必要があります。

" g:value と同等
let value = 42

" こういう i もグローバル変数として定義される
for i in [1, 2, 3]
   " ...
endfor

特に理由がなければ vimrc ではスクリプトローカル変数(s:)として定義することで意図しない副作用を抑えることが出来ます。

プラグインは入れたほうがいい?

これは宗教上の理由もあると思いますが、個人的には『プラグインはどんどん入れていっていい』と思います。
やっぱり世の中には便利なプラグインがたくさんあるのでどんどん使っていった方が得かなーと。
欲しいと思った機能がプラグインとしてすでにあったりするのも Vim の魅力の一つですしねー。
ただし、プラグインを使う上では以下のことに注意する必要はあります。

  • プラグインを入れることでパフォーマンスに影響することは覚えておく
  • 競合したり変な設定を行っているプラグインもあるので精査する必要がある
  • 設定が難しいプラグインもあるのでそれなりに Vim script の知識が必要
  • 定期的に使わないプラグインなどは削っていく


まあプラグインを削るのは後からでもできるので、最初は時に気にしないでどんどんプラグインをいれていっていいかなーと思います。

まとめ

  • Vim の機能は全て :help にかかれているのでわからないことがあればすぐにヘルプろう
  • まずはオプションの設定から始めるとわかりやすい
  • vimrc を書く上で Vim script の知識が必要になってくるので、慣れてきたら覚えよう
  • キーマッピングは過剰に設定してもどうせ使わないのでほどほどに
  • vimrc は定期的に見なおそう
  • プラグインはどんどん入れていこう


まー最初は慣れないので書くのが難しいと思うんですが、『習うより慣れろ』って言うことでどんどん設定を書いていくのがよいかなーと個人的には思います。
何度もいいますが、オプションやキーマッピングプラグインなどはあとからいくらでも変更する事ができますしね。
冒頭にも書きましたが『Vim らしい使い方』よりも『自分の使い勝手のよいエディタ』を目指しましょう!!

一人 Advent Calendar を終えて

vimrc は4000行とかあるからネタに困らないやろーと思って始めたんですが、意外とプラグインの設定やあまり汎用性がない設定などが多かったので思ったよりも厳しかったです。
そもそも汎用的な機能は全部プラグイン化しているんやった…。
まあでもそのおかげでネタを探すために vimrc を何回も見なおしたりしていたのでどんな設定をしているのかを見直すことが出来たのはよかったかな。

本当は最初と最後で vimrc がどうなったのか比較してみようかとも思ったのですがそんなに変わってないのでやめておきました。
さっきも書いたんですが設定の殆どがプラグインなので(ry。

そんなわけで来年は『一人 Vim plugin Advent Calendar』でも書こうかなーとは思っています。
思っているだけでやると言っていませんー。
それではまた来年ー。
と、いうか冷静に考えると 1ヶ月の間に 一人 Advent Calendar を2つやりつつ、通常の Advent Calendar の記事を3つ書いて、更に LT 用のスライドを4〜5個書くなんて頭おかC

【一人 Ruby Advent Calendar 2017】Ruby 2.5 がリリース!!【25日目】

一人 Ruby Advent Calendar 2017 25日目の記事になります。
今日で最後!!

Ruby 2.5 がリリース!!

直前にいろいろとあったようですが、無事リリースされました。
コミッタの皆さんお疲れ様でした。

Bundler の標準入りは延期

Ruby 2.5 で標準入りする予定だった Bundler ですが、リリース直前に問題が見つかった為、今回は延期されることに。
うーん、まあ Bundler 自体が結構不安定で反対している人もいたぽいんですが、残念といえば残念かなー。

個人的に気になる変更点や追加点

  • require "pp" が暗黙的に読み込まれるようになった
  • バックトレース順の変更
  • ブロック引数を定義した場合のパフォーマンスの改善
  • Set#===Method#=== の追加
  • レシーバをブロックの引数で受け取りブロックの戻り値を返す #yield_self が追加
  • define_methodattr_accessor などが public 化
  • 式展開時の #to_s が Refiements 時で定義されている場合にも呼ばれるように


などなど、他にも変更された機能や追加された機能はあるので気になる方は以下のサイトなどを参照してみるとよいと思います。
個人的には #=== がちょっと注目されてるのかなーという感じですかね。
#===case-when 以外でもいろいろと応用が効く機能なので今後もいろいろと利用していきたいと思います。

あと今回の式展開の #to_s のように Refinements まわりも年々制限が緩和されている流れですね。
Refinements に関してはまだまだ使う上でちょっとつらい部分があるので、そのあたりを今後 Ruby 本体に提案できればなーと思っていたりします。

参照

一人 Advent Calendar を終えて

と、言うわけでやっと終わりましたーやふー。
途中で少し遅れることはありましたが、なんとか完走する事が出来ました。
やる前からわかってたんですが最初にちょっと書き溜めておいたネタだけじゃ全然足りなかったですね、これ。
やーやっぱり勢いでやるものじゃないわー。

まあしかし、そのおかげでネタを絞りだす為に普段はあまり気にしないような部分も調べて書いたのでそういう意味ではよかったかなーとは思います。
トップレベルの扱いやキーワード引数まわりは結構てきとーに使っていたので、そのあたりをちゃんと調べて理解出来たのでそういう意味ではやってよかったかなーと思います。

次は1年ぐらい書き溜めて溜まってたら書くぞ〜。
書いた記事の一覧は以下から参照してくださいー。

さて、これからもう1つの方も書かないと…。

【Vue.js #1 Advent Calendar 2017】Vue.js + Element でフォームダイアログをいい感じに呼び出す【24日目】

Vue.js #1 Advent Calendar 2017 24日目の記事になります。
少し前に Electron + Vue.js + Element でネイティブアプリの開発を行っていたのですが、その時に利用したコードなどを簡単に紹介してみようかと思います。

Element とは

Element(element-ui) とは Vue 2.0 ベースのコンポーネントライブラリになります。
例えば、以下のようにモーダルダイアログからフォーム入力などを行うことが出来ます。

index.html

<!-- import CSS -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<div id="app">

  <!-- 登録ダイアログ -->
  <el-button @click="registry_visible = true">登録</el-button>
  <pre>{{ registry_data }}</pre>

  <el-dialog :visible.sync="registry_visible" title="保存">
    <el-form :model="registry_form" label-width="120px">
      <el-form-item label="first name">
        <el-input v-model="registry_form.first_name"></el-input>
      </el-form-item>
      <el-form-item label="last name">
        <el-input v-model="registry_form.last_name"></el-input>
      </el-form-item>
      <el-form-item label="age">
        <el-input v-model="registry_form.age"></el-input>
      </el-form-item>
    </el-form>

    <span slot="footer" class="dialog-footer">
      <el-button @click="registry_visible = false">Cancel</el-button>
      <el-button type="primary" @click="registry_submit">保存</el-button>
    </span>
  </el-dialog>
  
</div>
<!-- import Vue before Element -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="./script.js"></script>

script.js

new Vue({
  el: '#app',
  data: function() {
    return {
      registry_visible: false,
      registry_form: {},
      registry_data: [],
    }
  },

  methods: {
    registry_submit(){
      this.registry_data.push(this.registry_form);
      this.registry_form = {};
      this.registry_visible = false;
    },
  },
});

on jsbin.com

こんな感じのコードをもうちょっと使いやすくしてみたので今回はそれについて書いてみようかと思います。

問題点

問題点ってほどでもないと思うんですが、例えばフォーム入力にデフォルト値とかを設定したい場合にちょっと複雑になってきます。

<!-- 登録ダイアログ -->
<!-- 開く時に関数を呼び出す -->
<el-button @click="open_registry">登録</el-button>
<pre>{{ registry_data }}</pre>

<el-dialog :visible.sync="registry_visible" title="保存">
  <el-form :model="registry_form" label-width="120px">
    <el-form-item label="first name">
      <el-input v-model="registry_form.first_name"></el-input>
    </el-form-item>
    <el-form-item label="last name">
      <el-input v-model="registry_form.last_name"></el-input>
    </el-form-item>
    <el-form-item label="age">
      <el-input v-model="registry_form.age"></el-input>
    </el-form-item>
  </el-form>

  <span slot="footer" class="dialog-footer">
    <el-button @click="registry_visible = false">Cancel</el-button>
    <el-button type="primary" @click="registry_submit">保存</el-button>
  </span>
</el-dialog>
new Vue({
  el: '#app',
  data: function() {
    return {
      registry_visible: false,
      registry_form: {},
      registry_data: [],
    }
  },

  methods: {
    // ダイアログを開く関数
    open_registry(){
      this.registry_visible = true;
      form = this.registry_data[this.registry_data.length - 1] || {
        first_name: "yamada",
        last_name:  "tarou",
        age: 20
      };
      Object.assign(this.registry_form, form);
    },

    // フォームを登録する時に呼ばれる関数
    registry_submit(){
      this.registry_data.push(this.registry_form);
      this.registry_form = {};
      this.registry_visible = false;
    },

  },
});

JS Bin on jsbin.com

こんな感じで複数の関数を言ったり来たりするのがちょっともにょったのでなんとかしてみました。

async/await を利用する

まず、async/await を利用して上記open_registry()registry_submit() を1つの関数にまとめてみました。

index.html

<!-- import CSS -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<div id="app">
    <!-- 登録ダイアログ -->
    <el-button @click="registry">登録</el-button>
    <pre>{{ registry_data }}</pre>

    <el-dialog :visible.sync="registry_visible" title="保存" :before-close="close">
      <el-form :model="registry_form" label-width="120px">
        <el-form-item label="first name">
          <el-input v-model="registry_form.first_name"></el-input>
        </el-form-item>
        <el-form-item label="last name">
          <el-input v-model="registry_form.last_name"></el-input>
        </el-form-item>
        <el-form-item label="age">
          <el-input v-model="registry_form.age"></el-input>
        </el-form-item>
      </el-form>

      <span slot="footer" class="dialog-footer">
        <el-button @click="registry_visible = false">Cancel</el-button>
        <el-button type="primary" @click="submit">保存</el-button>
      </span>
    </el-dialog>
</div>
<!-- import Vue before Element -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="./script.js"></script>

script.js

new Vue({
  el: '#app',
  data: function() {
    return {
      registry_visible: false,
      registry_form: {},
      registry_data: [],
    }
  },

  methods: {
    open(name, form){
      this[`${name}_form`] = form;
      this[`${name}_visible`] = true;
      return new Promise((resolve, reject) => {
        var submitted = false;
        this.submit = () => {
          this[`${name}_visible`] = false;
          submitted = true;
          resolve(this[`${name}_form`]);
        };

        this.close = () => {
          this[`${name}_visible`] = false;
          if( !submitted ){
            reject("Dialog is closead.");
          }
        };
      });
    },
    // ダミー
    submit(){},
    close(){},

    async registry(){
      this.registry_visible = true;
      form = this.registry_data[this.registry_data.length - 1] || {
        first_name: "yamada",
        last_name:  "tarou",
        age: 20
      };
      result = await this.open("registry", {...form});
      this.registry_data.push(result);
    },
  },
});

JS Bin on jsbin.com

こんな感じで async/await を使うことで

result = await this.open("registry", {...form});

のように直接フォームを呼び出して、その値を取得することが出来ます。

コンポーネント化する

さてさて、先ほどのコードだとちょっと汎用性にかけるので簡単にコンポーネント化してみたいと思います。

Vue.component("form-dialog-wrapper", {
  template: `
    <div>
      <slot :submit="submit" :form="form" :close="close" :visible.sync="visible"></slot>
    </div>
  `,
  data(){
    return {
      visible: false,
      form: {},
    }
  },

  methods: {
    open(form){
      this.form = form;
      this.visible = true;
      return new Promise((resolve, reject) => {
        var submitted = false;
        this.submit = () => {
          this.visible = false;
          submitted = true;
          resolve(this.form);
        };

        this.close = () => {
          this.visible = false;
          if( !submitted ){
            reject("Dialog is closead.");
          }
        };
      });
    },

    // ダミー
    close(){},
    submit(){}
  }
});

コンポーネントはこんな感じです。
Vue.js のポイントとしては slot を使って form のデータのやり取りを行うところでしょうか。
HTML 側では以下のように使用します。

index.html

<!-- 登録ダイアログ -->
<el-button @click="registry">登録</el-button>
<pre>{{ registry_data }}</pre>

<!-- JavaScript 側から参照できるように ref で名前付けしておく -->
<form-dialog-wrapper ref="dialog_registry">
  <!-- form データへの参照は slo-scope 経由で行う -->
  <el-dialog title="保存" slot-scope="props" :visible.sync='props.visible' :before-close="props.close">
    <!-- slot-scope 経由でデータを参照することでコンポーネントのデータのみ bind 出来る -->
    <el-form :model="props.form" label-width="120px">
      <el-form-item label="first name">
      <el-input v-model="props.form.first_name"></el-input>
      </el-form-item>
      <el-form-item label="last name">
      <el-input v-model="props.form.last_name"></el-input>
      </el-form-item>
      <el-form-item label="age">
      <el-input v-model="props.form.age"></el-input>
      </el-form-item>
    </el-form>

    <span slot="footer" class="dialog-footer">
      <el-button @click="props.close">Cancel</el-button>
      <el-button type="primary" @click="props.submit">保存</el-button>
    </span>
  </el-dialog>
</form-dialog-wrapper>

こんな感じで slot-scope="props" を利用してコンポーネントのデータを参照します。
このようにすることでメインの Vue ではフォームの入力データなどを用意する必要がなくなります。
実際にメインの Vue は以下のようになります。

new Vue({
    el: '#app',
    data: function() {
        return {
            registry_data: [],
        }
    },

    methods: {
        async registry(){
            form = this.registry_data[this.registry_data.length - 1] || {
                first_name: "yamada",
                last_name:  "tarou",
                age: 20
            };

            // ref 経由でコンポーネントを参照する
            result = await this.$refs["dialog_registry"].open({...form});
            this.registry_data.push(result);
        },
    },
});

このようにかなりコードがすっきりしている事がわかると思います。

JS Bin on jsbin.com

まとめ

と、言う感じでフォームダイアログをいい感じにしてみました。
元々はフォームダイアログが複数あってどんどんデータが肥大化していったのでなんとかしたいと思いこんな感じのロジックを思いつきました。
Vue.js もそうですが async/await めちゃ便利ですね…。
Vue.js 自体はまだ全然やれてないんですが、こういうのを回避する手段って他にもあったりするんですかね…(ちなみに Vuex はまだやってない。

そんな感じで簡単に書いてみましたー。
もっとこうしたらいいよーみたいなのがあればコメントや Twitter までおねがいしますー。