Organizing models/controllers and classes in rails in subfolders - ruby-on-rails

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)

Related

Rails models in subfolders and relationships

I organized some of my rails models in folders which I am autoloading with
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**}')]
I can use all models directly(e.g Image.first.file_name) but when I try to access them through relationships, e.g. #housing.images.each do... with has_many: images I get the following error
Unable to autoload constant Housing::HousingImage, expected /path/app/models/housing/image.rb to define it
How do i get rails to use my models for the relationship methods?
I'm running ruby 2.2 and rails 4.2
Rails automatically loads models from subfolders but does expect them to have namespace.
/app/models/user.rb
class User
end
/app/models/something/user.rb
class Something::User
end
If you do not properly namespace your models in subfolders it will mess up Rails autoloader and cause errors like you see.
Remove this
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**}')]
And add the proper namespaces to your models and everything will work fine.
You can easily use namespaced models in your relationships like this:
class User
has_many :photos, class_name: 'Something::Photo'
end
user.photos (will be instances of Something::Photo)
If you do not want to use the namespacing but split up your models for other reason, you can do that at the top-level and use other folders next to models.
By default rails loads all the folders in apps, so you could just make a folder "models2" or whatever you want to call it next to "models".
This will not have any effect on the functionality of the rails class loading.
Given your example you could then do:
/app
/controllers
/models
for all your normal models
/housing
for your "housing" models
Like this you can directly access them at the top level namespace, no class_name settings or anything needed.

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

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.

Custom Views Scaffolding in Rails Engines

I'm trying to get custom scaffolding working from my engine.
I followed some tutorial on customizing Rails 3.2 scaffolding in a normal Rails App and put my customized templates in the engines /lib/templates/erb/scaffold directory but they don't get picked up by the app that includes the engine. Any suggestions?
Update:
I also tried to override the Rails ScaffoldGenerator's source_path and tried some other paths to put my template in, like:
lib/rails/generators/erb/scaffold/templates
zarazan's answer got me most of the way there, but there are a couple of things wrong with it. Here's what worked for me:
class Engine < Rails::Engine
config.generators do |g|
g.templates.unshift File::expand_path('../../templates', __FILE__)
end
end
Note that this goes in the generators section, not app_generators, and that the path is slightly different.
Also, I think the correct path to store your templates is lib/templates/erb/scaffold, optionally replacing erb with whatever language you are using (like haml or slim.) I know this works for slim. The file names are {_form,edit,index,new,show}.html.erb.
In the file that you declare your engine use this command:
class Engine < Rails::Engine
config.app_generators do |g|
g.templates.unshift File::expand_path('../templates', __FILE__)
end
end
It should shift the preference of what template folder Rails uses by default.
Now just put the template files in lib/templates/erb/scaffold/template_name.erb
Where template_name is one of the following: _form.html.erb, edit.html.erb, index.html.erb, new.html.erb, show.html.erb
Once you include the gem you should be able to use the rails generate scaffold command as normal.
Here is an example of an engine that overrides the default scaffolding in rails:
https://github.com/brocktoncg/gemboree
This is where the template directory is located:
https://github.com/brocktoncg/gemboree/tree/master/lib/templates/erb/scaffold
Are you talking about a controller template? Than you are using the wrong directory. Save your template at
lib/templates/rails/scaffold_controller/controller.rb
Have a look at http://xyzpub.com/en/ruby-on-rails/3.2/templates.html for an example.

Autoloading from namespace inside a custom folder in app

We're currently developing a custom cms engine for ROR 3.2. In this process several class types originated that we want to be first class citizens in our rails application, meaning they should reside under the app folder of the application and it's plugins.
Currently we're having the following types:
DataSource
DataType
View
I created multible directories under the app folder to hold these:
app/data_source
app/data_type
app/view
More types will follow and I'm a little worried with polluting the app folder with so many directories. Thus i want to move them into a subdirectory/module that holds all Types defined by the cms.
All classes should be inside a MyCms namespace and the directory layout should look like this:
app/my_cms/data_source
app/my_cms/data_type
app/my_cms/view
But now I'm having trouble with autoloading because rails default autoloading would excpect the paths to be like this:
app/data_source/my_cms
app/data_type/my_cms
app/view/my_cms
But this way I would not have grouped all object types in one directory.
What I want is somewhat similar to view grouping of isolated engines. In Devise for example, all views are grouped in the views/devise subdirectory.
Any idea how this could be achieved without to much custom implementation?
You would have to add app/my_cms to your autoload path within config/application.rb:
config.autoload_paths << "#{config.root}/app/my_cms"
provided that your classes are defined without a namespace like this:
class DataSource
...
end
If you namespace them like this in app/my_cms/data_source.rb:
class MyCms::DataSource
...
end
you might add the app folder to the load path:
config.autoload_paths << "#{config.root}/app"
Alternatively, you can do it manually but you lose the reloading for those classes in Rails development:
in app/my_cms.rb (and with autoloading for app in place):
module MyCms
autoload :AnotherDataSource, 'my_cms/data_source/one_data_source'
autoload :AnotherDataSource, 'my_cms/data_source/another_data_source'
...
end

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)

Resources