How can i put app subdir classess under a namespace - ruby-on-rails

I want to namespace all my classes put in a app/cms folder under Cms module. So, let's say i have a following file:
# /app/cms/types/post.rb
class Cms::Types::Post
end
Rails assumes that class definition of a file put in this dir should be Types::Post instead of Cms::Types::Post. So, when calling Cms::Types::Post.new, rails throws
LoadError (Unable to autoload constant Types::Post, expected /Users/xxx/workspace/personal/xxx/app/cms/types/post.rb to define it)
How can i namespace all these files under Cms?
I'm on rails 5.2.0

What is Post? I mean, what is the nature of Post? For instance, is it a service?
If it were a service (for example), I would put it in:
/app/services/cms/types/post_service.rb
And then define it as:
class Cms::Types::PostService
end
If it were a type (which seems like it might be a problematic name), then I would define it in:
/app/types/cms/post_type.rb
and define it as:
class Cms::PostType
end
In other words, name the directory under /app using the description of what Post is (just like rails does with models, controllers, etc.) and then add the description (in the singular form) to the end of your definition (rails does this with controllers, helpers, and mailers, but not with models).

Normally, when you use namespace you need to have your file structure as below:
module Cms
module Types
class Post
end
end
end
EDIT: Also to make it, autoload the path, you could add the below line in your application.rb:
config.autoload_paths << Rails.root.join('app', 'cms').to_s

Related

Rails autoload don't load module

In my rails app, I have a custom directory in app/entities/keycard_generator/
I have a data_source.rb (a module) and register_keycard.rb (a class)
module DataSource
...
end
class RegisterKeycard
include DataSource
...
end
I have added this line in my application.rb, to autoload the path.
Dir[Rails.root.join('app/entities/keycard_generator/**/*.rb')].each(&method(:require))
But I get this errors (cannot run rails) :
uninitialized constant RegisterKeycard::DataSource (NameError)
Edit
I think that the issue is only with modules
Given that you have:
app/entities/keycard_generator/data_source.rb
Then the content of that file, by convention, should be:
module KeycardGenerator
module DataSource
...
end
end
Because rails essentially ignores the directory name under app (here, entities) in terms of naming:
app/entities/keycard_generator/data_source.rb
^^^^^^^^
But expects the directory name under the directory under app (here, keycard_generator) to be a module name:
app/entities/keycard_generator/data_source.rb
^^^^^^^^^^^^^^^^^
And given that you have:
app/entities/keycard_generator/register_keycard.rb
Then the content of that file, by convention, should be:
module KeycardGenerator
class RegisterKeycard
include DataSource
end
end
When following the convention, you do not need:
Dir[Rails.root.join('app/entities/keycard_generator/**/*.rb')].each(&method(:require))
Personally, I like to append the type (here, indicated by entities) to the files and modules/classes, like:
app/entities/keycard_generator/data_source_entity.rb
module KeycardGenerator
module DataSourceEntity
...
end
end
Which is how rails often (e.g., with controller) but not always (e.g., with model) does it.
In which case you would also do:
app/entities/keycard_generator/register_keycard_entity.rb
module KeycardGenerator
class RegisterKeycardEntity
include DataSourceEntity
end
end
Mark the included module as global by prefixing the name with ::
class RegisterKeycard
include ::DataSource
...
end

Rails app: autoloading classes defined in modules

in Rails 5.0.1 application I have file app/actions/frontend/cart/get_cart_items_summarized.rb with:
module Actions
module Frontend
module Cart
class GetCartItemsSummarized
#content here
end
end
end
end
And in app/helpers/application_helper.rb I call it:
def get_cart_items
#...
items = Actions::Frontend::Cart::GetCartItemsSummarized.new.call
#...
end
But I'm getting:
uninitialized constant ApplicationHelper::Actions
Why? How should I use this class?
Thanks
In rails/autoloading, the first level of directories, the one directly under app, is not considered a part of the name. It is so that your model can be just User and not Models::User, etc.
My solution to this is to put all custom stuff in app/lib. This way, lib eats that non-naming layer and the rest of your folder structure become a name. In your example, put your file to
app/lib/actions/frontend/cart/get_cart_items_summarized.rb
Of course, feel free to replace "lib" with whatever you want ("app/custom", for example). This name doesn't matter.
Either use a full-qualified name:
::Actions::Frontend::Cart::GetCartItemsSummarized.new.call
or just stick to Rails constant lookup (the below should work):
GetCartItemsSummarized.new.call
In "config/application.rb", add "app" to autoload paths, e.g.:
class Application < Rails::Application
config.autoload_paths += Dir[Rails.root.join('app')]
end

Why Rails autoload failed with the file is there?

I am setting up Grape on Rails 4.1. I am putting it inside lib according to http://funonrails.com/2014/03/building-restful-api-using-grape-in-rails/ .
I'd like to put helper methods in separated files, file structure is like following:
lib
|--- api
|--- root.rb
|--- helpers
|--- base_helper.rb
And inside root.rb, API is defined:
module API
class Root < Grape::API
formatter :json, Grape::Formatter::Jbuilder
helpers API::BaseHelper
end
end
Content of base_helper.rb is just simple:
module API
module BaseHelper
def test
"I am a test helper"
end
end
end
When I fire up the application, I get:
/Users/Larry/.rvm/gems/ruby-2.1.2#maleskine/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:481:in `load_missing_constant': Unable to autoload constant BaseHelper, expected /Users/Larry/Gallows/jianshu.io/maleskine/lib/api/helpers/base_helper.rb to define it (LoadError)
But base_helper.rb is actually in the right path mentioned in the error.
And if I remove helpers API::BaseHelper, autoload could find BaseHelper correctly.
Why is that? What have I done wrong?
After debugging into Rails source code, I found the problem.
First, since the helper file is inside helpers folder, the file should be defined inside Helpers module:
module API
module Helpers
module BaseHelper
def test
"I am a test helper"
end
end
end
end
Second, API::Helpers::BaseHelper has the path suffix api/helpers/base_helper, so make sure "#{Rails.root}/lib/" is inside your autoload path. ThenActiveSupport` will find it for you.
Make sure your BaseHelper is inside the API module. It should be defined like this:
module API
module BaseHelper
end
end
Try changing
helpers API::BaseHelper
to
helpers ::API::BaseHelper
to specify absolutely the class you're trying to load.

What's the correct way to namespace service objects in Rails?

I want to create multiple classes each of which are in the same namespace and I'd like them to be in different files.
i.e.
# app/services/core_metatags
module Metatags::CoreMetatags
#...
end
and # app/services/twitter_card
module Metatags::TwitterCard
#...
end
and # app/services/facebook_open_graph
module Metatags::FacebookOpenGraph
#...
end
I'd like each of these modules to be put into different files. However when I try doing this I get the following error:
Unable to autoload constant TwitterCard, expected
/Users/peter/project/app/services/twitter_card.rb to define it
What am I doing wrong? Is it incorrect to namespace this way? Or is it a directory naming problem...?
You need a directory for the namespace, so that:
Metatags::CoreMetatags goes in app/services/metatags/core_metatags.rb.
Metatags::TwitterCard goes in app/services/metatags/twitter_card.rb.
Metatags::FacebookOpenGraph goes in app/services/metatags/facebook_open_graph.rb.

Organizing files in lib directory

I'm trying to extract a portion of my Rails project into my lib directory but I can't work out how to link my files up correctly. My directory structure looks like this:
lib/
eventable/
calendar.rb
helpers.rb
# Rest of rails directories/files
I'm requiring the eventable directory in config/application.rb:
config.autoload_paths += %W(#{config.root}/lib #{config.root}/lib/eventable)
My helpers and calendar rb files:
# helpers.rb
module Eventable
module Helpers
def calendar_for...
Calendar.new...
end
end
end
# calendar.rb
module Eventable
class Calendar
# methods defined here
end
end
I'm then mixing my Eventable::Helpers module in the regular Rails helpers so that I can use calendar_for in my views:
ActionView::Base.send :include, Eventable::Helpers
This last step seems to work fine. However, when I go to a view which is using this helper I get:
uninitialized constant Eventable::Helpers::Calendar
If I change my helper so that it tries to access Eventable::Calendar.new instead then I get:
uninitialized constant Eventable::Calendar
When I had all of these in a single file, it all worked perfectly. So how I can correctly link these files up?
It looks like you need a loader-type file to tell Rails where to find code for the Eventable module.
Try add a lib/eventable.rb with:
module Eventable
autoload :Calendar, 'eventable/calendar'
autoload :Helpers, 'eventable/helpers'
end
You shouldn't need to change your load path if you have the loader file in place.
I had a similar problem. I solved it by changing the way modules are required.
In application.rb
config.autoload_paths += Dir["#{config.root}/lib/"]
Create /lib/eventable.rb with the following code
require "eventable/helpers"
require "eventable/calendar"

Resources