読者です 読者をやめる 読者になる 読者になる

2017新卒に捧ぐ!俺のvimの設定!!

奥村です。

今回のエンジニアブログは前回に引き続き「新卒に捧ぐ!俺の〇〇」シリーズです (前回の記事はこちら↓)

blog.engineer.adways.net

第二回のテーマははタイトルの通り

「新卒に捧ぐ!俺のvimの設定」

です。

アドウェイズにはvimを採用している人が多くいます。
「これをするためにこのプラグインを入れている」や「これがあると便利」等のvimの設定をエンジニアから集めてきました。
本記事では4人のオススメvim設定を紹介します。新人vimmerは先人の設定を参考にすると良いでしょう!

それではどんどん紹介していきます。

一人目

こんにちは、井古田です。

設定1: 置換を便利に

開発やリファクタリングしている時に、変数名やメソッド名を置換したいなって思う時ありませんか?
僕はけっこうあるんですよね〜
Sublime TextやAtomでは優れた置換機能がデフォルトでありますが、
Vimだと :%s/◯◯/××/g または :%s/◯◯/××/gc を使ったりすると思います。
ただ :%s だと、置換対象の文字が分かりづらかったり、意図してない部分の文字列が置換されたりして悲しくなったりします。。

そんな時出会ったのが osyo-manga/vim-over です!

github.com

vim-over は置換対象の文字列をハイライトし、その横に変更予定の文字列を表示してくれるという優れものです。
視覚化され、一括で置換を行うことができるため、今年入ってきた新卒にVimでもかっこいい置換を披露することができます(^ ^)

設定ファイル

" ---------- 'osyo-manga/vim-over' ----------
" 全体置換
nnoremap <silent> <Space>o :OverCommandLine<CR>%s//g<Left><Left>

" 選択範囲置換
vnoremap <silent> <Space>o :OverCommandLine<CR>s//g<Left><Left>

" カーソルしたの単語置換
nnoremap sub :OverCommandLine<CR>%s/<C-r><C-w>//g<Left><Left>

実行イメージ

  • 全ての describehogefuga に置換(全体置換)

  • 選択範囲の varlet に置換(選択範囲置換)

  • カーソルしたの testMethodfugaMethod に置換(カーソルした単語変換)

二人目

こんにちは、澤藤です。

設定1:コメントアウトを楽に

開発時にはコメントアウトを結構使ったりするので毎度インサートモードに入ってからコメントを入力するのは面倒です

この設定をすることで\cと打つだけでコメントアウト出来てしまいます(複数行にも対応)

さらにファイルタイプに合わせたコメントアウトをしてくれます

‘tyru/caw.vim.git'というプラグインを使っています

github.com

設定ファイル

NeoBundle 'tyru/caw.vim.git'
" caw comment out
nmap <Leader>c <Plug>(caw:i:toggle)
vmap <Leader>c <Plug>(caw:i:toggle)

実行イメージ

  • ruby

  • html

設定2:日付、時間のショートカット

日付の入力って地味に面倒ですよね

なのでショートカットで今の日付、時間が入力できるようにしたりしています

議事録とかメモとかでも使えたりするので便利です

設定ファイル

inoremap ,todo <C-R>=strftime('%Y-%m-%d %a')<CR>
inoremap ,date <C-R>=strftime('%Y-%m-%d')<CR>
inoremap ,time <C-R>=strftime('%H:%M:%S')<CR>

実行イメージ

その他

色々プラグイン入れたり設定したりしていくとどんどん肥大化して後で見直したときに訳がわからなくなったりします

なのであらかじめファイルを分割してあげておくと後で楽になると思います

私の場合は下記のような設定をしています

  • .vimrc
" 通常設定
source ~/dotfiles/vimrc/rc/basic.vim
" プラグイン一覧
source ~/dotfiles/vimrc/rc/plugins.vim
" キーマッピング
source ~/dotfiles/vimrc/rc/mapping.vim
" タブ関連の設定
source ~/dotfiles/vimrc/rc/tab.vim
" quickhlの設定
source ~/dotfiles/vimrc/plugins/quickhl.vim
" cawの設定
source ~/dotfiles/vimrc/plugins/caw.vim
.....
.....
.....

三人目

こんにちは、清水です。

普段はRubyで開発しているエンジニアです。

使っているプラグインは40個ほどで、syntax, colorshcemeを除くと30個ほどでした。

プラグイン管理には dein.vim、ステータスバーには lightline.vim … など便利で有名なプラグインを使っています。

プラグインの入れ替えを定期的にしていて、vimawesome を探して、入れてみて手に馴染んだら残すようにしています。

設定1:カラースキーム設定

同じカラースキームでコーディングをしていると、言語によっては見づらく、目が疲れるのでよく色を変えています。

そこで設定している「さくさくカラースキーム変更」をご紹介します。

新しいカラースキームを探す時はvimcolors さんが便利です。

こちらからよさそうなカラースキームを探します。

colorschemeの変更は :colorscheme [色の名前] ですが、何度も変更するのは手間なので

unite.vimunite-colorscheme をインストールして :Unite -auto-preview colorscheme を実行すると便利です。

リアルタイムに色の変更が確認できます。

お気に入りのカラースキームがいくつか決まったら、いつでも変更できるようにします。

Hack #228: 見た目を気軽に変更する(その2) ColorRoller版 で紹介されている方法が便利です。

" color roller: "{{{
let ColorRoller = {}
let ColorRoller.colors = [
            \'default'   , 'adaryn'    , 'anotherdark' , 'solarized'   , 'moss'     ,
            \'dante'     , 'golden'    , 'ironman'     , 'jellybeans'  , 'mustang'  ,
            \'softblue'  , 'tir_black' , 'wombat256'   , 'yuroyoro256' , 'molokai'  ,
            \'desert256' , 'mrkn256'   , 'wuye'        , 'lucius'      , 'twilight' ,
            \'onedark',
            \ ]

function! ColorRoller.change()
    let color = get(self.colors, 0)
    silent exe "colorscheme " . color
    redraw
    echo self.colors
endfunction

function! ColorRoller.roll()
    let item = remove(self.colors, 0)
    call insert(self.colors, item, len(self.colors))
    call self.change()
endfunction

function! ColorRoller.unroll()
    let item = remove(self.colors, -1)
    call insert(self.colors, item, 0)
    call self.change()
endfunction
"}}}


" ColorRoller {{{
nnoremap <silent><F9> : <C-u>call ColorRoller.roll()<CR>
nnoremap <silent><F8> : <C-u>call ColorRoller.unroll()<CR>
" }}}

F9, F8 にマッピングして好きな色をいつでも交換できるようにしています。

お気に入りのカラースキームを見つけて、快適コーディングしましょー。

四人目

インフラエンジニアの渡瀬です。

設定1:vim から git を使う

vim-fugitive

Nebundleユーザーなら vimrc

NeoBundle 'tpope/vim-fugitive'

Deinの人は .vim/rc/dein.toml

[[plugins]]
repo = 'tpope/vim-fugitive'

コレだけで vim から git が呼べる様になります。

Screencasts - A complement to command line git

基本的な使い方はこちらのVim Castを見ると理解できると思います。

詳細は :help fugitive してください

使い方

基本的なコマンドだけ紹介します

:Gdiff
git diff %

diffモードで現在開いているファイルのgit diffを開きます。

この機能の為に入れている言っても過言ではないです。

:Gwrite
git add %

今開いているファイルをgit addしてくれます

:Gcommit
git commit

git commitを実行し、コミットメッセージを記述するバッファを開きます。

設定2:markdownのプレビューをみながら編集する

previm

markdownをプレビューを見ながら編集できます。

NeoBundle 'kannokanno/previm'

または

[[plugins]]
repo  = 'kannokanno/previm'
on_ft = ['markdown']

README.mkd に書いてある通り、open-browser.vimと同時に使うと便利です

設定3:markdownのテーブルをいい感じにする

markdwonを編集中にテーブルをいい感じに修正してくれる mdt というツールがあります。

markdownのテーブルは文字を編集するたびに全行を編集することになり、めんどうですよね?

これをサクッと修正してくれるとても素晴らしいツールです。

このコマンドを呼び出すには

command! -range=% Mdt :<line1>,<line2>!mdt

とするか、同じことをしているだけのmdt.vimがあります。

NeoBundle 'ywatase/mdt.vim'

設定後、テキストをvisualモードで選択して、:Mdt すればmdtがいい感じに整形してくれます。

実行イメージ

f:id:AdwaysEngineerBlog:20170414193342g:plain


いかがだったでしょうか。気になったものがあればすぐにでも試してみましょう!
vimmerへの道ははじまったばかりです。

2017新卒に捧ぐ!俺のお勧め技術書!!

久保田です。

こんにちは、桜が咲きましたね。春ですね。

春といえば新しい出会い、我が社にも新卒がたくさん入ってきました。

そこで4月はそんな新卒のために!
先輩方に協力していただき、「新卒に捧ぐ!俺の〇〇」シリーズをやりたいと思います。

少しでも新卒の成長に役に立てば、そしてブログに興味持ってくれたらいいなと思う所存でございます。

記念すべき第一回のテーマは、

「新卒に捧ぐ!俺のお勧め技術書!!」

です。

エンジニアといえば勉強、勉強といえば本!
エンジニアにとって技術書は必ず必要なもの。そんな技術書を、先輩方にお勧めしていただきました!

では、どんどん紹介していきます。

リーダブルコード

(Nさん 4年目 チーフエンジニア)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

  • 作者: Dustin Boswell,Trevor Foucher,須藤功平,角征典
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2012/06/23
  • メディア: 単行本(ソフトカバー)
  • 購入: 68人 クリック: 1,802回
  • この商品を含むブログ (136件) を見る

多くの人がおすすめの本にあげる王道の本です。
読みやすいコードを書くためのテクニックをシンプルでわかりやすく書いてあります。
個人的には「少しコードを書けるようになったかなー」と思った時くらいに読むと良いと思います。
一度読んで終わりというよりは「きれいなコードってなんだっけ?」って思った時に読み返したくなるので、手元に置いておきたい本です。

アホでマヌケなプログラミング

(Nさん チーフエンジニア)

アホでマヌケなプログラミング (プログラマーズ叢書)

アホでマヌケなプログラミング (プログラマーズ叢書)

プログラマという職業や、昔のプログラマあるあるとかをゆるーく書いたエッセイです。
ガチガチの技術書という感じではないです(笑)。
勉強するためというよりは軽い読み物で、サクサク読みつつちょっとしたプログラマの歴史や考え方、雑学を学ぶ事ができます。すごく読みやすい本です。

Webを支える技術

(K サーバーサイドエンジニア)

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

Webとはなんぞや、ということがよーくわかる本。
HTTPとは?
HTTPを考えた人は?
HTTPメソッドとは?正しい使い分けとは?
クールなURIとは?
RESTとは?
1つでもわからない人、自信がない人は必ず読むべき一冊です。
なんとなく知っているWebが、よくわかるWebになります。

メタプログラミングRuby 第2版

(K サーバーサイドエンジニア)

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

実践ではほぼ使えないテクニックが満載で避ける人が多い一冊。
しかし、この本のテクニックを知っていると、どのようにRubyが動いているかを垣間見ることができます。
そうだったのか、と何度もうなずかされ、プログラミングがより一層楽しくなること間違い無し!
この本を読み終わった頃にはあなたの目の前にはRubyの定数ツリーがそびえ立っている。はず。

ノンデザイナーズデザインブック

(Uさん アンドロイドアプリエンジニア)

ノンデザイナーズ・デザインブック [フルカラー新装増補版]

ノンデザイナーズ・デザインブック [フルカラー新装増補版]

  • 作者: Robin Williams,吉川典秀
  • 出版社/メーカー: 毎日コミュニケーションズ
  • 発売日: 2008/11/19
  • メディア: 単行本(ソフトカバー)
  • 購入: 58人 クリック: 1,019回
  • この商品を含むブログ (106件) を見る

アプリエンジニアやフロントエンドエンジニアのようにUIを実装するのにオススメ。
デザイナーさんから貰うUIに対して、今までとちょっと違った見方ができるはず。
個人的には「整列」の話が好き。

ゼロから作るDeep Learning

(Oさん チーフエンジニア)

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

ちまたで話題のDeepLearningが、学習の基礎からCNNなどの理論までプログラムで理解できる優れものな一冊。
基本的にライブラリに頼らずPythonで一から書いていくので、数学が苦手なエンジニアにもオススメです。
自分の書いたプログラムで人の顔が分類できるようになったときは感動ものです。

インフラエンジニアの教科書2 スキルアップに効く技術と知識

(Aさん ユニットマネージャー)

インフラエンジニアの教科書2 スキルアップに効く技術と知識

インフラエンジニアの教科書2 スキルアップに効く技術と知識

インフラエンジニアに必須となるハードウェアやOS、ネットワーク、セキュリティ知識がわかりやすく簡潔にまとめられた良書です。 これからインフラエンジニアになる方、目指す方は一読をオススメします。 前作インフラエンジニアの教科書ももちろんオススメです。

フロントエンドエンジニアのための現在とこれからの必須知識

(Tさん フロントエンドエンジニア)

フロントエンドエンジニアのための現在とこれからの必須知識

フロントエンドエンジニアのための現在とこれからの必須知識

  • 作者: 斉藤祐也,水野隼登,谷拓樹,菅原のびすけ,林優一,古沢宏太
  • 出版社/メーカー: マイナビ出版
  • 発売日: 2016/01/28
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (1件) を見る

必須知識が網羅されていて全体像を把握できます。
また、フロントエンドエンジニアの環境や求められる資質、成長戦略などが書かれています。
さらにトレンドが掲載されているため、お勧めの一冊です。

オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方

(Yさん マネージャー)

Rubyで開発を行っているのであれば、読んでおいて間違いがない本です。
オブジェクト指向を使い、どのように保守性を高めていくのか分かりやすく書いてあります。
個人的に、保守性が高いコードが必ずしも理解が容易なコードではないということが書かれていて刺さりました。

Webエンジニアが知っておきたいインフラの基本

(Wさん テクニカルマネージャー)

Webエンジニアが知っておきたいインフラの基本

Webエンジニアが知っておきたいインフラの基本

Webエンジニアとしてインフラ関係を勉強する最初の一冊としてオススメです。
インフラの人と話をする前に読んでおいてくれると嬉しいです。
MySQLのメトリクスについて30pほど割いて書いてあってMySQLにも詳しくなれてお得

闘うプログラマー

(Wさん テクニカルマネージャー)

闘うプログラマー[新装版] ビル・ゲイツの野望を担った男達

闘うプログラマー[新装版] ビル・ゲイツの野望を担った男達

プログラマーを題材としたノンフィクション小説(?)
Windows NTの開発を行ったカトラーのお話です。
昔のMicrosoftの様子が垣間見れてとても面白いです。
息抜きにどうぞ。


いかがでしたでしょうか、中にはレベルの高い本もありますが、どれも面白そうな本ですね。

気になったものは是非先輩に借りたり買ったりしてどんどん勉強しましょう。ではまた来週〜

deviseの独自ストラテジーの作り方

久保田です。

最近、deviseのストラテジー(プラグイン的なもの)を社内の認証システム用に作りました。
その時、古い情報が多く、半泣きになりながらいろいろ模索して作ったので、
備忘録がてら、同じく困る人がいなくなるように記事にします。

devise

Railsで認証つきのアプリケーションを作成したことのある人なら必ず使ったことのあるgemだと思います。
かなり充実したライブラリでデフォルトでよくあるパターンのストラテジーは入っているので、基本的にはそのまま使うことができます。

ストラテジー

deviseではストラテジーという認証のルールを設定することで、必要な機能だけを享受することができます。
例えば、db認証をやりたい時は、

class User < ApplicationRecord
  devise :database_authenticatable

  # ...
end

ロック機能をつけたい時は、

class User < ApplicationRecord
  devise :database_authenticatable, :lockable

  # ...
end

のような感じで宣言します。

今回は、このストラテジーをオリジナルで作ったから作り方を共有します!という話です。

完成イメージ

完成イメージは以下のような感じです。

class User < ApplicationRecord
  devise :foo_authenticatable

  # ...
end

foo_authenticatableというストラテジーを作成します。
これだけで、いつものようにdeviseを使うようにしてオリジナルの認証を通します。

フォルダ構成

最低限必要な構成は以下の通りです。
(lib以下に作りました。)

lib
└── devise
    └── models
        └── foo_authenticatable.rb
    └── strategies
        └── foo_authenticatable.rb

ストラテジーを作るためには、
- modelがincludeするファイル
- 実際に認証を行うstrategyファイル

が必要です。

動作イメージ

動作イメージは、以下のような流れになります。

sessions_controller#create  
↓  
strategies/foo_authenticatable#authenticate!(成功か失敗かを判断する)  
↓  
models/foo_authenticatable.独自のロジック(必要に応じて)  

strategy

まずはstrategyファイルからです。
このファイルは、実際に認証を通していいか、ダメかを判定するロジックを書きます。
以下のような構成になります。

  • strategies/foo_authenticatable.rb
require 'devise/strategies/authenticatable'

module Devise
  module Strategies
    class FooAuthenticatable < Authenticatable
      # optional
      def valid?
        # 事前に行いたいバリデーション。
        # パラメータなどの確認を行う。
        # ex: params['username'] || params['password']
      end
      
      # required
      def authenticate!
        # modelで定義されたメソッドで認証ロジックを通す。modelに書いた方がスッキリするため。
        # mapping.toでストラテジーを宣言したモデルが取得できる。
        res = mapping.to.authenticate(params[scope])
        if res
          success!(res) # 認証成功。
        else
          fail!(:invalid) # 認証失敗。
        end
      end
    end
  end
end

Warden::Strategies.add(:foo_authenticatable, Devise::Strategies::FooAuthenticatable) # ストラテジーの登録

定義されているメソッドは2つ、valid?authenticate!です。
valid?はオプショナルなメソッドなので、あってもなくても大丈夫です。あった場合はauthenticate!の前に動き、
結果がtrueの場合だけauthenticate!を実行します。

authenticate!は必須のメソッドです。
authenticate!の中でsuccess!もしくはfail!を呼び出すことで認証の判定が可能になります。
他のメソッドはこちらです。

これだけで、認証の判定は完成です。
次にmodelに任せた認証ロジックの部分です。

model

次はmodelにロジックを作ります。
modelは最初なくても大丈夫かなーと思っていましたが、ないとないよーってエラーを出しているようでしたので、どうやらいるようです。
そしてこのmodel、自動的にこのストラテジーをdevise :foo_authenticatableのように宣言したモデルにincludeされるようです。
なので、このmodelに実際のロジックを書いていきます。

  • models/foo_authenticatable.rb
require Rails.root.join('lib/devise/strategies/foo_authenticatable')

module Devise
  module Models
    module FooAuthenticatable
      extend ActiveSupport::Concern
            
      module ClassMethods
        def authenticate(attributes)
          # 認証ロジック
          # 仮にデータベースのidで認証
          res = find_by(id: attributes[:id])
          if res.present?
            res
          else
            nil
          end
        end
        
      end
    end
  end
end

最低限はこんな感じです。
extend ActiveSupport::Concernをし、module ClassMethodsを使うと簡単にクラスメソッドが定義できるので、綺麗ですね。
認証が成功したら、インスタンスを返してあげます。後ほどそれがstrategyのsuccess!に渡され、認証ユーザーとなります。

もしaccessorなどを足したい時はincludedを使ってあげましょう。

initializer

最後にinitializer/devise.rbに独自のauthenticationを登録するための処理を書きます。

  • config/initializer/devise.rb
Devise.setup do |config|
  config.warden do |manager|
    manager.default_strategies(scope: :user).unshift :foo_authenticatable
  end

  # ...
end


Devise.add_module(:foo_authenticatable, {
  strategy: true,
  controller: :sessions,
  model: 'devise/models/foo_authenticatable',
  route: :session,
})

これで完了です。
もし独自のストラテジーを作りたくなったらやってみてください。

angular2でelasticsearchの生ログ検索ページを作る

初めまして。アドテクdivのswfzです。

少し前に流行った fluentd + elasticsearch + kibanaでアクセスログを可視化、チームでもやっていましたが集計したグラフはみれるしめちゃいい!けど実際の生データを検索かけるのはちょっと操作が面倒ですよね。

f:id:AdwaysEngineerBlog:20170324155659p:plain

今回はangular2を使ってelasticsearchの生ログを検索できるSPAを作成してみました。

今回は作ったSPAの実際の動作と実装を軽く紹介させていただきます。

※データはダミーデータ生成用のgemがあるのでそれを使わせてもらいました。

動機

  • アクセスログの量が重いと調査でgrepとかする際時間がかかりストレスがマッハ(ノ`Д´)ノ.:・┻┻)
  • ログをelasticsearchに突っ込めばファイルから検索するより早く検索できる
    • 集計したグラフも見れて二度おいしい
    • ただ、検索クエリ覚えるのつらい
  • ディレクターや非エンジニアの方でもログ調査くらいなら出来るようになればお互いハッピー
    • エンジニアは作業を中断しなくて良く余計なコミュニケーションコストがなくなる
    • ディレクターはわざわざエンジニアに調査依頼せず自分で調査できる

elasticsearchにはRESTAPIがあるので直接リクエストを投げて問い合わせることで簡単にデータを検索することができます。

ということで、angular2を使ってelasticsearchのRESTAPIを叩くSPAを作ってみました。

angular2を使ったのは今後業務で使っていくので勉強し始めたからといった理由です。

ページ数が少なく簡単な作りになると思うのであえてangular2でやる必要はない気がしますが今回は気にしないことにします。

動作

それでは、実際の動作を見てみます。

各項目に対して検索条件を入力します。

f:id:AdwaysEngineerBlog:20170324155840p:plain

色々入力して検索すると

f:id:AdwaysEngineerBlog:20170324155849p:plain

こんな感じで結果が出てきます

クエリパラメータも確認することもあるなと思ったのでセルをクリックすることでクエリパラメータの一覧を見れるようにしました。

f:id:AdwaysEngineerBlog:20170324155906p:plain

大まかな流れはこんな感じです。

実装

次は実際に処理の流れを追っていきます。

angular-cliで雛形の作成

angular-cliを使ってアプリケーションの雛形を作成します。

  • インストール
npm install -g angular-cli rxjs
  • プロジェクトの作成
ng new ngapp --style=scss
cd ngapp
ng serve --host=0.0.0.0

これだけで動くものが作れてしまいます。簡単ですね。

  • 構成

下記のファイルたちが生成されました。

angular-cli.json
package.json
tslint.json
protractor.conf.js
src
|-- app
|   |-- app.component.html
|   |-- app.component.scss
|   |-- app.component.spec.ts
|   |-- app.component.ts
|   `-- app.module.ts
|-- assets
|-- environments
|   |-- environment.prod.ts
|   `-- environment.ts
|-- favicon.ico
|-- index.html
|-- main.ts
|-- polyfills.ts
|-- styles.scss
|-- test.ts
`-- tsconfig.json

ag-gridの導入

検索結果にgrid表示ができるライブラリを使用します。

有料版もあるのですが、無料の分だけでも十分使えると思います。

公式にサンプルが沢山あるので導入方法やグリッド部分の実装に関しては割愛させていただきます。

Javascript Datagrid

www.ag-grid.com

Angular Datagrid

www.ag-grid.com

コンポーネントの実装

今回は二つのタイプのログを検索できるようにしたかったので検索条件フォームのコンポーネントは二つ用意します

コンポーネント構成は図のようにしました

f:id:AdwaysEngineerBlog:20170324155934p:plain

実際のファイル構成は下記にしました

|-- components
|   | # 検索条件を入力するフォーム(access log)
|   |-- access-log
|   |   |-- access-log.component.html
|   |   |-- access-log.component.scss
|   |   |-- access-log.component.ts
|   |   `-- access-log.service.ts
|   |-- ag-grid
|   |   |-- ag-grid-cell
|   |   |   | # ag-gridのセル用(jsonデータ整形)
|   |   |   |-- ag-grid-cell-json-data
|   |   |   |   |-- ag-grid-cell-json-data.component.html
|   |   |   |   |-- ag-grid-cell-json-data.component.scss
|   |   |   |   `-- ag-grid-cell-json-data.component.ts
|   |   |   | # ag-gridのセル用(リクエストパラメータ整形)
|   |   |   |-- ag-grid-cell-search-params
|   |   |   |   |-- ag-grid-cell-search-params.component.html
|   |   |   |   |-- ag-grid-cell-search-params.component.scss
|   |   |   |   `-- ag-grid-cell-search-params.component.ts
|   |   |   `-- index.ts
|   |   | # 検索結果表示用
|   |   |-- ag-grid.component.html
|   |   |-- ag-grid.component.scss
|   |   `-- ag-grid.component.ts
|   | # 検索条件を入力するフォーム(twitter api log)
|   `-- twitter-api
|       |-- twitter-api.component.html
|       |-- twitter-api.component.scss
|       |-- twitter-api.component.ts
|       `-- twitter-api.service.ts

データバインド

検索条件フォームのコンポーネントから検索結果表示用のコンポーネントへ片方向のデータバインドを行っています。

まず子コンポーネントを呼び出す側から見ていきます。

  • src/app/components/access-log/access-log.component.html(検索条件フォーム)
<div class="container">
  <app-ag-grid [searchedData]="searchedData"
               [isSearchingToggle]="isSearchingToggle"
               [columnDefs]="columnDefs">
  </app-ag-grid>
</div>

app-ag-gridタグで子コンポーネントを呼び出します。

左辺が子コンポーネントで扱う値、右辺が親コンポーネントで持っている値です。

子コンポーネントは下記のようなコードになっています。先ほどのapp-ag-gridタグはselectorで定義された値です。

このselectorを他のhtmlで記述すればこのコンポーネントを呼び出すことが出来ます。

  • src/app/components/ag-grid/ag-grid.component.ts
import {Component, Input, OnChanges,AfterViewInit,SimpleChanges} from '@angular/core';
import {GridOptions} from "ag-grid";

@Component({
  selector: 'app-ag-grid',
  templateUrl: './ag-grid.component.html',
  styleUrls: ['./ag-grid.component.scss']
})
export class AgGridComponent implements OnChanges, AfterViewInit{
  @Input() searchedData: any;
  @Input() isSearchingToggle: boolean;
  @Input() columnDefs: any;

  private gridOptions: GridOptions;

  constructor(
  ) {
    this.gridOptions = <GridOptions>{
      enableSorting :true,
      enableFilter :true,
      enableColResize :true,
      rowHeight :50,
      enableCellChangeFlash :true
    };
    this.gridOptions.columnDefs = this.columnDefs;
    this.gridOptions.rowData = [];
  }

  ngAfterViewInit() {
    this.gridOptions.api.setColumnDefs(this.columnDefs);
  }

  ngOnChanges(changes: any) {
    if ( this.gridOptions.api ) {
      if ( changes.searchedData ) {
        this.gridOptions.api.hideOverlay();
        this.gridOptions.api.setRowData(this.searchedData);
      }

      if ( changes.isSearchingToggle ) {
        this.gridOptions.api.showLoadingOverlay();
      }
    }
  }
}

検索結果表示用のコンポーネントでは親コンポーネントから@Inputでデータを受け取ります。

受け取ったデータに変更があったらngOnChangesライフサイクルフックを用いてグリッドのデータを更新します。

また、初期のカラム定義this.columnDefsを親コンポーネントから受け取るようにしたためag-gridのapiを使ってカラム定義を更新する必要があります。

しかしag-gridのapithis.gridOptions.apiがviewを生成してからでないと使うことが出来ないためngAfterViewInitライフサイクルフックを用いてビューの生成後にカラム定義を行うようにしています。

またthis.columnDefsの定義次第でag-gridの各セルに対してもコンポーネントを用意することで自由に表現することが出来ますが、ここでは説明を割愛します。

elasticsearchへのクエリ組み立て

elasticsearchへのリクエストを送る部分は共通で使えるのでserviceディレクトリを作成しそこに実装します。

このファイルではelasticsearchへクエリを投げる処理のみを行います。

`-- services
    `-- es-search.service.ts

実際のクエリの中身は呼び出し元の親コンポーネントのディレクトリでservice.tsを作成しそこで対象のログに合わせた関数を書きます。

  • src/app/components/access-log/access-log.service.ts
.....
.....
.....
  buildRequestBody(params: any): any {
    let bodyParams = { "size": params.size };

    let paramsCount = Object.keys(params).filter(k => params[k].length > 0).length;
    if (paramsCount > 1) {
      bodyParams["query"] = {"bool": {"filter": []}};
    }

    if (params.code) {
      if (params.not_code){
        bodyParams["query"]["bool"]["filter"].push(
          {
            "bool": {
              "must_not": {
                "terms": {
                  "code": params.code.split(',')
                }
              }
            }
          }
        )
      }else{
        bodyParams["query"]["bool"]["filter"].push(
          {
            "terms":
              { "code": params.code.split(',') }
          }
        );
      }
    }
.....
.....
.....
    return bodyParams;
  }

boolクエリやfilterクエリを用いて複数の条件を入力してもよしなに検索してくれるようにjsonを組み立てます。

多分この作業が一番大変な気がします…

検索処理

最後に検索ボタンを押した際に呼ばれる関数searchを実装します。

accessLogServiceからリクエストに必要なパラメータを取得してきてesSearchServiceでelasticsearchへクエリを投げます。

返ってきたデータをインスタンス変数に格納し、view側で表示させます。

データバインドの項で紹介したように、検索結果表示用のコンポーネントにデータバインドしているので親コンポーネントのデータが変わったらその都度子コンポーネント側で更新してくれるような実装になっています。

  • src/app/components/access-log/access-log.component.ts
.....
.....
.....
  private columnDefs: any;
  private searchedData: any = [];
  private totalCount: number;
  private displayCount: number;
  private isSearchingToggle: boolean = false;

  constructor(
    private esSearchService: EsSearchService,
    private accessLogService: AccessLogService
  ) { }

  ngOnInit() {
    this.columnDefs = this.accessLogService.columnDefs;
  }

  search(params: any): void {
    this.isSearchingToggle =  ( this.isSearchingToggle ) ? false : true;

    let pathName  = this.accessLogService.getPath(params);
    let jsonQuery = this.accessLogService.buildRequestBody(params);
    this.esSearchService.search(pathName,jsonQuery).subscribe(
      data => {
        this.searchedData = data.hits.hits.map(row => row._source);
        this.totalCount   = data.hits.total;
        this.displayCount = data.hits.hits.length;
      },
      error => {
        console.log('search error');
      }
    );
  }
}

以上の流れでelasticsearchへクエリを投げてデータを表示するといった処理を実装することが出来ました。

まとめ

初めてのangular2ということで、入門書片手にangular2を触ってみました。

今までフロントエンドに対して避けてきた部分があったのですが、angular2はてとても楽しく実装を進めることができました。

感覚的にですが、サーバサイドのコードを書く感覚で書くことができるからかなと思います。

また機会があれば何か書きたいと思います。

下記サンプルです

swfz/angular2-es-raw-search: raw log search from Elasticsearch

github.com

ちょっとRubyの中を覗いてみよう

久保田です。

ある程度プログラムを書いていると、ふと、これはなぜ動いているんだろう。。。ただの文字列じゃないか。。。と思うことがあると思います。

というわけで今回はちょっとだけ、Rubyがどのように動いているかを覗き見してみます。
(Rubyがどういった規則で構文解析をしているかなどには触れません。あくまでYARVに渡されるまでの流れを追っていきます。)

例えば以下のようなRubyのプログラムです。

  • foo.rb
result = 1 + 2
puts result

誰でも3が出力されるとわかるようなプログラムですが、$ ruby foo.rb と動かし、3を出力する間に何が起きているでしょう。

実はRubyでは、渡されたプログラムをそのまま実行しているのではなく、 Rubyというプログラムが理解できる形に変換してから実行しています。

そして変換は、字句解析 -> 構文解析 -> コンパイル という3つのステップを経て変換されていきます。

今回はこの3つのステップを少しずつ追っていきたいと思います。

字句解析

まずは字句解析です。
字句解析はただの文字列であるプログラムを トークン という意味のある単語に分ける仕事をします。
foo.rbの一行目でいうと、
resultという変数 / = という演算子 / 1という数字 / +という演算子 / 2という数字 に分けていきます。
(空白や改行などもトークンとして認識しますが、一旦無視します。)

それでは、実際にfoo.rbを字句解析してみましょう。
Rubyには標準ライブラリとしてRipperというものがあり、Ripperを使うと字句解析や構文解析の結果を渡してくれます。

require 'ripper'
require 'pp'

code =<<EOF
result = 1 + 2
puts result
EOF

pp Ripper.lex(code)

結果

[[[1, 0], :on_ident, "result"],
 [[1, 6], :on_sp, " "],
 [[1, 7], :on_op, "="],
 [[1, 8], :on_sp, " "],
 [[1, 9], :on_int, "1"],
 [[1, 10], :on_sp, " "],
 [[1, 11], :on_op, "+"],
 [[1, 12], :on_sp, " "],
 [[1, 13], :on_int, "2"],
 [[1, 14], :on_nl, "\n"],
 [[2, 0], :on_ident, "puts"],
 [[2, 4], :on_sp, " "],
 [[2, 5], :on_ident, "result"],
 [[2, 11], :on_nl, "\n"]]

配列の入れ子でプログラムが トークン に分けられていますね。
一つ目の配列を見て、構成を見てみます。

[[1, 0], :on_ident, "result"]

配列の最小の要素に、プログラム中の[何行目, 何文字目]かが格納されています。
次に、このトークンの種類が入っています。
最後の要素には実際に使われている文字列が入っています。

字句解析はこのように行なわれて、意味のある文字列に分けられているようですね。
ただこの状態では、まだ文としては意味を持っていません。
なので、字句解析の後、Rubyはこの分けられたトークンを元に構文解析を行います。

構文解析

構文解析では、意味のある文字に分けられたRubyプログラムを意味のある文にグループ化し、ASTノードにしていきます。つまり、 パース をします。
パースは、パーサジェネレータを使って行います。Rubyではbisonを使ってパーサジェネレータを作成します。

早速どのような動作をするか確認してみます。

require 'ripper'
require 'pp'

code =<<EOF
result = 1 + 2
puts result
EOF

pp Ripper.sexp(code)

先ほどの字句解析のRipper.lexをRipper.sexpに変更しただけです。

結果

[:program,
 [[:assign,
   [:var_field, [:@ident, "result", [1, 0]]],
   [:binary, [:@int, "1", [1, 9]], :+, [:@int, "2", [1, 13]]]],
  [:command,
   [:@ident, "puts", [2, 0]],
   [:args_add_block, [[:var_ref, [:@ident, "result", [2, 5]]]], false]]]]

先程よりはちょっと複雑な入れ子の配列になっています。
これがASTノードと呼ばれる構造です。
一部を抜き出すと、こういった構造ですね。

f:id:AdwaysEngineerBlog:20170317135428p:plain

少しだけ見ていきます。
まず、配列の最初の要素に:programと書かれています。始まるよー!的な意味ですね。
そして同じ階層のもう一つの要素は配列になっており、その配列の要素は二つありどちらも配列です。
その配列の構造も[シンボル: [配列]]となっているようです。

ここではASTに関しては詳しくは述べませんが、
それぞれの配列の最初のシンボルになっている要素が グループ化された文の種類 を表し、後に続く要素が配列の場合はさらにグループ化されています。 そして続く要素が配列でなくなった時に解析が終了しています。

このように 意味のある文字列(トークン) 意味のある文(ASTノード) にしています。
ここまで来てやっと、Rubyはどのようにただの文字列であるプログラムをどのように扱っていいかがわかるというわけです。

実は、このRuby1.9以前はこの状態まででプログラムの変換は終了だったそうです。
しかしこのままでは実行速度に問題あったらしく、Ruby1.9から笹田耕一さんにより開発されたYARVというRubyの仮想マシンが組みこまれました。

そのYARVがプログラムを実行するため、先ほどの構文木をコンパイルし、YARVが実行できる形式に変換します。

コンパイル

3つのステップの最後、コンパイルです。
早速やってみましょう。

code =<<EOF
result = 1 + 2
puts result
EOF

puts RubyVM::InstructionSequence.compile(code).disasm

結果

== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1)
[ 2] result
0000 trace            1                                               (   1)
0002 putobject_OP_INT2FIX_O_1_C_
0003 putobject        2
0005 opt_plus         <callinfo!mid:+, argc:1, ARGS_SKIP>
0007 setlocal_OP__WC__0 2
0009 trace            1                                               (   2)
0011 putself
0012 getlocal_OP__WC__0 2
0014 opt_send_simple  <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0016 leave

さぁ、ついに意味がわからなくなってきました。笑
正直YARVの処理はまだあまり理解していません。。。
YARVの話だけで一ヶ月ブログ書けるくらいのボリュームだと思っています 。。

かろうじてわかるのは、
0007 setlocal_OP__WC__0 2
で変数をセットして、
0012 getlocal_OP__WC__0 2
で呼び出し、
0014 opt_send_simple <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
でputsメソッドを呼び出しているのだろう、ということくらいですね。笑

数字の後に続いているのがYARV命令というやつで、YARVが何をするかはここで決めています。

ここら辺の話はまた今度できたらな、と思います。

まとめ

Rubyは、プログラムを渡されると実行までに上記の3ステップの長〜い道のりを行くんですね。
いつも何気なく動かしているプログラムですが、ものすごく高度な技術が折り重なってできているんですね。
皆さんも是非自分のプログラムを覗き見してみてください。

参考文献