简体   繁体   English

attr_accessor强类型Ruby on Rails

[英]attr_accessor strongly typed Ruby on Rails

Just wondering if anyone can shed some light on the basics of getter setters in Ruby on Rails with a view on strongly typed. 只是想知道是否有人能够在Ruby on Rails中对getter setter的基础知识进行一些了解,并提供强类型视图。 I am very new to ruby on rails and predominately have a good understanding of .NET. 我对rails上的ruby非常新,并且主要对.NET非常了解。

For example, let's consider we have a .net class called Person 例如,我们假设我们有一个名为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;}
}

In Ruby, I would write this as 在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

Looking at the Ruby version of the Person class how do I specify the types for the accessor methods FirstName, LastName and HomeAddress? 查看Person类的Ruby版本如何指定访问器方法FirstName,LastName和HomeAddress的类型? If I were to consume this class I could feed any type into HomeAddress but I want this accessor method to accept only the TYPE Address. 如果我要使用这个类,我可以将任何类型提供给HomeAddress,但我希望这个访问器方法只接受TYPE地址。

Any suggestions? 有什么建议?

TL;DR : No it's not possible ... and long answer, yes it is possible, read the metaprogramming section :) TL; DR :不,这是不可能的...而且答案很长,是的,有可能,阅读元编程部分:)

Ruby is a dynamic language, that's why you won't get compile time type warnings/errors as you get in languages like C#. Ruby是一种动态语言,这就是为什么你不会像C#这样的语言得到编译时类型警告/错误。

Same as you can't specify a type for a variable, you can't specify a type for attr_accessor . 与无法为变量指定类型相同,您无法为attr_accessor指定类型。

This might sound stupid to you coming from .NET, but in the Ruby community, people kind of expect you to write tests. 对于来自.NET的人来说,这可能听起来很愚蠢,但在Ruby社区,人们希望你能编写测试。 If you do so, these types of problems will basically vanish. 如果你这样做,这些类型的问题基本上会消失。 In Ruby on Rails, you should test your models. 在Ruby on Rails中,您应该测试您的模型。 If you do so, you won't really have any trouble with accidentaly assigning something somewhere wrong. 如果你这样做,你就不会有任何麻烦,因为意外地分配错误的东西。

If you're talking about ActiveRecord in Ruby on Rails specifically, assigning a String into an attribute which is defined as an Integer in the database will result in exception being thrown. 如果您在Ruby on Rails中专门讨论ActiveRecord,则将String分配给在数据库中定义为Integer的属性将导致抛出异常。

By the way, according to convention, you shouldn't use CamelCase for attributes, so the correct class definition should be 顺便说一句,按照惯例,你不应该使用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

One reason for this is that if you Capitalize the first letter, Ruby will define a constant instead of a variable. 其中一个原因是,如果你将第一个字母大写,Ruby将定义一个常量而不是变量。

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

Metaprogramming hacks 元编程黑客攻击

By the way, Ruby also has insanely huge metaprogramming capabilities. 顺便说一下,R​​uby还具有疯狂的元编程功能。 You could write your own attr_accessor with a type check, that could be used something like 您可以使用类型检查编写自己的attr_accessor ,可以使用类似的方法

typesafe_accessor :price, Integer

with definition 有定义 something 某物 like 喜欢

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!

or at least something along these lines :) 或至少在这些方面的东西:) This code works! 这段代码有效! Ruby allows you to define methods on the fly, which is how attr_accessor works. Ruby允许您动态定义方法,这就是attr_accessor工作方式。

Also blocks are almost always closures, which means I can do the if value.is_a? type 块也几乎总是闭包,这意味着我可以做if value.is_a? type if value.is_a? type without passing it as a parameter. if value.is_a? type而不将其作为参数传递。

It's too complicated to explain here when this is true and when it's not. 如果这是真的,那么在这里解释太复杂了。 In short, there are different types of blocks 简而言之,有不同类型的块

  • Proc , which is created by Proc.new Proc ,由Proc.new创建
  • lambda , which is created by the keyword lambda lambda ,由关键字lambda创建

one of the differences is that calling return in a lambda will only return from the lambda itself, but when you do the same thing from a Proc , the whole method around the block will return, which is used when iterating, eg 其中一个区别是在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    

If you're into this kind of stuff, I recommend you check out PragProg Ruby Metaprogramming screencast . 如果您正在使用这种东西,我建议您查看PragProg Ruby Metaprogramming截屏视频

Ruby is a dynamically typed language; Ruby是一种动态类型语言; like many dynamically typed languages, it adheres to duck typing -- from the English Idiom, "If it walks like a duck and quacks like a duck, then it's a duck." 像许多动态类型的语言一样,它坚持使用鸭子打字 - 来自英语成语,“如果它像鸭子一样走路,像鸭子一样嘎嘎叫,那么它就像鸭子一样。”

The upside is that you don't have to declare types on any of your variables or class members. 好处是您不必在任何变量或类成员上声明类型 The restrictions on what types of objects you can store into the variables or class members comes only from how you use them -- if you use << to "write output", then you could use a file or array or string to store the output. 您可以存储到变量或类成员中的对象类型的限制仅来自您如何使用它们 - 如果您使用<<来“写入输出”,那么您可以使用文件或数组或字符串来存储输出。 This can greatly increase the flexibility of your classes. 这可以大大提高课程的灵活性。 (How many times have you been upset that an API you must use required a FILE * C standard IO file pointer rather than allowing you to pass in a buffer?) (你有多少次不满意你必须使用的API需要一个FILE * C标准IO文件指针,而不是允许你传入一个缓冲区?)

The downside (and, in my mind, it's a big one) is that there's no easy way for you to determine what data types you can safely store into any given variable or member. 缺点(在我看来,它是一个很大的缺点)是你没有简单的方法来确定你可以安全地存储到任何给定变量或成员的数据类型。 Perhaps once every leap year, a new method is called on a variable or member -- your program might crash with a NoMethodError , and your testing might have missed it completely because it relied on inputs you might not realize were vital. 也许每闰年一次,在变量或成员上调用一个新方法 - 您的程序可能会因NoMethodError而崩溃,并且您的测试可能完全错过了它,因为它依赖于您可能没有意识到的输入是至关重要的。 (This is a fairly contrived example. But corner cases are where most programming flaws exist and dynamic typing makes corner cases that much harder to discover.) (这是一个相当人为的例子。但角落情况是大多数编程缺陷存在的地方,动态类型使角落案例更难发现。)

In short: there's no restriction on what you can store in your Address fields. 简而言之:您可以在地址字段中存储的内容没有限制。 If it supports the methods you call on those objects, it is -- as far as the language is concerned -- an Address . 如果它支持你在这些对象上调用的方法,那么就语言而言,它就是一个Address If it doesn't support the methods you need, then it will crash during sufficiently-exhaustive testing. 如果它不支持您需要的方法,那么它将在充分详尽的测试期间崩溃。

Just be sure to use the testing facilities to their fullest, to make sure you're exercising your code sufficiently to find any objects not fully compliant with a required API. 请务必充分利用测试工具 ,确保您充分运用代码,找到不完全符合所需API的任何对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM