【问题标题】:Rails force to_param to return something even when not persistedRails 强制 to_param 返回一些东西,即使没有持久化
【发布时间】:2018-02-18 05:53:57
【问题描述】:

我需要处理使用非持久数据构造的 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=&gt;"show", :controller=&gt;"posts", :post_id=&gt;#&lt;Post _id: 59b3ea2aaba9cf202d4eecb6 ...

有没有办法只能在非常特定的范围内绕过这个限制?否则我可以用resource_path(@model.id.to_s) 或更好的@model.class.name 替换我所有的resource_path(@model),但这感觉不是正确的情况......

编辑:

主要问题是

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

即使模型没有持久化,我也需要强制 to_param 返回 ID(或其他内容)。现在我正在研究改进,看看我是否可以使用作用域猴子补丁,但如果你有更好的想法,请成为我的客人:-)

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

但是,在使用我的改进时,我似乎遇到了一个小范围问题,因为这并没有像 url_helpers 预期的那样冒泡。不过,在控制台中使用 te 细化时效果很好 (Foo.new.to_param # =&gt; 59b528e8aba9cf74ce5d06c0)

【问题讨论】:

    标签: ruby-on-rails mongoid ruby-on-rails-5 actionview mongoid6


    【解决方案1】:

    我找到了一种使用动态方法覆盖的方法。我真的不喜欢它,但它可以完成工作。我基本上是在修补我在测试期间使用的实例。

    为了方便起见,我创建了一个类方法example_model_accessor,它的行为基本上类似于attr_accessor,只是setter 修补了对象的#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" ! -->
    

    【讨论】:

      猜你喜欢
      • 2011-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-11
      • 2012-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多