Updating a database field when Creating a User using Devise gem - ruby-on-rails

Explanation
I am wanting to set a user's security level when they register for an account. I don't want this to be a hidden field in the signup form as a person can then send in their own value to try and gain administrative privileges. So, it needs to be in the RegistrationController.
Versions
Ruby: 2.2.1p85 (2015-02-26 revision 49769) [x86_64-darwin14]
Rails: 4.2.0
Files
Here is what I have so far.
Added the following line to the app/config/routes.rb file:
devise_for :users, :skip => [:sessions, :registrations],
:controllers => { registrations: 'users/registrations' }
devise_scope :user do
get "login", to: "devise/sessions#new", as: :new_user_session
post "login", to: "devise/sessions#create", as: :user_session
delete "logout", to: "devise/sessions#destroy", as: :destroy_user_session
get "register", to: "devise/registrations#new", as: :new_user_registration
post "register", to: "devise/registrations#create", as: :user_registration
get "account/delete", to: "devise/registrations#cancel", as: :cancel_user_registration
get "user/profile/edit", to: "devise/registrations#edit", as: :edit_user_registration
patch "user", to: "devise/registrations#update"
put "user", to: "devise/registrations#update"
put "register", to: "devise/registrations#update"
delete "user/delete", to: "devise/registrations#destrony"
get "user/profile", to: 'devise/registrations#edit', as: :user_root
end
Here is my RegistrationController in the app/controllers/users/registration_controller.rb file:
class Devise::RegistrationsController < DeviseController
before_filter :configure_permitted_parameters, :only => [:create]
def create
super
resource.update_attribute(:security_level, '1')
end
end
My database does have a field called 'security_level' for all users.
Thank you in advance for any help that you can provide.

If you want to set security_level = 1, by default i would suggest you write it to the rails migration.
Code for migration
change_column_default :users, :security_level, '1'
If you want to do more by overriding the default devise crete function use the bellow code.
class RegistrationsController < Devise::RegistrationsController
def create
super do
resource.security_level = '1'
resource.save
end
end
end

You can face logic duplication problem in future (e.g. creating user from console will not force Rails to fill security level attribute, so you will need to control this in every place). Better to use callbacks in User model:
class User < ActiveRecord::Base
before_save :set_security_level
def set_security_level
self.security_level = 1
end
end
So, everytime User is going to be saved, :set_security_level will be executed.
Callbacks manual http://guides.rubyonrails.org/active_record_callbacks.html

Related

Custom Routing in Rails 5

I'm having some issues with custom routing. What I'm looking to do is remove the model from the route and dynamically use the record name.
so instead of:
site.com/events/my-event
I would like it to be:
site.com/my-event
I hacked this to work with the below code, only issue is I can't access my admin namespace as it's being treated as an event record (and any other route):
get('/:id', to: redirect do |params, request|
id = request.path.gsub("/", "")
"/events/#{id}"
end)
I know this redirect is not right, I'm just not well versed in routing options. How should this be done properly?
routes.rb
Rails.application.routes.draw do
resources :events, param: :id, path: "" do
get 'login', to: 'sessions#new', as: :login
get 'logout', to: 'sessions#destroy', as: :logout
post 'sessions', to: 'sessions#create', as: :session_create
end
namespace 'admin' do
root "events#index"
resources :sessions, only: [:create]
get 'login', to: 'sessions#new', as: :login
get 'logout', to: 'sessions#destroy', as: :logout
resources :events
end
end
Rails lets you specify what a routing URL should look like:
https://guides.rubyonrails.org/routing.html#translated-paths
You don't need redirect.
You need to match all requests to one controller/action.
get '/(*all)', to: 'home#app'
Inside this action check what is coming in params and render appropriate view.
PS: it will capture all requests, even for not found images, js, etc.
(1) To fix /admin to go to the right path, move the namespace line above the events resources so that it is matched first:
Rails routes are matched in the order they are specified 2.2 CRUD, Verbs, and Actions
# routes.rb
namespace :admin do
root "events#index"
#...
resources :events
end
resources :events, param: :name, path: ""
Use :param and :path to match for site.com/myevent instead of site.com/events/:id
Your events controller:
# events_controller.rb
class EventsController < ApplicationController
# ...
private
def set_event
#event = Event.find_by(name: params[:name])
end
end
Your admin events controller:
# admin/events_controller.rb
class Admin::EventsController < ApplicationController
# ...
private
def set_event
#event = Event.find params[:id]
end
end
TIP: To get a complete list of the available routes use rails routes in your terminal 5.1 Listing Existing Routes

Cannot rake migration for my ruby project

When I try to rake a migration for my ruby project, it gives me an error "'API::sessions' is not a supported controller name". Does anyone know how to solve this problem? The following code is from my routes.rb file and sessions_controller.rb file.
Rails.application.routes.draw do
get 'projects/:id', to: 'projects#show'
get 'projects', to: 'projects#index'
get 'welcome/index'
root 'welcome#index'
match '/login', to: 'sessions#new', via: :get
match '/login_create', to: 'sessions#create', via: :post
resources :users
scope :format => true, :constraints => { :format => 'json' } do
post "/api/login" => "API::sessions#create"
delete "/api/logout" => "API::sessions#destroy"
end
end
class API::SessionsController < API::ApiController
skip_before_action :require_login, only: [:create], raise: false
def create
if user = User.valid_login?(params[:email], params[:password])
allow_token_to_be_used_only_once_for(user)
send_auth_token_for_valid_login_of(user)
else
render_unauthorized("Error with your login or password")
end
end
def destroy
logout
head :ok
end
private
def send_auth_token_for_valid_login_of(user)
render json: { token: user.token }
end
def allow_token_to_be_used_only_once_for(user)
user.regenerate_token
end
def logout
current_user.invalidate_token
end
end
The error appears when you define the post and delete routes within the format scope, to tell Rails which controller and action/method use for the URI being defined.
To refer then the controller name and action use the lowercase name followed by a slash, instead the uppercase controller name and colons ::, like:
post '/api/login' => 'api/sessions#create'
delete '/api/logout' => 'api/sessions#destroy'

Rails Devise Routes: If user signed in, point to index. Else, point to new user session

Pretty simple question, but I can't seem to find the answer with a good ole fashioned Google.
The error:
undefined method `user_signed_in?' for #<ActionDispatch::Routing::Mapper:0x007fc6369320e8> (NoMethodError)
Server won't even start.
My code:
Rails.application.routes.draw do
devise_for :users, :path_prefix => 'u'
resources :users
devise_scope :user do
get "login", to: "devise/sessions#new", as: :login
get 'logout', to: 'devise/sessions#destroy', as: :logout
get 'user/edit', to: 'devise/registrations#edit', as: :change_password
end
resources :apps do
resources :elements, controller: 'apps/elements'
end
resources :elements do
resources :features, except: [:index], controller: 'apps/elements/features'
end
if user_signed_in?
root to: 'apps#index'
else
devise_scope :user do
root to: 'devise/sessions#new'
end
end
end
What's the best way to go about making this work? I'm hesitant to try to work around it and make the site vulnerable, being a new RoR user. Thanks in advance.
In Rails access control is done on the controller layer - not on the routing layer.
Routes just define matchers for different sets of params and request urls. They are processed before rails even starts processing the request, they don't know anything about the session. Rails could have even used YML to define routes, except that Ruby is better at DSLs.
If you want to require that the user is signed in you would use:
class SomeController < ApplicationController
before_action :authenticate_user! # a Devise helper method
end

Ruby on Rails Devise after_sign_in_path_for called on unrelated controller

struggling for a few hours on this one now. I have integrated the Devise gem into my Rails project after originally making my own auth system but I am facing an issue I can't understand.
When the user signs in the method:
def after_sign_in_path_for(resource_or_scope)
user = resource_or_scope
user_path(user.username)
end
Is triggered to redirect the user to their profile.
I have an edit user route which takes the user to a page in which they can edit their details and add a 'wanted item'. Two separate forms with two separate controllers and actions.
The 'add wanted item' method posts to a different controller that rendered the view called WantsController and adds a wanted item for the user through an association.
For some reason the after_sign_in_path_for method is called when submitting this form? It has nothing to do with signing in...
Here are my routes:
#users/auth
devise_for :users, :skip => [:sessions, :registrations]
devise_scope :user do
# registration
get "/signup", to: "users#new", as: :sign_up
post "/signup", to: "users#create", as: :sign_up_create
# account
get "/:username/account", to: "users#edit", as: :user_account
put "/users/:id", to: "users#update", as: :user_update
# shows
get "/:username", to: "users#show", as: :user
get "/:username/interests", to: "users#interests", as: :user_interests
get "/:username/offers", to: "users#offers", as: :user_offers
get "/:username/trades", to: "users#trades", as: :user_trades
# auth
post "/signin" => 'devise/sessions#create', as: :sign_in
delete "/signout", to: "devise/sessions#destroy", as: :sign_out
#wants
resources :wants, only: [:create, :destroy]
end
If I place the wants resource outside of the devise scope (which is where I expect it should go) I receive the following:
Could not find devise mapping for path "/wants"
What's happening here? Stumped!
Thanks.
Argh, silly mistake. Why is it after hours of struggling that when you post a question on Stack Overflow you figure it out after like 5 minutes?!
I had copied and pasted my RegistrationsController into the WantsController file to save typing the controller code but forgot to make it inherit from ApplicationController rather than Devise::RegistrationsController.
Lesson: Don't copy and paste!

Devise routes /:param not working

Using devise 2.1.0
I am trying to send the new registration page a PricingPlan model.
So in my routes I have:
devise_scope :user do
delete "/logout" => "devise/sessions#destroy"
get "/login" => "devise/sessions#new"
get "/signup/:plan" => "devise/registrations#new"
end
And I override the devise registration controller. With this in my routes.rb to make it work:
devise_for :users, :controllers => {:registrations => "registrations"}
In my actual Registration controller which overrides Devise's controller I have:
class RegistrationsController < Devise::RegistrationsController
view_paths = "app/views/devise"
def new
super
#plan = PricingPlan.find_by_name(params[:plan])
end
So that the default views still go to devise....
In my new view for the registration controller I call this:
<h3>You've chosen the <%= #plan.name %> plan.</h3>
And I get this error:
undefined method `name' for nil:NilClass
Also... in my PricingPlan model:
class PricingPlan < ActiveRecord::Base
has_many :users
And in my User model:
class User < ActiveRecord::Base
belongs_to :pricing_plan
I'm rather new at rails.
For some reason your #plan is empty...
try changing the line below just to make sure that you are finding a plan...
#plan = PricingPlan.find_by_id(1)
if so, you are probabl trying to find by the wrong argument... do you have a column "plan" in your database?
#plan = PricingPlan.find_by_name(params[:______])
When I did a raise in my registration controller, I realize that when I was hitting /signup/:plan it wasn't hitting the registrations controller that I had overode from devises registration controller.
I figure out the reason:
Because I had made my own controller, the scope is no longer devises scope any more... So this was WRONG:
get "/signup/:plan" => "devise/registrations#new"
This however is CORRECT:
get "/signup/:plan" => "registrations#new"
So that part of my routes looks like this:
devise_scope :user do
delete "/logout" => "devise/sessions#destroy"
get "/login" => "devise/sessions#new"
get "/signup/:plan" => "registrations#new"
end
Everything else in the code stayed the same.
Thanks for #gabrielhilal for making me trace out the controller.

Resources