u16suzuの blog

日々学んだことのメモブログです。

2016/8/2(tue)の日記

shell起動時に自動的に指定のディレクトリに移動するようにした。

毎朝仕事を開始する時に、いつも仕事用のディレクトリにcdで移動するのが大変なので書いた。

以下のように .open_directoryディレクトリを指定すると、shell 起動時に移動してくれる。

$ cat ~/.open_directory
/Users/u16suzu/workGithub/rails_project
### 起動時に .open_directory ファイルで指定された directory に自動で移動する
function auto_move(){
  cd "`cat ~/.open_directory`"
}
auto_move

2016/8/1(mon)の日記

プロジェクトで使っている bundler で入れたgemの調査をするためのコマンドをbashで書いた。

Ruby 書くときは atom がメインエディタになりつつある。

RubyMineは重いので、コードリーディングの時だけ使っている。

### bundler でインストールした gem を編集したり、元に戻したりするコマンド
# gem の内部動作を調査する際に使う
# どちらのコマンドも操作対象のgemを引数で指定する必要がある
function gem_edit(){
  atom `bundle show $1`
}
alias gem_edit="gem_edit"
alias gem_restore="bundle exec gem pristine"

ただ、一つだけ問題があって、gemを書き換えた後に rails consoleを再起動しないと読み込んでくれない。

pry

binding.pry を書くと、gemのコードの中で処理を止めてpryで変数の中身を見たりできる。

Arelの定義位置を調べたい時、pryコンソールで show-source Arel とすると、arel のコードがどこで定義されているのかを調査することもできる。

2016/7/31(Sun)の日記

今日はマクドRuby script 用のDSLを書いて遊んだ。

仕事でよく使うテスト用のスクリプトなんだけど、実行するタスクを引数で指定できるようになっている。

それをDSLで指定できるようにした。

@argv = [ {env: "pro"}, 'login', 'start' ]
@env = "dev"

p ARGV

def debug(a)
  puts "---\n#{a}\n---"
end

def before_all(&block)
  @before_all = block
end

def after_all(&block)
  @after_all = block
end

def env(name, &block)
  @env = @argv.select{ |o| o.is_a?(Hash) }.first[:env]
  yield(block) if @env == name
end

before_all do
  debug 'before_all'
end

after_all do
  debug 'after_all'
end

def task(name, &block)
  @before_all.call if @before_all != nil && @argv.include?("all")
  yield block if block_given? && (@argv.include?( name ) || @argv.include?('all') )
  @after_all.call if @after_all != nil && @argv.include?("all")
end

env 'test' do
  debug 'test'
end

env 'stg' do
  debug 'stg'
end

env 'pro' do
  debug 'pro'
end

task 'login' do
  debug 'login'
end

task 'start' do
  debug 'start'
end

task 'finish' do
  debug 'finish'
end

2016/7/28(木)の日記

jbuilderについて

extract! # activerecord objectのアトリビュートを指定して書き出し
ignore_nil! # このメソッドを書いた後は, nil のkey-valueをjsonに書き出さない. ignore_nil! false でオフにできる.

Splat演算子

a, b = *[1,2] # => a = 1, b = 2
*c = 1,2,3 # => c = [1,2,3]

Rubyで、配列を展開するときに使う *array みたいな演算子 * を Splat演算子と呼ぶ.

上の動作は知っていたけど、下の動作は気づいていなかった.

展開後の値を代入で指定しているわけですね.

メソッドの引数にハッシュを指定するときのつまづきポイント

[42] pry(main)> fuga a: 123
=> {:a=>123}
[43] pry(main)> fuga {a: 123}
SyntaxError: unexpected ':', expecting '}'
fuga {a: 123}
[43] pry(main)> fuga( {a: 123})
=> {:a=>123}

メソッドの引数にハッシュを指定したいときに, fuga {a: 123} と書くとエラーになる。

これは、Rubyが引数の { をブロック引数の { として解釈してしまうため発生する。

fuga({a: 123})()明示的に引数指定をすればエラーにならない

はじめ、Splat演算子と関係あるのかなと思ったけど、関係なかった。

今回、仕事で久しぶりに遭遇して、同僚のA氏に昔教えていただいたことを思い出した。

テスト周りで久しぶりにハマった

BaseController の rescue_from で例外の出力処理を書いていなかったため

テストは落ちるけど、スタックトレースが出力されず、原因の特定に困った。

いわゆる例外の握りつぶしに意図せずになってしまっていた。

これから rescue_fromStandardError を補足する場合は注意したい。

2016/7/27の日記

Rubyのメソッドに関する雑多なメモ

<< # 右ビットシフト `*2` と同じ意味
>> # 左ビットシフト `/2` と同じ意味
| # 論理和演算子. 0011|0101 => 0111
8.to_s(2) #=> "1000" : 2進数表現の文字列を取得
"0x4d2".hex #=> 1234 : 16進数を10進数に変換. 0xはなくてもあっても良い.

super

Rubyのsuperはオーバーライドしているメソッドを呼び出す. 注意点として、引数を省略してsuperとコールすると、全ての引数を受け渡してコールしてしまう. super() と明示的に()をつけてコールすると、引数を渡さない. このことは Effective Ruby でも書かれていた.

Effective Ruby

Effective Ruby

クラスマクロを自分で定義してみる.

クラスマクロを使うことで、 attr_accessor みたいなメソッドを定義できる. 完全にクラスメソッドだけど、こういう attr_accessorっぽい宣言的な構文で記述が出来る奴をクラスマクロと言うらしい. Railshas_many, belongs_to もクラスマクロだ.

class User
  attr_accessor :name

  def self.attr_special_accessor( *key )
    p key
  end

  def self.before_hoge_hoge(key)
    p key
  end

  def self.after_hoge_hoge(k)
    p k
  end
end

class Boy < User
  attr_special_accessor :name, :age
  before_hoge_hoge :foo
  after_hoge_hoge :bar
end

このQiitaの記事がとても参考になった.

「Rubyのしくみ」(Ruby Under a Microscope)を読んだ

Rubyのしくみ」(Ruby Under a Microscope)を読みました。

Rubyのしくみ -Ruby Under a Microscope-

Rubyのしくみ -Ruby Under a Microscope-

Rubyの内部における実行の流れを調べる

字句解析、構文解析コンパイルが実際にどう行われているかを Rubyに添付されている、Rubyプログラムのパーサである Ripperクラス で調査することができます。その方法を説明します。

Ruby実行の流れは 1.8.xと1.9.xで大きく異なります。 1.9.xからYARV(Yet Another Ruby Virtual Machine)が導入されました。

  • v1.8.X

[Rubyコード] 字句解析 => 構文解析 => 実行

  • v1.9.Xより上のバージョン

[Rubyコード] =字句解析=> [トークン列] =構文解析=> [AST] =コンパイル=> [YARV命令文] => YARVで実行される

ASTは構文解析木(Abstract syntax tree)の略です。 まずは字句解析からです。 Rubyソースコードの読み込み処理です。

字句解析

require "ripper"
require "pp"

code = <<STR
10.times do |n|
  puts n
end
STR

puts code
pp Ripper.lex(code)
  • 実行結果
[[[1, 0], :on_int, "10"],
 [[1, 2], :on_period, "."],
 [[1, 3], :on_ident, "times"],
 [[1, 7], :on_sp, " "],
 [[1, 8], :on_kw, "do"],
 [[1, 10], :on_sp, " "],
 [[1, 11], :on_op, "|"],
 [[1, 12], :on_ident, "n"],
 [[1, 13], :on_op, "|"],
 [[1, 14], :on_ignored_nl, "\n"],
 [[2, 0], :on_sp, "  "],
 [[2, 2], :on_ident, "puts"],
 [[2, 6], :on_sp, " "],
 [[2, 7], :on_ident, "n"],
 [[2, 8], :on_nl, "\n"],
 [[3, 0], :on_kw, "end"],
 [[3, 3], :on_nl, "\n"]]

続いて、構文解析です。

構文解析

require "ripper"
require "pp"

code = <<STR
10.times do |n|
  puts n
end
STR

puts code
pp Ripper.sexp(code)
  • 実行結果
[:program,
 [[:method_add_block,
   [:call, [:@int, "10", [1, 0]], :".", [:@ident, "times", [1, 3]]],
   [:do_block,
    [:block_var,
     [:params, [[:@ident, "n", [1, 12]]], nil, nil, nil, nil, nil, nil],
     false],
    [[:command,
      [:@ident, "puts", [2, 2]],
      [:args_add_block, [[:var_ref, [:@ident, "n", [2, 7]]]], false]]]]]]]

最後にYARV実行命令です。

コンパイル(YARV命令を表示)

code = <<END
puts 2+2
END

puts RubyVM::InstructionSequence.compile(code).disasm
  • 実行結果
10.times do |n|
  puts n
end
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
0000 trace            1                                               (   1)
0002 putself
0003 putobject        2
0005 putobject        2
0007 opt_mult         <callinfo!mid:*, argc:1, ARGS_SKIP>
0009 opt_send_simple  <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0011 leave

KPTふり返りのテンプレート

最近、MTGのファッシリテーションをするようになったので、すぐに使い回せるようにテンプレート化しておく。

スケジュールのメール文フォーマット

お疲れ様です。 以下のフォーマットで、KPTふり返りMTGを行います。 もし、KPTに関してご存じない場合、参考資料に目を通しておいてください。

会議フォーマット

  • 全体で60分
  • 時間を計測しながら行う

タイムスケジュール

  • はじめのことば、 KPTのやり方の説明 5分

  • K 書き出し 5分

  • K ディスカッション 10分

  • P 書き出し 5分

  • P ディスカッション 15分

  • T 書き出し 5分

  • T ディスカッション 15分

参考資料