Rubyでプラグイン機構を作る
rack/rack · GitHub のコード読んでてプラグイン(Rackの場合はバックエンドにどのサーバを使うか)の処理があったので取り出して書いてみた.
Rubyではクラスは定数なのでHandler
っていう名前空間の中の定数を探すだけでいい.
# sample1.rb module Handler class Base def call run end def run raise NotImplementedError end end class PlugA < Base def run p "called in A" end end end # sample2.rb module Handler class PlugB < Base def run p "called in B" end end end # runner.rb require_relative './sample1.rb' require_relative './sample2.rb' def run(class_name) klass = Handler.const_get(class_name) klass.new.call end ['PlugA', 'PlugB'].each do |e| run e end # => "called in A" # => "called in B"
githubの分割バージョンがある
感想
研究室にyogiboがあってそれに乗っかりながらだらだら読んでて、夏休みの朝に再放送のアニメ見てる感じだった
RubyのStructの簡易版実装してみた
30分くらいでできる簡単な問題探しててmzpさんのやつを見つけてそれを解いてみてる.
これが動けばいい
Dog = MyStruct.new(:name, :age) fred = Dog.new('fred', 5) fred.age = 6 printf "name:%s age:%d\n", fred.name, fred.age
回答
class MyStruct def self.new(*names) Class.new do |_obj| def initialize(*values) @__values = values end names.each_with_index do |method, i| class_eval <<-EOS def #{method} @__values[#{i}] end def #{method}=(value) @__values[#{i}] = value end EOS end end end end
参考
モナディックなパーザーコンビネータ作ってみた
タイトル通りで作ってみたました. もともと研究で違うことやってたはずなのに目的を見失ってなんか面白そうだしこれならゼミで話しても怒られなさそうという考えからこの頃ずっと勉強してた.
OCamlで実装しようかHaskellで実装しようか迷っててどっちでも良かったんだけど最終的にはHaskellで書いた. 最初はOCamlの勉強も少ししてて、とにかく本を買いたくなかったので以下のページを読んでた.
けど素直にHaskellでやったほうが楽そうという気持ちになり途中からHaskellに切り替えた. Haskellの文法は雰囲気がわかる程度だったのでなんとかなるだろうと思い特に何もしなかった. モナドは「モナドは単なる自己関手の圏におけるモノイド対象だよ。何か問題でも?」という感じだったので以下のページを読んでだ.
パーザーコンビネータはググるといろんな人が実装しててるのでそれを参考にするといいかもしれない. こんなふうにParserの型が決まった時点で考えることはあんまりなくて型すごいって思いながら実装しいくとできたし型すごい.
type Result = Either String type Parser v = StateT String Result v
PEGパーザにした気でいるんですがもしかしたらこれではいけないかもしれないので間違っていたらスマンという感じです.
import Control.Monad.State import GHC.Base((<|>)) import Data.Char type Result = Either String type Parser v = StateT String Result v runParser = runStateT look :: Parser Char look = do x:_ <- get return x item :: Parser Char item = do x:xs <- get put xs return x satisfy :: (Char -> Bool) -> Parser Char satisfy f = do a <- item if f a then return a else mzero unsatisfy :: (Char -> Bool) -> Parser Char unsatisfy f = satisfy (not . f) cjoin :: Parser a -> Parser a -> Parser [a] cjoin p1 p2 = do a <- p1 b <- p2 return [a, b] select :: Parser a -> Parser a -> Parser a select = (<|>) many :: Parser a -> Parser [a] many f = many1 f <|> return [] many1 :: Parser a -> Parser [a] many1 f = do a <- f b <- many f return $ a:b
例えばExpr <- Number + Number
を受理するためのは以下のように書くとちゃんと計算結果が返ってくる.
number :: Parser Integer number = do n <- many1 digit return $ read n where digit = satisfy isDigit expr :: Parser Integer expr = do a <- number op <- token (=='+') >> return (+) b <- number return $ op a b main :: IO () main = print $ runStateT expr2 "1+2;" -- => Right (3,";")
4則演算バージョンがあるのでぜひ
参考
- http://d.hatena.ne.jp/kazu-yamamoto/20080920/1221881130
- http://www.geocities.jp/m_hiroi/func/haskell32.html
感想
ruby2.2.2で現在実行中の関数名を取得する
前提
ganmacs@ganmacs~% ruby -v ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]
内容
現在実行中の関数名を取得したいとき、ruby 1.8までは以下のようにしてとれた。
しかし1.9からto_s(Array)
の挙動が変わったらしく2.2.2では動かなった。
class Object def current_method caller.first.scan(/`(.*)'/).to_s end end # when ruby version is 1.8 or earlier puts current_method #=> "<main>" # when ruby verison is 1.9 or later puts current_method #=> [["<main>"]]
なので今は以下のように書くと実行中の関数名を取得できる
class Object def current_method_name caller.first.match(/`(?<method_name>.*)'/)[:method_name] end end puts current_method_name #=> "<main>"
参考
Working With TCP Socketsを読んだ
研究してるふりをして全くせずにこれよんだ。
Working With TCP Sockets (English Edition)
- 作者: Jesse Storimer
- 発売日: 2012/10/24
- メディア: Kindle版
- この商品を含むブログを見る
なぜ読んだかというと、少し前にこの記事いいよって教えてもらった記事があって読んでてたまたまそこに書いてあったから yuuki.hatenablog.com
雑にメモとか取りながら読んだのぜひ github.com
感想
webサーバの実装モデルっぽいのもがわかりやすく紹介されていて非常に面白かった。
英語よめるようになったし、これからも研究してるふりして積極的に関係ないことやっていくぞ!!!
while (token = lexer.read) != EOFをEnumeratorをつかってどうにかしたい
あたらしく研究室に入って(2度目)スクリプト言語の作り方って本が課題なのでやっていまして、そのときlexer作るんですが
while (token = lexer.read) != EOF ... end
的な書き方してて嫌な気持ちになったので他にまともな書き方がないか調べました.
どうしたいか
- while とかの中で代入はすきじゃないのでしたくない
- eachみたいにつかえる
- けど,任意の位置でreadできるようにしたい
- しかしメモリ使用量を抑えたいので一気に配列に突っ込むとかはしたくない
つまり下みたいにはしたくない
# ファイルひらいて一気に配列に突っ込む file = open(file_name).map { |f| f.strip } file.each do |f| ... end
どうしたか
Enumerator classを使いました.
例として以下の様なこと Enumerator
を使ってやってみました.
ファイルから1行ずつ読んでくる->読んできた1行になにかする(行番号をつける).
javaのLineNumberReader
みたいなやつ
class LineNumberReader def initialize(filename) @filename = filename end # nextがなかったらStopIterationをraiseする def read_line file.next end def peek_line file.peek end def has_next_line? !!file.peek rescue StopIteration false end # loopはStopIterationをrescueする def each loop { yield(read_line) } end private # each_line, with_indexは enumeratorを返すのでnextとかで逐次に取ってくるはず def file @file ||= open(@filename).each_line.with_index end end
LineNumberReader#file
がEnumerator
を返すようにしているので[メモリ使用量を抑えたいので一気に配列に突っ込むとかはしたくない]は解決出来てるはず
またLineNumberReader#read_line
を外から呼ぶようにすることで[任意の位置でreadできるようにしたい]も解決できる
reader = LineNumberReader.new('file.rb') p reader3.read_line ... p reader3.read_line if reader3.has_next_line?
またLineNumberReader#each
を作ってることで[eachみたいにつかえる]も解決
reader = LineNumberReader.new('file.rb') reader.each |f| ... end
while
使いたいときはloop
を使うと(実際eachで解消されてるけど)[while とかの中で代入はすきじゃないのでしたくない]も解決
reader = LineNumberReader.new('file.rb') loop p reader.read_line end
まとめ
ただのiteratorっぽいなにかだし,こんなことしてないで早く課題終わらせたほうが良さそう もっと賢い方法があればだれか教えてくれ!!
2週間でできる! スクリプト言語の作り方 (Software Design plus)
- 作者: 千葉滋
- 出版社/メーカー: 技術評論社
- 発売日: 2012/02/10
- メディア: 単行本(ソフトカバー)
- 購入: 11人 クリック: 318回
- この商品を含むブログ (18件) を見る
ansibleっぽいitamaeっぽいプロビジョニングツールminarai作った
作りました. ansibleとitamaeの名前を出しましたがお手本にしたのは @r7kamuraさんが作ったr7kamura/serverkit · GitHub というgemです.
動機
これが欲しかったわけではないんですがgem作ったことなかったし作りたいものもなかったのでなんか真似しようとおもって色々探してたらserverkitが出てきてちょうどよかったので自分でも書いてみました.
使い方
一番シンプルな使い方はrecipe.yml
を以下の内容で作って
- name: clone codic-clojure type: git repository: https://github.com/ganmacs/codic-clojure.git destination: codic-clojure - name: clone dot_emacs type: git repository: https://github.com/ganmacs/emacs.d.git destination: .emacs.d
以下のコマンドを実行するだけです.また、既に目的のファイルが存在する場合などはスキップされるようになってます.
$ minarai recipe.yml INFO : Minarai starting... INFO : [SKIP] clone codic-clojure INFO : [DONE] clone dot_emacs INFO : Minarai finish
以上でgit cloneしてきてくれます.
変数を使った場合
recipeに変数を使うこともできます.
まずvars.yml
に以下を記述します.
remote_dotfile_repo: git@github.com:ganmacs/dotfiles.git tmp_dir: /Users/ganmacs/tmp dotfile_path: /Users/ganmacs/tmp/dotfile vim_config_in_dotfile: /Users/ganmacs/tmp/dotfile/vim/vimrc dot_vim_path: /Users/ganmacs/tmp/.vim
そしてrecipe.yml
に以下を記述します
- name: install vim type: homebrew item: vim - name: create tmp directory type: directory destination: <%= tmp_dir %> - name: clone dotfile type: git repository: <%= remote_dotfile_repo %> destination: <%= dotfile_path %> - name: link dotfile to hgoe type: link source: <%= vim_config_in_dotfile %> destination: <%= dot_vim_path %>
そして実行すれば終わりです
$ minarai recipe_erb.yml.erb --variables=vars.yml INFO : Minarai starting... INFO : [SKIP] install vim INFO : [DONE] create tmp directory INFO : [DONE] clone dotfile INFO : [DONE] link dotfile to hgoe INFO : Minarai finish
機能
とにかくシンプルにしたくて他の構成管理ツールみたいに高機能なものを作るのはやめました. 基本的にローカルのMacの構成管理のためにしか使う気はないのでsshもできないです. コマンドも実行するrecipeを渡すだけです.(変数は使えますが)
実行できるタスク(アクション)は以下のとおりです
- git - git cloneしてきます
- file - fileのコピーをします
- link - symlinkをはります
- directory - directoryを作ります
- homebrew - homebrewでパッケージをインストールします
- homebrew_cask - homebrew_caskでappをインストールします
- url_get - 指定したurlからcurlしてきます
感想
ansibleとitamaeはすごいなーって感想とserverkitのコードが読みやすくて最高って感じでした.
今後
rbenvとかdefaultとかcommandのactionも増やして構成管理に使っていきたい