Rails заставляет to_param возвращать что-то, даже если оно не сохраняется

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

Пример: предположим, что мой пользователь может создавать сообщения, и это вызывает электронное письмо с уведомлением о создании сообщения, я хотел бы отправить пользователю пример создания поддельного сообщения. Для этого я использую FactoryGirl.build(:post) и передать это моему PostMailer.notify_of_creation(@post)

В повседневной жизни Rails мы используем маршрут url_helpers передавая в качестве аргумента саму модель, и генератор маршрутов автоматически преобразует модель в ее ID, который будет использоваться для генерации URL маршрута (в article_path(@article), маршруты помощник преобразует @article в @article.id для построения /articles/:id URL.

Я полагаю, что в ActiveRecord то же самое, но в любом случае в Mongoid это преобразование завершается неудачно, если модель не сохраняется (и это несколько неплохо, поскольку предотвращает создание URL-адресов, которые могут не соответствовать фактическим данным).

Так что в моем конкретном случае генерация URL падает, поскольку модель не сохраняется:

<%= post_url(@post_not_persisted) %> 

падает с

ActionView::Template::Error: No route matches {:action=>"show", :controller=>"posts", :post_id=>#<Post _id: 59b3ea2aaba9cf202d4eecb6 ...

Есть ли способ, которым я могу обойти это ограничение только в очень конкретной области? В противном случае я мог бы заменить все свои resource_path(@model) от resource_path(@model.id.to_s) или лучше @model.class.name но это не похоже на правильную ситуацию...

РЕДАКТИРОВАТЬ:

Основная проблема

Foo.new.to_param # => nil
# whereas
Foo.new.id.to_s # => "59b528e8aba9cf74ce5d06c0"

Мне нужно заставить to_param вернуть идентификатор (или что-то еще), даже если модель не сохраняется. Прямо сейчас я смотрю на доработки, чтобы посмотреть, смогу ли я использовать обезьянку с определенными областями, но если у вас есть лучшие идеи, пожалуйста, будьте моим гостем:-)

module ForceToParamToUseIdRefinement
  refine Foo do 
    def to_param
      self.class.name + 'ID'
    end
  end
end

Однако при использовании моего уточнения у меня возникает небольшая проблема с областью видимости, так как она не всплывает, как ожидается от url_helpers. Работает нормально при использовании доработки в консоли хотя (Foo.new.to_param # => 59b528e8aba9cf74ce5d06c0)

1 ответ

Решение

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

Чтобы было проще, я создал метод класса example_model_accessor что в основном ведет себя как attr_accessor исключает, что установщик исправляет метод #to_param объекта

def example_model_accessor(model_name)
  attr_reader model_name

  define_method(:"#{model_name}=") do |instance|
    def instance.to_param
      self.class.name + 'ID'
    end
    instance_variable_set(:"@#{model_name}", instance)
  end
end

Тогда в моем коде я могу просто использовать

class Testing
  example_model_accessor :message

  def generate_view_with_unpersisted_data
    self.message = FactoryGirl.build(:message)
    MessageMailer.created(message).deliver_now
  end
end

# views/message_mailer/created.html.erb
...
<%= message_path(@message) %> <!-- Will work now and output "/messages/MessageID" ! -->
Другие вопросы по тегам