pollett 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +81 -0
  4. data/Rakefile +25 -0
  5. data/app/controllers/concerns/pollett/controller.rb +37 -0
  6. data/app/controllers/pollett/application_controller.rb +4 -0
  7. data/app/controllers/pollett/keys_controller.rb +5 -0
  8. data/app/controllers/pollett/sessions_controller.rb +5 -0
  9. data/app/controllers/pollett/users_controller.rb +5 -0
  10. data/app/mailers/pollett/mailer.rb +5 -0
  11. data/app/models/concerns/pollett/user.rb +40 -0
  12. data/app/models/pollett/context.rb +5 -0
  13. data/app/models/pollett/key.rb +5 -0
  14. data/app/models/pollett/session.rb +5 -0
  15. data/app/serializers/pollett/key_serializer.rb +5 -0
  16. data/app/serializers/pollett/session_serializer.rb +5 -0
  17. data/app/serializers/user_serializer.rb +3 -0
  18. data/app/services/pollett/authenticate_user.rb +5 -0
  19. data/app/services/pollett/change_password.rb +5 -0
  20. data/app/services/pollett/create_session.rb +5 -0
  21. data/app/services/pollett/register_user.rb +5 -0
  22. data/app/services/pollett/reset_password.rb +5 -0
  23. data/app/views/pollett/mailer/reset.text.erb +5 -0
  24. data/app/views/pollett/mailer/welcome.text.erb +1 -0
  25. data/config/locales/en.yml +14 -0
  26. data/config/routes.rb +11 -0
  27. data/db/migrate/20150226024506_create_pollett_contexts.rb +21 -0
  28. data/lib/generators/pollett/install/install_generator.rb +123 -0
  29. data/lib/generators/pollett/install/templates/db/migrate/add_pollett_to_users.rb +21 -0
  30. data/lib/generators/pollett/install/templates/db/migrate/create_users.rb +15 -0
  31. data/lib/generators/pollett/install/templates/initializer.rb +3 -0
  32. data/lib/generators/pollett/install/templates/user.rb +3 -0
  33. data/lib/pollett.rb +20 -0
  34. data/lib/pollett/concerns.rb +5 -0
  35. data/lib/pollett/concerns/controllers.rb +3 -0
  36. data/lib/pollett/concerns/controllers/keys_controller.rb +37 -0
  37. data/lib/pollett/concerns/controllers/sessions_controller.rb +43 -0
  38. data/lib/pollett/concerns/controllers/users_controller.rb +28 -0
  39. data/lib/pollett/concerns/mailers.rb +1 -0
  40. data/lib/pollett/concerns/mailers/mailer.rb +24 -0
  41. data/lib/pollett/concerns/models.rb +3 -0
  42. data/lib/pollett/concerns/models/context.rb +42 -0
  43. data/lib/pollett/concerns/models/key.rb +13 -0
  44. data/lib/pollett/concerns/models/session.rb +20 -0
  45. data/lib/pollett/concerns/serializers.rb +2 -0
  46. data/lib/pollett/concerns/serializers/context_serializer.rb +21 -0
  47. data/lib/pollett/concerns/serializers/user_serializer.rb +16 -0
  48. data/lib/pollett/concerns/services.rb +5 -0
  49. data/lib/pollett/concerns/services/authenticate_user.rb +21 -0
  50. data/lib/pollett/concerns/services/change_password.rb +21 -0
  51. data/lib/pollett/concerns/services/create_session.rb +27 -0
  52. data/lib/pollett/concerns/services/register_user.rb +29 -0
  53. data/lib/pollett/concerns/services/reset_password.rb +27 -0
  54. data/lib/pollett/configuration.rb +34 -0
  55. data/lib/pollett/engine.rb +12 -0
  56. data/lib/pollett/rspec.rb +6 -0
  57. data/lib/pollett/testing/request_helper.rb +106 -0
  58. data/lib/pollett/version.rb +3 -0
  59. data/spec/dummy/README.rdoc +28 -0
  60. data/spec/dummy/Rakefile +6 -0
  61. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  62. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  63. data/spec/dummy/app/controllers/application_controller.rb +12 -0
  64. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  65. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  66. data/spec/dummy/app/models/user.rb +3 -0
  67. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  68. data/spec/dummy/app/views/layouts/mailer.text.erb +6 -0
  69. data/spec/dummy/bin/bundle +3 -0
  70. data/spec/dummy/bin/rails +4 -0
  71. data/spec/dummy/bin/rake +4 -0
  72. data/spec/dummy/bin/setup +29 -0
  73. data/spec/dummy/config.ru +4 -0
  74. data/spec/dummy/config/application.rb +32 -0
  75. data/spec/dummy/config/boot.rb +5 -0
  76. data/spec/dummy/config/database.yml +85 -0
  77. data/spec/dummy/config/environment.rb +5 -0
  78. data/spec/dummy/config/environments/development.rb +41 -0
  79. data/spec/dummy/config/environments/production.rb +79 -0
  80. data/spec/dummy/config/environments/test.rb +42 -0
  81. data/spec/dummy/config/initializers/active_model_serializers.rb +1 -0
  82. data/spec/dummy/config/initializers/assets.rb +11 -0
  83. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  84. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  85. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  86. data/spec/dummy/config/initializers/inflections.rb +16 -0
  87. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  88. data/spec/dummy/config/initializers/session_store.rb +3 -0
  89. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  90. data/spec/dummy/config/locales/en.yml +23 -0
  91. data/spec/dummy/config/routes.rb +3 -0
  92. data/spec/dummy/config/secrets.yml +22 -0
  93. data/spec/dummy/db/migrate/20150226030314_enable_uuid_extension.rb +5 -0
  94. data/spec/dummy/db/migrate/20150226030315_create_users.rb +15 -0
  95. data/spec/dummy/db/migrate/20150226030316_create_pollett_contexts.pollett.rb +22 -0
  96. data/spec/dummy/db/schema.rb +49 -0
  97. data/spec/dummy/log/development.log +1315 -0
  98. data/spec/dummy/log/test.log +181283 -0
  99. data/spec/dummy/public/404.html +67 -0
  100. data/spec/dummy/public/422.html +67 -0
  101. data/spec/dummy/public/500.html +66 -0
  102. data/spec/dummy/public/favicon.ico +0 -0
  103. data/spec/factories/pollett_context.rb +5 -0
  104. data/spec/factories/pollett_key.rb +5 -0
  105. data/spec/factories/pollett_session.rb +5 -0
  106. data/spec/factories/user.rb +7 -0
  107. data/spec/mailers/pollett/mailer_spec.rb +73 -0
  108. data/spec/rails_helper.rb +19 -0
  109. data/spec/requests/keys_spec.rb +67 -0
  110. data/spec/requests/sessions_spec.rb +176 -0
  111. data/spec/requests/user_spec.rb +41 -0
  112. data/spec/spec_helper.rb +9 -0
  113. data/spec/support/email_helper.rb +9 -0
  114. metadata +323 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0b840bb4badc9859eb7fda1c68585d2deb6b3de4
4
+ data.tar.gz: ca1179661479d54174900d8af6181f78d3a31b2a
5
+ SHA512:
6
+ metadata.gz: 0680f8f973d73b0b09ab6e3d463a2e3b5f975dd44f69a23eaf114ad1d65c30f3d818d1da46d17040d34d631fcc9eb3b303d63a40d598efb92385937db2299a3d
7
+ data.tar.gz: 4e7e6e4c1e72cdb5e0d9c903554831a1e52b7a05a31cecfa9cd2b33e27c2b521fc9f412be3bc59794e8f290ac4790e938c488d49302b08eba218ed9fa3cb5c7f
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2015 Jason Kriss
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # Pollett
2
+
3
+ Token-based authentication for your Rails API.
4
+
5
+ Pollett is a simple authentication library for your API-only Rails app. It treats sessions as a [first class domain concern]
6
+ (https://github.com/blog/1661-modeling-your-app-s-user-session) and takes its inspiration from [Clearance](https://github.com/thoughtbot/clearance).
7
+
8
+ Pollett currently requires Postgres and the use of UUID primary keys. This means you will need to have the `uuid-ossp` extension enabled before using Pollett.
9
+
10
+ ## Install
11
+
12
+ To get started, add Pollett to your `Gemfile`, `bundle install`, and run the
13
+ `install generator`:
14
+
15
+ ```sh
16
+ $ rails g pollett:install
17
+ ```
18
+
19
+ The generator:
20
+
21
+ * Inserts `Pollett::User` into your `User` model
22
+ * Inserts `Pollett::Controller` into your `ApplicationController`
23
+ * Creates an initializer to allow further configuration
24
+ * Creates a migration that either creates a users table or adds the necessary columns to the existing table
25
+ * Copies over the Session model migration
26
+ * Mounts the engine
27
+
28
+ Then, just migrate the database:
29
+
30
+ ```sh
31
+ $ rake db:migrate
32
+ ```
33
+
34
+ Finally, you must implement the `render_list` method in your `ApplicationController`. Use this method to paginate a list of records any way you want.
35
+
36
+ ## Configure
37
+
38
+ Override any of these defaults in `config/initializers/pollett.rb`:
39
+
40
+ ```ruby
41
+ Pollett.configure do |config|
42
+ config.user_model = ::User
43
+ config.minimum_password_length = 8
44
+ config.send_welcome_email = true
45
+ config.parent_mailer = ::ApplicationMailer
46
+ config.from_email = "noreply@example.com"
47
+ config.reset_url = ->(token) { "https://example.com/#{token}/reset" }
48
+ config.whitelist = []
49
+ end
50
+ ```
51
+
52
+ At minimum, you will need to configure `reset_url` so that the link will be correct in password reset emails. Also, if a default "from" email is not set in your `parent_mailer`, you will need to configure `from_email` as well.
53
+
54
+ ## Use
55
+
56
+ ### Access Control
57
+
58
+ Pollett authentication is opt-out rather than opt-in. This means that if there is an action that does not require authentication, you will need to use `skip_authentication`:
59
+
60
+ ```ruby
61
+ class ArticlesController < ApplicationController
62
+ skip_authentication only: :safe_action
63
+
64
+ def safe_action
65
+ # something that does not require authentication
66
+ end
67
+ end
68
+ ```
69
+
70
+ As you'd expect, `current_user` can be used from within controllers to access the authenticated user.
71
+
72
+ When authentication fails, Pollett raises a `Pollett::Unauthorized` error. You should [rescue_from](http://guides.rubyonrails.org/action_controller_overview.html#rescue-from) this to customize what is rendered.
73
+
74
+ ### Email
75
+ Pollett is capable of sending two types of emails. In addition to the standard password reset email, it will send a welcome email upon registration unless `config.send_welcome_email` is set to `false`.
76
+
77
+ These emails will make use of whatever `layout` you have specified in your `ApplicationMailer`. If you need to customize the subject of these emails or make minor tweaks to the messages, you can simply override them via [i18n translations]
78
+ (http://guides.rubyonrails.org/i18n.html). See [config/locales/en.yml](/config/locales/en.yml) for the
79
+ default behavior.
80
+
81
+ If you need to make more elaborate changes, you'll want to override the actual views. See [app/views/pollett/mailer](/app/views/pollett/mailer) for the default behavior.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Pollett'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('lib/**/*.rb')
14
+ end
15
+
16
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
17
+ load 'rails/tasks/engine.rake'
18
+
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+
23
+
24
+ Bundler::GemHelper.install_tasks
25
+
@@ -0,0 +1,37 @@
1
+ module Pollett
2
+ module Controller
3
+ extend ActiveSupport::Concern
4
+
5
+ include ActionController::HttpAuthentication::Token::ControllerMethods
6
+
7
+ included do
8
+ attr_accessor :current_context
9
+
10
+ before_action :authenticate!
11
+ end
12
+
13
+ module ClassMethods
14
+ def skip_authentication(options = {})
15
+ skip_before_action(:authenticate!, options)
16
+ end
17
+ end
18
+
19
+ private
20
+ def activate_context(context)
21
+ context.access(request)
22
+ self.current_context = context
23
+ end
24
+
25
+ def current_user
26
+ current_context.try(:user)
27
+ end
28
+
29
+ def authenticate!
30
+ if context = authenticate_with_http_token { |id, _| Context.authenticate(id) }
31
+ activate_context(context)
32
+ else
33
+ raise Unauthorized
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,4 @@
1
+ module Pollett
2
+ class ApplicationController < ::ApplicationController
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class KeysController < ApplicationController
3
+ include Concerns::Controllers::KeysController
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class SessionsController < ApplicationController
3
+ include Concerns::Controllers::SessionsController
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class UsersController < ApplicationController
3
+ include Concerns::Controllers::UsersController
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class Mailer < Pollett.config.parent_mailer
3
+ include Concerns::Mailers::Mailer
4
+ end
5
+ end
@@ -0,0 +1,40 @@
1
+ require "email_validator"
2
+
3
+ module Pollett
4
+ module User
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ has_secure_password
9
+
10
+ has_many :contexts, class_name: "Pollett::Context"
11
+ has_many :sessions, class_name: "Pollett::Session"
12
+ has_many :keys, class_name: "Pollett::Key"
13
+
14
+ before_validation :normalize_email
15
+
16
+ validates :name, presence: true
17
+
18
+ validates :email, presence: true,
19
+ uniqueness: true,
20
+ email: { strict_mode: true }
21
+
22
+ validates :password, length: { minimum: Pollett.config.minimum_password_length }, if: :password
23
+ end
24
+
25
+ module ClassMethods
26
+ def find_by_normalized_email(email)
27
+ find_by(email: normalize_email(email))
28
+ end
29
+
30
+ def normalize_email(email)
31
+ email.to_s.downcase.gsub(/\s+/, "")
32
+ end
33
+ end
34
+
35
+ private
36
+ def normalize_email
37
+ self.email = self.class.normalize_email(email)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class Context < ActiveRecord::Base
3
+ include Concerns::Models::Context
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class Key < Context
3
+ include Concerns::Models::Key
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class Session < Context
3
+ include Concerns::Models::Session
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class KeySerializer < ActiveModel::Serializer
3
+ include Concerns::Serializers::ContextSerializer
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class SessionSerializer < ActiveModel::Serializer
3
+ include Concerns::Serializers::ContextSerializer
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ class UserSerializer < ActiveModel::Serializer
2
+ include Pollett::Concerns::Serializers::UserSerializer
3
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class AuthenticateUser
3
+ include Concerns::Services::AuthenticateUser
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class ChangePassword
3
+ include Concerns::Services::ChangePassword
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class CreateSession
3
+ include Concerns::Services::CreateSession
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class RegisterUser
3
+ include Concerns::Services::RegisterUser
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Pollett
2
+ class ResetPassword
3
+ include Concerns::Services::ResetPassword
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ <%= t(".opening") %>
2
+
3
+ <%= @url %>
4
+
5
+ <%= t(".closing") %>
@@ -0,0 +1 @@
1
+ <%= t(".body") %>
@@ -0,0 +1,14 @@
1
+ en:
2
+ pollett:
3
+ mailer:
4
+ welcome:
5
+ subject: "Welcome!"
6
+ body: "Welcome aboard!"
7
+ reset:
8
+ subject: "Password Reset"
9
+ opening: "Someone recently requested a password change for your account. To reset your password, just follow this link:"
10
+ closing: "If you did not make this request, just ignore this email. Your password will remain unchanged."
11
+ activerecord:
12
+ attributes:
13
+ user:
14
+ password_digest: "Password"
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ Pollett::Engine.routes.draw do
2
+ scope shallow: true, format: false do
3
+ resource :user, only: [:show, :update, :destroy]
4
+
5
+ resources :sessions, except: [:new, :edit, :update] do
6
+ post :forgot, on: :collection
7
+ end
8
+
9
+ resources :keys, except: [:new, :edit, :update]
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ class CreatePollettContexts < ActiveRecord::Migration
2
+ def change
3
+ create_table :pollett_contexts, id: :uuid do |t|
4
+ t.string :type, null: false
5
+ t.uuid :user_id, null: false
6
+ t.string :client, null: false
7
+ t.datetime :revoked_at
8
+ t.datetime :accessed_at
9
+ t.string :ip
10
+ t.string :user_agent
11
+
12
+ t.timestamps null: false
13
+ end
14
+
15
+ add_foreign_key :pollett_contexts, :users, on_delete: :cascade
16
+
17
+ add_index :pollett_contexts, :user_id
18
+ add_index :pollett_contexts, :accessed_at
19
+ add_index :pollett_contexts, :revoked_at
20
+ end
21
+ end
@@ -0,0 +1,123 @@
1
+ require "rails/generators/base"
2
+ require "rails/generators/active_record"
3
+
4
+ module Pollett
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ source_root File.expand_path("../templates", __FILE__)
10
+
11
+ def mount
12
+ inject_into_file "config/routes.rb", after: "Rails.application.routes.draw do\n" do
13
+ " mount Pollett::Engine => \"/\"\n\n"
14
+ end
15
+ end
16
+
17
+ def create_initializer
18
+ copy_file "initializer.rb", "config/initializers/pollett.rb"
19
+ end
20
+
21
+ def inject_into_application_controller
22
+ inject_into_class "app/controllers/application_controller.rb", ApplicationController do
23
+ " include Pollett::Controller\n"
24
+ end
25
+ end
26
+
27
+ def create_or_inject_into_user_model
28
+ if File.exist? "app/models/user.rb"
29
+ inject_into_file "app/models/user.rb", after: "class User < ActiveRecord::Base\n" do
30
+ " include Pollett::User\n\n"
31
+ end
32
+ else
33
+ copy_file "user.rb", "app/models/user.rb"
34
+ end
35
+ end
36
+
37
+ def create_users_migration
38
+ if users_table_exists?
39
+ create_add_columns_migration
40
+ else
41
+ copy_migration "create_users.rb"
42
+ end
43
+ end
44
+
45
+ def install_migrations
46
+ Dir.chdir(Rails.root) { `rake pollett:install:migrations` }
47
+ end
48
+
49
+ private
50
+ def users_table_exists?
51
+ ActiveRecord::Base.connection.table_exists?(:users)
52
+ end
53
+
54
+ def create_add_columns_migration
55
+ if migration_needed?
56
+ config = {
57
+ new_columns: new_columns,
58
+ new_indexes: new_indexes
59
+ }
60
+
61
+ copy_migration("add_pollett_to_users.rb", config)
62
+ end
63
+ end
64
+
65
+ def migration_needed?
66
+ new_columns.any? || new_indexes.any?
67
+ end
68
+
69
+ def new_columns
70
+ @new_columns ||= {
71
+ email: "t.string :name, null: false",
72
+ email: "t.string :email, null: false",
73
+ password_digest: "t.string :password_digest, null: false",
74
+ reset_token: "t.string :reset_token"
75
+ }.reject { |column| existing_users_columns.include?(column.to_s) }
76
+ end
77
+
78
+ def new_indexes
79
+ @new_indexes ||= {
80
+ index_users_on_email: "add_index :users, :email, unique: true",
81
+ index_users_on_reset_token: "add_index :users, :reset_token, unique: true"
82
+ }.reject { |index| existing_users_indexes.include?(index.to_s) }
83
+ end
84
+
85
+ def copy_migration(migration_name, config = {})
86
+ unless migration_exists?(migration_name)
87
+ migration_template(
88
+ "db/migrate/#{migration_name}",
89
+ "db/migrate/#{migration_name}",
90
+ config
91
+ )
92
+ end
93
+ end
94
+
95
+ def migration_exists?(name)
96
+ existing_migrations.include?(name)
97
+ end
98
+
99
+ def existing_migrations
100
+ @existing_migrations ||= Dir.glob("db/migrate/*.rb").map do |file|
101
+ migration_name_without_timestamp(file)
102
+ end
103
+ end
104
+
105
+ def migration_name_without_timestamp(name)
106
+ name.sub(%r{^.*(db/migrate/)(?:\d+_)?}, "")
107
+ end
108
+
109
+ def existing_users_columns
110
+ ActiveRecord::Base.connection.columns(:users).map(&:name)
111
+ end
112
+
113
+ def existing_users_indexes
114
+ ActiveRecord::Base.connection.indexes(:users).map(&:name)
115
+ end
116
+
117
+ # for generating a timestamp when using `create_migration`
118
+ def self.next_migration_number(dir)
119
+ ActiveRecord::Generators::Base.next_migration_number(dir)
120
+ end
121
+ end
122
+ end
123
+ end