投稿者 okkez
2011-07-20 16:21:00 GMT
[ruby-list:48219] から始まるスレッドを見て久しぶりに更新してみることにしたら、まだできない。
今晩は諦めて寝てしまうかもしれないので doc.okkez.net/search/ のは止まるかもしれません。
okkez.net が止まっている間は以下のどちらかを利用してください。
- http://doc.ruby-lang.org/ja/search/
- http://rurema.clear-code.com/
doc.okkez.net は更新できたっぽいです。(追記)
カテゴリ Ruby, 雑記 | タグ るりま | コメントなし | トラックバックなし
投稿者 okkez
2011-07-14 15:09:00 GMT
RubyKaigi2011 に合わせて新しいノートPCを買ったので rabbit を入れてみた。
Debian sid に入れたんだけど、動くようになるまでちょっと苦労した。
インストールは gem i rabbit で出力されたエラーを解消するようにパッケージをひたすら入れるだけだった。
rabbit を起動しようとすると LoadError: can’t find theme: rabbit-images. が出力されてちゃんと動かなかった。
rvm を使っているのでたぶん $LOAD_PATH の設定が合わないんだと思った。
なのでコマンドのオプションに -I ~/.rvm/gems/ruby-1.9.2-p180/gems/rabbit-0.9.3/data/ を追加すればいい。
書いてから気付いたけど、普通に gem で入れたときでも同じなんじゃないかと思った。
一応、ちゃんと動いたので発表ができる。
カテゴリ Ruby, Debian | コメントなし | トラックバックなし
投稿者 okkez
2010-12-05 07:04:00 GMT
(この記事は
Ruby Advent Calendar jp: 2010 : ATND
の 5日目です。前日はauthorNariさんでした。)
buzztter - Twitter のイマを切り取ったー☆ や
Rubyリファレンスマニュアル全文検索 | るりまサーチ
で使われている groonga - an open-source fulltext search engine and column store. をご存知でしょうか。groonga 自体の説明は公式サイトを見てください。
groonga には Ruby バインディングの rroonga があります。
さらにその rroonga を ActiveRecord っぽいインターフェイスで使える ActiveGroonga があります。
ところで、先日 2010-11-29 に
全文検索エンジンgroongaを囲む夕べ #1 :
ATND
がありました。
その中で Ruby 枠の話もありました。
Ruby 枠の最後の方で Rails3 と ActiveGroonga を使ったデモがあったのです
が、それを自分なりに再構成してみたいと思います。
開発環境は groonga が 64bit 前提で、rroonga が Ruby1.9.2 必須です。
テーマは某ミニブログのクローンです。
名前は groonga を使っているのでちょっと長いですが groongatter にしましょう。
まずは、新しいプロジェクトを作成します。ActiveRecord と prototype.js
は使わないので、このプロジェクトには組み込みません。
$ rails new groongatter -O -J
Gemfile を編集して ActiveGroonga を使うようにします。
source 'http://rubygems.org'
gem 'rails', '3.0.3'
gem 'activegroonga'
編集したら以下のコマンドを実行します。
$ bundle install --path vendor/bundle
rroonga のインストール時に groonga の適切なバージョンが見つからない場
合は groonga もビルドするので時間がかかります。rroonga や groonga のビ
ルドに失敗したときは groonga のビルド環境を整えてからやってみてくださ
い。Debian や CentOS のパッケージは用意されているのでそちらをインストー
ルしてからやってみるのもいいでしょう。
次に config/application.rb を編集しておきましょう。
diff --git a/config/application.rb b/config/application.rb
index 1c1e1aa..296a1d5 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -7,6 +7,8 @@ require "action_mailer/railtie"
require "active_resource/railtie"
require "rails/test_unit/railtie"
+require "active_groonga/railtie"
+
# If you have a Gemfile, require the gems listed there, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env) if defined?(Bundler)
これで開発準備が整いました。
まずは、ユーザモデルを作ってみましょう。
$ rails generate scaffold user name:short_text
$ rake groonga:migrate
$ rails server
ウェブブラウザで http://localhost:3000/users にアクセスすると、いつもの
scaffold な画面が表示されるので適当に何人かユーザを作成しておく。
次につぶやきモデルを作ります。
$ rails generate scaffold tweet content:short_text user:reference
http://localhost:3000/tweets から適当につぶやいてみてください。
このとき User には既に存在する User の id を指定してください。
このままだと、表示が #<User:0x00000003bd9388> のようになってしまうので
view を修正します。とりあえず、 views/tweets/{index,show}.html.erb あ
たりを修正しておきましょう。
<h1>Listing tweets</h1>
<table>
<tr>
<th>User</th>
<th>Content</th>
<th></th>
<th></th>
<th></th>
</tr>
<% @tweets.each do |tweet| %>
<tr>
<td><%= tweet.user.name %></td>
<td><%= tweet.content %></td>
<td><%= link_to 'Show', tweet %></td>
<td><%= link_to 'Edit', edit_tweet_path(tweet) %></td>
<td><%= link_to 'Destroy', tweet, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New Tweet', new_tweet_path %>
<p id="notice"><%= notice %></p>
<p>
<b>User:</b>
<%= @tweet.user.name %>
</p>
<p>
<b>Content:</b>
<%= @tweet.content %>
</p>
<%= link_to 'Edit', edit_tweet_path(@tweet) %> |
<%= link_to 'Back', tweets_path %>
やっと全文検索用のインデックスを作ります。
$ rails generate migration create_full_text_search_index
こんな感じでインデックスを定義します。
オプション説明は groonga のドキュメントなどを見るといいと思います。
class CreateFullTextSearchIndex < ActiveGroonga::Migration
def up
create_table("bigram",
:type => :patricia_trie,
:key_type => "ShortText",
:default_tokenizer => "TokenBigram",
:key_normalize => true) do |table|
table.index("tweets.content")
end
end
def down
remove_table("bigram")
end
end
とりあえず、コンソールで試してみましょう。
$ rails console
>> result_set = Tweet.select(:content => "こん")
>> pp retult_set.first
=> #<Tweet id: 1,
user: #<User id: 1, updated_at: 1970-01-01 09:00:00 +0900, name:
"okkez", created_at: 2010-12-05 15:13:55 +0900>,
updated_at: 2010-12-05 15:14:58 +0900, created_at: 2010-12-05 15:14:44 +0900, content: "こんにちは!">
うまく検索出来ているみたいですね!
最後に検索用の UI を作っておきましょう。
$ rails generate controller search index
config/routes.rb はこんな感じにしておきます。
Groongatter::Application.routes.draw do
match "search", :to => "search#index", :as => "search"
resources :tweets
resources :users
end
app/controllers/search_controller.rb はこんな感じです。
class SearchController < ApplicationController
def index
@query = params[:query]
if @query.blank?
@result_set = []
else
@result_set = Tweet.select do |entry|
entry.content =~ @query
end
end
end
end
app/views/search/{index,_form}.html.erb はそれぞれこんな感じです。
<h1>Search</h1>
<%= render :partial => "form" %>
<%- unless @query.blank? -%>
<p>"<%= @query %>" で検索した結果は <%= @result_set.count %> 件です。</p>
<%- end -%>
<%- unless @result_set.blank? -%>
<ul>
<%- @result_set.each do |entry |-%>
<li><%= entry.user.name %>: <%= entry.content %></li>
<%- end -%>
</ul>
<%- end -%>
<typo:code>
<typo:code lang="html">
<%= form_tag(search_path) do %>
<p>
<%= text_field_tag(:query, "", :size => 25) %>
<%= submit_tag("検索") %>
</p>
<%- end -%>
あとはスペース区切りでの AND 検索くらいに対応したいところですが、それ
は自分で試してみてください。
カテゴリ Ruby | タグ Ruby | コメントなし | トラックバックなし
投稿者 okkez
2010-07-12 12:43:00 GMT
日曜日に「リファクタリング Ruby エディション」読書会の第二回に行ってきた。
二章から六章の途中まで読んだ。
二章から五障まではオリジナルのリファクタリングとあまり違いが無かった。
六章からリファクタリングのカタログが始まる。まだいくつかの節しか読んでいないが、参考になることも多いけど、
知っていることの方が多い。
でも、間違ったことも色々と書いてあるので、読みこなすには中級以上の知識が必要だと思った。
カテゴリ Ruby | タグ Ruby | コメントなし | トラックバックなし
投稿者 okkez
2010-05-31 14:40:00 GMT
リファクタリングRubyエディションの読書会に行ってきた。
まだ第一章しか読んでいないけど、一章は間違いが多すぎる。
まず、サンプルで紹介されているコードが徹頭徹尾動かない。一回も実行していないレベル。
動かない部分を直して、GitHub にアップしました。
他の勉強用のコードも混じってるけど、本の流れに沿ってリファクタリングして最低限動くようにした。
ログメッセージが全部 refactor になってるのはご愛嬌です。
第一章は残念な感じでしたが、二章以降に期待したいところです。
カテゴリ Ruby | タグ Ruby | コメントなし | トラックバックなし
投稿者 okkez
2010-05-25 13:53:00 GMT
HikiDoc - FrontPage.ja でプラグインを使いたかったので、プラグインを作る方法を調べてみた。
ソースを読めばプラグインを作る方法はわかる。
しかし、HikiDoc を使った上でプラグインも使っている例はあまりないようだった。
class CustomOutput < HikiDoc::HTMLOutput
def initialize(suffix = " />", options = {})
super suffix
@options = options
end
def inline_plugin(src)
# あんまり使わないので未実装
end
def block_plugin(src)
name, *args = parse_plugin(src)
klass = Plugins.const_get(name.classify)
@f.puts klass.new(@options).call(args)
rescue NameError => ex
@f.puts ex.message
end
private
def parse_plugin(src)
result = []
if /([a-z0-9_]+)\((.+)\)/m =~ src
result.push $1
result.push *$2.split(',').map(&:strip) if $2
end
result
end
end
こんな感じで HikiDoc::HTMLOutput を継承して inline_plugin と block_plugin を定義すれば良い。
今回の実装方法でプラグインを定義するには以下のようにする。
Rails で使用するのでヘルパーを使えるようにしておく。
module Plugins
end
class Plugins::Base
include ActionView::Helpers, ::ERB::Util
def initialize(options = {})
@options = options
end
def call(args)
raise 'must implement in subclass'
end
end
class Plugins::Echo < Plugin::Base
def call(args)
%Q!<pre>#{args.inspect}</pre>!
end
end
クラスを使わずに実装する方法もあるが、今回は Rails なので Rails っぽくなるようにクラスを使ってみた。
クラスを使わない場合は、プラグインの名前をキーにしたハッシュに処理を登録する方法が使えると思う。
Hiki では HikiDoc のプラグイン機構は使っていないように見えたけど気のせい?また今度調べてみる。
カテゴリ Ruby | タグ Rails, Ruby | コメントなし | トラックバックなし
投稿者 okkez
2010-05-25 13:17:00 GMT
ryanb’s cancan at master - GitHub を使ってみた。
元々 be9’s acl9 at master - GitHub を使っていたのだけど、
ちょっと手の込んだことをやろうとすると、やり方がわからなくなったので cancan を使うことにした。
Rails Authorization in The Ruby Toolbox でも人気だったし @kakutani にも紹介してもらったし。
ほとんどのことは Home - cancan - GitHub の Wiki を見ればわかる。これで分からない場合は rdoc.info :: cancan を見ればいいはず。
acl9 に比べていいところは
- 権限に関するコードが一ヶ所に書けるところ
- 権限だけのテストを簡単に書けるところ
- 複雑な権限管理もそれなりにシンプルに書けるところ
- たぶん DB に権限情報を書いてそれを読み込むように書いても使えるところ
良くないところは、権限のコードが長くなってしまうことくらい。
これはテストを書けば、特に問題にはならないかもしれない。
あるいは DB に権限データを登録するようにしてしまえば、シンプルになるだろう。
カテゴリ Ruby | タグ Rails, Ruby | コメントなし | トラックバックなし
投稿者 okkez
2010-05-25 13:00:00 GMT
thoughtbot’s paperclip at master - GitHub を使ってみた。
あるモデルにカラムを追加して使う方法の場合は Home - paperclip - GitHub をよく読んで使えば問題なさそうだった。
一つのレコードに複数の添付ファイルを持たせる場合に少しハマったので書いておく。
class Issue < ActiveRecord::Base
has_many :attachments, :class_name => "::Attachment", :dependent => :destroy
end
class Attachment < ActiveRecord::Base
belongs_to :issue
has_attached_file :attachment
end
このようなクラス構成の場合、Attachment というモデルは paperclip で定義している Paperclip::Attachment と名前が
被るので Issue の定義で注意する必要がある。
上で書いているように、:class_name オプションに「フルパス」でモデルのクラス名を書いておけば良い。
あと paperclip では xxx_{file_name,content_type,file_size,updated_at} というカラムを用意すると、
モデルに xxx という名前のメソッドが定義されるので xxx の部分を長くしすぎるとちょっとコードが読みづらくなる。
実際に Attachment モデルに attachment_* という名前でカラムを用意したので以下のようなコードが頻出して気持ち悪くなったことがある。
link_to @attachment.attachment_file_name, @attachment.attachment.url
Attachment クラスでエイリアスを定義しておけば多少はマシになるかと思う。
カテゴリ Ruby | タグ Rails, Ruby | コメントなし | トラックバックなし
投稿者 okkez
2010-04-10 02:27:00 GMT
Ruby reference manual (beta) の BitClust 版にコメント機能が付きました。
デザインとかちゃんと出来てないけど、動くようになっているので「一言、言いたいけどチケットを起票するのはちょっと。。。」という人はコメントしてみてください。
カテゴリ Ruby, 雑記 | タグ るりま | コメントなし | トラックバックなし
投稿者 okkez
2010-03-29 13:15:00 GMT
タイトルは適当。
Rails とか sinatra の tilt っぽい感じでレイアウトファイルに yield って書けるようにする方法がわかった。
レイアウトファイル。layout.html.erb
<html>
<head></head>
<body>
<%= yield %>
</body>
</html>
読み込むテンプレート。body.html.erb
コード。template.rb
require 'erb'
class Template
def initialize()
erb = ERB.new(File.read('layout.html.erb'), nil, '-')
erb.def_method(self.class, 'layout', 'layout.html.erb')
erb = ERB.new(File.read('body.html.erb'), nil, '-')
erb.def_method(self.class, 'body', 'body.html.erb')
end
def render
layout{ body }
end
end
puts Template.new.render
Template#initialize の引数を追加したりすればもう少し汎用的になると思う。
ERB#def_method の第二引数はメソッド名なのでメソッド名として使える文字列を指定しなければならない。
追記
以下のようにすると Rails の動作に少し近くなるはず。
class Template
def render
str = body()
layout{ str }
end
end
カテゴリ Ruby | タグ Ruby | コメントなし | トラックバックなし