Вопрос о синтаксисе Mongoid

#ruby-on-rails #mongoid #railscasts

#ruby-on-rails #mongoid #железнодорожные трансляции

Вопрос:

В эпизоде 189 Railscasts в пользовательской модели есть именованная область, которая выглядит следующим образом:

 field :roles_mask,      :type => Integer
ROLES = %w[admin moderator author]

named_scope :with_role, lambda { |role| {:conditions => "roles_mask amp; #{2**ROLES.index(role.to_s)} > 0"} }

 # roles related
 def roles=(roles)
  self.roles_mask = (roles amp; ROLES).map { |r| 2**ROLES.index(r) }.sum
 end

 def roles
   ROLES.reject { |r| ((roles_mask || 0) amp; 2**ROLES.index(r)).zero? }
 end

 def role_symbols
  roles.map(amp;:to_sym)
 end
  

Как заставить его работать на Mongoid, поскольку я перепробовал несколько вариантов и не смог заставить его работать?

Ответ №1:

Этот эпизод Railscasts действительно был разработан для баз данных, которые не поддерживают массивы как собственные типы (что делает Mongoid). Тогда вы могли бы просто создать область, которая использует один из критериев запроса массива.

Например:

 class User
  include Mongoid::Document

  field :email
  field :roles, :type => Array

  ROLES = %w[admin moderator author]

  class << self
    def with_role(*args)
      any_in(:roles => args)
    end
  end
end
  

Этот пример позволил бы вам передать либо одну роль User.with_role("admin") , либо массив ролей User.with_role("moderator", "author") , причем последние возвращают пользователей, которые являются либо модераторами, либо авторами.

Комментарии:

1. theTron, большое спасибо за ответ, но роли сохраняются в виде столбца битовой маски, а не столбца массива. Думаю, я могу избавиться от битовой маски и преобразовать ее в столбец массива для хранения ролей, но мне было интересно, есть ли способ сделать аналогичную вещь, которую Райан делает с mongoid.

2. Есть ли какая-либо причина, по которой вы настроены на использование битовой маски? Массив в конечном итоге делает реализацию намного проще в долгосрочной перспективе — и, конечно, намного более читаемой.

3. В итоге я использовал массивы, как вы рекомендовали. Большое спасибо.

Ответ №2:

Вы можете использовать машинную обработку MongoDB для уменьшения встроенной карты, которая предоставляется через mongoid с использованием метода for_js http://www.rubydoc.info/github/mongoid/mongoid/Mongoid/Criteria:for_js

 ROLES.each_with_index do |role, role_index|
  scope "#{role.to_s.pluralize}", -> { self.for_js("(this.roles_mask amp; (1 << role_val)) > 0", role_val: role_index) }
end
  

Это даст области в виде:

 User.admins
User.moderators
User.authors