Automatically reload rails yml files in config/locales - ruby-on-rails

In rails, the yml files in config/locales allow you to give locale-specific text and formatting directives. For example, you can specify date formatting like this:
# config/locales/en.yml
date:
formats:
month: "%B, %Y"
Then in your views you can use the helper, like this:
<%= l(Date.today, format: :month) %> => "December, 2013"
Annoyingly, rails only loads the locale files when you start your server, so you have to restart your development server if you want to make a change. Is it possible to automatically reload this on file changes?

I think Rails misses new translation files, but adding translations to an existing file should work.
Try force reload it with I18n.backend.reload!
I hope this helps ;)

There's attempted support for this in rails 3.2:
https://github.com/rails/rails/blob/v3.2.16/activesupport/lib/active_support/i18n_railtie.rb
However, it comes with this disclaimer:
# Add <tt>I18n::Railtie.reloader</tt> to ActionDispatch callbacks. Since, at this
# point, no path was added to the reloader, I18n.reload! is not triggered
# on to_prepare callbacks. This will only happen on the config.after_initialize
# callback below.
There's some better looking code in rails 4, so this problem might be fixed there (I don't use rails 4 yet).
I added the following initializer, which checks for changed files is config/locales and reloads I18n:
# config/initializers/reload_locale.rb
if Rails.env == 'development'
locale_reloader = ActiveSupport::FileUpdateChecker.new(Dir["config/locales/*yml"]) do
I18n.backend.reload!
end
ActionDispatch::Callbacks.to_prepare do
locale_reloader.execute_if_updated
end
end

I18n detects changes made to existing files in its load path, but if you want to detect new files under locales and add them to the load path at runtime, try this.
# config/initializers/i18n_load_path.rb
if Rails.env.development? || Rails.env.test?
locales_path = Rails.root.join("config/locales").to_s
i18n_reloader = ActiveSupport::FileUpdateChecker.new([], locales_path => "yml") do
Dir["#{locales_path}/*.yml"].each do |locale_path|
I18n.load_path << locale_path unless I18n.load_path.include? path
end
end
ActiveSupport::Reloader.to_prepare do
i18n_reloader.execute_if_updated
end
end
That will monitor the locales directory (or any other directory you want to store locales) and add missing ones to the load path when they are added. I18n picks up on these added files so no need to call reload!.

Related

Access APP_CONFIG['var'] inside routes? or supply routes with variables?

I need to do in rails 4 supply some ip address to set a constraint on certain routes.
Is there a way to get this data from a config file without harcoding it into the routes file?
Im using a yaml file and initializer for app variables like:
APP_CONFIG = YAML.load_file("#{Rails.root}/config/application.yml")[Rails.env]
so normally I could do:
constraints(:ip => %w[APP_CONFIG['app_url']]) do
.. my routes..
end
This fails in the routes.rb is there a way to fix this?
The routes.rb file is a ruby file which is instantiated once, and loaded into memory.
You can just add the ruby code inside it and it will be executed once:
Rails.application.routes.draw do
app_config = YAML.load_file("#{Rails.root}/config/application.yml")[Rails.env]
constraints(:ip => %w[app_config['app_url']]) do
.. my routes..
end
end
This will instantiate the routes.rb file with the variable loaded from the yml and available throughout your rails routes app. You don't even need to use a env variable. Local variable seems a better idea.
You can also put logic inside and make it environment dependant:
if Rails.env.production?
app_config = YAML.load_file("#{Rails.root}/config/application.yml")[Rails.env]
constraints(:ip => %w[app_config['app_url']]) do
.. my routes..
end
else
.. my routes ...
end
Taking a look at the initialization process of rails (http://guides.rubyonrails.org/initialization.html). You'll see that routing is actually loaded quite early (and earlier than application.rb or other initializers). It has therefore not yet loaded this file.
A way round this would be to place this into your boot.rb:
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
#Now load app config:
require 'yaml'
APP_CONFIG = YAML.load_file(File.expand_path('../../config/application.yml', __FILE__))
I believe you are running into a load order issue. You could probably hack around this, but...
I would highly recommend using Figaro to solve this problem. It is a gem specifically designed for rails configuration and will work nicely with 12 factor app deployments (like Heroku): https://github.com/laserlemon/figaro
I am using Figaro in the app I am currently working on and was able to confirm access to the env variables within my routes file. I believe this gem will solve your current issue and other config issues you don't even know you have yet!

Auto include in Rails Console

I find myself having to type (for example)
include PathHelper
every time I load the Rails Console.
Is there a way to configure the Rails console to automatically include certain modules?
The syntax for configuring rails console has changed. I found this on RailsGuides:
http://guides.rubyonrails.org/configuring.html#rails-general-configuration
console do
# this block is called only when running console,
# so we can safely require pry here
require "pry"
config.console = Pry
end
Just in case anyone still feel confused, the simplest way to do this is:
go to the root directory of your project
create an .irbrc file(if you use rails console) or .pryrc file(if you use pry)
put whatever you need to include in it
For example, if you use the default rails console and need to include PathHelper, just put it in the file:
# RootDirectoryOfYourProject/.irbrc
include PathHelper
The PathHelper will be included automatically when you do rails console
If you are still looking for an answer, this is what I do,
I created a file ~/.irbrc in which you put all the code you want to be auto loaded in your rails console.
This is the content of my file:
require "awesome_print"
include Rails.application.routes.url_helpers
AwesomePrint.irb!
def y(obj)
puts obj.to_yaml
end
I would check out this question.
Basically, modify your config/application.rb file to include the paths to any modules you want to auto-load.

Always preprocess a specific Javascript file with Rail 3.1 asset pipeline

Is there a way to always run the ERB preprocessor on a Javascript file?
I'm using Mustache to use the same templates on the client and server. I'd like to include these templates in my application.js files so they're available on the client. So I'm preprocessing my Javascript file (templates.js.erb, which then gets required in application.js) with erb:
App.templates.productShow = <%= MustacheController.read("product/show").to_json %>;
This works great but when I edit the "product/show.html.mustache" template I need to also edit "templates.js.erb" so Rails knows to recompile this file which then picks up the latest changes from the mustache template.
There's no issue running this in production since the assets get compiled when I deploy, but it's annoying when developing. Ideally I could set the preprocessor to run on "templates.js.erb" every time I reload. My current solution is to inline the Javascript in the application layout but it would be nice to keep it separate.
I ended up writing a guardfile for this that adds a timestamp to the end of the file. Just touching the file is enough for sprockets to recompile but if you're using git you need to actually alter the file. Otherwise anyone else who pulls in the code won't get the latest preprocessed files. Here's the code...
#!/usr/bin/ruby
guard 'mustache' do
watch(%r{app/templates/.+\.mustache})
end
require 'guard/guard'
module ::Guard
class Mustache < ::Guard::Guard
def run_on_change(paths)
# file to be updated when any mustache template is updated
file_name = "./app/assets/javascripts/templates.js.erb"
# get array of lines in file
lines = File.readlines(file_name)
# if last line is a comment delete
lines.delete_at(-1) if lines[-1].match /\/\//
# add timestamp
lines << "// #{Time.now}"
# rewrite file
File.open(file_name, "w") do |f|
lines.each{|line| f.puts(line)}
end
end
end
end
This seems to be a common complaint with the pipeline - that you have to touch a file that references variables for those changes to be reflected in development mode. A few people have asked similar questions, and I do not think there is a way around it.
Forcing Sprockets to recompile for every single request is not really a viable solution because it takes so long to do the compilation.
Maybe you could set up guard to watch your mustache directory and recompile templates.js.erb when you make changes to it. Similar to how guard-bundler watches your Gemfile and rebundles on change.

Rails 3 engine and code reloading in development mode

I have a rails 3 engine. In initializer it requires a bunch of files from some folder.
In this file user of my engine defines code, business logic, configures engine, etc..
All this data is stored statically in my engine main module (in application attribute)
module MyEngine
class << self
def application
#application ||= MyEngine::Application.new
end
end
end
I want this files to be reloaded on each request in development mode.
(So that the user don't have to reload server to see changes he just made)
Of course I can do something like this instead of initializer
config.to_prepare do
MyEngine.application.clear!
load('some/file')
end
But this way i will have issues (because constants defined in this file won't really be reloaded).
The ideal solution would be to make my whole engine reloadable on each request, but a have not found the way to do it.
It's an old question but I think adding ActiveSupport::Dependencies.explicitly_unloadable_constants += %w[ GemName ] to your development.rb should do the trick.
Have you tried turning reload_plugins on?
# environments/development.rb
config.reload_plugins = true
Its a bit of hack but using require_dependency and just reopening the class might work?
# app/models/project.rb
require_dependency File.join(MyEngine::Engine.root, 'app', 'models', 'project')
class Project
end
For those who are working on Engine views or I18n translations only: Those parts are autoreloaded by default, no need to restart the server!

How to gemify a Rails (engine) plugin?

I have a engine style Rails plugin from which I can create a gem using Jeweler. But when I require it in my Rails environment (or erb) the models within the plugin are not loaded. I have followed a number of tutorials and read just about everything on the subject.
# environment.rb
config.gem 'myengine'
# in irb
require 'myengine'
I have unpacked the gem and verified that all files are present. My init.rb has been moved to a new folder called 'rails' as per. All files in 'lib' are automatically added to the $LOAD_PATH, so require 'myengine' runs lib/myengine.rb. I verified this by putting a puts 'hello' within.
Is it because of the physical presence of plugins in a known place that Rails can add all the models, controller etc. to the relevant load_paths? Do I need to replicate this manually when using a gem?
Would gemspec require_paths be a way of adding additional paths other than lib? I assume however that Rails does not just require every single file, but loads them on demand hence the need for the filename and class name to match?
%w{ models controllers helpers }.each do |dir|
path = File.join(File.dirname(__FILE__), 'app', dir) + '/'
$LOAD_PATH << path
puts 'requiring'
Dir.new(path).entries.each do |file|
if file =~ /\.rb/
puts file
require file
end
end
end
By adding the above to lib/myengine.rb all the models/controllers are required. But like I said in my question this is unlikely to be a good way forward.
Offhand I'd say the part about adding those directories to the search path is right on. What you shouldn't need to do is require each file manually (as you allude to in your last sentence). What Rails does when you reference a non-existent constant is to search for a file with the same name (underscored of course) in the load path.
If for some reason you can not abide by the constraint (think about it long and hard) then you are going to need to dig deeper into Rails and see how the reloading mechanism works so you can tie into it properly in development mode.
The problem was the files (in app) where not being added to the gem because when using Jeweler it only automatically adds files to required_paths which are committed to git.

Resources