attr_accessor强类型Ruby on Rails

eky*_*nox 17 ruby ruby-on-rails accessor attr-accessor

只是想知道是否有人能够在Ruby on Rails中对getter setter的基础知识进行一些了解,并提供强类型视图.我对rails上的ruby非常新,并且主要对.NET非常了解.

例如,我们假设我们有一个名为Person的.net类

class Person
{
 public string Firstname{get;set;}
 public string Lastname{get;set;}
 public Address HomeAddress{get;set;}
}

class Address
{
 public string AddressLine1{get;set;}
 public string City{get;set;}
 public string Country{get;set;}
}
Run Code Online (Sandbox Code Playgroud)

在Ruby中,我会写这个

class Person
 attr_accessor :FirstName
 attr_accessor :LastName
 attr_accessor :HomeAddress
end

class Address
 attr_accessor :AddressLine1
 attr_accessor :City
 attr_accessor :Country
end
Run Code Online (Sandbox Code Playgroud)

查看Person类的Ruby版本如何指定访问器方法FirstName,LastName和HomeAddress的类型?如果我要使用这个类,我可以将任何类型提供给HomeAddress,但我希望这个访问器方法只接受TYPE地址.

有什么建议 ?

谢谢

Jak*_*old 37

TL; DR:不,这是不可能的...而且答案很长,是的,有可能,阅读元编程部分:)

Ruby是一种动态语言,这就是为什么你不会得到像C#这样的语言的编译时类型警告/错误.

与您无法为变量指定类型相同,您无法为其指定类型attr_accessor.

对于来自.NET的人来说,这可能听起来很愚蠢,但在Ruby社区中,人们希望您编写测试.如果你这样做,这些类型的问题基本上会消失.在Ruby on Rails中,您应该测试您的模型.如果你这样做,你就不会有任何麻烦,因为意外地分配错误的东西.

如果您在Ruby on Rails中专门讨论ActiveRecord,则将String分配给在数据库中定义为Integer的属性将导致抛出异常.

顺便说一句,按照惯例,你不应该使用CamelCase属性,所以正确的类定义应该是

class Person
 attr_accessor :first_name
 attr_accessor :last_name
 attr_accessor :home_address
end

class Address
 attr_accessor :address_line1
 attr_accessor :city
 attr_accessor :country
end
Run Code Online (Sandbox Code Playgroud)

其中一个原因是,如果你将第一个字母大写,Ruby将定义一个常量而不是变量.

number = 1   # regular variable
Pi = 3.14159 # constant ... changing will result in a warning, not an error
Run Code Online (Sandbox Code Playgroud)

元编程黑客攻击

顺便说一下,R​​uby还具有疯狂的元编程功能.您可以attr_accessor使用类型检查编写自己的类型,可以使用类似的方法

typesafe_accessor :price, Integer
Run Code Online (Sandbox Code Playgroud)

有定义 某物 喜欢

class Foo

  # 'static', or better said 'class' method ...
  def self.typesafe_accessor(name, type)

    # here we dynamically define accessor methods
    define_method(name) do
      # unfortunately you have to add the @ here, so string interpolation comes to help
      instance_variable_get("@#{name}")
    end

    define_method("#{name}=") do |value|
      # simply check a type and raise an exception if it's not what we want
      # since this type of Ruby block is a closure, we don't have to store the 
      # 'type' variable, it will 'remember' it's value 
      if value.is_a? type
        instance_variable_set("@#{name}", value)
      else
        raise ArgumentError.new("Invalid Type")
      end
    end
  end

  # Yes we're actually calling a method here, because class definitions
  # aren't different from a 'running' code. The only difference is that
  # the code inside a class definition is executed in the context of the class object,
  # which means if we were to call 'self' here, it would return Foo
  typesafe_accessor :foo, Integer

end

f = Foo.new
f.foo = 1
f.foo = "bar" # KaboOoOoOoM an exception thrown here!
Run Code Online (Sandbox Code Playgroud)

或至少在这些方面的东西:) 这段代码有效!Ruby允许您动态定义方法,这是如何attr_accessor工作的.

块也几乎总是闭包,这意味着我可以if value.is_a? type不将它作为参数传递.

如果这是真的,那么在这里解释太复杂了.简而言之,有不同类型的块

  • Proc,由...创建 Proc.new
  • lambda,由关键字创建 lambda

其中一个区别是调用return一个lambda只会从lambda本身返回,但是当你从a执行相同的操作时Proc,块周围的整个方法将返回,这在迭代时使用,例如

def find(array, something)
  array.each do |item| 
    # return will return from the whole 'find()' function
    # we're also comparing 'item' to 'something', because the block passed
    # to the each method is also a closure
    return item if item == something
  end
  return nil # not necessary, but makes it more readable for explanation purposes
end    
Run Code Online (Sandbox Code Playgroud)

如果您正在使用这种东西,我建议您查看PragProg Ruby Metaprogramming截屏视频.


sar*_*old 5

Ruby 是一种动态类型语言;像许多动态类型语言一样,它坚持鸭子类型——来自英语成语,“如果它像鸭子一样走路,像鸭子一样嘎嘎叫,那么它就是一只鸭子。”

好处是您不必在任何变量或类成员上声明类型。您可以将哪些类型的对象存储到变量或类成员中的限制仅取决于您如何使用它们——如果您习惯于<<“写入输出”,那么您可以使用文件、数组或字符串来存储输出。这可以大大增加类的灵活性。(您有多少次因为必须使用的 API 需要FILE *C 标准 IO 文件指针而不是允许您传入缓冲区而感到不安?)

缺点(在我看来,这是一个很大的缺点)是您没有简单的方法来确定可以安全地将哪些数据类型存储到任何给定的变量或成员中。也许每闰年一次,对变量或成员调用一个新方法——您的程序可能会因 a 崩溃NoMethodError,而您的测试可能完全错过了它,因为它依赖于您可能没有意识到的重要输入。(这是一个相当人为的例子。但极端情况是大多数编程缺陷存在的地方,动态类型使得极端情况更难发现。)

简而言之:您可以在地址字段中存储的内容没有限制。如果它支持您在这些对象上调用的方法,那么它——就语言而言——是一个Address. 如果它不支持您需要的方法,那么它会在充分详尽的测试中崩溃。

请务必充分利用测试工具,以确保您充分运用您的代码以找到任何不完全符合所需 API 的对象。