Subdomain routing in Rails and main site - ruby-on-rails

I am trying to set the following up:
www.domain.com goes to the main site (user can register, information about application)
foo.domain.com goes to the main application that is customized to the user(subdomain)
what is the best way to separate the rails parts? Namespaced controllers seem frowned upon.

Don't separate them. Just use a before_filter to require login and a subdomain on the controllers that requires a subdomain/client to be present.
class ApplicationController < ActionController::Base
private
def require_subdomain_scope
# check if request.subdomains is blank or www. Something like that.
end
end
class StaticPagesController < ApplicationController
# no before_filter!
end
class ProjectsController < ApplicationController
before_filter :require_login, :require_subdomain_scope
end

Related

Rails Controller Namespace Inheritance Missing Layout

Using a namespaced controller and the app's Application layout isn't being loaded.
# controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
end
# controllers/servers/base_controller.rb
module Servers
class BaseController < ApplicationController
def initialize
# I noticed params are not accessible here either...
# Only in the child controller.
end
end
end
# controllers/servers/test_controller.rb
module Servers
class TestController < BaseController
def index
end
end
end
A pretty basic setup here. Anytime I route to /servers/:id it loads the page with the parameter, but the application layout does not load. I have a series of a couple controllers I'd like to all inherit from this BaseController to initialize a few things. I also noticed that besides the layout issue, the BaseController doesn't have access to url parameters. The TestController does though... Can anyone explain these two issues I'm seeing?
Rails 5.1.4
ruby 2.4.1p111
Figured out my issue. Don't use an initialize method in your class. Rails doesn't like that. Use before_action instead.

Two devise models, two application controllers, one controller

So I am running into a problem where I have two separate devise models (Admin and User), and because I am using a multi tenancy gem called Milia I need to two application controllers (One for the Admin which is outside of the tenancy and one for the User which is inside the tenancy. The problem is I need to access one controller from both of those devise models, but I can only inherit that controller from one of the below application controllers. Is there a way around this where I can specify which user should use which application controller when hitting the controller for a particular resource? Or is there another way around this problem?
The two application controllers look like this
class AdminApplicationController < ActionController::Base
before_action :authenticate_admin!
end
class ApplicationController < ActionController::Base
before_action :authenticate_tenant!
end
Give you some inspiration :)
class ApplicationController < ActionController::Base
before_action :authenticate_them
def authenticate_them
authenticate_tenant!
end
end
class AdminApplicationController < ApplicationController
before_action :authenticate_them
def authenticate_them
authenticate_admin!
end
end
class AnotherController < ApplicationController
before_action :authenticate_them
def authenticate_them
if admin_signed_in? or tenant_signed_in?
#or whatever other authentication you want
end
end
end

Rails module scope

Given the following controller structure:
# application_controller.rb
class ApplicationController < ActiveController::Base; end
# pages_controller.rb
class PagesController < ApplicationController; end
# admin/application_controller.rb
module Admin
class ApplicationController < ::ApplicationController; end
end
# admin/pages_controller.rb
module Admin
class PagesController < ApplicationController; end
end
One would expect Admin::PagesController to inherit from Admin::ApplicationController and it does. But I have noticed that sometimes it inherits from ::ApplicationController.
So I decided not to risk it and changed declaration of all controllers in /admin to specifically target Admin::ApplicationController
# admin/pages_controller.rb
module Admin
class PagesController < Admin::ApplicationController; end
end
Okay that works, but from what I know it was correct in the first place. Why Rails inherits from a wrong controller sometimes?
Admin::PagesController sometimes inherits from ApplicationController instead of Admin::ApplicationController despite both being in the same module Admin
The problem here is rails' development mode code loading: in general code is loaded when you try to do something with a constant (eg subclass from it) and that constant doesn't exist. This results in const_missing being called and rails uses it this to try to load the class (for a detailed description see the guide).
If neither ApplicationController nor Admin::ApplicationController exist then when you access your admin pages controller ruby will hit that const_missing and try to load admin/application_controller.rb
However if ApplicationController is already loaded then ruby won't fire const_missing since it perfectly legal for a class in the admin module to inherit from something at the toplevel.
The solution as you say is to make explicit what you are inheriting from. Personally in my own apps I use Admin::BaseController as the base class.
Another option is to use require_dependency to point Rails to the correct file:
# admin/application_controller.rb
require_dependency 'admin/application_controller'
module Admin
class PagesController < ApplicationController
end
end

ruby on rails best place to put layout database code (common database calls)

I'm making a control panel (user accounts) in rails.
in the layout I need to show things like messages or notifications (facebook-like style).
the problem is these things require an access to database and I'm not sure where to put this code because it's not related to a controller, but the layout is shared with multiple controlers.
so where is the best place to put the code to fetch messages from database should I put in the layout itself (I don't think its right), or as helper ?
the best solution was to build a control-panel controller which handles authentication and permissions and loads common user data from database such as messages...
here is an example code
class ControlPanelController < ApplicationController
before_filter :authenticate_user!
before_filter :get_user_data
helper_method :mailbox
authorize_resource
protected
def get_user_data
#header_conversations=mailbox.inbox.limit(3)
#uevents= Event.scoped
#uevents= #uevents.after(Time.now)
end
def mailbox
#mailbox ||= current_user.mailbox
end
end
and then all the classes in my web application extends this class :)
I found a way is to use before_filter. by defining the filter in the ApplicationController (so that you can access it from any controller).
class ApplicationController < ActionController::Base
# ..
protected
def load_messages
#messages = Message.all
end
end
and then in the any controller:
class FooController < ApplicationController
before_filter :load_messages
def index
# #messages is set
end
end

How to configure Devise+Cancan correctly for guest users

In a Rails 3.2 app I'm using Devise + CanCan. The app previously restricted access to only logged in users. I'm in the process of adding a Guest user/ability that will be able to read certain sections of the site.
I'm having trouble understanding the "correct" way to set this up, specifically what combination of before_filter :authenticate! and load_and_authorize_resource is needed in controllers.
While working on this I've stripped the ability class to a minimum.
#Ability.rb
class Ability
include CanCan::Ability
def initialize(user_or_admin)
user_or_admin ||= User.new
can :manage, :all
end
end
In a model-less/ static page Home controller
#home_controller.rb
class HomeController < ApplicationController
load_and_authorize_resource
def index
...some stuff
end
end
and
#application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :authenticate!
...more stuff
end
With this set up, un-logged-in users are redirected to Devise sign in page.
If I remove before_filter :authenticate! from the application controller I get an error uninitialized constant Home from activesupport-3.2.11/lib/active_support/inflector/methods.rb
If I remove load_and_authorize_resource from the home controller, this error goes away.
This is ok with my simplified testing Ability class, but as I start adding roles and abilities back in I will need to have CanCan handling the Home controller, i.e., will need load_and_authorize_resource to be called.
Can anyone help me understand why this error occurs when before_filter :authenticate! is removed, and point me towards any info that explain the "correct" way to set up Devise+Cancan for guest users. The info I've found thus far only explains how to set up the Ability class, not how to configure Devise.
The problem is that there is no resource to authorize. Therefore, you need only call authorize_resource not load_and_authorize_resource. See authorizing controller actions in the cancan documentation for further information.
Update: You must also specify the class as false: authorize_resource class: false.
Then your home controller will look like this:
class HomeController < ActionController::Base
authorize_resource class: false
def show
# automatically calls authorize!(:show, :home)
end
end
This information is in the Non-RESTful controllers section. Sorry about that.

Resources