Rails namespace admin on custom subdomain - ruby-on-rails

My rails app is set to use subdomains as described in this RailsCast:
http://railscasts.com/episodes/221-subdomains-in-rails-3
Now, I would like to add an admin subdomain to the front of my blog subdomain as follows:
admin.company.lvh.me:3000
I've tried to namespace admin outside of my Subdomain constraint:
namespace :admin, path: '/', constraints: { subdomain: 'admin' } do
constraints(Subdomain) do
match '/', to: 'blogs#show', via: 'get'
end
end
But instead of routing through my app/controllers/admin/blogs_controller it attempts to route through my "normal user" controller (app/controllers/blogs_controller).
Am I just missing something simple or is doing something like this in rails much more difficult?

I was able to solve this, although it feels a little hackish. Understanding that Rails treats constraints either true or false, I set another constraint inside the initial subdomain constraint check. It splits the subdomain in 2 and examines the first subdomain to see if it equals "admin". If true, it routes to the admin/controllers and admin/views (because of module: "admin"), if not, it routes to the less specific routes that are not inside the "admin" module.
At first I didn't have the namespace :admin, and my route helpers were incorrect (the admin routes weren't prefixed with "admin" and the less specific routes weren't being set since they were duplicates). Once I added namespace :admin and the path: "" (this is important, too, because it removes "admin/" from the URI Pattern), it worked!
One last thing, in the admin/controllers, you have to edit the set_blog method, since "admin.company" is being interpreted instead (see admin/blogs_controller.rb).
routes.rb
Blog::Application.routes.draw do
constraints(Subdomain) do
namespace :admin, module: "admin", path: "", constraints: lamda { |r| r.subdomain.split('.')[0] == 'admin' } do
match '/', to: 'blogs#show', via: 'get'
...
end
match '/', to: 'blogs#show', via: 'get'
...
end
...
end
Rake Routes:
Prefix Verb URI Pattern Controller#Action
admin GET / admin/blogs#show
...
​ GET / blogs#show
...
admin/blogs_controller.rb
BlogController < ApplicationController
before_action :set_blog
...
private
set_blog
#blog = Blog.find_by_subdomain!(request.subdomain.split('.')[1])
end
end
Let me know if there's anything cleaner out there, if not, hopefully this helps others with this issue.

There are several important factors here
Firstly, you'll need to see what the constraint params look like with "multi" subdomains. Instead of splitting, Rails may have admin.company as the subdomain
If we take the idea that Rails will split the subdomains into two, which one is being called as the "parent"?
namespace :admin, path: '/', constraints: { subdomain: 'admin' } do
constraints(Subdomain) do
resources :blogs, only: :show, path_names: { show: "" }
end
end
If you give us some more info on the request (params etc), we'll be in a much better position to help!

Related

allow access to one page on sub-domain, and if tried accessing other pages redirect to main domain

allow access to one page(campaigns/test) on sub-domain(aap.example.com), and if tried accessing other pages redirect to main domain(example.com)
app.example.com is my sub-domain of my main domain example.com, so same rails app is running on domain and sub-domain.
I tried some solutions like adding constraints in routes file and nginx file as well, but not getting what I need.
routes.rb
constraints(Subdomain) do
match '/campaigns/:slug' => 'campaigns#show', via: [:get]
match "/cam_enquiry" => "campaigns#cam_enquiry", via: [:post]
end
and the subdomain.rb module
class Subdomain
def self.matches?(request)
case request.subdomain
when 'app', '', nil
false
else
true
end
end
end
Let me know if you need anymore details.
Define all your routes and after it define constraint for app subdomain. You need to match test route and use redirect for all other subdomain routes. Specify only blank subdomain in redirect parameters, in this case it will get all other info from request and only change subdomain to main domain
get 'campaigns/test', to: redirect(subdomain: 'app'), constraints: { subdomain: '' }
get '/campaigns/:slug', to: 'campaigns#show'
post '/cam_enquiry', to: 'campaigns#cam_enquiry'
constraints subdomain: 'app' do
get 'campaigns/test', to: 'contoroller#action'
get '/*any', to: redirect(subdomain: '')
end

Rails routes: Nested, Member, Collection, namespace, scope and customizable

I am trying to understand more about Rails routes.
Member and Collection
# Example resource route with options:
resources :products do
member do
get 'short'
post 'toggle'
end
collection do
get 'sold'
end
end
Namespace and Scope
# Example resource route within a namespace:
namespace :admin do
resources :products
end
scope :admin do
resources :products
end
Constraints, Redirect_to
# Example resource route with options:
get "/questions", to: redirect {|params, req|
begin
id = req.params[:category_id]
cat = Category.find(id)
"/abc/#{cat.slug}"
rescue
"/questions"
end
}
Customization:
resources :profiles
original url from resource profiles for edit.
http://localhost:3000/profiles/1/edit
I want to make it for users available only through click edit profile and see url like in below.
http://localhost:3000/profile/edit
Also, is there advanced routing, How most big companies design their routes in rails ? I would be really glad to see new kind of routes if there exist.
Thank You !
**Collection & Member routes**
A member route requires an ID, because it acts on a member.
A collection route doesn't require an ID because it acts on a
collection of objects
:member creates path with pattern /:controller/:id/:your_method
:collection creates path with the pattern /:controller/:your_method
For example :
map.resources :users, :collection => { :abc => :get } => /users/abc
map.resources :users, :member => { :abc => :get } => /users/1/abc
**Scopes & Namespaces routes**
namespace and scope in the Rails routes affect the controller
names, URIs, and named routes.
The scope method gives you fine-grained control:
scope 'url_path_prefix', module: 'module_prefix', as: 'named_route_prefix' do
resources :model_name
end
For Example :
scope 'foo', module: 'bar', as: 'baz' do
resources :posts
end
produces routes as :
Prefix Verb URI Pattern Controller#Action
baz_posts GET /foo/posts(.:format) bar/posts#index
POST /foo/posts(.:format) bar/posts#create
new_baz_post GET /foo/posts/new(.:format) bar/posts#new
edit_baz_post GET /foo/posts/:id/edit(.:format) bar/posts#edit
baz_post GET /foo/posts/:id(.:format) bar/posts#show
PATCH /foo/posts/:id(.:format) bar/posts#update
PUT /foo/posts/:id(.:format) bar/posts#update
DELETE /foo/posts/:id(.:format) bar/posts#destroy
The namespace method is the simple case — it prefixes everything.
namespace :foo do
resources :posts
end
produces routes as :
Prefix Verb URI Pattern Controller#Action
foo_posts GET /foo/posts(.:format) foo/posts#index
POST /foo/posts(.:format) foo/posts#create
new_foo_post GET /foo/posts/new(.:format) foo/posts#new
edit_foo_post GET /foo/posts/:id/edit(.:format) foo/posts#edit
foo_post GET /foo/posts/:id(.:format) foo/posts#show
PATCH /foo/posts/:id(.:format) foo/posts#update
PUT /foo/posts/:id(.:format) foo/posts#update
DELETE /foo/posts/:id(.:format) foo/posts#destroy
**Constraints & Redirect**
Rails routes are executed sequentially, you can mimic conditional
login in the following manner:
match '/route' => 'controller#action', :constraints => Model.new
match '/route' => 'user#action'
The first line checks whether the conditions of the constraint are met (i.e., if the request is emanating from a Model domain). If the constraint is satisfied, the request is routed to controller#action.
We can add constraints to routes for multiple uses like for ip-matching, params matching, restrict format parameter, request-based restrictions etc as :
- ip-matching
=> resources :model, constraints: { ip: /172\.124\.\d+\.\d+/ }
- filtering id params
=> match 'model/:id', to: 'model#show' ,constraints: { id: /\d+/}, via: :get
- restrict format params
=> match 'model/:id', to: 'model#show' ,constraints: { format: 'json' }, via: :get
- request-based constraints
=> get 'admin/', to: 'admin#show', constraints: { subdomain: 'admin' }
Use a singular resource for it:
resource :profile
and in controller manipulate the profile of current user.
As for complex routes - usually namespaces, nested resources with shallow routes and custom actions are all that is needed.
You can go through this answer which answers you first part of the question.
To answer second part of your question. You can treat "profile" as your singular resource (the singularity of the noun itself represents a singular resource). For a detailed description you can refer to this link.

Getting 'Unknown action in controller'

I'm getting this error :
"The action 'create' could not be found for ObjectController"
I know it should be obvious but I'm missing something, that's my controller :
class ObjectController < ApplicationController
def index
end
def create
end
end
And that is my routes :
Rails.application.routes.draw do
get 'object/index'
get 'object/create'
match ':controller(/:action(/:id))', :via => :get
resources :objets
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
root 'object#index'
You probably want to scrap those routes and try something simpler like
resources :objects, only: [:get, :create, :show]
Then use
$ rake routes
To make sure your routes are as the should be. You will want a POST route to /objects to create a new object etc..
Ok that one was dumb, actually I had two directories and I wasn't modifying the right one, sorry about that...
Your routes could be greatly improved:
#config/routes.rb
Rails.application.routes.draw do
root 'objects#index'
resources :objects
--
Next, the "standard" way to achieve what you're looking for is to use the new action; IE not the "create" action. If you wanted to use the create path name (instead of new), you'll be able to define it in the path_names argument:
#config/routes
resources :objects, path_names: { new: "create", create: "create" } #-> url.com/objects/create
To understand why you should be using new instead of create, you should look up resourceful routing, and how it pertains to object orientated programming.
Finally, your controller should be named in the plural:
#app/controllers/objects_controller.rb
class ObjectsController < ApplicationController
...
end
Whilst you can call it whatever you like, Rails defaults to plural controller names, singular model names.

add subdomain if namespace present

Not sure if my topic title is correct, but here is my question
I have namespace called :admin, so it looks like mysite.com/admin. In this section i have some links, that pointing to controllers inside this namespace. But since we have subdomain admin, and my namespace :admin as well, i'd like to all links that are being generated by routes.rb to prepend string admin., so the link would look like admin.mysite.com/admin/some_other_path
I've tried to add constraints to routes.rb, but that didn't work for me
But since we have subdomain admin, and my namespace :admin as well,
i'd like to all links that are being generated by routes.rb to prepend
string admin.
Routes
In your routes, you should have this:
constraints({ subdomain: "admin" }) do
namespace :admin do
# routes here
end
end
If you wanted to have no path for your admin namespace (I.E admin.domain.com/some_other_path), you can do this:
constraints({ subdomain: "admin" }) do
namespace :admin, path: "" do
# routes here
end
end
--
URL
When using URLs, you have to use the _url helpers (not _path). We literally just discovered this yesterday - the _path helpers only work to append relative paths to your url; the _url gives you a totally fresh url
This means if you have a route as follows:
admin_root_path "admin/application#index, constraints => {subdomain: "admin"}
You'll call this with this route helper:
<%= link_to "Admin", admin_root_url %>
This will prepend the required subdomain for you when calling links etc
you can do:
constraints subdomain: 'admin' do
namespace :admin do
# ...
end
end
Define routes in routes.rb under admin namespace like this
namespace :admin, path: '/', constraints: { subdomain: 'admin' } do
constraints(Subdomain) do
# your routes
end
end
Routes defined under this block will always come under admin in link, e.g., /admin/some_other_path
To add subdomain to the admin namespace take a look at this question
Rails namespace admin on custom subdomain

Exclude all other resources from subdomain in Rails

I have a Rails 4.0 app with that allows users to access blogs through subdomains. My routes currently look like this:
match '', to: 'blogs#show', via: [:get, :post], constraints: lambda { |r| r.subdomain.present? && r.subdomain != 'www' }
resources :foobars
Now, when I navigate to somesubdomain.example.com I am indeed taken to the showaction of the blogs controller action, as expected.
When I navigate to example.com/foobars I can access the index action of the foobars controller, as expected.
However, I only get a behavior I do not desire:
When I navigate to somesubdomain.example.com/foobars, I can still access the the index action of foobars controller.
Is there a way to limit or exclude all resources that I do not specifically allow for a particular subdomain (i.e. somesubdomain.example.com/foobars will not work unless otherwise specified).
Thanks!
If you need to define a specific subdomain to exclude from a set of routes you can simply do this (uses negative lookahead regex):
# exclude all subdomains with 'www'
constrain :subdomain => /^(?!www)(\w+)/ do
root to: 'session#new'
resources :foobars
end
Or similarly, to define a specific subdomain to include a set of routes you could do this:
# only for subdomain matching 'somesubdomain'
constrain :subdomain => /^somesubdomain/ do
root to: 'blog#show'
resources :foobars
end
Another approach would be to define the constraint match in a class (or module) and then wrap all routes within constraints block:
class WorldWideWebSubdomainConstraint
def self.matches?(request)
request.subdomain.present? && request.subdomain != 'www'
end
end
App::Application.routes.draw do
# All "www" requests handled here
constraints(WorldWideWebSubdomainConstraint.new) do
root to: 'session#new'
resources :foobars
end
# All non "www" requests handled here
root to: 'blogs#show', via: [:get, :post]
end

Resources