【问题标题】:Ruby: Custom Implementation of attribute accessors with validationRuby:具有验证的属性访问器的自定义实现
【发布时间】:2015-05-04 11:16:12
【问题描述】:

我正在尝试实现custom attribute accessors with validation

假设attr_validated。现在这个attr_validated

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

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

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

这个问题可能看起来像任何其他问题,但事实并非如此。 谷歌搜索时我得到了

1: Ist Part

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:我们如何实施第二部分

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

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    这样的事情怎么样:

    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>'
    

    希望有帮助!

    祝你好运!

    更新

    您可以添加另一个方法,该方法将对第一个参数应用验证,如下所示:

    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
    

    【讨论】:

    • 非常感谢@Paweł Dawczak ..它解决了我的问题。我还有一件事,块条件将应用每个属性..所以我们可以只为第一个参数做..
    • @Vinay 通常像这样的方法将块条件应用于所有项目,而不仅仅是第一个项目,至少由 Rails 的约定规定。如果您需要更有选择性地应用它们,请拨打多个attr_validated 电话,一个有块,一个没有。您可能还想采用如下模式:attr_validated :example, with: -&gt; { |v| v.length &gt; 6 },它作为 lambda 传入。
    • 感谢@Paweł 的帮助!!
    【解决方案2】:

    这很容易。如果块说参数无效,只需在 setter 中 raiseArgumentError

    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'
    

    【讨论】:

    • 你能解释一下..self.rejector = rejector 在这里做什么..我知道rejector 是一个proc 对象,self 是person 对象..那么引擎盖下发生了什么..跨度>
    • 因为我改变了问题。感谢您之前的回答。请你帮忙解决这个问题。提前致谢
    猜你喜欢
    • 2014-03-23
    • 1970-01-01
    • 2013-01-29
    • 2019-09-06
    • 2011-03-26
    • 1970-01-01
    • 2015-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多