Rails 4: organize rails models in sub path without namespacing models? - ruby-on-rails

Would it be possible to have something like this?
app/models/
app/models/users/user.rb
app/models/users/education.rb
The goal is to organize the /app/models folder better, but without having to namespace the models.
An unanswered question for Rails 3 is here:
Rails 3.2.9 and models in subfolders.
Specifying table_name with namespaces seems to work (see Rails 4 model subfolder), but I want to do this without a namespace.

By default, Rails doesn't add subfolders of the models directory to the autoload path. Which is why it can only find namespaced models -- the namespace illuminates the subdirectory to look in.
To add all subfolders of app/models to the autoload path, add the following to config/application.rb:
config.autoload_paths += Dir[Rails.root.join("app", "models", "{*/}")]
Or, if you have a more complex app/models directory, the above method of globing together all subfolders of app/models may not work properly. In which case, you can get around this by being a little more explicit and only adding the subfolders that you specify:
config.autoload_paths += Rails.root.join("app", "models", "<my_subfolder_name1>")
config.autoload_paths += Rails.root.join("app", "models", "<my_subfolder_name2>")
UPDATE for Rails 4.1+
As of Rails 4.1, the app generator doesn't include config.autoload_paths by default. So, note that the above really does belong in config/application.rb.
UPDATE
Fixed autoload path examples in the above code to use {*/} instead of {**}. Be sure to read muichkine's comment for details on this.

Related

Load a module from app/*/*/some_module.rb directly into the global namespace

Rails loads files from app/models/concerns and app/controllers/concerns directly into the global namespace instead of the Concerns namespace.
So if you define a module SomeConcern at app/models/concerns/some_concern.rb, instead of Concerns::SomeConcern, you have just SomeConcern available.
I would like to add a concerns folder in another subdirectory of app that behaves the same way. More specifically: I want to define SomeSerializerConcern at app/serializers/concerns/some_serializer_concern.rb, but currently it's only working if I define the module as Concerns::SomeSerializerConcern.
I have this line in config/application.rb:
config.paths.add "app/serializers/concerns", eager_load: true
This excellent post on autoload and eager_load led me to believe that this is how Rails itself accomplishes what I want to do, but it doesn't seem to work in this scenario. I'm using Rails 4.2.6. Any ideas?
Turns out this gist provides the answer.
All I needed to do was add the path directly to eager_load_paths in config/application.rb:
config.eager_load_paths += [Rails.root.join('lib'),
Rails.root.join('app', 'serializers', 'concerns')]

Autoloading Rails models that don't follow the default file name structure

THE SHORT VERSION: How do I tell Rails to autoload an ActiveRecord class called ClassName that isn't located in the default location (app/models/class_name.rb). E.g. what if it's in app/models/subdirectory/class_name.rb and I don't want to rename the class to Subdirectory::ClassName?
THE LONGER VERSION:
I know that, by default in Rails, my class names have to follow a specific structure for Rails to be able to autoload them.
E.g. If my class is called Person, if I put it in app/models/person.rb, Rails can load it fine, but if I put it in e.g. app/models/person_class.rb, it can't. If I namespace it, e.g. Humanity::Person, I need to put it in the right folder app/model/humanity/person.
(Plus I can put classes in lib but I'll leave aside that detail for now)
So far so good. But what happens when I have a ton of ActiveRecord classes clogging up my app/models folder and I want to logically organise them into directory, but don't want to rename or namespace the actual classes? How can I tell Rails to autoload this classes?
Or is there a good reason why I shouldn't do this?
Add this line to config/application.rb
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**/}')]
Then you can use your model with their name as it is without name-spacing.

Organizing models/controllers and classes in rails in subfolders

I am working in a Rails project in which i have used the below names for model/controller and class files
/app/models/friends/friend.rb
/app/controllers/friends/friends_controller.rb
/lib/classes/friends/friend.rb
I tried to add all the models, controllers and class files in autoload path in application.rb.
But i am facing issues since the class names are same.
How should i handle this? and organize files in such a way that files are organized with name spaces.
Thanks,
Balan
A much better approach would be to use Rails Engines & divide your app in isolated modules.
rails plugin new friends --full --mountable --dummy-path spec/dummy
the above command will generate a full mountable engine with isolated namespace, meaning that all the controllers and models from this engine will be isolated within the namespace of the engine. For instance, the Post model later will be called Friends::Post, and not simply Post. to mount this app inside your main rails app, you need do two things:
Add entry to Gemfile
gem 'friends', path: "/path/to/friends/engine"
And then add route to config/routes.rb
mount Friends::Engine, at: "/friends"
For more information on this approch, checkout:
Rails Guide to Engines
Taming Rails Apps with Engines
RailsCast #277 Mountable Engines
The class names are same but path's are different, and you don't need to add classes to autoload except /lib/classes/friends/friend.rb
Did you tried the following way:
# app/models/friends/friend.rb
class Friends::Friends
#...
end
# Friends::Friends.new
# app/controllers/friends/friends_controller.rb
class Friends::FriendsController < ApplicationController
#...
end
# lib/classes/friends/friend.rb
module Classes
module Friends
class Friends
#...
end
end
end
# Classes::Friends::Friends.new
To add lib files to autoload add following to your applicaion.rb
config.autoload_paths += %W(#{config.root}/lib)

Rails lib directory

Question about lib directory.
What are good practices in using the lib directory?
When should it be used over app/models or app/helpers?
And somewhat related how do you get Rails 3 to include files from the lib directory?
Thanks
One use of the lib directory (how I use it most often) is to share code between models to stay DRY. For example, if you are defining a tag_tokens attribute on many different models for use with a tokenizer input, you could put that in "tag_accessor.rb" or something, place it in /lib', and then include it with include TagAccessor. The ruby file might look like:
module TagAccessor
def tag_tokens
tags.map(&:name).join(',')
end
def tag_tokens=(names)
self.tag_ids = names.split(",").uniq
end
end
(This is an example from one of my apps, which is why it's so specific). Then to load the /lib folder in Rails 3, place this in your application.rb:
config.autoload_paths += %W(#{config.root}/lib)

Adding a directory to the load path in Rails?

As of Rails 2.3, what's the right way to add a directory to the load path so that it hooks into Rails' auto-reloading mechanisms?
The specific example I'm thinking of is I have a class that has several sub-classes using STI and I thought it would be a good idea to put them in a sub-directory rather than clutter the top-level. So I would have something like:
#app/models/widget.rb
class Widget < ActiveRecord::Base
add_to_load_path File.join(File.dirname(__FILE__), "widgets")
end
#app/models/widgets/bar_widget.rb
class BarWidget < Widget
end
#app/models/widgets/foo_widget.rb
class FooWidget < Widget
end
It's the add_to_load_path method that I'm looking for.
In the current version of Rails (3.2.8), this has been changed in the application.rb file.
The code is currently commented out as:
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
Will need to update the autoload_paths value. Attempting to modify the the former load_paths variable causes this error.
/configuration.rb:85:in `method_missing': undefined method `load_paths' for #<Rails::Application::Configuration:0xac670b4> (NoMethodError)
for an example, for each path to add to autoload_paths config, add a line similar to the following:
config.autoload_paths += %W(#{config.root}/app/validators)
config.autoload_paths accepts an array of paths from which Rails will autoload constants. Default is all directories under app.
http://guides.rubyonrails.org/configuring.html
From commentor (hakunin) below:
If the directory is under app/, you don't need to add it anywhere, it should just work by default (definitely in 3.2.12). Rails has eager_load_paths that acts as autoload_paths in development, and eager load in production. All app/* directories are automatically added there.
For older versions of Rails:
You can do this in your environment.rb config file.
config.load_paths << "#{RAILS_ROOT}/app/widgets"
--
For Rails 3, see answers bellow
In Rails 5 you don't have to explicitly load folders from within the app directory anymore. All folders placed inside are directly available. You don't have to touch any of the config files. But it seems as if there are some issues with Spring.
The new workflow therefore is:
create a new folder and class inside the /app directory
run spring stop on the command line
check the autoload-paths with bin/rails r 'puts ActiveSupport::Dependencies.autoload_paths' on the command line. The new folder should now be listed.
run spring start on the command line
In Rails 3, you can set this in config/application.rb, where this sample is provided by default:
# Add additional load paths for your own custom dirs
# config.load_paths += %W( #{config.root}/extras )
On Rails 5 you need to add the following code to environment.rb:
# Add the widgets folder to the autoload path
Rails.application.configure do
config.autoload_paths << "#{Rails.root}/app/widgets"
end
Another update for rails 3 -- activesupport 3.0.0:
Instead of:
ActiveSupport::Dependencies.load_paths << "#{RAILS_ROOT}/app/widgets"
You may need to do this:
ActiveSupport::Dependencies.autoload_paths << "#{RAILS_ROOT}/app/widgets"
I found I needed to do this after config block-- no access to config object anymore.
This did the trick
ActiveSupport::Dependencies.load_paths << "#{RAILS_ROOT}/app/widgets"
In config/application.rb add config.autoload_paths << "#{config.root}/models/widgets".
File should look like this:
module MyApp
class Application < Rails::Application
config.autoload_paths << "#{config.root}/models/widgets"
end
end
I know this works for Rails 4 and 5. Probably others as well.
If you want to add multiple directories:
config.autoload_paths += Dir[Rails.root / "components/*/app/public"]
(this is an example for packwerk autoload)

Resources