Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating Ruby Gem to insert HTML/JavaScript into main Application

I create a Rails Engine to easily include a JavaScript plugin for rails apps. The problem is that this plugin is really only for development and when pushing an app to production the developer needs to remove the require marx in the javascript manifest file and then the actual call in the JavaScript.

What I'm hoping to do is allow the developer to just add this to the Gemfile with gem 'marxjs-rails', group: :development and setup some configurations in config/application.rb or config/environments/development.rb for the plugin settings.

The gem should then insert html to the parent application:

<script src='marx.js'></script>
<script>
  var marx = new Marx()
</script>

I've been playing around with a helper method to insert the html but I can't get it to insert it as html. Rails continues to escape the HTML. Though I don't really like this path any how.

I've been looking through the jasmine-gem and rack-olark as examples, but can't quite get anything to work.

UPDATE

This is not the solution I'm really looking for but this may help others and is what I'm using till I can track down a better way to do this. You can view the whole code here but I'll give you the highlights:

# lib/marxjs/rails.rb
require "marxjs/rails/version"
require "marxjs/view_helpers"

module Marxjs
  module Rails
    class Engine < ::Rails::Engine
      initializer "marxjs.view_helpers" do
        ActionView::Base.send :include, ViewHelpers
      end
    end
  end
end

and the view helpers:

# lib/marxjs/view_helpers.rb
require 'erb'

module Marxjs
  module ViewHelpers
    include ActionView::Helpers::OutputSafetyHelper

    def render_marxjs options={}, dev_only=true
      if dev_only == false || (dev_only && ::Rails.env.development?)
        binding.local_variable_set(:options, options.to_json)
        template_file = ::File.read(::File.expand_path('../templates/marxjs.erb', __FILE__))
        raw ERB.new(template_file).result(binding)
      end
    end
  end
end

biggest thing to take away here is the include ActionView::Helpers::OutputSafetyHelper and also the require 'erb' This gives us the ability to load a template erb file and use ERB.new to render it and use raw to keep rails from escaping the html. I also bound an options variable to the binding object to send it to the template.

<script src="assets/marx.js"></script>
<script>
  var marx = new Marx(<%= options %>);
</script>

and finally in my main rails application I call render_marxjs(controls: 'toggle-all') in the application.html.haml (or .erb if you like)

Again I'm still looking for someone to help me handle this without a view helper method and also to be able to set some configurations via an environment file, but I hope this may help some...

like image 390
Sparkmasterflex Avatar asked Jun 28 '15 21:06

Sparkmasterflex


3 Answers

You can achieve this without creating an engine.

  • Add a file called lib/marxjs/script_inserter.rb

    module Marxjs
      module ScriptInserter
        BODY_TAG = %r{</body>}
    
        JS_SCRIPT = %q{
          <script src='marx.js'></script>
          <script>
            var marx = new Marx()
          </script>    
        }
    
        def insert_marxjs
          if (
            response.content_type == 'text/html' &&
            response.body.match(BODY_TAG)
          )
            response.body = response.body.gsub(BODY_TAG, JS_SCRIPT + '\\0')
          end      
        end
      end
    end
    
  • Add lib/marxjs/railtie.rb

    module Marxjs
      class Railtie < Rails::Railtie
        initializer "marxjs-rails" do |app|
          ActionController::Base.send :include Marxjs::ScriptInserter      
          ActionController::Base.after_filter :insert_marxjs
        end
      end
    end
    
  • Update lib/marxjs/rails.rb

    require "marxjs/rails/version"
    require "marxjs/view_helpers"
    require "marxjs/script_inserter"
    
    # move rails initialization code to railtie.rb
    
  • Add the gem to Gemfile

    gem 'marxjs-rails', group: :development
    
like image 117
Harish Shetty Avatar answered Oct 18 '22 01:10

Harish Shetty


The rack-mini-profiler gem does something like this, where it adds UI elements to your screen but only in development (by default). See https://github.com/MiniProfiler/rack-mini-profiler/blob/master/lib/mini_profiler_rails/railtie.rb

like image 29
Greg Fleming Avatar answered Oct 18 '22 02:10

Greg Fleming


For me the best way is to add the engine on all environments, but require it separately on application.html.haml:

= javascript_include_tag "mark.js" if Rails.env.development?

Be sure to add it to your config/application.rb as well to be compiled:

config.assets.precompile += %w( mark.js )
like image 1
Pedro Nascimento Avatar answered Oct 18 '22 01:10

Pedro Nascimento