rubyruby-on-rails-3activeview

access ActionView helpers from inside the Model


I have a simple template system which calls a show_me method defined inside different classes of my model (kinda Widgets) when rendering a template. These widgets originally returned the html as a string. So in my erb I have somethink like this.

<% @template.widgets.each do |widget| %>
   <%= widget.show_me %>    
<% end %>

As the views of the widgets became more complex I start using partials to render them, calling the ActionView::Base render method from inside my widgets (please don't throw up yet :)

def show_me
      # ... specific calculations and other magic.
    ActionView::Base.new(MyApp::Application.config.view_path).render(:partial => "widgets/weather_widget", :locals => {:data => data, :settings => settings})  
end

So, this works like a charm (really...) but when I want to use helpers inside the widget specific partial (eg. widgets/_weater_widget.html.erb) they don't work. for example. javascript_tag raises

can't convert nil into String
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:790:in `join'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:790:in `rails_asset_id'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:813:in `rewrite_asset_path'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:742:in `compute_public_path'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:256:in `javascript_path'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:822:in `javascript_src_tag'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:362:in `block in javascript_include_tag'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:362:in `collect'
actionpack (3.0.0) lib/action_view/helpers/asset_tag_helper.rb:362:in `javascript_include_tag'

I think I'm missing something when I create the ActionView::Base.

Any thoughts how to solve this? Also any suggestion on the overall design is welcome too :)


Solution

  • Please for the love of all things MVC don't render view code from a model. :( There is always a better way to do it.

    Example:

    Model

    class Widget < ActiveRecord::Base
      def name
        "weather_widget"
      end
    
      def settings
        ## something here
      end
    
      def data
        ## calculations here
      end
    end
    

    Helper

    module WidgetHelper
    
      def render_widget(widget)
        render(:partial => "widgets/_#{widget.name}", :locals => {:data => widget.data, :settings => widget.settings})
      end
    
    end
    

    View

    <% @template.widgets.each do |widget| %>
      <%= render_widget(widget) %>    
    <% end %>
    

    Obviously that may not be the exact solution for your situation, but is just a guide to show you something that can be done to keep your code clean. :)