good_migrations 0.0.2 → 0.2.1

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 (48) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +32 -0
  3. data/.gitignore +0 -1
  4. data/CHANGELOG.md +19 -0
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +113 -0
  7. data/LICENSE.txt +1 -1
  8. data/README.md +70 -10
  9. data/Rakefile +3 -2
  10. data/example/Gemfile +7 -5
  11. data/example/Rakefile +2 -2
  12. data/example/app/assets/config/manifest.js +0 -0
  13. data/example/app/models/pant.rb +0 -1
  14. data/example/app/models/shirt.rb +2 -0
  15. data/example/bin/rails +4 -0
  16. data/example/bin/rake +4 -0
  17. data/example/config/application.rb +7 -2
  18. data/example/config/boot.rb +14 -11
  19. data/example/config/environment.rb +1 -1
  20. data/example/config/environments/development.rb +3 -3
  21. data/example/config/environments/production.rb +1 -1
  22. data/example/config/environments/test.rb +2 -2
  23. data/example/config/initializers/good_migrations.rb +7 -0
  24. data/example/config/initializers/secret_token.rb +1 -1
  25. data/example/config/initializers/session_store.rb +1 -1
  26. data/example/config.ru +1 -1
  27. data/example/db/migrate/20160202162849_create_pants.rb +1 -1
  28. data/example/db/migrate/20160202163803_change_pants.rb +2 -1
  29. data/example/db/migrate/20160202182520_change_pants_dangerously.rb +1 -1
  30. data/example/db/migrate/20170101150000_create_shirts.rb +8 -0
  31. data/example/db/migrate/20170102150000_change_shirts_dangerously.rb +10 -0
  32. data/example/lib/tasks/load_pants.rake +5 -0
  33. data/example/script/rails +3 -3
  34. data/example/test/performance/browsing_test.rb +3 -3
  35. data/example/test/test_helper.rb +2 -2
  36. data/good_migrations.gemspec +17 -15
  37. data/lib/good_migrations/configuration.rb +38 -0
  38. data/lib/good_migrations/load_error.rb +1 -1
  39. data/lib/good_migrations/migration_details.rb +30 -0
  40. data/lib/good_migrations/patches_autoloader.rb +61 -0
  41. data/lib/good_migrations/permits_autoload.rb +19 -0
  42. data/lib/good_migrations/railtie.rb +1 -1
  43. data/lib/good_migrations/raises_load_error.rb +44 -0
  44. data/lib/good_migrations/version.rb +1 -1
  45. data/lib/good_migrations.rb +10 -3
  46. data/tasks/good_migrations.rake +9 -54
  47. metadata +47 -18
  48. data/.travis.yml +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 05eb20b7481ddae6720347b0c84275997ff53e72
4
- data.tar.gz: b7c85f764f8f2c22a3988517f2c272cdb59d0016
2
+ SHA256:
3
+ metadata.gz: e08c6b787542e1a8881109c1f100e3e98c30a0da7ff9e05a53a1676f4526dd55
4
+ data.tar.gz: 07deb33dcb07c43abe3083081abaec2c3ffd7790b06032f07834a548e00eb0fc
5
5
  SHA512:
6
- metadata.gz: b79f08edebb75e97c2358064c31d8c5d2de7e4cb483b5e4f7e61a4fe04b6550156c271995249af7327a66b1f5767269830ec0fe01b7144a4fcaa6a257d244689
7
- data.tar.gz: 285bfb5be43c16860c0e6bff42c5e024d24199d4a9a84e9de1f946d04a979bbac87156c0603124c87aac80aecef81f0cd47690daf699bf27c73f2770ad0465fc
6
+ metadata.gz: 78ee5b98395010837864b20c06f111be13fd276620baf03283eb619028d4e8b585cf90d6e70804c0e32a189465910861aacba2572324181f6a789e126e3fc8f9
7
+ data.tar.gz: 3afeb8e60d5217df9a4b83069875c5ac52c106229909e1bbf4dccd84339f4911ee37d5c1900226323a2c28067c6a8f4a06e0695e2c5a70a250a3cd6c726eefcc
@@ -0,0 +1,32 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ branches: [ main ]
13
+ pull_request:
14
+ branches: [ main ]
15
+
16
+ jobs:
17
+ test:
18
+
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ matrix:
22
+ ruby-version: ['2.5', '2.7', '3.0']
23
+
24
+ steps:
25
+ - uses: actions/checkout@v2
26
+ - name: Set up Ruby
27
+ uses: ruby/setup-ruby@v1
28
+ with:
29
+ ruby-version: ${{ matrix.ruby-version }}
30
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
31
+ - name: Run tests
32
+ run: bundle exec rake
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
4
3
  /_yardoc/
5
4
  /coverage/
6
5
  /doc/
data/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # CHANGELOG
2
+
3
+ ## 0.2.1
4
+
5
+ * Fix Windows paths [#31](https://github.com/testdouble/good-migrations/pull/31)
6
+ by [@MaxLap](https://github.com/MaxLap)
7
+
8
+ ## 0.2.0
9
+
10
+ * Add `permit_autoloading_before` configuration
11
+
12
+ ## 0.1.0
13
+
14
+ * Add support for zeitwerk@2.5 & higher
15
+ * Disable the autoloader patch after migrations finish
16
+
17
+ ## 0.0.2
18
+
19
+ * Support classic autoloader
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in good_migrations.gemspec
4
4
  gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,113 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ good_migrations (0.2.1)
5
+ activerecord (>= 3.1)
6
+ railties (>= 3.1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actionpack (7.0.2.3)
12
+ actionview (= 7.0.2.3)
13
+ activesupport (= 7.0.2.3)
14
+ rack (~> 2.0, >= 2.2.0)
15
+ rack-test (>= 0.6.3)
16
+ rails-dom-testing (~> 2.0)
17
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
18
+ actionview (7.0.2.3)
19
+ activesupport (= 7.0.2.3)
20
+ builder (~> 3.1)
21
+ erubi (~> 1.4)
22
+ rails-dom-testing (~> 2.0)
23
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
24
+ activemodel (7.0.2.3)
25
+ activesupport (= 7.0.2.3)
26
+ activerecord (7.0.2.3)
27
+ activemodel (= 7.0.2.3)
28
+ activesupport (= 7.0.2.3)
29
+ activesupport (7.0.2.3)
30
+ concurrent-ruby (~> 1.0, >= 1.0.2)
31
+ i18n (>= 1.6, < 2)
32
+ minitest (>= 5.1)
33
+ tzinfo (~> 2.0)
34
+ ast (2.4.2)
35
+ builder (3.2.4)
36
+ coderay (1.1.3)
37
+ concurrent-ruby (1.1.10)
38
+ crass (1.0.6)
39
+ erubi (1.10.0)
40
+ i18n (1.10.0)
41
+ concurrent-ruby (~> 1.0)
42
+ loofah (2.16.0)
43
+ crass (~> 1.0.2)
44
+ nokogiri (>= 1.5.9)
45
+ method_source (1.0.0)
46
+ mini_portile2 (2.8.0)
47
+ minitest (5.15.0)
48
+ nokogiri (1.13.4)
49
+ mini_portile2 (~> 2.8.0)
50
+ racc (~> 1.4)
51
+ parallel (1.22.1)
52
+ parser (3.1.2.0)
53
+ ast (~> 2.4.1)
54
+ pry (0.14.1)
55
+ coderay (~> 1.1)
56
+ method_source (~> 1.0)
57
+ racc (1.6.0)
58
+ rack (2.2.3)
59
+ rack-test (1.1.0)
60
+ rack (>= 1.0, < 3)
61
+ rails-dom-testing (2.0.3)
62
+ activesupport (>= 4.2.0)
63
+ nokogiri (>= 1.6)
64
+ rails-html-sanitizer (1.4.2)
65
+ loofah (~> 2.3)
66
+ railties (7.0.2.3)
67
+ actionpack (= 7.0.2.3)
68
+ activesupport (= 7.0.2.3)
69
+ method_source
70
+ rake (>= 12.2)
71
+ thor (~> 1.0)
72
+ zeitwerk (~> 2.5)
73
+ rainbow (3.1.1)
74
+ rake (13.0.6)
75
+ regexp_parser (2.3.0)
76
+ rexml (3.2.5)
77
+ rubocop (1.27.0)
78
+ parallel (~> 1.10)
79
+ parser (>= 3.1.0.0)
80
+ rainbow (>= 2.2.2, < 4.0)
81
+ regexp_parser (>= 1.8, < 3.0)
82
+ rexml
83
+ rubocop-ast (>= 1.16.0, < 2.0)
84
+ ruby-progressbar (~> 1.7)
85
+ unicode-display_width (>= 1.4.0, < 3.0)
86
+ rubocop-ast (1.17.0)
87
+ parser (>= 3.1.1.0)
88
+ rubocop-performance (1.13.3)
89
+ rubocop (>= 1.7.0, < 2.0)
90
+ rubocop-ast (>= 0.4.0)
91
+ ruby-progressbar (1.11.0)
92
+ standard (1.10.0)
93
+ rubocop (= 1.27.0)
94
+ rubocop-performance (= 1.13.3)
95
+ thor (1.2.1)
96
+ tzinfo (2.0.4)
97
+ concurrent-ruby (~> 1.0)
98
+ unicode-display_width (2.1.0)
99
+ zeitwerk (2.5.4)
100
+
101
+ PLATFORMS
102
+ ruby
103
+
104
+ DEPENDENCIES
105
+ bundler
106
+ good_migrations!
107
+ minitest
108
+ pry
109
+ rake
110
+ standard
111
+
112
+ BUNDLED WITH
113
+ 2.3.11
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016 Test Double, LLC
3
+ Copyright (c) 2016-2021 Test Double, Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
data/README.md CHANGED
@@ -1,14 +1,12 @@
1
1
  # good_migrations
2
2
 
3
- [![Build Status](https://travis-ci.org/testdouble/good-migrations.svg?branch=master)](https://travis-ci.org/testdouble/good-migrations)
4
-
5
3
  This gem prevents Rails from auto-loading app code while it's running migrations,
6
4
  preventing the common mistake of referencing ActiveRecord models from migration
7
5
  code.
8
6
 
9
7
  ## Usage
10
8
 
11
- Add good_migrations to your gemfile:
9
+ Add `good_migrations` to your Gemfile:
12
10
 
13
11
  ``` ruby
14
12
  gem 'good_migrations'
@@ -16,6 +14,15 @@ gem 'good_migrations'
16
14
 
17
15
  And you're done! That's it.
18
16
 
17
+ ## Prerequisites
18
+
19
+ This gem requires that your app uses either of these autoloader strategies:
20
+
21
+ * The classic `ActiveSupport::Dependencies` autoloader (e.g. `config.autoloader
22
+ = :classic`), which is going away with Rails 7
23
+ * **Version 2.5 or higher** of the Zeitwerk autoloader (e.g. `config.autoloader =
24
+ :zeitwerk`) If your app uses an earlier version of zeitwerk, you'll see a
25
+ warning every time `db:migrate` is run
19
26
 
20
27
  ## Background
21
28
 
@@ -49,17 +56,70 @@ good!
49
56
  For more background, see the last section of this blog post on [healthy migration
50
57
  habits](http://blog.testdouble.com/posts/2014-11-04-healthy-migration-habits.html)
51
58
 
52
- ## Options
59
+ ## Adding to an existing app
60
+
61
+ If you add `good_migrations` to an existing application **and** any of those
62
+ migrations relied on auto-loading code from `app/`, then you'll see errors
63
+ raised whenever those migrations are run.
64
+
65
+ You have several options if this happens:
66
+
67
+ * If you're confident that every long-lasting environment has run the latest
68
+ migrations, you could consider squashing your existing migrations into a
69
+ single migration file that reflects the current state of your schema. This is
70
+ a tricky procedure to pull off in complex apps, and can require extra
71
+ coordination in cases where a high number of contributors are working on the
72
+ application simultaneously. The
73
+ [squasher](https://github.com/jalkoby/squasher) gem may be able to help.
74
+ * You can rewrite those past migrations to inline any application code inside
75
+ the migration's namespace. One way to do this is to run migrations until they
76
+ fail, check out the git ref of the failing migration so the codebase is
77
+ rewound to where it was at the time the migration was written, and finally
78
+ inline the necessary app code to get the migration passing before checking out
79
+ your primary branch. Rewriting any migration introduces risk of the resulting
80
+ schema diverging from production, so this requires significant care and
81
+ attention
82
+ * If neither of the above options are feasible, you can configure the
83
+ `good_migrations` gem to ignore migrations prior to a specified date with the
84
+ [permit_autoloading_before](#permit_autoloading_before-configuration)
85
+ option, which will effectively disable the gem's auto-loading prevention for
86
+ all migrations prior to a specified time
87
+
88
+ ## Configuration
89
+
90
+ To configure the gem, call `GoodMigrations.config` at some point as Rails is
91
+ loading (a good idea would be an initializer like
92
+ `config/initializers/good_migrations.rb`)
93
+
94
+ ```ruby
95
+ GoodMigrations.config do |config|
96
+ # Setting `permit_autoloading_before` will DISABLE good_migrations for
97
+ # any migrations before the given time. Don't set this unless you need to!
98
+ #
99
+ # Accepts parseable time strings as well as `Date` & `Time` objects
100
+ # config.permit_autoloading_before = "20140728132502"
101
+ end
102
+ ```
53
103
 
54
- There's no public API to this gem. If you want to work around its behavior, you
55
- have a few options:
104
+ ## Working around good_migrations
56
105
 
57
- 1. Run the command with the env var `GOOD_MIGRATIONS=skip`
58
- 2. Explicitly `require` the app code you need in your migration
59
- 3. Remove the gem from your project
106
+ The gem only prevents auto-loading, so you can always can explicitly `require`
107
+ the app code that you need in your migration.
108
+
109
+ If needed, it is possible to run a command with `good_migrations` disabled by
110
+ running the command with the env var `GOOD_MIGRATIONS=skip`.
60
111
 
61
112
  ## Acknowledgements
62
113
 
63
114
  Credit for figuring out where to hook into the ActiveSupport autoloader goes
64
115
  to [@tenderlove](https://github.com/tenderlove) for [this
65
- gist](https://gist.github.com/tenderlove/44447d1b1e466a28eb3f).
116
+ gist](https://gist.github.com/tenderlove/44447d1b1e466a28eb3f). And thanks to
117
+ [@fxn](https://github.com/fxn) for implementing the hook necessary for zeitwerk
118
+ support to be possible.
119
+
120
+ ## Caveats
121
+
122
+ Because this gem works by augmenting the auto-loader, it will not work if your
123
+ Rails environment (development, by default) is configured to eager load your
124
+ application's classes (see:
125
+ [config.eager_load](http://edgeguides.rubyonrails.org/configuring.html#rails-general-configuration)).
data/Rakefile CHANGED
@@ -1,10 +1,11 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
+ require "standard/rake"
3
4
 
4
5
  Rake::TestTask.new(:test) do |t|
5
6
  t.libs << "test"
6
7
  t.libs << "lib"
7
- t.test_files = FileList['test/**/*_test.rb']
8
+ t.test_files = FileList["test/**/*_test.rb"]
8
9
  end
9
10
 
10
- task :default => :test
11
+ task default: [:test, "standard:fix"]
data/example/Gemfile CHANGED
@@ -1,9 +1,11 @@
1
- source 'http://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
- rails_version = ENV['RAILS_VERSION'] || '3.1.0'
4
- gem 'rails', rails_version
3
+ gem "rails", "~> 6.1"
5
4
 
6
- gem 'good_migrations', :path => '..'
5
+ gem "good_migrations", path: ".."
7
6
 
8
- gem 'sqlite3-ruby', :require => 'sqlite3'
7
+ gem "sqlite3"
9
8
 
9
+ gem "zeitwerk", github: "fxn/zeitwerk"
10
+
11
+ gem "bootsnap", require: false
data/example/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  # Add your own tasks in files placed in lib/tasks ending in .rake,
2
2
  # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
3
 
4
- require File.expand_path('../config/application', __FILE__)
5
- require 'rake'
4
+ require File.expand_path("../config/application", __FILE__)
5
+ require "rake"
6
6
 
7
7
  Example::Application.load_tasks
File without changes
@@ -1,3 +1,2 @@
1
1
  class Pant < ActiveRecord::Base
2
2
  end
3
-
@@ -0,0 +1,2 @@
1
+ class Shirt < ActiveRecord::Base
2
+ end
data/example/bin/rails ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path("../config/application", __dir__)
3
+ require_relative "../config/boot"
4
+ require "rails/commands"
data/example/bin/rake ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../config/boot"
3
+ require "rake"
4
+ Rake.application.run
@@ -1,6 +1,6 @@
1
- require File.expand_path('../boot', __FILE__)
1
+ require File.expand_path("../boot", __FILE__)
2
2
 
3
- require 'rails/all'
3
+ require "rails/all"
4
4
 
5
5
  # If you have a Gemfile, require the gems listed there, including any gems
6
6
  # you've limited to :test, :development, or :production.
@@ -8,6 +8,9 @@ Bundler.require(:default, Rails.env) if defined?(Bundler)
8
8
 
9
9
  module Example
10
10
  class Application < Rails::Application
11
+ if config.respond_to?(:eager_load=)
12
+ config.eager_load = false
13
+ end
11
14
  # Settings in config/environments/* take precedence over those specified here.
12
15
  # Application configuration should go into files in config/initializers
13
16
  # -- all .rb files in that directory are automatically loaded.
@@ -38,5 +41,7 @@ module Example
38
41
 
39
42
  # Configure sensitive parameters which will be filtered from the log file.
40
43
  config.filter_parameters += [:password]
44
+
45
+ config.autoloader = ENV["AUTOLOADER"] == "zeitwerk" ? :zeitwerk : :classic
41
46
  end
42
47
  end
@@ -1,13 +1,16 @@
1
- require 'rubygems'
1
+ require "rubygems"
2
2
 
3
3
  # Set up gems listed in the Gemfile.
4
- gemfile = File.expand_path('../../Gemfile', __FILE__)
5
- begin
6
- ENV['BUNDLE_GEMFILE'] = gemfile
7
- require 'bundler'
8
- Bundler.setup
9
- rescue Bundler::GemNotFound => e
10
- STDERR.puts e.message
11
- STDERR.puts "Try running `bundle install`."
12
- exit!
13
- end if File.exist?(gemfile)
4
+ gemfile = File.expand_path("../../Gemfile", __FILE__)
5
+ if File.exist?(gemfile)
6
+ begin
7
+ ENV["BUNDLE_GEMFILE"] = gemfile
8
+ require "bundler"
9
+ Bundler.setup
10
+ rescue Bundler::GemNotFound => e
11
+ warn e.message
12
+ warn "Try running `bundle install`."
13
+ exit!
14
+ end
15
+ end
16
+ require "bootsnap/setup"
@@ -1,5 +1,5 @@
1
1
  # Load the rails application
2
- require File.expand_path('../application', __FILE__)
2
+ require File.expand_path("../application", __FILE__)
3
3
 
4
4
  # Initialize the rails application
5
5
  Example::Application.initialize!
@@ -10,8 +10,7 @@ Example::Application.configure do
10
10
  config.whiny_nils = true
11
11
 
12
12
  # Show full error reports and disable caching
13
- config.consider_all_requests_local = true
14
- config.action_view.debug_rjs = true
13
+ config.consider_all_requests_local = true
15
14
  config.action_controller.perform_caching = false
16
15
 
17
16
  # Don't care if the mailer can't send
@@ -22,5 +21,6 @@ Example::Application.configure do
22
21
 
23
22
  # Only use best-standards-support built into browsers
24
23
  config.action_dispatch.best_standards_support = :builtin
25
- end
26
24
 
25
+ config.active_record.migration_error = false
26
+ end
@@ -6,7 +6,7 @@ Example::Application.configure do
6
6
  config.cache_classes = true
7
7
 
8
8
  # Full error reports are disabled and caching is turned on
9
- config.consider_all_requests_local = false
9
+ config.consider_all_requests_local = false
10
10
  config.action_controller.perform_caching = true
11
11
 
12
12
  # Specifies the header that your server uses for sending files
@@ -11,14 +11,14 @@ Example::Application.configure do
11
11
  config.whiny_nils = true
12
12
 
13
13
  # Show full error reports and disable caching
14
- config.consider_all_requests_local = true
14
+ config.consider_all_requests_local = true
15
15
  config.action_controller.perform_caching = false
16
16
 
17
17
  # Raise exceptions instead of rendering exception templates
18
18
  config.action_dispatch.show_exceptions = false
19
19
 
20
20
  # Disable request forgery protection in test environment
21
- config.action_controller.allow_forgery_protection = false
21
+ config.action_controller.allow_forgery_protection = false
22
22
 
23
23
  # Tell Action Mailer not to deliver emails to the real world.
24
24
  # The :test delivery method accumulates sent emails in the
@@ -0,0 +1,7 @@
1
+ GoodMigrations.config do |config|
2
+ # Setting `permit_autoloading_before` will DISABLE good_migrations for
3
+ # any migrations before the given time. Don't set this unless you need to!
4
+ #
5
+ # Accepts parseable time strings as well as `Date` & `Time` objects
6
+ config.permit_autoloading_before = ENV["PERMIT_AUTOLOADING_BEFORE"]
7
+ end
@@ -4,4 +4,4 @@
4
4
  # If you change this key, all old signed cookies will become invalid!
5
5
  # Make sure the secret is at least 30 characters and all random,
6
6
  # no regular words or you'll be exposed to dictionary attacks.
7
- Example::Application.config.secret_token = '5b687ab969ef4f33a41125ae95e73c368d1c391045639df9a1690221b13f8b8cbeb515db9cf5990548af5c309b8d3ab206648837001866cdf854278030285e3c'
7
+ Example::Application.config.secret_token = "5b687ab969ef4f33a41125ae95e73c368d1c391045639df9a1690221b13f8b8cbeb515db9cf5990548af5c309b8d3ab206648837001866cdf854278030285e3c"
@@ -1,6 +1,6 @@
1
1
  # Be sure to restart your server when you modify this file.
2
2
 
3
- Example::Application.config.session_store :cookie_store, :key => '_example_session'
3
+ Example::Application.config.session_store :cookie_store, key: "_example_session"
4
4
 
5
5
  # Use the database for sessions instead of the cookie-based default,
6
6
  # which shouldn't be used to store highly confidential information
data/example/config.ru CHANGED
@@ -1,4 +1,4 @@
1
1
  # This file is used by Rack-based servers to start the application.
2
2
 
3
- require ::File.expand_path('../config/environment', __FILE__)
3
+ require ::File.expand_path("../config/environment", __FILE__)
4
4
  run Example::Application
@@ -1,4 +1,4 @@
1
- class CreatePants < ActiveRecord::Migration
1
+ class CreatePants < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  create_table :pants do |t|
4
4
  t.integer :length
@@ -1,6 +1,7 @@
1
- class ChangePants < ActiveRecord::Migration
1
+ class ChangePants < ActiveRecord::Migration[4.2]
2
2
  class Pant < ActiveRecord::Base
3
3
  end
4
+
4
5
  def change
5
6
  Pant.all.each do |pant|
6
7
  # do a migration depending on the redefined Pant model
@@ -1,4 +1,4 @@
1
- class ChangePantsDangerously < ActiveRecord::Migration
1
+ class ChangePantsDangerously < ActiveRecord::Migration[4.2]
2
2
  def up
3
3
  Pant.find_each do |pant|
4
4
  # uh oh!
@@ -0,0 +1,8 @@
1
+ class CreateShirts < ActiveRecord::Migration[4.2]
2
+ def change
3
+ create_table :shirts do |t|
4
+ t.integer :size
5
+ t.integer :color
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ class ChangeShirtsDangerously < ActiveRecord::Migration[4.2]
2
+ def up
3
+ Shirt.find_each do |shirt|
4
+ # uh oh!
5
+ end
6
+ end
7
+
8
+ def down
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ # Imagine this being run immediately after a migrate, and SHOULD be allowed
2
+ # to load from app/ because it's not a migration
3
+ task load_pants: :environment do
4
+ puts "This many pants: #{Pant.count} pants"
5
+ end
data/example/script/rails CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
3
 
4
- APP_PATH = File.expand_path('../../config/application', __FILE__)
5
- require File.expand_path('../../config/boot', __FILE__)
6
- require 'rails/commands'
4
+ APP_PATH = File.expand_path("../../config/application", __FILE__)
5
+ require File.expand_path("../../config/boot", __FILE__)
6
+ require "rails/commands"
@@ -1,9 +1,9 @@
1
- require 'test_helper'
2
- require 'rails/performance_test_help'
1
+ require "test_helper"
2
+ require "rails/performance_test_help"
3
3
 
4
4
  # Profiling results for each test method are written to tmp/performance.
5
5
  class BrowsingTest < ActionDispatch::PerformanceTest
6
6
  def test_homepage
7
- get '/'
7
+ get "/"
8
8
  end
9
9
  end
@@ -1,6 +1,6 @@
1
1
  ENV["RAILS_ENV"] = "test"
2
- require File.expand_path('../../config/environment', __FILE__)
3
- require 'rails/test_help'
2
+ require File.expand_path("../../config/environment", __FILE__)
3
+ require "rails/test_help"
4
4
 
5
5
  class ActiveSupport::TestCase
6
6
  # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
@@ -1,28 +1,30 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path("../lib", __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'good_migrations/version'
3
+ require "good_migrations/version"
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "good_migrations"
8
- spec.version = GoodMigrations::VERSION
9
- spec.authors = ["Justin Searls", "Kevin Baribeau"]
10
- spec.email = ["searls@gmail.com", "kevin.baribeau@gmail.com"]
6
+ spec.name = "good_migrations"
7
+ spec.version = GoodMigrations::VERSION
8
+ spec.authors = ["Justin Searls", "Kevin Baribeau"]
9
+ spec.email = ["searls@gmail.com", "kevin.baribeau@gmail.com"]
11
10
 
12
- spec.summary = %q{Prevents Rails from auto-loading app code in database migrations}
13
- spec.description = %q{Referencing code in app/ from a database migration risks breaking the migration when your app code changes; this gem prevents that mistake}
14
- spec.homepage = "https://github.com/testdouble/good-migrations"
11
+ spec.summary = "Prevents Rails from auto-loading app code in database migrations"
12
+ spec.description = "Referencing code in app/ from a database migration risks breaking the migration when your app code changes; this gem prevents that mistake"
13
+ spec.homepage = "https://github.com/testdouble/good-migrations"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.3.0"
15
16
 
16
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
- spec.bindir = "exe"
18
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
20
  spec.require_paths = ["lib"]
20
21
 
21
22
  spec.add_dependency "railties", ">= 3.1"
22
23
  spec.add_dependency "activerecord", ">= 3.1"
23
24
 
24
- spec.add_development_dependency "bundler", "~> 1.10"
25
- spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "bundler"
26
+ spec.add_development_dependency "rake"
26
27
  spec.add_development_dependency "minitest"
27
28
  spec.add_development_dependency "pry"
29
+ spec.add_development_dependency "standard"
28
30
  end
@@ -0,0 +1,38 @@
1
+ module GoodMigrations
2
+ def self.config(&blk)
3
+ @configuration ||= Configuration.new
4
+
5
+ @configuration.tap do |config|
6
+ blk&.call(config)
7
+ end
8
+ end
9
+
10
+ class Configuration
11
+ # Migrations with timestamps (the numbers at the beginning of the file name) from
12
+ # before this configured time will be allowed to perform autoloading, bypassing the
13
+ # mechanism of this gem. Accepts:
14
+ # nil (default): never permit autoload
15
+ # String accepted by `Time.parse`, such as: 20211103150610 or 20211103_150610
16
+ # object responding to `to_time`, such as Date and Time
17
+ attr_reader :permit_autoloading_before
18
+ def permit_autoloading_before=(value)
19
+ case value
20
+ when nil
21
+ # Stay nil
22
+ when String
23
+ value = Time.parse(value)
24
+ else
25
+ if value.respond_to?(:to_time)
26
+ value = value.to_time
27
+ else
28
+ raise "Received an invalid value for permit_autoloading_before: #{value.inspect}"
29
+ end
30
+ end
31
+ @permit_autoloading_before = value
32
+ end
33
+
34
+ def initialize
35
+ @permit_autoloading_before = nil
36
+ end
37
+ end
38
+ end
@@ -1,4 +1,4 @@
1
1
  module GoodMigrations
2
- class LoadError < ::LoadError
2
+ class LoadError < RuntimeError
3
3
  end
4
4
  end
@@ -0,0 +1,30 @@
1
+ module GoodMigrations
2
+ class MigrationDetails
3
+ attr_reader :path
4
+ def initialize(path)
5
+ @path = path
6
+ end
7
+
8
+ def self.currently_executing
9
+ migrate_dir_path = Rails.root.join("db/migrate/").to_s
10
+
11
+ if (loc = caller.detect { |loc| loc.start_with?(migrate_dir_path) })
12
+ line_number_index = loc.index(":", migrate_dir_path.size)
13
+ new(loc[0...line_number_index])
14
+ end
15
+ end
16
+
17
+ def associated_time
18
+ timestamp_string = File.basename(@path).partition("_").first
19
+ return if timestamp_string.size != 14
20
+ Time.parse(timestamp_string)
21
+ end
22
+
23
+ def considered_before?(time)
24
+ return false if time.nil?
25
+ my_time = associated_time
26
+ return false if my_time.nil?
27
+ my_time < time
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,61 @@
1
+ module GoodMigrations
2
+ class PatchesAutoloader
3
+ def self.instance
4
+ @instance ||= new
5
+ end
6
+
7
+ def initialize
8
+ @permits_autoload = PermitsAutoload.new
9
+ @raises_load_error = RaisesLoadError.new
10
+
11
+ @disabled = false
12
+ end
13
+
14
+ def prevent_autoload_if_necessary!(path)
15
+ return if @disabled || @permits_autoload.permit?(path)
16
+
17
+ @raises_load_error.raise!(path)
18
+ end
19
+
20
+ def patch!
21
+ if Rails.singleton_class.method_defined?(:autoloaders) &&
22
+ Rails.autoloaders.zeitwerk_enabled?
23
+ if Rails.autoloaders.main.respond_to?(:on_load) &&
24
+ Rails.autoloaders.main.method(:on_load).arity <= 0
25
+ @disabled = false
26
+ Rails.autoloaders.each do |loader|
27
+ loader.on_load do |_, _, path|
28
+ GoodMigrations::PatchesAutoloader.instance.prevent_autoload_if_necessary!(path)
29
+ end
30
+ end
31
+ else
32
+ warn <<-UNSUPPORTED.strip_heredoc
33
+ WARNING: good_migrations is unable to ensure that your migrations are
34
+ not inadvertently loading application code, because your application
35
+ uses the zeitwerk autoloader (`config.autoloader = :zeitwerk`), but
36
+ is using a version prior to zeitwerk 2.5.0, which adds an on_load
37
+ hook that good_migrations can latch onto.
38
+
39
+ Solution: Ensure that zeitwerk isn't pinned below 2.5.0 in your
40
+ Gemfile and try running `bundle update zeitwerk`.
41
+
42
+ UNSUPPORTED
43
+ end
44
+ else
45
+ @disabled = false
46
+ ActiveSupport::Dependencies.class_eval do
47
+ extend Module.new {
48
+ def load_file(path, const_paths = loadable_constants_for_path(path))
49
+ GoodMigrations::PatchesAutoloader.instance.prevent_autoload_if_necessary!(path)
50
+ super
51
+ end
52
+ }
53
+ end
54
+ end
55
+ end
56
+
57
+ def unpatch!
58
+ @disabled = true
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,19 @@
1
+ module GoodMigrations
2
+ class PermitsAutoload
3
+ def permit?(path)
4
+ !app_path?(path) || permit_autoloading_based_on_migration_time?
5
+ end
6
+
7
+ private
8
+
9
+ def permit_autoloading_based_on_migration_time?
10
+ permit_before_date = GoodMigrations.config.permit_autoloading_before
11
+ migration_details = GoodMigrations::MigrationDetails.currently_executing
12
+ migration_details.considered_before?(permit_before_date)
13
+ end
14
+
15
+ def app_path?(path)
16
+ path.starts_with? File.join(Rails.application.root, "app")
17
+ end
18
+ end
19
+ end
@@ -3,7 +3,7 @@ require "active_record/railtie"
3
3
  module GoodMigrations
4
4
  class Railtie < Rails::Railtie
5
5
  rake_tasks do
6
- Dir[File.join(File.dirname(__FILE__), '../../tasks/*.rake')].each do |file|
6
+ Dir[File.join(File.dirname(__FILE__), "../../tasks/*.rake")].each do |file|
7
7
  load file
8
8
  end
9
9
  end
@@ -0,0 +1,44 @@
1
+ module GoodMigrations
2
+ class RaisesLoadError
3
+ def raise!(path)
4
+ raise GoodMigrations::LoadError, <<-ERROR.strip_heredoc
5
+ Rails attempted to auto-load:
6
+
7
+ #{path}
8
+
9
+ Which is in your project's `app/` directory. The good_migrations
10
+ gem was designed to prevent this, because migrations are intended
11
+ to be immutable and safe-to-run for the life of your project, but
12
+ code in `app/` is liable to change at any time.
13
+
14
+ The most common reason for this error is that you may be referencing an
15
+ ActiveRecord model inside the migration in order to use the ActiveRecord API
16
+ to implement a data migration by querying and updating objects.
17
+
18
+ For instance, if you want to access a model "User" in your migration, it's safer
19
+ to redefine the class inside the migration instead, like this:
20
+
21
+ class MakeUsersOlder < ActiveRecord::Migration
22
+ class User < ActiveRecord::Base
23
+ # Define whatever you need on the User beyond what AR adds automatically
24
+ end
25
+
26
+ def up
27
+ User.find_each do |user|
28
+ user.update!(:age => user.age + 1)
29
+ end
30
+ end
31
+
32
+ def down
33
+ #...
34
+ end
35
+ end
36
+
37
+ For more information, visit:
38
+
39
+ https://github.com/testdouble/good-migrations
40
+
41
+ ERROR
42
+ end
43
+ end
44
+ end
@@ -1,3 +1,3 @@
1
1
  module GoodMigrations
2
- VERSION = "0.0.2"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -1,3 +1,10 @@
1
- require "good_migrations/version"
2
- require "good_migrations/load_error"
3
- require "good_migrations/railtie" if defined?(Rails)
1
+ require "time"
2
+
3
+ require_relative "good_migrations/version"
4
+ require_relative "good_migrations/load_error"
5
+ require_relative "good_migrations/migration_details"
6
+ require_relative "good_migrations/configuration"
7
+ require_relative "good_migrations/patches_autoloader"
8
+ require_relative "good_migrations/permits_autoload"
9
+ require_relative "good_migrations/raises_load_error"
10
+ require_relative "good_migrations/railtie" if defined?(Rails)
@@ -1,63 +1,18 @@
1
- require 'active_support/dependencies'
2
- require 'good_migrations'
1
+ require "active_support/dependencies"
2
+ require "good_migrations"
3
3
 
4
4
  namespace :good_migrations do
5
5
  task :disable_autoload do
6
- next if ENV['GOOD_MIGRATIONS'] == "skip"
7
- ActiveSupport::Dependencies.class_eval do
8
- extend Module.new {
9
- def load_file(path, const_paths = loadable_constants_for_path(path))
10
- if path.starts_with? File.join(Rails.application.root, 'app')
11
- raise GoodMigrations::LoadError, <<-ERROR
12
- Rails attempted to auto-load:
13
-
14
- #{path}
15
-
16
- Which is in your project's `app/` directory. The good_migrations
17
- gem was designed to prevent this, because migrations are intended
18
- to be immutable and safe-to-run for the life of your project, but
19
- code in `app/` is liable to change at any time.
20
-
21
- The most common reason for this error is that you may be referencing an
22
- ActiveRecord model inside the migration in order to use the ActiveRecord API
23
- to implement a data migration by querying and updating objects.
24
-
25
- For instance, if you want to access a model "User" in your migration, it's safer
26
- to redefine the class inside the migration instead, like this:
27
-
28
- class MakeUsersOlder < ActiveRecord::Migration
29
- class User < ActiveRecord::Base
30
- # Define whatever you need on the User beyond what AR adds automatically
31
- end
32
-
33
- def up
34
- User.find_each do |user|
35
- user.update!(:age => user.age + 1)
36
- end
6
+ next if ENV["GOOD_MIGRATIONS"] == "skip"
7
+ GoodMigrations::PatchesAutoloader.instance.patch!
37
8
  end
38
9
 
39
- def down
40
- #...
10
+ task :reenable_autoload do
11
+ next if ENV["GOOD_MIGRATIONS"] == "skip"
12
+ GoodMigrations::PatchesAutoloader.instance.unpatch!
41
13
  end
42
14
  end
43
15
 
44
- For more information, visit:
45
-
46
- https://github.com/testdouble/good-migrations
47
-
48
- ERROR
49
- else
50
- super
51
- end
52
- end
53
- }
54
- end
55
- end
16
+ Rake::Task["db:migrate"].enhance(["good_migrations:disable_autoload"]) do
17
+ Rake::Task["good_migrations:reenable_autoload"].invoke
56
18
  end
57
-
58
- Rake.application.in_namespace('db:migrate') do |namespace|
59
- ([Rake::Task['db:migrate']] + namespace.tasks).each do |task|
60
- task.prerequisites << "good_migrations:disable_autoload"
61
- end
62
- end
63
-
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: good_migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Searls
8
8
  - Kevin Baribeau
9
- autorequire:
9
+ autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2016-02-02 00:00:00.000000000 Z
12
+ date: 2022-04-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: railties
@@ -43,30 +43,30 @@ dependencies:
43
43
  name: bundler
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - "~>"
46
+ - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: '1.10'
48
+ version: '0'
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - "~>"
53
+ - - ">="
54
54
  - !ruby/object:Gem::Version
55
- version: '1.10'
55
+ version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: rake
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - "~>"
60
+ - - ">="
61
61
  - !ruby/object:Gem::Version
62
- version: '10.0'
62
+ version: '0'
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - "~>"
67
+ - - ">="
68
68
  - !ruby/object:Gem::Version
69
- version: '10.0'
69
+ version: '0'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: minitest
72
72
  requirement: !ruby/object:Gem::Requirement
@@ -95,6 +95,20 @@ dependencies:
95
95
  - - ">="
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: standard
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
98
112
  description: Referencing code in app/ from a database migration risks breaking the
99
113
  migration when your app code changes; this gem prevents that mistake
100
114
  email:
@@ -104,9 +118,11 @@ executables: []
104
118
  extensions: []
105
119
  extra_rdoc_files: []
106
120
  files:
121
+ - ".github/workflows/ruby.yml"
107
122
  - ".gitignore"
108
- - ".travis.yml"
123
+ - CHANGELOG.md
109
124
  - Gemfile
125
+ - Gemfile.lock
110
126
  - LICENSE.txt
111
127
  - README.md
112
128
  - Rakefile
@@ -116,10 +132,14 @@ files:
116
132
  - example/Gemfile
117
133
  - example/README
118
134
  - example/Rakefile
135
+ - example/app/assets/config/manifest.js
119
136
  - example/app/controllers/application_controller.rb
120
137
  - example/app/helpers/application_helper.rb
121
138
  - example/app/models/pant.rb
139
+ - example/app/models/shirt.rb
122
140
  - example/app/views/layouts/application.html.erb
141
+ - example/bin/rails
142
+ - example/bin/rake
123
143
  - example/config.ru
124
144
  - example/config/application.rb
125
145
  - example/config/boot.rb
@@ -129,6 +149,7 @@ files:
129
149
  - example/config/environments/production.rb
130
150
  - example/config/environments/test.rb
131
151
  - example/config/initializers/backtrace_silencers.rb
152
+ - example/config/initializers/good_migrations.rb
132
153
  - example/config/initializers/inflections.rb
133
154
  - example/config/initializers/mime_types.rb
134
155
  - example/config/initializers/secret_token.rb
@@ -138,9 +159,12 @@ files:
138
159
  - example/db/migrate/20160202162849_create_pants.rb
139
160
  - example/db/migrate/20160202163803_change_pants.rb
140
161
  - example/db/migrate/20160202182520_change_pants_dangerously.rb
162
+ - example/db/migrate/20170101150000_create_shirts.rb
163
+ - example/db/migrate/20170102150000_change_shirts_dangerously.rb
141
164
  - example/db/seeds.rb
142
165
  - example/doc/README_FOR_APP
143
166
  - example/lib/tasks/.gitkeep
167
+ - example/lib/tasks/load_pants.rake
144
168
  - example/public/404.html
145
169
  - example/public/422.html
146
170
  - example/public/500.html
@@ -161,14 +185,20 @@ files:
161
185
  - example/vendor/plugins/.gitkeep
162
186
  - good_migrations.gemspec
163
187
  - lib/good_migrations.rb
188
+ - lib/good_migrations/configuration.rb
164
189
  - lib/good_migrations/load_error.rb
190
+ - lib/good_migrations/migration_details.rb
191
+ - lib/good_migrations/patches_autoloader.rb
192
+ - lib/good_migrations/permits_autoload.rb
165
193
  - lib/good_migrations/railtie.rb
194
+ - lib/good_migrations/raises_load_error.rb
166
195
  - lib/good_migrations/version.rb
167
196
  - tasks/good_migrations.rake
168
197
  homepage: https://github.com/testdouble/good-migrations
169
- licenses: []
198
+ licenses:
199
+ - MIT
170
200
  metadata: {}
171
- post_install_message:
201
+ post_install_message:
172
202
  rdoc_options: []
173
203
  require_paths:
174
204
  - lib
@@ -176,16 +206,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
176
206
  requirements:
177
207
  - - ">="
178
208
  - !ruby/object:Gem::Version
179
- version: '0'
209
+ version: 2.3.0
180
210
  required_rubygems_version: !ruby/object:Gem::Requirement
181
211
  requirements:
182
212
  - - ">="
183
213
  - !ruby/object:Gem::Version
184
214
  version: '0'
185
215
  requirements: []
186
- rubyforge_project:
187
- rubygems_version: 2.4.5.1
188
- signing_key:
216
+ rubygems_version: 3.3.6
217
+ signing_key:
189
218
  specification_version: 4
190
219
  summary: Prevents Rails from auto-loading app code in database migrations
191
220
  test_files: []
data/.travis.yml DELETED
@@ -1,11 +0,0 @@
1
- language: ruby
2
- sudo: false
3
- rvm:
4
- - 2.2.3
5
- before_script:
6
- - cd example && bundle install
7
- env:
8
- - RAILS_ENV=3.1.0
9
- - RAILS_ENV=4.0.0
10
- - RAILS_ENV=4.2.0
11
- - RAILS_ENV=5.0.0.beta2