code0-zero_track 0.0.0 → 0.0.2

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +0 -0
  3. data/README.md +48 -0
  4. data/Rakefile +0 -0
  5. data/lib/code0/zero_track/context.rb +133 -0
  6. data/lib/code0/zero_track/database/column_methods.rb +35 -0
  7. data/lib/code0/zero_track/database/migration.rb +26 -0
  8. data/lib/code0/zero_track/database/migration_helpers/add_column_enhancements.rb +41 -0
  9. data/lib/code0/zero_track/database/migration_helpers/constraint_helpers.rb +22 -0
  10. data/lib/code0/zero_track/database/migration_helpers/index_helpers.rb +18 -0
  11. data/lib/code0/zero_track/database/migration_helpers/table_enhancements.rb +78 -0
  12. data/lib/code0/zero_track/database/postgresql_adapter/dump_schema_versions_mixin.rb +27 -0
  13. data/lib/code0/zero_track/database/postgresql_database_tasks/load_schema_versions_mixin.rb +26 -0
  14. data/lib/code0/zero_track/database/schema_cleaner.rb +51 -0
  15. data/lib/code0/zero_track/database/schema_migrations/context.rb +48 -0
  16. data/lib/code0/zero_track/database/schema_migrations/migrations.rb +62 -0
  17. data/lib/code0/zero_track/database/schema_migrations.rb +32 -0
  18. data/lib/code0/zero_track/injectors/active_record_schema_migrations.rb +20 -0
  19. data/lib/code0/zero_track/injectors/active_record_timestamps.rb +21 -0
  20. data/lib/code0/zero_track/loggable.rb +48 -0
  21. data/lib/code0/zero_track/logs/json_formatter.rb +42 -0
  22. data/lib/code0/zero_track/memoize.rb +50 -0
  23. data/lib/code0/zero_track/railtie.rb +15 -0
  24. data/lib/code0/zero_track/version.rb +1 -1
  25. data/lib/code0/zero_track.rb +10 -2
  26. data/lib/rubocop/code0/zero_track/file_helpers.rb +25 -0
  27. data/lib/rubocop/cop/code0/zero_track/logs/rails_logger.rb +31 -0
  28. data/lib/rubocop/cop/code0/zero_track/migration/create_table_with_timestamps.rb +72 -0
  29. data/lib/rubocop/cop/code0/zero_track/migration/datetime.rb +53 -0
  30. data/lib/rubocop/cop/code0/zero_track/migration/timestamps.rb +38 -0
  31. data/lib/rubocop/cop/code0/zero_track/migration/versioned_class.rb +93 -0
  32. data/lib/rubocop/zero_track.rb +5 -0
  33. data/lib/tasks/code0/zero_track_tasks.rake +33 -4
  34. metadata +60 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05f6505ffccae6a321db55632618e6a624580871f44cc65aba902e4b59094d4c
4
- data.tar.gz: bc47bd22bfd34e73f62bc3869b1e6578441712e6eed93d440d8ed57fb0192594
3
+ metadata.gz: 6d7131b86c5135e76e99a0b4edb22b76c5fdcc765ef7d00160ce6e85dc8f3ea4
4
+ data.tar.gz: ab601c8af299d5679e16a57748977b0762a71aab0802470acb2a43873109c892
5
5
  SHA512:
6
- metadata.gz: f44199c91669e5a544a0eba5a069ff91e46fa1b725d474a4966d6d3ab6633a716ac4cff781d16569529f73f8860926e0cca7a9c0c8256177987634f8d08f624f
7
- data.tar.gz: 8798de80e9d97a7c0f5c874c7b9671468f9152a5a445c0ccb8d3d61cbfb3f7479af32ae8587847be31d12ab4ba53433d4d5a41a6690e3a12845a9cb33962411a
6
+ metadata.gz: 18fb171af0b27a2d1cf48a690a3d4529ad8daa95bb4d192e2ac6c0a7a5a2c501b922fa5ce48effee6ebced6ec545522b8ef9008587fd6b24d1115b21628ffd16
7
+ data.tar.gz: 5c6e8f01a01d2427aa75265260ea603eb902480e29b4342fa8c6d6ff9e7103da7c3a2be93d4b5b0ee739248de50e4c83d848c1701c8232e5d267e132b39da8a4
data/LICENSE CHANGED
File without changes
data/README.md CHANGED
@@ -18,3 +18,51 @@ Or install it yourself as:
18
18
  ```bash
19
19
  $ gem install code0-zero_track
20
20
  ```
21
+
22
+ ## Features
23
+
24
+ ### `Code0::ZeroTrack::Context`
25
+
26
+ Context allows you to save data in a thread local object. Data from the Context is merged into the
27
+ log messages, if `Code0::ZeroTrack::Logs::JsonFormatter` or `Code0::ZeroTrack::Logs::JsonFormatter::Tagged`
28
+ is used.
29
+
30
+ `.with_context(data, &block)` creates a new context inheriting data from the previous context and adds the
31
+ passed data to it. The new context is dropped after the block finished execution. \
32
+ `.push(data)` creates a new context inheriting data from the previous context and adds the passed data to it. \
33
+ `.current` returns the context from the top of the stack.
34
+
35
+ ### `Code0::ZeroTrack::Memoize`
36
+
37
+ This module can be included to get access to the `memoize(name, reset_on_change, &block)` method.
38
+
39
+ This method allows to memoize a value, so it only gets computed once.
40
+ Each memoize is identified by the name. You can pass a proc to `reset_on_change` and the memoization
41
+ will automatically clear every time returned value changes.
42
+
43
+ `memoized?(name)` allows to check if a value for the given name is currently memoized. \
44
+ Memoizations can be cleared with `clear_memoize(name)` or `clear_memoize!(name)`.
45
+
46
+ ### `config.zero_track.active_record.schema_cleaner`
47
+
48
+ When using `config.active_record.schema_format = :sql`, Rails produces a `db/structure.sql`.
49
+ This file contains a lot of noise that doesn't provide much value.
50
+
51
+ This noise can be cleaned out with `config.zero_track.active_record.schema_cleaner = true`.
52
+
53
+ ### `config.zero_track.active_record.timestamps`
54
+
55
+ Setting `config.zero_track.active_record.timestamps = true` adds `timestamps_with_timezone`
56
+ and `datetime_with_timezone` as methods on the table model when creating tables in migrations.
57
+
58
+ They behave just like `timestamps` and `datetime`, just including timezones.
59
+
60
+ ### `config.zero_track.active_record.schema_migrations`
61
+
62
+ Rails uses the `schema_migrations` table to keep track which migrations have been executed.
63
+ This information is also persisted in the `db/structure.sql`, so the `schema_migrations` table
64
+ can be filled with the correct entries when the schema is loaded from the schema file.
65
+
66
+ This approach is prone to git conflicts, so you can switch to a file based persistence
67
+ with `config.zero_track.active_record.schema_migrations = true`. Instead of an `INSERT INTO` in
68
+ the `db/structure.sql`, this mode creates files in the `db/schema_migrations` directory.
data/Rakefile CHANGED
File without changes
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Code0
4
+ module ZeroTrack
5
+ class Context
6
+ LOG_KEY = 'meta'
7
+ CORRELATION_ID_KEY = 'correlation_id'
8
+ RAW_KEYS = [CORRELATION_ID_KEY].freeze
9
+
10
+ class << self
11
+ def with_context(attributes = {})
12
+ context = push(attributes)
13
+
14
+ begin
15
+ yield(context)
16
+ ensure
17
+ pop(context)
18
+ end
19
+ end
20
+
21
+ def push(new_attributes = {})
22
+ new_context = current&.merge(new_attributes) || new(new_attributes)
23
+
24
+ contexts.push(new_context)
25
+
26
+ new_context
27
+ end
28
+
29
+ def pop(context)
30
+ contexts.pop while contexts.include?(context)
31
+ end
32
+
33
+ def correlation_id
34
+ current&.correlation_id
35
+ end
36
+
37
+ def current
38
+ contexts.last
39
+ end
40
+
41
+ def log_key(key)
42
+ key = key.to_s
43
+ return key if RAW_KEYS.include?(key)
44
+ return key if key.start_with?("#{LOG_KEY}.")
45
+
46
+ "#{LOG_KEY}.#{key}"
47
+ end
48
+
49
+ private
50
+
51
+ def contexts
52
+ Thread.current[:labkit_contexts] ||= []
53
+ end
54
+ end
55
+
56
+ def initialize(values = {})
57
+ @data = {}
58
+
59
+ assign_attributes(values)
60
+ end
61
+
62
+ def merge(new_attributes)
63
+ new_context = self.class.new(data.dup)
64
+ new_context.assign_attributes(new_attributes)
65
+
66
+ new_context
67
+ end
68
+
69
+ def to_h
70
+ expand_data
71
+ end
72
+
73
+ def [](key)
74
+ to_h[log_key(key)]
75
+ end
76
+
77
+ def correlation_id
78
+ data[CORRELATION_ID_KEY]
79
+ end
80
+
81
+ def get_attribute(attribute)
82
+ raw = call_or_value(data[log_key(attribute)])
83
+
84
+ call_or_value(raw)
85
+ end
86
+
87
+ protected
88
+
89
+ def assign_attributes(attributes)
90
+ attributes = attributes.transform_keys(&method(:log_key))
91
+
92
+ data.merge!(attributes)
93
+
94
+ # Remove keys that had their values set to `nil` in the new attributes
95
+ data.keep_if { |_, value| valid_data?(value) }
96
+
97
+ # Assign a correlation if it was missing in the first context or when
98
+ # explicitly removed
99
+ data[CORRELATION_ID_KEY] ||= new_id
100
+
101
+ data
102
+ end
103
+
104
+ private
105
+
106
+ attr_reader :data
107
+
108
+ def log_key(key)
109
+ self.class.log_key(key)
110
+ end
111
+
112
+ def call_or_value(value)
113
+ value.respond_to?(:call) ? value.call : value
114
+ end
115
+
116
+ def expand_data
117
+ data.transform_values do |value|
118
+ value = call_or_value(value)
119
+
120
+ value if valid_data?(value)
121
+ end.compact
122
+ end
123
+
124
+ def new_id
125
+ SecureRandom.hex
126
+ end
127
+
128
+ def valid_data?(value)
129
+ value == false || value.present?
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Code0
4
+ module ZeroTrack
5
+ module Database
6
+ module ColumnMethods
7
+ module Timestamps
8
+ # Appends columns `created_at` and `updated_at` to a table.
9
+ #
10
+ # It is used in table creation like:
11
+ # create_table 'users' do |t|
12
+ # t.timestamps_with_timezone
13
+ # end
14
+ def timestamps_with_timezone(**options)
15
+ options[:null] = false if options[:null].nil?
16
+
17
+ %i[created_at updated_at].each do |column_name|
18
+ column(column_name, :datetime_with_timezone, **options)
19
+ end
20
+ end
21
+
22
+ # Adds specified column with appropriate timestamp type
23
+ #
24
+ # It is used in table creation like:
25
+ # create_table 'users' do |t|
26
+ # t.datetime_with_timezone :did_something_at
27
+ # end
28
+ def datetime_with_timezone(column_name, **options)
29
+ column(column_name, :datetime_with_timezone, **options)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Code0
4
+ module ZeroTrack
5
+ module Database
6
+ class Migration
7
+ # rubocop:disable Naming/ClassAndModuleCamelCase
8
+ class V1_0 < ::ActiveRecord::Migration[7.1]
9
+ include Database::MigrationHelpers::AddColumnEnhancements
10
+ include Database::MigrationHelpers::ConstraintHelpers
11
+ include Database::MigrationHelpers::IndexHelpers
12
+ include Database::MigrationHelpers::TableEnhancements
13
+ end
14
+ # rubocop:enable Naming/ClassAndModuleCamelCase
15
+
16
+ def self.[](version)
17
+ version = version.to_s
18
+ name = "V#{version.tr('.', '_')}"
19
+ raise ArgumentError, "Invalid migration version: #{version}" unless const_defined?(name, false)
20
+
21
+ const_get(name, false)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Code0
4
+ module ZeroTrack
5
+ module Database
6
+ module MigrationHelpers
7
+ module AddColumnEnhancements
8
+ def add_column(table_name, column_name, type, *args, **kwargs, &block)
9
+ helper_context = self
10
+
11
+ limit = kwargs.delete(:limit)
12
+ unique = kwargs.delete(:unique)
13
+
14
+ super
15
+
16
+ return unless type == :text
17
+
18
+ quoted_column_name = helper_context.quote_column_name(column_name)
19
+
20
+ if limit
21
+ name = helper_context.send(:text_limit_name, table_name, column_name)
22
+
23
+ definition = "char_length(#{quoted_column_name}) <= #{limit}"
24
+
25
+ add_check_constraint(table_name, definition, name: name)
26
+ end
27
+
28
+ if unique.is_a?(Hash)
29
+ unique[:where] = "#{column_name} IS NOT NULL" if unique.delete(:allow_nil_duplicate)
30
+ column_name = "LOWER(#{quoted_column_name})" if unique.delete(:case_insensitive)
31
+
32
+ add_index table_name, column_name, unique: true, **unique
33
+ elsif unique
34
+ add_index table_name, column_name, unique: unique
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Code0
4
+ module ZeroTrack
5
+ module Database
6
+ module MigrationHelpers
7
+ module ConstraintHelpers
8
+ def text_limit_name(table, column, name: nil)
9
+ name.presence || check_constraint_name(table, column, 'max_length')
10
+ end
11
+
12
+ def check_constraint_name(table, column, type)
13
+ identifier = "#{table}_#{column}_check_#{type}"
14
+ hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
15
+
16
+ "check_#{hashed_identifier}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Code0
4
+ module ZeroTrack
5
+ module Database
6
+ module MigrationHelpers
7
+ module IndexHelpers
8
+ def index_name(table, column, type)
9
+ identifier = "#{table}_#{column}_index_#{type}"
10
+ hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
11
+
12
+ "index_#{hashed_identifier}"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Code0
4
+ module ZeroTrack
5
+ module Database
6
+ module MigrationHelpers
7
+ module TableEnhancements
8
+ def create_table(table_name, *args, **kwargs, &block)
9
+ helper_context = self
10
+
11
+ super do |t|
12
+ enhance(t, table_name, helper_context, &block)
13
+ end
14
+ end
15
+
16
+ def change_table(table_name, *args, **kwargs, &block)
17
+ helper_context = self
18
+
19
+ super do |t|
20
+ enhance(t, table_name, helper_context, &block)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def enhance(t, table_name, helper_context, &block)
27
+ t.define_singleton_method(:text) do |column_name, **inner_kwargs|
28
+ limit = inner_kwargs.delete(:limit)
29
+ unique = inner_kwargs.delete(:unique)
30
+
31
+ super(column_name, **inner_kwargs)
32
+
33
+ quoted_column_name = helper_context.quote_column_name(column_name)
34
+
35
+ if limit
36
+ name = helper_context.send(:text_limit_name, table_name, column_name)
37
+
38
+ definition = "char_length(#{quoted_column_name}) <= #{limit}"
39
+
40
+ t.check_constraint(definition, name: name)
41
+ end
42
+
43
+ if unique.is_a?(Hash)
44
+ index_definition = column_name
45
+ unique[:where] = "#{column_name} IS NOT NULL" if unique.delete(:allow_nil_duplicate)
46
+ index_definition = "LOWER(#{quoted_column_name})" if unique.delete(:case_insensitive)
47
+
48
+ t.index index_definition, unique: true, **unique
49
+ elsif unique
50
+ t.index column_name, unique: unique
51
+ end
52
+ end
53
+
54
+ t.define_singleton_method(:integer) do |column_name, **inner_kwargs|
55
+ unique = inner_kwargs.delete(:unique)
56
+
57
+ super(column_name, **inner_kwargs)
58
+
59
+ t.index column_name, unique: unique unless unique.nil?
60
+ end
61
+
62
+ return if block.nil?
63
+
64
+ t.instance_eval do |obj|
65
+ if block.arity == 1
66
+ block.call(obj)
67
+ elsif block.arity == 2
68
+ block.call(obj, helper_context)
69
+ else
70
+ raise ArgumentError, "Unsupported arity of #{block.arity}"
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Heavily inspired by the implementation of GitLab
4
+ # (https://gitlab.com/gitlab-org/gitlab/-/blob/7983d2a2203aff265fae479d7c1b7066858d1265/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin.rb)
5
+ # which is licensed under a modified version of the MIT license which can be found at
6
+ # https://gitlab.com/gitlab-org/gitlab/-/blob/7983d2a2203aff265fae479d7c1b7066858d1265/LICENSE
7
+ #
8
+ # The code might have been modified to accommodate for the needs of this project
9
+
10
+ module Code0
11
+ module ZeroTrack
12
+ module Database
13
+ module PostgresqlAdapter
14
+ module DumpSchemaVersionsMixin
15
+ extend ActiveSupport::Concern
16
+
17
+ def dump_schema_information
18
+ # rubocop:disable Rails/SkipsModelValidations -- not an active record object
19
+ Database::SchemaMigrations.touch_all(self) unless Rails.env.production?
20
+ # rubocop:enable Rails/SkipsModelValidations
21
+ nil
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Heavily inspired by the implementation of GitLab
4
+ # (https://gitlab.com/gitlab-org/gitlab/-/blob/7983d2a2203aff265fae479d7c1b7066858d1265/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin.rb)
5
+ # which is licensed under a modified version of the MIT license which can be found at
6
+ # https://gitlab.com/gitlab-org/gitlab/-/blob/7983d2a2203aff265fae479d7c1b7066858d1265/LICENSE
7
+ #
8
+ # The code might have been modified to accommodate for the needs of this project
9
+
10
+ module Code0
11
+ module ZeroTrack
12
+ module Database
13
+ module PostgresqlDatabaseTasks
14
+ module LoadSchemaVersionsMixin
15
+ extend ActiveSupport::Concern
16
+
17
+ def structure_load(...)
18
+ super
19
+
20
+ Database::SchemaMigrations.load_all(connection)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Heavily inspired by the implementation of GitLab
4
+ # (https://gitlab.com/gitlab-org/gitlab/-/blob/7983d2a2203aff265fae479d7c1b7066858d1265/lib/gitlab/database/schema_cleaner.rb)
5
+ # which is licensed under a modified version of the MIT license which can be found at
6
+ # https://gitlab.com/gitlab-org/gitlab/-/blob/7983d2a2203aff265fae479d7c1b7066858d1265/LICENSE
7
+ #
8
+ # The code might have been modified to accommodate for the needs of this project
9
+
10
+ module Code0
11
+ module ZeroTrack
12
+ module Database
13
+ class SchemaCleaner
14
+ attr_reader :original_schema
15
+
16
+ def initialize(original_schema)
17
+ @original_schema = original_schema
18
+ end
19
+
20
+ def clean(io)
21
+ structure = original_schema.dup
22
+
23
+ # Remove noise
24
+ structure.gsub!(/^COMMENT ON EXTENSION.*/, '')
25
+ structure.gsub!(/^SET.+/, '')
26
+ structure.gsub!(/^SELECT pg_catalog\.set_config\('search_path'.+/, '')
27
+ structure.gsub!(/^--.*/, "\n")
28
+
29
+ # We typically don't assume we're working with the public schema.
30
+ # pg_dump uses fully qualified object names though, since we have multiple schemas
31
+ # in the database.
32
+ #
33
+ # The intention here is to not introduce an assumption about the standard schema,
34
+ # unless we have a good reason to do so.
35
+ structure.gsub!(/public\.(\w+)/, '\1')
36
+ structure.gsub!(
37
+ /CREATE EXTENSION IF NOT EXISTS (\w+) WITH SCHEMA public;/,
38
+ 'CREATE EXTENSION IF NOT EXISTS \1;'
39
+ )
40
+
41
+ structure.gsub!(/\n{3,}/, "\n\n")
42
+
43
+ io << structure.strip
44
+ io << "\n"
45
+
46
+ nil
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Heavily inspired by the implementation of GitLab
4
+ # (https://gitlab.com/gitlab-org/gitlab/-/blob/7983d2a2203aff265fae479d7c1b7066858d1265/lib/gitlab/database/schema_migrations/context.rb)
5
+ # which is licensed under a modified version of the MIT license which can be found at
6
+ # https://gitlab.com/gitlab-org/gitlab/-/blob/7983d2a2203aff265fae479d7c1b7066858d1265/LICENSE
7
+ #
8
+ # The code might have been modified to accommodate for the needs of this project
9
+
10
+ module Code0
11
+ module ZeroTrack
12
+ module Database
13
+ module SchemaMigrations
14
+ class Context
15
+ attr_reader :connection
16
+
17
+ class_attribute :default_schema_migrations_path, default: 'db/schema_migrations'
18
+
19
+ def initialize(connection)
20
+ @connection = connection
21
+ end
22
+
23
+ def schema_directory
24
+ @schema_directory ||= Rails.root.join(database_schema_migrations_path).to_s
25
+ end
26
+
27
+ def versions_to_create
28
+ versions_from_database = @connection.pool.schema_migration.versions
29
+ versions_from_migration_files = @connection.pool.migration_context.migrations.map { |m| m.version.to_s }
30
+
31
+ versions_from_database & versions_from_migration_files
32
+ end
33
+
34
+ private
35
+
36
+ def database_name
37
+ @database_name ||= @connection.pool.db_config.name
38
+ end
39
+
40
+ def database_schema_migrations_path
41
+ @connection.pool.db_config.configuration_hash[:schema_migrations_path] ||
42
+ self.class.default_schema_migrations_path
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Heavily inspired by the implementation of GitLab
4
+ # (https://gitlab.com/gitlab-org/gitlab/-/blob/7983d2a2203aff265fae479d7c1b7066858d1265/lib/gitlab/database/schema_migrations/migrations.rb)
5
+ # which is licensed under a modified version of the MIT license which can be found at
6
+ # https://gitlab.com/gitlab-org/gitlab/-/blob/7983d2a2203aff265fae479d7c1b7066858d1265/LICENSE
7
+ #
8
+ # The code might have been modified to accommodate for the needs of this project
9
+
10
+ module Code0
11
+ module ZeroTrack
12
+ module Database
13
+ module SchemaMigrations
14
+ class Migrations
15
+ MIGRATION_VERSION_GLOB = '20[0-9][0-9]*'
16
+
17
+ def initialize(context)
18
+ @context = context
19
+ end
20
+
21
+ def touch_all
22
+ return unless @context.versions_to_create.any?
23
+
24
+ version_filepaths = version_filenames.map { |f| File.join(schema_directory, f) }
25
+ FileUtils.rm(version_filepaths)
26
+
27
+ @context.versions_to_create.each do |version|
28
+ version_filepath = File.join(schema_directory, version)
29
+
30
+ File.open(version_filepath, 'w') do |file|
31
+ file << Digest::SHA256.hexdigest(version)
32
+ end
33
+ end
34
+ end
35
+
36
+ def load_all
37
+ return if version_filenames.empty?
38
+ return unless @context.connection.pool.schema_migration.table_exists?
39
+
40
+ values = version_filenames.map { |vf| "('#{@context.connection.quote_string(vf)}')" }
41
+
42
+ @context.connection.execute(<<~SQL.squish)
43
+ INSERT INTO schema_migrations (version)
44
+ VALUES #{values.join(',')}
45
+ ON CONFLICT DO NOTHING
46
+ SQL
47
+ end
48
+
49
+ private
50
+
51
+ def schema_directory
52
+ @context.schema_directory
53
+ end
54
+
55
+ def version_filenames
56
+ @version_filenames ||= Dir.glob(MIGRATION_VERSION_GLOB, base: schema_directory)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Heavily inspired by the implementation of GitLab
4
+ # (https://gitlab.com/gitlab-org/gitlab/-/blob/7983d2a2203aff265fae479d7c1b7066858d1265/lib/gitlab/database/schema_migrations.rb)
5
+ # which is licensed under a modified version of the MIT license which can be found at
6
+ # https://gitlab.com/gitlab-org/gitlab/-/blob/7983d2a2203aff265fae479d7c1b7066858d1265/LICENSE
7
+ #
8
+ # The code might have been modified to accommodate for the needs of this project
9
+
10
+ module Code0
11
+ module ZeroTrack
12
+ module Database
13
+ module SchemaMigrations
14
+ module_function
15
+
16
+ def touch_all(connection)
17
+ context = Database::SchemaMigrations::Context.new(connection)
18
+
19
+ # rubocop:disable Rails/SkipsModelValidations -- not an active record object
20
+ Database::SchemaMigrations::Migrations.new(context).touch_all
21
+ # rubocop:enable Rails/SkipsModelValidations
22
+ end
23
+
24
+ def load_all(connection)
25
+ context = Database::SchemaMigrations::Context.new(connection)
26
+
27
+ Database::SchemaMigrations::Migrations.new(context).load_all
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end