Другой способ вывести хелпер вместо eval - PullRequest
0 голосов
/ 22 февраля 2012
(@products + @collections + @users + @questions).map do |r|
  @results << {
     :label => ["Product", "Collection", "User"].include?(r.class.name) ? r.name : r.question,
     :category => r.class.name,
     :href => eval("#{r.class.name.downcase}_path(r)")
  }
end

В настоящее время я смотрю, есть ли способ не использовать eval в строке для преобразования ее в помощник.

Примечание: Этот код в настоящее время находится в контроллере. Рельсы 2.3.11

Ответы [ 5 ]

1 голос
/ 22 февраля 2012

Использование polymorphic_path

@results = (@products + @collections + @users + @questions).map do |r|
  {
    :label => ["Product", "Collection", "User"].include?(r.class.name) ? r.name : r.question,
    :category => r.class.name,
    :href => polymorphic_path(r)
  }
end
1 голос
/ 22 февраля 2012

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

@results = (@products + @collections + @users + @questions).map do |r|
  {
     :label => r.try(:question) || r.name,
     :category => r.class.model_name.human,
     :href => send(:"#{r.class.model_name.underscore}_path", r)
  }
end
  1. Вам не нужно создавать массив результатов вручную - вы уже используете map, просто присвойте результат @results.
  2. Нет необходимости выполнять всю ручную работу, чтобы определить, должны ли вы публиковать вопрос или имя. Вы могли бы дать всем этим моделям метод to_label, который был бы довольно простым, но предполагая, что у вопросов нет имен, использующих try и двоичного кода, или это просто мертвый простой способ решения проблемы.
  3. Имена моделей, состоящих из одного слова, довольно легко преобразуются в отображаемые заголовки, но когда вы получите класс, подобный BedSheet, вы захотите, чтобы он отображался как «Простыня». Можешь сделать это правильно, чтобы начать.
  4. Другой стороной этой же проблемы является преобразование имени класса в вспомогательный метод path / url: вы хотите bed_sheet_path, а не bedsheet_path.
  5. Использование send над eval, как объяснено в других ответах. Нет необходимости использовать явный to_sym, поскольку Ruby поддерживает использование двойных кавычек для прямого создания символа.

Еще одно быстрое замечание, я не знаю, был ли Rails 2.x таким же, но в Rails 3 вам не нужно использовать помощник пути для ссылки на экземпляр модели, так как большинство вспомогательных методов html преобразуют автоматически (например, link_to 'A Product', @product).

Вуаля.

1 голос
/ 22 февраля 2012

Существует 3 способа динамического вызова методов с помощью тестов, показанных здесь . Я подведу итог статье ниже:

Одним из способов динамического вызова метода в ruby ​​является отправка сообщения объекту:

p s.send(:length) #=> 6 
p s.send(:include?,"hi") #=> true

Второй способ - создать экземпляр объекта метода и затем вызвать его:

method_object = s.method(:length) 
p method_object.call #=> 6
method_object = s.method(:include?)
p method_object.call('hi')  #=> true

И третий способ - использовать метод eval:

eval "s.length" #=> 6
eval "s.include? 'hi'" #=>true

Согласно результатам тестов, SLOWEST является пробным, поэтому вместо него я бы использовал send.

#######################################
#####   The results
#######################################
#Rehearsal ----------------------------------------
#call   0.050000   0.020000   0.070000 (  0.077915)
#send   0.080000   0.000000   0.080000 (  0.086071)
#eval   0.360000   0.040000   0.400000 (  0.405647)
#------------------------------- total: 0.550000sec

#          user     system      total        real
#call   0.050000   0.020000   0.070000 (  0.072041)
#send   0.070000   0.000000   0.070000 (  0.077674)
#eval   0.370000   0.020000   0.390000 (  0.399442)
1 голос
/ 22 февраля 2012

Вы можете попробовать что-то вроде этого:

:href => self.send("#{r.class.name.downcase}_path".to_sym, r)

Так как я не совсем уверен в контексте здесь, я не уверен на 100%, что это будет работать, но если это метод, выпытаясь сослаться, то для него наиболее вероятной является self.

0 голосов
/ 22 февраля 2012

Вы можете использовать справочную таблицу:

class_procs = {
    Product => {
        :path  => lambda { |r| product_path(r) },
        :label => lambda { |r| r.name }
    },
    Collection => {
        :path  => lambda { |r| collection_path(r) },
        :label => lambda { |r| r.name }
    }
    User => {
        :path  => lambda { |r| user_path(r) },
        :label => lambda { |r| r.name }
    },
    Question => {
        :path  => lambda { |r| question_path(r) },
        :label => lambda { |r| r.question }
    }
}

(@products + @collections + @users + @questions).map do |r|
    procs = class_procs[r.class]
    @results << {
        :label    => procs[:label].call(r),
        :category => r.class.name,
        :href     => procs[:path].call(r)
    }
end

И если ваши потребности усложняются, вы можете легко преобразовать хеши для каждого класса в отдельные классы, чтобы внутренние хеши не становились слишком большими и сложными.

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