Ruboto: gemを使う。

RubotoのGitHubに書いてあるので、まぁいらないかもと思ったけど一応。
RubotoならActiveRecordをAndroidでも使えるよ!というチュートリアルのところで、gemの使い方が書いてあった。
Gemfile.apkを作って保存せぇ!と。

しかし、俺はGemfile.apkというAndroidアプリを作れということか?そうなのか?と困惑した。ぱっと見、そう思ってしまったのだが、どうもそうではなく、普通にテキストファイルでいい。apk用のGemfileという意味だと解釈すればいい。

Gemfile.apkの中身はこう書いた。まんまコピーしてきている。

source "http://rubygems.org"

gem 'activerecord', '!=3.1.0', '!=3.2.0', '!=3.2.1', '!=3.2.2'
gem 'activerecord-jdbc-adapter'
gem 'activerecord-jdbcsqlite3-adapter'
gem 'sqldroid'

【訂正】
その後、以下のコマンドを流す。

rake bundle

これでbundle installしてくれる。

コンパイル前にbundle installされてなかったら、自動的に行われる。

rake install start

これでもよい。

gemが使えるとなると、Rubotoの強みがでてくるんじゃないだろうか?
でもまだアプリ作ってないのでなんともいえない。


メタプログラミングRuby読書会をしました

6/9(土)にメタプログラミングRuby読書会を私の家で行いました。
参加者は4人。

  • @kazuhisa1976(かずさん)
  • @sutorada(すーさん。高校生)
  • 吉田和弘さん(twitterアカウントわからず。Rubyの本を書かれてる)
  • @patorash(パト)

初めてのプログラミング本の読書会ということで、どのように進めていくかを考えた。音読を交代でしていき、コードが出てきたところで写経。irbで試したり、実際にコードを書いて実行して、という方式に。

ATND上の告知では、月曜日と火曜日(1章と2章。メタプログラミングRubyは月曜から金曜の5日の物語となっている)をやるというふうにしていたのであるが、内容が濃いので、月曜日を終わらせるだけで3時間半くらいかかった。

しかし、音読をしつつ、実際にコードをみんなで書いて試していくというのは、かなりタメになる。集中力を使うのでどっと疲れるが、なんとなく読み飛ばしてしまっていた部分についても深く勉強できるし、よくわからなかった部分について質問できる。
勉強会でよくありがちな、参加しただけで勉強した気になってしまう、というようなことはない。なぜなら、実際にコードを書き、本を読み、すげー勉強しているからだ。

月曜日のメソッド探索についてなどが終わった後、火曜日の動的ディスパッチと動的メソッド定義などを勉強したのだが、冗長的なメソッドがぐんぐん短くなっていく様をみているとRuby変態だー!と叫びたくなる。まぁ実際に、変態ですね。だがそれがいい。

この勉強会はすごく面白かったから、続けていこうという話になり、次回は8/4(土)に行う事にしました。
本当は7月にやりたかったんですが、来月は岡山Ruby会議あるし、岡山Javaユーザー会の勉強会あるし、AWS勉強会もあるし、勉強会ありすぎるんだよ、岡山!(行くけどね!!)

それにしても、メタプログラミングRubyですが、本屋でもう売ってないし、Amazonで見かけてもマーケットプレイスで超高くなってるし、アスキーさん、お願いだから増刷してください!!すごくいい本なんですよ!


Rubotoを読み解いていく(ListView)

Rubotoでウィジェット(View)を使おうとしたら、

ruboto_import_widgets :LinearLayout, :ListView, :TextView

とかをする必要がある。まぁ何をやっているのかはわかるが、実際にどういう処理をやっているのかを読んでみようと思って、ruboto/widget.rbを開いてみた。

def ruboto_import_widget(class_name, package_name="android.widget")
  klass = java_import("#{package_name}.#{class_name}") || eval("Java::#{package_name}.#{class_name}")
  
  return unless klass

  RubotoActivity.class_eval "
     def #{(class_name.to_s.gsub(/([A-Z])/) { '_' + $1.downcase })[1..-1]}(params={})
        if force_style = params.delete(:default_style)
          rv = #{class_name}.new(self, nil, force_style)
        elsif api_key = params.delete(:apiKey)
          rv = #{class_name}.new(self, api_key)
        else
          rv = #{class_name}.new(self)
        end

        if parent = (params.delete(:parent) || @view_parent)
          parent.addView(rv, (params.delete(:parent_index) || parent.child_count))
        end

        rv.configure self, params

        return rv unless block_given?

        old_view_parent, @view_parent = @view_parent, rv
        yield
        @view_parent = old_view_parent

        rv
     end
   "

  setup_list_view if class_name == :ListView
  setup_spinner if class_name == :Spinner
  setup_button if class_name == :Button
  setup_image_button if class_name == :ImageButton
  setup_linear_layout if class_name == :LinearLayout
  setup_relative_layout if class_name == :RelativeLayout
end

メタプログラミングRubyを読んでいたおかげで意味がわかる!!
まず最初の行で、クラスのインポートを行っている。3行目でクラスがなければリターン。

クラスがあった場合、RubotoActivityをオープンしてウィジェット名のメソッドを定義している。:ListViewを渡したら、RubotoActivity.list_viewメソッドができるわけだ。ブロックを渡しているとなんか処理してくれるっぽい。

:ListViewがある場合、setup_list_viewメソッドが実行される。
では、setup_list_viewを見てみよう。

def setup_list_view
  Java::android.widget.ListView.class_eval do
    attr_reader :adapter, :adapter_list

    def configure(context, params = {})
      if params.has_key? :list
        @adapter_list = Java::java.util.ArrayList.new
        @adapter_list.addAll(params[:list])
        item_layout = params.delete(:item_layout) || R::layout::simple_list_item_1
        @adapter    = Java::android.widget.ArrayAdapter.new(context, item_layout, @adapter_list)
        setAdapter @adapter
        params.delete :list
      end
      if params.has_key? :adapter
        @adapter = params[:adapter]
      end
      super(context, params)
    end

    def reload_list(list)
      @adapter_list.clear
      @adapter_list.addAll(list)
      @adapter.notifyDataSetChanged
      invalidate
    end
  end
end

これもまた、ListViewクラスをオープンしてメソッドを定義している。メタプログラミング恐るべし。
ListView.configureメソッドとListView.reload_listメソッドを定義して、ListViewを使いやすくしているようである。

チュートリアルにあったソースコードを元に、ListViewを使ってみたので、載せておく。

ruboto_import_widgets :LinearLayout, :TextView, :ListView

$activity.start_ruboto_activity "$sample_activity" do
  setTitle 'This is the Title'

  def on_create(bundle)
    self.content_view =
        linear_layout(:orientation => :vertical) do
          @list = %w(aaa bbb ccc ddd eee fff ggg)
          @list_view = list_view(:list => @list,
                                 :on_item_click_listener => proc{|av, v, pos, item_id|
                                   toast(@list[pos])
                                 })
                     
    end
  end
end

:ListViewを読み込んでいるため、RubotoActivity内ではlist_viewメソッドが使える。
パラメータとして:list => @listを渡しているが、:item_layoutを省略しているので、R::layout::simple_list_item_1というListViewでは最もシンプルなViewが自動的に使われ、配列がArrayAdapterに渡されてListViewが表示される。

:on_item_click_listenerを無名関数(proc)で定義できるみたいだった。このソースでは、ListViewの押された行の文字列をトーストで表示するという処理をしている。非常に簡単な処理だが、これを調べるだけでもひと苦労するのである。コードは短いけど開発環境がいいのがないのかなぁ、Rubotoは。


Android: Rubotoの調査中

うちの会社のLTのネタにするために、Rubotoを調査中。ネタバレしてしまうけれど、メモしながらじゃないと忘れるからメモっとく。
RubotoはJruby on Androidを動かすための環境。Ruboto Coreがインストールされていれば、アプリを起動することができる。

まぁ詳しくはGitHubをみればわかる(元も子もない)
https://github.com/ruboto/ruboto

Ruboto自体はgemで公開されているので、

gem ruboto

でインストール可能。
Rubotoでのプロジェクトの作成もrubotoコマンドを使えばできる。
rails newするような感覚だ。ここら辺までは非常に楽だ。

また、アプリのインストールはrakeコマンドを使って可能。
ここら辺も非常にRubyぽくていい。
rake install startとかしたら、インストールしてアプリを起動してくれるので、素晴らしい。

ただ、日本語情報が少ないのと、一歩ふみこんでアプリを作ろうと思ったら途端にわからない。
まぁそれは俺の実力不足でもあるのだが(Ruby力も低いし)
まぁとはいってもチュートリアルがGitHubのWikiに書いてあるので、そういうのをやっていくだけでも多少はわかるようになる…気がする…が…。

あと致命的なのが、アプリの起動が超遅い。

でもRubyの文法でAndroidアプリを作るというのはなんか新鮮だ。

小さく目標を持って、1つずつこなしていってみようと思う。