I want to be able to edit a YAML file and reload it in a Rails 4 app. Right now I am loading the YAML file via an initializer, and I know that this will load the file only once and a restart will be required after changing it.
How could I accomplish a YAML reloading/refreshing as it is accomplished via i18n YAML files in Rails?
You can try something in the lines of checking for file's change time, for example:
module MyFileReader
def self.my_yaml_contents
if #my_yaml_file_ctime != File.ctime(file_name)
#my_yaml_contents = YAML.load(File.open(file_name))
#my_yaml_file_ctime = File.ctime(file_name)
end
#my_yaml_contents
end
end
MyFileReader.my_yaml_contents method will load and parse the file only on startup and change and serve the already parsed data in the meantime,
see http://www.ruby-doc.org/core-2.0.0/File.html#method-c-ctime
When you load the file, I assume you assign it to some variable or constant. If instead you don't assign it, then the load will be performed every time.
Instead of:
CONTENT = Yaml.load_file('your_file.yml')
create a simple class or function:
module YourFileReader
def self.load
Yaml.load_file('your_file.yml')
end
end
and use the defined method to read the file in your app
YourFileReader.load
or even simpler, use
Yaml.load_file('your_file.yml')
directly in your app where you need to read the file.
Instead of require use load to load the file.
require will load files only once. But load will load when it is called.
See more about this here http://ionrails.com/2009/09/19/ruby_require-vs-load-vs-include-vs-extend/
Related
This is probably a simple "Navigating directory" problem since I'm new but..
Inside of a file named tasks_lists.rb I am attempting to save the text inside of a text file to a variable named draft.
Here is that code:
class FivePoints::Tasks
attr_accessor :draft, :efile, :serve
def self.start
binding.pry
draft = File.open("./text_files/draft.txt")
efile = File.open("./text_files/efile.txt")
serve = File.open("./text_files/serve.txt")
self.new(draft, efile, serve)
end
def initialize(draft , efile, serve)
#draft = draft
#efile = efile
#serve = serve
end
end
I have tried to pry around, but no luck. I have also tried variations such as ("../text_files/efile.txt"), ("/text_files/efile.txt"). I will attach an image of the directory tree as well.
I am simply trying to eventually puts out some text in the CLI for a sample program. That text will be in the draft.txt file. Any ideas?
Assuming you only want to run this locally, then find out the path of the file (you can view its properties from file explorer), and use that explicitly in your code.
Find the exact path (If you're on UNIX it'll be something like /home/USER/projects/five_points/bla....) then replace your string with the exact full path and you'll be able to open the files as needed
It looks like your files are in a directory called five_points but your code is under lib so one level higher ?
Also, you need to be sure you open the file for appending.
draft = File.open("./five_points/text_files/draft.txt", "a")
I have a js.erb file that loads YAML from a config file. The problem is that Rails / the asset pipeline will cache the results and never invalidate that cache, even when I change the YAML file contents. I can restart the rails server and even reboot the machine to no avail. The only workaround I've found so far is doing a "rake assets:clean".
I would like to find a way to tell the asset pipeline that when the YAML file changes, it needs to re-compute my js.erb. Or, alternatively, tell it it can only cache the js.erb for the lifetime of the rails server / ensure somehow that re-generation occurs every time the rails server comes up or is restarted.
Any suggestions would be greatly appreciated.
Add this into a file under config/initializers and it will tell the asset pipeline to re-compute the js.erb file that loads the YAML data whenever one of the backing YAML files changes:
class ConstantsPreprocessor < Sprockets::Processor
CONSTANTS_ASSET = "support/constants"
def evaluate(context, locals)
if (context.logical_path == CONSTANTS_ASSET)
Constants.load_path.each do |dir|
dir.each do |yml|
next unless yml.end_with?".yml"
context.depend_on("#{dir.path}/#{yml}")
end
end
end
data
end
end
Rails.application.assets.register_preprocessor(
'application/javascript',
ConstantsPreprocessor)
If you're using Sprockets 3 (with Rails 5, for example), you can use // depends_on. For example, my-constants.js.erb:
//= depend_on my_constants.yml
angular
.module('services.myConstants', [])
.factory('myConstants', [
function() {
return <%= YAML::load_file(Rails.root.join('config/shared/my_constants.yml')).to_json %>;
}
]);
Just make sure the directory containing my_constants.yml is included in asset paths in application.rb:
config.assets.paths.unshift Rails.root.join('config', 'shared').to_s
I think you have 2 options:
Disable the asset pipeline and let Rails do the compilation on the go (bad for performance)
Create a daemon process, separated from Rails (look for Ruby Daemon) to look for any changes in that specific file and recompile the assets.
3 (extra!). Remove the js-YAML dependency and read the content of the YAML from a AJAX call to the app. The scenario is: the JS make a AJAX call, the controller read the YAML file and return the content of it to the JS file. So no need to recompile or watch for changes in the YAML file.
if you choose the 3, don't read the YAML in the controller, create a utility class to do it and let the controller ask that class to read the file and pass it's content.
You can add your own processor directive that works on files outside of the assets directory. = depend_on only works with asset files (https://github.com/rails/sprockets#depend_on)
In config/initializers/sprockets.rb:
Sprockets::DirectiveProcessor.class_eval do
def process_depend_on_project_file_directive(file)
path = Rails.root.join(file).to_s
if File.exists?(path)
deps = Set.new
deps << #environment.build_file_digest_uri(path)
#dependencies.merge(deps)
end
end
end
Usage:
//= depend_on_project_file "config/setting.yml"
See this comment on github for details: https://github.com/rails/sprockets/issues/500#issuecomment-491043517
I want users to be able to upload files and then I want to be able to parse them, taking out pieces of information and then declaring them as global variables to be used by other parts of my web application. I know you can easily put in a file upload form but then where would I store the script for parsing the file? Would it be under models, views, controllers, or somewhere else? Also how can I tell my application to immediately run this script upon the file upload. Would I put it in the view before the form's <% end %> tag? When it does parse the file, how can I make sure the variables (probably array's) are declared globally so that I can call those variables in all other parts of my application
With EventMachine you can watch a folder for file operations and then process them.
The Library rb-inotify does fit aswell.
# Create the notifier
notifier = INotify::Notifier.new
# Run this callback whenever the file path/to/foo.txt is read
notifier.watch("path/to/foo.txt", :access) do
puts "Foo.txt was accessed!"
end
# Watch for any file in the directory being deleted
# or moved out of the directory.
notifier.watch("path/to/directory", :delete, :moved_from) do |event|
# The #name field of the event object contains the name of the affected file
puts "#{event.name} is no longer in the directory!"
end
# Nothing happens until you run the notifier!
notifier.run
I store a value in a class variable inside of a module, such as:
module TranslationEnhancer
def self.install! klass
#dictionaries ||= [] << klass
end
...
end
I call this from an initializer in config/initializers:
require Rails.root + "lib" + "translation_enhancer.rb"
TranslationEnhancer::install! TranslationDictionary
Now, if I start the server in development environment, everything is ok during the first request. However, after that request, #dictionaries are suddenly nil. I have commented all other code in TranslationEnhancer, so I am absolutely sure the whole module must get reloaded every time I do a request.
I tried to move the module outside of the lib directory (moved it to lib_unloadable), then I tried:
ActiveSupport::Dependencies.explicitly_unloadable_constants << "TranslationEnhancer"
but failed again. I have no idea how to solve this, please help.
Got Ruby 1.9.2 # Rails 3.1.rc4.
EDIT: I know I could set the dictionaries as a constant. But I would like to use TranslationEnhancer as a library - so I could use it unchanged in a different project and install different Directories, such as:
TranslationEnhancer.install! EnglishDirectory, FrenchDirectory
These values won'd change during the runtime, they will just change project to project.
Solved!
I realized that the whole application.rb and environment.rb files are reloaded along with all other files. The only thing that does not get reloaded are initializers (config/initializers/*). The solution was to move the initialization to application.rb.
#dictionaries is not a "class variable". It is a "class-level instance variable".
Look here for a better explanation: Class and instance variables
Try using ##dictionaries instead.
In my Rails 3 application, I have a module in the lib/ folder. The module requires a constant variable, which is a big dictionary loaded from a data file. Since the dictionary won't change over the course of the application and its time consuming to reload this data file everytime a method from the module is called, I want to create a constant which holds the dictionary that can be accessed by the module in the lib.
lib/my_module:
module My_Module
def do_something(x)
y = CONSTANTVAR[x]
...
end
end
to initialize the constant, I have to load a file:
file = File.new('dataFile.dat','r') #I'm not sure where to put this data file
file.each_line { |line|
lineInfo = line.split
CONSTANTVAR[line[0]] = line[1] }
file.close
Where is the standard place to initialize variables that can be accessed by modules in the lib folder (this is the only place I will be accessing the variable)?
Also, the module loads a data file, is it standard to put data files in the lib/ folder as well?
Thanks!
You can take a look on how it is done here: https://github.com/sevenwire/forgery
It is a lib for generating words and it uses dictionaries. They are saved in : /lib/forgery/dictionary/*
So, save you dictionary on /lib/module-name/dictionaries/DATA.dat
And you can initialise your variable in config/initialisers/module-name.rb
I should put my, say, init_dictionary.rb in config/initializers. I think it's the better place for your requirement.