TL; DR : Нет, это невозможно ... и длинный ответ, да, это возможно, прочитайте раздел метапрограммирования:)
Ruby - это динамический язык, поэтому вы не будете получать предупреждения / ошибки типа времени компиляции, как в таких языках, как C #.
То же, что вы не можете указать тип для переменной, вы не можете указать тип для 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
с определением что-то как
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
без передачи его в качестве параметра.
Слишком сложно объяснить здесь, когда это правда, а когда нет. Короче, есть разные типы блоков
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 .