Rails namespace service objects in app/services folder - ruby-on-rails

I'm trying to put service objects into app/services folder but reference them via Services::SomeModel::SomeClass to better organize my code (the project is pretty large).
# app/services/tasks/create.rb
module Services
module Tasks
class Create
# ...
end
end
end
However, because of the way Rails loads the files, app/services is treated like a base folder and Rails keeps wanting the classes to be defined as ::Tasks::Create instead of ::Services::Tasks::Create.
I can solve this by placing my service objects in a subdirectory, like app/classes/services and then referencing them via ::Services::Tasks::Create, but I would prefer to have the /services folder in the /app directory itself.
I'm not a huge fan of the convention some use with services, naming them ::Tasks::CreateService. The whole reason I'm namespacing them the way I am is so anyone can look at the code and understand where the related files are located and to keep everything organized.
I've tried playing with auto_load in the config/application.rb file but that doesn't seem to work, since Rails is already pre-loading the files in app/* (that's my understanding).
Is there a way to reference service objects via ::Services::Model::Action and have the files located in app/services/model/action.rb?

Related

Rails 4.x and sub folders in app/controllers/concerns

I like to keep things organized in my applications and since I have only been coding in ROR for a year. I have known how to use the app/controllers/concerns directory for custom modules to be included in a controller.
This is great however I am wondering if there is a way to add sub folders to the concerns folder to allow for better organization.
For example app/controllers/concerns/members/profile_methods.rb and the module is named 'ProfileMethods'. If I include 'ProfileMethods' RubyMine see's the module and allows me to include it, including offering it as an option in the tooltip pane. However the Rails Server says that 'ProfileMethods' is an undefined constant if it is in a sub folder of app/controllers/concerns .
Is there something I have to add to the rails application configuration? Any input would be great as it seems a little illogical that you could not further organize the concerns directory with sub folders.
The best way to keep things organized is to name the module the following way. That way you have your code organized in both files and code itself.
module Members
module ProfileMethods
extend ActiveSupport::Concern
...
end
end
This way it will load your modules fine. Other one option is to tweak eager_load_paths in config/application.rb for instance.

Rails Engines: When to put code in app, when in lib, and when in vendor folder?

I'm developing a Rails engine, and so I've taken some looks on existing ones. I noticed that many of them have files in app, but also in lib and vendor.
It's clear to me that I should put any code that should be replaceable by the host app into the app folder (e.g. when having a model app/user.rb, the host app can easily have its own app/user.rb file and use this one instead of the engine one's).
But I'm unsure, when I have to put stuff in to lib, and when into vendor? I thought, that in vendor, I should only put "external" code from other developers or projects, that I want to use in my project, and in lib I put my own additional libraries that I'm actually working on in the project. But why, for example, does WiceGrid put stuff into its wice_grid/vendor/assets directory? It doesn't look to me like external code, but code that is developed only for WiceGrid and hence should be in the lib directory?
Update
While experimenting a bit, I noticed that all code in the lib folder is not reloaded while developing the engine (I guess the same is the case for the vendor directory), so I should put them into a folder within app, but where exactly?
For example, I have a file lib/iq_list_controller.rb that holds some class and instance methods for ApplicationController which I mix into it in engine.rb like this:
initializer "wice_grid_railtie.configure_rails_initialization" do |app|
ActiveSupport.on_load(:action_controller) do
extend IqList::Controller::ClassMethods
include IqList::Controller::InstanceMethods
end
end
Where should I put this file so Ruby properly finds it?
Regarding the development reloading issue, if the lib folder is a natural home for your files, then add it to Rails' load path with something like:
module MyEngine
class Engine < ::Rails::Engine
config.autoload_paths << File.expand_path("../../lib", __FILE__)
end
end
Regarding the specific case of vendor assets, it seems reasonable to place your engine's assets in app/assets, where they will be found by the host Rails app.
if you want things to be autoloaded then put them in /app. Otherwise I think that anything in /lib should be manually required. I generally believe that autoloading the lib folder is bad practice.
your basic code for MVC comes in app folder.now suppose you have a generic method/module which is frequently used,so to implement DRYness for your code...all generic and common utilities can be simply put in lib folder.
theres a beautiful explaination when to use lib and using lib folder in rails

How can I create globally-accessible modules in Rails?

In Rails, I can create models which are global (accessible anywhere in the application). However, I'd like to create some constructs that are global, but don't correspond to any database table (which is why they can't be models) and won't ever be instantiated (so I need modules instead of classes).
I've tried using the initializers directory, which works, but is annoying because it requires restarting my server every time I change a value (whereas I can change the code in my models without restarting the server). What's the best way to create globally-accessible modules in this manner?
Just put it into lib sub-directory and add that to your config/application.rb:
config.autoload_paths += ["#{config.root}/lib"]
In addition to adding modules to the lib directory (which is appropriate), you can also put classes in the models directory. They don't have to be ActiveRecord based.

Re-opening a class located in lib folder (Rails 3)

Similar questions have been asked many times. However, I have slightly different scenario and I still to find a clean solution to the following problem while developing a Rails 3 app:
I have a ruby code located in a single file. This code is one module MyModule which contains a bunch of classes. I can't modify this code because it's auto-generated by an external tool. The file name is my_module.rb and it resides in the lib folder. This is the usual place for a code which doesn't belong to the main web app. I made sure it is auto-loaded once I access MyModule in one of my app controllers by modifying config.autoload_paths setting in application.rb.
Now I need to re-open one of the classes in MyModule. I created a separate file some_class.rb where I put the following code:
class MyModule::SomeClass
def some_new_method
...
end
end
My question is: what is the best place to put this code? And what should be the name of the file?
The things I tried:
Placing the code into initializers folder. it works fine in production environment. However, it breaks in development because the code in initiliazers folder is loaded once when the server starts up and lib is reloaded on each request. So, the first request in development works fine, however, the subsequent requests fail. Also I don't like placing the code into initializers because it's not really initialization piece but rather a part of business logic.
Placing the code into lib folder also doesn't work. It's not loaded. Only my_module.rb is.
Placing the code into lib or some other folder under app and then explicitly requiring it in controllers. Again it works in production but fails in development for the same reason aforementioned in case #1.
I switched off auto-loading, placed both my_module.rb and some_class.rb into lib folder and requireed them in an initializer. I think this is the best approach. The downside is that I still have to create a custom initializer and the code is not loaded lazily.
UPDATE:
5. Thanks to raggi's suggestion, with auto-loading switched on, I put require_dependency 'some_class' in my controller. In this case some_class.rb is loaded on each request in development mode. This is my preferred approach . However, it still requires some sort of require :)
Remember that files in lib folder are auto-loaded lazily. Rails checks the folder only when you use modules or classes that rails can't find. The file name must follow the naming convention, hence the name my_module.rb.
I wonder if there is a solution when both files are auto-loaded lazily without any require statements.
Did you try require_dependency in your controller instead of require?
You can put the files in the lib directory and have them not lazily loaded but autoloaded, in config/application.rb if you want to put files in different subfolders:
config.autoload_paths += Dir["#{config.root}/lib/**/"]
Or if you need only the lib folder:
config.autoload_paths += %W(#{config.root}/lib)
TL;DR: Hide the require outside of your mainline code.
I ran in to a similar problem when trying to create a validator within a module during development. I ran across this blog entry when I was trying to solve what sounds like the same problem.
// Added to application.rb
Rails::Application.config.autoload_paths.each do |d|
Dir["#{d}/*.rb"].each do |p|
puts "Requiring '#{p}' on startup..."
require p
end
end

Best way to load module/class from lib folder in Rails 3?

Since the latest Rails 3 release is not auto-loading modules and classes from lib anymore,
what would be the best way to load them?
From github:
A few changes were done in this commit:
Do not autoload code in *lib* for applications (now you need to explicitly
require them). This makes an application behave closer to an engine
(code in lib is still autoloaded for plugins);
As of Rails 2.3.9, there is a setting in config/application.rb in which you can specify directories that contain files you want autoloaded.
From application.rb:
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
# Autoload lib/ folder including all subdirectories
config.autoload_paths += Dir["#{config.root}/lib/**/"]
Source: Rails 3 Quicktip: Autoload lib directory including all subdirectories, avoid lazy loading
Please mind that files contained in the lib folder are only loaded when the server is started. If you want the comfort to autoreload those files, read: Rails 3 Quicktip: Auto reload lib folders in development mode. Be aware that this is not meant for a production environment since the permanent reload slows down the machine.
The magic of autoloading stuff
I think the option controlling the folders from which autoloading stuff gets done has been sufficiently covered in other answers. However, in case someone else is having trouble stuff loaded though they've had their autoload paths modified as required, then this answer tries to explain what is the magic behind this autoload thing.
So when it comes to loading stuff from subdirectories there's a gotcha or a convention you should be aware. Sometimes the Ruby/Rails magic (this time mostly Rails) can make it difficult to understand why something is happening. Any module declared in the autoload paths will only be loaded if the module name corresponds to the parent directory name. So in case you try to put into lib/my_stuff/bar.rb something like:
module Foo
class Bar
end
end
It will not be loaded automagically. Then again if you rename the parent dir to foo thus hosting your module at path: lib/foo/bar.rb. It will be there for you. Another option is to name the file you want autoloaded by the module name. Obviously there can only be one file by that name then. In case you need to split your stuff into many files you could of course use that one file to require other files, but I don't recommend that, because then when on development mode and you modify those other files then Rails is unable to automagically reload them for you. But if you really want you could have one file by the module name that then specifies the actual files required to use the module. So you could have two files: lib/my_stuff/bar.rb and lib/my_stuff/foo.rb and the former being the same as above and the latter containing a single line: require "bar" and that would work just the same.
P.S. I feel compelled to add one more important thing. As of lately, whenever I want to have something in the lib directory that needs to get autoloaded, I tend to start thinking that if this is something that I'm actually developing specifically for this project (which it usually is, it might some day turn into a "static" snippet of code used in many projects or a git submodule, etc.. in which case it definitely should be in the lib folder) then perhaps its place is not in the lib folder at all. Perhaps it should be in a subfolder under the app folderĀ· I have a feeling that this is the new rails way of doing things. Obviously, the same magic is in work wherever in you autoload paths you put your stuff in so it's good to these things. Anyway, this is just my thoughts on the subject. You are free to disagree. :)
UPDATE: About the type of magic..
As severin pointed out in his comment, the core "autoload a module mechanism" sure is part of Ruby, but the autoload paths stuff isn't. You don't need Rails to do autoload :Foo, File.join(Rails.root, "lib", "my_stuff", "bar"). And when you would try to reference the module Foo for the first time then it would be loaded for you. However what Rails does is it gives us a way to try and load stuff automagically from registered folders and this has been implemented in such a way that it needs to assume something about the naming conventions. If it had not been implemented like that, then every time you reference something that's not currently loaded it would have to go through all of the files in all of the autoload folders and check if any of them contains what you were trying to reference. This in turn would defeat the idea of autoloading and autoreloading. However, with these conventions in place it can deduct from the module/class your trying to load where that might be defined and just load that.
Warning: if you want to load the 'monkey patch' or 'open class' from your 'lib' folder, don't use the 'autoload' approach!!!
"config.autoload_paths" approach: only works if you are loading a class that defined only in ONE place. If some class has been already defined somewhere else, then you can't load it again by this approach.
"config/initializer/load_rb_file.rb" approach: always works! whatever the target class is a new class or an "open class" or "monkey patch" for existing class, it always works!
For more details , see: https://stackoverflow.com/a/6797707/445908
Very similar, but I think this is a little more elegant:
config.autoload_paths += Dir["#{config.root}/lib", "#{config.root}/lib/**/"]
In my case I was trying to simply load a file directly under the lib dir.
Within application.rb...
require '/lib/this_file.rb'
wasn't working, even in console and then when I tried
require './lib/this_file.rb'
and rails loads the file perfectly.
I'm still pretty noob and I'm not sure why this works but it works. If someone would like to explain it to me I'd appreciate it :D I hope this helps someone either way.
I had the same problem. Here is how I solved it. The solution loads the lib directory and all the subdirectories (not only the direct). Of course you can use this for all directories.
# application.rb
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
As of Rails 5, it is recommended to put the lib folder under app directory or instead create other meaningful name spaces for the folder as services , presenters, features etc and put it under app directory for auto loading by rails.
Please check this GitHub Discussion Link as well.
config.autoload_paths does not work for me. I solve it in other way
Ruby on rails 3 do not automatic reload (autoload) code from /lib folder. I solve it by putting inside ApplicationController
Dir["lib/**/*.rb"].each do |path|
require_dependency path
end
If only certain files need access to the modules in lib, just add a require statement to the files that need it. For example, if one model needs to access one module, add:
require 'mymodule'
at the top of the model.rb file.
Spell the filename correctly.
Seriously. I battled with a class for an hour because the class was Governance::ArchitectureBoard and the file was in lib/governance/architecture_baord.rb (transposed O and A in "board")
Seems obvious in retrospect, but it was the devil tracking that down. If the class is not defined in the file that Rails expects it to be in based on munging the class name, it is simply not going to find it.
There are several reasons you could have problems loading from lib - see here for details - http://www.williambharding.com/blog/technology/rails-3-autoload-modules-and-classes-in-production/
fix autoload path
threadsafe related
naming relating
...

Resources