How to access a yaml in rails config path - ruby-on-rails

I'm writting a ruby gem and I want to let user use your own yaml file configuration, but I don't know how to test if the user_config.yml exists into rails config path.
I want to use user_config.yml only if this file exists into config path.

Rails.root (or RAILS_ROOT in earlier versions of Rails) gives you the path of the application root. From there, you can see if the file you're interested in exists.
e.g.
path = File.join(Rails.root, "config", "user_config.yml")
if File.exists?(path)
# do something
end

you can access your Rails App's load path with the $LOAD_PATH variable which is of type Array.
Thus
if $LOAD_PATH.include?('/path/where/user/should/put/their/user_config.yml')
# EDIT
config_options = File.new('rails_app/config/user_config.yml', 'r')
else
# EDIT
config_options = File.new('path/to/default/config.yml', 'r')
end
There is also another useful method for the Ruby File class called #exists? which will of course check to see if a file exists. See File class docs for more.
Hope this gets you started. Your question was rather vague, reply with more details if you need more help.

Related

I moved my folder from lib to /app in a rails 6, I can't seem to instantiate an object still because of load path issue

I have a new rails 6 application and in the lib folder I had this:
/lib/some_app_name/stripe/subscription/subscription_service.rb
module Someappname # Someappname is also in my application.rb
module Stripe
class SubscriptionService
def initialize(a)
#a = a
end
end
end
end
I then moved the 'some_app_name' folder to:
/app/some_app_name/stripe/subscription_service.rb
I read that anything inside of /app will be autoloaded and reloaded so I moved in here. It wasn't working in /lib also.
In my home_controller.rb I tried this:
ss = Someappname::Stripe::SubscriptionService.new("a")
I get an error saying:
uninitialized constant Someappname::Stripe::SubscriptionService
What am I doing wrong here?
I suspect it's spring, try this
bin/spring stop
And then start rails console, stopping Spring will force Rails to load your app fresh
Also,
if the name of your module is Someappname, then the directory name should be app/someappname and not some_app_name
Hope that helps!
Rails does auto-load everything under /app, but there's a caveat. First level of directories is not becoming a part of module name. That's why you can have class User in /app/models/user.rb (and not class Models::User).
Solution: place your code into some folder under /app. I usually call it /app/lib or /app/custom or something like that.
/app/custom/some_app_name/stripe/subscription/subscription_service.rb
(and yes, make sure that names in your filepath correctly represent names in your module path. You can't have directory some_app_name for module Someappname, but you can for SomeAppName)

How do you tell Rails / the asset pipeline that a js.erb depends on a YAML file?

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

How to create a folder (if not present) with Logger.new?

I'm trying to register a new log
##my_logger ||= Logger.new("#{Rails.root}/log/my.log")
but when I try to generate new folders , to put it inside
##my_logger ||= Logger.new("#{Rails.root}/log/#{today.to_s}/my.log")
it returns Errno::ENOENT: No such file or directory
May it be a permission problem?
How to create a folder (if not present) with Logger.new?
Try something like this.
dir = File.dirname("#{Rails.root}/log/#{today}/my.log")
FileUtils.mkdir_p(dir) unless File.directory?(dir)
##my_logger ||= Logger.new("#{Rails.root}/log/#{today}/my.log")
You can also do this way
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
Automatic creation of logging directories has been deprecated in rails. Here's a code snippet from the Logger.new code:
ActiveSupport::Deprecation.warn("Automatic directory creation for '#{log}' is deprecated. Please make sure the directory for your log file exists before creating the logger. ")
Accepted practice now is to make sure the log file (and directory) exist before creating the logger.
A way to make sure the directory exists ahead of time might be to use code similar to this:
log_file_name = '/path/to/my.log'
unless File.exist?(File.dirname(log_file_name))
FileUtils.mkdir_p(File.dirname(log_file_name))
end

Warble Custom war file name

I am using Wabler gem to generate the war file for our Ruby on Rails application. By default, it generates a WAR file named the Rails project home directory name by default.
I would like to know if it is possible to specify custom war file name instead of the default project home directory name.
Thanks
Noman A.
Found answer to my question, so sharing with all who might need it.
File to modify:
warble.rb
For our application, this resides inside [project_dir]/config/
This file would have a section like:
Warbler::Config.new do |config|
.....
.....
end
Add the following line to specify custom war/jar file name (without extension):
config.jar_name = "testname"
So, the segment would look like:
Warbler::Config.new do |config|
.....
.....
config.jar_name = "testname"
end
Note: The "...." shown above are not part of the actual configuration, I have replaced original values for example purpose.
Now, when I run warble war/ rake war, it generates war file as "testname.war"
Hope this might help another newbie like me!
Thanks
Noman A.
Add config/warble.rb file.
Warbler::Config.new do |config|
config.war_name = "test_war_name"
end
Edit: config.war_name is deprecated, see logged here
# Deprecated
def war_name
$stderr.puts "config.war_name deprecated; replace with config.jar_name" #:nocov:
jar_name #:nocov:
end
Use the following:
Warbler::Config.new do |config|
config.jar_name = "test_war_name"
end

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