2015-07-16 6 views
3

У меня есть следующие модели:Определение пользовательских методов в рамках модели/класса в Rails 4

class ActivityLog < ActiveRecord::Base 
    validates :user_id, :instance_id, :action, presence: true 
    validates :user_id, :instance_id, :action, numericality: true 

    def log 
    ActivityLog.create(
     user_id: current_user ? current_user.id : -1, 
     instance_id: instance_id, 
     action: actions.index(action) 
    ) 
    end 

    private 

    def actions 
    ['start','stop','create','destroy'] 
    end 

end 

Когда я звоню следующую строку из консоли рельсы, я получаю ошибку:

ActivityLog.log(user_id: 1, instance_id:1, action: 'create') 

# Error returned from console 
NoMethodError: undefined method `log' for #<Class:0x007fb4755a26a8> 

Почему мой метод не работает? Я определил его в классе, но он говорит, что он не определен. Что я теряю или недопонимаю? Спасибо.

+0

вам нужно определить метод as'self.log', чтобы сделать его как метод класса –

ответ

9

Создание метода log

Скажем, у вас есть класс User, и в классе, вы определяете метод has_cell_phone. (Содержание этого метода не имеет значения.) Когда вы определяете метод в классе как def has_cell_phone, этот метод можно вызвать на любом объекте User. В то время как class User сам по себе является объектом класса, вы бы назвали его объектом, ближайшим классом которого является User. В правильных выражениях вы бы написали метод экземпляра для экземпляра класса User.

Вы получаете эту ошибку, потому что метод log, который вы определили, работает только для _instance класса ActivityLog.Если вы выполните следующие действия, вы можете позвонить log правильно, учитывая ваш текущий код:

activity_log = ActivityLog.create # with required params 
activity_log.log 

Во-вторых, вы вызываете log с параметрами, в то время как ваше определение метод не требует каких-либо. (Это будет выглядеть как def log(params).)

Теперь здесь вы можете изменить существующий код. Если вы хотите вызвать метод на всем классе (что означает сам класс), вы добавляете ключевое слово self в определение класса. Например, для класса User это будет def self.create_user_with_cell_phone. Вы также можете добавить аргументы к этому методу. Аргументы вы предоставите в вашей линии «вызов метода», я хотел бы добавить те к вашему методу класса, например, так:

def self.log(instance_id, action) 
    # ... 
end 

ActivityLog.log(1, 'create') 

Вам не нужно будет включать user_id, потому что, основываясь на вашей логике, он проверяет, Объект current_user - true, и следует оттуда.

Создание класса постоянной

Второй взгляд на ваш вопрос, я обнаружил, что вы определяете метод actions. Помните, что я сказал о методах экземпляра? Поскольку кажется, что actions всегда будет оставаться постоянным, я рекомендую вам сделать его одним! Чтобы сделать это, рекомендуется поместить следующую строку в свой класс перед любыми определениями методов.

ACTIONS = ['start','stop','create','destroy'] 

Тогда, в любое время вы хотите позвонить ACTIONS в то время как внутри ActivityLog класса, вы сделали следующее: ACTIONS.index(action). Если вы хотите назвать эту константу за пределами своего класса, вы бы сделали это: ActivityLog::ACTION. Это аналогичный синтаксис вызова метода класса, вместо этого вы используете ::, чтобы отделить класс от константы. Пересматривая код, он должен выглядеть следующим образом:

class ActivityLog < ActiveRecord::Base 
    ACTIONS = ['start','stop','create','destroy'] 

    validates :user_id, :instance_id, :action, presence: true 
    validates :user_id, :instance_id, :action, numericality: true 

    def self.log(instance_id, action) 
    ActivityLog.create(
     user_id: (current_user ? current_user.id : -1), 
     instance_id: instance_id, 
     action: ACTIONS.index(action) 
    ) 
    end 
end 
2

log - метод экземпляра, как определено; он будет работать, только если у вас есть конкретный пример ActivityLog.

Если вы хотите сделать метод класса, вы должны прикрепить его к классу с помощью ключевого слова self.

def self.log 
    # code here 
end 
0

Вы пишете метод экземпляра и называете его как метод класса.

Чтобы написать метод класса, необходимо добавить self перед именами методов (self.log, self.actions). Это позволит вам вызвать метод, как вы ожидаете, и, вероятно, лучший способ написать альтернативный конструктор, подобный этому. Если ваши методы не зависят от конкретного экземпляра класса, который, как кажется, вы здесь делаете, лучше сделать их методами класса.

В качестве альтернативы вы можете создать экземпляр своего класса и вызвать методы экземпляра, которые вы определили. Вместо ActivityLog.log создайте новый регистратор с Activity.new и вызовите на него метод журнала. В одной строке это будет выглядеть как Activity.new.log, но вы должны, вероятно, сохранить новый объект в переменной, чтобы отслеживать его.

Последним вариантом является использование метода initialize. Написав def initialize, вы меняете конструктор для своего класса, чтобы вместо вызова ActivityLog.log вы можете позвонить ActivityLog.new. Это делает более ясным, что вы строите новый объект и идиоматичны в рубине. Однако он удаляет имя описательного метода. Я бы рекомендовал этот маршрут, если вы не собираетесь создавать свой класс с помощью каких-либо других методов, но если вы хотите иметь несколько, перейдите к методу класса.

+0

Я не согласен с 'ActivityLog.new.log' , поскольку целью метода «log» является создание нового объекта. Почему один плавает вокруг, не будучи спасенным? – onebree

+0

Я согласен, это не очень хороший способ сделать это, но я тоже хотел включить методы работы экземпляра в свой ответ, так как это было похоже на путаницу. –

+0

Хорошо, просто убедившись. Я бы сказал, что это не самый лучший способ, просто чтобы не путать ОП дальше. – onebree

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

  • Нет связанных вопросов^_^