0

I have a controller that will server widgets:

class WidgetsController < ApplicationController
  def widget1
    respond_to do |format|
      format.html
      format.js { render js: js_constructor }
    end
  end

  def widget2
    respond_to do |format|
      format.html
      format.js { render js: js_constructor }
    end
  end

  private
  def js_constructor
    content = render_to_string(params[:action], layout: false)
    "document.write(#{content.to_json})"
  end
end

This controller will get bigger, so I would like to avoid repeating this block of code in all actions:

respond_to do |format|
  format.html
  format.js { render js: js_constructor }
end

Any idea?

EDIT: just to give some context...

The route is flexible/dynamic: get 'widgets/:action', to: 'widgets#:action'

So, if I visit widgets/widget1 it will render the view widget1.html.erb.

If I include the script in another server it will construct the widget1 over there:

<script type='text/javascript' src='http://localhost:3000/widgets/widget1.js'></script>
gabrielhilal
  • 10,660
  • 6
  • 54
  • 81
  • 3
    If there's literally no difference between the widgets, why are you creating multiple routes for them? – Makoto Jun 09 '15 at 17:07
  • 1
    Each action returns the content of its respective view. There is only one flexible route (`get 'widgets/:action', to: 'widgets#:action'`). So the html format is used for development and the js will construct the view in somewhere else (``). – gabrielhilal Jun 09 '15 at 17:12
  • @Makoto: I have edited my question to provide some context. I hope it is clear now ;) – gabrielhilal Jun 09 '15 at 17:19
  • You could write `alias widget2 widget1` or `more_widgets = %w|widget2 widget3|; w.each { |w| alias_method w.to_sym, :widget1 }` or `def method_missing(m) { |m| (m.to_s =~ /^widget\d+$/) ? widget1 : super }`. – Cary Swoveland Jun 09 '15 at 17:44

2 Answers2

1

And the answer is meta-programming:

class WidgetsController < ApplicationController
  [:widget1, :widget2].each do |name|
    define_method(name) do
      respond_to do |format|
        format.html
        format.js { render js: js_constructor }
      end
    end
  end

  private

  def js_constructor
    content = render_to_string(params[:action], layout: false)
    "document.write(#{content.to_json})"
  end
end
phoet
  • 18,688
  • 4
  • 46
  • 74
1

I would write a single action that takes the "view" as a parameter. You're basically doing a #show action that renders different views.

get 'widgets/:template', to: 'widgets#show'

class WidgetsController < ApplicationController
  def show
    respond_to do |format|
      format.html { render params.require(:template) }
      format.js { render js: js_constructor }
    end
  end

  private
  def js_constructor
    content = render_to_string(params.require(:template), layout: false)
    "document.write(#{content.to_json})"
  end
end
engineersmnky
  • 25,495
  • 2
  • 36
  • 52
supermoogle
  • 1,207
  • 10
  • 7
  • in terms of security and maintainability you should use a array containing all valid templates and provide a default template. – phoet Jun 10 '15 at 08:09