attr_accessor强类型Ruby on Rails

只是想知道是否有人可以通过强类型的视图阐明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;} } 

在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 

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

有什么建议 ?

谢谢

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

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

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

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

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

顺便说一下,按照惯例,你不应该使用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 

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

 number = 1 # regular variable Pi = 3.14159 # constant ... changing will result in a warning, not an error 

元编程黑客攻击

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

 typesafe_accessor :price, Integer 

有定义 某物 喜欢

 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! 

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

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

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

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

其中一个区别是在lambda中调用return只会从lambda本身返回,但是当你从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 

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

Ruby是一种动态类型语言; 像许多动态类型的语言一样,它坚持打字 – 从英语成语,“如果它像鸭子一样走路,像鸭子一样嘎嘎叫,那就是鸭子。”

好处是您不必在任何变量或类成员上声明类型 。 您可以存储到变量或类成员中的对象类型的限制仅来自您如何使用它们 – 如果您使用<<来“写入输出”,那么您可以使用文件或数组或字符串来存储输出。 这可以大大提高课程的灵活性。 (你有多少次不满意你必须使用的API需要一个FILE * C标准IO文件指针,而不是允许你传入一个缓冲区?)

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

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

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

Interesting Posts