RubyKaigiに行ってきた(3): 2日目

先週いっぱい忙しかった上に割と不調だったのでこれは本格的に崩れそうと思ったら日曜くらいから体調崩したので予定通りの更新は諦めました。あとノート整理して載せるのも諦めました。ほぼまんまです。

Coming soon...

Matzのキーノート。Rubyの未来について。ずっと内容についてはサイトに掲載されていなかったけれど本当にComing soon...というタイトルで持ち込んできた(!)。
最近はstatic typingのない言語はダメだみたいな風潮が強まっている気がしてきていて、Rubyもその圧にさらされているのは例外ではないようですが、なんでダメかっていう話になったときにパフォーマンスの話がよくされることについて、JavaScriptがV8とかであれだけパフォーマンスが出るようになってきたことを考えるとパフォーマンスの問題に持っていくのはどうなんだろう…と思っていたところやっぱりRubyコアのほうでもそういう話は出ているらしい。 個人的には型付けに関して厳密でありすぎるとそれはもはやRubyとは言えない何かになりそうと思うので、必要なところに型付けの恩恵が得られるようになってそれ以外は自己責任で便利で強力な機能を使える今のRubyのあり方が保たれるようだといいなぁ。

ちなみに前日に通訳打ち合わせにご案内したのだけど、恐縮しきってろくに話すことができませんでした(汗)。


タイトル「Coming Soon...」、プログラムに嘘は書いてなかったと…(一同爆笑)

何が来るか?→未来。

スポンサーのご意向で宣伝しないといけないので…
「プロフェッショナルのための実践Heroku入門」出版されました。まだ読んでないけど私でもHeroku使えるようになるかも。 ちなみに私Herokuアカウントもってないんですけど(爆笑)。
Ruby2.2.0-preview1、Herokuでさっそく使えるようになりました

未来が来る。
あちこちでキーノートしてます。過去にも未来の話をしてきた。

RubyConf 2001: rubyカンファレンスで最初に行われた。フロリダのタンパ。11月。9/11のあと。炭疽菌が話題になってた。 家族みんなが止めたけど大丈夫だった。
キーノートでは: VMを作るよ!と言ってた。
-> 1.9(2007)で。6年越し。
2001年時点でVMのプロトタイプを書いてた。VMは簡単なものを書くのは簡単だけど(昨日のささださん)、ちゃんとしたものが動くまでは大変。挫折した。
Yet Anotherとついたものは生き残る、というジンクス -> ほんとに生き残った。現実化しました。

RubyConf 2002: Ruby 2について話した。

  • M17N - multilingualization : 2007
  • Native thread (vs user level) : 2007
  • Generational GC : 2013 V2.1

RubyConf 2003: Ruby 2についてagain

  • local variable scopeのルールをかえよう : x
  • multiple assignmentのルールを変えよう : 1.9
  • method visibilityについても検討しよう : x
  • keyword argument 2.0
  • method combination 2.0
  • selector namespace 2.0
  • optional --

x のもの: 互換性を決定的に破壊しない限り入れられない。
Optional static typingはご存知のとおりまだ。

RubyConf 2004: 欠席

娘が生まれた。一生なんか言われそうだったのでやめた。ささだ先生がYARVの発表をした。

RubyConf 2005: またRuby2

  • Stabby Lambda (->) : 1.9
  • Real multi-value : --
  • Traits : --

stabby lambda、評判悪かった

RubyConf 2006: またry

"Bikeshed argument encouraged"

no new ideas

RubyConf 2008

Rubyの哲学を紹介 何も新しいアイデアは紹介せず

RubyConf2009

DSLの力について話した

Kaigi2009

  • Complex literal : 2.1
  • Rational literal : 2.1
  • True division : --
  • Bitmap marking : 2.0
  • Symbol GC : 2.2
  • Mix(traits) : --
  • Module#prepend 2.0
  • Refinements 2.0
  • mruby(Rite) : 2012

2011-14 conf: no new ideas

たくさんのアイデアについて未来の言語に入れたいと語ってきた。

false rate 7/22 := 32%
1/3は言っただけ

2011-14までは何も話してない。
コアチームの人はRuby2をどうするかについて、また安定化について改善を行ってきたが、「未来のRubyについて」はここ何年かしてこなかった

しかしOSSコミュニティはサメのようなもの、泳ぎ続けないと死ぬ > そろそろ燃料を投下しなければいけないのでは?ヨタ話しないといけないのでは?

-> Ruby 3.0の話をしよう。10年以内にこうなるよ:

このへんについて考えないといけないなあ

  • Concurrency
  • JIT (LLVM?)
  • Static Typing

Static typing?

All new kids in the street: Scala, TypeScript, Dart, Go
20cの言語、スクリプト言語は変数に型のない言語が多い。dynamic typing。

悔しいのでRubyもしたい

Feature #9999 by Davide D'Agostino
Type Annotations

def connect(r -> Stream, c -> Client) -> Fiber
  ...
end

Python PEP #3107: Function annotation : やっぱこんな感じ
rubyだとcolonつけちゃうとkeyword variableになっちゃうので…

Pythonは書いても型チェックしない。static typingとは一言もいってない。ドキュメント。
処理系に任せる、言語としては知らん。Dartにもそんなモードがあるみたい

mypy: optional static type checker

why static typing? :

  • Performance
  • Compile-time check
  • Documentation

このために導入したいのでは。

パフォーマンスのために静的型は必要か?V8, LuaJIT...これらは動的型だけどパフォーマンスはいい。どれだけの労力・資源を費やしたかに比例するのでは?
動的型でパフォーマンスを出すことは可能。パフォーマンスのために静的型は要らない。思い込みに近い。

Compile-time checkは?
static analysis
多くのバグは型の矛盾によって起こる リファクタリング漏れとかも検出可能

型を導入すると柔軟性が減る

against duck typing

(ここで一旦腹痛につき離脱)

"どっかのPで始まる言語みたいにver5とver6で違う言語って言いたくない"

best-effort type checking

based on duck typing

soft typing

Type is represented by:

  • Set of methods
    • name
    • number of type-parguments
  • Class name

targets subset of language

subsetというと機能制限版というイメージをもたれがちだが 対象となるクラスが狭いという意味

  • require : 変数使われるとtype checkできない
  • define_method : これもできない
  • method_missing : 同様

これらには制限がかかる

(再度腹痛につき離脱)

Extreme Makeover: Rubygems Edition

RubyGems.org と Bundlerの話。BundlerのアップデートによりRubyGems.orgがパンクしてしまってどうやって直したか、という話。
個人的にはLocal mirrorが作れるようになるというのが面白いと思った。大量にインスタンス立てて同時にデプロイ走らせるときとかにローカルミラーを参照してそこだけが必要なGem拾ってきて保存し残りはそこを参照、ってなると外との無駄な通信が抑えられて良さそう。あとプロキシ超えがめんどくさいときとか…


@indirect
CloudCity development

Bundler

Bundler DDoS on rubygems.org
totally unexpected ><
people updating bundler -> server died

last year: why that happened, how we fixed ..

security breach: how to upload a gem that run code on rubygems.org

rebuilt rubygems.org
runs on EC2, chef scripts public

Travis network issues, DNS issues
DNS hardcoded... requests very slow... gems on east coast, west coast users' traffic travelled across company

SSL failures
no obvious fixes...

rebuilt rubygems.org again!
original was functional but hard to maintain
rewrote chef scripts
everything seems to be working

Bundler Source CVE
if you have two different sources in gemfile, if you use gem that exists on both, you won't know which server you will get gem from
four months to fix this

Bundler 1.7 released
Bundler 1.7.2 released...

How it works today

with a fast connection, at least
talks to bundler API
this version of rails depends on this activesupport, ...

Gemspecs
what platform is it, what ruby, what rubygems ver, ...

before: Marshalled Gemspecs
-> list of gemspecs: specs.4.8.gz (names of gemspecs)
-> then the bundler api: tons of network traffic

could be better

we have a plan

simple plaintext indexes
no yaml security issues
parse with strain and split

client cache the indexes

put a CDN on it

easy local mirrors

the new index

/version
/deps/gemname

/versions
rack 1.0.0,1.0.1,1.0.2
sinatra 0.9.6,1.0,1.1.4
rack 2.0.0 <- if new version pushed, add to end of file, then client can ask for latter parts of the file

/deps/sinatra
0.9.6 rack:>= 0.9.1
1.0 rack:>= 1.0
1.1.4 rack:~>1.1,tilt:<2.0&>=1.2.2

what's left

new index from rubygems.org
new index in rubygems
fastly-hosted index files : CDN

things should be alot better!

The present

gem install bundler

Gem mirrors

New resolver! - better errors, faster installs, many bugfixes

github.com/bundler/bundler CONTRIBUTING.md

MRuby as Development Platform for Payments

MRubyを使ってPOS端末のアプリを開発したという話。組み込み機器のアプリケーション開発のプロセスが重くて仕方ないのをMRubyで改善できた、という話。ライブデモもあった。
Rubyコミュニティの広めた習慣としてアジャイル開発などの軽量な開発プロセスがあるがそれを組み込み機器の分野でも適用できるという興味深い事例。デプロイが高速化できることや部分的な修正を入れやすいことによってプロダクションでのバグへの対応が速くなるというのは非常に魅力的。


Thiago Scalone @scalone
ruby and beer
met gf @ rubyconf

Daniel Rodriguez @sadasant
JS dev
not related to sasada-san

cloudwalk (not crowdworks) payments

What is an "embedded system?"

computerized system that is purpose-built for its application (Elecia White)

Most of Embedded Systems: No OS, Limited Syscalls, No POSIX, Crazy API, No ANSI C

Scenario: 1000 POS terminals, Bug in production...
Different binary for each terminal

Update manually? Network issues? Energy issues? Transactions? ...

-> There should be an easy way to deploy

Change how we compile the code

IDE that can compile code on the browser

Web IDE <-> Website <-> Cloudwalk servers -> System

POSXML
Structured XML, Static memory

Enough. We need to move forward. Need to go mobile, Need to be developer friendly.

Why not use Ruby as a runtime?

I feel Happy when I code

We want Ruby everywhere
starting with POS terminals

DoItYourself FAILed.

Tried mobile first -> Ruboto
No JRuby core-team, 40 second to boot, No community

then tried RubyMotion
recommend to use rubymotion. beautiful
but only mobile.

we need smaller ruby.
CRuby has too many dependencies

-> mruby!
resource saving, high portability, RiteVM bytecode, static gems, ANSI C, Size

http://www.musl-libc.org

https://github.com/jamruby Java to mruby

MRuby App
da_funk
gem
libc
OS

CloudWalk MRuby Framework

  • Hardware agnostic
  • modular
  • simple

CloudWalk MRuby on the web

emscripten

webruby
basic approach that we started using

  1. new webruby instance
  2. compile_to_file
  3. read bytecode
  4. deploy to server

EMULATOR
POSXML compiler -> now MRuby compiler

browsers run asynchronously
-> use Fiber

call Javascript functions and wait for callbacks

EmScripten is not intended to work in the browser, so memory leaks can happen => keep it clean

JS strings as array buffers are uneasy to handle
Use hex strings or Arrays
Sometimes string arrays

Override default Ruby classes
use Emscripten's API directly

you need workers. -> it would be a shame if it breaks
in some browsers we use workers, some not

demo

verifone, ingenico : 90% share

continuous delivery

NFC (we have it working w/ POSXML) on verifone and ingenico > move to mruby

Open the door of embedded systems to IoT! mruby on LEGO Mindstorms EV3 ®

もう1個引き続きmrubyで。タイトルの通りLEGO Mindstorms EV3でmrubyのプログラムを動かしてみた、ライブデモもあります、という話。
今のMindstormsって中身がオープンソースになってるんですね…。VMまでLEGO社が自作してるってのもすごい。初代を触ったことがあったんですが、その当時から考えると同じ規模のものにちゃんとPC向けのOSが動くような本体が乗ってるってのがすごい進歩。一方でCPUは初代は日本製だったが今やARMに完全にとって代わられてしまった悲しい事実。時代ですね…。
GCのせいでRubyは組み込みダメ」っていうのに対する考察が行われていたのが重要だと思う。機器の構成次第ではRubyでもいけるし、実時間制約の強いところはマイコンでどうにかしてしまい、その上の処理を今まで組み込みエンジニアしかできなかったのがRubyプログラマができるようになる、っていうことで生まれる価値は大きいだろう。


Educational Tool > Toy
STEM Education (Science/Technology/Engineering/Mathematics)

4〜5万します。高い。RasPi10台以上買える。
学校とか社員研修とかロボコンとか
そう考えると4〜5万は安いか?

Mindstormsの歴史
RCX(1st gen): 1998, H8 16MHz, 32KB RAM (当時の日立、現ルネサス)
NXT(2nd gen): 2006, ARM7 48MHz, 64KB
EV3 : 2013, ARM9 300MHz, 16MB (TexasInst製)

iPhone初代が2007。高いけどNXTは今でも使われてる。
去年EV3出たってんで取材にいったら研修でRCX使われてた。

Visual programming environment: EV3の標準
Rubyなくたって小学生がすごいプログラム書く。

remote control: 3代目はリモートコントロールできます。iOS/Android向けオフィシャルアプリある。
パソコン上でSDKを使ってmindstormsをコントロールすることができる。

今回はリモートコントロールじゃなくて単体で動かすことを目的とする。
C#, Javaでもできますよというけどそれはリモートコントロール。EV3のうえで動かすのは

標準ファームウェアLinuxが入ってる。4〜5秒かかる。切るにも4〜5秒かかる。暴走して止めるにはモーター抜くのが一番速い。
VM: LEGO社が作ったバイトコードで動くVM

Extended firmware ev3dev
full Debian7 wheezy
Customized for LEGO Mindstorm EV3
Bootable from microSD/SDHC
source code on GH! can send pull requests!
Available programming language: C/C++, Python, JS(node), Lua, Go, Ruby(CRuby)

初代はclosed sourceだったけどhackerががんばって解析したら2ndから公開するようになった。

IOを直接叩いてしまえばどんな言語でもできるけど…
mrubyのAPIを実装してみました。

Apache, nginxなどに組み込む、という組み込みもあるけど
機器に組み込む、という組み込みもある

Can we use mruby in embedded systems?ほんとに?

  • 実時間制約いける?
  • GCは他の割り込みを止める

ほんとは3本線がほしいんだけどRCXには線が2本しかない!これでどうにかする方法: コンデンサを挟む ソフトウェアを使って充電放電を繰り返す

「mruby使うと実時間制約守れないね!」 > RCXではそう。
EV3では線が太くなった。光を取るモードが3種類ある。
ハードウェアの進化。光センサの中のマイコンが実時間制約やってくれる。ARM9は通信するだけ。実時間制約は光センサのほうのマイコンが勝手にやってくれる。

  • mrubyが向いてない -> ハードウェアの限界に挑戦
  • ハードウェアにマイコン入ってて協調するとかには向いてる。高機能なハードとの連携とかには強いのでは?

Scalable deployments - How we deploy Rails app to 100+ hosts in a minute

クックパッドにおけるデプロイ高速化をどのように行ったかの話。
いちおう継続的デプロイ・デプロイ自動化の行われているチームに所属している身としても、デプロイが高速化されることってどのくらい嬉しいんだろう、というのは思っていたが、投入されるデプロイの回数が増えれば増えるほど高速になることは重要で、チームメンバーも多く開発者がガンガンコミットを入れてくる環境だったらその重要性はさらに高い。でサーバーも多ければ時間はさらに増えるし、そうなってくるとテストの短縮・デプロイの短縮に向けて動く必要があるのも納得である。そのためのツールを作る専門のチームがある、というのも面白い文化だと思った。

んでなんでmamiyaなんだろうと思ったけどあとで鎮守府にログインしてやっと理解した。


140servers, 10times/day

rules on deployment

  • revisions which CI build passes
  • only during work time
  • monitor errors after dep for hour
  • rollback if error rate increase

was:

merge -> CI builds -> CI passes -> tag git repo -> developer checks -> developer ships

deploy server: capistrano2 -> (ssh+rsync) -> App App App

sshが重くなってきたなっていうのは

チャットでdeployするってのは最近やってる
Hubotでデプロイする
ログのコピペを待つよりエラー

how long time spent for deployment?
CI: 10分くらいかかる
目視: 1〜5min
結果15-20min

どのように問題になるか?

  • Capistrano2 -> super historical, complicatedなdeploy script. seemed time to renew
  • 2595行のdeploy scriptがある
  • SSHが重い : high cpu usage on deployment, sometimes fails. たまにこけるというのがマズい。

開発基盤チームとは:improve developers' productivity

改善案

upgrade to capistrano3?
better ssh handling, but still depends on ssh. ssh is slow.

新しいツールを作ろう!
sorah/mamiya

オーケストレーションにserf
file distributionはAmazon S3

serf? -> hashicorp/serf
decentralized
Gossip protocol
ソーシャルに噂が広がるようにイベントを伝搬

sorah/villein
side-effect gem for Mamiya, controls serf from Ruby

master node - has HTTP API to control cluster. sends request to agens via serf. watches agents' status
agent node - accepts requests from master node, runs deploy tasks
deploy script - writes how to build, prepare, and release
package - is a tarball of files to deploy. can be pushed to storage. contains deploy script. deploy scriptの更新も同梱される。
storage - パッケージを置くことができる。標準実装ではS3とか。

Step - is a part of deployment. can be run separately, called remotely

Steps - Fetch package, prepare fetched package(bundle instal etc), switch to prepared package(reload, graceful)

Mamiya's deploy flow

  1. CI builds package
  2. CI pushes package to storage
  3. deployment starts
  4. master sends prepare request to Agents
  5. Agents fetch package, then prepare
  6. Master confirms all agents have prepared
  7. Master sends switch request to agents
  8. agent switches symlinks, then reload app process

sshへの依存性をなくすことができた。

もっと何かあるのでは?

step -> 独立に実行できる

do preparation before developer says deploy
prepareは実行されてるのでappがreloadされる

before: 8.4min
after: 45sec

110servers, 11.2x faster!

(demo)

documentの充実、WebUI,エラートラッキングを充実したい

Write ruby code to change ruby code

Rubyシンタックスツリーを解析してそれを元に新しいシンタックスに書き換えるツール、Synvertの紹介。
これは聞いていて明日にでも使える!という印象が非常に強かった。Rspec2からRspec3への書き換え、Rails2からRails3、Rails3からRails4への書き換えのシンタックス面での変化をこれ1つで吸収できてしまう。強い!
AST解析で構造を保ったまま書き換えるという方法自体も応用の幅が広くぜひともソースを読んでみたいと思った内容だった。あ、オープンなんだからそのうち読もう。


@flyerhzm, Xinmin Labs

Problem

manager asks to use rspec new syntax, developer loves new syntax
for different project, engineer is not excited anymore
another project, ... don't wan to repeat again!

Developers love refactoring code, but hate changing code again and again

Automation!

Metric tools

  • cane
  • reek
  • rails_best_practices

most tell what to do, but they can't do it for you

IDE

can't customize

based on text

Replace FactoryGirl.create with create : sed ...

write ruby code, change based on string ...

hard to be accurate: you should not convert in comments and strings

hard to convert complicated cases: rails 2 style User.find to rails 3 style User.where

based on AST

Why AST -> much more accurate

for instance:

  • (str "FactoryGirl.create") -> ignore
  • (const nil :FactoryGirl) :create -> replace

for rails 2 style User.find -> it is a "send" node, takes a hash arguments

How to use AST

  • ripper (standard ruby library, 1.9)
  • ParseTree
  • ruby_parser
  • parser

based on parser gem

Example : transpec (rspec2 to rspec3 syntax)

solution based on AST is accurate but hard to read and write

Synvert

xinminlabs/synvert

already have syntax snippets

DSL for defining syntaxes

7 years of Ruby & Rails with the same web site

7年間同じRailsサイトを開発・運営してきた上での「つらい話」。実況タイムラインにも「あるある」「これはつらい」みたいな内容がたくさん並ぶ、現場の生々しい話がたくさん聞けた回だった。
特に人が入れ替わっていく中でコードが死んでいく・知識が失われていくという話は非常に耳が痛いとともに今後仕事していく中で間違いなく遭遇するであろう事態なので気をつけなければならないだろう。


"iKnow" @ Cerego
Online English language learning

Super SRS (spaced repetition software) : super flash card system

2007 iknow.co.jp
2008 showed at rubykaigi
2009 smart.fm
2011 iknow.jp
2013 cerego english(@brazil), cerego.com

3 lessons learned

  1. sharing code is hard
  2. custom solutions require effort
  3. people change

1. Sharing code is hard

3 large websites, sharing a common code. how do we do that?

single monolithic rails 1 code => split, Web app, Web API + Shared core

most of the LOC are in core
200000 LOC total

Strategies for sharing

  • git submodule in vendor/plugins
  • rails engine
  • gem

  • bundler doesn't work well

  • editors and tools can't deal with submodules
    • esp your models are not in ordinary rails structure
  • deployment and jenkins builds are complex

-> move to an API-driven websites

3 sites forked from the same code base: means 9 repositories!

failures with forks
rails upgrades are a pain
ruby versions, rubygems, c-extensions, etc.

successes with forks
starting a new site is fast
known codebase, everybody knew how it works, got up with speed
room for divergent changes

other ways to share

  • git cherry-pick
  • gems
  • engines

branches and merges -> diverted too much, conflict fest, gave up

Custom solutions require effort

new comers -> WTF!? why aren't we using common tools!?

https://www.google.co.jp/search?client=safari&rls=en&q=rails+russian+doll+caching&ie=UTF-8&oe=UTF-8&gfe_rd=cr&ei=E9obVIyDH4HH8gf3w4DQBg

too many monkey patches
is it rails? is it ruby? is it us?
vendor/patches > keep all patches in a same directory

when to be custom?
concepts that haven't made their way into rails
owning the core code: performant and specific
domain-specific code

when to move on?
things break, upgrading
too much code builds around private APIs
authors leave the project

Use standard tools
new developers may be familiar with it
can share back to the world with experience

Open Source Custom Stuff

進撃のカスタム Attack on Customization

3. People Change

60 developers

> 1400 migrations

knowledge gets lost
test lose meaning

when knowledge gets lost
workarounds are lost
no one knows what to do ...
data structure meaning is lost
dead code piles up

tests lose meaning
test helpers are forgotten
different interfaces, different bugs, ...
"why should it return 6?" no one knows...
fixtures rot

well-designed code goes bad
intention isn't maintained
single-responsibility isn't maintained
lots of small hacks build up
code duplication

what can we do?
delete dead code : zev's favorite pastime!
keep everything up-to-date
bootstrap new developers

5751894 LOC added ><

3332234 LOC deleted!! キター!

Delete dead code
reduces mental overhead : no need to refactor unnecessary code!
speeds up test
makes upgrading easier
don't fear the code

dead payment code is dangerous though...

keep everything up-to-date
"Rails 1.3 & Ruby 1.8 ..."

Developer bootstrap
make it easy to get started on a fresh computer
chef solo script that can install stuff on a mac

good for everyone

stick your head up from the sand and say "hey, what's going on in the world"

Code changes, People change

Q: how to check for dead code? gems?
A: I killed code that I really wanted to kill. remove a line of code -> see if test passes -> remove method -> if it still works code may be dead

Q: How do you convince managers of the importance of technical debt?
A: "We wanna make changes to do something new, ..." "We have to get done with Ruby1.9 cause we have to scale for coming new business" but no secret magic

https://twitter.com/junya/status/512868701238870019

Power Assert in Ruby

Ruby版Power Assertの実装の話。こういうテスト用のツール、つまり言語のメタな機能を使い込んでいるツールの実装面の話が聞けるのは言語カンファレンスならではなので非常に貴重。スタックトレースを取るための便利機能がRubyに組み込みで用意されてるんですね…。


今日のメインはimplementation.

proposal note: 無駄に深く実装を解説します : acceptされた以上解説するよ

power-assertがサポートしているのはunit test (not spec test)
原理的にサポートが不可能というわけではない

assertion methodを使ってテスト。

assert_include 3.times.to_a, 3
assert 3.times.to_a.include?(3)

どうちがうの? -> レポートの出方が違う。assert_includeを使ったらレポートから何で失敗したかわかりやすい。

assertion methodを使い分けるのは大事だけどassertion methodが多すぎる。覚えられない。テストの本筋から違う。

Power assertのつかいかた:assertにブロックを使う。以上。
これでテストが失敗したらassertionのブロックの評価式が与えられる。

Power-assertはGroovy 1.7から標準になってる。これが普及に一役買ってる。

power_assert gem (CRuby 2.0.0 or later) - 理由は実装のところで説明
test-unit-power_assert
test-unit 3(@ktou) > 2.2から標準で入る予定
minitest-power_assert(@hsbt)
pry-power_assert(@spikeolaf)

pメソッドを拡張してpower_assertですべての値をinspectするようにする

module PowerPをprependする

実装の話

  • ブロックの途中の各値をどのように値を取る?
  • メソッド呼び出しの位置の情報をどのように取ることができるか?

tracepointすごいなーという話。10人くらいしか使ったことある人がいない。
Tracing API which aims to replce set_trace_func
2.0から導入

TracePoint.newでオブジェクト作る。ブロック内のイベント情報を取る。
return_valueを取る、raised_exceptionで投げられた例外を拾える

exception#cause : ruby2.1から入った。例外をthrowしたときにその1個前にthrowされた例外をとれる。(Javaみたいらしい)

tracepointだと情報が取れすぎる。フィルターする必要あり。

use Binding#eval と Kernel#caller_locations
みんな大好き黒魔術。
tp.binding.eval('caller_locations').length
によってコールスタックの深さがわかる。これによって内部的に呼ばれてるメソッドを区別できる。

Binding#evalは変数の値を取るのにも使う。

トラップ:==の値が返らない
テストコードより先にpower_assertを先にrequireすれば大丈夫。

なぜ?

バイトコードを見てみよう
RubyVM::InstructionSequence.disasm
メソッドの呼び出しの部分 send
==の部分 opt_eq
バイトコード見るとはじめてわかる。

TracePointの呼び出し > EXEC_EVENT_HOOKによって
対策 -> VMの最適化を無効化。これによってopt_eqという==の最適化を無効化してEXEC_EVENT_HOOKが呼ばれる。
特化命令(specializeD_instruction)をfalseにする。あくまでメソッド呼び出しなので実行しないといけない。

おまじないアリ: power_assert実行 -> (最適化無効) -> テストコード実行
おまじないナシ: 最適化アリなままテストコードの評価、power_assertが走る

どのようにpositionの情報をとるか

ripperを使います

ruby script parser
Ripper.sexp create S-exp tree including position information

多重代入を使ってマシに書けるけど
昔はパターンマッチで書いていた。性能の話やモンキーパッチングなどの関係で最新

コーナーケース、限界

  • 書き方の違い
  • 再代入
  • 分岐

conclusion

enjoy programming with power_assert / tracepoint

performance? -> 5%くらいのoverheadで済むと思ったら10倍くらい遅くなった。原因はRipper。

最初にrequireするのがいやなら、メソッド再定義によって最適化オプションを切れるという挙動があったはずなのでことごとく==を再定義すればいいのでは? -> 確かに

tracepointで遅くなるのはbindingを作るとき。これをもうちょっとaggressiveにやればマシかも