Override "show" resource route in Rails - ruby

resources :some_resource
That is, there is a route /some_resource/:id
In fact, :id for some_resource will always be stored in session, so I want to override the path /some_resource/:id with /some_resource/my. Or I want to override it with /some_resource/ and remove the path GET /some_resource/ for index action.
How can I reach these two goals?

In your routes.rb put:
get "some_resource" => "some_resource#show"
before the line
resources :some_resource
Then rails will pick up your "get" before it finds the resources... thus overriding the get /some_resource
In addition, you should specify:
resources :some_resource, :except => :index
although, as mentioned, rails won't pick it up, it is a good practice

Chen's answer works fine (and I used that approach for some time), but there is a standardized way. In the Official Rails Guides the use of collection routes is preferred.
Collection routes exist so that Rails won't assume you are specifying a resource :id. In my opinion this is better than overriding a route using precedence within the routes.rb file.
resources :some_resource, :except => :index do
get 'some_resource', :on => :collection, :action => 'show'
end
If you need to specify more than collection route, then the use of the block is preferred.
resources :some_resource, :except => :index do
collection do
get 'some_resource', :action => 'show'
# more actions...
end
end

Related

Understanding 'GET' routes in rails

In my routes file I have specified:
resources :cards do
end
Apart from the basic CRUD routes I have another route which is as follows:
get '/cards/get_schema' => 'cards#get_schema'
When I hit this endpoint, I'm actually taken to cards#show. Why does this happen?
One route generated by resources :cards is get '/cards/:id'. Can you see the issue? get_schema is recognized as id. Try this one
resources :cards do
get 'get_schema', on: :collection
end
Or just put that route on top
get '/cards/get_schema' => 'cards#get_schema'
resources :cards
Rails is treating get_schema as the id of a card. The solution is to reorder the route declarations, like so:
get '/cards/get_schema' => 'cards#get_schema'
resources :cards do
end
This way the get_schema route will be matched before the show route.
It depends on the order of routes defined.
Order 1
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :cards do
end
get '/cards/get_schema' => 'cards#get_schema'
end
Running routes
rake routes
Output
~/D/p/p/s/console_test> rake routes
Prefix Verb URI Pattern Controller#Action
cards GET /cards(.:format) cards#index
POST /cards(.:format) cards#create
new_card GET /cards/new(.:format) cards#new
edit_card GET /cards/:id/edit(.:format) cards#edit
card GET /cards/:id(.:format) cards#show #<========
PATCH /cards/:id(.:format) cards#update
PUT /cards/:id(.:format) cards#update
DELETE /cards/:id(.:format) cards#destroy
cards_get_schema GET /cards/get_schema(.:format) cards#get_schema #<========
Since show expects cards/:id and is above /cards/get_schema it gets routed to cards#show
Order 2
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
get '/cards/get_schema' => 'cards#get_schema'
resources :cards do
end
end
Running routes
rake routes
Output
~/D/p/p/s/console_test> rake routes
Prefix Verb URI Pattern Controller#Action
cards_get_schema GET /cards/get_schema(.:format) cards#get_schema #<========
cards GET /cards(.:format) cards#index
POST /cards(.:format) cards#create
new_card GET /cards/new(.:format) cards#new
edit_card GET /cards/:id/edit(.:format) cards#edit
card GET /cards/:id(.:format) cards#show #<========
PATCH /cards/:id(.:format) cards#update
PUT /cards/:id(.:format) cards#update
DELETE /cards/:id(.:format) cards#destroy
In this scenario /cards/get_schema will be top level and won't conflict with cards#show

URL Rewrite Ruby on Rails

We have a Redmine installation on an old server and moved it to a new one with a new domain. Problem is, we need to redirect urls from the old domain to the new one. I really don't have much knowledge about ruby. I was thinking redirecting URLs on this one is as easy as some rewrite rules with .htaccess but I found it different. I've read some answers here redirect but can't figure out where to put those codes.
The scenario should be like:
from http://www.old-domain.com:3000/issues/3456
should be redirected to http://www.new-domain.com:3000/issues/3456
Can anyone help me how to do this? Or if you have better idea how to achieve this?
I'm planning on reading some ruby guides for the meantime.
Thanks guys!
Update:
I managed to create a simple redirect by doing the following:
I created a controller redirect_controller.rb:
class RedirectController < ApplicationController
before_filter :show
def show
redirect_to "http://www.new-domain.com:3000/", :status => :moved_permanently, :notice => "Notice: A redirect!!!"
end
end
And added this to routes.rb:
map.connect '/', :controller => 'redirect'
But I only managed to redirect the page after a successful login. How can I redirect all pages to the new one retaining parameters such as /issues/3456 if there are any?
You can go to your application.rb file (I found it better than place the redirection in the application controller), which is loaded to start all the rails and all engines. The key here is to use
head :moved_permanently, :location => "http://www.newdomain.com/"
To call that you can wrap it in a method I found in a blog. I added some comment
def perm_redirect_to(options)
url = case options
when String # if you pass a string url, which is your case
options
else
url_for(options) # if you pass some more complex option hash in
# `options`, which doesn't seem to be your case
end
head :moved_permanently, :location => url
end
You can call this method passing your url perm_redirect_to(your_new_url)!

routing to a static .md page in /public (ruby on rails 2.3.5)

this question was asked before, but the answer was for ruby on rails 3.0+
Basically I want to put my wiki pages (ie .md pages) inside my public folder in my 2.3.5 ruby on rails project. and I want users to access the home of the wiki page when they type mysite.com/wiki (ie this would map to /public/wiki/home.md)..
how do I do this in ruby on rails 2.3.5? (the routing documentation online wasn't very informative)
Also in general if for some reason I'm stuck with an RoR 2.3.5 project.. where do I go for documentation? It seems the official documentation only pertains to the latest RoR version (ie 3+)
I presume that you want the Markdown to be rendered. If you simply serve it up from your public directory, then Rails won't render it.
What you could do is an a new controller, say WikiController, which could render markdown files that you store somewhere like lib/wiki. I haven't tested any of this directly, so you should take it only as a guide, but it should work okay.
The controller might look like something like this:
# app/controllers/wiki_controller.rb
class WikiController < ApplicationController
def show
page = File.open(File.join(Rails.root, 'lib', 'wiki', "#{params[:page_id]}.md"), 'r') { |f| f.read }
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, :autolink => true)
render :html => markdown.render(File.join(Rails.root, 'lib', 'wiki', "#{params[:page_id]}.md"))
end
end
And you could add a route like this:
# config/routes.rb
map.connect 'wiki', :controller => 'wiki', :action => 'show', :page_id => 'home'
map.connect 'wiki/*page_id', :controller => 'wiki', :action => 'show', :as => :wiki
The first route handles your special case (home.md) and the second will allow you to structure your wiki however you like (including placing files in subdirectories, etc). Linking to /wiki/help/getting_started will try to render the file lib/wiki/help/getting_started.md.
You also have a link helper method, so that if you need to link to a wiki page from within your app you can call wiki_path(:page_id => 'help/getting_started').
This solution assumes you're using RedCarpet for Markdown rendering, however you could switch up any renderer you like.

Padrino, name route differently from path?

I want to be able to follow a convention closer to what Rails does with resourceful routing. For example, I'm considering "signups" to be a resource, with it's own controller containing "new" and "create" actions.
In app/controllers/signup.rb I have:
MyApp.controllers :signups do
get :index do
# ...
end
post :index do
# ...
end
end
Is there any way I can use these route names, while actually responding on a path other than '/signups'? It feels like Padrino's route naming system is very tightly coupled with the URLs the routes map to.
I've tried:
MyApp.controllers :signups, :map => '/another-path' do
# ...
end
Among various other things without success. Perhaps I should just go back to using Rails... I was just getting frustrated with the startup overhead in TDD and I'm embarking on a new project at the moment (please don't refer me to Spork... that has it's own issues).
This is how I would do what you are asking
# in app/controller/signups.rb
MyApp.controllers :'another-path' do
get '/' do
# ...
end
end

What is a very simple authentication scheme for Sinatra/Rack

I am busy porting a very small web app from ASP.NET MVC 2 to Ruby/Sinatra.
In the MVC app, FormsAuthentication.SetAuthCookie was being used to set a persistent cookie when the users login was validated correctly against the database.
I was wondering what the equivalent of Forms Authentication would be in Sinatra? All the authentication frameworks seem very bulky and not really what I'm looking for.
Here is a very simple authentication scheme for Sinatra.
I’ll explain how it works below.
class App < Sinatra::Base
set :sessions => true
register do
def auth (type)
condition do
redirect "/login" unless send("is_#{type}?")
end
end
end
helpers do
def is_user?
#user != nil
end
end
before do
#user = User.get(session[:user_id])
end
get "/" do
"Hello, anonymous."
end
get "/protected", :auth => :user do
"Hello, #{#user.name}."
end
post "/login" do
session[:user_id] = User.authenticate(params).id
end
get "/logout" do
session[:user_id] = nil
end
end
For any route you want to protect, add the :auth => :user condition to it, as in the /protected example above. That will call the auth method, which adds a condition to the route via condition.
The condition calls the is_user? method, which has been defined as a helper. The method should return true or false depending on whether the session contains a valid account id. (Calling helpers dynamically like this makes it simple to add other types of users with different privileges.)
Finally, the before handler sets up a #user instance variable for every request for things like displaying the user’s name at the top of each page. You can also use the is_user? helper in your views to determine if the user is logged in.
Todd's answer does not work for me, and I found an even simpler solution for one-off dead simple authentication in Sinatra's FAQ:
require 'rubygems'
require 'sinatra'
use Rack::Auth::Basic, "Restricted Area" do |username, password|
[username, password] == ['admin', 'admin']
end
get '/' do
"You're welcome"
end
I thought I would share it just in case anyone wandered this question and needed a non-persistent solution.
I' have found this tutorial and repository with a full example, its working fine for me
https://sklise.com/2013/03/08/sinatra-warden-auth/
https://github.com/sklise/sinatra-warden-example
I used the accepted answer for an app that just had 2 passwords, one for users and one for admins. I just made a login form that takes a password(or pin) and compared that to one that I had set in sinatra's settings (one for admin, one for user). Then I set the session[:current_user] to either admin or user according to which password the user entered and authorized accordingly. I didn't even need a user model. I did have to do something like this:
use Rack::Session::Cookie, :key => 'rack.session',
:domain => 'foo.com',
:path => '/',
:expire_after => 2592000, # In seconds
:secret => 'change_me'
As mentioned in the sinatra documentation to get the session to persist in chrome. With that added to my main file, they persist as expected.
I found JWT to be the simple, modern/secure solution I was searching for. OP mentioned bulky frameworks, so for reference I downloaded the tag of the latest jwt gem at the time of writing (2.2.3) and it's 73 KB zipped and 191 KB unzipped. Seems to be well-maintained and open sourced on GitHub.
Here's a good blog post about it with code and a walkthrough for near-beginners: https://auth0.com/blog/ruby-authentication-secure-rack-apps-with-jwt/

Resources