Routing and nested resources - ruby-on-rails

I have a set of nested resources -- :users and :messages.
resources :users, :only => [:show,:index] do
resources :messages
end
And also a formtastic form in the "new.html.haml" template for the Message model and it needs to use these resources:
=semantic_form_for [#user,#message] do |f|
=f.input :title
The problem is in the actual action path that is generated from [#user,#message], and it looks like this:
/users/4cdc7f9dfae434029a0000aa/messages
This is wrong because I am using a named route, so this should look like "/users/nickname/messages"
How can I make sure that #user produces a proper path output?
Edit: Here is the name routes I am using to slugify the user.
match "/users/:nickname/messages" => "messages#index"
match "/users/:nickname/messages/new" => "messages#new", :as=>:new_message
match "/users/:nickname/messages" => "messages#create", :as=>:create_message

I guess what you want is to use the nickname as the url slug. What you need for that is just the regular nested resources;
# in routes.rb
resources :users do
resources :messages
end
which would give you the same routes except they would have :id instead of :nickname in there.
Now for the actual trick that lets you use the user's nickname as the url identifier:
# user.rb
class User < ActiveRecord::Base
def to_param
self.nickname
end
end
You would automatically get the nickname in the url, and have it passed on to your controllers as params[:id] so you have to act accordingly in your controller
# users_controller.rb
…
def show
#user = User.find_by_nickname(params[:id])
end
…

This should do the trick
=semantic_form_for :message, :url => create_message_path(:nickname => #user.nickname) do |f|
# ...

Related

How to add comments to a specific post?

I am very new to rails and I don't know much about where my problem lies. Just started a few days ago and right now I am trying to add comments to a specific post. Well, technically these are not comments but more of logs added to a certain request when other people have interacted with the request like Request A has log B, log C, log D so its basically the same as a comment sytem.
I already can create the logs and display them but my main problem is showing the right logs for the right request. My logs already have the correct request id but I don't know how to get the specific logs to display for the specific request
This is my historys_controller.rb
class HistorysController < ActionController::Base
before_action :require_login
def index
#historys = History.where(job_order: 1)
end
def require_login
unless session['user_credentials_id']
redirect_to '/'
end
end
end
It is still set as 1 as a temporary placeholder and trying something like
History.where(job_order: #job_order.id)
and
History.where(job_order: params [:id])
does not work so what should I put there?
EDIT
history.rb
class History < ApplicationRecord
belongs_to :job_order, polymorphic: true
belongs_to :actor, class_name: "User"
end
routes.rb
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
root 'user_sessions#new'
get '/sign_in', to: 'user_sessions#new', as: :sign_in
get '/signup' => 'users#signup', as: 'signup_user'
delete '/sign_out', to: 'user_sessions#destroy', as: :sign_out
post '/register' => 'users#register', as: 'register_user'
get '/users/activate/:id' => 'users#activate', as: 'activate_user'
get '/users/deactivate/:id' => 'users#deactivate', as: 'deactivate_user'
get '/job_order_tracking_system' => 'job_orders#dashboard', as: 'job_order_tracking_system'
get '/job_orders/approve_job_order/:id' => 'job_orders#approve_job_order', as: 'approve_job_order'
get '/job_orders/reject_job_order/:id' => 'job_orders#reject_job_order', as: 'reject_job_order'
get '/job_orders/start_job_order/:id' => 'job_orders#start_job_order', as: 'start_job_order'
get '/job_orders/done_job_order/:id' => 'job_orders#done_job_order', as: 'done_job_order'
get '/job_orders/cancel_job_order/:id' => 'job_orders#cancel_job_order', as: 'cancel_job_order'
resources :job_orders do
resources :historys
end
resources :users
resources :offices
resources :user_sessions, only: [:create, :destroy]
resources :notifications do
collection do
post :mark_as_read
end
end
# resources :historys
end
you want the histories for a specific job order?
Do
class JobOrder < ApplicationRecord
has_many :histories
end
Then you can get all the related histories with
#job_order.histories
And in your view you can do somthing like...
<% #job_order.histories.each do |history| %>
<%= history.name %>
<%= history.comment %>
<br/>
<% end %>
Incidentally, use the correct spelling for the plural of "history"... your controller should be HistoriesController Rails is smarter than you realise.
I also noticed you've got some spacing between params and [:id]. It should look this way: params[:id].

Ignore part of the path in Rails 3

I would like to be able to ignore part of the paths in my application.
For example:
example.com/products/toys/big-toy, should be routed by ignoring the 'toys' part (just products/big-toy). I am aware of the wildcard symbol available in the routes, but it ignores everything after the products path. I am not sure how to do this and keep my nested resources working.
Routes:
resources :products do
member do
match :details
end
resources :photos
end
product.rb:
def to_param
"#{category.slug}/#{slug}"
end
One way to solve this would be to use a route constraint.
Try this:
resources :products, constraints: { id: /[^\/]+\/[^\/]+/ } do
member do
match :details, via: :get
end
resources :photos
end
This will capture the product :id as anything with a slash in the middle, so /products/abc/xyz/details will route to products#details with params[:id] equal to abc/xyz.
Then, you could add a before filter in your ProductsController, like this:
class ProductsController < ApplicationController
before_filter :parse_id
// ...
def parse_id
slugs = params[:id].split("/")
params[:category_id] = slugs[0]
params[:id] = slugs[1]
end
end

rails custom rest route with parameter

I have a questions controller and an associated model and a number of rest routes. Here is how it's set up in routes.rb:
resources :questions
I want to add a custom route that has the format /questions/widget/ID (where ID is the id of the question for which I want to generate a widget). I want this to be processed by the "widget" action in my questions controller. I've tried a number of things such as:
resources :questions do
member do
get 'widget/:id'
end
end
But nothing is working. I'm sure I'm missing something simple. Any ideas? Thanks in advance.
You do not have to specify the id since you are inside resources. It should look like:
resources :questions do
member do
get 'widget'
end
end
You can get more information from the Rails Guide. Look at section 2.9.1.
Edit: I just noticed that you are trying to match get /questions/widget/:id. This will set up a route for get /questions/:id/widget. This is more in line with Rails convention. If you really want it the other way, you need to set up a custom match statement:
match "/questions/widget/:id" => "questions#widget"
However, I would stick with convention.
I know it is old, but looking to fix another routing problem I ended here, it is possible, to do what you are asking for, here is an example
resources :articles do
get 'by_tag/:tag' => :by_tag, on: :collection
get 'by_author/:author' => :by_author, on: :collection
resources :comments, except: :show
end
now you have /artices/by_tag/:tag . The trick was to use on:collection.
Obviously don't forget to add the by_tag action and by_author.
class ArticlesController < ApplicationController
.....
def by_tag
...
end
end
Check this route works with
melardev#local~$ rails routes
Why don't you use this routes:
resources :questions do
resources :widgets
end
it will create path like questions/:question_id/widgets/new for you to create new widget for question with specific id of question.
This is what ended up working for me:
resources :post do
get "author/:author", to: "posts#author", on: :collection, as: "author"
end
Which outputs the following route:
author_posts GET /posts/author/:author(.:format) posts#author
Then in your controller, you need to create the author action:
class PostsController < ApplicationController
def author
#roles = Post.where(author: params[:author])
render :index # to reuse the index view
end
end
Then in your view:
<%= link_to post.author, author_posts_path(post.author), data: { turbo_frame: "_top" } %>

Ruby on Rails: How to override the 'show' route of a resource?

Currently I have a route that looks like this:
resources :posts
I want to override the 'show' action so that I can display a url like this:
posts/:id/:slug
I am currently able to do this by adding a custom match route:
resources :posts
match 'posts/:id/:slug' => 'posts#show'
However, when I use the link_to helper, it does not use my custom show route.
<%= link_to 'show', post %> # renders /posts/123
How can I define my show route so that I can still use the link_to helper?
Update: As you can read in the following answers, you can override the route to the 'show' action, but it's probably more work than it's worth. It's easier to just create a custom route:
# config/routes.rb
match 'posts/:id/:slug' => 'posts#show', as: 'post_seo'
# app/views/posts/index.html.erb
<%= link_to post.title, post_seo_path(post.id, post.slug) %>
You have two routes which point to posts#show (you should be able to confirm this by running rake routes), and your link is using the wrong one.
When you call link_to('show', post) the URL of the link is generated by calling url_for(post) which (eventually, after passing through several other methods on the way) calls post_path(post). Since the route to posts#show that was created by your call to resources(:posts) is named post, that is the route that post_path generates.
You also currently have inconsistent routes for the show, update and destroy actions which will probably cause you problems later on.
You can fix this by changing your routes to the following:
resources :posts, :except => ['show', 'update', 'destroy']
get 'posts/:id/:slug' => 'posts#show', :as => 'post'
put 'posts/:id/:slug' => 'posts#update'
delete 'posts/:id/:slug' => 'posts#destroy'
Unfortunately you still can't use link_to('show', post) just yet, because it relies on being able to use post.to_param as the single argument needed to build a path to a post. Your custom route requires two arguments, an id and a slug. So now your link code will need to look like this:
link_to 'show', post_path(post.id, post.slug)
You can get around that problem by defining your own post_path and post_url helpers in app/helpers/posts_helper.rb:
module PostsHelper
def post_path(post, options={})
post_url(post, options.merge(:only_path => true))
end
def post_url(post, options={})
url_for(options.merge(:controller => 'posts', :action => 'show',
:id => post.id, :slug => post.slug))
end
end
Which means we're finally able to use:
link_to 'show', post
If that all seems like too much work, a common alternative is to use URLs that look more like posts/:id-:slug, in which case you can stick with the standard RESTful routes and just override the to_param method in your Post class:
def to_param
"#{id}-#{slug}"
end
You'll also need to do a little bit of work splitting up params[:id] into an ID and a slug before you can look up the relevant instance in your show, edit, update and destroy controller actions.
resources :posts, except: :show do
get ":slug" => :show, as: "", on: :member
end
and define helper
def post_path post
"/posts/#{post.id}/#{post.slug}"
end
db/migrate/add_slug_to_articles.rb
add_column :articles, :slug, :string
add_index :articles, :slug
models/article.rb
class Article < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :slugged
def should_generate_new_friendly_id?
new_record?
end
end
Or...
class Article < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :history
end
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid
https://github.com/norman/friendly_id

How do I add a route which maps to a slug url generated by the stringex gem in ruby on rails 3.1?

It seems simple, in my model I have:
class CustomerAccount < ActiveRecord::Base
acts_as_url :name
def to_param
url # or whatever you set :url_attribute to
end
end
And in my controller, I have:
class CustomerAccountsController < ApplicationController
def show # dashboard for account, set as current account
#account = CustomerAccount.find_by_url params[:id]
no_permission_redirect if !#account.has_valid_user?(current_user)
set_current_account(#account)
#latest_contacts = Contact.latest_contacts(current_account)
end
end
What's currently in the routes.rb is:
resources :customer_accounts, :path => :customer_accounts.url do
member do
get 'disabled'
post 'update_billing'
end
end
That gives me the following error when I try to generate data via rake db:seed, or at least I assume the entry in routes is what's doing it.
undefined method `url' for :customer_accounts:Symbol
So what do I need to do to get the route set up? What I'd like is http://0.0.0.0/customeraccountname to map to the view for the customer account page.
UPDATE:
Here is the code that ended up working in routes.rb, which I discovered after looking at the examples in the answer below:
resources :customer_accounts, :path => '/:id' do
root :action => "show"
member do
get 'disabled'
post 'update_billing'
end
end
If you want to set it up so you have a route like you show, do this:
get '/:id', :to => "customer_accounts#show"
If you want the disabled and update_billing actions underneath this:
get '/:id/disabled', :to => "customer_accounts#disabled"
post '/:id/update_billing', :to => "customer_accounts#update_billing"
Alternatively (and much neater):
scope '/:id' do
controller "customer_accounts" do
root :action => "show"
get 'disabled'
get 'update_billing'
end
end

Resources