Trying to override controller in Thoughtbot's Clearance gem for Rails - ruby-on-rails

I am using Clearance 1.1.0 gem with Ruby on Rails 4.0.1. I am trying to override the sessions controller to provide my own custom method. I have not been able to successfully get rails to use my controller.
/app/controllers/sessions_controller.rb
class SessionsController < Clearance::SessionsController
private
def flash_failure_after_create
flash.now[:notice] = translate(:bad_email_or_password,
:scope => [:clearance, :controllers, :sessions],
:default => t('flashes.failure_after_create', :new_password_path => new_password_path).html_safe)
end
end
I have tried a few different things inside my routes.rb file, and have been unsuccessful. I want to change the route sign_in.
get '/sign_in' => 'sessions#new', :as => 'sign_in'
Yields the following error.
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.
Any ideas? Thank you!
Edit: I made a mistake. I actually need sessions#create to use my controller. I'm trying to pass a different variable to the yaml file for the flash when the session login fails.
Edit 2: I the appropriate session#create line to to my routes. In my session controller, I copied and edited for testing the flash_failure_after_create method. It is not being called. So I then copy the create method over. Now, my create method is being called, but not my flash_failure_after_create method. To get it to be called, I had to have the create method copied from gem, and changed status.failure_message to directly call the flash_failure_after_create method. Is this some sort of bug with clearance?
routes.rb
post 'session' => 'sessions#create', :as => nil
sessions_controller.rb
class SessionsController < Clearance::SessionsController
def create
#user = authenticate(params)
sign_in(#user) do |status|
if status.success?
redirect_back_or url_after_create
else
#flash.now.notice = status.failure_message
flash.now.notice = flash_failure_after_create
render :template => 'sessions/new', :status => :unauthorized
end
end
end
private
def flash_failure_after_create
# Changed flash for easy testing
flash.now[:notice] = 'Ballz.'
#flash.now[:notice] = translate(:bad_email_or_password,
# :scope => [:clearance, :controllers, :sessions],
# :default => t('flashes.failure_after_create', :sign_up_path => sign_up_path).html_safe)
end
end

I believe this will work:
get '/sign_in' => 'sessions#new', :as => nil
Rails 4 no longer supports overriding route names, so don't name your override. The mapping is still the same so sign_in_path should still work.

Related

What is the best way to allow several methods on the controller without adding on routes?

Hi everybody I'm from the old school using rails 2.
Actually I'm using rails 4 and I'm trying to find a way to create methods on the controller without writting
On RAILS 2 used: (only needed to write the name on the controller)
#controller
def report_a
end
def report_b
end
def report_c
end
...and whatever def
#ROUTES
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
On RAILS 4
#controller
def report_a
end
def report_b
end
def report_c
end
#ROUTES
match ':controller(/:action(/:id(.:format)))', :via => [:get, :post]
The problem is when I create a view report like this: (views/reports/report_a.html.erb)
<%= form_tag :action=>"report_a" do %>
<% end %>
I get this message:
No route matches [GET] "/reports/report_a"
To resolve this issue and doing Rails instruccions works like this:
#controller
def report_a
#users= User.search(params[:name])
end
def result_report_a
#users= User.search(params[:name])
end
#view/reports/report_a.html.erb
<%= form_tag :action=>"result_report_a" do %>
<% end %>
#routes.rb
get "reports#report_a"
post "reports#result_report_a"
get "reports#report_b"
post "reports#result_report_b"
get "reports#report_c"
post "reports#result_report_c"
Also I found this better way:
#controller reports.rb
def search_report_a
report_a
render :report_a
end
def report_a
#users = User.where(:name=>params[:name])
end
def search_report_b
report_b
render :report_b
end
def report_b
#users = User.where(:address=>params[:address])
end
...
#Routes.rb
resources :users do
match 'search_report_a', :via => [:post,:get], :on => :collection
match 'search_report_b', :via => [:post,:get], :on => :collection
...
end
Is there any other way to create methods without adding all inside ROUTES.RB ?
Any suggestions or the only way is adding get and post?
Imagine a case where you have several methods.
Best approach in Rails is to use REST architecture. Your controller should be able to view, create, update and destroy some resource (of course all actions are not mandatory).
For example:
def ReportsController
def index
# Actions to show links to all possible reports
end
def show
# Show report based on params
end
end
Your #show method may show any of report (report_a, report_b, etc) just by checking param from GET request.
And you don't need to make all logics inside #show method. It would be better to place report-related logic in, maybe, some service objects.

Contact Form (SimpleForm) - Route not found/unable to autoload?

I'm trying to install the contact page on my Ruby on Rails app. It seems straight forward enough, but after installing the mailer gems, and creating my controller with:
$ rails generate controller contact_form new create
I navigate to my contact URL (/contact_form/new), and it says
"Unable to autoload constant ContactFormController, expected
/home/ubuntu/workspace/app/controllers/contact_form_controller.rb to
define it"
Routes and controller are as follows:
routes.rb
get 'contact_form/new'
get 'contact_form/create'
resources :contact_forms
contact_form_controller.rb
class ContactFormsController < ApplicationController
def new
#contact_form = ContactForm.new
end
def create
begin
#contact_form = ContactForm.new(params[:contact_form])
#contact_form.request = request
if #contact_form.deliver
flash.now[:notice] = 'Thank you for your message!'
else
render :new
end
rescue ScriptError
flash[:error] = 'Sorry, this message appears to be spam and was not delivered.'
end
end
end
contact_form.rb
class ContactForm < MailForm::Base
attribute :name, :validate => true
attribute :email, :validate => /\A([\w\.%\+\-]+)#([\w\-]+\.)+([\w]{2,})\z/i
attribute :message
attribute :nickname, :captcha => true
# Declare the e-mail headers. It accepts anything the mail method
# in ActionMailer accepts.
def headers
{
:subject => "My Contact Form",
:to => "your_email#example.org",
:from => %("#{name}" <#{email}>)
}
end
end
Note that your class is named ContactFormsController and Rails is looking for ContactFormController. You need to pay careful attention to the pluralization in Rails.
Controllers are plural.
Models are singular.
Routes are plural in most cases.
The pluralization of classes must always match the file name.
So why is Rails looking for ContactFormController? Because your routes are not defined properly:
get 'contact_form/new'
get 'contact_form/create'
get 'contact_forms/new' is the proper route for a form to create a new resource. You don't create resources with GET so get rid of get 'contact_form/create'.
resources :contact_forms
Is actually all that you need.
So to fix this error you should:
rename contact_form_controller.rb -> contact_forms_controller.rb.
change your route definition.
request /contact_forms/new instead.

Allowing admins to add users with Devise

I'm trying to make it so only admins can add uses with devise. I've gotten it mostly working however now when I'm logged in as an admin and submit the sign up form it kicks me back with the error: You are already signed in.
I've tried to follow the instructions here: http://wiki.summercode.com/rails_authentication_with_devise_and_cancan but it doesn't seem to mention this situation.
Do I need to do further overriding in the editors_controller to allow this?
Here are my routes ("editors" is the name of my user model):
devise_for :admins, :skip => [:registrations]
as :admin do
get 'admin/editors' => 'editors#index', as: :admin_editors
get 'admin/editors/new' => 'editors#new', as: :new_editor
delete 'admin/editors/:id' => 'editors#destroy', as: :destroy_editor
end
devise_for :editors, :skip => [:registrations], :controllers => { :registrations => "editors" }
and my editors_controller in "app/controllers/"
class EditorsController < Devise::RegistrationsController
before_filter :check_permissions, :only => [:new, :create, :cancel]
skip_before_filter :require_no_authentication
def dashboard
render "editors/dashboard.html.haml"
end
def index
#editors = Editor.all
respond_to do |format|
format.html
end
end
private
def check_permissions
authorize! :create, resource
end
end
EDIT
I noticed this Processing by Devise::RegistrationsController#create as HTML in the logs when I submit the form. I had suspected that perhaps the skip_before_filter :require_no_authentication wasn't being called, but assumed that because the EditorsController was inheriting from RegistrationController that before filter would work properly. Is that not the case?
You'll want to implement your own create method on EditorsController instead of inheriting that action from Devise::RegistrationsController. As you're seeing, the method in Devise::RegistrationsController will first check to see if you're already logged in and kick you back if you are. If you're not logged in it will create a User account and then log you in as that user.
You're trying to get around that problem with skip_before_filter :require_no_authentication, but it's likely that your form is POSTing to /editors instead of /admin/editors. So, you'll need to add a route that allows you to get to create on the EditorsController :
as :admin do
post 'admin/editors' => 'editors#create'
# your other :admin routes here
end
Then you'd want to implement a scaled down version of create. You probably want something kind of like this :
class EditorsController < Devise::RegistrationsController
def create
build_resource(sign_up_params)
if resource.save
redirect_to admin_editors_path
else
clean_up_passwords resource
respond_with resource
end
end
# your other methods here
end
You'll also want to make sure that the admin/editors/new template is pointing the form to the correct route ('admin/editors').
None of the googleable solutions worked when I tried them. This works
What I did was create a new action in the controller and a new route for it, and connect the links on my views that normally connect to create to now call my route and action.
But that wasn't enough. Because Devise is listening and will grab any add you try to do and validate it through it's own code. So instead I just add the new user record with a sql insert.
Add this route
post 'savenew', to: 'users#savenew'
Add this action to the user controller:
def savenew
rawsql = "insert into users (email, created_at,updated_at) values ('#{user_params[:email]}',now(), now())"
sql = rawsql
ActiveRecord::Base.connection.execute(sql)
redirect_to action: 'index''
end
View: new.html.erb
change the form_for so that submit will go to the new route and action, not the default Rails one.
<%= form_for User, :url => {:action => "savenew"} do |f| %>
Using Rails 4.2.6 here (my model is User instead of Editor). The following solution bypasses (I think) any devise actions that may interfere with new User creation by the admin:
Add this action to the Users controller:
def savenew
User.create_new_user(user_params)
redirect_to action: 'index'
end
Add this private method to the Users controller if it does not exist:
private
def user_params
params.require(:user).permit(:email, :password,
:password_confirmation)
end
Add this to config/routes.rb:
match '/savenew', to: 'users#savenew', via: :post
Add this class method to the User model:
def self.create_new_user(params)
#user = User.create!(params)
end
I don't have a separate Admin class in my application. Instead, I defined an admin attribute for Users and check for it with a :validate_admin before_action filter in the UsersController.
I wanted to be able to create a new user from the :index view, so I added a button:
<%= button_to 'New User', '/new_user', class: 'btn btn-primary',
method: :get %>
You might have to tweak the above solution if you have any after_create actions in the User model (e.g. sending a welcome email).

How can I customize Devise's "resend confirmation email"

I have a custom mailer (UserMailer.rb) and a few methods to override the default Devise methods for the welcome email and forgot password emails. The mailer uses a custom template to style the emails--and it works great.
In config/initializers, I have a file with
module Devise::Models::Confirmable
# Override Devise's own method. This one is called only on user creation, not on subsequent address modifications.
def send_on_create_confirmation_instructions
UserMailer.welcome_email(self).deliver
end
...
end
(Again, UserMailer is setup and works great for the welcome email and reset password email.)
But what's not working is the option to "Resend confirmation instructions." It sends with the default Devise styling and I want it to use the styling of my mailer layout. I know I can manually add the layout to the default Devise layout, but I'd like to keep DRY in effect and not have to do that.
I've tried overriding the send_confirmation_instructions method found here, but I'm getting a wrong number of arguments (1 for 0) error in create(gem) devise-2.2.3/app/controllers/devise/confirmations_controller.rb at
7 # POST /resource/confirmation
8 def create
9 self.resource = resource_class.send_confirmation_instructions(resource_params)
In my initializer file, I'm able to get to this error by adding a new override for Devise, but I'm probably not doing this correctly:
module Devise::Models::Confirmable::ClassMethods
def send_confirmation_instructions
UserMailer.send_confirmation_instructions(self).deliver
end
end
Any ideas?
You don't have to go through that initializer to do that. I've done this by overriding the confirmations controller. My routes for devise look like:
devise_for :user, :path => '', :path_names => { :sign_in => 'login', :sign_out => 'logout', :sign_up => 'signup'},
:controllers => {
:sessions => "sessions",
:registrations => "registrations",
:confirmations => "confirmations"
}
Then, create the confirmations_controller and extend the Devise::ConfirmationsController to override:
class ConfirmationsController < Devise::ConfirmationsController
In that controller, I have a create method to override the default:
def create
#user = User.where(:email => params[:user][:email]).first
if #user && #user.confirmed_at.nil?
UserMailer.confirmation_instructions(#user).deliver
flash[:notice] = "Set a notice if you want"
redirect_to root_url
else
# ... error messaging or actions here
end
end
Obviously, in UserMailer you can specify the html/text templates that will be used to display the confirmation message. confirmation_token should be a part of the #user model, you can use that to create the URL with the correct token:
<%= link_to 'Confirm your account', confirmation_url(#user, :confirmation_token => #user.confirmation_token) %>

URLs in mailer view files using namespaces and custom member actions

I am running Ruby on Rails 3 and I would like to use URLs in mailer view files with namespaces and custom member actions. I read this but I have still problems on implementing that.
In routes.rb I have:
namespace "users" do
resources :accounts do
member do
get 'my_action_name'
post 'my_action_name'
end
end
end
In users_mailer.rb I have:
class Users::Accounts < ActionMailer::Base
default_url_options[:host] = "test_name_site.com"
def account_delete(account)
#account = account
#action_url = users_account_url(:controller => "users/accounts", :action => "my_action_name")
...
end
end
When in the controller I try to do this:
#account = Users::Account.find(params[:id])
Users::Accounts.account_delete(Users::Account.find_by_id(#account.id)).deliver
I get this error:
ActionController::RoutingError (No route matches {:action=>"my_action_name", :controller=>"users/accounts"})
How I can solve that?
In users_mailer.rb I tryed also
# the difference is: from 'users_account_url' to 'users_accounts_url'
#action_url = users_accounts_url(:controller => "users/account_authentications", :action => "confirm_account_authentication_delete")
# the difference is: from 'users_account_url' to 'url_for'
#action_url = url_for(:controller => "users/account_authentications", :action => "confirm_account_authentication_delete")
but it doesn't work.
You are not specify an ID of a record in users_account_url
try this
#action_url = my_action_name_users_account_url(account)
http://0.0.0.0:3000/users/accounts/1/my_action_name
Routing gude

Resources