Class Inheritance and Ruby Modules - ruby-on-rails

I've decided to break up by Rails application into different modules for clarity, and as a part of this I'd like each module to have its own ApplicationController. So I went through and defined an application controller for each module like so:
module Login
class ApplicationController < ::ApplicationController
...
end
end
Then I'd create a different controller in the login module:
module Login
class HomeController < ApplicationController
end
end
My expectation was that this would first search the Login module for the application controller, but from what I'm seeing it's actually inheriting the global application controller (any declarations I make in Login::ApplicationController aren't picked up). I've searched around for some information on this, but haven't been able to figure out why it is using the ApplicationController in the top level name space and not the one in Login. It works just fine having HomeController inherit from Login::ApplicationController, but I'd like to learn why it doesn't work without the Login:: prefix for the future.

Try
module Login
class HomeController < Login::ApplicationController
end
end

Related

How to access ApplicationHelper methods in namespaced views?

I have a module ApplicationHelper in app/helpers/application_helper.rb
defined like this
module ApplicationHelper
def some_method(arg)
end
end
and i have my view file is here
app/views/v1/admin/messages/show.json.jbuilder
So i am trying to access the
some_method()
in view file but it doesn't reflect!
Is this due to namespacing? or what i am not able to understand.
It would be Great if someone explains the concept.
Thanks in advance!
it says undefined method error
what could be the reason?
You didn't include your controller code, but we'll assume it ultimately inherits from ActionController::API (as it should it if it is an API controller). If so, that is the root of it rather than namespacing, etc. Per the ActionController documentation:
An API Controller is different from a normal controller in the sense
that by default it doesn't include a number of features that are
usually required by browser access only: layouts and templates
rendering, flash, assets, and so on. This makes the entire controller
stack thinner, suitable for API applications. It doesn't mean you
won't have such features if you need them: they're all available for
you to include in your application, they're just not part of the
default API controller stack.
One of the side effects of the thinner API controller is that they don't automatically include helpers like a standard Rails controller. You can easily add that back in, though.
messages_controller.rb
class Api::V1::MessagesController < ActionController::API
include ActionController::Helpers
helper ApplicationHelper
def show
# whatever
end
end
app/helpers/application_helper.rb
module MessagesHelper
def some_method(arg)
# whatever
end
end
app/views/messages/show.json.jbuilder
json.bogus do
thing1: some_method('banana')
end
If you have lots of API controllers, you can of course stick it in a base controller class they all inherit from like so:
class Api::V1::ApiController < ActionController::API
include ActionController::Helpers
helper ApplicationHelper
end

Controllers not being loaded when mounting a Rails Engine

I am currently creating a Rails engine. So far the engine has a few new routes and a few new controllers. The routes work fine, as I was able to mount them adding the following to config/routes.rb:
mount MyEngine::Engine => '/'
The problem comes with the controllers. When trying to access the actions I have defined, I get:
uninitialized constant MyEngine::ApplicationController
I am a bit surprised, as the documentation suggests that everything inside app/ is autoloaded from the engine. I have even tried to explicitly load the controllers in engine.rb, to no avail:
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
Dir["#{config.root}/app/controllers/**/"].each do |path|
config.eager_load_paths << path
end
end
end
I'm confused. Aren't the contents of app/controllers/ supposed to be autoloaded by the application from the engine?
It looks like your engine's controller is trying to reference an ApplicationController inside your engine, when you might have wanted to inherit from the main application's ApplicationController (correct me if I'm wrong).
I don't know how the controller code in your engine looks like (feel free the share if I am not on the right track), but I assume it looks something like this
module MyEngine
class MyController < ApplicationController
end
end
In this scenario, it requires you to also have defined MyEngine::ApplicationController somewhere, and based on the error message, I assume this controller does not exist.
To solve this, you could either define the engine specific ApplicationController, or you can specify that you want to inherit from the root controller, like this:
module MyEngine
class MyController < ::ApplicationController
end
end
If you later want to be even more flexible in your engine, it is often left to an initializer configuration option to set which controller to inherit from (see example from Devise).
module MyEngine
class MyController < MyEngine.parent_controller.constantize
end
end

Alphabetical controller initialization

I have a controller Admin::AddressController that is a subclass of AdminController...and since "Address" comes before "Admin" I get a "uninitialized constant" error for the Admin Controller...
So to fix this, I've done a require ./app/controllers/admin/AdminController.rb before my AddressController class declaration.
Is the the right way to do things? Or is there a way I can tell rails to initialize the AdminController first?
Rails should be able to handle that automatically if you're following the conventions. If you're placing AdminController inside the admin folder, the class should be named Admin::AdminController. It's either that or:
module Admin
class AdminController
...
On AddressController you can use:
class Admin::AddressController < Admin::AdminController
...
or
module Admin
class AddressControler < AdminController
...

Rails 3 engine application controller

I have a rails engine that expose the following controller:
class ActsAsAssets::AssetsController < ApplicationController
..........
The main application uses devise into the ApplicationController. From the main application I use to extend the Engine Controller normally like this:
class MainApplicationController < ActsAsAssets::AssetsController
.......
What I was aspecting is that MainApplicationController was extending the main application ApplicationController via the engine.
Note that the engine does not have any ApplicationController so I was expecting ActsAsAssets::AssetsController < ApplicationController to actually extend the ApplicationController of the rails app that uses the engine.
Looks like I am wrong.
Any suggestion?
Basically what I want to achieve is that a controller from my main app extends a rails engine controller that extends the main ApplicationController cause does not have one inside the engine.
Hoper is clear.
Change the declaration to look like this:
class ActsAsAssets::AssetsController < ::ApplicationController
That instructs the controller to extend the non-namespaced ApplicationController from the main application.

Call a controller's method in other controllers (staying DRY)

I'm slightly new to Rails (i.e. stupid and need some teachin').
I have a controller (call it ControllerFoo) that performs a particular task (theMethod) which could be useful in other controllers (say, from within ControllerBar). So, of course, the method is defined as self.theMethod in ControllerFoo (which means it's a class method, right?), and access in ControllerBar as ControllerFoo.theMethod. Confused yet?
Here's the problem: the ControllerFoo.theMethod uses session data, and when called from ControllerBar, session is nil. In fact, it seems that session is also nil when being called from itself. I guess what I'm saying is class methods can't access session data?
<rant>I hate how session data can't simply be accessed anywhere like in PHP</rant>
So for now, since I'm not smart enough to know how to do this correctly, I've just duplicated the logic in several places throughout my app. But this is not DRY at all, and I hate it.
So how can I create a method in a controller that's accessible to other controllers and can also access session data?
class ControllerFoo < ApplicationController
def self.theMethod (greeting)
p "#{greeting} #{session[:user]}!"
end
end
class ControllerBar < ApplicationController
def show
ControllerFoo.theMethod("Hello,")
end
end
Couple of options...
Put the shared method in the shared parent ApplicationController
Create a module that both ControllerFoo and ControllerBar will include
e.g.
module SharedModule
def theMethod (greeting)
p "#{greeting} #{session[:user]}!"
end
end
class ControllerFoo < ApplicationController
include SharedModule
end
class ControllerBar < ApplicationController
include SharedModule
def show
theMethod("Hello,")
end
end
The way you would do this is Ruby would be to create a module containing the class (or instance) methods you wish to share and include it in the classes you need to have those methods defined in.

Resources