diff --git a/README.md b/README.md index 04c4f9a3..6bd8750d 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,31 @@ for `:secure_validatable` you need to add gem 'rails_email_validator' ``` + +### Configuring views +The view generator in Devise Security extensions works the same way as the view generator in [Devise](https://github.com/plataformatec/devise) does. + +If you would like to change the default views, you just need to invoke the following generator, and it will copy all views to your application: + +```console +$ rails g devise_security_extension:views +``` + +If you have more than one Devise model in your application (such as `User` and `Admin`), you will notice that Devise uses the same views for all models. Fortunately, Devise offers an easy way to customize views. All you need to do is set `config.scoped_views = true` inside the `config/initializers/devise.rb` file. + +After doing so, you will be able to have views based on the role like `users/sessions/new` and `admins/sessions/new`. If no view is found within the scope, Devise will use the default view at `devise/sessions/new`. You can also use the generator to generate scoped views: + +```console +$ rails generate devise_security_extension:views users +``` + +If you would like to generate only a few sets of views, like the ones for the `registerable` and `confirmable` module, +you can pass a list of modules to the generator with the `-v` flag. + +```console +$ rails generate devise_security_extension:views -v paranoid_verification_code password_expired +``` + ## Configuration ```ruby diff --git a/devise_security_extension.gemspec b/devise_security_extension.gemspec index 7e5c3ada..fd181b18 100644 --- a/devise_security_extension.gemspec +++ b/devise_security_extension.gemspec @@ -20,7 +20,7 @@ Gem::Specification.new do |s| s.require_paths = ['lib'] s.required_ruby_version = '>= 2.1.0' - s.add_runtime_dependency 'railties', '>= 3.2.6', '< 5.0' + s.add_runtime_dependency 'railties', '>= 3.2.6', '< 5.1' s.add_runtime_dependency 'devise', '>= 3.0.0', '< 5.0' s.add_development_dependency 'bundler', '>= 1.3.0', '< 2.0' s.add_development_dependency 'sqlite3', '~> 1.3.10' diff --git a/lib/generators/devise_security_extension/views_generator.rb b/lib/generators/devise_security_extension/views_generator.rb new file mode 100644 index 00000000..6bf9e67b --- /dev/null +++ b/lib/generators/devise_security_extension/views_generator.rb @@ -0,0 +1,71 @@ +require 'rails/generators/base' + +module DeviseSecurityExtension + module Generators + # Include this module in your generator to generate Devise views. + # `copy_views` is the main method and by default copies all views + # with forms. + module ViewPathTemplates #:nodoc: + extend ActiveSupport::Concern + + included do + argument :scope, required: false, default: nil, + desc: "The scope to copy views to" + + class_option :views, aliases: "-v", type: :array, desc: "Generate views" + + public_task :copy_views + end + + # TODO: Add this to Rails itself + module ClassMethods + def hide! + Rails::Generators.hide_namespace self.namespace + end + end + + def copy_views + if options[:views] + options[:views].each do |directory| + view_directory directory.to_sym + end + else + view_directory :paranoid_verification_code + view_directory :password_expired + end + end + + protected + + def view_directory(name, _target_path = nil) + directory name.to_s, _target_path || "#{target_path}/#{name}" do |content| + content + end + end + + def target_path + @target_path ||= "app/views/#{plural_scope || :devise}" + end + + def plural_scope + @plural_scope ||= scope.presence && scope.underscore.pluralize + end + end + + class SharedViewsGenerator < Rails::Generators::Base #:nodoc: + include ViewPathTemplates + source_root File.expand_path("../../../../app/views/devise", __FILE__) + desc "Copies shared Devise views to your application." + hide! + end + + class ViewsGenerator < Rails::Generators::Base + desc "Copies Devise views to your application." + + argument :scope, required: false, default: nil, + desc: "The scope to copy views to" + + invoke SharedViewsGenerator + end + end +end \ No newline at end of file diff --git a/test/generators/views_generator_test.rb b/test/generators/views_generator_test.rb new file mode 100644 index 00000000..975324c6 --- /dev/null +++ b/test/generators/views_generator_test.rb @@ -0,0 +1,39 @@ +require "test_helper" + +class ViewsGeneratorTest < Rails::Generators::TestCase + tests Devise::Generators::ViewsGenerator + destination File.expand_path("../../tmp", __FILE__) + setup :prepare_destination + + test "Assert all views are properly created with no params" do + run_generator + assert_files + end + + test "Assert all views are properly created with scope param" do + run_generator %w(users) + assert_files "users" + + run_generator %w(admins) + assert_files "admins" + end + + test "Assert only views within specified directories" do + run_generator %w(-v paranoid_verification_code password_expired) + assert_file "app/views/devise/paranoid_verification_code/show.html.erb" + assert_file "app/views/devise/password_expired/show.html.erb" + end + + test "Assert specified directories with scope" do + run_generator %w(users -v password_expired) + assert_file "app/views/users/password_expired/show.html.erb" + assert_no_file "app/views/users/paranoid_verification_code/show.html.erb" + end + + def assert_files(scope = nil) + scope = "devise" if scope.nil? + + assert_file "app/views/#{scope}/paranoid_verification_code/show.html.erb" + assert_file "app/views/#{scope}/password_expired/show.html.erb" + end +end \ No newline at end of file