CSSの勉強(1)
本日はHTMLの課題とドットインストールのCSS入門でCSSの勉強をしました
自分は長くJavaをやって来ました。C触ったり、Ruby触ったりしました。 プログラムは逐次実行、if分、forループなどの文法は似通っているため、 新しい言語を学ぶときにはそれが活かされるんですが、さすがにObjective-Cの引数にラベルがついているのには戸惑いました。 Objective-Cはオブジェクト指向プログラミングなので、他のプログラミング言語と文法は 違いますが、やっていくうちにわかってきます。
CSSの文法も見慣れない文法なので、すぐに頭に入ってきませんが、だんだん使っていくうちに 慣れようと思います
CSSの基本は以下のとおりです。設定するプロパティが複数ある場合は(;)で区切ります
セレクタ { プロパティ : 値; プロパティ : 値; プロパティ : 値; }
セレクタは以下の3つがあります
- 要素: h1, p など
- id: #sub
- class: .item
色の指定はプロパティにcolorを設定し、値に色を設定します
h1 { color: red; } /* 固有名詞で指定 */ h1 { color: rgb(255, 0, 0); } h1 { color: #f00; } /* 16進数3桁 */ h1 { color: #ff0000; } /* 16進数6桁 */
本日はドットインストールの#13まで行いました。明日も引き続きCSSの勉強をします
HTMLの勉強(1)
RailsでWebアプリケーションが作れるようになることが目標です
本日はHTMLの勉強をしました。主にドットインストールのHTML入門 で勉強しました
十何年前にJavaでWebアプリケーションを作るところでやりましたが、Webアプリの基本なのでHTMLをやることにしました。
最近はHTML5が使われるようになり、HTMLは文章の構造を記述し、見た目はCSSで行います。 色を指定を行うFontタグや中央にするcenterタグなどは廃止されており、これらは見た目なのでCSSで行います
ブラウザはMacのGoogle Chromeで確認しました。バージョンは33.0.1750.152です
太文字にするには、strongタグありましたが、太くなりませんでした。太くするにはbタグを使用すると太くなりました
strongタグが使えるかどうかを以下のページ(http://caniuse.com/)で確認しましたが、使用できるブラウザはヒットしませんでした
インターネットで調べると、SEO対策でwebクローラーにはstrongタグをつけると意味的に強調になると書いてありましたが、本当かどうか、調べようがありませんでした
タグは省略されていて、覚えにくいものがありましたが、ドットインストールでは省略前の単語について、説明されており、覚える助けになりました。
例
- ulタグ (unorder list)
- olタグ (order list)
tableタグで、ヘッダ部分をグループ化するタグ、内容部分をグループ化する
タグを知りました。ググると、フッター部分をグループ化するリンクを別のタブで開く時には、target属性に_blankを指定します
<a href="http://www.yahoo.co.jp" target属性に="_blank">Yahoo!</a>
labelタグを知りました。ラジオボタンで、ラジオボタンだけでなく、labelタグで囲まれた部分をクリックしてもラジオボタンをクリックしたことになります。ユーザービリティーが上がっています
divタグ、span要素はいままで使ってなかったので勉強になった
- divタグ (汎用ブロック要素) 改行を含む
- spanタグ (汎用インライン要素) 改行を含まない
HTML5では各ブロックに意味をつけるようになった。
- header
- footer
- nav
- article
- aside
- section
スクリプトタグは実行中にとまってしまうので、bodyタグの閉じタグの上に記述するのが一般的だそうです
明日は課題のHTMLをやりたいと思います
Rubyでツイキャスからコメントを取得してみた
はじめに
2014/01/01からひとりFluentdハッカソンをやっていました。インプットかアウトプットプラグインをなにしようかと思ったんですが、まず、ゲームの生放送するTwitch APIからチャットコメントを取得しようと思いました。ですが、1日のおわりにコメントが取得できないことに、やっと気づきいて、振り出しにもどりました。本日、目をつけたのがiPhone, Androidから生放送するツイキャス(TwitCasting)です。
そこでRubyからツイキャスからコメントを取得するGem TwitCasting-rbを作りました。
インストール
$ git clone https://github.com/suzukaze/Twicasting-rb $ cd Twicasting-rb $ rake build $ gem install pkg/twicasting-x.x.x.gem $ rbenv rehash # if you use rbenv.
ツイキャスからコメントを取得する
twitcasting.rbに以下の内容を保存してください。
require 'twitcasting' # コメントを取得する twitcast = TwitCasting::Client.new comments = twitcast.get_comments({:movieid => '31846367'}) # コメントを表示する puts "-" * 40 puts "date user message" puts "-" * 40 comments.each do |comment| time = Time.at(comment.created).strftime("%Y/%m/%d %H:%M:%S") puts "#{time}, #{comment.userid}, #{comment.message}" end
コンソールから以下のコマンドを実行してください。:movieidが'31846367'の日付、ユーザーID、コメントを取得します。
$ ruby twicasting.rb ---------------------------------------- date user message ---------------------------------------- 2014/01/02 14:11:21, [user1], こういう道は吹き矢に気をつけてねw 2014/01/02 14:09:31, [user2], この辺も吹き矢に気をつけてねw 2014/01/02 12:31:03, [user3], 今日やってるバイク屋少なそう・・・ 2014/01/02 12:26:25, [user4], 普通ボルト外れないぞ 2014/01/02 12:25:46, [user5], e 2014/01/02 12:14:46, [user6], もい 2014/01/02 12:14:44, [user7], 待機 2014/01/02 12:14:32, [user8], 食事中..... 2014/01/02 12:14:24, [user9], 来た道を戻って、一号線へ行って下さい! ----------------------------------------
TwitCasting::Client#get_comments({:user => 'user', :movieid => 'movieid'})
はuserに指定したユーザーID, movieidは指定したムービーIDのコメントを取得します。
おわりに
TwitCasting APIのJSONを返すAPIはまだあるので、今後実装していきたいです。本来の目的はFluentdのインプットプラグインをつくることなので、次はツイキャスのコメントを取得するFluentdプラグインに取りかかりたいです。
ServerEngineを動かしてみた
はじめに
Server EngineはUnicornのような強固なマルチプロセスサーバーを実装するためのフレームワークで、rubyで記述されています。ruby 1.9.3, 2.0.0で動作します。ログイベント収集ミドルウェア Fluentd v11に使用されています。
メイン機能は以下のとおりです。 https://github.com/frsyuki/serverengine#serverengine
Supervisorは設定ファイルを読み込み、Serverを起動します。Serverは作業する複数のWorkを起動します SupervisorはWorkerにpipe経由でハートビートを送り、自動リスタートさせます
他の機能:
- ロギンングとログローテーション
- シグナルハンドラー
- シグナル時にスタックトレースとヒープダンプ
- user変更, group変更 および umask変更
- プロセス名の変更
Server Engineをインストール
Server Engineをインストールします
$ gem install serverengine
Simplest serverを実行
https://github.com/frsyuki/serverengine#simplest-server
Workerを1つ起動し、ログファイルに"Awesome work!"ログを書き込みます。デーモンで起動されますので、終了させるまでバックグラウンドで実行されます。筆者はMac 0S X 10.8.4、ruby ruby 2.0.0p0で確認しました。simple-server.rbとしてコードを保存します
$ ruby simple-server.rb
ファイルが2つ作成されますので、確認してみましょう。
$ ls myserver.log myserver.pid simplest-server.rb
simple-server.rbに以下が設定されているため、ログファイルとしてmyserver.log
,起動したプロセスIDをmyserver.pid
に記録されます
se = ServerEngine.create(nil, MyWorker, { :daemonize => true, :log => 'myserver.log', :pid_path => 'myserver.pid', })
myserver.logがログを書き出し先のファイルです。 myserver.pidが起動しているプロセスIDです
$ tail -f myserver.log I, [2013-12-22T21:52:32.750339 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:33.753536 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:34.754886 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:35.756227 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:36.756767 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:37.758424 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:38.759863 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:39.761215 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:40.762543 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:41.763787 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:42.765654 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:43.766261 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:44.767841 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:45.768601 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:46.769669 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:47.770607 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:48.771009 #12873] INFO -- : Awesome work! I, [2013-12-22T21:52:49.771709 #12873] INFO -- : Awesome work!
1秒ごとにログが増えていることがわかります
myserver.pidファイルで起動しているプロセスIDを確認します
$ cat myserver.pid 12873
psコマンドでプロセスの生存を確認します
$ ps 12873 PID TT STAT TIME COMMAND 12873 ?? S 0:00.20 ruby simplest-server.rb
それでは、プログラムを終了してみましょう。TERMシグナルを送り、プログラムを止めます
$ kill -TERM 12873
myserver.logに@stop=trueが表示されました。これはTERMシグナルによりMyWoker#stopメソッドが呼ばれ、@stop=trueが設定され、MyWoker#runメソッドのループが終了したためです
... I, [2013-12-22T22:46:11.712393 #13510] INFO -- : Awesome work! I, [2013-12-22T22:46:12.713562 #13510] INFO -- : Awesome work! I, [2013-12-22T22:46:13.714934 #13510] INFO -- : Awesome work! I, [2013-12-22T22:46:14.715912 #13510] INFO -- : Awesome work! I, [2013-12-22T22:46:15.717315 #13510] INFO -- : @stop=true
psコマンドでプロセスが消えていることを確認しましょう
$ ps 12873 PID TT STAT TIME COMMAND
おわりに
今回はWorkerが1つというものすごく単純な構成をみました。Workerにデーモンで作業させて、TERMシグナルによる終了をみました。本当は、次のサーバー1つと4つのWokerのmulti-tcp-serverもやりたかったのですが、うまく動作させることができませんでした。たぶん、Workerに送信するクライアントプログラムが必要になると思います。それは次回やりたいと思います
mruby-jsonとmruby-msgpackの速度比較してみた!
はじめに
JSONとMessagePackでは、シリアライズとデシリアライズにおいて、MessagePackがバイナリフォーマットなので、数倍速いです。今回はmruby版でシリアライズとデシリアライズの速度を比べてみようと思います。 ちなみにシリアライズはデータをファイルに保存したり、ネットワークで送受信できるようにすることです。反対に、デシリアライズとはシリアライズしたファイルやネットワーク上のデータをプログラムで扱えるように復元することです。
ビルド
1. build_config.rbにmruby-jsonとmruby-msgpackのconfig.gem行を追加:
MRuby::Build.new do |conf| # ...(省略)... conf.gem :git => 'https://github.com/mattn/mruby-json' conf.gem :git => 'https://github.com/suzukaze/mruby-msgpack.git' end
2.コマンドプロプトからビルド:
ruby ./minirake
ベンチマークプログラム
自分はmacを使用しているので、計測したいmrubyファイルをmrubyで実行して、timeコマンドで計測すればよいです。 time mruby mruby-benchmark.rb
ですが、いくつも計測するのに汎用的なベンチマークプログラムはないかと探しましたら、rubyの標準にBenchmarkモジュールがありました。 よさそうと思ったのですが、すべて理解するのは大変だったので、少し参考にしてmruby用に自作しました。
SimpleBenchmarkクラス
class SimpleBenchmark def initialize(width = 0) @width = width end def measure(label) start = Time.now yield if block_given? passed = Time.now - start puts "#{make_fixed_label(label)}passed time #{passed} sec" end def make_fixed_label(label) if @width - label.length > 0 label + ' ' * (@width - label.length) else label end end end
次のハッシュをシリアライズとデシリアライズをJSONとmsgpackで10000回繰り返します。
{'name' => 'suzukaze', 'level' => 15})
計測プログラムは次のようになります:
benchmark = SimpleBenchmark.new("MessagePack:".length) n = 10000 #JSONでシリアライズとデシリアライズする時間を計測する benchmark.measure("JSON:") do n.times{ JSON.parse(JSON.generate({'name' => 'suzukaze', 'level' => 15})) } end #MessagePackでシリアライズとデシリアライズする時間を計測する benchmark.measure("MessagePack:") do n.times{ MessagePack.unpack(MessagePack.pack({'name' => 'suzukaze', 'level' => 15})) } end
計測環境
OS:Mac OS X 10.8.4
プロセッサ: 2.13GHz intel Core 2 Duo
計測
次のコマンドをコマンドプロンプトから実行します:
mruby mruby-msgpack-profile.rb
mruby-msgpack-profile.rbは最後に全部、掲載しています。
計測結果
JSON: passed time 0.543839 sec MessagePack:passed time 0.092397 sec
計測結果はMessagePackがJSONの約5.8倍の速さとなりました。
最後に
計測に使ったソースの全文を掲載します。
mruby-msgpack-profile.rb
class SimpleBenchmark def initialize(width = 0) @width = width end def measure(label) start = Time.now yield if block_given? passed = Time.now - start puts "#{make_fixed_label(label)}passed time #{passed} sec" end def make_fixed_label(label) if @width - label.length > 0 label + ' ' * (@width - label.length) else label end end end benchmark = SimpleBenchmark.new("MessagePack:".length) n = 10000 #JSONでシリアライズとデシリアライズする時間を計測する benchmark.measure("JSON:") do n.times{ JSON.parse(JSON.generate({'name' => 'suzukaze', 'level' => 15})) } end #MessagePackでシリアライズとデシリアライズする時間を計測する benchmark.measure("MessagePack:") do n.times{ MessagePack.unpack(MessagePack.pack({'name' => 'suzukaze', 'level' => 15})) } end
mrubyでMessagePackを作った!
MessagePackって何?
MessagePackとはなにかといいますと、JSON互換の異なる言語間でデータ交換するためのシリアライズフォーマットです。JSONのような型を扱え、 JSONよりコンパクトで、速いと言われています。(mrubyではまだ性能評価していないので、今後やってみたいと思います。) ログコレクターで有名なfluentdというミドルウェアで使用されています。
MessagePack for mruby
今回、MessagePack for ruby v 0.5.5からmrubyにGEMとして移植しました。すべて移植したわけではないので、α版というところでしょうか。 MessagePack#pack,MessagePack#unpackメソッドでだいたいの型を使用出来るようになっています。
次の例は配列をシリアライズして、MessagePack#unpackメソッドでデシリアライズで元に戻しています。
msg = MessagePack.pack([1, 2, 3]) #=> "\x93\x01\x02\x03" MessagePack.unpack(msg) #=> [1, 2, 3]
または
msg = [1, 2, 3].to_msgpack #=> "\x93\x01\x02\x03" MessagePack.unpack(msg) #=> [1, 2, 3]
このようなことがmrubyでできるようになります。
それではまずMessagePack for mruby(以下mruby-msgpack)をビルドしてみましょう
ビルド
1. コマンドプロンプトでmruby-msgpackをダウンロードします:
git clone https://github.com/suzukaze/mruby-msgpack.git
2. mrubyのルートディレクトリにあるbuild_config.rb
にconf.gem
行追加します:
MRuby::Build.new do |conf| # ...(省略)... conf.gem :git => 'https://github.com/suzukaze/mruby-msgpack.git' end
3. テストコードを実行して、プログラムが正常かを確認します:
rake test
または、rakeがなければ
./minirake test
テスト結果を確認し、エラーがなければOKです(KOやCrashの値が0):
Total: 682 OK: 682 KO: 0 Crash: 0 Time: 0.068817 seconds
4. ビルドします
rake
または
./minirake
mruby-msgpackを試す
mruby-msgpackをさっそく試してみましょう。まず、コマンドプロプトから:
mruby -e 'msg = MessagePack.pack([1, 2, 3]); puts MessagePack.unpack(msg)'
[1, 2, 3]とコマンドプロンプトに表示されたでしょうか。 MessagePackモジュールにあるpackとunpackメソッドを使用して 要素3つ持った配列をシリアライズ後に、デシリアライズして元に戻しています。 今回は[1, 2, 3]をシリアライズ後にでデシリアライズしましたので、結果が[1, 2, 3]になります。
また、シリアライズは次のように配列に対してto_msgpackメソッドを呼ぶことで同じことができます。
mruby -e 'msg = [1, 2, 3].to_msgpack; puts MessagePack.unpack(msg)'
これはArrayクラスを拡張してシリアライズするto_msgpackメソッドを使用出来るようにしています。
mruby-msgpackで扱える型
ではここで、mruby-msgpackで扱える型について紹介しましょう。
- nil
- true
- false
- Fixnum
- Float
- String
- Symbol (Stringに変換されます)
- Array
- Hash (mrubyのHashは順番が保証されません。ruby 1.9からHashが順序付けに)
内部ではさらに、データの長さによって同じ型でも、フォーマットが異なります。 そこは省略しますが、短いデータには短い型情報のビット数、大きなデータには大きい型情報のビット数が割り当てられています。
rubyのMessagePackと異なる点は、mrubyにはBignumがありません。そのためmrubyではFixnumで収まらない大きな値はFloatに自動的に変換されます。これはmrubyの仕様です。Floatも扱えるのですが、大きな値でrubyと比べて値が異なることがあったのでここは要検証する必要があると思っています。
いろいろ試す
では、いろいろ試してみましょう。
1. Fixnum (整数)
1
mruby -e 'msg = 1.to_msgpack; puts MessagePack.unpack(msg)'
-1
mruby -e 'msg = -1.to_msgpack; puts MessagePack.unpack(msg)'
256
mruby -e 'msg = 256.to_msgpack; puts MessagePack.unpack(msg)'
63535
mruby -e 'msg = 65535.to_msgpack; puts MessagePack.unpack(msg)'
2. Float (浮動小数点)
1.23
mruby -e 'msg = 1.23.to_msgpack; puts MessagePack.unpack(msg)'
-1.0
mruby -e 'msg = -1.0.to_msgpack; puts MessagePack.unpack(msg)'
3. String(文字列)
"abc"
mruby -e 'msg = "abc".to_msgpack; puts MessagePack.unpack(msg)'
4. Symbol(シンボル)
:abcというシンボルは"abc"になります
mruby -e 'msg = :abc.to_msgpack; puts MessagePack.unpack(msg)'
5. true
true
mruby -e 'msg = true.to_msgpack; puts MessagePack.unpack(msg)'
6. false
false
mruby -e 'msg = false.to_msgpack; puts MessagePack.unpack(msg)'
7. nil
nilも可能ですがnil.to_sは空文字なので、if文でnilだったら、"nil!"と表示しています
mruby -e 'msg = nil.to_msgpack; puts "nil!" if MessagePack.unpack(msg) == nil'
8. Array(配列)
[1, "a", true]
mruby -e 'msg = [1, "a", true].to_msgpack; puts MessagePack.unpack(msg)'
9. Hash(ハッシュ)
{"name" => "suzukaze", "level" => 10, "list" => [1, 2, 3]}
mruby -e 'msg = {"name" => "suzukaze", "level" => 10, "list" => [1, 2, 3]}.to_msgpack; puts MessagePack.unpack(msg)' {"list"=>[1, 2, 3], "name"=>"suzukaze", "level"=>10} #順番は保証されない
最後に
mruby-msgpackはrubyにはあって、mrubyにない実装はまだありますが、ある程度使えるのではないかと思います。 ruby版には内部バッファを使用してコピー処理を抑えて高速に動作するクラスがあります。こちらも移植していきたいと思います。 なおこの実装にはIOクラス(実際にはStringIOが使用されていた)のでmruby-ioの実装にも貢献していけたならと思います。 mruby-msgpackのソースはここあります。よかったらのぞきにきてください。
mruby-fluent-loggerの作者の方がmruby-msgpackがないので代わりにmruby-jsonを使っているとのこと。mruby-msgpack作ったので、使ってください!!おかしかったら直します。
mrubyでC言語のGEMを作成する
rubyの方言に組み込み向け用のmrubyというものがあります。rubyには簡単にライブラリが組み込めます。mrubyにも便利なライブラリがあったらいいですよね。
mrubyはまだ若い言語ですが、GEMがあります。
rubyはGEMをあとからインストールできますが、mrubyのGEMはmruby本体と同時にビルドします。ではまず、mrubyをGitHubから取得してビルドしてみましょう。
$ git clone https://github.com/mruby/mruby.git
$ cd murby$ ls
AUTHORS MITL TODO doc mrblib
CONTRIBUTING.md Makefile benchmark examples src
ChangeLog NEWS bin include tasks
INSTALL README.md build minirake test
LEGAL Rakefile build_config.rb mrbgems tools
mrubyのソースを取得できたのでビルドします。
$ make
ruby ./minirake
(in /Users/xxxxxx/work/mruby/origin/mruby)
CC tools/mruby/mruby.c -> build/host/tools/mruby/mruby.o
CC src/array.c -> build/host/src/array.o
CC src/class.c -> build/host/src/class.o
CC src/codegen.c -> build/host/src/codegen.o
省略
Build summary:
================================================
Config Name: host
Output Directory: build/host
Binaries: mruby, mrbc, mirb
================================================
エラーがでずに上記のようにbuild summaryが表示されたら、ビルド成功です。mrubyが動作するかどうか確かめてみましょう。
$ cd bin
$ ls
mirb mrbc mruby
$ ./mruby -e 'p 1+1'
2
-eオプションはワンライナーと言いまして、一行でmrubyのコードを実行するオプションです。1+1の結果が2と表示されました。
毎回binディレクトリで実行するのは面倒ですのでパスを通します。私はmac OS Xを使用していますので、~/.profileに以下のように記載しました。
export MRUBY=$HOME/mruby
ホームの直下のmrubyディレクトリがあるとします。
source ./profileで有効にします。
$ source ./profile
$ mruby -e 'p 1 + 2'
3
これでどこでもmrubyを実行出来るようになりました。
次は、ビルドの設定ファイルについて見てみましょう。
mrubyのルートに戻ります。
$ cd ..
$ cd murby$ ls
AUTHORS MITL TODO doc mrblib
CONTRIBUTING.md Makefile benchmark examples src
ChangeLog NEWS bin include tasks
INSTALL README.md build minirake test
LEGAL Rakefile build_config.rb mrbgems tools
build_config.rbがビルドの設定ファイルです。rubyで記述されています。
MRuby::Build.new do |conf|
# load specific toolchain settings
toolchain :gcc
# Use mrbgems
# conf.gem 'examples/mrbgems/ruby_extension_example'
# conf.gem 'examples/mrbgems/c_extension_example' do |g|
# g.cc.flags << '-g' # append cflags in this gem
# end
# conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
# conf.gem :github => 'masuidrive/mrbgems-example', :branch => 'master'
# conf.gem :git => 'git@github.com:masuidrive/mrbgems-example.git', :branch => 'master', :options => '-v'
# 省略
end
GEMを作成するにはC言語、ruby、C言語とrubyの3種類がありますが、ここはC言語で作成してみようと思います。インタフェースはrubyで実装はC言語で高速にすることがライブラリとしてよいのではないかと思ったからです。
すでにmruby/examples/mrbgems/c_extension_exampleディレクトリにサンプルがありますのでそれを使用します。config.gem のあとにGEMのパスを記述します。
MRuby::Build.new do |conf|
# load specific toolchain settings
toolchain :gcc
# Use mrbgems
conf.gem 'examples/mrbgems/c_extension_example'
別端末を起動し、サンプルディレクトリに移動します。
$ cd mruby/examples/mrbgems/c_extension_example
$ ls
README.md mrbgem.rake src test
mrbgem.rakeのgemの情報を書き込みます。
spec.licenseがラインセンス、spec.authorsが著者権者になります。公開するときは書き換えましょう。
MRuby::Gem::Specification.new('c_extension_example') do |spec|
spec.license = 'MIT'
spec.authors = 'mruby developers'
end
srcがC言語のソースになります。
$ cd src
$ ls
example.c
example.cをテキストエディタで開きます。
#include <mruby.h>
#include <stdio.h>
static mrb_value
mrb_c_method(mrb_state *mrb, mrb_value self)
{
puts("A C Extension");
return self;
}
void
mrb_c_extension_example_gem_init(mrb_state* mrb) {
struct RClass *class_cextension = mrb_define_module(mrb, "CExtension");
mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, ARGS_NONE());
}
void
mrb_c_extension_example_gem_final(mrb_state* mrb) {
// finalizer
}
まずはそのままサンプルをビルドしてみましょう。先ほど、ビルドした端末でビルドします。
$ make
エラーがでなければビルド成功です。
別端末を起動します。mrubyを実行できるように~/.profileを読み込みます。
$ source ~/.profile
適当な場所でgemを実行するファイルを作成します。
example.rb
# encoding : utf-8
# 引数なしメソッド
CExtension::c_method
それでは実行してみましょう。
$ mruby example.rb A C Extension
CExtension::c_methodメソッドが実行され、A C Extensionと表示されました。
GEMのソースexample.cを見てみましょう。
CExtension::c_methodメソッドがよばれるとmrb_c_method関数が呼ばれます。
static mrb_value
mrb_c_method(mrb_state *mrb, mrb_value self)
{
puts("A C Extension");
return self;
}
初期化です。
void mrb_c_extension_example_gem_init(mrb_state* mrb)
{
struct RClass *class_cextension = mrb_define_module(mrb, "CExtension");
mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, ARGS_NONE());
}
mrb_define_class_methodの第2引数"c_method"がrubyでのメソッド名、第3引数mrb_c_methodが第2引数で指定されたメソッドが呼ばれたときに実行されるC言語の関数となります。
終了処理です。
void mrb_c_extension_example_gem_final(mrb_state* mrb)
{
// finalizer
}
それでは、rubyからGEMを呼び出してみましょう。rubyのコードです。
example.rb
# encoding : utf-8
# 引数なしメソッド
CExtension::c_method
実行してみましょう。
$ ruby example.rb
A C Extension
mrb_c_method関数が実行されて、「A C Extension」と表示されました。 次は引数に整数、戻り値に整数を返すメソッドを実装してみましょう。
example.c
#include <stdio.h>
#include "mruby.h"
#include "mruby/variable.h"
static mrb_value
mrb_c_method(mrb_state *mrb, mrb_value self)
{
puts("A C Extension");
return self;
}
// インクリメントする
static mrb_value
mrb_inc(mrb_state *mrb, mrb_value self)
{
mrb_int i;
mrb_get_args(mrb, "i", &i);
i++;
return mrb_fixnum_value(i);
}
void
mrb_c_extension_example_gem_init(mrb_state* mrb) {
struct RClass *class_cextension = mrb_define_module(mrb, "CExtension");
mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, ARGS_NO\
NE());
mrb_define_class_method(mrb, class_cextension, "inc", mrb_inc, ARGS_ANY());
}
void
mrb_c_extension_example_gem_final(mrb_state* mrb) {
// finalizer
}
example.cにC言語で整数をインクリメントするmrb_inc()関数を追加しました。mrb_c_extension_example_gem_init()関数に>mrb_define_class_method(mrb, class_cextension, "inc", mrb_inc, ARGS_ANY())を追加しました。第4引数に引数任意を指定しました。
example_inc.rb
# encoding : utf-8
# インクリメント
x = 2
puts "x=#{x}"
x = CExtension::inc(x)
puts "インクリメント"
puts "x=#{x}"
実行してみましょう。
$ mruby example_inc.rb x=2 インクリメント x=3
xの値が2から3にインクリメントされました。
1: static mrb_value
2: mrb_inc(mrb_state *mrb, mrb_value self)
3: {
4: mrb_int i;
5: mrb_get_args(mrb, "i", &i);
6: i++;
7: return mrb_fixnum_value(i);
8: }
1行目mrb_valueはmrubyの値を表す構造体です。
4行目はmruby内部のintを表します。
5行目でmrubyメソッドの第1引数をiに代入します。
6行目でインクリメントします。
7行目のmrb_fixnum_value関数でrubyの整数にとして戻り値を返します。
C言語のintとmrubyは同じではないので変換する必要があります。
他にfloatやtrue,false,nilをmrb_valueとして表す関数があります。
インスタンスメソッドや他の型も扱える方法を調べていきたいと思います。