Выполнить блок, определенный для класса в области видимости экземпляра - PullRequest
1 голос
/ 12 марта 2020

Я хотел бы создать нечто похожее на проверку ActiveRecord: before_validate do ... end. Я не уверен, как я мог ссылаться на атрибуты экземпляра класса из данного блока. Есть идеи?

class Something
 attr_accessor :x

 def self.before_validate(&block)
   @before_validate_block = block
 end

 before_validate do
  self.x.downcase
 end

 def validate!
   # how should this method look like? 
   # I would like that block would be able to access instance attributes
 end
end

Ответы [ 3 ]

3 голосов
/ 12 марта 2020
Ответ

@ 3limin4t0r очень хорошо имитирует поведение на простом ruby. Но если вы работаете в Rails, вам не нужно изобретать велосипед просто потому, что вы не используете ActiveRecord.

Вы можете использовать ActiveModel :: Callbacks для определения обратных вызовов в любом простом виде старый ruby объект:

class Something
  extend ActiveModel::Callbacks
  define_model_callbacks :validate, scope: :name

  before_validate do
    self.x.downcase
  end

  def validate!
    run_callbacks :validate do
      # do validations here
    end
  end
end 

В особенности это сдувает носки с любого из ответов, которые вы получите здесь. Он позволяет определять обратные вызовы до, после и вокруг события и обрабатывает несколько обратных вызовов для каждого события.

Если проверки действительно то, что вам действительно нужно, хотя вы можете просто включить ActiveModel::Validations, который дает вам все проверки, кроме, конечно, validates_uniqueness_of, который определяется ActiveRecord.

ActiveModel::Model включает все модули, составляющие API моделей рельсов, и является хорошим выбором, если вы объявляете виртуальную модель.

3 голосов
/ 12 марта 2020

Это может быть достигнуто с помощью instance_eval или instance_exec.

class Something
 attr_accessor :x

 # You need a way to retrieve the block when working with the
 # instance of the class. So I've changed the method so it
 # returns the +@before_validate_block+ when no block is given.
 # You could also add a new method to do this.
 def self.before_validate(&block)
   if block
     @before_validate_block = block
   else
     @before_validate_block
   end
 end

 before_validate do
  self.x.downcase
 end

 def validate!
   block = self.class.before_validate # retrieve the block
   instance_eval(&block) # execute it in instance context
 end
end
1 голос
/ 12 марта 2020

Как насчет этого?

class Something
  attr_accessor :x

  class << self
    attr_reader :before_validate_blocks
    def before_validate(&block)
      @before_validate_blocks ||= []
      @before_validate_blocks << block
    end
  end

  def validate!
    blocks = self.class.before_validate_blocks
    blocks.each {|b| instance_eval(&b)}
  end
end

Something.before_validate do
  puts x.downcase
end

Something.before_validate do
  puts x.size
end

something = Something.new
something.x = 'FOO'
something.validate! # => "foo\n3\n"

Эта версия позволяет нам определять несколько проверок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...