アレルを拡張するためのより良い方法があるはずだと思います。私はこの結果に到達するのに苦労しました。
ともかく; このソリューションはModel#extendingを使用します:
module BitOperations
def bitwise_and_sql
@bitwise_and_sql ||=
case connection.adapter_name
when 'Oracle' # probably wrong!
"BITAND(%s, %s)"
else
"%s & %s"
end
end
def bitwise_and(i, j)
where(bitwise_and_sql % [i, j])
end
def bitmask(i, j)
where('%s > 0' % scoped.bitwise_and(i, j).wheres.to_a.last.to_sql)
end
end
p User.scoped.extending(BitOperations).bitwise_and(1, 2).bitmask(3, 4).to_sql
#=> "SELECT \"users\".* FROM \"users\" WHERE (1 & 2) AND ((3 & 4) > 0)"
.wheres
アレル関係が含まれています。が含まれてEnumerable
いるため、最後のリレーションを取得して配列に変換し、最後の要素を取得できます。で使用するためにのSQLを取得するbitwise_and(i, j)
ために使用しましたbitmask(i, j)
。どこからSQLを取得するためのより良い方法があるのだろうか...
.wheres
非推奨に関する警告が表示wheres
されますが、現時点では無視できます(Rails 4ベータ版でも機能します)。
次のクラスメソッドを定義できますUser
。
class User
def self.scope_with_bit_operations
@scope_with_bit_operations ||= scoped.extending(BitOperations)
end
def self.bitwise_and(i, j)
scope_with_bit_operations.bitwise_and(i, j)
end
def self.bitmask(i, j)
scope_with_bit_operations.bitmask(i, j)
end
end
p User.bitwise_and(1, 2).bitmask(3, 4).to_sql
#=> "SELECT \"users\".* FROM \"users\" WHERE (1 & 2) AND ((3 & 4) > 0)"
またはすべてのモデルの場合:
class ActiveRecord::Base
def self.scope_with_bit_operations
@scope_with_bit_operations ||= scoped.extending(BitOperations)
end
def self.bitwise_and(i, j)
scope_with_bit_operations.bitwise_and(i, j)
end
def self.bitmask(i, j)
scope_with_bit_operations.bitmask(i, j)
end
end
p Post.bitwise_and(1, 2).bitmask(3, 4).to_sql
#=> "SELECT \"posts\".* FROM \"posts\" WHERE (1 & 2) AND ((3 & 4) > 0)"
そして最後に、もう少しエレガントなwith_role
スコープを実装できます。
class User < ActiveRecord::Base
ROLES = %w[admin moderator author]
scope :with_role, ->(role) do
# I'm a fan of quoting everything :-P
bitmask connection.quote_column_name(:roles_mask),
connection.quote(2**ROLES.index(role.to_s))
end
end
p User.with_role('admin').to_sql
#=> "SELECT \"users\".* FROM \"users\" WHERE ((\"roles_mask\" & 1) > 0)"
IMOは、より概念実証であると言わなければなりません。再利用する予定がなくbitwise_and
、bitmask
他のモデルではそれらを抽象化する必要がない場合は、おそらくscope
、このようなfe何か:
class User < ActiveRecord::Base
ROLES = %w[admin moderator author]
BITMASK_SQL =
case connection.adapter_name
when 'Oracle' # probably wrong!
"BITAND(%s, %s) > 0"
else
"%s & %s > 0"
end
scope :with_role, ->(role) do
where BITMASK_SQL % [ connection.quote_column_name(:roles_mask),
connection.quote(2**ROLES.index(role.to_s)) ]
end
end
p User.with_role('admin').to_sql
#=> "SELECT \"users\".* FROM \"users\" WHERE (\"roles_mask\" & 1 > 0)"
ルールは、必要なときに抽象化を追加することだと思いますが、必要のないときは追加しないでください(このフレーズの英語が正しいかどうかはわかりません:-))
別のことを言いたいのですが、あなたはRuby / Railsに慣れていないので、Rails&cをたくさん読むことをお勧めします。コード; IMOは、Railsがどのように機能するかを学ぶための最良の方法です(これが、私があなたの質問に答えるために時間を費やした理由です:私はArel関係のRails管理に興味があったからです:-))。