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 %>.
Related
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 have a simple problem where in a routes/url name is determined by a user role. Currently the route displayed is /new_admin/dispensaries. If the user has a role of either manager or executive then the named route should be '/dashboards/dispensaries'.
It's kind of simple but the hard part is that in my routes.rb:
namespace :new_admin do
resources :vendor_templates
resources :markdown_docs
resources :email_lists
namespace :moderation do
resources :reported_reviews
end
resources :users do
member do
get :user_bans
post :ban_unban, to: 'user_bans#create'
delete :ban_unban, to: 'user_bans#destroy'
end
end
# TODO - this should be written generically to support dispensary/doctors/whatever
get '/dispensaries/reviews', :to => "reviews#all", :as => :all_reviews
get '/dispensaries/pictures', :to => "pictures#all", :as => :all_pictures
get '/dispensaries/videos', :to => "videos#all", :as => :all_videos
get "/dispensaries/autocomplete", to: "dispensaries#autocomplete"
resources :vendors do
resources :ownership_transfers, only: [:new, :create]
end
...
I'm kind of stuck since if I change the new_admin routes, so many other routes will be affected. Any idea guys?
We've actually done something like this. It's not pretty, but this solution worked for us:
Slugs
You're basically alluding to a type of your routes called Slugs. This is where you use a name instead of an ID, allowing you to make a user-friendly route (such as /delivery/today). The problem is that in order to create these routes, you have to define them individually in the routes file
There are two Gems you can use to handle your slugged routes -- FriendlyID & Slugalicious. Both of these allow you to create slugged routes, but FriendlyID basically just changes the ID, whilst Slugalicious is a totally independent system
We used Slugalicious for the code below, however, you'll probably want FriendlyID (there's a RailsCast for it here):
Routing
The problem you have is that routes are outside the scope of the RESTful controller interface, which means you'll have to call all the routes exclusive of your resources references in the routes.rb file
If you use Slugalicious, it has its own Slugs database, which means we can use it to create the routes on the fly, like this:
#Slugs
begin
Slug.all.each do |s|
begin
get "#{s.slug}" => "#{s.sluggable_type.downcase.pluralize}#show", :id => s.slug
rescue
end
end
rescue
end
This is live code, and outputs all the slugs in the routes file dynamically. The way we managed to get this to update programmatically was to use an Observer Class like this:
class SlugObserver < ActiveRecord::Observer
def after_save(slug)
Rails.application.reload_routes!
end
def after_destroy(slug)
Rails.application.reload_routes!
end
end
I appreciate you may have your answer already, but as you're a beginner, I felt I could help out by explaining the slug stuff for you
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
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
My homecontroller has:
def about()
end
And I have a rspec test that does GET 'about' and it fails saying that there is no route that matches.
doesn't this map all actions in the controller:
resources :home
or do I have to explicitly state each action in the home controller?
resources :home sets up the default RESTful routes - index, show, new, create, edit, update, and destroy. Any additional routes have to be specified. It looks like you're adding a simple collection route, so you'd specify it like this:
resources :home
collection do
get 'about'
end
end
This will give your the route '/home/about'. I assume this is Rails 3. If you're in Rails 2.x, do it like so:
map.resources :home, :collection => {:about => :get}
And from the command line, you can always see what routes you have available with this command:
rake routes
I hope this helps!
EDIT: If you want a default route, you can add this:
match ':controller(/:action(/:id))'
This is a default route that will match any generic requests.
FULL ARTICLE: Routing in Rails 3 is its own beast. There have been a lot of questions about it lately, so I've created a very detailed article with code samples to help others:
Routing in Ruby on Rails 3
I created a companion Rails 3 app that can be downloaded to play around with, as well:
https://github.com/kconrails/rails3_routing
If you have any questions, please hit up my site and ask. Thanks!
resources will give you the 7 CRUD methods for a controller, if you want additional actions, you need to do something like the following:
resources :homes do
collection do
match "about" => "homes#about", :as => "about"
end
end
Then you'll also have an additional about_homes_path/url helper available.