Ruby中的动态验证和元编程 [英] Dynamic validation and metaprogramming in Ruby

查看:187
本文介绍了Ruby中的动态验证和元编程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试开发可以向不同用户提供相同资源的应用程序,以及资源可能基于用户的不同验证行为。

I am trying to development an application that can present the same resource to different users and where the resource may have different validation behavior based on the user.

我有试图使用Ruby元编程来解决这个问题,但是看起来我缺少一些关键的知识。

I have tried to use Ruby metaprogramming to solve this in an easy way but it looks like I am missing some key knowledge of the matter.

我可以通过模型例如

class Profile < ActiveRecord::Base
  # validates_presence_of :string1
end

该模型有属性string1,有时是必需的,有时不是。我想为每个用户创建子类(由于在这个简化中不明显的原因),并且已经创建了一个我想要包括的模块:

The model has a property 'string1' that sometimes are required and sometimes not. I would like to create sub-classes for each user (for reasons not apparent in this simplification) and have created a module that I would like to include:

module ExtendProfile
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def configure_validation(required)
      if required
        class_eval("ActiveRecord::Base.validates_presence_of :string1")
      end
    end
  end
end

它的唯一目的是添加一种根据给定的参数添加条件验证的方法。

Its only purpose is to add a method that add the conditional validation based on the arguments given.

当调用的参数为true时,它会添加验证,但不会干净地执行。看起来它不会像我以为这样分离子类。

It does add the validation when called with an argument that is true but it does not do it cleanly. It appears that it does not separate the subclasses as I thought it would.

可以通过以下测试来说明:

It can be illustrated by the following tests:

profile = Profile.new
profile.save; profile.errors
 => []

默认情况下,配置文件可以保存而不会出错。

A profile can by default be saved without errors.

Object::const_set('FirstExtendedProfile'.intern, Class::new(Profile) { include ExtendProfile })
FirstExtendedProfile.configure_validation(true)
fep = FirstExtendedProfile.new; fep.save; fep.errors
 => {:string1=>["skal udfyldes", "skal udfyldes"]}

子类并调用 configure_validation 添加验证,但由于某种原因,它在验证期间被调用两次(skal udfyldes - 是丹麦语,意味着需要)。

Creating a new subclass and calling configuring_validation adds validation but for some reason it is called twice during validation ("skal udfyldes" - is Danish and means that it is required).

Object::const_set('SecondExtendedProfile'.intern, Class::new(Profile) { include ExtendProfile })
sep = SecondExtendedProfile.new; sep.save; sep.errors
 => {:string1=>["skal udfyldes"]}

创建另一个后代, code> configure_validation 不会调用它仍然验证 string1 属性(但现在只有一次)。

Another descendant is created and even though configure_validation isn't called it still validates the string1 property (but now only once).

添加另一个后代并调用 configure_validation 再次添加验证...

Adding yet another descendant and calling configure_validation adds the validation once more...

为什么我无法将验证添加到特定的配置文件后代?

Why am I not able to add the validation to the specific Profile descendant?

我使用的是Ruby 1.9.2和Rails 3.06。请理解,我想了解如何使这个动态类创建工作 - 我知道标准自定义验证。

I am using Ruby 1.9.2 and Rails 3.06. Please understand that I would like to understand how to make this dynamic class creation to work - I am aware of "standard" custom validation.

推荐答案

由于在使用单表继承时变得清楚的原因,验证将存储在root类中的变量中,即直接继承ActiveRecord :: Base的类。我们的建议是将每个类中的一些配置数据存储起来,以确保您尝试执行的操作不起作用。

For reasons that become clear when working with Single Table Inheritance, validations are stored in a variable in the "root" class, that is the class that directly inherits for ActiveRecord::Base. There is quite a bit of work behind the scenes to make sure that what your trying to do does not work.

编写一个单一的动态验证,根据它在那里找到的内容来检查配置并进行验证。然而,这可能是我意识到标准自定义验证的意思。

My suggestion is to store some configuration data in each class then write a single dynamic validation that checks the configuration and validates based on what it finds there. This may however be what you mean by "I am aware of "standard" custom validation."

 with_options :if => lambda { |obj| obj.class.validation_configuration[:string1]} do |t|
   t.validates_presence_of :string1
 end 

这篇关于Ruby中的动态验证和元编程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆