Зачем миграциям нужен параметр блока таблицы? - PullRequest
0 голосов
/ 05 октября 2009

Почему синтаксис миграции ruby ​​on rails выглядит следующим образом:

create_table :my_table do |t|
     t.integer :col
     t.integer :col2
     t.integer :col3
end

А не:

create_table :my_table do
     integer :col
     integer :col2
     integer :col3
end

Лично я нахожу второй фрагмент гораздо более читабельным, есть ли причины, по которым реализация использует первый?

Ответы [ 3 ]

3 голосов
/ 05 октября 2009

Фундаментальная реализация двух подходов различна. В первом (и фактическом) случае create_table вызывает yield с объектом TableDefinition. Так что t в вашем примере блока указывает на это TableDefinition. Альтернативный метод заключается в использовании instance_eval. Это будет выглядеть примерно так:

def create_table(name, &block)
  table_definition = TableDefinition.new
  # Other setup
  table_definition.instance_eval(&block)
  # More work
end

То, как вы это делаете, частично зависит от предпочтений. Тем не менее, некоторые люди не являются поклонниками Eval, поэтому они хотели бы избежать этого. Кроме того, использование метода yield проясняет, с каким объектом вы работаете.

0 голосов
/ 05 октября 2009

По сути, дизайнер интерфейса должен был сделать это из-за этого небольшого трюка с областями действия и тем, как работают eval и instance_eval, посмотрите этот пример:

Имеет 2 класса Foo и Boo со следующими определениями:

class Foo
  def speak(phrase)
    puts phrase
  end 
  def self.generate(&block)
    f = Foo.new
    f.instance_eval(&block)
  end
end

class Boo
  attr_reader :name
  def initialize(name) ; @name = name ; end
  def express
    Foo.generate { speak name}
  end
end

Обычно это должно работать нормально в большинстве случаев, но в некоторых ситуациях, таких как следующий оператор, выдается ошибка:

Boo.new("someone").express #`express': undefined local variable or method `name' for #<Foo:0xb7f582fc> (NameError)

У нас нет здесь доступа к методам экземпляров Boo внутри Foo экземпляров, потому что мы используем instance_eval, поэтому метод name определен для Boo экземпляры не входят в область действия Foo экземпляров.

Чтобы преодолеть такие проблемы, лучше переопределить генерирование следующим образом:

class Foo
  def speak(phrase)
    puts phrase
  end
  def self.generate(&block)
    f = Foo.new
    block.arity < 1 ? f.instance_eval(&block) : block.call(f)
  end
end

Это гибкий интерфейс, в котором вы оцениваете блок кода в зависимости от переданных параметров блока. Теперь мы должны передать текущий объект foo в качестве параметра, когда нам нужно вызвать методы экземпляра для него, давайте переопределим Boo, проверим express и talk:

class Boo
  attr_reader :name
  def initialize(name) ; @name = name ; end
  def express
    Foo.generate { |f| f.speak name}
  end
  def talk(anything)
    Foo.generate { speak anything}
  end
end

Boo.new("someone").express #=> someone
Boo.new("someone").talk("whatever") #=> whatever
0 голосов
/ 05 октября 2009

Я понимаю, что ruby ​​имеет лексическую область, что означает, что "integer" должен относиться к чему-то определенному в той точке, в которой оно встречается в коде. Вам нужно динамическое определение объема, чтобы выполнить то, что вы просите.

Может быть, я ошибаюсь, и по крайней мере один из процессов, блоков и лямбда-выражений динамически ограничен, но тогда у вас все еще есть свой ответ - неясные детали того, как ведет себя область действия, не стоит ожидать от программиста знаю.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...