LoginSignup
23

More than 5 years have passed since last update.

ActiveRecordにこういう機能があったらどうでしょう?をgemにしてみた

Last updated at Posted at 2013-09-05

説明

よくこういうコードありますよね。

class User < ActiveRecord::Base
  # User.admin
  scope :admin, -> {
    where(role: 'admin')
  }
  # user.admin? # => true or false
  def admin?
    self.role == 'admin'
  end
end

scope(クラスメソッド)とインスタンスメソッド両方の定義が必要ですが、内部でやっていることは本質的には同じことです。

これをscope :adminだけ定義しておくと、admin?も自動的に使えるようになるっていう機能、作ったら需要ありますかねぇ?

https://github.com/amatsuda/arel_ruby
を使ったらできるんじゃないか、という思いつき。複雑なのは無理だろうけど…。

実装してみた

(9/6 追記)
arel_ruby の対応の都合で、Rails 3.2でしか動きません。Rails 4でも動きます!

gem 'arel_ruby'
config/initializers/scope_ext.rb
ActiveRecord::Base.class_eval do
  def self.scope(name, scope_options = {})
    super

    class_eval <<-METHOD
      def #{name}?
        self.class.#{name}.arel.to_ruby.call([self]).present?
      end
    METHOD
  end
end

たったこれだけで、上のuser.admin?は余裕で動きます。すごい!

しかし

  scope :not_admin, -> {
    where('role != "admin"')
  }

こうすると、user.not_admin?は動きません。
文字列で書くとSQLの一部(Arel::Nodes::SqlLiteral)になるので、SQL依存になってしまうのですね。

そこでArel

直接Arelを使えば、複雑な条件もSQL文字列を使わずに記述できます。

  scope :not_admin, -> {
    where(arel_table[:role].not_eq('admin'))
  }

しかしuser.not_admin?は動かない…。
理由は

  • Cannot visit Arel::Nodes::NotEqual visit_Arel_Nodes_NotEqualが未実装
    • でもこれはEqualityの逆なのですぐ実装できる
  • Arel::Nodes::Groupingがつく
    • SQLでいう(...)。これはRubyではどう表せばいいんだ?
    • とりあえずこんな感じでいけるっぽい
lib/arel/visitors/ruby.rb
      def visit_Arel_Nodes_Grouping o
        v = visit o.expr
        ProcWithSource.new("select {|o| o.#{v.to_source}}") { |collection| collection.select {|obj| v.call(obj) } }
      end

これで動いたよ。
まだまだ実装されてないvisitorがいっぱいだけど…。
https://github.com/amatsuda/arel_ruby/blob/master/lib/arel/visitors/ruby.rb

ひとまずこっちでがんばって実装してます。だいたいのqueryは動くはず。
https://github.com/tkawa/arel_ruby/blob/master/lib/arel/visitors/ruby.rb

現段階でのまとめ

思いつきにしてはそれなりに動いたし、なかなか良いのでは?

gemつくりました。テストがまだだけど。テストも書きました。
https://github.com/tkawa/activerecord-endoscope

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23