I am trying to build a one page application and having trouble with Rails routing. Basically I want everything within admin route to admin controller index but json rails to specific resource. I've tried
namespace :admin do
constraints :format => 'html' do
match '*path' => 'admin#index'
end
constraints :format => 'json' do
resources :user, :items
end
end
In this case path will match greedily and match /admin/users.json
If I move the :format => 'json' block up. It matches /admin/users
Looks like the constraints blocks I specify does not works at all.
Rails version 3.2.6
rake routes
/admin/*path(.:format) admin/admin#index {:format=>"html"}
admin_users GET /admin/users(.:format) admin/users#index {:format=>"json"}
/* other normal resources routes for admin users and admin items */
I have checked and there is no route /admin/users(.format) admin/users#index {:format=>"html"}
so looks like it is exactly what I think it would be. but somehow still does not works
Update:
I have managed to get it workings if it move the json block up
However if the html block is on top. It is still causing me problem. But I think it is good enough for me now. Thanks guys
The original problem is I used request.xhr? in the controller where I should have used respond_to
Update 2
Uhm not acutally working now when I go to /admin/users I got a Not acceptable error. Where I would think that the first rule wont be match and match the second rule.
Does it work if you move the json block up and make the format segment mandatory for each json resource? In Rails 3.2 this happens by setting the format option to true:
namespace :admin do
constraints(format: "json") do
resources :items, format: true
resources :users, format: true
end
constraints(format: "html") do
match "*path" => "admin#index"
end
end
Related
Hi guys I am new to rails. Sorry if I can't define this question properly.
What I wanted is for:
domain.com/posts/1-sample-post
to be routed like this:
domain.com/1-sample-post
How do I achieve this in rails routes? I've tried searching for this for almost 3 hours. This is very easy in PHP frameworks. I thought this is easy in Rails too.
I forgot to mention I have High_voltage gem installed in my app for my static pages.
Did this:
#routes.rb
resources :posts
get '/:id' => 'posts#show'
Now my High_voltage pages could not be rendered.
Update Solution:
So here is what we did in the routes:
Rails.application.routes.draw do
resources :authors
constraints(lambda { |req| Author.exists?(slug: req.params["id"]) }) do
get '/:id' => 'authors#show'
end
devise_for :users
resources :posts
constraints(lambda { |req| Post.exists?(slug: req.params["id"]) }) do
get '/:id' => 'posts#show'
end
end
Note that it is important to only use an exists? query here as it is very fast than other methods, so it won't eat that much loading time to render a record.
Special thanks to the guys below who helped a lot. Nathanvda, rwold, and Tai.
So the other answer correctly suggested something like
get '/:id', to: 'posts#show'
But this is a catch-all route and if there are no other routes defined this will catch all routes, also your HighVoltage, if it is configured to serve pages on root. You now have two catch-alls: one to find a static page and one to find a post.
Best solution in this case, imho is to make the static pages explicit (since I am assuming there will not be that many?)
get '/about' => 'high_voltage/pages#show', id: 'about'
get '/:id' => 'posts#show'
If you have a lot of pages, it seems easiest to just present the high-voltage on a different route? E.g. something like
get '/pages/:id' => 'high_voltage/pages#show'
get '/:id' => 'posts#show'
In both of these cases, since we use explicit routing, you would have to disable the default routing in the high-voltage initializer:
# config/initializers/high_voltage.rb
HighVoltage.configure do |config|
config.routes = false
end
[UPDATE: add special controller to consider both posts and pages]
Add a HomeController like this:
class HomeController < ApplicationController
# include the HighVoltage behaviour --which we will partly overwrite
include HighVoltage::StaticPage
def show
# try to find a post first
#post = Post.where(id: params[:id).first
if #post.present?
render 'posts/show'
else
# just do the high-voltage thing
render(
template: current_page,
locals: { current_page: current_page },
)
end
end
end
Of course I did not test this code, but I think this should get you started. Instead of doing the rendering of the post, you could also redirect to the posts-controller which is maybe easier (and you will use the PostsController fully) but adds a redirect and will change the url.
In your routing you will then have to write
get '/:id', 'home#show'
In your routes.rb file:
get '/:id-sample-post', to: 'posts#show', as: :sample_post
assuming that posts is your controller and show is the action that calls the view for your article with the given id.
EDIT AFTER OP COMMENT:
The as: :sample_post clause should create a helper sample_post_path that can be invoked as <%= link_to "Show", sample_post %>.
In rails 4.2 I have a controller that I want to fill with non resourceful routes something like
class TwilioController < ApplicationController
def add_to_queue
end
def another_action
end
end
I then want to access theese actions like so
http://appdomain/twilio/add-to-queue
and
http://appdomain.com/twilio/another-action
I realise I could do this like so in the routes file
get 'twilio/add-to-queue', to: 'twilio#add_to_queue'
get 'twilio/another-action', to: 'twilio#another_action'
but is there a way of grouping all of them together so I don't have to explicitly add twilio at the beginning of each route.
Ok I have figured out a solution, it seems quite a succinct one.
scope path: 'twilio', as: 't' do
get 'add-to-queue', to: 'twilio#add_to_queue'
end
So i now have routes like:
t_add_to_queue GET /twilio/add-to-queue(.:format) twilio#add_to_queue
Here's my solution that works in Rails 3.2.
scope :path => :twilio, :controller => :twilio, :as => :twilio do
get :add_to_queue
get :another_action
get :yet_another_action
end
:path adds \twillio\... to the URL
:controller maps all the nested routes to TwillioController
:as appends the URL helpers with twillio_....
I'm almost certain that someone has asked this question before, but I can't seem to hit on the right series of words to find it.
I have a resource, Games, with all of the normal resource-y paths. Create, Edit, etc.
I've created a new action within GamesController called json that I want to be able to access at mydomain.com/games/json but the routing keeps picking up 'json' as the ID and routing it to the 'show' action instead.
Presumably this is because of the default route:
match ':controller(/:action(/:id))'
I've tried a number of things, but no matter what I do it keeps routing to 'show.' I've been attempting to figure it out using this guide, but for someone that's pretty new to rails its quite a bit to take in and apply.
I'd like to say that for any controller /json would take you to the json action (instead of show with id 'json'), but I'd settle for having to specify it for every controller individually.
Any help is greatly appreciated. (Even if that's just pointing me to the already answered question.) In all cases I've been placing the route I'm attempting to create above the default route.
My routes:
root :to => 'home#index'
resources :events, :players, :sets, :matches, :characters, :videos, :games, :event_instances, :game_instances
resource :user_session
resource :account, :controller => "users"
resources :users
match '/login', :to => 'user_sessions#new', :as => 'login'
match '/logout', :to => 'user_sessions#destroy', :as => 'logout'
match '/register', :to => 'users#create', :as => 'register'
match '/games/json', :to => 'games#json', :as => 'gameList'
match ':controller(/:action(/:id))'
Ok so what you need to do is put your custom json route above your other resources routes and make it default like so:
get '/:controller/json(/:id)', action: 'json'
resources :events, :players, :sets, :matches, :characters, :videos, :games, :event_instances, :game_instances
...
Your confusion was linked to the way the routes are built and urls are parsed
Think of your routes.rb file as a set of consecutive filters to be applied to the url requested by the client.
If you put a default route above another it will catch all request that match the given pattern => this means that when you were adding all your resources routes above your json route, all urls matching the /#{resources}/:id pattern (and /games/json is one of them) were caught before they could reach the json custom route.
By putting your custom route above the others, you make it catch any request matching the pattern, that means in that case all requests with
/#{controller_name or resource_name)}/json(/:id)
NB:
I am not quite sure this is the best design pattern to use for your routes and I'd rather go with conventional REST routes with basic CRUD actions that would implement the respond_to pattern to answer specific json requests.
See docs for this: http://apidock.com/rails/ActionController/MimeResponds/InstanceMethods/respond_to
Adding such 'catch all' routes on top of all your resources may become risky when your app grows.
But you may have your reasons to go this way, in that case, I think my answer matches what you need.
resources :games do
collection do
get :json
end
end
if you also wanted to add an action that would take an :id path segment and return, say, json representation of a specific game, you'd do that with
resources :games do
collection do
get :json
end
member do
get :json
end
end
I'd add that when I was brand new to Rails (less than 1.5 years ago), the routing dsl was really hard for me to get my head around for some reason. But it's worth studying that guide, despite the fact that it's a bit overwhelming at first. Otherwise you'll end up with a bunch of match '/route/to/somewhere' => 'controller#action', :as => :some_helper, which ends up being a pain to read & maintain and will make it harder to have a consistent "grammar" for your urls.
Edit: looking at your now-posted routes file, the problem is that you declare the games/json route after the resources :games declaration. Route-matching precedence proceeds from the top of the file down, so the resourceful route for games matches first, and the specific route declared later is never checked. If you use the syntax above to make your json route a part of the resource declaration, this won't happen.
First off, I'm using rails 3.0.8 with friendly_id 3.2.1.1.
I'd like to be able to view the posts at website.com/:title, so drop the "/posts".
But I'd also like to have an /admin view. From there, a user should be able to create/edit/destroy posts. I already have a admin.html.erb view with links to various actions.
Right now my routes file look like:
MyApp::Application.routes.draw do
root :to => 'posts#index'
resources :posts
match "/:id" => "posts#show"
match "/admin" => "posts#admin"
end
This works for website.com/:title, but for website.com/admin I get an error:
Couldn't find Post with ID=admin
.... which makes sense. But I'm not sure how to solve this problem.
The rules are run through top to bottom. So put your admin rule on top of the resource definition.
If you put /admin first then it will work (as noted by cellcortex). You can also use :constraints if you can neatly separate your :id from 'admin'; for example, if your :id values are numeric, then something like this should work:
match '/:id' => 'posts#show', :constraints => { :id => /\d+/ }
match '/admin' => 'posts#admin'
In a simple case like yours, putting things in the right order will work fine. However, if your routing is more complicated, then the :constraints approach might work better and avoid a some confusion and chaos.
Use this
resource :posts, :path => '/'
with this all of your article will be directly under root
So in Posts Class you may add this:
def to_param
"#{id}-#{title.parameterize}"
end
I am migrating a Rails 2.3.8 version to Rails 3.0 and so ive rewritten my routes file. When i list the routes using rake routes, i see some route names have _index appended to them. I cant figure out why this is.
Relevant routes:
Rails 2.3.8:
map.namespace "tracker", :path_prefix => "" do |planner|
planner.resources :planner, :collection => {:step1 => :get,
:add => :get,
:unsubscribe => [:get, :post] }
end
Rails 3.0 route:
namespace "tracker", :path => "" do
resources :planner do
collection do
get :step1
get :add
get :unsubscribe
post :unsubscribe
end
end
end
Output from rake routes
Rails 2.3.8
step1_tracker_planner GET /planner/step1(.:format)
add_tracker_planner GET /planner/add(.:format)
unsubscribe_tracker_planner GET /planner/unsubscribe(.:format)
POST /planner/unsubscribe(.:format)
Rails 3.0
step1_tracker_planner_index GET /planner/step1(.:format)
add_tracker_planner_index GET /planner/add(.:format)
unsubscribe_tracker_planner_index GET /planner/unsubscribe(.:format)
POST /planner/unsubscribe(.:format)
Any ideas as to why this _index is being added would be much appreciated.
It is because your resource is named :planner instead of :planners that Rails decided to add the _index to any collection nested underneath. My guess it is there for readability.
The action named in the collection normally translates to a verb, so I can see why this makes sense. Take the typical photos resource example given in the routing docs:
resources :photos do
collection do
get 'search'
end
end
search_photos GET /photos/search(.:format)
But if instead we called the resources 'photo'...
resources :photo do
collection do
get 'search'
end
end
search_photo_index GET /photo/search(.:format)
In the first case, you search the "photos", and in the second case you search the "photo index".
You should be using either resource :planner either resources :planners depending on what you need. To learn about singular resource and it differences check out Rails Guides.
Following on from Semyon Perepelitsa's response, note that resource :planner expects the controller's name is PlannersController, whereas resources :planners expects PlannerController.
If you don't want to rename your controller when changing from resources to resource, you can override the default by specifying resource :planner, controller: :planner.