devise-tokens 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/devise_tokens/application_controller.rb +77 -0
  3. data/app/controllers/devise_tokens/concerns/resource_finder.rb +42 -0
  4. data/app/controllers/devise_tokens/concerns/set_user_by_token.rb +160 -0
  5. data/app/controllers/devise_tokens/confirmations_controller.rb +79 -0
  6. data/app/controllers/devise_tokens/omniauth_callbacks_controller.rb +284 -0
  7. data/app/controllers/devise_tokens/passwords_controller.rb +204 -0
  8. data/app/controllers/devise_tokens/registrations_controller.rb +203 -0
  9. data/app/controllers/devise_tokens/sessions_controller.rb +128 -0
  10. data/app/controllers/devise_tokens/token_validations_controller.rb +29 -0
  11. data/app/controllers/devise_tokens/unlocks_controller.rb +87 -0
  12. data/app/models/devise_token_auth/concerns/active_record_support.rb +16 -0
  13. data/app/models/devise_token_auth/concerns/mongoid_support.rb +19 -0
  14. data/app/models/devise_token_auth/concerns/tokens_serialization.rb +19 -0
  15. data/app/models/devise_token_auth/concerns/user.rb +253 -0
  16. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +28 -0
  17. data/app/validators/devise_token_auth_email_validator.rb +23 -0
  18. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  19. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  20. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  21. data/app/views/devise_token_auth/omniauth_external_window.html.erb +38 -0
  22. data/config/locales/da-DK.yml +52 -0
  23. data/config/locales/de.yml +51 -0
  24. data/config/locales/en.yml +57 -0
  25. data/config/locales/es.yml +51 -0
  26. data/config/locales/fr.yml +51 -0
  27. data/config/locales/he.yml +52 -0
  28. data/config/locales/it.yml +48 -0
  29. data/config/locales/ja.yml +48 -0
  30. data/config/locales/nl.yml +32 -0
  31. data/config/locales/pl.yml +50 -0
  32. data/config/locales/pt-BR.yml +48 -0
  33. data/config/locales/pt.yml +50 -0
  34. data/config/locales/ro.yml +48 -0
  35. data/config/locales/ru.yml +52 -0
  36. data/config/locales/sq.yml +48 -0
  37. data/config/locales/sv.yml +52 -0
  38. data/config/locales/uk.yml +61 -0
  39. data/config/locales/vi.yml +52 -0
  40. data/config/locales/zh-CN.yml +48 -0
  41. data/config/locales/zh-HK.yml +50 -0
  42. data/config/locales/zh-TW.yml +50 -0
  43. data/lib/devise_tokens.rb +14 -0
  44. data/lib/devise_tokens/blacklist.rb +2 -0
  45. data/lib/devise_tokens/controllers/helpers.rb +161 -0
  46. data/lib/devise_tokens/controllers/url_helpers.rb +10 -0
  47. data/lib/devise_tokens/engine.rb +92 -0
  48. data/lib/devise_tokens/errors.rb +6 -0
  49. data/lib/devise_tokens/rails/routes.rb +116 -0
  50. data/lib/devise_tokens/token_factory.rb +126 -0
  51. data/lib/devise_tokens/url.rb +39 -0
  52. data/lib/devise_tokens/version.rb +3 -0
  53. data/lib/generators/devise_tokens/USAGE +31 -0
  54. data/lib/generators/devise_tokens/install_generator.rb +91 -0
  55. data/lib/generators/devise_tokens/install_generator_helpers.rb +98 -0
  56. data/lib/generators/devise_tokens/install_mongoid_generator.rb +46 -0
  57. data/lib/generators/devise_tokens/install_views_generator.rb +18 -0
  58. data/lib/generators/devise_tokens/templates/devise_tokens.rb +55 -0
  59. data/lib/generators/devise_tokens/templates/devise_tokens_create_users.rb.erb +49 -0
  60. data/lib/generators/devise_tokens/templates/user.rb.erb +9 -0
  61. data/lib/generators/devise_tokens/templates/user_mongoid.rb.erb +56 -0
  62. data/lib/tasks/devise_tokens_tasks.rake +6 -0
  63. metadata +208 -4
  64. data/lib/devise-tokens.rb +0 -5
@@ -0,0 +1,6 @@
1
+ module DeviseTokens
2
+ module Errors
3
+ class NoResourceDefinedError < StandardError; end
4
+ class InvalidModel < StandardError; end
5
+ end
6
+ end
@@ -0,0 +1,116 @@
1
+
2
+
3
+ module ActionDispatch::Routing
4
+ class Mapper
5
+ def mount_devise_tokens_for(resource, opts)
6
+ # ensure objects exist to simplify attr checks
7
+ opts[:controllers] ||= {}
8
+ opts[:skip] ||= []
9
+
10
+ # check for ctrl overrides, fall back to defaults
11
+ sessions_ctrl = opts[:controllers][:sessions] || 'devise_tokens/sessions'
12
+ registrations_ctrl = opts[:controllers][:registrations] || 'devise_tokens/registrations'
13
+ passwords_ctrl = opts[:controllers][:passwords] || 'devise_tokens/passwords'
14
+ confirmations_ctrl = opts[:controllers][:confirmations] || 'devise_tokens/confirmations'
15
+ token_validations_ctrl = opts[:controllers][:token_validations] || 'devise_tokens/token_validations'
16
+ omniauth_ctrl = opts[:controllers][:omniauth_callbacks] || 'devise_tokens/omniauth_callbacks'
17
+ unlocks_ctrl = opts[:controllers][:unlocks] || 'devise_tokens/unlocks'
18
+
19
+ # define devise controller mappings
20
+ controllers = { sessions: sessions_ctrl,
21
+ registrations: registrations_ctrl,
22
+ passwords: passwords_ctrl,
23
+ confirmations: confirmations_ctrl }
24
+
25
+ controllers[:unlocks] = unlocks_ctrl if unlocks_ctrl
26
+
27
+ # remove any unwanted devise modules
28
+ opts[:skip].each{ |item| controllers.delete(item) }
29
+
30
+ devise_for resource.pluralize.underscore.gsub('/', '_').to_sym,
31
+ class_name: resource,
32
+ module: :devise,
33
+ path: opts[:at].to_s,
34
+ controllers: controllers,
35
+ skip: opts[:skip] + [:omniauth_callbacks]
36
+
37
+ unnest_namespace do
38
+ # get full url path as if it were namespaced
39
+ full_path = "#{@scope[:path]}/#{opts[:at]}"
40
+
41
+ # get namespace name
42
+ namespace_name = @scope[:as]
43
+
44
+ # clear scope so controller routes aren't namespaced
45
+ @scope = ActionDispatch::Routing::Mapper::Scope.new(
46
+ path: '',
47
+ shallow_path: '',
48
+ constraints: {},
49
+ defaults: {},
50
+ options: {},
51
+ parent: nil
52
+ )
53
+
54
+ mapping_name = resource.underscore.gsub('/', '_')
55
+ mapping_name = "#{namespace_name}_#{mapping_name}" if namespace_name
56
+
57
+ devise_scope mapping_name.to_sym do
58
+ # path to verify token validity
59
+ get "#{full_path}/validate_token", controller: token_validations_ctrl.to_s, action: 'validate_token' if !opts[:skip].include?(:token_validations)
60
+
61
+ # omniauth routes. only define if omniauth is installed and not skipped.
62
+ if defined?(::OmniAuth) && !opts[:skip].include?(:omniauth_callbacks)
63
+ match "#{full_path}/failure", controller: omniauth_ctrl, action: 'omniauth_failure', via: [:get]
64
+ match "#{full_path}/:provider/callback", controller: omniauth_ctrl, action: 'omniauth_success', via: [:get]
65
+
66
+ match "#{DeviseTokens.omniauth_prefix}/:provider/callback", controller: omniauth_ctrl, action: 'redirect_callbacks', via: [:get, :post]
67
+ match "#{DeviseTokens.omniauth_prefix}/failure", controller: omniauth_ctrl, action: 'omniauth_failure', via: [:get, :post]
68
+
69
+ # preserve the resource class thru oauth authentication by setting name of
70
+ # resource as "resource_class" param
71
+ match "#{full_path}/:provider", to: redirect{ |params, request|
72
+ # get the current querystring
73
+ qs = CGI::parse(request.env['QUERY_STRING'])
74
+
75
+ # append name of current resource
76
+ qs['resource_class'] = [resource]
77
+ qs['namespace_name'] = [namespace_name] if namespace_name
78
+
79
+ set_omniauth_path_prefix!(DeviseTokens.omniauth_prefix)
80
+
81
+ redirect_params = {}.tap { |hash| qs.each{ |k, v| hash[k] = v.first } }
82
+
83
+ if DeviseTokens.redirect_whitelist
84
+ redirect_url = request.params['auth_origin_url']
85
+ unless DeviseTokens::Url.whitelisted?(redirect_url)
86
+ message = I18n.t(
87
+ 'devise_tokens.registrations.redirect_url_not_allowed',
88
+ redirect_url: redirect_url
89
+ )
90
+ redirect_params['message'] = message
91
+ next "#{::OmniAuth.config.path_prefix}/failure?#{redirect_params.to_param}"
92
+ end
93
+ end
94
+
95
+ # re-construct the path for omniauth
96
+ "#{::OmniAuth.config.path_prefix}/#{params[:provider]}?#{redirect_params.to_param}"
97
+ }, via: [:get]
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ # this allows us to use namespaced paths without namespacing the routes
104
+ def unnest_namespace
105
+ current_scope = @scope.dup
106
+ yield
107
+ ensure
108
+ @scope = current_scope
109
+ end
110
+
111
+ # ignore error about omniauth/multiple model support
112
+ def set_omniauth_path_prefix!(path_prefix)
113
+ ::OmniAuth.config.path_prefix = path_prefix
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,126 @@
1
+ require 'bcrypt'
2
+
3
+ module DeviseTokens
4
+ # A token management factory which allow generate token objects and check them.
5
+ module TokenFactory
6
+ # For BCrypt::Password class see:
7
+ # https://github.com/codahale/bcrypt-ruby/blob/master/lib/bcrypt/password.rb
8
+
9
+ # Creates a token instance. Takes an optional client, lifespan and cost options.
10
+ # Example:
11
+ # DeviseTokens::TokenFactory.create
12
+ # => #<struct DeviseTokens::TokenFactory::Token client="tElcgkdZ7f9XEa0unZhrYQ", token="rAMcWOs0-mGHFMnIgJD2cA", token_hash="$2a$10$wrsdlHVRGlYW11wfImxU..jr0Ux3bHo/qbXcSfgp8zmvVUNHosita", expiry=1518982690>
13
+ #
14
+ # DeviseTokens::TokenFactory.create(lifespan: 10, cost: 4)
15
+ # => #<struct DeviseTokens::TokenFactory::Token client="5qleT7_t9JPVcX9xmxkVYA", token="RBXX43u4xXNSO-fr2N_4pA", token_hash="$2a$04$9gpCaoFbu2dUKxU3qiTgluHX7jj9UzS.jq1QW0EkQmoaxARo1WxTy", expiry=1517773268>
16
+ def self.create(client: nil, lifespan: nil, cost: nil)
17
+ # obj_client = client.nil? ? client() : client
18
+ obj_client = client || client()
19
+ obj_token = token
20
+ obj_token_hash = token_hash(obj_token, cost)
21
+ obj_expiry = expiry(lifespan)
22
+
23
+ Token.new(obj_client, obj_token, obj_token_hash, obj_expiry)
24
+ end
25
+
26
+ # Generates a random URL-safe client.
27
+ # Example:
28
+ # DeviseTokens::TokenFactory.client
29
+ # => "zNf0pNP5iGfuBItZJGCseQ"
30
+ def self.client
31
+ secure_string
32
+ end
33
+
34
+ # Generates a random URL-safe token.
35
+ # Example:
36
+ # DeviseTokens::TokenFactory.token
37
+ # => "6Bqs4K9x8ChLmZogvruF3A"
38
+ def self.token
39
+ secure_string
40
+ end
41
+
42
+ # Returns token hash for a token with given cost. If no cost value is specified,
43
+ # the default value is used. The possible cost value is within range from 4 to 31.
44
+ # It is recommended to not use a value more than 10.
45
+ # Example:
46
+ # DeviseTokens::TokenFactory.token_hash("_qxAxmc-biQLiYRHsmwd5Q")
47
+ # => "$2a$10$6/cTAtQ3CBLfpkeHW7dlt.PD2aVCbFRN5vDDJUUhGsZ6pzYFlh4Me"
48
+ #
49
+ # DeviseTokens::TokenFactory.token_hash("_qxAxmc-biQLiYRHsmwd5Q", 4)
50
+ # => "$2a$04$RkIrosbdRtuet2eUk3si8eS4ufeNpiPc/rSSsfpniRK8ogM5YFOWS"
51
+ def self.token_hash(token, cost = nil)
52
+ cost ||= DeviseTokens.token_cost
53
+ BCrypt::Password.create(token, cost: cost)
54
+ end
55
+
56
+ # Returns the value of time as an integer number of seconds. Takes one argument.
57
+ # Example:
58
+ # DeviseTokens::TokenFactory.expiry
59
+ # => 1518983359
60
+ # DeviseTokens::TokenFactory.expiry(10)
61
+ # => 1517773781
62
+ def self.expiry(lifespan = nil)
63
+ lifespan ||= DeviseTokens.token_lifespan
64
+ (Time.zone.now + lifespan).to_i
65
+ end
66
+
67
+ # Generates a random URL-safe string.
68
+ # Example:
69
+ # DeviseTokens::TokenFactory.secure_string
70
+ # => "ADBoIaqXsEDnxIpOuumrTA"
71
+ def self.secure_string
72
+ # https://ruby-doc.org/stdlib-2.5.0/libdoc/securerandom/rdoc/Random/Formatter.html#method-i-urlsafe_base64
73
+ SecureRandom.urlsafe_base64
74
+ end
75
+
76
+ # Returns true if token hash is a valid token hash.
77
+ # Example:
78
+ # token_hash = "$2a$10$ArjX0tskRIa5Z/Tmapy59OCiAXLStfhrCiaDz.8fCb6hnX1gJ0p/2"
79
+ # DeviseTokens::TokenFactory.valid_token_hash?(token_hash)
80
+ # => true
81
+ def self.valid_token_hash?(token_hash)
82
+ !!BCrypt::Password.valid_hash?(token_hash)
83
+ end
84
+
85
+ # Compares a potential token against the token hash. Returns true if the token is the original token, false otherwise.
86
+ # Example:
87
+ # token = "4wZ9gcc900rMQD1McpcSNA"
88
+ # token_hash = "$2a$10$ArjX0tskRIa5Z/Tmapy59OCiAXLStfhrCiaDz.8fCb6hnX1gJ0p/2"
89
+ # DeviseTokens::TokenFactory.token_hash_is_token?(token_hash, token)
90
+ # => true
91
+ def self.token_hash_is_token?(token_hash, token)
92
+ BCrypt::Password.new(token_hash).is_password?(token)
93
+ rescue StandardError
94
+ false
95
+ end
96
+
97
+ # Creates a token instance with instance variables equal nil.
98
+ # Example:
99
+ # DeviseTokens::TokenFactory.new
100
+ # => #<struct DeviseTokens::TokenFactory::Token client=nil, token=nil, token_hash=nil, expiry=nil>
101
+ def self.new
102
+ Token.new
103
+ end
104
+
105
+ Token = Struct.new(:client, :token, :token_hash, :expiry) do
106
+ # Sets all instance variables of the token to nil. It is faster than creating new empty token.
107
+ # Example:
108
+ # token.clear!
109
+ # => true
110
+ # token
111
+ # => #<struct DeviseTokens::TokenFactory::Token client=nil, token=nil, token_hash=nil, expiry=nil>
112
+ def clear!
113
+ size.times { |i| self[i] = nil }
114
+ true
115
+ end
116
+
117
+ # Checks token attribute presence
118
+ # Example:
119
+ # token.present?
120
+ # => true
121
+ def present?
122
+ token.present?
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,39 @@
1
+ module DeviseTokens::Url
2
+
3
+ def self.generate(url, params = {})
4
+ uri = URI(url)
5
+
6
+ res = "#{uri.scheme}://#{uri.host}"
7
+ res += ":#{uri.port}" if (uri.port && uri.port != 80 && uri.port != 443)
8
+ res += uri.path.to_s if uri.path
9
+ query = [uri.query, params.to_query].reject(&:blank?).join('&')
10
+ res += "?#{query}"
11
+ res += "##{uri.fragment}" if uri.fragment
12
+
13
+ res
14
+ end
15
+
16
+ def self.whitelisted?(url)
17
+ url.nil? || \
18
+ !!DeviseTokens.redirect_whitelist.find do |pattern|
19
+ !!Wildcat.new(pattern).match(url)
20
+ end
21
+ end
22
+
23
+ # wildcard convenience class
24
+ class Wildcat
25
+ def self.parse_to_regex(str)
26
+ escaped = Regexp.escape(str).gsub('\*','.*?')
27
+ Regexp.new("^#{escaped}$", Regexp::IGNORECASE)
28
+ end
29
+
30
+ def initialize(str)
31
+ @regex = self.class.parse_to_regex(str)
32
+ end
33
+
34
+ def match(str)
35
+ !!@regex.match(str)
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,3 @@
1
+ module DeviseTokens
2
+ VERSION = '1.1.3'.freeze
3
+ end
@@ -0,0 +1,31 @@
1
+ Description:
2
+ This generator will install all the necessary configuration and migration
3
+ files for the devise_tokens gem. See
4
+ https://github.com/lynndylanhurley/devise_tokens for more information.
5
+
6
+ Arguments:
7
+ USER_CLASS # The name of the class to use for user authentication. Default is
8
+ # 'User'
9
+ MOUNT_PATH # The path at which to mount the authentication routes. Default is
10
+ # 'auth'. More detail documentation is here:
11
+ # https://github.com/lynndylanhurley/devise_tokens#usage-tldr
12
+
13
+ Example:
14
+ rails generate devise_tokens:install User auth
15
+
16
+ This will create:
17
+ config/initializers/devise_tokens.rb
18
+ db/migrate/<%= Time.zone.now.utc.strftime("%Y%m%d%H%M%S") %>_create_devise_tokens_create_users.rb
19
+ app/models/user.rb
20
+
21
+ If 'app/models/user.rb' already exists, the following line will be inserted
22
+ after the class definition:
23
+ include DeviseTokens::Concerns::User
24
+
25
+ The following line will be inserted into your application controller at
26
+ app/controllers/application_controller.rb:
27
+ include DeviseTokens::Concerns::SetUserByToken
28
+
29
+ The following line will be inserted at the top of 'config/routes.rb' if it
30
+ does not already exist:
31
+ mount_devise_tokens_for "User", at: 'auth'
@@ -0,0 +1,91 @@
1
+
2
+
3
+ require_relative 'install_generator_helpers'
4
+
5
+ module DeviseTokens
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+ include DeviseTokens::InstallGeneratorHelpers
9
+
10
+ class_option :primary_key_type, type: :string, desc: 'The type for primary key'
11
+
12
+ def copy_migrations
13
+ if self.class.migration_exists?('db/migrate', "devise_tokens_create_#{user_class.pluralize.gsub('::','').underscore}")
14
+ say_status('skipped', "Migration 'devise_tokens_create_#{user_class.pluralize.gsub('::','').underscore}' already exists")
15
+ else
16
+ migration_template(
17
+ 'devise_tokens_create_users.rb.erb',
18
+ "db/migrate/devise_tokens_create_#{user_class.pluralize.gsub('::','').underscore}.rb"
19
+ )
20
+ end
21
+ end
22
+
23
+ def create_user_model
24
+ fname = "app/models/#{user_class.underscore}.rb"
25
+ if File.exist?(File.join(destination_root, fname))
26
+ inclusion = 'include DeviseTokens::Concerns::User'
27
+ unless parse_file_for_line(fname, inclusion)
28
+
29
+ active_record_needle = (Rails::VERSION::MAJOR == 5) ? 'ApplicationRecord' : 'ActiveRecord::Base'
30
+ inject_into_file fname, after: "class #{user_class} < #{active_record_needle}\n" do <<-'RUBY'
31
+ # Include default devise modules.
32
+ devise :database_authenticatable, :registerable,
33
+ :recoverable, :rememberable, :trackable, :validatable,
34
+ :confirmable, :omniauthable
35
+ include DeviseTokens::Concerns::User
36
+ RUBY
37
+ end
38
+ end
39
+ else
40
+ template('user.rb.erb', fname)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def self.next_migration_number(path)
47
+ Time.zone.now.utc.strftime('%Y%m%d%H%M%S')
48
+ end
49
+
50
+ def json_supported_database?
51
+ (postgres? && postgres_correct_version?) || (mysql? && mysql_correct_version?)
52
+ end
53
+
54
+ def postgres?
55
+ database_name == 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter'
56
+ end
57
+
58
+ def postgres_correct_version?
59
+ database_version > '9.3'
60
+ end
61
+
62
+ def mysql?
63
+ database_name == 'ActiveRecord::ConnectionAdapters::MysqlAdapter'
64
+ end
65
+
66
+ def mysql_correct_version?
67
+ database_version > '5.7.7'
68
+ end
69
+
70
+ def database_name
71
+ ActiveRecord::Base.connection.class.name
72
+ end
73
+
74
+ def database_version
75
+ ActiveRecord::Base.connection.select_value('SELECT VERSION()')
76
+ end
77
+
78
+ def rails5?
79
+ Rails.version.start_with? '5'
80
+ end
81
+
82
+ def primary_key_type
83
+ primary_key_string if rails5?
84
+ end
85
+
86
+ def primary_key_string
87
+ key_string = options[:primary_key_type]
88
+ ", id: :#{key_string}" if key_string
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,98 @@
1
+ module DeviseTokens
2
+ module InstallGeneratorHelpers
3
+ class << self
4
+ def included(mod)
5
+ mod.class_eval do
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ argument :user_class, type: :string, default: 'User'
9
+ argument :mount_path, type: :string, default: 'auth'
10
+
11
+ def create_initializer_file
12
+ copy_file('devise_tokens.rb', 'config/initializers/devise_tokens.rb')
13
+ end
14
+
15
+ def include_controller_concerns
16
+ fname = 'app/controllers/application_controller.rb'
17
+ line = 'include DeviseTokens::Concerns::SetUserByToken'
18
+
19
+ if File.exist?(File.join(destination_root, fname))
20
+ if parse_file_for_line(fname, line)
21
+ say_status('skipped', 'Concern is already included in the application controller.')
22
+ elsif is_rails_api?
23
+ inject_into_file fname, after: "class ApplicationController < ActionController::API\n" do <<-'RUBY'
24
+ include DeviseTokens::Concerns::SetUserByToken
25
+ RUBY
26
+ end
27
+ else
28
+ inject_into_file fname, after: "class ApplicationController < ActionController::Base\n" do <<-'RUBY'
29
+ include DeviseTokens::Concerns::SetUserByToken
30
+ RUBY
31
+ end
32
+ end
33
+ else
34
+ say_status('skipped', "app/controllers/application_controller.rb not found. Add 'include DeviseTokens::Concerns::SetUserByToken' to any controllers that require authentication.")
35
+ end
36
+ end
37
+
38
+ def add_route_mount
39
+ f = 'config/routes.rb'
40
+ str = "mount_devise_tokens_for '#{user_class}', at: '#{mount_path}'"
41
+
42
+ if File.exist?(File.join(destination_root, f))
43
+ line = parse_file_for_line(f, 'mount_devise_tokens_for')
44
+
45
+ if line
46
+ existing_user_class = true
47
+ else
48
+ line = 'Rails.application.routes.draw do'
49
+ existing_user_class = false
50
+ end
51
+
52
+ if parse_file_for_line(f, str)
53
+ say_status('skipped', "Routes already exist for #{user_class} at #{mount_path}")
54
+ else
55
+ insert_after_line(f, line, str)
56
+
57
+ if existing_user_class
58
+ scoped_routes = ''\
59
+ "as :#{user_class.underscore} do\n"\
60
+ " # Define routes for #{user_class} within this block.\n"\
61
+ " end\n"
62
+ insert_after_line(f, str, scoped_routes)
63
+ end
64
+ end
65
+ else
66
+ say_status('skipped', "config/routes.rb not found. Add \"mount_devise_tokens_for '#{user_class}', at: '#{mount_path}'\" to your routes file.")
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def insert_after_line(filename, line, str)
73
+ gsub_file filename, /(#{Regexp.escape(line)})/mi do |match|
74
+ "#{match}\n #{str}"
75
+ end
76
+ end
77
+
78
+ def parse_file_for_line(filename, str)
79
+ match = false
80
+
81
+ File.open(File.join(destination_root, filename)) do |f|
82
+ f.each_line do |line|
83
+ match = line if line =~ /(#{Regexp.escape(str)})/mi
84
+ end
85
+ end
86
+ match
87
+ end
88
+
89
+ def is_rails_api?
90
+ fname = 'app/controllers/application_controller.rb'
91
+ line = 'class ApplicationController < ActionController::API'
92
+ parse_file_for_line(fname, line)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end