4

I tried to create a Sprocket directive to take into account changes in my config files:

in initializers/sprockets.rb

class Sprockets::DirectiveProcessor
  def process_depend_on_config_directive(file)
    path = File.expand_path(file, "#{Rails.root}/config/locales")
    context.depend_on(path)
  end
end

In application.js

//= depend_on_config en.yml
//= depend_on_config fr.yml

Refreshing the page on the concerned js file gives me different answers, I have no way of fixing this behavior:

15:02:03 web.1 | 127.0.0.1 - - [17/Jul/2014 15:02:03] "GET /angular_js/services/better_translate_service-04bd3e149eb227767d3910de31fb2489.js?body=1 HTTP/1.1" 200 23560 0.0105 15:02:05 web.1 | 127.0.0.1 - - [17/Jul/2014 15:02:05] "GET /angular_js/services/better_translate_service-04bd3e149eb227767d3910de31fb2489.js?body=1 HTTP/1.1" 200 23564 0.0079 15:02:06 web.1 | 127.0.0.1 - - [17/Jul/2014 15:02:06] "GET /angular_js/services/better_translate_service-04bd3e149eb227767d3910de31fb2489.js?body=1 HTTP/1.1" 304 - 0.0061 15:02:06 web.1 | 127.0.0.1 - - [17/Jul/2014 15:02:06] "GET /angular_js/services/better_translate_service-04bd3e149eb227767d3910de31fb2489.js?body=1 HTTP/1.1" 200 23560 0.0091 15:02:07 web.1 | 127.0.0.1 - - [17/Jul/2014 15:02:07] "GET /angular_js/services/better_translate_service-04bd3e149eb227767d3910de31fb2489.js?body=1 HTTP/1.1" 200 23564 0.0076 15:02:07 web.1 | 127.0.0.1 - - [17/Jul/2014 15:02:07] "GET /angular_js/services/better_translate_service-04bd3e149eb227767d3910de31fb2489.js?body=1 HTTP/1.1" 304 - 0.0093 15:02:08 web.1 | 127.0.0.1 - - [17/Jul/2014 15:02:08] "GET /angular_js/services/better_translate_service-04bd3e149eb227767d3910de31fb2489.js?body=1 HTTP/1.1" 200 23560 0.0080 15:02:08 web.1 | 127.0.0.1 - - [17/Jul/2014 15:02:08] "GET /angular_js/services/better_translate_service-04bd3e149eb227767d3910de31fb2489.js?body=1 HTTP/1.1" 200 23564 0.0091 15:02:09 web.1 | 127.0.0.1 - - [17/Jul/2014 15:02:09] "GET /angular_js/services/better_translate_service-04bd3e149eb227767d3910de31fb2489.js?body=1 HTTP/1.1" 304 - 0.0059

Edit: Some clarifications:

  • I want to point ou that the hash of the asset file is the same and that the content is different.
  • When I say refreshing the page, I mean I open the asset file in a different tab, and when I refresh the page, the content changes.

Edit2: Adding more information:

Better translate service file (the .erb):

<% environment.context_class.instance_eval { include ApplicationHelper } %>
<%# encoding: utf-8 %>

app.factory('Pg', ['SETTINGS', function(SETTINGS) {
  var polyglot = {};

  polyglot.translations = {

    en: new Polyglot({ phrases: <%=  I18n.with_locale(:en) { phrases_for_polyglot.to_json } %>, locale: "en" }),
    fr: new Polyglot({ phrases: <%=  I18n.with_locale(:fr) { phrases_for_polyglot.to_json } %>, locale: "fr" })
  };

  polyglot.current = 'en';

  polyglot.t = _.memoize(function(key, opts) {
    return polyglot.translations[polyglot.current].t(key, opts);
  }, function(key, opts){
    if(opts===undefined){ return polyglot.current + ''+ key; }
    return polyglot.current + '' + key + polyglot.serialize(opts);
  });


  polyglot.serialize = function(obj) {

    var str = [];
    for(var p in obj){
      if(obj.hasOwnProperty(p)) {
        str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
      }
    }
    return str.join("&");
  };
  //fde


  polyglot.selectLanguage = function(lang){
    polyglot.current = lang;
  };

  return polyglot;
}]);

Content of development.rb:

# Settings specified here will take precedence over those in config/application.rb.

  # In the development environment your application's code is reloaded on
  # every request. This slows down response time but is perfect for development
  # since you don't have to restart the web server when you make code changes.
  config.cache_classes = false

  # Do not eager load code on boot.
  config.eager_load = false

  # Show full error reports and disable caching.
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  # Don't care if the mailer can't send.
  config.action_mailer.raise_delivery_errors = false

  # Print deprecation notices to the Rails logger.
  config.active_support.deprecation = :log

  # Raise an error on page load if there are pending migrations
  # config.active_record.migration_error = :page_load

  # Debug mode disables concatenation and preprocessing of assets.
  # This option may cause significant delays in view rendering with a large
  # number of complex assets.
  config.assets.debug = true
  config.assets.digest = true

  config.assets.configure do |env|
    env.cache = ActiveSupport::Cache.lookup_store(:null_store)
  end

I am using rails 4.1.1. Any suggestions? I can provide more information as needed.


Update after trying the answer: I made the following changes following the answer bellow:

application.js => application.js.erb

<%= depend_on 'en.yml' %>
<%= depend_on 'fr.yml' %>
<% I18n.backend.send(:init_translations) unless I18n.backend.initialized? %>
console.log(<%= I18n.backend.send(:translations).to_json %>);
//= require angular-rails-templates
//= require honeybadger.min
//= require_tree ./dependencies
//= require lodash.min
//= require polyglot.min
//= require lunr.min
//= require angulartics.min
//= require angulartics-ga.min
//= require ./angular_js/fancyinput_library/ac-fancy-input
//= require angular-main
//= require_tree ./angular_js
//= require_tree ../templates

added the following to application.rb

config.assets.paths << Rails.root.join('config', 'locales')

New development.rb

# Settings specified here will take precedence over those in config/application.rb.

  # In the development environment your application's code is reloaded on
  # every request. This slows down response time but is perfect for development
  # since you don't have to restart the web server when you make code changes.
  config.cache_classes = false

  # Do not eager load code on boot.
  config.eager_load = false

  # Show full error reports and disable caching.
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  # Don't care if the mailer can't send.
  config.action_mailer.raise_delivery_errors = false

  # Print deprecation notices to the Rails logger.
  config.active_support.deprecation = :log

  # Raise an error on page load if there are pending migrations
  # config.active_record.migration_error = :page_load

  # Debug mode disables concatenation and preprocessing of assets.
  # This option may cause significant delays in view rendering with a large
  # number of complex assets.

  config.assets.compile = true

  config.serve_static_assets = true
  config.assets.debug = false
  config.assets.digest = true

  # config.assets.configure do |env|
  #   env.cache = ActiveSupport::Cache.lookup_store(:null_store)
  # end
  # config.middleware.use Rack::Prerender
  config.root_url = 'lvh.me:5000'
  config.api_host = 'http://lvh.me:3000'

Result: js error, app not loading.

JS error:

(anonymous function) MINERR_ASSET:22
(anonymous function) angular.js:3650
q angular.js:303
e angular.js:3616
$b angular.js:3556
Zb.c angular.js:1299
Zb angular.js:1314
Tc angular.js:1263
(anonymous function) angular.js:20555
a angular.js:2342
(anonymous function) angular.js:2613
q angular.js:310
Zc.c angular.js:2612

js file in the browser:

console.log( ... );
//= require angular-rails-templates
//= require honeybadger.min
//= require_tree ./dependencies
//= require lodash.min
//= require polyglot.min
//= require lunr.min
//= require angulartics.min
//= require angulartics-ga.min
//= require ./angular_js/fancyinput_library/ac-fancy-input
//= require angular-main
//= require_tree ./angular_js
//= require_tree ../templates

;

Update 2: I almost got it. I noticed I was not including the depend_on methods in the right file. I moved them to the relevant .js.erb and things started to work. My only remaining issue is that I have to refresh the page twice. I think it's better if application.js is left untouched anyway. :)

Alex C
  • 1,334
  • 2
  • 18
  • 41

1 Answers1

2

There are a few issues here.

First:

I believe you have a misunderstanding of how rails treats assets in production by default.

In production, assets will be precompiled once, and that's it. It's typically unwise to fall back to the asset pipeline in production, which is why you see this in /config/environments/production.rb:

# Do not fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = false

So, your Sprockets directive is not going to work - by default convention - in production; and, for the good reason of not letting your production server fall back to the assets pipeline.

Second:

You don't need to write your own DirectiveProcessor to do what you're trying to achieve. All you need to do is add this to the top of your dependent file (ie: application.js):

//= depend_on en.yml

and this to your /config/application.rb:

config.assets.paths << Rails.root.join("config", "locales")

And Sprocket will check that file for changes.

Also, if you do decide to do this by writing your own method, please inject them into Environment#context_class as described here: https://github.com/sstephenson/sprockets/blob/0d47a4fb459b170f2b2db7386ce7cbf9af1aa590/lib/sprockets/context.rb#L9-L22

Third:

You really should not be using i18n inside of a js.erb file! Even if you don't care about the code smell, consider that you shouldn't enable config.assets.compile in production, as you'll take a big performance hit.

A better solution

May be found in this answer: Rails: Internationalization of Javascript Strings?

However:

For completeness, here is an example that will work in development after you've added the above changes:

/config/environments/development.rb

PartnerUrlApp::Application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # In the development environment your application's code is reloaded on
  # every request. This slows down response time but is perfect for development
  # since you don't have to restart the web server when you make code changes.
  config.cache_classes = false

  # Do not eager load code on boot.
  config.eager_load = false

  # Show full error reports and disable caching.
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  # Don't care if the mailer can't send.
  config.action_mailer.raise_delivery_errors = false

  # Print deprecation notices to the Rails logger.
  config.active_support.deprecation = :log

  # Raise an error on page load if there are pending migrations
  config.active_record.migration_error = :page_load

  config.assets.compile = true

  config.serve_static_assets = true

  config.assets.digest = true
  # Email
  config.action_mailer.default_url_options = { :host => 'localhost:3000' }

  # Debug mode disables concatenation and preprocessing of assets.
  # This option may cause significant delays in view rendering with a large
  # number of complex assets.
  config.assets.debug = false
end

/app/assets/application.js.erb

<%= depend_on "en.yml" %>
<% I18n.backend.send(:init_translations) unless I18n.backend.initialized? %>
console.log(<%= I18n.backend.send(:translations).to_json %>);

/config/locales/en.yml

en:
  hello: "Hello world"

Now, loading up our app we see this in console:

...
Started GET "/assets/application-240790de490ac241985d22ea6d94de38.js" for 127.0.0.1 at 2014-07-23 18:45:04 -0500
...

Then, change the yaml file, and save:

/config/locales/en.yml

en:
  hello: "DONT DO THIS!"

And, without restarting the server, we can see that the javascript has been recompiled due to our dependency:

...
Started GET "/assets/application-ddd20afc0453d275b63d846fee2a5fed.js" for 127.0.0.1 at 2014-07-23 18:45:24 -0500
...
Community
  • 1
  • 1
Momer
  • 3,158
  • 22
  • 23
  • Thanks @Momer, I will look at your answer in more details tomorrow and try it out on our application. – Alex C Jul 24 '14 at 01:11
  • adding those lines in application.js.erb destroyed my application. I get a blank page with the following error Uncaught object MINERR_ASSET:22 – Alex C Jul 24 '14 at 14:49
  • "Destroyed my application" isn't a very good explanation of the issue. I used the exact config above to produce the results I pasted. What, exactly, did you change; and, what, exactly, was the stack trace? – Momer Jul 24 '14 at 14:54
  • The problem is that I cant require the other js assets anymore. I am getting // require angular-rails-template ... in the .js file followed by a ; at the end for the file. – Alex C Jul 24 '14 at 14:57
  • Again, "What, exactly, did you change; and, what, exactly, was the stack trace?" – Momer Jul 24 '14 at 14:57
  • I updated my question with my implementation attempt @momer :). – Alex C Jul 24 '14 at 15:08