Routing in Rails making the Username an URL: - ruby-on-rails

In my Rails App there is Device Model - User, and a Registry model( Each user has one registry).
I wanted to change my routes so that instead of:
"http://localhost:3000/registries/3"
it shows:
"http://localhost:3000/erinwalker"
So I changed routes to
match '/:name' => "registries#show"
And the show action in my controller to:
def show
#user = current_user
#user = User.find_by_name!(params[:name])
#registry = #user.registry
And it works, but when I create or update the registry now first it says:
Couldn't find User with name =
app/controllers/registries_controller.rb:21:in `show'
Even though the show action works?
The registries controller:
class RegistriesController < ApplicationController
before_filter :authenticate_user!, :except => :show
load_and_authorize_resource
# GET /registries
# GET /registries.json
def index
#registries = Registry.all
#user = current_user
respond_to do |format|
format.html # index.html.erb
format.json { render json: #registries }
end
end
# GET /registries/1
# GET /registries/1.json
def show
#user = current_user
#user = User.find_by_name!(params[:name])
#registry = #user.registry
respond_to do |format|
format.html # show.html.erb
format.json { render json: #registry }
end
end
# GET /registries/new
# GET /registries/new.json
def new
#registry = Registry.new
#user = current_user
respond_to do |format|
format.html # new.html.erb
format.json { render json: #registry }
end
end
# GET /registries/1/edit
def edit
#registry = Registry.find(params[:id])
#user = current_user
end
# POST /registries
# POST /registries.json
def create
#registry = current_user.build_registry(params[:registry])
#user = current_user
respond_to do |format|
if #registry.save
format.html { redirect_to #registry, notice: 'Registry was successfully created.' }
format.json { render json: #registry, status: :created, location: #registry }
else
format.html { render action: "new" }
format.json { render json: #registry.errors, status: :unprocessable_entity }
end
end
end
# PUT /registries/1
# PUT /registries/1.json
def update
#registry = Registry.find(params[:id])
#user = current_user
respond_to do |format|
if #registry.update_attributes(params[:registry])
format.html { redirect_to #registry, notice: 'Registry was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #registry.errors, status: :unprocessable_entity }
end
end
end
All my Routes:
Mystorkparty::Application.routes.draw do
devise_for :users
resources :registries
root :to => "static_pages#home"
match '/home', to: 'static_pages#home'
match '/:name' => "registries#show"

When you create or update your models, you send POST /registries or PUT /registries/1.
But /registries is matched by your last rule match '/:name' => "registries#show", so the request hits the show action.
If you run rake routes you should see something like this:
POST /registries(.:format) registries#create
PUT /registries/:id(.:format) registries#update
DELETE /registries/:id(.:format) registries#destroy
/:name(.:format) registries#show
You can add method parameter to your route, so that it will hit show only on GET request.
match '/:name' => "registries#show", :via => :get
But there are still can be collisions in the future. For example, if you have registry name users.
So, it's commonly suggested to use prefixes (match '/r/:name') or define set of allowed names, or choose safe names for registries.
P.S. I don't think load_and_authorize_resource will work for your show action by default. Because it expects params[:id] to load the resource automatically.

Related

Nested Namespace Update Action No route matches [PATCH]

Been trying to progress through rails using namspacing, there's been similar questions I think my problem is everything is namespaced.
I tried adding a class to the models (did that before with devise user with nest object and worked) but it doesn't seem to work when dealing with namespaces.
I get a routing error.
No route matches [PATCH] "/backend/membercontacts/1/memberlistings"
routes.rb
# Application Client Backend
namespace :backend do
# Member Routes
resources :membercontacts do
resources :memberaddresses
resources :memberlistings
end
end
backend/memberlisting.rb
class Backend::Memberlisting < ActiveRecord::Base
# Model Relationships
belongs_to :membercontact
end
backend/membercontact.rb
class Backend::Membercontact < ActiveRecord::Base
# Model Relationship
has_many :memberlistings, dependent: :destroy
end
backend/memberlistings_controller.rb
class Backend::MemberlistingsController < ApplicationController
# Security & Action Filters
layout '/backend/application.html.erb'
before_action :set_memberlisting, only: [:show, :edit, :update, :destroy]
# Member Listing Index
def index
membercontact = Backend::Membercontact.find(params[:membercontact_id])
#memberlistings = membercontact.memberlistings.order('mlcontactname ASC')
end
# Detailed Member Listing Profile
def show
membercontact = Backend::Membercontact.find(params[:membercontact_id])
#membercontact = membercontact.memberlistings.find(params[:id])
end
# New Member Listing
def new
membercontact = Backend::Membercontact.find(params[:membercontact_id])
#memberlisting = membercontact.memberlistings.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #membercontact }
end
end
# Edit Member Listing
def edit
membercontact = Backend::Membercontact.find(params[:membercontact_id])
#memberlisting = membercontact.memberlistings.find(params[:id])
end
# Create Member Listing Action
def create
#membercontact = Backend::Membercontact.find(params[:membercontact_id])
#memberlisting = #membercontact.memberlistings.create(memberlisting_params)
respond_to do |format|
if #memberlisting.save
format.html { redirect_to backend_membercontact_memberlistings_path, notice: 'Address for Membercontact was Successfully Created.' }
format.json { render action: 'show', status: :created, location: #memberlisting }
else
format.html { render action: 'new' }
format.json { render json: #memberlisting.errors, status: :unprocessable_entity }
end
end
end
# Update Member Listing Action
def update
respond_to do |format|
if #memberlisting.update(memberlisting_params)
format.html { redirect_to backend_membercontact_memberlistings_path, notice: 'Address for Membercontact was Successfully Updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #memberlisting.errors, status: :unprocessable_entity }
end
end
end
# Delete Member Listing Action
def destroy
#membercontact = Backend::Membercontact.find(params[:membercontact_id])
#memberlisting = #membercontact.memberlistings.find(params[:id])
#memberlisting.destroy
respond_to do |format|
format.html { redirect_to backend_membercontact_memberlistings_path, notice: 'Address for Membercontact was Successfully Deleted.' }
format.json { head :no_content }
end
end
private
def set_memberlisting
#memberlisting = Backend::Memberlisting.find(params[:id])
end
def memberlisting_params
params.require(:backend_memberlisting).permit(:mlcontactname, :mlcompanyname, :mladdressline1, :mladdressline2, :mlcity, :mlprovince, :mlpostalcode, :mlphone, :mlwebsite, :mlemail, :membercontact_id, :account_id)
end
end
Routes Rake
backend_membercontact_memberlistings_path GET /backend/membercontacts/:membercontact_id/memberlistings(.:format) backend/memberlistings#index
POST /backend/membercontacts/:membercontact_id/memberlistings(.:format) backend/memberlistings#create
new_backend_membercontact_memberlisting_path GET /backend/membercontacts/:membercontact_id/memberlistings/new(.:format) backend/memberlistings#new
edit_backend_membercontact_memberlisting_path GET /backend/membercontacts/:membercontact_id/memberlistings/:id/edit(.:format) backend/memberlistings#edit
backend_membercontact_memberlisting_path GET /backend/membercontacts/:membercontact_id/memberlistings/:id(.:format) backend/memberlistings#show
PATCH /backend/membercontacts/:membercontact_id/memberlistings/:id(.:format) backend/memberlistings#update
PUT /backend/membercontacts/:membercontact_id/memberlistings/:id(.:format) backend/memberlistings#update
DELETE /backend/membercontacts/:membercontact_id/memberlistings/:id(.:format) backend/memberlistings#destroy
So far I've tried to work with classes (still learning) in the models file and have tried some variations like below. Also the reason why I define the url path, it defaulted to backend_membercontact_backend_memberlisting
, class_name: 'Backend::Memberlisting'
<%= form_for([#memberlisting.membercontact, #memberlisting], url: backend_membercontact_memberlistings_path, method: :post) do |f| %>
No route matches [PATCH] "/backend/membercontacts/1/memberlistings"
From your rake routes output, the path is backend_membercontact_memberlisting_path( no s memberlisting) not backend_membercontact_memberlistings_path
Also, you should change method: post to method: :patch
<%= form_for([#memberlisting.membercontact, #memberlisting], url: backend_membercontact_memberlisting_path, method: :patch) do |f| %>
Try to use
<%= form_for([#memberlisting.membercontact, #memberlisting], url: backend_membercontact_memberlisting_path, method: :post) do |f| %>
Your are using backend_membercontact_memberlistings_path according to your routes is should be backend_membercontact_memberlisting_path
Hope this works.

Association not recognized, undefined method error

I created 2 scaffolds 'mepager' and 'pimp' and linked the models like this:
class Mepager < ActiveRecord::Base
belongs_to :pimp
end
class Pimp < ActiveRecord::Base
has_one :mepager
end
I added following lines to my routing:
resources :pimps do
resources :mepagers
end
And my mepager create action looks like this:
def create
#mepager = #pimp.build_mepager(mepager_params)
respond_to do |format|
if #mepager.save
format.html { redirect_to #mepager, notice: 'Mepager was successfully created.' }
format.json { render action: 'show', status: :created, location: #mepager }
else
format.html { render action: 'new' }
format.json { render json: #mepager.errors, status: :unprocessable_entity }
end
end
end
And for #pimp to set
def setPimp
#pimp = Pimp.find_by_id(:pimp_id)
end
But if I try to create a new mepager at pimps/1/mepagers/new I get an undefined method build_mepager error.
undefined method `build_mepager' for nil:NilClass
I tried the same in the rails console and it worked with just that method. So I guess somethings still missing to make it work on my web application.
Btw I m using rails 4.0.0
Regards!
Routing
Prefix Verb URI Pattern Controller#Action
pimps GET /pimps(.:format) pimps#index
POST /pimps(.:format) pimps#create
new_pimp GET /pimps/new(.:format) pimps#new
edit_pimp GET /pimps/:id/edit(.:format) pimps#edit
pimp GET /pimps/:id(.:format) pimps#show
PATCH /pimps/:id(.:format) pimps#update
PUT /pimps/:id(.:format) pimps#update
DELETE /pimps/:id(.:format) pimps#destroy
root GET / pimps#index
pimp_mepagers GET /pimps/:pimp_id/mepagers(.:format) mepagers#index
POST /pimps/:pimp_id/mepagers(.:format) mepagers#create
new_pimp_mepager GET /pimps/:pimp_id/mepagers/new(.:format) mepagers#new
edit_pimp_mepager GET /pimps/:pimp_id/mepagers/:id/edit(.:format) mepagers#edit
pimp_mepager GET /pimps/:pimp_id/mepagers/:id(.:format) mepagers#show
PATCH /pimps/:pimp_id/mepagers/:id(.:format) mepagers#update
PUT /pimps/:pimp_id/mepagers/:id(.:format) mepagers#update
DELETE /pimps/:pimp_id/mepagers/:id(.:format) mepagers#destroy
GET /pimps(.:format) pimps#index
POST /pimps(.:format) pimps#create
GET /pimps/new(.:format) pimps#new
GET /pimps/:id/edit(.:format) pimps#edit
GET /pimps/:id(.:format) pimps#show
PATCH /pimps/:id(.:format) pimps#update
PUT /pimps/:id(.:format) pimps#update
DELETE /pimps/:id(.:format) pimps#destroy
Altered line in pimp controller
def create
#pimp = Pimp.new(pimp_params)
respond_to do |format|
if #pimp.save
format.html { redirect_to new_pimp_mepager_path, notice: 'Product Improvement was successfully created.' }
format.json { render action: 'show', status: :created, location: #pimp }
else
format.html { render action: 'new' }
format.json { render json: #pimp.errors, status: :unprocessable_entity }
end
end
end
mepager controller
class MepagersController < ApplicationController
before_action :set_mepager, only: [:show, :edit, :update, :destroy]
def setPimp
#pimp = Pimp.find(params[:pimp_id])
end
# GET /mepagers
# GET /mepagers.json
def index
#mepagers = Mepager.all
end
# GET /mepagers/1
# GET /mepagers/1.json
def show
end
# GET /mepagers/new
def new
#mepager = Mepager.new
end
# GET /mepagers/1/edit
def edit
end
# POST /mepagers
# POST /mepagers.json
def create
raise params[:pimp_id].inspect
#mepager = #pimp.build_mepager(mepager_params)
respond_to do |format|
if #mepager.save
format.html { redirect_to #mepager, notice: 'Mepager was successfully created.' }
format.json { render action: 'show', status: :created, location: #mepager }
else
format.html { render action: 'new' }
format.json { render json: #mepager.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /mepagers/1
# PATCH/PUT /mepagers/1.json
def update
respond_to do |format|
if #mepager.update(mepager_params)
format.html { redirect_to #mepager, notice: 'Mepager was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #mepager.errors, status: :unprocessable_entity }
end
end
end
# DELETE /mepagers/1
# DELETE /mepagers/1.json
def destroy
#mepager.destroy
respond_to do |format|
format.html { redirect_to mepagers_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_mepager
#mepager = Mepager.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def mepager_params
params.require(:mepager).permit(:pre, :post, :comment, :save_h, :save_c, :save_other, :save_otherv, :affect_design, :affect_stress, :affect_me, :affect_other, :affect_dno, :affect_mid, :affect_otherdoc, :owner, :pps, :reference)
end
end
The problem is probably here:
#pimp = Pimp.find_by_id(:pimp_id)
what you are doing is finding a pimp that has an id of the literal symbol :pimp_id (which is highly unlikely to exist)
Probably what you want is the parameter: params[:pimp_id]
EDIT:
and for the second part.
for the create action to work with a real #pimp (that can be found by :pimp_id), you must also do the same on the new action too!
Otherwise create won't work.
Can I recommend something like...
before_action :fetch_pimp, only: [:new, :create]
# GET /mepagers/new
def new
#mepager = Mepager.new
end
# POST /mepagers
# POST /mepagers.json
def create
#mepager = #pimp.build_mepager(mepager_params)
respond_to do |format|
# as previously...
end
end
# ...
private
def fetch_pimp
#pimp = Pimp.find(params[:pimp_id])
end

Authenticating to devise

I want to be able to simply determine if a user credentials are correctly supplied in an iOS app I'm creating.
The way I have it setup now is with a sessions_controller.rb that handles and returns a user token. The problem is if I want to still log on through the web (not just a check via curl or similar), it doesn't authenticate and spits out
{"success":false,"message":"Error with your login or password"}
So my question is...how can I do authentication and still keep my web-login operational? Here are my related files. My hope was I could curl to a url such as localhost:3000/auth_checks and get one type of authentication response, and continue to have my users login through localhost:3000/sign_in.
From devise.rb
config.skip_session_storage = [:http_auth, :token_auth]
config.token_authentication_key = :auth_token
From routes.rb
resources :clippings
root to: 'clippings#index'
#devise_for :users
resources :auth_checks
devise_for(:users, :controllers => { :sessions => "sessions" })
resources :posts do
end
From auth_checks_controller.rb
class AuthChecksController < ApplicationController
before_filter :authenticate_user!
# GET /auth_checks
# GET /auth_checks.json
def index
#auth_checks = AuthCheck.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #auth_checks }
end
end
# GET /auth_checks/1
# GET /auth_checks/1.json
def show
#auth_check = AuthCheck.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #auth_check }
end
end
# GET /auth_checks/new
# GET /auth_checks/new.json
def new
#auth_check = AuthCheck.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #auth_check }
end
end
# GET /auth_checks/1/edit
def edit
#auth_check = AuthCheck.find(params[:id])
end
# POST /auth_checks
# POST /auth_checks.json
def create
#auth_check = AuthCheck.new(params[:auth_check])
respond_to do |format|
if #auth_check.save
format.html { redirect_to #auth_check, notice: 'Auth check was successfully created.' }
format.json { render json: #auth_check, status: :created, location: #auth_check }
else
format.html { render action: "new" }
format.json { render json: #auth_check.errors, status: :unprocessable_entity }
end
end
end
# PUT /auth_checks/1
# PUT /auth_checks/1.json
def update
#auth_check = AuthCheck.find(params[:id])
respond_to do |format|
if #auth_check.update_attributes(params[:auth_check])
format.html { redirect_to #auth_check, notice: 'Auth check was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #auth_check.errors, status: :unprocessable_entity }
end
end
end
# DELETE /auth_checks/1
# DELETE /auth_checks/1.json
def destroy
#auth_check = AuthCheck.find(params[:id])
#auth_check.destroy
respond_to do |format|
format.html { redirect_to auth_checks_url }
format.json { head :no_content }
end
end
end
Learned a lot...here's what I ended up doing. If you're in this position, I highly recommend putting in the time (not very much) to do this method. http://www.cocoahunter.com/blog/2013/02/13/restful-api-authentication/
If you're like me, you already have a userbase using the standard devise login structure.
I added this to my routes.rb
namespace :api do
namespace :v1 do
resources :tokens,:only => [:create, :destroy]
end
end
Then created and added tokens_controller.rb inside of controllers/api/v1/ (which I created)
# encoding: utf-8
class Api::V1::TokensController < ApplicationController
skip_before_filter :verify_authenticity_token
respond_to :json
def create
email = params[:email]
password = params[:password]
if request.format != :json
render :status=>406, :json=>{:message=>"The request must be json"}
return
end
if email.nil? or password.nil?
render :status=>400,
:json=>{:message=>"The request must contain the user email and password."}
return
end
#user=User.find_by_email(email.downcase)
if #user.nil?
logger.info("User #{email} failed signin, user cannot be found.")
render :status=>401, :json=>{:message=>"Invalid email or passoword."}
return
end
# http://rdoc.info/github/plataformatec/devise/master/Devise/Models/TokenAuthenticatable
#user.ensure_authentication_token!
if not #user.valid_password?(password)
logger.info("User #{email} failed signin, password \"#{password}\" is invalid")
render :status=>401, :json=>{:message=>"Invalid email or password."}
else
render :status=>200, :json=>{:token=>#user.authentication_token}
end
end
def destroy
#user=User.find_by_authentication_token(params[:id])
if #user.nil?
#logger.info(“Token wasnot found.”)
#render :status=>404, :json=>{:message=>”Invalid token.”}
else
#user.reset_authentication_token!
render :status=>200, :json=>{:token=>params[:id]}
end
end
end
That's literally all I had to do. I can now test to this api for authentication from my iOS app. Hope that makes sense to someone out there!

No route matches {:action=>"show", :controller=>"controller_name"}

I'm always getting this error and I don't understand where I am wrong. I think I have everything I need action in controller, resources in route file and view for controller action. I put the route current_events/new in the browser when I get this error. I also try with just resources :current_events
output of rake routes:
current_events GET /current_events(.:format) current_events#index
new_current_event GET /current_events/new(.:format) current_events#new
current_event GET /current_events/:id(.:format) current_events#show
config/routes.rb:
appname::Application.routes.draw do
devise_for :users, controllers: { omniauth_callbacks: "omniauth_callbacks"}
resources :current_events, only: [:show, :new, :index]
end
CurrentEventsController:
class CurrentEventsController < ApplicationController
def index
#current_event = CurrentEvent.all
respond_to do |format|
format.html
format.json { render json: #current_event }
end
end
def create
#current_event = CurrentEvent.new(params[:current_event])
respond_to do |format|
if #current_event.save
format.html { redirect_to #current_event, notice: 'current event was created.' }
format.json { render json: #current_event, status: :created, location: #current_event }
else
format.html { render action: "new" }
format.json {render json: #current_event.errors, status: :unprocessable_entity}
end
end
end
def new
#current_event = CurrentEvent.new
respond_to do |format|
format.html
format.json { render json: #current_event }
end
end
def edit
#current_event = CurrentEvent.find(params[:id])
end
def destroy
#current_event = CurrentEvent.find(params[:id])
#current_event.destroy
respond_to do |format|
format.html { redirect_to current_event_url}
format.json { head :no_content }
end
end
def show
#current_event = CurrentEvent.find(params[:id])
respond_to do |format|
format.html
format.json{ render json: #current_event}
end
end
end
I forgot to say, I am trying to go to new page so in browser I say current_events/new
It will still throw an error if there's a link that doesn't work on the page.
In your view, the link should look something like this:
<%= link_to "name of current event", current_event_path(#current_event) %>
update
based on your rake routes
current_event GET /current_events/:id(.:format) #note ":id"
when you're trying to see a specific current event, you need to pass it an :id which makes sense - if you're trying to call a specific person you need to use their telephone number. so your code should look like this:
<%= link_to 'Back', current_event_path(#event) %>
But keep in the mind that #event won't work unless you define it correctly in the controller action for this view.

Generating scaffolding for existing model

Newbie to RoR here. I have built models with no namespace. One of them is called 'Brand'. I then proceeded to use rails g "admin/brands" to put maintenance functionality under an admin namespace, using rails generate scaffolding_controller "admin/brand" - which produced the views and the controller. The unit tests fail when I rake test:
NoMethodError: undefined method `admin_brands' for #<Admin::BrandsControllerTest:0x1034c0730>
test/functional/admin/brands_controller_test.rb:5:in `_callback_before_193'
in routes.rb I have:
# Administration routes
namespace :admin do
resources :brands
end
The generated controller code is as follows:
class Admin::BrandsController < ApplicationController
# GET /admin/brands
# GET /admin/brands.json
def index
#admin_brands = Brand.all
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #admin_brands }
end
end
# GET /admin/brands/1
# GET /admin/brands/1.json
def show
#admin_brand = Brand.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #admin_brand }
end
end
# GET /admin/brands/new
# GET /admin/brands/new.json
def new
#admin_brand = Brand.new
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #admin_brand }
end
end
# GET /admin/brands/1/edit
def edit
#admin_brand = Brand.find(params[:id])
end
# POST /admin/brands
# POST /admin/brands.json
def create
#admin_brand = Brand.new(params[:admin_brand])
respond_to do |format|
if #admin_brand.save
format.html { redirect_to #admin_brand, :notice => 'Brand was successfully created.' }
format.json { render :json => #admin_brand, :status => :created, :location => #admin_brand }
else
format.html { render :action => "new" }
format.json { render :json => #admin_brand.errors, :status => :unprocessable_entity }
end
end
end
# PUT /admin/brands/1
# PUT /admin/brands/1.json
def update
#admin_brand = Brand.find(params[:id])
respond_to do |format|
if #admin_brand.update_attributes(params[:admin_brand])
format.html { redirect_to #admin_brand, :notice => 'Brand was successfully updated.' }
format.json { head :no_content }
else
format.html { render :action => "edit" }
format.json { render :json => #admin_brand.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /admin/brands/1
# DELETE /admin/brands/1.json
def destroy
#admin_brand = Brand.find(params[:id])
#admin_brand.destroy
respond_to do |format|
format.html { redirect_to admin_brands_url }
format.json { head :no_content }
end
end
end
Not sure how to debug this type of issue... I gather that paths are messed up somehow, but that is much as I can fathom at this point. Help appreciated.
In our company we dont use scaffold, especially when we need to generate admin namespace.
You can simply write admin namespace yourself.
config/routes.rb
namespace :admin do
root :to => "base#index"
resources :pages
# resources :states do
# member do
# get :make_default
# end
# end
end
app/controllers/admin/base_controller.rb
class Admin::BaseController < ApplicationController
before_filter :authenticate_user!, :admin_user?
layout "admin/admin"
def index
#page = Page.all
end
private
def admin_user?
redirect_to root_path, :alert => 'This page is allowed for admin' unless current_user.admin
end
end
app/views/admin/base/index.html.haml
= link_to "New Post", new_admin_post_path
%ul
- #post.each do |post|
%li= post.title

Resources