Rails loading initializer classes - ruby-on-rails

In my Rails app, I created an initalizer which responsability is to:
Require the service layer of my app
Dir[File.expand_path('services/*.rb',\__FILE__)].each { |file| require file }
Create those services and inject dependencies in it
But now, when I update a file in my project in development my rails server starts complaning that classes are not available anymore.
To make it work I have to delete my tmp directory and run rails server again to have it work again.
How can I work around that ?

Which tmp/ directory ? what is there in it ?
The reboot is not a surprise since you're using initializers. They are loaded once on boot and that's it.
Maybe https://github.com/guard/guard could help you reload the env automaticaly.
Maybe it's not initializers you're looking for ...
The answer depends on what you're trying to achieve. This is more an architectural problem than a code/dev one tough.

I think a better solution to creating an initializer to load those files is to use the following syntax in your application.rb file:
module AppName
class Application < Rails::Application
#other config stuff here
config.eager_load_paths << "path to your services directory"
end
end
This will make rails reload the files for every request in development mode, as well as requiring them normally in production mode.

Related

LoadError for custom Ruby module

I am new to Ruby. I am dealing with a codebase of the following nature.
I have a main/ directory containing my entire codebase, and inside it I have files like:
main/lib/foo/test1.rb and
main/app/bar/test2.rb
Inside main/app/bar/test2.rb there is the line: require 'test1'
However, if I am in the main/ directory and I run ruby main/app/bar/test2.rb I get the following error: require': cannot load such file -- access_control (LoadError)
Now, upon Googling, I think this has something to do with /config/application.rb and adding the line: config.autoload_paths += Dir["#{config.root}/lib", "#{config.root}/lib/**/". I do this, but it doesn't seem to make any difference.
Presumably my production environment knows where to look in the require 'test1.rb' statement, while my environment does not. How can I fix this?
Thanks!
It sounds like you have a Ruby on Rails application.
Given that, you probably don't want to run any contained script directly with ruby. If it's designed to be executed directly, try running it with rails runner path/to/file.rb (from inside the directory that contains app/, lib/, config/, etc).
If it's inside app/ or lib/, though, it's more likely intended to be loaded as a library from some other script elsewhere in the application... those would both be unusual places to keep a stand-alone executable.

Load lib files in production

I've upgraded one of my apps from Rails 4.2.6 to Rails 5.0.0. The Upgrade Guide says, that the Autoload feature is now disabled in production by default.
Now I always get an error on my production server since I load all lib files with autoload in the application.rb file.
module MyApp
class Application < Rails::Application
config.autoload_paths += %W( lib/ )
end
end
For now, I've set the config.enable_dependency_loading to true but I wonder if there is a better solution to this. There must be a reason that Autoloading is disabled in production by default.
My list of changes after moving to Rails 5:
Place lib dir into app because all code inside app is autoloaded in dev and eager loaded in prod and most importantly is autoreloaded in development so you don't have to restart server each time you make changes.
Remove any require statements pointing to your own classes inside lib because they all are autoloaded anyway if their file/dir naming are correct, and if you leave require statements it can break autoreloading. More info here
Set config.eager_load = true in all environments to see code loading problems eagerly in dev.
Use Rails.application.eager_load! before playing with threads to avoid "circular dependency" errors.
If you have any ruby/rails extensions then leave that code inside old lib directory and load them manually from initializer. This will ensure that extensions are loaded before your further logic that can depend on it:
# config/initializers/extensions.rb
Dir["#{Rails.root}/lib/ruby_ext/*.rb"].each { |file| require file }
Dir["#{Rails.root}/lib/rails_ext/*.rb"].each { |file| require file }
I just used config.eager_load_paths instead of config.autoload_paths like mention akostadinov on github comment:
https://github.com/rails/rails/issues/13142#issuecomment-275492070
# config/application.rb
...
# config.autoload_paths << Rails.root.join('lib')
config.eager_load_paths << Rails.root.join('lib')
It works on development and production environment.
Thanks Johan for suggestion to replace #{Rails.root}/lib with Rails.root.join('lib')!
Autoloading is disabled in the production environment because of thread safety. Thank you to #Зелёный for the link.
I solved this problem by storing the lib files in a lib folder in my app directory as recommended on Github. Every folder in the app folder gets loaded by Rails automatically.
There must be a reason that Autoloading is disabled in production by
default.
Here is a long discussion about this issue. https://github.com/rails/rails/issues/13142
This allows to have lib autoreload, and works in production environment too.
P.S. I have changed my answer, now it adds to both eager- an autoload paths, regardless of environment, to allow work in custom environments too (like stage)
# config/initializers/load_lib.rb
...
config.eager_load_paths << Rails.root.join('lib')
config.autoload_paths << Rails.root.join('lib')
...
Just change config.autoload_paths to config.eager_load_paths in config/application.rb file. Because in rails 5 autoloading is disabled for production environment by default. For more details please follow the link.
#config.autoload_paths << "#{Rails.root}/lib"
config.eager_load_paths << Rails.root.join('lib')
It works for both environment development and production.
In some sense, here is a unified approach in Rails 5 to centralize eager and autoload configuration, in the same time it adds required autoload path whenever eager load is configured otherwise it won't be able to work correctly:
# config/application.rb
...
config.paths.add Rails.root.join('lib').to_s, eager_load: true
# as an example of autoload only config
config.paths.add Rails.root.join('domainpack').to_s, autoload: true
...
For anyone struggled with this like me, it's not enough to just place a directory under app/. Yes, you'll get autoloading but not necessary reloading, which requires namespacing conventions to be fulfilled.
Also, using initializer for loading old root-level lib will prevent reloading feature during development.
The only thing that worked for me is adding the nested lib path in eager load paths AND adding a require_dependency in a config.to_prepare block.
# application.rb
...
config.to_prepare do
require_dependency("#{Rails.root}/lib/spree/core/product_filters.rb")
end
config.eager_load_paths << Rails.root.join('lib').join('spree').join('core')
...
Moving the lib folder to app helped solve a problem, my Twitter api would not run in production. I had "uninitialized constant TwitterApi" and my Twitter API was in my lib folder.
I had config.autoload_paths += Dir["#{Rails.root}/app/lib"] in my application.rb but it didn't work before moving the folder.
This did the trick
I agree that some dependencies belong in lib and some may belong in app/lib.
I prefer to load all files I've chosen to put in lib for all environments, hence I do this in config/application.rb immediately after requiring the bundle but before opening the MyApplicationName module.
# load all ruby files in lib
Dir[File.expand_path('../../lib/**/*.rb', __FILE__)].each { |file| require file }
This doesn't depend on Rails.root (which isn't defined yet), and doesn't depend on eager loading (which may be off for an environment).
to summarize Lev's answer: mv lib app was enough to have all my lib code autoloaded / auto-reloaded.
(rails 6.0.0beta3 but should work fine on rails 5.x too)

How to handle uploads and logs in Rails when deployed as a .war file?

I am trying to deploy rails on jRuby using a .war file with the help of Warbler (Tomcat) and/or Torquebox 4 (Wildfly). The problem I face is that I don't know how to handle uploads with Carrierwave or Paperclip in this case.
Ideally uploads should be stored outside the war, as it may be replaced with a newer version of the app anytime.
I tried to create a symlink (uploads) in the public directory before I packaged the app as a war file to /home/username/uploads (permissions are set to 777) directory but that doesn't work (I get a 500 error).
Also how can I access the production.log after I deployed the war file? Or where should I place the logs?
UPDATE
I figured out how to config Carrierwave to store uploads outside the war file:
if Rails.env.development?
CarrierWave.configure do |config|
config.root = "/Users/Username/username_uploads/uploads"
end
elsif Rails.env.production?
CarrierWave.configure do |config|
config.root = "/home/username/username_uploads/uploads"
end
end
Now Carrierwave uploads the files without a problem, but I get a 404 error when I try to view them.
I tried to include a symlink inside the war file to the uploads folder but no success. I tried to create it before running warble war, and also after the app was deployed to Tomcat ( inside the app_name folder ).
Any idea how to solve this?
UPDATE 2
I found a working solution here:
Configure Symlinks for single directory in Tomcat
In short:
cd into the exploded war directory ( you can find this under tomcat/webapps ) that tomcat created ( if the name of the uploaded war file is yourapp.war then the directory name will be yourapp in Tomcat 8 ).
Create an uploads folder with sudo mkdir uploads
Create a mount point: sudo mount --bind /path/to/actual/upload/directory/uploads uploads
I haven't yet tested this with Wildfly, but I will later today or tomorrow. If I remember correctly it won't automatically explode war files by default.
I would still like to know additional, simpler, different solutions for the problem though, and also opinions about the solution I found.
Just food for thought on a different approach...
Using a "cloud based" storage service would make the upload and serving of the assets problem go away, it would also make it simpler to scale the app should you ever need a second node,
it would also reduce the need to scale the app because you would effectively delegate the large data operations which ruby traditionally handles badly to a different service
Amazon S3 is an obvious choice, but check out Riak too
I think the problem you are experiencing with this solution:
if Rails.env.development?
CarrierWave.configure do |config|
config.root = "/Users/Username/username_uploads/uploads"
end
elsif Rails.env.production?
CarrierWave.configure do |config|
config.root = "/home/username/username_uploads/uploads"
end
end
is that you are storing the images outside of the rails public folder, and therefore rails cannot serve them anymore. Carrierwave is uploading everything properly as you would expect it to do, and it creates links, relative to the storage dir.
If you would use the default #{Rails.root}/public storage, rails would serve your images, as any content from public is served (also static html or other assets). As Rails doesn't serve these anymore it's up to you to serve them.
Maybe you can just directly serve them through Tomcat (as I have no expertise in Tomcat configuration you have to figure this out yourself). This would maybe even be faster, as the request surpass the rails stack.
By default, Tomcat does not follow symbolic links (due security reasons). To enable this, you have to add the following inside the Host tag in your server.xml:
<!-- Tomcat 7: -->
<Context allowLinking="true" />
<!-- Tomcat 8: -->
<Context>
<Resources allowLinking="true" />
</Context>
See the docs and the latest migration guide.
If you could provide your logs it would be much easier to diagnose the problem (I take it this problem is only in production because you are asking about how to access that log?). Goto the rails app directory and look in log/production.log these log levels are often lower so you may have to configure them to be more informative config/enviroments/prouction.rb should have a config.log.level this is probably set to info set it to debug for more verbose logging.

Why does require load differently in production (Heroku) and development environments for Rails?

I am trying to require a file located in lib (in my Rails app).
In development, this line works without any problems.
require "km"
However, in Heroku, I get a "no such file to load" error.
Via the Heroku console, I've tried a number of variants in terms of specifying paths to the file. For example:
require "lib/km"
require "./lib/km.rb"
And so forth. This is based on answers from these questions, which suggest that the issue is specifying the path to the file.
Rails, Heroku does not load my lib
Heroku console not loading file in /lib?
I've also tried adding this autoload line suggested here, which doesn't seem to solve it.
config.autoload_paths += Dir["#{config.root}/lib/**/"]
http://www.williambharding.com/blog/technology/rails-3-autoload-modules-and-classes-in-production/
How do I tell Rails to find the lib file? And why is there a difference in behaviour for require in development vs production?
UPDATE:
The issue turns out to be related to git. The original filename was "KM.rb", which was later changed to "km.rb". However, git didn't pick up the case change, so the change never got pushed onto the Heroku environment.
A key step was using the Ruby Dir commands in Heroku console to investigate the file structure and confirm the filename within the environment on Heroku.
I was getting the "no such file to load" error and found that I had the wrong case on a require statement
require_relative './place/Places.rb'
when the actual filename was places.rb
Worked fine on Windows since it's case insensitive

Redmine Plugin - requires restart after every code change

I am working on some plugin on redmine (project management web application written using Ruby on Rail).
For every change i make to the code of the plugin(say view.html.erb file), i have to restart the redmine(application) server. This is because, it runs on production mode by default.
Will running the application on development mode, solve this problem?
If yes, how can i change its running mode or over-ride this behavior so that classes are loaded per every request (yes this will not be efficient but will be good for development) and changes to the code reflect without restarting the application application server(redmine in this case)
I tried adding this line to environment.rb file
ENV['RAILS_ENV'] ||= 'development'
Also tried answers/comments posted below, but they did'nt solve my problem.
Any working solution would be of great help.
Thank You.
Other Related information:
It uses Rails 2.3.14
and its installed using bitnami stack
For automatic plugin reload on Rails 2.3:
Add config.reload_plugins = true on config/environment.rb. It has to be there, you can't put it on config/environments/development.rb due to the Rails start up steps. You may add if RAILS_ENV = 'development' instead.
config/environment.rb
config.reload_plugins = true
On the plugin's init.rb, add the following line:
init.rb
ActiveSupport::Dependencies.explicitly_unloadable_constants = 'YourPluginModuleName'
That's all. Don't forget to remove it when you're done.

Resources