devise_scope in namespaces, rails api_only mode - ruby-on-rails

I'm trying to remove the obsolete routes of devise, in my api_only rails setup. However i'm in a fuss about how to define them properly with devise_scope. I have the following routes.rb:
# config/routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :users do
devise_scope :user do
resource :confirmations, only: %i[create show], format: false
end
end
end
end
Which refers to the confirmations_controller that contains custom json renders instead of the typical respond_with:
# app/controllers/api/users/confirmations_controller.rb
module Api
module Users
class ConfirmationsController < Devise::ConfirmationsController
# POST /resource/confirmation
def create
self.resource = resource_class.send_confirmation_instructions(resource_params)
yield resource if block_given?
if successfully_sent?(resource)
# respond_with({}, location: after_resending_confirmation_instructions_path_for(resource_name))
render json: { status: 201 }, status: :created
else
# respond_with(resource)
render json: { status: 422, errors: resource.errors.keys },
status: :unprocessable_entity
end
end
# GET /resource/confirmation?confirmation_token=abcdef
def show
self.resource = resource_class.confirm_by_token(params[:confirmation_token])
yield resource if block_given?
if resource.errors.empty?
set_flash_message!(:notice, :confirmed)
# respond_with_navigational(resource) { redirect_to after_confirmation_path_for(resource_name, resource) }
render json: { status: 200 }, status: :ok
else
# respond_with_navigational(resource.errors, status: :unprocessable_entity) { render :new }
render json: { status: 422, errors: resource.errors.keys },
status: :unprocessable_entity
end
end
end
end
end
As can be seen in the routes.rb I only need the create and show endpoints of confirmations. However the current definition results in the following error when running rspec:
Failure/Error: get api_users_confirmations_path, params: { confirmation_token: 'incorrect_token' }
AbstractController::ActionNotFound:
Could not find devise mapping for path "/api/users/confirmations?confirmation_token=incorrect_token".
This may happen for two reasons:
1) You forgot to wrap your route inside the scope block. For example:
devise_scope :user do
get "/some/route" => "some_devise_controller"
end
2) You are testing a Devise controller bypassing the router.
If so, you can explicitly tell Devise which mapping to use:
#request.env["devise.mapping"] = Devise.mappings[:user]
Which tends mostly to the missing devise mapping, considering that the devise_scope is defined properly. However i'm not sure how to solve this properly without having to include the bindings in every devise controller. Is this doable from the routes?

I have never tried to use resources inside of devise_scope.
This is how I have defined it.
devise_scope :user do
delete 'logout', to: 'devise/sessions#destroy'
end
This is how I have defined in one of my application
devise_for :users, path: 'api/v1/accounts', controllers: {
:registrations => 'api/v1/accounts/registrations',
:sessions => 'api/v1/accounts/sessions',
:passwords => 'api/v1/accounts/passwords'
}
devise_scope :user do
get '/sessions/new' => "sessions#new", :as => :new_sessions
get '/sessions/forgot_password' => "sessions#forgot_password", :as => :forgot_password
post '/validate_referral_code' => 'validates#validate_referral_code', as: :validate_referral_code
post '/validate_employment_code' => 'validates#validate_employment_code', as: :validate_employment_code
post '/get_weather' => 'temperature#weather', as: :weather
get '/fetch' => 'zip_codes#fetch', as: :fetch_zip_code
post '/request_demo' => 'demos#create', as: :create
end
namespace :api do
namespace :v1 do
scope :accounts do
resources :third_party_logins, only: [] do
collection do
get :action_name
end
end
end
end
end

Related

Rails (5.2.0) reCaptcha Devise (4.4.3) undefined method users_url

Got an error on tests. Development and production shows no error.
Error:
UsersTest#test_creating_a_user:
NoMethodError: undefined method `users_url' for #<RegistrationsController:0x00007fd3e69d1a18>
Did you mean? user_session_url
Did you mean? user_session_url
app/controllers/registrations_controller.rb:11:in `check_captcha'
Got a devise registration controller:
class RegistrationsController < Devise::RegistrationsController
prepend_before_action :check_captcha, only: [:create]
private
def check_captcha
unless verify_recaptcha
self.resource = resource_class.new sign_up_params
resource.validate # Look for any other validation errors besides Recaptcha
set_minimum_password_length
respond_with resource
# self.resource = resource_class.new sign_up_params
# respond_with_navigational(resource) { render :new }
end
end
def after_inactive_sign_up_path_for resource
new_user_session_path
end
end
Routing
Rails.application.routes.draw do
devise_for :users, only: :omniauth_callbacks, controllers: {omniauth_callbacks: 'omniauth_callbacks'}
scope '(:locale)', :locale => /en|ru/ do
root :to => 'index#index'
devise_for :users, :controllers => {
:registrations => 'registrations',
:invitations => 'invitations'
},
:path => 'auth',
:path_names => {
:sign_in => 'login',
:sign_out => 'logout',
:password => 'secret',
:confirmation => 'verification',
:unlock => 'unblock',
:registration => 'register',
:sign_up => 'cmon_let_me_in'
},
skip: :omniauth_callbacks
end
Resource passed to respond_with resource is valid.
What's that?!?!
I think because of your views.
checkout your views "/devise/registrations/new.html.erb" and make sure you put <%= recaptcha_tags %> on sign up button /devise/registrations/new.html.erb
And here is my simple routes:
routes.rb
This error happens when resource is valid itself, but captcha didn't pass. Devise thinks that model is persisted (but it has been validated only) and tries to redirect to it.
Solution is to replace respond_with resource to render :new in check_captcha method.
Now bypassing it like that
def check_captcha
return true if Rails.env.test?
unless verify_recaptcha
self.resource = resource_class.new sign_up_params
resource.validate # Look for any other validation errors besides Recaptcha
set_minimum_password_length
respond_with resource
end
end
also does not work without recaptcha, check devise docs to get it working standalone first

undefined method `update' for nil:NilClass. Friendly_id and Scaffolded controller

I am getting this error saying undefined method update. The show and Edit buttons work well. It's just the update that isn't working. I am using friendly_id and the controller is scaffolded with better routing.
Here is my routes:
Rails.application.routes.draw do
resources :static_pages, except: [:show, :edit]
devise_for :users
mount Bootsy::Engine => '/bootsy', as: 'bootsy'
get 'profile' => "users#profile"
root 'indexes#index'
get ':slug', to: 'static_pages#show'
get ':slug/edit', to: 'static_pages#edit'
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
# root 'welcome#index'
# Example of regular route:
# get 'products/:id' => 'catalog#view'
# Example of named route that can be invoked with purchase_url(id: product.id)
# get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
# Example resource route (maps HTTP verbs to controller actions automatically):
# resources :products
# Example resource route with options:
# resources :products do
# member do
# get 'short'
# post 'toggle'
# end
#
# collection do
# get 'sold'
# end
# end
# Example resource route with sub-resources:
# resources :products do
# resources :comments, :sales
# resource :seller
# end
# Example resource route with more complex sub-resources:
# resources :products do
# resources :comments
# resources :sales do
# get 'recent', on: :collection
# end
# end
# Example resource route with concerns:
# concern :toggleable do
# post 'toggle'
# end
# resources :posts, concerns: :toggleable
# resources :photos, concerns: :toggleable
# Example resource route within a namespace:
# namespace :admin do
# # Directs /admin/products/* to Admin::ProductsController
# # (app/controllers/admin/products_controller.rb)
# resources :products
# end
end
Here is the static page controller for the update method:
class StaticPagesController < ApplicationController
before_action :set_static_page, only: [:show, :edit, :update]
before_action :static_page_params, only: [:update, :create]
before_action :destroy_static_page, only: :destroy
before_filter :except => [:show] do
redirect_to :new_user_session unless current_user && current_user.role == 5
end
# GET /static_pages
# GET /static_pages.json
def index
#static_pages = StaticPage.all
end
# GET /static_pages/1
# GET /static_pages/1.json
def show
end
# GET /static_pages/new
def new
#static_page = StaticPage.new
end
# GET /static_pages/1/edit
def edit
end
# POST /static_pages
# POST /static_pages.json
def create
#static_page = StaticPage.new(static_page_params)
respond_to do |format|
if #static_page.save
format.html { redirect_to #static_page, notice: 'Static page was successfully created.' }
format.json { render :show, status: :created, location: #static_page }
else
format.html { render :new }
format.json { render json: #static_page.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /static_pages/1
# PATCH/PUT /static_pages/1.json
def update
respond_to do |format|
if #static_page.update(static_page_params)
format.html { redirect_to #static_page, notice: 'Static page was successfully updated.' }
format.json { render :show, status: :ok, location: #static_page }
else
format.html { render :edit }
format.json { render json: #static_page.errors, status: :unprocessable_entity }
end
end
end
# DELETE /static_pages/1
# DELETE /static_pages/1.json
def destroy
#static_page.destroy
respond_to do |format|
format.html { redirect_to static_pages_url, notice: 'Static page was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_static_page
#static_page = StaticPage.find_by_slug(params[:slug])
end
def destroy_static_page
#static_page = StaticPage.find_by_slug(params[:slug])
end
# Never trust parameters from the scary internet, only allow the white list through.
def static_page_params
params.require(:static_page).permit(:title, :video_id, :content, :tags, :author, :date, :slug)
end
end
Here are the views that have been scaffolded for the edit page:
%h1 Editing static_page
= render 'form'
= link_to 'Show', #static_page
\|
= link_to 'Back', static_pages_path
Edit: It has to do with my routes. I can't get the show button to work on the fourm bellow. I try #static_page.slug but that takes me to /page_name/page_name. When I just do #static_page it takes me to /static_page/page_name. What I need it to do is take me to /page_name.
Edit 2: Here is the form partial:
= simple_form_for(#static_page) do |f|
= f.error_notification
.form-inputs
= f.input :title
= f.input :video_id
.bootsy_text_area
= f.bootsy_area :content, :class => 'form-control', rows: 12
= f.input :tags
= f.input :author
= f.input :date
.form-actions
= f.button :submit
You're calling #static_page.update(static_page_params), but in this case, #static_page is nil.
You've defined #static_page as #static_page = StaticPage.find_by_slug(params[:slug]), but this will return nil if no page with that slug is found.
There are two ways you can handle this:
Use find_by_slug! instead, which will raise an exception if no record is found, or:
Check for the presence of nil yourself, and decide how you want to handle it.

Error when editing a page: param is missing or the value is empty: static_page

I am having a hard time creating the routes to my pages. I have show which works but edit keeps popping up with an error. The current code is a modification of this railscast along with the friendly ID gem. Thank you if you can help.
Here is the controller:
class StaticPagesController < ApplicationController
before_action :set_static_page, only: [:show, :edit, :update, :destroy]
before_filter :except => [:show] do
redirect_to :new_user_session unless current_user && current_user.role == 5
end
# GET /static_pages
# GET /static_pages.json
def index
#static_pages = StaticPage.all
end
# GET /static_pages/1
# GET /static_pages/1.json
def show
end
# GET /static_pages/new
def new
#static_page = StaticPage.new
end
# GET /static_pages/1/edit
def edit
#static_page = StaticPage.find_by_slug(params[:slug])
end
# POST /static_pages
# POST /static_pages.json
def create
#static_page = StaticPage.new(static_page_params)
respond_to do |format|
if #static_page.save
format.html { redirect_to #static_page, notice: 'Static page was successfully created.' }
format.json { render :show, status: :created, location: #static_page }
else
format.html { render :new }
format.json { render json: #static_page.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /static_pages/1
# PATCH/PUT /static_pages/1.json
def update
respond_to do |format|
if #static_page.update(static_page_params)
format.html { redirect_to #static_page, notice: 'Static page was successfully updated.' }
format.json { render :show, status: :ok, location: #static_page }
else
format.html { render :edit }
format.json { render json: #static_page.errors, status: :unprocessable_entity }
end
end
end
# DELETE /static_pages/1
# DELETE /static_pages/1.json
def destroy
#static_page.destroy
respond_to do |format|
format.html { redirect_to static_pages_url, notice: 'Static page was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_static_page
#static_page = StaticPage.find_by_slug(params[:slug])
end
# Never trust parameters from the scary internet, only allow the white list through.
def static_page_params
params.require(:static_page).permit(:title, :permalink, :content, :tags, :author, :date)
end
end
Here is the route:
Rails.application.routes.draw do
resources :static_pages, except: [:show, :delete, :update]
devise_for :users
mount Bootsy::Engine => '/bootsy', as: 'bootsy'
get 'profile' => "users#profile"
root 'indexes#index'
get ':slug', to: 'static_pages#show'
get ':slug', to: 'static_pages#delete'
get ':slug/edit', to: 'static_pages#update'
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
# root 'welcome#index'
# Example of regular route:
# get 'products/:id' => 'catalog#view'
# Example of named route that can be invoked with purchase_url(id: product.id)
# get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
# Example resource route (maps HTTP verbs to controller actions automatically):
# resources :products
# Example resource route with options:
# resources :products do
# member do
# get 'short'
# post 'toggle'
# end
#
# collection do
# get 'sold'
# end
# end
# Example resource route with sub-resources:
# resources :products do
# resources :comments, :sales
# resource :seller
# end
# Example resource route with more complex sub-resources:
# resources :products do
# resources :comments
# resources :sales do
# get 'recent', on: :collection
# end
# end
# Example resource route with concerns:
# concern :toggleable do
# post 'toggle'
# end
# resources :posts, concerns: :toggleable
# resources :photos, concerns: :toggleable
# Example resource route within a namespace:
# namespace :admin do
# # Directs /admin/products/* to Admin::ProductsController
# # (app/controllers/admin/products_controller.rb)
# resources :products
# end
end
Here is the form where the error keeps poping up on form.html.haml:
= simple_form_for(#static_page) do |f|
= f.error_notification
.form-inputs
= f.input :title
= f.input :permalink
.bootsy_text_area
= f.bootsy_area :content, :class => 'form-control', rows: 12
= f.input :tags
= f.input :author
= f.input :date
.form-actions
= f.button :submit
In your routes you have:
get ':slug/edit', to: 'static_pages#update'
Where you're sending a GET request to your update action, rather than your edit action.
Change that to:
get ':slug/edit', to: 'static_pages#edit'
You're also setting the same get :slug route to two different actions as well. The one pointing to the destroy action should be a DELETE request rather than a GET request.
One more thing, you can remove #static_page = StaticPage.find_by_slug(params[:slug]) from your controller edit action, since you're already setting the #static_page variable in your set_static_page in your before_action.

Devise - creating users only by admin

I'm creating an app where I need only admins to create new users:
routes.rb:
devise_for :users, :skip => [:registrations]
resources :users
root 'dashboard#index'
users_controller.rb
# GET /users/1/edit
#def edit
#
#end
# POST /users
# POST /users.json
def create
build_resource(sign_up_params)
respond_to do |format|
if resource.save
format.html { redirect_to user_path(resource), notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: user }
else
clean_up_passwords resource
respond_with resource
end
end
end
When I open http://localhost:3000/users/new
I got this error:
AbstractController::ActionNotFound at /users/new
Could not find devise mapping for path "/users/new".
This may happen for two reasons:
1) You forgot to wrap your route inside the scope block. For example:
devise_scope :user do
get "/some/route" => "some_devise_controller"
end
2) You are testing a Devise controller bypassing the router.
If so, you can explicitly tell Devise which mapping to use:
#request.env["devise.mapping"] = Devise.mappings[:user]
What is wrong there? Thank you a lot!
The problem is that you're confusing Devise functionality with that of your app:
#config/routes.rb
resources :users #-> nothing to do with devise
When you create a user, you're using the devise build_resource helper. Problem being that this will require devise functionality, which is not going to happen for users_controller.
To use sign_up_params or build_resource, you'll have to scope your routes to a devise controller (so all the available session data is there)...
#config/routes.rb
devise_for :user, skip: [:registrations]
devise_scope :user do
resources :users, path: "", only: [:new, :create], controller: "registrations" #-> url.com/users/new
end
This way, you'll be able to override the standard Devise::RegistrationsController with your own code:
#app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
before_action :authenticate_user!
before_action :check_admin
def create
build_resource(sign_up_params)
...
end
private
def check_admin
redirect_to root_path unless current_user.admin?
end
end
--
What I would recommend is either removing the Devise functionality from your users controller, or overriding the registrations controller so that only an admin can create a user (which it seems you're trying to do already).

redirect_to leading to a different action than requested

I'm new in Ruby on Rails and this is my first post on stackoverflow.
I have this page where the user can select which features he wants. Since he selected the features, the action brings him to a page where he can see the attributes of the selected features and for each of the selected features there is a link for editing the feature.
I can perfectly edit and update the feature, but I'd like the action update to redirect to that page with the selected features so that the user wouldn't have to select them again.
The problem is that when I do redirect_to select_multiple_features_path, it is not leading to the select_multiple action. I'm getting the following error:
ActiveRecord::RecordNotFound in FeaturesController#show
Couldn't find Feature with id=select_multiple
Server output: http://img138.imageshack.us/img138/1382/sfp4.jpg
Controllers:
def edit
#feature = Feature.find(params[:id])
end
def update
#feature = Feature.find(params[:id])
respond_to do |format|
if #feature.update_attributes(params[:feature])
format.html { redirect_to select_multiple_features_path, notice: 'Feature was successfully updated.' }
else
format.html { render action: "edit" }
format.json { render json: #feature.errors, status: :unprocessable_entity }
end
end
end
def select_multiple
if params[:features_ids].nil?
#features = Feature.find(session[:features_ids])
else
#features = Feature.find(params[:features_ids])
session[:features_ids] = params[:features_ids]
end
end
Routes:
resources :attached_assets
resources :modifications
resources :maps
resources :coordinates
resources :results do
collection do
post 'select_number_of_groups'
end
end
resources :features do
collection do
post 'select_multiple'
end
end
resources :home
devise_for :users, :path => "auth", :path_names => { :sign_in => 'login',
:sign_out => 'logout',
:password => 'secret',
:confirmation => 'verification',
:unlock => 'unblock',
:registration => 'register',
:sign_up => 'cmon_let_me_in'
}
devise_scope :user do
root to: "devise/sessions#new"
end
Any idea?
In your route, you're defining your post action but you aren't defining your get action.
You have to define:
get 'features/select_multiple' => 'features#select_multiple'
or else it will assume that you're trying to use the #show action by default.
You can use rake routes in your console to show all your routes and matching actions

Resources