Ruby:自定义实现属性访问器验证

问题描述:

我试图执行custom attribute accessors with validationRuby:自定义实现属性访问器验证

比方说attr_validated。现在这个attr_validated

1:应该有与attr_accessor相同的setter和getter方法。 ##这部分完成

2:它应该验证给定的块。

attr_validated :num_legs do |v| 
v <= 4 
end 

这个问题可能看起来像任何其他问题,但它不是。 虽然google搜索,我得到

1: IST部分

class Class 
    def attr_validated(*args) 
    args.each do |arg| 
     # getter 
     self.class_eval("def #{arg};@#{arg};end") 
     # setter 
     self.class_eval("def #{arg}=(val);@#{arg}=val;end") 
    end 
    end 
end 

class Dog 
    attr_validated :num_legs ## Instead of this i need to validate a block also attr_validated :num_legs do |v| 
v <= 4 
end 

dog = Dog.new 
p dog.num_legs 
p dog.num_legs = 'Stack' 

2:我们该如何实施第二部分

任何帮助将不胜感激!!!

怎么是这样的:

class Class 
    def attr_validated(*args, &validator) 
    args.each do |name| 
     define_method("#{name}=") do |value| 
     if block_given? 
      raise ArgumentError, "Value '#{value}' is invalid" unless validator.call(value) 
     end 

     instance_variable_set("@#{name}", value) 
     end 

     define_method(name) do 
     instance_variable_get("@#{name}") 
     end 
    end 
    end 
end 

class Person 
    attr_validated(:name, :occupation) { |name| name.length > 3 } 
end 

p1 = Person.new 
p1.name = "John The Tester" 
p1.occupation = "Software developer" 
p "#{p1.name} - #{p1.occupation}" 

p2 = Person.new 
p2.name = "test" 
p2.occupation = "Tester" 
p "#{p2.name} - #{p2.occupation}" 

这会产生这样的输出:

"John The Tester - Software developer" 
app.rb:6:in `block (2 levels) in attr_validated': Value 'test' is invalid (ArgumentError) 
     from app.rb:28:in `<main>' 

希望这有帮助!

祝你好运!

UPDATE

您可以添加另一种方法,将应用验证的第一个参数是这样的:

class Class 
    def attr_validated_first(*args, &validator) 
    args.each_with_index do |name, index| 
     define_method("#{name}=") do |value| 
     if block_given? && index == 0 
      raise ArgumentError, "Value '#{value}' is invalid" unless validator.call(value) 
     end 

     instance_variable_set("@#{name}", value) 
     end 

     define_method(name) do 
     instance_variable_get("@#{name}") 
     end 
    end 
    end 
end 

但是我不推荐这种方法,这将是混乱!如果你想用不同的验证规则注册几个属性......您可以使用attr_validated从第一个例子多次,像这样:

class Person 
    attr_validated(:name)  { |name| name.length > 3 } 
    attr_validated(:occupation) { |occupation| occupation == "Ruby Developer" } 
end 
+0

许多许多谢谢@PawełDawczak ..它解决了我的问题。仍然我有一件事,块条件将适用于每一个属性..我们可以只为第一参数.. – VKatz

+0

@Vinay - 请检查更新的答案 –

+0

@Vinay通常这样的方法适用于块条件的所有项目,不只是第一个,至少是由Rails的约定来规定的。如果您需要更有选择性地应用它们,请进行多个“attr_validated”调用,其中一个调用块,另一个不调用。您可能还想采用如下模式:'attr_validated:example,with: - > {| v | v.length> 6}'作为lambda传入。 – tadman

这很简单。只是raise在二传手的ArgumentError如果块说的论据是无效:

class Person 
    attr_reader :name 

    def name=(name) 
    raise ArgumentError, "'#{name}' is not a valid name!" if rejector.(name) 
    @name = name 
    end 

    private 

    attr_accessor :rejector 

    def initialize(&rejector) 
    self.rejector = rejector 
    end 
end 

artist = Person.new do |person| 
    person.length > 6 
end 

artist.name = 'The Artist Formerly Known As Prince' 
# ArgumentError: 'The Artist Formerly Known As Prince' is not a valid name! 
artist.name = 'ruby' 
# => 'ruby' 
+0

能否请您解释一下..what self.rejector =剔除在这里做..As我知道剔除是一个进程内对象和自我是人的对象..那么什么是在引擎盖下.. – Tyshawn

+0

当我改变了问题。并感谢您的上一个答案。你能帮忙解决这个问题吗?在此先感谢 – Tyshawn