2016-08-01 3 views
0

У меня есть класс под названием User, который has_many: roles, through: user_roles. Я пытаюсь метапрограммных предикатные методы связанных ролей внутри класса пользователей, как так:Методы предикатов метапрограммирования внутри класса - Rails 4

class User < ActiverRecord::Base 
...  
    Role.all.pluck(:name).each do |role_name| 
    define_method("#{role_name}?") do 
     roles.map(&:name).include?(role_name) 
    end 
    end 
... 
end 

Хотя Role.all.pluck(&:name) действительно возвращает массив существующих имен ролей, то define_method никогда не вызывается, и моя спекуляция терпит неудачу с неопределенным способом:

... 
    subject.roles << create(:role, name: 'foo') 
    expect(subject.foo?).to be true #<= undefined method `foo?' for #<User...> 
    ... 
+0

Это должно быть 'pluck (: name)' без '&'. Это может отбросить здание коллекции, что приведет к тому, что 'define_method' никогда не будет вызван. – sixty4bit

+0

@ sixty4bit oops, обновлено. Я решил заменить '.map' на' pluck' и забыл удалить 'to_proc'. Тем не менее возвращает 'undefined method' однако –

+1

Ah ha. В этом случае, я думаю, ответ может быть более фундаментальным в том, как работает метапрограммирование. Логика 'Role.all ...' выполняется, когда загружается класс 'User', который находится перед запуском теста. Таким образом, единственные методы, которые будут определены, будут для ролей, которые уже существовали до загрузки пользователя. Создание роли в вашем тесте после загрузки «Пользователь» не повлияет на предыдущий код. Надеюсь, это имеет смысл ... – sixty4bit

ответ

2

Техника метапрограммирования, которую вы используете, предназначена для того, чтобы использовать тот факт, что код, просто сидящий внутри класса, будет выполнен, когда класс загружен. Поэтому, когда Rails загружает User класса, в тот момент логик определения методов сказуемого, выполняемые и методы которые создаются для любых Role.all возвращаются в тот момент, когда User загружается.

Создание новой роли, как и в тесте, не будет влиять на то, какие предикатные методы были созданы при загрузке класса и выполнении кода.

Вы можете увидеть это в действии, создав файл с именем count.rb в любой директории со следующим кодом внутри:

$count += 1

Затем откройте irb и тип:

irb(main):001:0> $count = 0 
=> 0 
irb(main):002:0> require './count' 
=> true 
irb(main):003:0> $count 
=> 1 

Обратите внимание, что $count был увеличен на единицу, когда файл был загружен. Теперь, если бы вы снова были в файле require, ничего не произошло бы. Вы могли бы заставить код, который будет повторно загружен с помощью load вместо требуется:

# ...continued from above 
irb(main):004:0> require './count' 
=> false 
irb(main):005:0> $count 
=> 1 
irb(main):006:0> load './foo.rb' 
=> true 
irb(main):007:0> $count 
=> 2 

Таким образом, чтобы получить ваш тест пройти, вы должны создать роль, а затем заставить перезагрузку User класса, то сделайте это утверждение.

+0

абсолютно! закончил тем, что переместил эту тестовую логику на единичный тест для «Роли», чтобы создать экземпляр «Пользователь» после создания новой роли. –

+0

Хотя это ответ, который дает рабочее испытание, я бы не пошел с таким подходом. Таким образом, добавление новых ролей в приложение не будет иметь никакого эффекта, если холодный перезапуск/перезагрузка. Я бы определил обратный вызов в 'Role # after_commit', обновив предикатные методы в классе' User'. – mudasobwa

+0

Чтобы быть ясным: я не пытался сказать в своем ответе, что это подход, который должен быть реализован в коде приложения. Я просто пытался объяснить, почему * @Dimitry_N испытывал эту проблему в тесте, чтобы убедиться, что он понимает, как работает базовая концепция метапрограммирования и т. Д. – sixty4bit

Смежные вопросы