Rails 4 subdomains, tied to controller - ruby-on-rails

grateful to have you guys as a resource! I think this should be a simple question but haven't been able to find a simple answer through searching yet, any help/guidance would be appreciated!!
I have a "subdomain" controller which is setup in the following way:
get 'subdomain/:store' => 'subdomain#index'
get 'subdomain/:store/products' => 'subdomain#product_index'
get 'subdomain/:store/products/:id' => 'subdomain#products_show'
As you can see, the subdomain controller matches the request with a Store ID and can also get an index of all the associated products with the Store ID. I'd like to somehow convert each of these requests into a subdomain rather than a path. Each Store has a "subdomain" attribute (in the example below, one of the Store records has a subdomain value of "nike").
For example
host.com/subdomain/nike => nike.host.com
host.com/subdomain/nike/products => nike.host.com/products
host.com/subdomain/nike/products/5 => nike.host.com/products/5
Notice the controller "subdomain" was removed from the path. Any help? I looked into gems such as apartment but they look like they are way too complex for this. Also subdomain-fu but it looks like it's outdated for Rails 4. Thoughts? THANKS!

For this, you can add routing Constraint.
Add the file to lib/subdomain_required.rb
class SubdomainRequired
def self.matches?(request)
request.subdomain.present? && request.subdomain != 'www'
end
end
Then, in your routes.rb, you can enclose your routes into a contraint block, somewhat like this:
constraints(SubdomainRequired) do
get '/' => 'subdomain#index'
get '/products' => 'subdomain#product_index'
get '/products/:id' => 'subdomain#products_show'
end
Now the last step is to load the store based on subdomain which can be done using a before_action like this
class SubdomainController < ActionController::Base
before_action :ensure_store!
def index
#products = current_store.products.all
end
def ensure_store!
#store ||= Store.find_by subdomain: request.subdomain
head(:not_found) if #store.nil?
#store
end
def current_store
#store
end
end
now anywhere you want to get the store, you can use current_store helper method.
Hope it helps

Related

(Rails) How to get 'id' out of edit url

I have a model called studies.
After action redirect redirect_to edit_study_path(#new_study),
URL: http://localhost:3000/studies/2/edit.
Is there anyway to customize an url after passing id ?
For example, http://localhost:3000/study
(still going to the edit path, and still with the :id in the params)
I guess what you want is to edit the current study?
In this case, it's possible, using ressource instead of ressources in the routes.
Let's have an example:
#in routes.rb
resources :studies
resource :study
Both of them will by default link to the StudiesController and call the same actions (eg. edit in your case) but in two different routes
get "/studies/:id/edit" => "studies#edit"
get "/study/edit" => "studies#edit"
in your edit action, you should then setup to handle correctly the parameters:
def edit
#study = params[:id].nil? ? current_study : Study.find(params[:id])
end
Note you need a current_study method somewhere, and store the current_study in cookies/sessions to make it works.
Example:
# In application_controller.rb
def current_study
#current_study ||= Study.find_by(id: session[:current_study_id]) #using find_by doesn't raise exception if doesn't exists
end
def current_study= x
#current_study = x
session[:current_study_id] = x.id
end
#... And back to study controller
def create
#...
#Eg. setup current_study and go to edit after creation
if study.save
self.current_study = study
redirect_to study_edit_path #easy peesy
end
end
Happy coding,
Yacine.

Redirect all non-model-named urls to named ones in Rails

I've got an app where I'm overriding Rails' def to_param method to include the model name like so: www.myapp.com/1-some-model-name, however when you go to www.myapp.com/1 you get the same page with the same content, but these are two different URLs. What's the best way to do a redirect so that everytime I hit www.myapp.com/1 I go to www.myapp.com/1-some-model-name?
Thanks and any advice is much appreciated.
I've tried solutions found here and elsewhere, but can't get my app to work properly. (I got the error Error 310 (net::ERR_TOO_MANY_REDIRECTS))
I've make simple solution, but this solution works well enough.
If you are using to_param like this :
def to_param
[id, name.parameterize].join("-")
end
You just add some condition to action on controller looks like :
def show
#user = User.find(params[:id])
formal = #user.to_param
if formal != params[:id]
# on routes : match "/user/:id" => "users#show", :as => :show_user
redirect_to show_user_path([#user.id, #user.firstname.parameterize].join("-"))
end
end

Rails Routes based on condition

I have three roles: Instuctor, Student, Admin and each have controllers with a "home" view.
so this works fine,
get "instructor/home", :to => "instructor#home"
get "student/home", :to => "student#home"
get "admin/home", :to => "admin#home"
I want to write a vanity url like below which will route based on the role of the user_id to the correct home page.
get "/:user_id/home", :to => "instructor#home" or "student#home" or "admin#home"
How do I accomplish this?
I'm providing an alternate approach as this SO question comes up near the top when searching for role based routing in Rails.
I recently needed to implement something similar but wanted to avoid having a large number of conditionals in the controller - this was compounded by the fact that each of my user roles required completely different data to be loaded and presented. I opted to move the deciding logic to the routing layer by using a Routing Constraint.
# app/constraints/role_route_constraint.rb
class RoleRouteConstraint
def initialize(&block)
#block = block || lambda { |user| true }
end
def matches?(request)
user = current_user(request)
user.present? && #block.call(user)
end
def current_user(request)
User.find_by_id(request.session[:user_id])
end
end
The most important part of the above code is the matches? method which will determine whether or not the route will match. The method is passed the request object which contains various information about the request being made. In my case, I'm looking up the :user_id stored in the session cookie and using that to find the user making the request.
You can then use this constraint when defining your routes.
# config/routes.rb
Rails.application.routes.draw do
get 'home', to: 'administrators#home', constraints: RoleRouteConstraint.new { |user| user.admin? }
get 'home', to: 'instructors#home', constraints: RoleRouteConstraint.new { |user| user.instructor? }
get 'home', to: 'students#home', constraints: RoleRouteConstraint.new { |user| user.student? }
end
With the above in place, an administrator making a request to /home would be routed the home action of the AdministratorsController, an instructor making a request to /home would be routed to the home action of the InstructorsController, and a student making a request to /home would be routed to the home action of the StudentsController.
More Information
If you're looking for more information, I recently wrote about this approach on my blog.
You can't do this with routes because the routing system does not have the information required to make this decision. All Rails knows at this point of the request is what the parameters are and does not have access to anything in the database.
What you need is a controller method that can load whatever data is required, presumably the user record, and redirects accordingly using redirect_to.
This is a fairly standard thing to do.
Update:
To perform all of this within a single controller action you will need to split up your logic according to role. An example is:
class HomeController < ApplicationController
def home
case
when #user.student?
student_home
when #user.admin?
admin_home
when #user.instructor
instructor_home
else
# Unknown user type? Render error or use a default.
end
end
protected
def instructor_home
# ...
render(:template => 'instructor_home')
end
def student_home
# ...
render(:template => 'student_home')
end
def admin_home
# ...
render(:template => 'admin_home')
end
end

How to set up routes to show additional information in the URL using namespaces?

I am running Ruby on Rails 3 and I would like to set up my routes to show additional information in the URL using namespaces.
In the routes.rb file I have:
namespace "users" do
resources :account
end
So, the URL to show an account page is:
http://<site_name>/users/accounts/1
I would like to rewrite/redirect that URL as/to
http://<site_name>/user/1/Test_Username
where "Test_username" is the username of the user. Also, I would like to redirect all URLs like
# "Not_real_Test_username" is a bad entered username of the user.
http://<site_name>/users/accounts/1/Not_real_Test_username
to the above.
At this time I solved part of my issuelike this:
scope :module => "users" do
match 'user/:id' => "accounts#show"
end
My apologies for not answering your question (#zetetic has done that well enough), but the best practice here is to stay within the RESTful-style Rails URL scheme except for rare exceptions. The way most people make pretty URLs in this way is to use a hyphen, e.g.:
/accounts/1-username
This does not require any routing changes. Simply implement:
class Account < ActiveRecord::Base
def to_param
"#{self.id}-#{self.username}"
end
end
And handle the extra string data in your finds by calling to_i.
class AccountController < ApplicationController
def show
#account = Account.find(params[:id].to_i)
end
end
When you do link_to 'Your Account', account_path(#account), Rails will automatically produce the pretty URL.
It's probably best to do this in the controller, since you need to retrieve the account to get the username:
#account = Account.find(params[:id])
if #account && #account.username
redirect_to("/user/#{#account.id}/#{#account.username}")
return
end
As to the second issue, you can capture the remaining parameter by defining it in the route:
get "/users/accounts/:id(/:other)" => "users/accounts#show"
This maps like so:
/users/accounts/1/something # => {:id => "1", :other => "something"}
/users/accounts/1 # => {:id => "1"}
And you can simply ignore the :other key in the controller.

Rails route dependent on current user

I'd like to create a rails route for editing a user's profile.
Instead of having to use /users/:id/edit, I'd like to have a url like /edit_profile
Is it possible to create a dynamic route that turns /edit_profile into /users/{user's id}/edit, or should I do thing in a controller or?
You might want to create a separate controller for this task but you could also continue using users_controller and just check whether there is a params[:id] set:
def edit
if params[:id]
#user = User.find(params[:id])
else
#user = current_user
end
end
But you should note that /users normally routes to the index action and not show if you still have the map.resources :users route. But you could set up a differently called singular route for that:
map.resources :users
map.resource :profile, :controller => "users"
This way /users would list all the users, /users/:id would show any user and /profile would show the show the currently logged in users page. To edit you own profile you would call '/profile/edit'.
Since a route and controller serve two different purposes, you will need both.
For the controller, assuming you're storing the user id in a session, you could just have your edit method do something like:
def edit
#user = User.find(session[:user_id])
end
Then have a route that looks something like:
map.edit_profile "edit_profile", :controller => "users", :action => "edit"
This route would give you a named route called edit_profile_path
Tomas Markauskas's answer could work, but here's the answer to your question from the Rails Guide:
get 'edit_profile', to: 'users#edit'
So, when someone goes to www.yoursite.com/edit_profile, it will route to www.yoursite.com/users/edit.
Then, in your controller you can access the user with
#user = User.find(session[:current_user_id])
Assuming you set that session variable when someone logs in. Also, don't forget to check if they're logged in. This will work if your using Resourceful Routing (the Rails default) or not.
Source: http://guides.rubyonrails.org/routing.html
make the route as
get '/users/:id/edit', to: 'users#edit', as: 'edit_profile'
As explained in this link section 'The hard way' :
http://augustl.com/blog/2009/styling_rails_urls/
The url will be
/users/edit_profile
Because the ID is no longer in the URL, we have to change the code a bit.
class User < ActiveRecord::Base
before_create :create_slug
def to_param
slug
end
def create_slug
self.slug = self.title.parameterize
end
end
When a user is created, the URL friendly version of the title is stored in the database, in the slug column.
For better understanding read the link below
http://blog.teamtreehouse.com/creating-vanity-urls-in-rails
write it in any home controler.
def set_roots
if current_user
redirect_to dashboard_home_index_path
else
redirect_to home_index_path
end
end
in routes.rb file
root :to => 'home#set_roots'
match "/find_roots" => "home#set_roots"

Resources