2012年7月20日金曜日

Play framework 2.0.2 (Java) Guiceプラグインを利用する

Play 2.0 for Javaで、Guiceプラグインを利用するため試行錯誤したので備忘録として記録。 project/Build.scalaに依存関係を登録。
    val appDependencies = Seq(
      // Add your project dependencies here,
      "com.typesafe" % "play-plugins-guice" % "2.0.3"
    )
ターミナルから依存関係の取得を実行。
$ play dependencies
Eclipseで編集するので、Eclipse用の設定を更新。
$ play eclipsify
ここまでで環境構築は完了。 ここから、環境設定。 conf/直下にplay.pluginsファイルを作成して、GuicePluginを利用することを宣言する。
ここハマッタ。pluginを利用する場合は準備する必要アリ。
# conf/play.plugins
1500:com.typesafe.plugin.inject.GuicePlugin
依存関係をバインドするinitializerを作成。
app/module/Dependencies.javaを作成。
はまったのがここで、パッケージ名とクラス名が決められているので注意
かっこつけてmodules.Dependencies.javaなんてつくったら動かなかった。
DI対象は好きな場所に好きな名前で準備。
package module;

import services.UserSearchService;
import services.impl.UserSearchServiceImpl;

import com.google.inject.Binder;
import com.google.inject.Module;

public class Dependencies implements Module {

    public void configure(final Binder binde) {
        binde.bind(UserSearchService.class).to(UserSearchServiceImpl.class);
    }
}
後はDI先でDIフィールドの宣言を行う。
ここも注意点アリ。
@javax.inject.Injectである必要アリ。
package controllers;

import static play.Logger.debug;

import javax.inject.Inject;

import org.codehaus.jackson.node.ObjectNode;

import play.data.Form;
import play.libs.Json;
import play.mvc.Controller;
import play.mvc.Result;
import services.UserSearchService;
import forms.UserSearch;

public class UserController extends Controller {

    @Inject
    static UserSearchService userSearchService;
}
これで動くはず。

2011年9月9日金曜日

Ubuntu10.04でGAE on Python

GAE on Python を Python の勉強がてらやってみようかと。
で、イロイロ踊らされてしまった内容をツラツラと書こうかと。

まず、前提条件としてGAEはPython2.5でないとイカンという有名な話がある。
Ubuntu10.04はPython2.6が標準インストールされている。
根が横着なので、Python2.6でGAEを動かそうと考えた訳で。
結論から言うとちゃんと動きました。App Engine SDK に手を加えることなく動きます。

何が問題だったかと言うと、まず、単純にSDKの最新版をダウンロードして適当なフォルダに解凍した。んでチュートリアルに従ってプロジェクトを作成し実行したら、下記エラーが発生した。

Warning: You are using a Python runtime (2.6) that is more recent than the production runtime environment (2.5). Your application may use features that are not available in the production environment and may not work correctly when deployed to production.
INFO     2011-09-09 08:40:30,324 appengine_rpc.py:159] Server: appengine.google.com
WARNING  2011-09-09 08:40:30,330 datastore_file_stub.py:512] Could not read datastore data from /tmp/dev_appserver.datastore
INFO     2011-09-09 08:40:30,333 rdbms_sqlite.py:58] Connecting to SQLite database '' with file '/tmp/dev_appserver.rdbms'
Traceback (most recent call last):
  File "google_appengine/dev_appserver.py", line 77, in 
    run_file(__file__, globals())
  File "google_appengine/dev_appserver.py", line 73, in run_file
    execfile(script_path, globals_)
  File "/app_path/google_appengine/google/appengine/tools/dev_appserver_main.py", line 689, in 
    sys.exit(main(sys.argv))
  File "/app_path/google_appengine/google/appengine/tools/dev_appserver_main.py", line 653, in main
    default_partition=default_partition)
  File "/app_path/google_appengine/google/appengine/tools/dev_appserver.py", line 4971, in CreateServer
    server = HTTPServerWithScheduler((serve_address, port), handler_class)
  File "/app_path/google_appengine/google/appengine/tools/dev_appserver.py", line 4999, in __init__
    request_handler_class)
  File "/usr/lib/python2.6/SocketServer.py", line 400, in __init__
    self.server_bind()
  File "/usr/lib/python2.6/BaseHTTPServer.py", line 108, in server_bind
    SocketServer.TCPServer.server_bind(self)
  File "/usr/lib/python2.6/SocketServer.py", line 411, in server_bind
    self.socket.bind(self.server_address)
  File "", line 1, in bind
socket.error: [Errno 98] Address already in use

ここでちゃんとエラーを読んでググってればここで終わったのにちゃんと読まなかったため
ぁぁ、やっぱりPython2.5が要るんだなと判断しちゃって、瞑想しちゃいました。
ちなみに、このエラーはAddress already in useと出てて、アドレス使ってますよと言っています。
Tomcatをインストールしていて、それが自動起動しちゃってたため、ポート8080がかぶって起動しなかったのです。
それをPython2.5入れて、環境汚さないようにvirtualenv入れてとかイロイロやったけど、起動するときに
google_appengine/dev_appserver.py -p 8081 /app_path
で、起動しちゃいました。 なんか、どっと疲れた。 これから、ちょっとずつPythonやってなんかサイト作ります。

2011年6月8日水曜日

ubuntu 10.04 で Ruby(環境構築編) その2

だいぶ前に ここで RVM の設定方法を書き込んだが、今ではだいぶ楽になっていた。
ビックリしたのでメモ。

1.事前準備

rvm を動作させるのに必要なものを一式インストールする。
sudo aptitude install git-core curl zlib1g-dev

rvm をセットアップする場所を作成して、移動する。
mkdir ~/.rvm
cd ~/.rvm

2.インストール

以下のコマンドを実行する。
bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
すると、カレントディレクトリに rvm 絡みが一式できてる。(ここでは ~/.rvm 配下)

後は、コンソールに表示されている内容の修正を .bashrc or .bash_histry に加える。
具体的には、
#.bashrc
[ -z "$PS1" ] && return
の欄を
#.bashrc
if [[ -n "$PS1" ]] ; then

  # ... original content that was below the '&& return' line ...
  [ -z "$PS1" ] && return

fi # <= be sure to close the if at the end of the .bashrc.

# This is a good place to source rvm v v v
[[ -s "/home/matsumoto/.rvm/scripts/rvm" ]] && source "/home/matsumoto/.rvm/scripts/rvm"  # This loads RVM into a shell session.
に書き換えるだけ。

書き換え後は、
source .bashrc
で変更を反映させる。



■おまけ

RVM のコマンドリスト(少しだけ)
rvm list インストールされている Ruby を一覧表示
rvm list known rvm でインストール可能なすべてのバージョンを表示
rvm install version 指定されたバージョンの Ruby をインストール
rvm [use] version [--default] 使用する Ruby を選択する。
--defaultを指定すれば、それが常に利用される。
rvm uninstall version 指定したバージョンの Ruby を削除する。
rvm --help 困ったときはヘルプを見るべし!


■最後に

RVM 自体のアンインストール
rvm implode

した後に、RVM_HOME のソース類をゴソッと削除して、.bashrc or .bash_histry を修正する。

かなぁ。

近状

なんか、久しぶりの投稿になる。

身の回りが忙しくブログ書いてる暇がない。
けどネタはいっぱい増えている。
  • ubuntu 10.04 64bit の日本語化
  • ubuntu 10.04 の OpenOffice.org を LibreOffice に変更
  • Play framework for Scala のこと
  • RVM のインストール方法(改)
  • JRuby on Rails のこと
時間見つけて書き込んでいこう。
(いけるかな?)

2011年4月8日金曜日

Rails3 routes

やはり触れることになってしまった routes.rb。これを期に意味不明な部分を理解しよう。
勉強して得られた情報をメモ。

routes.rb にて生成される URL については rake routes コマンドにて確認できる。
記載する出力結果はすべて rake routes にて出力された結果です。


match(path, options={})


route 師弟の基本。アクセス可能な path を指定してやることで、HTTP リクエストを処理できるようになる。
match "foo/bar"
# foo_bar  /foo/bar(.:format) {:controller=>"foo", :action=>"bar"}
path は必ず一つ以上 "/" を入れてやる必要がある。
  • match "foo" => NG
  • match "foo/bar" => :controller=>"foo", :action=>"bar"
  • match "foo/bar/buz" => :controller=>"foo/bar", :action=>"buz"
"/" を複数指定した場合、controller の namespace を表すように解釈される。

以下指定可能オプション。(全部じゃないかも)

:controller
:action
動作させるコントローラとアクションを指定する。
必ずセットで指定する。
path に "/" を入れなくてもこのオプションを指定してやれば解釈されるようになる。
match "foo", :controller=>"foo", :action=>"bar"
# foo  /foo(.:format)  {:controller=>"foo", :action=>"bar"}
:to :controller, :action の短縮形。"#" でコントローラとアクションを区切って記述する。
match "foo", :to=>"foo#bar"
:to の記述自体を短縮する方法もある。
match "foo"=>"foo#bar"
:via HTTP メソッドを付加する。
match "foo/bar", :via=>:get
# foo_bar GET /foo/bar(.:format) {:controller=>"foo", :action=>"bar"}
:as ルート名を指定する。ルート名は form_tag や link_to などで指定できる名前で、その名前に該当する path を生成して form を作成してくれる。
match "foo/bar", :as=>"bar"
# bar GET /foo/bar(.:format) {:controller=>"foo", :action=>"bar"}


get

post

put

delete


match の :via オプションの HTTPHelper メソッド。
指定可能なパラメータは match と同様。なので
get "foo/bar", :via=>:delete
なんていうふざけた記述もちゃんと解釈してくれる。(HTTP メソッドは GET になります)


resource

resources


Rails のルールに従って RESTful 的な URL を自動で生成してくれる有名なメソッド。
単数形の場合は resource を、複数形の場合は resources を利用する。*指定する名前も単数形、複数形を意識して指定してやる。(resource :foo, resources :foos)
二つの違いは一覧出力 URL (scaffold 生成時の index アクション) の有無の違い。

以下指定可能オプション。(全部じゃないかも)
:as ルート名に利用する別名。
resource "foo", :as=>"bar"
#   bar POST   /foo(.:format)      {:action=>"create", :controller=>"foos"}
# new_bar GET    /foo/new(.:format)  {:action=>"new", :controller=>"foos"}
#edit_bar GET    /foo/edit(.:format) {:action=>"edit", :controller=>"foos"}
#       GET    /foo(.:format)      {:action=>"show", :controller=>"foos"}
#       PUT    /foo(.:format)      {:action=>"update", :controller=>"foos"}
#       DELETE /foo(.:format)      {:action=>"destroy", :controller=>"foos"}
:controller 処理するコントローラを指定する。
アクション名は Rails ルールに従う必要がある。
resource "foo", :controller=>"bar"
#   foo POST   /foo(.:format)      {:action=>"create", :controller=>"bar"}
# new_foo GET    /foo/new(.:format)  {:action=>"new", :controller=>"bar"}
#edit_foo GET    /foo/edit(.:format) {:action=>"edit", :controller=>"bar"}
#        GET    /foo(.:format)      {:action=>"show", :controller=>"bar"}
#        PUT    /foo(.:format)      {:action=>"update", :controller=>"bar"}
#        DELETE /foo(.:format)      {:action=>"destroy", :controller=>"bar"}
:path URL を置き換える。
resource "foo", :path=>"b/a"
#   foo POST   /b/a(.:format)  {:action=>"create",:controller=>"foos"}
# new_foo GET    /b/a/new(.:format) {:action=>"new",:controller=>"foos"}
#edit_foo GET    /b/a(.:format)     {:action=>"edit",:controller=>"foos"}
#         GET    /b/a(.:format)     {:action=>"show",:controller=>"foos"}
#         PUT    /b/a(.:format)     {:action=>"update",:controller=>"foos"}
#         DELETE /b/a(.:format)     {:action=>"destroy",:controller=>"foos"}
:only 作成される URL を絞り込む。指定されたアクションのみ URL が生成される。
resources "foo", :only=>["index"]
# foo_index GET /foo(.:format) {:action=>"index", :controller=>"foo"}
:except 作成する URL を削除する。指定されたアクションは URL が生成されない。
こう書けば、:only と同じ結果になる。
resources "foo", :except=>["create","edit","new","show","update","destroy"]
# foo_index GET /foo(.:format) {:action=>"index", :controller=>"foo"}
:module controller に namespace を付加する。
resources "foo", :only=>["index"], :module=>"module"
# foo_index GET /foo(.:format) {:action=>"index", :controller=>"module/foo"}

has_many の関係を定義する場合、block を渡して resources を定義する。

resources "foo" do
  resources "bar"
end


scope


URL に namespace を付けるイメージ。controller には付かない所がポイント。
scope "scope" do
  resources "foo", :only=>["index"]
end
# foo_index GET /scope/foo(.:format) {:action=>"index", :controller=>"foo"}

block 内に match(path) を指定した場合、namespace として扱われる。(controller にも scope が付加される)
scope "scope" do
  match "foo/bar"
end
# foo_bar  /scope/foo/bar(.:format) {:controller=>"scope/foo", :action=>"bar"}
:to オプションを付けて controller と action を宣言してやれば、URL のみ対象となる。

以下指定可能オプション。(全部じゃないかも)
:module controller の namespace を指定する。
scope "sco", :module=>"mod" do
  resources "foo", :only=>["index"]
end
# foo_index GET /sco/foo(.:format) {:action=>"index", :controller=>"mod/foo"}
:as ルート名に prefix を付加する。resources は別名に置き換えるがこっちは付加する。
scope "sco", :as=>"as" do
  resources "foo", :only=>["index"]
end
# as_foo_index GET /sco/foo(.:format) {:action=>"index", :controller=>"foo"}

controller のみに namespace を付加したい場合、
scope :module=>"module" do
  resources "foo", :only=>["index"]
end
としてやればよい。


namespace


URL 名, URL と controller に(要は全てに) namespace を付加する。
namespace "nspe" do
  resources "foo", :only=>["index"]
end
# nspe_foo_index GET /nspe/foo(.:format) {:action=>"index", :controller=>"nspe/foo"}

以下指定可能オプション。(全部じゃないかも)
:as ルート名に prefix を付加する。動きは scope と同じ。


controller


controller を一括で指定できる。
controller "foo" do
  get "search"
end
# search GET /search(.:format) {:action=>"search", :controller=>"foo"}
match でエラーになるパターン ("/" なし) も controller を付加してくれてちゃんと動く。
優先度は block 内のメソッドの方が高く、resources "bar" や match "bar/buz" など指定しても適用されない。(:controller=>"bar"になる)

以下指定可能オプション。(全部じゃないかも)
:path URL に prefix を付加する。デフォルトでは controller 名は URL に付加されないので、必要な場合は指定する。
controller "foo", :path=>"foo" do
  get "index"
end
# index GET /foo/index(.:format) {:action=>"index", :controller=>"foo"}
:as ルート名に prefix を付加する。動きは scope と同じ。


root


root ("/") にアクセスされた際の routes を指定する。
root :to=>"foo#bar"
# root  /(.:format) {:controller=>"foo", :action=>"bar"}



長文でダラダラとまとめたが、まだまだ全部でない感じがする。(ソース見た感じ)
けど、これで routes への恐怖心が若干薄まった感がある。

2011年3月30日水曜日

Rails3 :method => :delete が動かない件

環境
  • Windows XP SP2
  • Internet Explorer 6
  • ruby 1.9.2p136 (2010-12-25) [i386-mingw32]
  • Rails 3.0.3

上記環境にて、link_to :method => :delete が正しく動作しない。
scaffold で生成したコンテンツでもダメだった。
原因を調査してみたら、rails.js 内の以下の箇所で TypeError が発生していた。
function handleMethod(element) {
  var method = element.readAttribute('data-method'),
      url = element.readAttribute('href'),
      csrf_param = $$('meta[name=csrf-param]')[0],
      csrf_token = $$('meta[name=csrf-token]')[0];

  var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
  element.parentNode.insert(form); // ←←←この箇所

  if (method !== 'post') {
    var field = new Element('input', { type: 'hidden', name: '_method', value: method });
    form.insert(field);
  }

  if (csrf_param) {
    var param = csrf_param.readAttribute('content'),
        token = csrf_token.readAttribute('content'),
        field = new Element('input', { type: 'hidden', name: param, value: token });
    form.insert(field);
  }

  form.submit();
}

debug してみると、たしかに element.parentNode に insert という function は存在していなかった。
とりあえず、以下のように修正することで回避可能。
//  element.parentNode.insert(form);
  element.insert(form);

以下の環境だと、上記修正を行わなくてもちゃんと動いた。
  • Ubuntu 10.04
  • Google Chrome 10, Firefox 4.0
  • ruby 1.9.2p136 (2010-12-25 revision 30365) [i686-linux]
  • Rails 3.0.4

怪しいのは IE6。別バージョンで試すことができない環境なのでどうしようもない。
んー、おかしい。

2011年3月26日土曜日

Rails3 selectbox を動的に変更する。

Rails3 で selectbox (combobox) の中身を動的に入れ替える処理が必要になった。
ググってみると大概 observe_field を利用した Ajax.update を行うものだったが、Rails3 には
observe_field が削除されている。(正確には Rails2.3.9 で削除されたっぽい)
なので無い脳ミソをフル稼働させて考えてみた結果をメモ。


■仕様(やりたい事・目指した事)

  • selectboxA を変更したら、その内容で selectboxB の内容が置き換わる。
  • n段に対応。(selectboxA → selectboxB → selectboxC → ... → selectbox(N))
    今回は大分類・中分類・小分類の 3 段で考える。
  • 全ての selectbox の先頭に :inclide_blank => true を入れる。
  • DRY を徹底する。(同じコードはなるべく書かない)

調べてみるとやり方はかなり豊富。仕様と照らし合わせてどのやり方をチョイスするか選んだ方がよさそう。
今回の仕様から、RJS つかって動的にコンテンツを更新する方法にした。


■考え方

まず selectbox のデータを格納する model を考える。今回は kind(分類) という model を作成する。
kind
PKid : integer
 name : string
FKkind_id : integer
kind_id が外部キーとなっており、自分の親のキーを保持する。
(中分類のデータなら所属する大分類のIDを保持する)

次に html タグの動的入れ替えだが、以下の範囲を入れ替えるように考えた。
<p id="middle_select">
<select id="kind_id_middle"> <option></option> *黄色の部分を Ajax で入れ替える </select>
</p>
理由は、selectbox の先頭に blank option をつけたかったから。直に <option value=""></option>
タグを書けば option 範囲の入れ替えでもいけるのだが、それはあまりやりたくなかった。(要こだわり)


■実装

model は作ってあり、データも適当にいれているものとする。
大分類の kind_id は 0 を指定しておく。(TOP レベルで親はいませんという意味で)
必要な routes.rb は記述してあるものとする。
参考データ
idnamekind_id
1大分類10
8中分類11
22中分類21
43小分類18
44小分類28
65小分類322

初期表示時の view。
# select.html.erb

<%= label "kind_id", "large", "大分類:" %> <%= select "kind_id", "large", Kind.where("kind_id = 0").map{|p| [p.name, p.id]}, {:include_blank => true}, {:onchange => remote_function(:url => {:action => "change_select"}, :with => "'kind_id[large]=' + escape(this.value)")} %>

<%= render :partial => "middle_select", :locals => {:middle_kinds => @middle_kinds} %>

<%= render :partial => "small_select", :locals => {:small_kinds => @small_kinds} %>

ポイントは
  • render 使って動的入れ替えする部分を外だしにする。(RJS でも render すれば DRY になる)
  • observe_field がないので、onchange イベントに remote_function ひっつける。
  • remote_function :with で大分類の値を POST する。
  • 複数のコンテンツをいっぺんに変えるため (大分類 → [中分類, 小分類]) remote_function :update は未指定。

中分類の view。select.html.erb と同じ階層に作成する。
# _middle_select.html.erb
<%= label "kind_id", "middle", "中分類:" %>
<%= select "kind_id", "middle",
      middle_kinds.map{|p| [p.name, p.id]},
      {:include_blank => true},
      {:onchange => remote_function(:url => {:action => "change_select"},
                                    :with => "'kind_id[middle]=' + escape(this.value)")} %>

小分類の view。select.html.erb と同じ階層に作成する。
# _small_select.html.erb
<%= label "kind_id", "small", "小分類:" %>
<%= select "kind_id", "small",
          small_kinds.map{|p| [p.name, p.id]},
          {:include_blank => true} %>
中分類には選択したら小分類を入れ替える必要があるので onchange イベントを追加するが、小分類は入れ替え処理が不要なので、onchange は省略する。

次は controller。
# controller.rb
# 初回表示時 action
def select
  @middle_kinds = []
  @small_kinds = []
end

# onchange 時のイベント
def change_select
  if params[:kind_id][:large]
    @middle_kinds = params[:kind_id][:large] != "" ?
        Kind.where("kind_id = #{params[:kind_id][:large]}") :
        []
    @small_kinds = []
  else
    @middle_kinds = nil
    @small_kinds = params[:kind_id][:middle] != "" ?
        Kind.where("kind_id = #{params[:kind_id][:middle]}") :
        []
  end
end
初回表示時は大分類だけに値が設定されているシチュエーションなので、中分類、小分類は空にしておく。
change_select イベントは大分類、中分類で共同で利用するイベント。params[:kind_id][:large]が存在すれば(nil でなければ)大分類の change イベントと判別できるので、大分類変更時は、中分類を読み込んで小分類をリセットする。
中分類変更時は (params[:kind_id][:middle] != nil) 小分類のみ書き換えするため、あえて @middle_kinds に nil を設定して書き込み対象外の判別が行えるようにした。(後述 RJS 参照)

最後に RJS。select.html.erb と同じ階層に作成。
# change_select.js.rjs
if @middle_kinds
  page.replace_html "middle_select", :partial => "middle_select", :locals => {:middle_kinds => @middle_kinds}
  page.visual_effect :highlight, "middle_select"
end
page.replace_html "small_select", :partial => "small_select", :locals => {:small_kinds => @small_kinds}
page.visual_effect :highlight, "small_select"
@middle_kinds == nil 時は更新不要なので、更新するかしないかを最初に判定している。
一応、視覚的効果があるほうがインターフェース的に親切らしいので、動的入れ替え後は effect した。


■最後に

今回は複数更新することを念頭に入れていたため RJS を利用したが、1個のみの更新 (大分類 → 中分類) だけだったら、remote_finction :update => "middle_select" を指定して、controller で render :partial => "middle_select" ってする方が簡単。けど、こっちの方が応用 & 拡張性に優れている気がするので、RJS 積極的に使っていこうかな。

以上。長文でわかりづらく、失礼しました。