Ответ 1
TL; DR: Нет, это невозможно... и длинный ответ, да, возможно, прочитайте раздел метапрограммирования:)
Ruby - динамический язык, поэтому вы не получите предупреждения/ошибки типа времени компиляции, поскольку вы получаете на таких языках, как С#.
Так же, как вы не можете указать тип переменной, вы не можете указать тип для attr_accessor
.
Это может показаться глупым для вас, начиная с .NET, но в сообществе Ruby люди ожидают, что вы напишете тесты. Если вы это сделаете, эти типы проблем в основном исчезнут. В Ruby on Rails вам следует протестировать свои модели. Если вы это сделаете, у вас не будет никаких проблем с случайным назначением чего-то не так.
Если вы говорите об ActiveRecord в Ruby on Rails специально, назначая 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
Одна из причин этого заключается в том, что если вы заглавные буквы первой буквы, Ruby будет определять константу вместо переменной.
number = 1 # regular variable
Pi = 3.14159 # constant ... changing will result in a warning, not an error
Метапрограммирование хаков
Кстати, Ruby также обладает безумно огромными возможностями метапрограммирования. Вы можете написать свой собственный attr_accessor
с проверкой типа, который можно использовать как-то вроде
typesafe_accessor :price, Integer
с определением something, например
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 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 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
, не передавая его в качестве параметра.
Это слишком сложно объяснить здесь, когда это правда, а когда нет. Короче говоря, существуют различные типы блоков
-
Proc
, который создаетсяProc.new
-
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 screencast.