31
メタプログラミング実践 @shokai shinjuku.rb #18 2013/06/26

メタプログラミング実践 - shinjuku.rb #18

Embed Size (px)

DESCRIPTION

shinjuku.rb #18で発表しました

Citation preview

Page 1: メタプログラミング実践 - shinjuku.rb #18

メタプログラミング実践

@shokai

shinjuku.rb #182013/06/26

Page 2: メタプログラミング実践 - shinjuku.rb #18

•@shokai (しょうかい)

•初参加•趣味:料理、glitch、プログラミング

•Sinatra派

•minitest派

Page 3: メタプログラミング実践 - shinjuku.rb #18

せっかく新宿に来たのだから聖地巡礼しよう!!

→ 小屋が見つからない靴が濡れて足痛い

Page 4: メタプログラミング実践 - shinjuku.rb #18

引きこもってコード書いてたらネタが溜まってきたので放出していきたい

Page 5: メタプログラミング実践 - shinjuku.rb #18

メタプログラミングとは

Page 7: メタプログラミング実践 - shinjuku.rb #18

なるほどわからん

Page 8: メタプログラミング実践 - shinjuku.rb #18

実際やってみたら

わかった!やってみるの大事

Page 9: メタプログラミング実践 - shinjuku.rb #18

今日はメタプログラミングを使って実装してみた3つのGem

をご紹介いたします

Page 10: メタプログラミング実践 - shinjuku.rb #18

% gem install skypeSkype Desktop APIのラッパー

Mac/Linux対応https://github.com/shokai/skype-ruby

method_missingを使用

Page 11: メタプログラミング実践 - shinjuku.rb #18

require 'rubygems'require 'skype'

# チャットSkype.message("shokaishokai", "電話かけます")

# 電話Skype.call("shokaishokai")

call関数やmessage関数はgem内に実装されていないしかしなぜか呼び出せる

Page 12: メタプログラミング実践 - shinjuku.rb #18

module Skype def self.method_missing(name, *args) self.exec "#{name.upcase} #{args.join(' ')}" endend

method_missing

Skype Desktop API

"CALL shokaishokai""MESSAGE shokaishokai こんにちは"

Query文字列をSkype.appに送るだけの簡単仕様

実装されていない関数の呼び出しを受け取る関数

Skype.execの中身はMac/Linux別々に実装

Page 13: メタプログラミング実践 - shinjuku.rb #18

• method_missingはAPIのラッパーを作るのに便利

• (APIに規則性がある場合のみ)

• API側が変わっても、gemを修正する必要がない

• 例:”CALL shokaishokai” が “CALLTO shokaishokai”

に仕様変更されても、Skype.callto “shokaishokai” で呼び出せる

• Twitter gem等でも使われている

Page 14: メタプログラミング実践 - shinjuku.rb #18

% gem install babascriptコンピュータが得意な事はコンピュータが、

人間が得意な事は馬場くん  がやってくれる言語

https://github.com/masuilab/babascript

instance_eval, method_missingを使用

@takumibaba

Page 15: メタプログラミング実践 - shinjuku.rb #18

% baba -e 'アイス買ってきて("#{rand 5}本")'

baba -e ’コード’

もしくはbaba ファイル名

Page 16: メタプログラミング実践 - shinjuku.rb #18

結果

Page 17: メタプログラミング実践 - shinjuku.rb #18

#!/usr/bin/env babaif 0 < Time.now.hour and Time.now.hour < 5 もう寝ろ!!else 意識を高めてコードを書こう!end

wake-up.bb

% baba wake-up.bb

実行

ただのRuby…ではなく日本語で書いた部分を馬場君が実行してくれる

Page 18: メタプログラミング実践 - shinjuku.rb #18

class Foo def initialize @name = "bar" endend

foo = Foo.newfoo.instance_eval do puts @name # => "bar"end

instance_eval

babaコマンドの中身 = instance_evalFile.open(fname) do |f| BabaScript::Baba.instance_eval f.readend

コードやブロックをそのインスタンスのコンテキストで実行する

fooのコンテキストで実行されるのでアクセサが無いプロパティ@nameも読める

Page 19: メタプログラミング実践 - shinjuku.rb #18

module BabaScript class Baba

def self.method_missing(name, *args) ## (略) Androidにnameとargsを送信する end

endend

Rubyの関数名には日本語が使える→ method_missingで全部取れる

File.open(fname) do |f| BabaScript::Baba.instance_eval f.readend

Page 20: メタプログラミング実践 - shinjuku.rb #18

• instance_eval + method_missingで言語が歪む

• エラーメッセージがわかりにくくなるので、何でもかんでもinstance_evalするとコード追えなくなる

• rspecなど、英語っぽいDSLで書けるgemで使われているはず

Page 21: メタプログラミング実践 - shinjuku.rb #18

人間を関数のように扱えるようになるスマホアプリ

+人間に命令を送る構文を追加したプログラム言語

→ 人間とプログラム言語の新しい関係

Page 22: メタプログラミング実践 - shinjuku.rb #18

% gem install event_emitter全てのクラス/インスタンスにイベント機能を追加できる

https://github.com/shokai/event_emitter

mix-in, instance_evalを使用(これメタプログラミングじゃない気がする!)

Page 23: メタプログラミング実践 - shinjuku.rb #18

class Crawler include EventEmitter def start(urls) Thread.new do urls.each do |url| begin emit :get, url, HTTParty.get(url).body rescue => e emit :error, url, e end emit :complete end end endend

crawler = Crawler.new

crawler.once :error do |err| STDERR.puts e exit 1end

crawler.on :get do |url, page_data| puts "page #{url} get!!" puts page_dataend

crawler.start ["http://shokai.org", "http://example.com", "http://github.com"]

mix-in

イベント発行

イベント受信

Page 24: メタプログラミング実践 - shinjuku.rb #18

class Foo include EventEmitterend

class宣言時にinclude → instance全てにイベント機能が付く

EventEmitter.apply Time

classにapply → class methodとしてイベント機能が付く

now = Time.nowEventEmitter.apply now

instanceにapply → そのinstanceだけにイベント機能が付く(特異メソッド)

全てのオブジェクトにイベント機能を追加できる非同期プログラミングに大変便利

Page 25: メタプログラミング実践 - shinjuku.rb #18

module EventEmitter def self.included(klass) klass.__send__ :include, InstanceMethods end

def self.apply(object) object.extend InstanceMethods end

module InstanceMethods def __events @__events ||= [] end

def on(type, params={}, &block) ## 略 end

def emit(type, *data) ## 略 end endend

includeされた時に呼び出される

特異メソッド注入用

module内のインスタンスメソッドとプロパティが注入される

private methodを強引に呼ぶ

Page 26: メタプログラミング実践 - shinjuku.rb #18

• なぜincludedで受けてもう一度includeを呼んでいるか?というと

• gemのネームスペース直下をそのままincludeさせるのが嫌だった

• 子moduleに機能を集約したい

Page 27: メタプログラミング実践 - shinjuku.rb #18

メソッドが増えている

mix-in前後を比較

Page 28: メタプログラミング実践 - shinjuku.rb #18

• included関数でincludeを受け止めて、複数includeしちゃう事も可能

• 動物とか犬とか人間とかの階層分けオブジェクト指向ではなく、機能レベルでの抽象化に向いてる気がする

• Sinatraのプラグイン機構に使われている

• EventEmitterはブラウザ用JavaScript版もあります

https://github.com/shokai/event_emitter.js

Page 29: メタプログラミング実践 - shinjuku.rb #18

Sinatra::RocketIOでも使っている

SinatraでNode.jsのSocket.IOのような事ができるRubygem

websocket/comet対応

Page 30: メタプログラミング実践 - shinjuku.rb #18

おわり

Page 31: メタプログラミング実践 - shinjuku.rb #18

質問などあれば

•@shokai •お気軽に•お声かけください