如何在ActionController :: Base之外的类中正确包含ActionView :: Helpers?

时间:2017-01-20 10:40:56

标签: ruby-on-rails ruby

我想创建将为用户生成通知的自定义类。我的问题是:如何通过link_to Rails帮助程序生成通知文本中的资源链接?

我所尝试的内容:

来自this的相关说明以及"如何在模型"中使用link_to:包括ActionView::Helpers::UrlHelperRails.application.routes.url_helpersMaker等类似答案} class并且有以下错误,当我尝试使用create_text时出现在link_to方法中:

EmployeesControllerTest#test_company_vacancy_daily:
NameError: undefined local variable or method `controller' for #<Notifies::Maker:0x000000069ae218>
    /usr/local/rvm/gems/ruby-2.2.5/gems/actionview-5.0.0.1/lib/action_view/routing_url_for.rb:132:in `optimize_routes_generation?'
    /usr/local/rvm/gems/ruby-2.2.5/gems/actionpack-5.0.0.1/lib/action_dispatch/routing/route_set.rb:192:in `optimize_routes_generation?'
    /usr/local/rvm/gems/ruby-2.2.5/gems/actionpack-5.0.0.1/lib/action_dispatch/routing/route_set.rb:172:in `call'
    /usr/local/rvm/gems/ruby-2.2.5/gems/actionpack-5.0.0.1/lib/action_dispatch/routing/route_set.rb:295:in `block (2 levels) in define_url_helper'
    /usr/local/rvm/gems/ruby-2.2.5/gems/actionpack-5.0.0.1/lib/action_dispatch/routing/polymorphic_routes.rb:262:in `handle_model_call'
    /usr/local/rvm/gems/ruby-2.2.5/gems/actionview-5.0.0.1/lib/action_view/routing_url_for.rb:116:in `url_for'
    /usr/local/rvm/gems/ruby-2.2.5/gems/actionview-5.0.0.1/lib/action_view/helpers/url_helper.rb:196:in `link_to'
    /app/lib/notifies.rb:32:in `block in create_text'
    /app/lib/notifies.rb:32:in `map!'
    /app/lib/notifies.rb:32:in `create_text'
    /app/lib/notifies.rb:38:in `run'
    /app/test/controllers/notifications_test.rb:167:in `block (2 levels) in <class:EmployeesControllerTest>'
    /usr/local/rvm/gems/ruby-2.2.5/gems/activesupport-5.0.0.1/lib/active_support/testing/assertions.rb:71:in `assert_difference'
    /app/test/controllers/notifications_test.rb:166:in `block in <class:EmployeesControllerTest>'

我的代码是:

module Notifies
  class Maker

    include ActionView::Helpers::UrlHelper
    include Rails.application.routes.url_helpers

    def initialize(model, kind)
      @model = model
      @kind = kind

      case @model.class.to_s
      when 'Company'
        @to = @model.admin
      when 'Employee'
        @to = @model
      end
    end

    def build_list
      present = Time.now.utc
      past = present - 24.hours

      popularities = Popularity.where(to: @model.entity.id).select do |e|
        e.updated_at.utc.between?(past, present)
      end

      popularities.map! { |p| p.from_entity.turn }
    end

    def create_text(popularities = build_list)
      text = 'Those users interested in your contacts: '
      popularities.map! { |p| link_to p.full_name, p }
      text + popularities.join(', ')
    end

    def run
      popularities = build_list
      text = create_text(popularities)
      @to.notify(kind: @kind, text: text) if popularities.any?
    end
  end
end

2 个答案:

答案 0 :(得分:2)

我想出了如何做到这一点。该错误是由不正确的url_for调用引起的,应该使用参数only_path调用,如下所示:

def create_text(popularities = build_list)
  text = 'Those users interested in your contacts: '
  popularities.map! do |reciever|
    link_to reciever.full_name, url_for([reciever, only_path: true])
  end
  text + popularities.join(', ')
end

或者作为替代方案,应在enviropment配置文件中设置default_host参数。

答案 1 :(得分:1)

我建议使用Presenter Pattern的变体(它允许您在Plain Old Ruby Object中调用link_to之类的方法)。 Ryan Bates在这个话题上有一个很好的RailsCast。这大致是我如何做到的......

首先,我要创建一个PresenterBase类(我在presenters目录中保留一个app文件夹):

  app/presenters/presenter_base.rb

  class PresenterBase

    class << self
      def present(controller)     new(controller).present                 end
    end

      def initialize(controller)  @controller = controller                end

    private

      def controller()            @controller                             end
      def view_context()          controller.view_context                 end

      def get_controller_variable(sym)
        controller.instance_variable_get("@#{sym}")
      end

      def method_missing(*args, &block)
        view_context.send(*args, &block)
      end

  end

请注意,我传递了controller,然后通过view_context访问了controller.view_context。然后,我覆盖method_missing,将未知方法(例如link_to)发送到view_context(具有link_to方法)。

然后你的Notifies::Maker看起来像这样:

  class Notifies::Maker < PresenterBase

    class << self
      def run(controller) new(controller).run end
    end

      def run
        to_notify.notify(kind: kind, text: text) if popularities.any?
      end

    private

      def kind()            @kind ||= get_controller_variable(:kind)      end
      def model()           @model ||= get_controller_variable(:model)    end
      def past_time()       present_time - 24.hours                       end
      def popularities()    @popularities ||= get_popularities            end
      def present_time()    @present_time ||= Time.now.utc                end

      def to_notify() 
        case model.class.to_s
        when 'Company'
          model.admin
        when 'Employee'
          model
        end
      end

      def get_popularities
        Popularity.where(to: model.entity.id).select do |e|
          e.updated_at.utc.between?(past_time, present_time)
        end
      end

      def text
        'Those users interested in your contacts: ' << links
      end

      def links
        popularities.
          map!{ |p| p.from_entity.turn }.
          map!{ |p| link_to p.full_name, p }.
          join(', ')
      end

  end

最后,您的控制器可能看起来像这样:

  class FooController < ApplicationController

    def foo_method
      @model = some_method_to_set_model
      @kind = some_method_to_set_kind
      Notifies::Maker.run(self)
    end

  end

我没有测试,所以它可能是错误的。但是,也许它会有所帮助。