I am really confused about rails namespaces. I tried to create my own admin namespace so added namespace to routes, this works good. Then i added folder admin into controllers.
Admin::Controller
this is how my controllers in that folder looks.
but here comes the problem. How can i separate Helpers? rails automatically loads all helpers. I disabled that in config but now it wont load it manually like module Admin::ApplicationHelper.
How about next things what needs to be separated? Like i18N, sessions, flashes? Is there a tutorial for this problem? Im using Rails 4. Thanks for advices
As you've noticed rails defaults to including all helpers into all views. You can turn this off by adding
config.application_controller.include_all_helpers = false
This will result in only ApplicationHelper and the controller's helper being included. Adding
helper :foo
To a controller would result in FooHelper being included in addition to the defaults. If there are helpers that should be loaded for all of the admin controllers then add this to their base class. If you need anything more than this then consider using a rails engine (with the isolate_namespaces option turned on)
You only namespace controllers, views, models, and helpers, not everything else you mentioned. If you disabled autoloading helpers you'll have to manually require each one that you need:
require 'admin/admin_helper'
class Admin::Controller < ActionController::Base
... code ...
Same goes for any other helper such as application_helper, etc. Everything else, sessions, flashes, i18n and so on are merely methods from ActionController::Base that all controller's inherit. There's no namespacing these.
Going back to the helpers question: you namespace them the same way you namespace the controllers:
# app/helpers/admin/admin_helper.rb
module Admin::AdminHelper
... code ...
end
And then just require it in your admin controllers if you need to. I'd keep autoloading helpers enabled in order to forego having to require them everywhere.
Related
I'm trying to build an admin panel to my rails application, but want to keep my admin controllers away from my other controllers. Is there anyway I can have a admin folder in my app folder which contains controllers just for admin stuff.
Thanks in advance.
Yes, sure.
You can put all the admin related controllers in app/controllers/admin/ directory.
Yes, you can do this by namespacing your controllers under an admin module.
The easiest way to set this up is to use the rails generator, and prefix your resource with "admin":
rails generate controller admin/user
Type rails g controller for specific helps.
Here's a page from the guide with more info: http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing
If you want to keep your admin completely separate, you can use an engine. To generate the engine, do:
rails plugin new admin --mountable
Then in your main app's routes file, you can mount the engine with:
mount Admin::Engine => "/admin"
See http://guides.rubyonrails.org/engines.html for complete details on engines.
Thats very simple, usually it makes sense to put those in app/controllers/admin but if you use this, you'll need to use a namespace. Rails will then autoload these classes.
It's a good practice to make an ApplicationController per namespace (I'm calling it base controller) like this:
module Admin
class BaseController < ApplicationController
end
end
and here's an exapmle controller:
module Admin
class ExampleController < Admin::BaseController
def example
end
end
end
I'm writing a configurable Rails engine. I have an authentication_helper configuration option to define which helper should be called in a before_action in all controllers needing authentication.
The problem is that I don't have access to the parent app's helpers from the engine's controllers. My understanding is that this happens because the engine is isolated.
I have considered using a block instead of a method name, but I'm not sure if that would work, or if I would be able to cleanly access the authorization logic from outside my controllers.
Active Admin, which I have used in the past, has a similar configuration option. I have noticed that their engine is not isolated, so perhaps I'm overrating the importance of engine isolation?
Is there an elegant way to have the benefits of engine isolation while also allowing this kind of customization? Or should I just forego isolation altogether?
EDIT #1
Brad Werth pointed my in the right direction, as this works with a regular controller inheriting from ApplicationController::Base:
module MyBigFancyEngine
class Engine < Rails::Engine
isolate_namespace MyBigFancyEngine
config.to_prepare do
# Make the implementing application's helpers available to the engine.
# This is required for the overriding of engine views and helpers to work correctly.
MyBigFancyEngine::ApplicationController.helper Rails.application.helpers
end
end
end
However, my engine's ApplicationController inherits from RocketPant::Base, which does not provide a helper method. I've tried to use a simple include (which works fine for regular controllers), but that doesn't work either (the controller can't find the helper).
Any ideas?
You can expose the implementing application's helpers available to the engine by including the following code in your engine.rb file:
engine.rb
module MyBigFancyEngine
class Engine < Rails::Engine
isolate_namespace MyBigFancyEngine
config.to_prepare do
# Make the implementing application's helpers available to the engine.
# This is required for the overriding of engine views and helpers to work correctly.
MyBigFancyEngine::ApplicationController.helper Rails.application.helpers
end
end
end
The RailsAdmin Engine is also isolated, but they have the same configuration options as you would like to implement. They have configurable before_filters for both authentication and authorization. Have a look at this.
As far as I can tell, they just subclass the parent controller like this::ApplicationController or instead you can configure one (ref).
For your controller you could just create your own EngineController, that inherits from RocketPant::Base and maybe just create a method there that calls the configured authentication method directly via send on the parent controller.
As the RocketPant::Base Class does not inherit from ApplicationController::Base I guess you have to find some custom way around this and can't go the normal ways for Rails Engines. Maybe you could also try to file an issue to the rocket_pant repo, to add the helper method. As far as I read they soon want to inherit from ApplicationController::Base anyway in 2.0 (ref).
I think you impulse on keeping the isolation is good - because a solution that relies on a method 'just being there' is a pain to debug if s.th. goes wrong in the app using your gem (i.e. typo in method name).
Further if your gem evolves, an some day it won't need the method, in an explicit way it's very convient to give a meainingful error:
You could provide a config method to be called in an initializer:
YourGem.configure do |config|
config.add_callback { MyApp.doIt() }
end
I found this discussion particularly insightful. There are also some interesting ideas in the Rails Engine API under Isolated engine helpers.
The Rails Engine API docs helped me figure out a good solution for url_helpers
You probably moved on by now, but if anyone else needs access to the "parent app" application helpers. You could always just include it explicitly in the application controller in your engine. like so:
include Rails.application.helpers
What I've run into is this:
AlchemyCMS is a Rails Engine for allowing Rails applications to have a Content Management System. It also has a preview page where it can load up an iframe of the example page with the layout. The layout here is the Spree layout. I've modified Alchemy to be able to load up the spree application layout and not its default.
In doing so, it is not loading up the helper methods. I am currently receiving:
undefined local variable or method `title' for #<#<Class:0x007f8dcc359498>:0x007f8de17dd6a8>
Where title is the first helper method in the application.
I've tried 5000 different techniques to try to load in Spree's helper methods into AlchemyCMS and I just can't do it.
Does anyone know how?
Ben,
You could do so by either including Spree's helpers within your application controller or within the base Alchemy controllers.
There is an extension for alchemy and spree together, which does a similar thing here:
https://github.com/magiclabs/alchemy_spree/blob/master/app/controllers/spree/base_controller_decorator.rb
You will just want to go in the opposite direction so instead of decorating a Spree controller to add Alchemy in you would decorate Alchemy controllers to include whichever of Spree's controller helpers you need to use:
https://github.com/spree/spree/blob/master/core/app/controllers/spree/base_controller.rb
In this case you need to include the common controller helpers:
https://github.com/spree/spree/blob/master/core/lib/spree/core/controller_helpers/common.rb
EDIT:
Alchemy::BaseController.class_eval do
include Spree::Core::ControllerHelpers
include Spree::Core::ControllerHelpers::Store
helper Spree::Core::Engine.helpers
end
Im using a Rails engine as a cms. It all works fine. Im adding devise to this.
My generated devise controllers inherit from Devise::SessionsController. But there are some filters that are run from another controller in the engine that wont run in this case. A lot of the site relies on these filters being run. Of course I could just duplicate them, but thats bad juju.
So my Question is: How can I make one controller run the filters from another? I would prefer not to edit either of the gems.
Multiple inheritance is not supported in Ruby. I think extracting the filters into a module and mixing them in would be the cleanest solution.
See for example:
http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
There is another option available for Devise: config.parent_controller. It defaults to ApplicationController but can be changed to something else. In my case (a Rails API) I use ApiController. In config/initializers/devise.rb:
Devise.setup do |config|
# ... other configuration options
config.parent_controller = 'ApiController'
end
I'm in the proces of converting my standard Rails app to an mountable engine. The app is comparable to a standard blogging app and i want every model, controller and view to be extendable hence my choice for a mountable engine.
One of the gems i use is Devise which is as far as i understand a sort of a mountable engine itself. It can be used inside a mountable engine as stated here.
I'm able to use it partially within my engine. Everything is working fine including some Devise controller i override like this one:
# config/routes.rb
Bbronline::Engine.routes.draw do
devise_for :users, class_name: "Bbronline::User", module: :devise,
controllers: { registrations: "bbronline/devise_overrides/registrations"}
...
# controllers/bbronline/devise_overrides/registrations_controller.rb
require_dependency "bbronline/application_controller"
module Bbronline
class DeviseOverrides::RegistrationsController < Devise::RegistrationsController
def new_intermediair
#user = User.new
end
...
The correct view 'views/bbronline/devise_overrides/registrations/new_intermediair.html.haml' is also correctly loading as expected.
However my issue is that the views that i override without a custom controller are not properly loaded. For example the view that should the login view is located in views/bbronline/devise/sessions/new.html.haml and is not loaded. Instead the standard Devise login view gets loaded i.e. devise-2.1.0/app/views/devise/sessions/new.html.erb
Of course i could solve this problem by overriding every controller with my own controller like i did with the registrations_controller above but this seems very ugly. Is overriding every controller the way to do this? Is there a more convenient way to override views from an mountable engine from within another mountable engine?
If you don't want to adjust the config.railties_order in every app that uses your engine, just require 'devise' on top of your lib\my_engine\engine.rb file.
The view_paths are in incorrect order. Checking the view paths of Devise::SessionsController shows:
Devise::SessionsController.view_paths
=> #<ActionView::PathSet:0x007fa1bf0e36f8 #paths= [/Users/harmdewit/Dropbox/Code/projects/brightin/bbr-online/bbr-online-gem/test/dummy/app/views,
/Users/harmdewit/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/devise-2.1.0/app/views,
/Users/harmdewit/Dropbox/Code/projects/brightin/bbr-online/bbr-online-gem/app/views]>
The last path of the mountable engine should come before the middle devise path. The solution is setting the loading priority in application.rb like this:
#test/dummy/config/application.rb (the app that uses my mountable engine)
...
config.railties_order = [Blog::Engine, :main_app, :all]
...
This is also documented in the rails api: http://api.rubyonrails.org/classes/Rails/Engine.html#label-Loading+priority
Thanks to José Valim for pointing in the right direction.
I need more information. Which controller are you defining and from which controller is it inheriting from? Which view is being rendered and which one did you expect to render? Also, .view_paths is your friend so try in your rails console the following:
Devise::SessionsController.view_paths
YourApp::SomeDeviseController.view_paths
This will give you a better idea of where each controller is searching for templates.