Merge branch 'master' into lm-rails-4-2

This commit is contained in:
Lucas Mazza 2014-09-18 17:46:14 -03:00
commit 8482e485d1
26 changed files with 358 additions and 23 deletions

View File

@ -1,7 +1,16 @@
### Unreleased
* enhancements
* bug fixes
* The Simple Form templates follow the same change from 3.3.0 by using `Log in` and adding
a hint about the minimum password length when `validatable` is enabled (by @aried3r)
* Remove reloading of routes when eager loading is enabled. This change was added during Rails 3 and it doesn't seem to be relevant to currently supported Rails versions (by @fgro)
* Controller generator added as `devise:controllers SCOPE`. You can use the `-c` flag
to pick which controllers (`unlocks`, `confirmations`, etc) you want to generate. (by @Chun-Yang)
* bug fix
* Fixed a regression where the devise generator would fail with a `ConnectionNotEstablished`
exception when executed inside a mountable engine
* Ensure to return symbols in find_scope! fixing a previous regression from 3.3.0 (by @micat)
* Ensure all causes of failed login have the same error message (by @pjungwir)
### 3.3.0

View File

@ -186,7 +186,7 @@ When you customize your own views, you may end up adding new attributes to forms
There are just three actions in Devise that allows any set of parameters to be passed down to the model, therefore requiring sanitization. Their names and the permitted parameters by default are:
* `sign_in` (`Devise::SessionsController#new`) - Permits only the authentication keys (like `email`)
* `sign_in` (`Devise::SessionsController#create`) - Permits only the authentication keys (like `email`)
* `sign_up` (`Devise::RegistrationsController#create`) - Permits authentication keys plus `password` and `password_confirmation`
* `account_update` (`Devise::RegistrationsController#update`) - Permits authentication keys plus `password`, `password_confirmation` and `current_password`
@ -282,15 +282,25 @@ rails generate devise:views -v registrations confirmations
If the customization at the views level is not enough, you can customize each controller by following these steps:
1. Create your custom controller, for example a `Admins::SessionsController`:
1. Create your custom controllers using the generator which requires a scope:
```console
rails generate devise:controllers [scope]
```
If you specify `admins` as the scope, controllers will be created in `app/controllers/admins/`.
And the sessions controller will look like this:
```ruby
class Admins::SessionsController < Devise::SessionsController
# GET /resource/sign_in
# def new
# super
# end
...
end
```
Note that in the above example, the controller needs to be created in the `app/controllers/admins/` directory.
2. Tell the router to use this controller:
```ruby

View File

@ -12,7 +12,7 @@ en:
invalid: "Invalid email or password."
locked: "Your account is locked."
last_attempt: "You have one more attempt before your account is locked."
not_found_in_database: "Invalid email address or password."
not_found_in_database: "Invalid email or password."
timeout: "Your session expired. Please sign in again to continue."
unauthenticated: "You need to sign in or sign up before continuing."
unconfirmed: "You have to confirm your email address before continuing."

View File

@ -33,7 +33,7 @@ module Devise
def self.find_scope!(obj)
case obj
when String, Symbol
return obj
return obj.to_sym
when Class
Devise.mappings.each_value { |m| return m.name if obj <= m.to }
else

View File

@ -10,9 +10,6 @@ module Devise
Devise.warden_config = config
end
# Force routes to be loaded if we are doing any eager load.
config.before_eager_load { |app| app.reload_routes! }
initializer "devise.url_helpers" do
Devise.include_helpers(Devise::Controllers)
end

View File

@ -36,7 +36,6 @@ module Devise
result = resource && resource.valid_for_authentication?(&block)
if result
decorate(resource)
true
else
if resource
@ -47,7 +46,7 @@ module Devise
end
# Get values from params and set in the resource.
def decorate(resource)
def remember_me(resource)
resource.remember_me = remember_me? if resource.respond_to?(:remember_me=)
end

View File

@ -9,6 +9,7 @@ module Devise
encrypted = false
if validate(resource){ encrypted = true; resource.valid_password?(password) }
remember_me(resource)
resource.after_database_authentication
success!(resource)
end

View File

@ -25,15 +25,18 @@ module Devise
end
if validate(resource)
remember_me(resource)
extend_remember_me_period(resource)
success!(resource)
end
end
private
def decorate(resource)
super
resource.extend_remember_period = mapping.to.extend_remember_period if resource.respond_to?(:extend_remember_period=)
def extend_remember_me_period(resource)
if resource.respond_to?(:extend_remember_period=)
resource.extend_remember_period = mapping.to.extend_remember_period
end
end
def remember_me?

View File

@ -83,7 +83,8 @@ RUBY
end
def postgresql?
ActiveRecord::Base.connection.adapter_name.downcase == "postgresql"
config = ActiveRecord::Base.configurations[Rails.env]
config && config['adapter'] == 'postgresql'
end
end
end

View File

@ -0,0 +1,44 @@
require 'rails/generators/base'
module Devise
module Generators
class ControllersGenerator < Rails::Generators::Base
CONTROLLERS = %w(confirmations passwords registrations sessions unlocks omniauth_callbacks).freeze
desc <<-DESC.strip_heredoc
Create inherited Devise controllers in your app/controllers folder.
User -c to specify which controller you want to overwrite.
If you do no specify a controller, all controllers will be created.
For example:
rails generate devise:controllers users -c=sessions
This will create a controller class at app/controllers/users/sessions_controller.rb like this:
class Users::ConfirmationsController < Devise::ConfirmationsController
content...
end
DESC
source_root File.expand_path("../../templates/controllers", __FILE__)
argument :scope, required: true,
desc: "The scope to create controllers in, e.g. users, admins"
class_option :controllers, aliases: "-c", type: :array,
desc: "Select specific controllers to generate (#{CONTROLLERS.join(', ')})"
def create_controllers
@scope_prefix = scope.blank? ? '' : (scope.camelize + '::')
controllers = options[:controllers] || CONTROLLERS
controllers.each do |name|
template "#{name}_controller.rb",
"app/controllers/#{scope}/#{name}_controller.rb"
end
end
def show_readme
readme "README" if behavior == :invoke
end
end
end
end

View File

@ -0,0 +1,14 @@
===============================================================================
Some setup you must do manually if you haven't yet:
Ensure you have overridden routes for generated controllers in your route.rb.
For example:
Rails.application.routes.draw do
devise_for :users, controllers: {
sessions: 'sessions'
}
end
===============================================================================

View File

@ -0,0 +1,28 @@
class <%= @scope_prefix %>ConfirmationsController < Devise::ConfirmationsController
# GET /resource/confirmation/new
# def new
# super
# end
# POST /resource/confirmation
# def create
# super
# end
# GET /resource/confirmation?confirmation_token=abcdef
# def show
# super
# end
# protected
# The path used after resending confirmation instructions.
# def after_resending_confirmation_instructions_path_for(resource_name)
# super(resource_name)
# end
# The path used after confirmation.
# def after_confirmation_path_for(resource_name, resource)
# super(resource_name, resource)
# end
end

View File

@ -0,0 +1,28 @@
class <%= @scope_prefix %>OmniauthCallbacksController < Devise::OmniauthCallbacksController
# You should configure your model like this:
# devise :omniauthable, omniauth_providers: [:twitter]
# You should also create an action method in this controller like this:
# def twitter
# end
# More info at:
# https://github.com/plataformatec/devise#omniauth
# GET|POST /resource/auth/twitter
# def passthru
# super
# end
# GET|POST /users/auth/twitter/callback
# def failure
# super
# end
# protected
# The path used when omniauth fails
# def after_omniauth_failure_path_for(scope)
# super(scope)
# end
end

View File

@ -0,0 +1,32 @@
class <%= @scope_prefix %>PasswordsController < Devise::PasswordsController
# GET /resource/password/new
# def new
# super
# end
# POST /resource/password
# def create
# super
# end
# GET /resource/password/edit?reset_password_token=abcdef
# def edit
# super
# end
# PUT /resource/password
# def update
# super
# end
# protected
# def after_resetting_password_path_for(resource)
# super(resource)
# end
# The path used after sending reset password instructions
# def after_sending_reset_password_instructions_path_for(resource_name)
# super(resource_name)
# end
end

View File

@ -0,0 +1,60 @@
class <%= @scope_prefix %>RegistrationsController < Devise::RegistrationsController
# before_filter :configure_sign_up_params, only: [:create]
# before_filter :configure_account_update_params, only: [:update]
# GET /resource/sign_up
# def new
# super
# end
# POST /resource
# def create
# super
# end
# GET /resource/edit
# def edit
# super
# end
# PUT /resource
# def update
# super
# end
# DELETE /resource
# def destroy
# super
# end
# GET /resource/cancel
# Forces the session data which is usually expired after sign
# in to be expired now. This is useful if the user wants to
# cancel oauth signing in/up in the middle of the process,
# removing all OAuth session data.
# def cancel
# super
# end
# protected
# You can put the params you want to permit in the empty array.
# def configure_sign_up_params
# devise_parameter_sanitizer.for(:sign_up) << :attribute
# end
# You can put the params you want to permit in the empty array.
# def configure_account_update_params
# devise_parameter_sanitizer.for(:account_update) << :attribute
# end
# The path used after sign up.
# def after_sign_up_path_for(resource)
# super(resource)
# end
# The path used after sign up for inactive accounts.
# def after_inactive_sign_up_path_for(resource)
# super(resource)
# end
end

View File

@ -0,0 +1,25 @@
class <%= @scope_prefix %>SessionsController < Devise::SessionsController
# before_filter :configure_sign_in_params, only: [:create]
# GET /resource/sign_in
# def new
# super
# end
# POST /resource/sign_in
# def create
# super
# end
# DELETE /resource/sign_out
# def destroy
# super
# end
# protected
# You can put the params you want to permit in the empty array.
# def configure_sign_in_params
# devise_parameter_sanitizer.for(:sign_in) << :attribute
# end
end

View File

@ -0,0 +1,28 @@
class <%= @scope_prefix %>UnlocksController < Devise::UnlocksController
# GET /resource/unlock/new
# def new
# super
# end
# POST /resource/unlock
# def create
# super
# end
# GET /resource/unlock?unlock_token=abcdef
# def show
# super
# end
# protected
# The path used after sending unlock password instructions
# def after_sending_unlock_instructions_path_for(resource)
# super(resource)
# end
# The path used after unlocking the resource
# def after_unlock_path_for(resource)
# super(resource)
# end
end

View File

@ -65,7 +65,7 @@ Devise.setup do |config|
# :database = Support basic authentication with authentication key + password
# config.http_authenticatable = false
# If http headers should be returned for AJAX requests. True by default.
# If 401 status code should be returned for AJAX requests. True by default.
# config.http_authenticatable_on_xhr = true
# The realm used in Http Basic Authentication. 'Application' by default.

View File

@ -5,7 +5,7 @@
<div class="form-inputs">
<%= f.input :email, required: true, autofocus: true %>
<%= f.input :password, required: true %>
<%= f.input :password, required: true, hint: ("#{@minimum_password_length} characters minimum" if @validatable) %>
<%= f.input :password_confirmation, required: true %>
</div>

View File

@ -1,4 +1,4 @@
<h2>Sign in</h2>
<h2>Log in</h2>
<%= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="form-inputs">
@ -8,7 +8,7 @@
</div>
<div class="form-actions">
<%= f.button :submit, "Sign in" %>
<%= f.button :submit, "Log in" %>
</div>
<% end %>

View File

@ -13,6 +13,12 @@ class RoutesTest < ActionController::TestCase
assert_equal @controller.send(:"#{prepend_path}#{name}_url", :user),
send(:"#{prepend_path}user_#{name}_url")
# With string
assert_equal @controller.send(:"#{prepend_path}#{name}_path", "user"),
send(:"#{prepend_path}user_#{name}_path")
assert_equal @controller.send(:"#{prepend_path}#{name}_url", "user"),
send(:"#{prepend_path}user_#{name}_url")
# Default url params
assert_equal @controller.send(:"#{prepend_path}#{name}_path", :user, param: 123),
send(:"#{prepend_path}user_#{name}_path", param: 123)

View File

@ -0,0 +1,48 @@
require "test_helper"
class ControllersGeneratorTest < Rails::Generators::TestCase
tests Devise::Generators::ControllersGenerator
destination File.expand_path("../../tmp", __FILE__)
setup :prepare_destination
test "Assert no controllers are created with no params" do
run_generator
assert_no_file "app/controllers/sessions_controller.rb"
assert_no_file "app/controllers/registrations_controller.rb"
assert_no_file "app/controllers/confirmations_controller.rb"
assert_no_file "app/controllers/passwords_controller.rb"
assert_no_file "app/controllers/unlocks_controller.rb"
assert_no_file "app/controllers/omniauth_callbacks_controller.rb"
end
test "Assert all controllers are properly created with scope param" do
run_generator %w(users)
assert_class_names 'users'
run_generator %w(admins)
assert_class_names 'admins'
end
test "Assert specified controllers with scope" do
run_generator %w(users -c sessions)
assert_file "app/controllers/users/sessions_controller.rb"
assert_no_file "app/controllers/users/registrations_controller.rb"
assert_no_file "app/controllers/users/confirmations_controller.rb"
assert_no_file "app/controllers/users/passwords_controller.rb"
assert_no_file "app/controllers/users/unlocks_controller.rb"
assert_no_file "app/controllers/users/omniauth_callbacks_controller.rb"
end
private
def assert_class_names(scope, options = {})
base_dir = "app/controllers#{scope.blank? ? '' : ('/' + scope)}"
scope_prefix = scope.blank? ? '' : (scope.camelize + '::')
controllers = options[:controllers] ||
%w(confirmations passwords registrations sessions unlocks omniauth_callbacks)
controllers.each do |c|
assert_file "#{base_dir}/#{c}_controller.rb", /#{scope_prefix + c.camelize}/
end
end
end

View File

@ -42,7 +42,7 @@ class HttpAuthenticationTest < ActionDispatch::IntegrationTest
sign_in_as_new_user_with_http("unknown")
assert_equal 401, status
assert_equal "application/xml; charset=utf-8", headers["Content-Type"]
assert_match "<error>Invalid email address or password.</error>", response.body
assert_match "<error>Invalid email or password.</error>", response.body
end
test 'returns a custom response with www-authenticate and chosen realm' do

View File

@ -62,6 +62,7 @@ class MappingTest < ActiveSupport::TestCase
test 'find scope for a given object' do
assert_equal :user, Devise::Mapping.find_scope!(User)
assert_equal :user, Devise::Mapping.find_scope!(:user)
assert_equal :user, Devise::Mapping.find_scope!("user")
assert_equal :user, Devise::Mapping.find_scope!(User.new)
end

View File

@ -33,7 +33,7 @@ class CreateTables < ActiveRecord::Migration
t.string :unlock_token # Only if unlock strategy is :email or :both
t.datetime :locked_at
t.timestamps
t.timestamps null: false
end
create_table :admins do |t|
@ -60,7 +60,7 @@ class CreateTables < ActiveRecord::Migration
## Attribute for testing route blocks
t.boolean :active, default: false
t.timestamps
t.timestamps null: false
end
end

View File

@ -27,3 +27,4 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
require "rails/generators/test_case"
require "generators/devise/install_generator"
require "generators/devise/views_generator"
require "generators/devise/controllers_generator"