Определение динамического метода в ruby

Что делают $1.to_sym => args[0] и ($1.to_sym,*args,&block) в следующей строке кода?

class Table
  def method_missing(id,*args,&block)
    return as($1.to_sym,*args,&block) if id.to_s =~ /^to_(.*)/
    return rows_with($1.to_sym => args[0]) if id.to_s =~ /^rows_with_(.*)/
    super
  end
  # ...
end

Контекст:
Ruport - это библиотека отчетов на Ruby. Вы можете использовать класс Ruport :: Data :: Table для создания табличных данных и преобразования их в различные форматы - текст, например:

require 'ruport'  
table = Ruport::Data::Table.new :column_names => ["country" , "wine" ], :data => [["France" , "Bordeaux" ], ["Italy" , "Chianti" ], ["France" , "Chablis" ]]  
puts table.to_text

⇒    
+--------------------+
| country |   wine   |
+--------------------+
| France | Bordeaux |
| Italy  | Chianti |
| France | Chablis |
+--------------------+

Допустим, вы выбрали только французские вина и преобразовали их в значения, разделенные запятыми:

found = table.rows_with_country("France" )
found.each do |row|
  puts row.to_csv
end

⇒
France, Bordeaux
France, Chablis

Вы только что вызывали метод с именем rows_with_country () в Ruport :: Data :: Table. Но как автор этого класса мог знать, что у вас будет столбец с именем country? Дело в том, что автор этого не знал. Если вы заглянете внутрь Ruport, вы увидите, что и rows_with_country (), и to_csv () являются призрачными методами. Класс Ruport :: Data :: Table в некоторой степени соответствует определению, приведенному выше.

Вызов rows_with_country () становится вызовом более традиционного метода, rows_with (: country), который принимает имя столбца в качестве аргумента. Кроме того, вызов to_csv () становится вызовом as (: csv). Если имя метода не начинается ни с одного из этих двух префиксов, Ruport возвращается к Kernel # method_missing (), что вызывает ошибку NoMethodError. (Вот для чего нужно ключевое слово super.)


person shailesh    schedule 10.07.2012    source источник
comment
Краткий ответ: он берет первую группу совпадений и преобразует ее в символ, в данном случае конечное значение rows_with_ или to_, ​​и либо передает его как параметр метода, либо использует его как хэш-ключ с первым аргументом для method_missing.   -  person Dave Newton    schedule 10.07.2012


Ответы (1)


Посмотрим на method_missing:

def method_missing(id,*args,&block)
  return as($1.to_sym,*args,&block) if id.to_s =~ /^to_(.*)/
  return rows_with($1.to_sym => args[0]) if id.to_s =~ /^rows_with_(.*)/
  super
end

Необходимый фон: method_missing вызывается для объекта, когда запрошенный метод не определен явно. id будет символом вызываемого метода; args будет массивом аргументов, а block будет Proc, если был блок, или nil.

  return as($1.to_sym,*args,&block) if id.to_s =~ /^to_(.*)/

Выполнение действительно начинается в конце, в if: оно проверяет условие

id.to_s =~ /to_(.*)/

Это в основном соответствует регулярному выражению в строке вызываемого метода. x =~ y возвращает целочисленное смещение в x, где найдено y, если где-либо, в противном случае - ноль. например.:

> "abc" =~ /a/
 => 0
> "abc" =~ /.$/
 => 2
> "abc" =~ /\d/
 => nil

Помните, что 0 рассматривается как истина в логических условиях, и поэтому if будет считаться истинным только в том случае, если имя вызываемой функции начинается с to_. Остальная часть имени метода фиксируется (.*).

Сейчас мы явно не сохраняем группы захвата, но Ruby заимствует у Perl то, что содержимое первой группы захвата будет сохранено в $1, второй - в $2 и т. Д .:

> "abc" =~ /a(.)(.)/
 => 0
> $1
 => "b"
> $2
 => "c"

Теперь вернемся к рассматриваемой строке:

  return as($1.to_sym,*args,&block) if id.to_s =~ /^to_(.*)/

Итак, если имя вызываемого метода имеет форму to_XYZ, он вызывает метод as() с первым аргументом, установленным в :XYZ, а остальные аргументы из вызова добавлены, а также переданным блоком (если есть).

Чтобы продолжить:

  return rows_with($1.to_sym => args[0]) if id.to_s =~ /^rows_with_(.*)/

Это в основном то же самое: если имя метода похоже на rows_with_ABC, тогда он вызывает rows_with() с хешем {:ABC => args[0]}, где args[0] - это первый аргумент, переданный отсутствующему вызову метода.

person Asherah    schedule 10.07.2012
comment
в чем польза хеша {: ABC = ›args [0]} - person shailesh; 10.07.2012
comment
@shaileish: это просто хэш с единственным отображением "ключ-значение" :ABC и args[0]. Другими словами, если вы вызвали table.rows_with_country("France") в соответствии с примером, method_missing преобразует его в вызов table.rows_with({:country => "France"}). - person Asherah; 11.07.2012