How to fix "No Route Matches" error with Rails i18n - ruby-on-rails

Now I'm translating my rails app with rails i18n.
But path tests already passed have shown errors because i18n changed routes.
The error below is one of the errors.
I think my app is working well, so my tests have something wrong though.
I wanna know how to fix it.
Any solutions?
Error
ERROR["test_should_redirect_edit_when_not_logged_in", UsersControllerTest, 3.4624758772552013]
test_should_redirect_edit_when_not_logged_in#UsersControllerTest (3.46s)
  ActionController::UrlGenerationError:
  ActionController::UrlGenerationError: No route matches {:action=>"edit", :controller=>"users", :locale=>#<User id: 762146111,
   name: "Michael Example", email: "michael#example.com", created_at: "2022-11-09 09:53:30", updated_at: "2022-11-09 09:53:30",
   password_digest: "$2a$04$r.WTFttwDcJsuUdBSHQDi.IvFzECkpL37SEdlAKzpdX...", remember_digest: nil,
   admin: true, activation_digest: nil, activated: true, activated_at: "2022-11-09 09:53:30", reset_digest: nil, reset_sent_at: nil>},
   missing required keys: [:id], possible unmatched constraints: [:locale]
   test/controllers/users_controller_test.rb:21:in `block in <class:UsersControllerTest>'
Codes relating to the error
routes.rb
Rails.application.routes.draw do
scope "(:locale)", locale: /en | ja/ do
root 'static_pages#home'
get '/:locale' => 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/search', to: 'movies#search'
get '/show', to: 'movies#show'
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
post '/guest_login', to: 'guest_sessions#create'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users do
member do
get :following, :followers
end
end
resources :movies
resources :account_activations, only: [:edit]
resources :resend_activations, only: [:new, :create]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :comments do
resource :comments, only: [:create, :new, :edit, :update, :destroy]
resource :favorites, only: [:create, :destroy]
end
resources :relationships, only: [:create, :destroy]
end
end
application.rb
require_relative 'boot'
require 'rails/all'
Bundler.require(*Rails.groups)
module Moviest
class Application < Rails::Application
config.load_defaults 5.1
config.i18n.load_path += Dir[Rails.root.join('my/locales/*.{rb,yml}')]
config.i18n.default_locale = :ja
config.action_view.embed_authenticity_token_in_remote_forms = true
config.time_zone = 'Tokyo'
config.active_record.default_timezone = :local
end
end
application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_request
protect_from_forgery with: :exception
include SessionsHelper
def set_request
Thread.current[:request] = request
end
around_action :switch_locale
def switch_locale(&action)
locale = params[:locale] || I18n.default_locale
I18n.with_locale(locale, &action)
end
def default_url_options
{ locale: I18n.locale }
end
〜〜〜〜
end
users_controller_test.rb
require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest
def setup
#user = users(:michael)
#other_user = users(:archer)
end
~
test "should redirect edit when not logged in" do
get edit_user_path(#user)     #error point
assert_not flash.empty?
assert_redirected_to login_url
end
~
end

I fixed the error with defining locale on a test file and calling it when #user was called.
The error occurred because test-driven development was used for the test based on Rails Tutorial, which means that locale is need to be defined on a test file.
Anyway, thank you.

Related

Record Not found Couldn't find Contrato with id

I'm trying to export a docx file using caracal but I'm getting a routing error, but everything seems to be okay.
I did this 3 days ago exactly like now and worked, now I'm getting an error.
Routes.rb
Rails.application.routes.draw do
get 'grayscale/index'
devise_for :users, path: '', path_names: {sign_in: 'login', sign_out: 'logout', sign_up: 'registrar'}
resources :contratos
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
root 'contratos#index'
get 'contratos/page'
end
contratos_controller.rb
class ContratosController < ApplicationController
before_action :authenticate_user!
before_action :set_contrato, only: [:show, :edit, :update, :destroy, :export, :page]
access all: [:show, :index], user: {except: [:destroy, :new, :create, :update, :edit]}, site_admin: :all
require './lib/generate_pdf'
# GET /contratos
# GET /contratos.json
def index
#contratos = Contrato.all
end
# GET /contratos/1
# GET /contratos/1.json
def show
end
# GET /contratos/new
def new
#contrato = Contrato.new
end
# GET /contratos/1/edit
def edit
end
def page
Caracal::Document.save(Rails.root.join("public", "example.docx")) do |docx|
# page 1
docx.h1 'Page 1 Header'
docx.hr
docx.p
docx.h2 'Section 1'
docx.p 'Lorem ipsum dolor....'
docx.p
end
path = File.join(Rails.root, "public")
send_file(File.join(path, "example.docx")
end
show.html.erb
<%= link_to 'Generate Docx', contratos_page_path %>
The full error
ActiveRecord::RecordNotFound in ContratosController#show
Couldn't find Contrato with 'id'=page
def set_contrato
#contrato = Contrato.find(params[:id])
end
This is a very common beginner issue which is due to the fact that routes have precedence in the order they are declared (thus the comment on top of routes.rb).
Since resources :contratos already defines a GET /contratos/:id route it will always match the request for GET /contratos/page to contratos#show. Rails does not assume that your ids are numerical when routing. These paths will all match the GET /contratos/:id route:
GET /contratos/1
GET /contratos/page
GET /contratos/page?foo=bar
GET /contratos/foo-bar-baz
GET /contratos/alksjd-usfiugi%-dfgd
But these will not:
GET /contratos/new # declared before the show route
GET /contratos/1/foo
GET /contratos/foo/bar
You can fix this by moving your custom route to the top:
get 'contratos/page'
resources :contratos
But there is a better Rails way of adding additional restful actions to a resource:
resources :contratos do
get :page, on: :collection
end

Route does not match in test but is recognized in application

I am following Michael Hartl's tutorial, chapter 10. Test UsersControllerTest#test_should_redirect_edit_when_logged_in_as_wrong_user fails when trying to do get edit_user_path(#user).
get edit_user_path(#user)
ActionController::UrlGenerationError: No route matches {:action=>"/users/762146111/edit", :controller=>"users"}
from /Users/cello/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/actionpack-5.1.4/lib/action_dispatch/journey/formatter.rb:55:in `generate'
However:
Rails.application.routes.recognize_path '/users/762146111/edit', method: :get
=> {:controller=>"users", :action=>"edit", :id=>"762146111"}
Below is the code that might have a bug (Rails 5.1.4).
routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
post 'signup', to: 'users#create'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
get 'sessions/new'
resources :users
end
users_controller_test.rb
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
def setup
#user = users(:michael)
#other_user = users(:archer)
end
test 'should redirect edit when logged in as wrong user' do
log_in_as(#other_user)
get edit_user_path(#user)
assert flash.empty?
assert_redirected_to root_url
end
end
users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:edit, :update]
before_action :correct_user, only: [:edit, :update]
def edit
#user = User.find(params[:id])
end
private
def logged_in_user
unless logged_in?
flash[:danger] = 'Please log in.'
redirect_to login_url
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
end
The tutorial is defining an Integration Test (inherits from ActionDispatch::IntegrationTest), whereas your above code is defining a Controller Test (inherits from ActionController::TestCase).
get :edit, ... is the correct syntax for a controller test, because it bypasses URL recognition and directly specifies the :action. This is confusing, and is one of several reasons that controller tests are now discouraged in favour of integration tests, which is probably what you want to create.
To do so, change:
class UsersControllerTest < ActionController::TestCase
to:
class UsersControllerTest < ActionDispatch::IntegrationTest
(Note the tutorial, somewhat confusingly, uses ActionDispatch::IntegrationTest as the base class both for tests it puts in tests/integration/ and those it puts in tests/controllers/.)
You can not use direct url with 'get' method in spec.
In your spec instead of
get edit_user_path(#user)
use
get :edit, params: { id: #user.id }
the same with
patch user_path(#user)
use patch :update instead

Rails - Deploying to Heroku with duplicate routes

I'm deploying my Rails app that uses the clearance gem to Heroku. Everything works fine in development but I'm running into trouble with the gem generated routes I get.
When attempting to deploy to Heroku, I get the error...
ArgumentError: Invalid route name, already in use: 'sign_in'
You may have defined two routes with the same name using the `:as` option, or you may be overriding a route already defined by a resource with the same naming. For the latter, you can restrict the routes created with `resources` as explained here:
remote: http://guides.rubyonrails.org/routing.html#restricting-the-routes-created
I'm not seeing where to restrict the duplicates or where they would be generated with any of my resources:
Please see routes.rb file below
Routes.rb
Rails.application.routes.draw do
resources :passwords, controller: "clearance/passwords", only: [:create, :new]
resource :session, controller: "clearance/sessions", only: [:create]
resources :users, controller: "clearance/users", only: [:create] do
resource :password,
controller: "clearance/passwords",
only: [:create, :edit, :update]
end
get "/sign_in" => "clearance/sessions#new", as: "sign_in"
delete "/sign_out" => "clearance/sessions#destroy", as: "sign_out"
get "/sign_up" => "clearance/users#new", as: "sign_up"
get 'newSignUp', to: 'signups#new'
post 'newSignUp', to: 'signups#create'
get 'newTrip', to: 'trips#new'
post 'newTrip', to: 'trips#create'
get 'trips/:id/send_itinerary' => 'trips#send_itinerary', as: :trips_send_itinerary
root 'static_pages#home'
get 'static_pages/home'
get 'static_pages/help'
get 'static_pages/about'
get 'static_pages/contact'
resources :signups
resources :tripitems
resources :trips
end
This issue has to do with the clearance gem.
I am not totally familiar with the gem, so as per usual, I checked out the github and found the following:
# config/routes.rb
if Clearance.configuration.routes_enabled?
Rails.application.routes.draw do
resources :passwords,
controller: 'clearance/passwords',
only: [:create, :new]
resource :session,
controller: 'clearance/sessions',
only: [:create]
resources :users,
controller: 'clearance/users',
only: Clearance.configuration.user_actions do
resource :password,
controller: 'clearance/passwords',
only: [:create, :edit, :update]
end
get '/sign_in' => 'clearance/sessions#new', as: 'sign_in'
delete '/sign_out' => 'clearance/sessions#destroy', as: 'sign_out'
if Clearance.configuration.allow_sign_up?
get '/sign_up' => 'clearance/users#new', as: 'sign_up'
end
end
end
This is basically creating the same routes for you, only if the config routes_enabled? is true.
You need to configure clearance as follows to handle the routes yourself:
config.routes = false
After looking at the gems GitHub, it looks like I raked the routes earlier and even though config.routes was set to false in the initializer, there was a conflict generate in the generated resources in production.
I wound up deleting the raked routes and making config.routes=true.

No route matches {:action=>"/microposts", :controller=>"microposts", :params=>{:micropost=>{:content=>"Lorem ipsum"}}}

I am currently stuck on the rails tutorial from Michael Hartl (railstutorial.org), Chapter 13
and getting the following two errors:
1) Error:
MicropostsControllerTest#test_should_redirect_create_when_not_logged_in:
ActionController::UrlGenerationError: No route matches {:action=>"/microposts", :controller=>"microposts", :params=>{:micropost=>{:content=>"Lorem ipsum"}}}
test/controllers/microposts_controller_test.rb:11:in 'block (2 levels) in <class:MicropostsControllerTest>'
test/controllers/microposts_controller_test.rb:10:in 'block in <class:MicropostsControllerTest>'
2) Error:
MicropostsControllerTest#test_should_redirect_destroy_when_not_logged_in:
ActionController::UrlGenerationError: No route matches {:action=>"/microposts/499495288", :controller=>"microposts"}
test/controllers/microposts_controller_test.rb:18:in 'block (2 levels) in <class:MicropostsControllerTest>'
test/controllers/microposts_controller_test.rb:17:in 'block in <class:MicropostsControllerTest>'
As far as i know, 'action' should be something like get, post, delete etc.
But i don't know, why it says 'micropost' here.
Content of microposts_controller_test.rb:
require 'test_helper'
class MicropostsControllerTest < ActionController::TestCase
def setup
#micropost = microposts(:orange)
end
test 'should redirect create when not logged in' do
assert_no_difference 'Micropost.count' do
post microposts_path, params: {micropost: {content: 'Lorem ipsum'}}
end
assert_redirected_to login_url
end
test 'should redirect destroy when not logged in' do
assert_no_difference 'Micropost.count' do
delete micropost_path(#micropost)
end
assert_redirected_to login_url
end
end
Content of micropost_controller.rb:
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = 'Micropost created!'
redirect_to root_url
else
render 'static_pages/home'
end
end
def destroy
end
private
def micropost_params
params.require(:micropost).permit(:content)
end
end
Content of routes.rb:
Rails.application.routes.draw do
root 'static_pages#home'
get '/help' => 'static_pages#help'
get '/about' => 'static_pages#about'
get '/contact' => 'static_pages#contact'
get '/signup' => 'users#new'
get '/login' => 'sessions#new'
post '/login' => 'sessions#create'
delete '/logout' => 'sessions#destroy'
resources :users
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :microposts, only: [:create, :destroy]
end
Any help is appreciated.
Thanks
Edit:
In micropost_controller_test.rb, it should've been:
class MicropostsControllerTest < ActionDispatch::IntegrationTest
instead of
class MicropostControllerTest < ActionCntroller::TestCase
The problem with the first test is that you dont have a route in the controller /microposts" on which the test is trying to hit and if you see your routes file it confirms resources :microposts, only: [:create, :destroy], so no route for that. Secondly after destroy it should redirect and in your controller method no redirection is there. So eventually test fails.
In micropost_controller_test.rb, it should've been:
class MicropostsControllerTest < ActionDispatch::IntegrationTest
instead of
class MicropostControllerTest < ActionCntroller::TestCase

How to create an admin user with Ruby on Rails

I'm new to ruby on rails, and so far, I have created the user login, signup, signout, but I wanna create an admin user, and I have no idea how to do this.
route.rb is:
Rails.application.routes.draw do
get 'courses/index'
get 'courses/new'
get 'courses/edit'
get 'courses/show'
get 'course/index'
get 'course/new'
get 'course/edit'
get 'course/show'
get 'sessions/signin'
get 'sessions/main'
get 'sessions/profile'
get 'sessions/setting'
get 'users/new'
get 'home/index'
resources :users, path_names: {new: "signup"} do
collection do
get "main", action: :new
end
end
resources :sessions, path_names: {new: "signin", destroy: "signout"} do
collection do
get :signin
get :main
get :profile
get :setting
end
end
resources :home, only: [], path: "" do
get :about
end
root 'home#index'
resources :courses, only: [:index, :new, :edit, :show]
#resources :course, only: [:index, :new, :edit, :show]
resources :courses
resources :course_groups
patch 'course_groups/:id/add_course', to: 'course_groups#add_course', as: 'course_group_add_course'
delete 'course_groups/:id/delete_course', to: 'course_groups#destory_course', as: 'course_group_destory_course'
resources :rules
patch 'rules/:id/add_group', to: 'rules#add_group', as: 'rule_add_course_group'
delete 'rules/:id/delete_group', to: 'rules#destroy_group', as: 'rule_destroy_course_group'
resources :requirements
patch 'requirements/:id/add_rule', to: 'requirements#add_rule', as: 'requirement_add_rules'
delete 'requirements/:id/delete_rule', to: 'requirements#destroy_rule', as: 'requirement_destroy_rules'
#resources :plans
resources :users do
resources :plans
end
patch 'users/:user_id/plans/:id/add_course', to: 'plans#add_course', as: 'plan_add_course'
delete 'users/:user_id/plans/:id/delete_course', to: 'plans#destory_course', as: 'plan_destory_course'
match '/about', to: 'home#about', via: 'get'
match '/signup', to: 'users#new', via: 'get'
match ':controller(/:action(/:id))(.:format)', via: 'get'
match '/signin', to: 'sessions#new', via: 'get'
match '/signout', to: 'sessions#destroy', via: 'delete'
match '/main', to: 'users#new', via: 'get'
#match '/profile', to: 'sessions#profile', via: 'get'
match '/setting', to: 'sessions#setting', via: 'get'
match '/editname', to: 'users#edit', via: 'get'
match '/show', to: 'users#show', via: 'get'
match '/profile', to: 'users#profile', via: 'get'
#match '/info', to: 'users#info', via: 'get'
#match '/changepass', to: 'users#edit', via: 'get'
end
and my users_controller.rb is:
class UsersController < ApplicationController
before_filter :check_if_signned_in, only: [:show, :edit, :update, :delete]
before_filter :signed_in_user, only: [:edit, :update]
before_filter :skip_password_attribute, only: :update
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to #user, notice: 'User was sucessfully created!'
else
render action: "new"
end
end
#if current user exits, then it can get back to the main page of user
def show
if current_user.nil?
#user = User.find(params[:id])
else
#user = User.find(current_user)
end
end
def edit
##user = User.find(params[:id])
##user = User.find(current_user)
#user = current_user
#user.save
#title = "Edit profile"
end
def update
#user = User.find(params[:id])
##user = User.find(current_user)
#if #user.update_attributes(user_params)
if #user.update_attributes(user_params)
flash[:success] = "Profile updated."
#sign_in #user_params
redirect_to #user
else
#title = "Edit profile"
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def signed_in_user
redirect_to signin_path, notice: "Please sign in." unless signed_in?
end
def edit_user
params.require(:user).permit(:name)
end
def skip_password_attribute
if params[:password].blank? && params[:password_confirmation].blank?
params.except!(:password, :password_confirmation)
end
end
end
the application_controller.rb is:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
#protect_from_forgery with: :exception
protect_from_forgery
#before_filter :session_expire
#before_filter :update_active_time
include SessionsHelper
def check_if_signned_in
deny_access unless signed_in?
end
#def update_active_time
# session[:expires_at] = default_expire
#end
def is_owner
current_user.id == params[:id]
end
#private
# def get_session_time_left
# expire_time = session[:expires_at] #|| default_expire
# a = expire_time
# b = Time.now
# #session_time_left = (a - b).do_i
##session_time_left = (expire_time - Time.now).do_i
# end
end
Anyone can teach me how to create an admin user please? When the admin user logins, it will go to another different page.
Thank you in advance!
Without being rude, your code really needs to be cleaned up.
Your routes alone are a mess - I think I remember giving you some code to help them the other day, and it appears you've used it! However, you've left the other routes in there still - which is likely going to cause big issues down the line
Routes
Firstly, you need to get your routes in order:
#config/routes.rb
resources :courses
resources :users, path_names: {new: "signup"} do
collection do
get "main", action: :new
end
end
resources :sessions, path_names: {new: "signin", destroy: "signout", index: "main", edit: "profile", update: "setting"}
resources :home, only: [], path: "" do
get :about
end
root 'home#index'
resources :courses, only: [:index, :new, :edit, :show]
resources :course_groups do
patch :add_course
delete :delete_course
end
resources :rules do
patch :add_group
delete :delete_group
end
resources :requirements do
patch :add_rule
delete :delete_rule
end
resources :users do
resources :plans do
patch :add_course
delete :delete_course
end
end
match '/about', to: 'home#about', via: 'get'
match '/signup', to: 'users#new', via: 'get'
match ':controller(/:action(/:id))(.:format)', via: 'get'
match '/signin', to: 'sessions#new', via: 'get'
match '/signout', to: 'sessions#destroy', via: 'delete'
match '/main', to: 'users#new', via: 'get'
match '/setting', to: 'sessions#setting', via: 'get'
match '/editname', to: 'users#edit', via: 'get'
Simply, everything in Rails is resource / object - orientated. This includes the routes, and you should therefore make sure that every route you have is constructed around a resource (that's why you can define routes as resources etc)
--
Admin
To create an admin user, there are several options available to you:
Have a separate admin model
Create an admin attribute in your User model
I would strongly recommend including an admin attribute in your User model - make it a boolean column like this:
#new migration
add_column :users, :admin, :boolean
This will allow you to call the following
#user = User.find params[:id]
#user.admin? #-> true / false
This will allow you to use several conditional statements in both the controllers & views of your application, to determine if the user had admin privileges or not

Resources