Unable to add custom folder under app directory in Rails 7 - ruby-on-rails

I am trying to add custom folder named foo inside app directory like this app/foo. But rails is not able to find module. This is just a example class inside app/foo/bar.rb
module Foo
class Bar
end
end
so if I try to do Foo::Bar.new in console I get uninitialized constant Foo (NameError). But if i remove the namespace from the bar.rb file then it works. e.g this will work
class Bar
end
Bar.new works fine is rails console, if i remove namespace from the bar.rb file(NOTE: bar.rb file is still under app/foo/bar.rb. I also tried adding config.autoload_paths << "#{root}/app/foo" in application.rb file but no luck.
If I put foo directly under api directory like this app/api/foo/bar.rb then it works fine. If I also add foo directly under models directory then it works fine e.g app/models/foo/bar.rb
Whats going on? How can I add foo directory under app directory ?
ActiveSupport::Dependencies.autoload_paths shows the directory foo is getting loaded fine.
UPDATE
Looks like what I am trying to achieve can't be done without putting the foo directory inside some other directory. But it makes sense because if we have user.rb file inside app/models/user.rb we don't do class Models::User we do class User. so make sense.

You need to add the following line to application.rb file in order to configure autoloading.
config.autoload_paths << "#{root}/foo"
for more about this you can read at https://guides.rubyonrails.org/autoloading_and_reloading_constants.html
UPDATE
Anything under app directory will be autoloaded by default.
for app/foo/bar.rb
content should be.
class Bar
end
if you need to make it something like this.
module Foo
class Bar
end
end
then your directory structure should be app/custom_folder_name/foo/bar.rb
I will recommend to use custom_folder_name as extras

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)

Rails modules as strict namespaces

I'm quite new to rails and I'm a bit confused of how modules work here. I have a project structure like this:
# app/models/foo.rb
class Foo < ActiveRecord
# lib/external_service/foo.rb
module ExternalService
class Foo
# lib/external_service/bar.rb
module ExternalService
class Bar
attribute :foo, Foo # not the model
I have worked with many coding languages before and I expected it to be easily possible to use 'Foo' inside Bar and ExternalService just like that but
LoadError: Unable to autoload constant Foo, expected lib/external_service/foo.rb to define it
The ExternalService::Foo should normally not even be visible outside of ExternalService but the whole project dies on this thing
Am I just missing a kinda 'strict mode'-notation or anything to make sure that I obviously mean ExternalService::Foo inside the service and prevent the service from killing my model?
I know I can just prepend the module but i wanna keep the code readable.
so you are using rails 4
if you want to create a module, first you need to import or autoload your lib folder
for example in application.rb you can add lib folder to autoload:
config.autoload_paths << Rails.root.join('lib')
after that because you are using rails you should create a folder hierarchy with snake cased name of your module hierarchy
for example if you have:
module ExternalService
class Foo
...
end
end
your foo.rb file should be in a folder with name 'external_service'
{{project_root}}/lib/external_service/foo.rb
folder hierarchy is convention of rails.
Ruby behaves just like this and it's totally ok.
In this case the Foo-Model is already loaded, so ruby prefers this instead of the local one. Also alphabetically app/ is before lib/
A not so beautiful but quick fix is just to call it like this:
attribute :foo, ExternalService::Foo

Rails autoloader not loading module

I have the following structure in my /lib folder
/lib
/example
/foo
bar.rb
foo.rb
foo.rb
module Example
module Foo
def self.some_methods
end
end
end
bar.rb
module Example
module Foo
class Bar
...
end
end
end
I then have in application.rb
config.autoload_paths << Rails.root.join('lib')
and in an initializer
...
Example::Foo.some_methods
...
I'm running into problems when trying to run my test suite in that it seems to be failing when setting up the Rails environment. It's complaining that some_methods does not exist on Example::Foo. I can boot a local development server up without any errors, however.
Digging into the problem using a breakpoint, the Example::Foo module is defined at the point of failure, but it's empty. I am assuming that the autoloader is loading bar.rb first (I can access that class in the initializer no problems)? It seems to be the case that Rails doesn't try to load foo.rb as Example::Foo has already been defined by bar.rb?
What's happening? If I require foo.rb in the initializer it works, but it feels like a bad solution. Also, any idea why this problem would only surface when running rake spec?
The whole point of the autoloader is that it loads missing constants by turning a module/class hierarchy into a path. As you've defined your constant in two files, it will never automatically load the second file.
You need to manually require your foo.rb.

Namespacing something in a custom directory under app?

I'm wanting to create a custom directory under app, and have a corresponding namespace, like:
app/my_widgets
and
#app/my_widgets/foo
module MyWidgets
class Foo
#...
end
end
This doesn't work.
LoadError: Unable to autoload constant Foo, expected /project_dir/app/my_widgets/foo.rb to define it.
I am calling:
MyWidgets::Foo.new()
I had guessed this could be because rails autoloads constants from subdirectories of app/... However, adding:
config.autoload_paths += %W{#{config.root}/app"}
Doesn't fix it as I thought it might. Is this a lost cause?
I have tried moving my_widgets/ to something else, eg 'services/' under app, and then everything works as expected.
Is there any way to have a namespace & corresponding directory directly under app/, though?

Class in Folder autoloading

In one of my controllers I would like to use a Service-Class that I located inside of: services/cars/strategies/unload_car_strategy.rb
Although the unload_car_strategy.rb is located inside the folders, I don't want to use namespacing: The class right now looks like this:
class UnloadCarStrategy
....
end
When I call in my controller UnloadCarStrategy.new I get the error:
NameError: uninitialized constant UnloadCarStrategy
How can I instruct Rails to load this Class?
By default everything under your app/ dir gets auto- and eager- loaded when your application starts. So you got your services dir loaded when the application started.
Even though if you add as many folders and files in those folders you want, Rails will load them for you until you follow the namespacing.
Since you are not using the namespacing the class UnloadCarStrategy doesn't get loaded.
You have to explicitly require it either in the respective controller where it is needed or in the application.rb file.
you either add
require "#{Rails.root}/app/services/cars/strategies/unload_car_strategy" in the controller from where it has to be called
or add the below line to application.rb
config.autoload_paths += %W({config.root}/app/services/cars/strategies/unload_car_strategy.rb)
source : https://gist.github.com/maxim/6503591#if-you-add-a-dir-under-appsomething

Resources