query: tag:21

config/environment.rbの中で、config.gemによってversion指定したgemを、
rake gems:unpackしようとすると、以下のようなエラーが出るようです。

pre>>
% rake gems:unpack GEM=genki-pagination_scope
(in /Users/takiuchi/project/formula)
ERROR: While executing gem ... (ArgumentError)
Illformed requirement ["">= 0.0.4""]
<<--

RailsのLightHouseでも報告されていました。

**rake gems:unpack version handling broken **

ということで、この問題を回避するために、上記Ticketに添付されていた
Patchを参考に、以下のようなMonkeyPatchを作ってみました。

config/initializers/fix_gem_unpack_for_2_1_1.rb

ruby>>
module Rails
class GemDependency
def unpack_to(directory)
FileUtils.mkdir_p directory
Dir.chdir directory do
Gem::GemRunner.new.run(
unpack_command.map {|i| i.gsub(/\A["']|["']\z/, '')})
end

  spec_filename = File.join(gem_dir(directory), '.specification')
  File.open(spec_filename, 'w') do |file|
    file.puts specification.to_yaml
  end
end

end
end
<<--

これをconfig/initializers/の下に置いておけば、とりあえず正常にunpackする事ができます。

posted by genki genki on Wed 24 Sep 2008 at 01:54 with 0 comments

Railsアプリケーションでpaginationといえば、
will_paginate等のプラグインやGemを使うのが一般的だと思います。
しかし、named_scopeでjoinsを使った場合にうまく
paginationができなかったので、
named_scopeだけを使ってpaginationする方法を考えてみました。

まずは以下のようなnamed_scopeを作ります。
以下の例はPostクラスで宣言される事を想定しています。

rails>>
named_scope :paginate, proc{|page, per_page|
{:offset => per_page*((page || 1).to_i - 1),
:limit => per_page}} do
def count
proxy_scope.count(:group => 'posts.id').size
end
def num_pages
(count.to_f/proxy_options[:limit]).ceil
end
def page
proxy_options[:offset]/proxy_options[:limit] + 1
end
def pages(window = 5, left = 2, right = 2)
(1..num_pages).inject([]) do |result, i|
i <= left || (num_pages - i) < right ||
(i-page).abs < window ? result << i :
(result.last.nil? ? result : result << nil)
end
end
end
<<--

countを再定義しているのは、:joinsを含む別なnamed_scopeをチェーンした時に、正しいcountを求めるためです。

コントローラでは、以下のようにScopeを取得します。

rails>>
@posts = Post.paginate(params[:page], 5)
<<--

Viewでは以下のように記述します。

rails>>
<% if @posts.page > 1 %>
<%= link_to '« Newer',
url_for(:page => @posts.page - 1) %>
<% else %>
« Newer
<% end %>
<% @posts.pages.each do |i| %>
<% if i.nil? %>
...
<% elsif i == @posts.page %>
<%= i %>
<% else %>
<%= link_to i, url_for(:page => i) %>
<% end %>
<% end %>
<% if @posts.page < @posts.num_pages %>
<%= link_to 'Older »',
url_for(:page => @posts.page + 1) %>
<% else %>
Older »
<% end %>
<<--

posted by genki genki on Sat 16 Aug 2008 at 20:57 with 0 comments

このところ、named_scope関係のバグの原因と対処法を調べるために
ActiveRecordのEager Loadingの実装を調べていました。
ActiveRecordのEager Loadingは、
Rails2.1から、若干生成するSQLが変化しています。

Rails 2.1 Eager Loading

上記サイトでも紹介されているように、
今まではLEFT OUTER JOINを使って、
一回のクエリで全て読み込むようなSQLを生成していたのですが、
Rails2.1からは、複数回のクエリに分割するようになりました。

ということで、Eager Loadingを実際にやっているコードを調べてみたのですが、自分で好きなタイミングでEager Loadingを行う方法を見つけたので紹介します。

ActiveRecord::BaseクラスのprotectedメソッドActiveRecord.preload_associationsを利用します。

ruby>>
articles = Article.all(:limit => 5)
Article.send :preload_associations, articles, [:user => :face]
<<ruby

1行目は普通にEager LoadingせずにArticleリストを取得していますが、
二行目を実行することで、各ArticleのアソシエイションがEager Load
されます。あとは、

ruby>>
articles.first.user.face
<<--

などを実行してもSQLクエリは発行されません。
実質的に発行されるSQLは通常の:include
を指定した場合と同じになるので、好きなタイミングでEager Load
を実行できるようになったといえます。

posted by genki genki on Mon 4 Aug 2008 at 20:23 with 0 comments

named_scopeは大変素晴らしいRailsの新しい機能ですが、
おなじみのwith_scopeのように、
スコープつきのブロックを伴った利用ができないという問題がありました。
例えばこんな感じに利用しようとしてもうまくいきません。

ruby>>
User.active do
User.count # => Not run
end

User.active.with_scope do
User.count #=> Not scoped
end
<<ruby

そこで、昔ながらのwith_scopeと同じようにnamed_scopeを使えるようにする
Gemプラグインを作りました。

with_named_scope

これを使うと、以下のように期待通りに動きます。

ruby>>
User.active.with do
User.count #=> User.active.count
end
<<--

なかなか便利だと思うので、Rails本家にパッチを送っておきました。

Improved named_scope to be used like as with_scope

posted by genki genki on Fri 1 Aug 2008 at 21:09 with 0 comments

以下の記事は、調査の結果現状のRuby on Rails
では期待通りに動かないことがわかりました。


Rails-2.1の新機能であるnamed_scopeを使うと、従来のwith_scope
では綺麗にかけなかったスコープの選択を簡潔に記述することができます。

rails>>
def gadgets_on_sidebar(place = 'index')
case place.to_s
when 'index'; Gadget.on_index
when 'show'; Gadget.on_show
else Gadget.all
end.with_scope{active_gadgets + Gadget.mandatories}
end
<<--

各スコープメソッドは、ActiveRecord::NamedScope::Scope
オブジェクトを返すので、
これを条件にしたがって選択し、最後に.with_scope
を呼んでスコープを適用します。


そこで、
with_named_scope
というGemプラグインをつくり、
上述のように処理できるようにしました。
以下のようになります。

rails>>
def gadgets_on_sidebar(place = 'index')
case place.to_s
when 'index'; Gadget.on_index
when 'show'; Gadget.on_show
else Gadget.all
end.with{active_gadgets + Gadget.mandatories}
end
<<--

ということで、Rails本家にもpatchを送っておきました。

posted by genki genki on Fri 1 Aug 2008 at 16:06 with 3 comments

Rails 2.1がどんな感じになるのか、面白そうな記事があったので紹介します。

A taste of what's coming in Rails 2.1

Rails 2.1 is not far off the horizon and we’ve been adding a ton of extra deliciously nice goodies in preparation of its release lately.

個人的にはCache周りのネタが気になりました。

What's New in Edge Rails: Pluggable Controller Caching

ふむふむ。

posted by genki genki on Wed 2 Apr 2008 at 11:42 with 0 comments