I"m trying to create a rails app that functions as a url shortener. I'm having trouble configuring the routes. How can I allow for the user visit my site and be redirected to the site base on the url they enter. I.E. mysite.com/any_random_url.
routes.rb
Rails.application.routes.draw do
get 'home/index'
get 'home/about'
get 'home/:id' => 'home#show'
root 'home#show/:id'
..
home_controller.rb
class HomeController < ApplicationController
def index
end
def about
end
def show
url = params[:id]
#url = ShortUrl.where(["url = ?", url]).first
if #url.nil?
return redirect_to action: 'index', status: 307
else
return redirect_to #url
end
end
If you want to be able to have multiple slashes you'll need something like:
get '*id', to: 'home#show'
If you only want a single subpath (i.e. /23af1) it's probably better to use:
get ':id', to: 'home#show'
You can find more info in the Rails Guide
Related
I have a route with my JWT token as a path variable but it doesn't work with the JWT token eyJhbGciOiJub25lIn0.eyJjb25maXJtYXRpb25fc2VudF9hdCI6IjIwMTgtMDEtMjQgMDY6MDQ6MzEgKzEzMDAiLCJleHBpcmVzX2F0IjoiMjAxOC0wMS0yNyAwNjowNDozMSArMTMwMCIsInVzZXJfaWQiOjM5fQ.
When I switch the JWT token to something less complicated e.g. 5 then the route works. I'm guessing that the format of the JWT need some special treatment in the rails router?
get '/verify/:jwt', to: 'users#verify_email'
No route matches [GET] "/verify/eyJhbGciOiJub25lIn0.eyJjb25maXJtYXRpb25fc2VudF9hdCI6IjIwMTgtMDEtMjQgMDY6MDQ6MzEgKzEzMDAiLCJleHBpcmVzX2F0IjoiMjAxOC0wMS0yNyAwNjowNDozMSArMTMwMCIsInVzZXJfaWQiOjM5fQ
routes.rb
Rails.application.routes.draw do
extend DeviseRoutes
extend PageRoutes
# Root route
root to: "pages#home"
end
devise_routes.rb
module DeviseRoutes
def self.extended(router)
router.instance_exec do
devise_for :users, path: '', path_names: {
sign_in: 'login',
sign_out: 'logout',
sign_up: 'register',
edit: '/user/edit'
}, controllers: { registrations: 'users' }
# User profile management
devise_scope :user do
get '/profile/:id', to: 'users#profile_home', as: 'profile'
# Verify email
get '/verify', to: 'users#verify_email'
end
end
end
end
users_controller
class UsersController < Devise::RegistrationsController
include AuthenticationConcern
require 'utilities/custom_mailer'
require 'jwt'
def profile_home
#user_id = params[:id]
check_user_route_access current_user, #user_id
#user = User.includes(:skills).find_by(id: #user_id)
#skills = #user.skills.eager_load(:profession)
end
def create
super
if current_user
CustomMailer.send_initial_user_signup(user_id: current_user.id,
to_email: current_user.email,
user_full_name: current_user.full_name)
end
end
def verify_email
jwt_token = params[:token]
#jwt_token_decoded = true
# make sure the jwt_token can be decoded, if not crash softly via
# error on the page rather than hard crash
begin
decoded = (JWT.decode jwt_token, nil, false)[0]
rescue
#jwt_token_decoded = false
end
if #jwt_token_decoded
decoded_expires_at = decoded["expired_at"]
user_id = decoded["user_id"]
#user = User.find_by(id: user_id)
# 1. if user is verified, redirect to login page
if #user != nil and #user.confirmed_at != nil
# flash[:success] = t('successfully_created')
redirect_to new_user_session_path
end
# 2. once verified, provide option in view to go to login page
end
# render verification page
end
end
Does anyone have any suggestions?
In your route the JWT consists of various special characters like '.' because of which the rails router is unable to route it properly.
Solution :-
1:- Use route globbing using wildcard segments
Example :-
Change you route to this
get '/verify/*jwt', to: 'users#verify_email', constraints: { jwt: /.*/ }
This will give params[:jwt] = "passed jwt in url"
You can customize the regex used here to make it more meaningful for jwt token which consists of two '.' and other special characters.
Read this for more information :- Rails route globbing and wildcard segments
The most probable cause of this issue is that you have another route before than get '/verify/:jwt', to: 'users#verify_email'. Rails Router gives priority to the first instance it finds.
So if for example, your routes.rb looks like this:
resources :verify # this may not be your case, just an example
get '/verify/:jwt', to: 'users#verify_email'
In this case, rails will ignore get line, and whenever you GET /verify/<anything> will be routed to verify#show
On the other hand, if you swap those lines like this,
get '/verify/:jwt', to: 'users#verify_email'
resources :verify # this may not be your case, just an example
Then every GET /verify/<anything> will be routed to verify_email.
could one advise me how to get a url like this in rails
http://www.example.com/users/5/ian
i tried the below but unsure:
route file:
devise_for :users
resources :users do
resources :socials
end
get '/users/:id/:firstname', controller: 'users', action: 'show'
users_controller.rb
def show
#user = User.find(params[:id], params[:firstname])
end
If you are trying to achieve 'friendly urls' then I suggest using this:
You don't have to create a special route:
get '/users/:id', controller: 'users', action: 'show'
Instead you have your model overwrite the to_param method:
class User
...
def to_param
"#{id}-#{firstname.try(:parameterize)}"
end
...
end
The url helper calls to_param to build the urls. If you overwrite it this way, you will receive a url like this:
http://localhost:3000/users/1-artloe
The rails find method calls .to_i on the params[:id] which, thankfully, interprets strings as number until it arrives at a character that can't become a number.
Examples:
'123abcde'.to_i # 123
'123-asdf'.to_i # 123
'asdf-123'.to_i # 0
So except for overwriting to_param, you don't have to do anything.
Try replacing this
def show
#user = User.find_by_id_and_firstname(params[:id], params[:firstname])
end
If what you are trying accomplish is "friendly urls" you would do it by:
# GET /users/1
# GET /users/joe
def show
#user = User.find_by!('id = :x OR firstname = :x', x: params[:id])
end
However you must ensure that property you are using in URLs is URL safe and unique. Usually a separate username or slug field is used.
Nothing special is needed in terms of routes.
These gems provide "friendly urls":
stringex
friendly_id
In my Rails routes.rb file I'm wanting to do something like the following.
get '/:id' => 'pages#show'
get '/:id' => 'articles#show'
So that if a visitor types in
http://www.example.com/about-this-site
The pages controller in the above example would get first shot at handling it. Then if not, the next controller in line would get a shot.
REASONs for wanting to do this:
1) I'm trying to port my Wordpress site over without establishing new urls for all my pages and blog posts. As it stands, all of my blog post files and pages are accessed directly off the root uri '/' folder.
2) Because I'm not able to, it's a learning thing for me. But, I want to do it without a hack.
How about redirecting to the second controller from your first controller?
in PagesController
def show
unless Page.find_by(id: params[:id])
redirect_to controller: :articles, action: :show, id: params[:id]
end
end
in ArticlesController
def show
# Handle whatever logic here...
end
Edit
If you really don't want to redirect then you can consolidate the logic into a single action:
def show
if Page.find_by(id: params[:id])
render :show
elsif Article.find_by(id: params[:id])
render controller: :articles, action: :show
else
# Handle missing case, perhaps a 404?
end
end
However, I'd recommend using a redirect if possible. It's a cleaner solution and keeps your controller code isolated.
I have a preview page up with a form that takes in emails(#premails). I've created a model & migration for this.
I have a pages controller with a Home, About & Contact actions and corresponding views.
After they submit their email on the Home page, I want to redirect them to a static About page. I have not been able to achieve this.
this is my pages controller:
class PagesController < ApplicationController
def home
#premail = Premail.new
if #premail.save
redirect_to about_path
else
render home_path
end
end
def about
end
end
But when I open my localhost with this code I get:
NameError in PagesController#home
undefined local variable or method `about_path' for #<PagesController:0x337ac40>
How can I make this happen?
For your case, use:
if #premail.save
redirect_to :action => :about
end
else is not needed here, since by default Rails would render app/views/pages/home.html.erb, be sure you have this file.
Also when you redirect to about, you will need app/views/pages/about.html.erb file to be present.
Update
Seems you don't have this route in config/routes.rb, for Rails 3.x:
match ':controller(/:action(/:id))'
In Rails 4:
match ':controller(/:action(/:id))', :via => [:get , :post]
If you are planning to just answer to get, i.e. there are nor forms posting to controllers:
get ':controller(/:action(/:id))'
This will detect routes like localhost:3000/asd/qwe/1 and:
Use asd as controller AsdController
Use qwe as action:
class AsdController
def qwe; end
params[:id] would be equal to 1.
() means optional, for example if you go in your browser to localhost:3000/asd, Rails would call Asd#index, i.e.:
class AsdController
def index
# whatever you have here
end
So I have a ChatsController, and from my index action, I'm trying to redirect to a custom action, "decide":
def index
#chat = Chat.customfind(params[:search])
if(#chat.is_a?(Array))
session[:chats] = #chat
redirect_to :action => 'decide'
else
respond_to do |format|
format.html { redirect_to #chat if !#chat.nil? }
end
end
def decide
#chats = session[:chats]
#choice = Chat.find(params[:id])
redirect_to #choice if !#choice.nil?
end
..where #choice is going to be decided by the params of the form on the decide page. But for some reason, instead of redirecting to decide, Rails redirects to show:
Started GET "/chats/decide" for 127.0.0.1 at 2012-03-14 17:13:36 -0400
Processing by ChatsController#show as HTML
Parameters: {"id"=>"decide"}
..............
Can anyone explain how to fix this?
Edit:
I'm assuming this is what you want... the relevant parts of my routes.rb:
match "chats/decide" => "chats#decide"
resources :chats do
member do
post 'send_message'
end
end
get "chats/logout"
..yeah it's a bit of a hodgepodge :/
It seems you are trying to achieve the following:
Find all chats matching a given search string
If 1 chat is found, redirect to it
If 2+ chats are found, redirect to /chats/decide so the user can pick one
I would implement this as follows:
1) Update routes.rb as follows:
resources :chats do
member do
post :send_message
end
collection do
get :decide # Produces the '/chats/decide/' route
end
end
2) Change your chats#decide action to this:
def decide
#chats = session[:chats]
end
3) When you list the available chats in your decide.html.erb file, link them directly to the appropriate show link.
4) Be explicit about your redirect in chats#index:
redirect_to :controller => 'chats', :action => 'decide'
Your chats#decide action should not respond differently based on whether it's receiving an id or not. You can link directly to the specific chats in that view.