Pretty urls in rails - ruby-on-rails

Im trying to make it work. THis is my rake routes right now:
groups GET /groups(.:format) groups#index
POST /groups(.:format) groups#create
new_group GET /groups/new(.:format) groups#new
edit_group GET /groups/:id/edit(.:format) groups#edit
group GET /groups/:id(.:format) groups#show
PATCH /groups/:id(.:format) groups#update
PUT /groups/:id(.:format) groups#update
DELETE /groups/:id(.:format) groups#destroy
what i want to do is remove "groups" from the link and add slug instead of id.
This is my controller right now:
class GroupsController < ApplicationController
before_action :find_group, only: [:show]
after_action :assign_slug, only: [:create, :update]
def index
#groups = Group.all
end
def show
end
def new
#group = Group.new
end
def create
#group = Group.new(group_params)
if #group.save
flash.notice = "Group has been successfully created!"
redirect_to #group
binding.pry
else
flash.alert = "oops!"
redirect_to action: :new
end
end
private
def group_params
params.require(:group).permit(:title, :description, :country, :city, :category_id)
end
def find_group
#group = Group.find_by_slug(params[:slug])
end
def assign_slug
#group.update(slug: #group.title.parameterize)
end
end
How do i make it work? Thanks.:)

If this is what you want:
routes.rb
get ':slug', to: 'pages#show', as: :groups_show
put ':slug', to: 'pages#update', as: :groups_update
delete ':slug', to: 'pages#destroy', as: :groups_delete
This way the slug will be catched as param and sended to the controller.

Related

NoMethodError (undefined method `product_url'

I have the following error when debugging my first project on rails:
NoMethodError (undefined method `product_url' for #
Did you mean? products_url):
app/controllers/products_controller.rb:13:in `create'
class ProductsController < ApplicationController
def show
#product = Product.find(params[:id])
end
def new
end
def create
#product = Product.new(product_params)
#product.save
redirect_to #product
end
private
def product_params
params.require(:product).permit(:title, :price, :count)
end
end
----------config/routes.rb------
Rails.application.routes.draw do
get 'welcome/index'
resource :products
root 'welcome#index'
end
----------rake routes----------
Prefix Verb URI Pattern Controller#Action
welcome_index GET /welcome/index(.:format) welcome#index
new_products GET /products/new(.:format) products#new
edit_products GET /products/edit(.:format) products#edit
products GET /products(.:format) products#show
PATCH /products(.:format) products#update
PUT /products(.:format) products#update
DELETE /products(.:format) products#destroy
POST /products(.:format)
You need to initialise #product. It can be done like:
before_action :set_document, only: [:show, :edit, :update, :destroy]
def set_document
#document = Document.find(params[:id])
end
You can scaffold the Controller with
rails g scaffold_controller Document
to see exactly how rails is generating classes for a model.
In your routes.rb it should be
resources: products

How to define routes that include a dynamic organization name in Rails

I'm building a Rails 5.2 SaaS application that allows users to belong to many "organizations". Users will only see content for their currently active organization.
I started down the path of using subdomains, but after a little more research have decided to avoid them for now.
My new approach (to make it explicit to the user what organization they are using, support sharing links, browser history etc..) is to embed the organization name in the path. For example:
https://app.example.com/foo/posts # Posts for org "foo"
https://app.example.com/foo/posts/7 # Post for org "foo"
https://app.example.com/bar/posts # Posts for org "bar"
https://app.example.com/settings # General account settings
https://app.example.com/signin # Sign in
My problem is how to do this with Rails routes? I've tried to use a dynamic scope:
scope ':org' do
resources :posts
end
Results in errors like:
No route matches {:action=>"show", :controller=>"posts", :org=>#<Organization id: 1, name: "My Organization", ...}, missing required keys: [:id]
For the code:
# layouts/application.html.erb
<%= link_to post, post, class: 'dropdown-item' %>
Any suggestions on how to configure routes to support this use case?
Use the resources macro:
Rails.application.routes.draw do
resources :posts, except: [:new, :create]
resources :organizations, path: '/', only: [] do
resources :posts, module: :organizations, only: [:new, :index, :create]
end
end
$ rails routes:
Prefix Verb URI Pattern Controller#Action
posts GET /posts(.:format) posts#index
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy
organization_posts GET /:organization_id/posts(.:format) organizations/posts#index
POST /:organization_id/posts(.:format) organizations/posts#create
new_organization_post GET /:organization_id/posts/new(.:format) organizations/posts#new
By using the module: option you can setup a separate controller for the nested context:
# app/controllers/organizations/posts_controller.rb
class Organizations::PostsController < ApplicationController
before_action :set_organization!
# Index for posts belonging to a specific organization
# GET /:organization_id/posts
def index
#posts = #organization.posts
end
# GET /:organization_id/posts/new
def new
#post = #organization.posts.new
end
# POST /:organization_id/posts
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
else
render :new
end
end
private
def set_organization!
#organization = Organization.includes(:posts)
.find_by!(name: params[:organization_id])
end
def post_params
params.require(:post).permit(:title)
end
end
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
# Index for posts belonging to all organizations
# GET /posts
def index
#posts = Post.all
end
# GET /posts/1
def show
end
# GET /posts/1/edit
def edit
end
# PATCH/PUT /posts/1
def update
if #post.update(post_params)
redirect_to #post, notice: 'Post was successfully updated.'
else
render :edit
end
end
# DELETE /posts/1
def destroy
#post.destroy
redirect_to posts_url, notice: 'Post was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def post_params
params.require(:post).permit(:title)
end
end
However you should be beware when creating routes that start with a dynamic segment - routes have priority in the order that they are defined and a route that starts with a dynamic segment will be "greedy" and swallow other routes if they are not defined first.

Admin function in rails: Error: Couldn't find User with 'id'=

I am trying to write an update function, which allows to pick a user in a list of users and update that user to make it an admin. The important function in the controller should be def change_admin.
Thanks for your help!
I tried several options, but I run into that error:
Couldn't find User with 'id'=
My Controller:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def new
#user = User.new
end
def index
#users = User.where(activated: true).paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
redirect_to root_url and return unless #user.activated
end
def edit
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "Der Nutzer wurde gelöscht"
redirect_to users_url
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to root_path
else
render 'edit'
end
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Bitte öffnen Sie Ihr E-Mail Postfach, um den Account zu aktivieren."
redirect_to root_url
else
render 'new'
end
end
def admin
#users = User.where(activated: true).paginate(page: params[:page])
end
def change_admin
#user = User.find(params[:id])
#user.update_attribute(:admin,true)
respond_to do |format|
format.html { redirect_to admin_path }
end
end
# Before filters
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation,
:mat_number, :ects, :grade_avg, :enrolled, :matched_institute)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
I also tried to delete the user.find line, but that gives me another error:
undefined method `update_attribute' for nil:NilClass
My routes file:
Rails.application.routes.draw do
resources :preferences
resources :institutes
get 'password_resets/new'
get 'password_resets/edit'
get '/users_show', to: 'users#show'
get '/users/new'
root 'static_pages#home'
get '/home', to: 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/matching', to: 'static_pages#matching'
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
get '/admin', to: 'users#admin'
post '/change_admin', :to => 'users#change_admin', as: 'change_admin'
get '/performance_show', to: 'users#performance_show'
get '/performance_update', to: 'users#performance_update'
post 'preferences/create_all', :to => 'preferences#create_all'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
post 'preferences/delete_matching', :to => 'preferences#delete_matching'
post 'preferences/read_and_show_ofv', :to => 'preferences#read_and_show_ofv'
post 'preferences/read_matching', :to => 'preferences#read_matching'
post 'preferences/optimize_matching', :to => 'preferences#optimize_matching'
post 'preferences/optimize', to: 'preferences#optimize'
end
Your routes are in pretty bad shape. You have tons of duplication as well as routes that are missing the :id segment to make them work:
get '/users_show', to: 'users#show'
get '/users/new'
post '/change_admin', :to => 'users#change_admin', as: 'change_admin'
resources :users already declares a proper show route as GET /users/:id and new as /users/new.
To add additional RESTful routes you should instead pass a block to resources:
resources :users do
# this should be PATCH not POST
patch :change_admin
end
This will create the proper route as /users/:id/change_admin.
You are also using the wrong HTTP verb in many places like for example get '/performance_update', to: 'users#performance_update'. Never use GET for actions that create or alter a resource as the call ends up in the browsers history and may be cached.
Adding update, change, create in the path of your route should be a big red flag that you´re doing it wrong.
I would recommend that you read the guides thoroughly before adding more cruft.

Rake routes 'match' mapping to the wrong action

My question has to do with mapping to controllers/actions using named routes. I am trying to map '/profile' to 'customers#show'. My routes file looks like this:
root :to => 'pages#home'
## named routes
match "profile" => "customers#show", :as => 'profile'
match 'signin' => 'sessions#new', :as => 'signin'
match 'signout' => 'sessions#destroy', :as => 'signout'
resources :customers do
member do
get 'add_card'
post 'submit_card'
end
end
resources :payments, :only => [:show, :new]
delete 'payments/delete_recurring_payment'
post 'payments/submit_non_recurring'
post 'payments/submit_recurring'
resources :sessions, :only => [:create, :destroy, :new]
Running 'rake routes' gives me this:
root / pages#home
profile /profile(.:format) customers#show
signin /signin(.:format) sessions#new
signout /signout(.:format) sessions#destroy
add_card_customer GET /customers/:id/add_card(.:format) customers#add_card
submit_card_customer POST /customers/:id/submit_card(.:format) customers#submit_card
customers GET /customers(.:format) customers#index
POST /customers(.:format) customers#create
new_customer GET /customers/new(.:format) customers#new
edit_customer GET /customers/:id/edit(.:format) customers#edit
customer GET /customers/:id(.:format) customers#show
PUT /customers/:id(.:format) customers#update
DELETE /customers/:id(.:format) customers#destroy
new_payment GET /payments/new(.:format) payments#new
payment GET /payments/:id(.:format) payments#show
Here is where I'm stumped. When I go to localhost:3000/profile I get a routing error saying this:
No route matches {:action=>"edit", :controller=>"customers"}
This seems odd because there is indeed a route to 'customers#edit' due to my declaring customers as a resource.
However, when I go to 'localhost:3000/signin' I get routed to 'customers#show' which is where I want '/profile' to route to.
It seems like my routes are 'one off' in my routes file but I have no idea why. Any help would be much appreciated.
Thanks
UPDATE 1: Adding my Customers Controller
class CustomersController < ApplicationController
layout "payments_layout"
def show
#customer = current_user
get_account_info(#customer)
get_payment_history(#customer, 10)
end
def new
#title = 'Create an account'
#customer = Customer.new
end
def edit
#customer = current_user
get_account_info(#customer)
end
def update
#customer = current_user
if #customer.update(params[:customer])
redirect_to #customer
else
#card_message = "Use this form to add a credit card to your account. You must have a credit card associated with your account in
in order to make payments on our system."
get_account_info(#customer)
render 'edit'
end
end
def create
#customer = Customer.new(params[:customer])
if #customer.save_and_get_stripe_id
sign_in(#customer)
redirect_to #customer
else
#title = 'Create an account'
render 'new'
end
end
def add_card
#customer = current_user
get_account_info(#customer)
#card_message = "Use this form to add a credit card to your account. You must have a credit card associated with your account in
in order to make payments on our system."
end
def submit_card
#customer = current_user
res = #customer.add_or_update_card(params)
if res
redirect_to #customer
else
#error = res
customer.get_account_info(#customer)
render 'add_card'
end
end
end
Check your views.
Do you have a link to edit your user profile?
It seems like you actually route to the right controller and action but have some links that rake can't route, e.g. you don't pass the ID to your edit_customer_path link.

Add Edit and Delete feature in comment polymorphic-association-revised posts

I read this http://railscasts.com/episodes/154-polymorphic-association-revised posts and implement it as it have. But i want to add edit and delete features also in this tuotorial.
I have comments_controller.rb is like this
class CommentsController < ApplicationController
before_filter :load_commentable
def index
#comments = #commentable.comments
end
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(params[:comment])
if #comment.save
redirect_to #commentable, notice: "Comment created."
else
render :new
end
end
private
def load_commentable
resource, id = request.path.split('/')[1, 2]
#commentable = resource.singularize.classify.constantize.find(id)
end
# def load_commentable
# klass = [Article, Photo, Event].detect { |c| params["#{c.name.underscore}_id"] }
# #commentable = klass.find(params["#{klass.name.underscore}_id"])
# end
end
my _comments.html.erb as given is like this
<div id="comments">
<% #comments.each do |comment| %>
<div class="comment">
<%= simple_format comment.content %>
</div>
<% end %>
</div>
my routes is like this
Blog::Application.routes.draw do
resources :articles do
resources :comments
end
resources :photos do
resources :comments
end
resources :events do
resources :comments
end
resources :comments
root to: 'articles#index'
end
My rake routes is like this
article_comment GET /articles/:article_id/comments/:id(.:format) comments#show
PUT /articles/:article_id/comments/:id(.:format) comments#update
DELETE /articles/:article_id/comments/:id(.:format) comments#destroy
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
photo_comments GET /photos/:photo_id/comments(.:format) comments#index
POST /photos/:photo_id/comments(.:format) comments#create
new_photo_comment GET /photos/:photo_id/comments/new(.:format) comments#new
edit_photo_comment GET /photos/:photo_id/comments/:id/edit(.:format) comments#edit
photo_comment GET /photos/:photo_id/comments/:id(.:format) comments#show
PUT /photos/:photo_id/comments/:id(.:format) comments#update
DELETE /photos/:photo_id/comments/:id(.:format) comments#destroy
photos GET /photos(.:format) photos#index
POST /photos(.:format) photos#create
new_photo GET /photos/new(.:format) photos#new
edit_photo GET /photos/:id/edit(.:format) photos#edit
photo GET /photos/:id(.:format) photos#show
PUT /photos/:id(.:format) photos#update
DELETE /photos/:id(.:format) photos#destroy
event_comments GET /events/:event_id/comments(.:format) comments#index
POST /events/:event_id/comments(.:format) comments#create
new_event_comment GET /events/:event_id/comments/new(.:format) comments#new
edit_event_comment GET /events/:event_id/comments/:id/edit(.:format) comments#edit
event_comment GET /events/:event_id/comments/:id(.:format) comments#show
PUT /events/:event_id/comments/:id(.:format) comments#update
DELETE /events/:event_id/comments/:id(.:format) comments#destroy
events GET /events(.:format) events#index
POST /events(.:format) events#create
new_event GET /events/new(.:format) events#new
edit_event GET /events/:id/edit(.:format) events#edit
event GET /events/:id(.:format) events#show
PUT /events/:id(.:format) events#update
DELETE /events/:id(.:format) events#destroy
comments GET /comments(.:format) comments#index
POST /comments(.:format) comments#create
new_comment GET /comments/new(.:format) comments#new
edit_comment GET /comments/:id/edit(.:format) comments#edit
comment GET /comments/:id(.:format) comments#show
PUT /comments/:id(.:format) comments#update
DELETE /comments/:id(.:format) comments#destroy
root / articles#index
Like so....? This assumes your routes are nested for :Edit, and :update actions under the "commentable" routes.
def edit
#comment = #commentable.comments.find(params[:id])
end
def create
#comment = #commentable.comments.find(params[:id])
if #comment.update_attributes(params[:comment])
redirect_to #commentable, notice: "Comment updated."
else
render :edit
end
end
Your edit should look like this:
<%= link_to 'Edit', [:edit, #comment.commentable, #comment] %>
However... you will want to have some kind of authentication and/or authorization.
I don't have a lot of time to look this over right now, but in response to your tweet I'm posting the code I was using in my test app that I got to work. I'm using HAML here.
When calling for an edit, or delete link in my view I used this partial:
- #category.photos.each do |photo|
= image_tag photo.image_url(:thumb)
%figcaption
= link_to "Change", [:edit, #category, photo]
= link_to "Delete", [#category, photo], :method => :delete
= link_to "New photo", [:new, #category, :photo]
My photos controller:
class PhotosController < ApplicationController
def create
#category = Category.find(params[:category_id])
#photo = #category.photos.create!(params[:photo])
redirect_to #category, :notice => "Photo created."
end
def edit
#category = Category.find(params[:category_id])
#photos = #category.photos
#photo = #photos.find(params[:id])
end
def update
#category = Category.find(params[:category_id])
#photo = #category.photos.find(params[:id])
respond_to do |format|
if #photo.update_attributes(params[:photo])
format.html { redirect_to #category, notice: "<i class=icon-ok /> #{#category.name} was successfully updated."}
format.js
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #category.errors, status: :unprocessable_entity }
end
end
end
def new
#category = Category.find(params[:category_id])
#photo = #category.photos.new
end
def destroy
#category = Category.find(params[:category_id])
#photos = #category.photos
#photo = #photos.find(params[:id])
#photo.destroy
redirect_to #category, :notice => "Photo deleted."
end
end
In my photo model I have:
class Photo
include Mongoid::Document
embedded_in :category, :inverse_of => :photos
field :image
attr_accessible :image
# Set uploader
mount_uploader :image, ImageUploader
end
In the category model:
class Category
# Includes to set up the model
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Ancestry
include Mongoid::Versioning
include Mongoid::Paranoia
include Mongoid::Slug
include Mongoid::History::Trackable
# tell it that it can go nest itself
has_ancestry
embeds_many :photos
# Accept nested attributes
accepts_nested_attributes_for :photos, :autosave => true
# tell history how it can track things
track_history :on => [:name, :description], # track these fields
#:modifier_field => :modifier, # adds "referenced_in :modifier" to track who made the change, default is :modifier
:version_field => :version, # adds "field :version, :type => Integer" to track current version, default is :version
:track_create => false, # track document creation, default is false
:track_update => true, # track document updates, default is true
:track_destroy => false # track document destruction, default is false
# Keep at most 5 versions of a record
max_versions 5
# Add the models fields here
field :name, type: String
field :ancestry, type: String
field :description, type: String
# Set which field the url slug should use
slug :name
# Make sure these attributes can be accessed
attr_accessible :ancestry, :name, :parent_id, :description
# Make name fields capitalized on save
def name=(t)
write_attribute(:name, t.to_s.split(' ').map {|w| w.capitalize}.join(' '))
end
# Create some scopes
scope :except, lambda{ |category| where("id <> ?", category.id)}
end
Notes:
I just pasted a bunch of my code, cause I didn't have too much time to parse it down to answer specifically.
Hopefully the way I've written some of my code can help you.
Keep in mind that I'm using:
Mongoid
Rails 3
Cheers,
-Brandon
This is the destroy action for polymorphic association, I am sure you can figure out the Edit :)
VIEW
<%= link_to content_tag(:i, "", class: "icon-trash icons"), [#commentable, comment], method: :delete,
data: { confirm: "Are you sure?" },
title: "Delete" %>
CONTROLLER
def destroy
#comment = Comment.find(params[:id])
#commentable = #comment.commentable
if #comment.destroy
flash[:success] = "Comment Destroyed!"
redirect_to :back
end
end

Resources