0

世界一柔軟で好意的に解釈してくれるプログラム言語BabaScriptを作りました

rubygemsでインストールできます

% gem install babascript

ソースコードはこちら
https://github.com/masuilab/babascript


BabaScriptとは

コンピュータが得意なことはコンピュータに、人間が得意なことは@takumibabaが処理するプログラム言語です


使用方法

ワンライナー
% baba -e "アイス買ってきて"


馬場スクリプトはRuby風に書ける言語で、日本語で書いた部分は馬場くんが実行してくれます

test1.bb
#!/usr/bin/env baba
if 0 < Time.now.hour and Time.now.hour < 5
もう寝ろ!!
else
意識を高めてコードを書こう!
end
実行
% baba test1.bb
0時から5時の間は寝ろ、という指令が送れます。



Rubyの中に馬場スクリプトを埋め込む事も可能です。
例えばRubyでArduinoの照度センサーを読んで、明るくなったら起こしてもらうスクリプトはこうなります

wakeup_arduino.rb
#!/usr/bin/env ruby
require 'arduino_firmata'
require 'babascript'
arduino = ArduinoFirmata.connect
loop do
if arduino.analog_read(0) > 80 ## 明るくなってきたら
BabaScript.baba do
明るくなったから起こしてくださいby橋本
それと朝食におにぎり買ってきてください("#{rand(5)}個", "しゃけ味")
end
end
end
実行
% ruby wakeup_arduino.rb
おにぎりが0〜5個ランダムで配送されます


実行環境

BabaScriptはシングルスレッドでの実行なので、負荷をかけないでください。
こういう通知がAndroidケータイに送信されます。そのうち値を返す関数も実装されるのでは?

引数部分は配列で渡されます。アイス買ってきての[“9本”]は引数です。


馬場の切り替え

BabaScriptはジョブ通知にWeb版Lindaを使っています。
環境変数BABAを切り替える事で別のBABAマシンでスクリプトを実行させる事が可能です。
環境変数BABAのデフォルト値は”takumibaba”で、彼のジョブは http://linda.masuilab.org/takumibaba/notifications で確認できます。

% export BABA=shokai

環境変数BABAを”shokai”に切り替えると、以後のジョブの通知先が http://linda.masuilab.org/shokai/notifications になります。


今後の開発方針

当面の悩みはテストが書きにくいことです。
テストコードがない · Issue #1 · masuilab/babascript · GitHub


Android版のBabaScriptジョブ通知アプリが公開されれば、馬場を自分に切り替えて実行が可能になります。例えばサーバーがおかしい時とか
BabaScript.baba do
ちょっとuser_nameの値がおかしい、調べて(user_name)
end
などと書けば自分のケータイに通知が来ます。
またLindaなので分散処理も簡単だと思います


あとはibb(インタラクティブBaba)で対話的実行ができるようになるといいと思う。

0

Skype API Rubyラッパーのgem作った

今日は早起きしたのでさくっと作った。

rb-skypemacがRuby1.8でしか動かなかったり、Ruby4Skypeがソースを修正しないと動かなかったり、どのskype gemもリポジトリが公開されてなくてパッチ送れなかったので1から作りなおした。

https://github.com/shokai/skype-ruby
https://rubygems.org/gems/skype


linuxだとruby-dbus、macだとrb-appscriptを使うんだけどOS毎に別のgemをdependencyに入れるのどうすればいいんだろう・・
gemspecの中でRUBY_PLATFORMを見てspec.add_dependencyしたらローカルでbundle installすると大丈夫だけど、rubygems.org経由でgem installするとlinuxなのにrb-appscriptが入ってしまってcocoa.hが無くてビルドエラーする。


インストール

for Mac
% gem install skype rb-appscript

for Linux
% gem install skype ruby-dbus

Gemfile でインストールする場合
gem 'skype'
gem 'rb-appscript' if RUBY_PLATFORM =~ /darwin/i
gem 'ruby-dbus' if RUBY_PLATFORM =~ /linux/i

使い方

これはRubyのメタプログラミングを使った超単純なラッパーなので、使う前にSkype Developer – Skype Desktop API Reference Manualを読んで理解してください
読めばわかると思う


require 'rubygems'
require 'skype'

Skype.config :app_name => "my_skype_app"

## send message
Skype.message "USER_NAME", "hello!!"

## call
Skype.call "USER_NAME"

## get recent chat list
puts Skype.search("recentchats")

## send message to group chat
Skype.chatmessage "#name1/name2;$a1b2cdef3456", "hello chat!!"

簡単に電話かけたりチャットに投稿したりできますね。やりましたね。
しかもMacでもLinuxでも動く。


くわしい内部実装

Skype.messageやSkype.callなどの関数は実装されていません。
実装されていませんが、method_missingを使った簡単なメタプログラミングでSkype.execに変換されて実行されます。
以下と等価です
## send message
Skype.exec "MESSAGED USER_NAME hello!!"

## call
Skype.exec "CALL USER_NAME"

## get recent chat list
puts Skype.exec "SEARCH recentchats"

## send message to group chat
Skype.exec "CHATMESSAGE #name1/name2;$a1b2cdef3456 hello chat!!"
Skypeには各OS毎に通信方法こそ違うものの、同じフォーマットのQuery StringでやりとりするAPIがあります。
それが”MESSAGE ユーザー名 本文”とか、”CALL ユーザー名”です。
messageという関数を呼び出すとmethod_missingによってexec “MESSAGE ~~”になるわけですね


これだけの実装です
module Skype
def self.method_missing(name, *args)
self.exec "#{name.upcase} #{args.join(' ')}"
end

def self.exec(command)
Appscript.app("skype").send_ :script_name => self.config[:app_name], :command => command
end
end
実際には、LinuxとMacでSkype.execの中の実装がわかれています。


返り値がまだ全部Stringなので、SEARCH RECENTCHATSとかGET CHATMESSAGEとかを自分でparseしないとならないのがつらい。そのうちなんとかする。

0

信用出来ないRubyスクリプトを安全にevalしたかった

たぶん大丈夫だと思うんだけどヤバかったら教えて下さい。
(皆様からの温かいトマホーク(2)(3)によるとやっぱダメなようです

要件

– ブラウザでRubyのコード書かせて、サーバーに保存してサーバーで実行したい
– 危険な事はされたくない。ファイルへのアクセスやコマンドの実行、やたら時間のかかる処理など
– 安全に実行できたらコードの返り値を取得したい。コードが危険だったらエラーを取得したい。
– 危険な事されても、コード実行しているプロセスは終了しないでエラーをブラウザに返したい。
– コードはWebサーバーと同じプロセスで実行したい

調査

ということで調べていたらsafelevelを使えばいいらしい
Programming Ruby: The Pragmatic Programmer's Guide
Rubyのセーフレベル4環境とその使い方 – ¬¬日常日記

$SAFEに0〜4までの数値を入れるとセーフレベルが変化する。Ruby起動時は0。
4(最高)の時はファイル操作やコマンド実行、標準出力もできなくなる。
一度上げたセーフレベルは下げれないが、Threadを作ってその中でセーフレベルを上げればThread外はセーフレベル0のままになるのと、
セーフレベル0の時にProcオブジェクトを作ればセーフレベル0環境をThread内に持ってこれるのを利用すれば、要件は満たせそうだ。


できた

このSandBoxクラスを使えば安全に実行できる。
SandBox.eval(コード, コールバック)で実行する。
コールバックでコードの実行結果か例外オブジェクトが返ってくる。

sandbox.rb
require 'timeout'

class SandBox
def self.eval(code)
throw ArgumentError, "callback not given" unless block_given?
result_or_error = nil
begin
Timeout::timeout 1 do
result_or_error = Thread.new do
$SAFE = 4
instance_eval code
end.value
end
rescue StandardError, SecurityError, Timeout::Error => e
result_or_error = e
end
yield result_or_error
end
end

## 安全なコードを実行
goodcode = File.open("goodcode.rb").read
SandBox.eval goodcode do |res|
puts "-- goodcode.rb --"
p res
end

## 危険なコードを実行
badcode = File.open("badcode.rb").read
SandBox.eval badcode do |res|
puts "-- badcode.rb --"
p res
end



安全なコード(goodcode.rb)と危険なコード(badcode.rb)を実行してみた。

goodcode.rb – ちょっと計算して値を返しているだけなので安全
"cool!" * 10


gadcode.rb – ファイルアクセスや標準出力をしていて危険
File.delete "./hoge" if File.exists? "./hoge"  # ファイル削除
puts "this is bad code" # 標準出力
system "ls" # コマンド実行
1+2*3/4 # ふつうの計算


sandbox.rb の実行結果
-- goodcode.rb --
"cool!cool!cool!cool!cool!cool!cool!cool!cool!cool!"
-- badcode.rb --
#<SecurityError: Insecure operation `eval' at level 4>
File.exists? の時点でエラーがでていた。もちろんputsもsystemもSecurityErrorになる。
無限ループとか書かれても1秒でタイムアウトする。

このセーフレベル4環境を破るには、セーフレベル0を持っているコールバック関数を呼び出す必要があるのだが
ちょっと試した感じでは無理っぽいのでまあまあ安全だと思う。
ヤバイ処理を実行させられそうになったら即例外をコールバックするので、アプリも落ちないし俺の要求を満たしてる。

物凄いサイズのHash作られたりしたらダメかも。

0

1台のArduinoを複数のRubyプロセスで共有できるようになった

Arduinoを複数のRubyプロセスで共有したいの続き


Arduino Firmata on Ruby使うと複数のプログラムで同時に1台のArduino動かせるようになった。

v0.2.9をリリースした

gem install arduino_firmata


結局dRuby使う必要は無かった。
昔はserialport gemは1つのプロセスがportを専有してしまっていた気がするのだが、そうでもなくなってた。
Firmataの初期化プロセスを見なおしたら複数プロセスから使えるようになった。


サンプルの
arduino_firmata/samples at master · shokai/arduino_firmata · GitHub
– led_blink.rb
– on_analog_read.rb
– servo.rb
– digital_read.rb
を同時に動かせた。


1つのArduinoにあるセンサーを同時の複数のアプリから読み取ったりできるようになってコードがすっきりします。
1台で複数アプリ動かせて、アプリがそれぞれ別々にバージョン管理できるというのはものすごい利点です。よほど速度が重要な処理でない限りArduinoにコード直書きやめて、Ruby Firmata使いましょう。

0

LAN内の全てのホストのIPアドレスとMacアドレスを取得する

1から254までpingうって反応あったホストにarpしてMacアドレスを取得した。
昔LAN内の全ホストにgrowl送りつけたりしていた時と同じノリだ

Raspberry PiがDHCPでどこに行ってしまったかもわかるし、定期的に実行すれば端末が(つまり人が)増えたり減ったりするのもわかって楽しい。



実行にはparallel gemが必要

gem install parallel
ruby macaddrs.rb 192.168.1.1