temporal_tables 0.7.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +53 -0
  3. data/.rubocop.yml +158 -0
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +5 -7
  6. data/Gemfile +2 -0
  7. data/README.md +15 -5
  8. data/Rakefile +7 -2
  9. data/config.ru +2 -0
  10. data/gemfiles/Gemfile.6.0.mysql.lock +84 -84
  11. data/gemfiles/Gemfile.6.0.pg.lock +103 -98
  12. data/gemfiles/Gemfile.6.1.mysql.lock +180 -0
  13. data/gemfiles/Gemfile.6.1.pg.lock +180 -0
  14. data/gemfiles/{Gemfile.5.1.mysql → Gemfile.7.0.mysql} +2 -2
  15. data/gemfiles/Gemfile.7.0.mysql.lock +173 -0
  16. data/gemfiles/{Gemfile.5.2.pg → Gemfile.7.0.pg} +1 -1
  17. data/gemfiles/Gemfile.7.0.pg.lock +173 -0
  18. data/lib/temporal_tables/arel_table.rb +10 -9
  19. data/lib/temporal_tables/association_extensions.rb +2 -0
  20. data/lib/temporal_tables/connection_adapters/mysql_adapter.rb +8 -3
  21. data/lib/temporal_tables/connection_adapters/postgresql_adapter.rb +5 -3
  22. data/lib/temporal_tables/history_hook.rb +8 -5
  23. data/lib/temporal_tables/preloader_extensions.rb +2 -0
  24. data/lib/temporal_tables/reflection_extensions.rb +11 -14
  25. data/lib/temporal_tables/relation_extensions.rb +11 -24
  26. data/lib/temporal_tables/temporal_adapter.rb +93 -97
  27. data/lib/temporal_tables/temporal_adapter_six_oh.rb +187 -0
  28. data/lib/temporal_tables/temporal_class.rb +29 -28
  29. data/lib/temporal_tables/version.rb +3 -1
  30. data/lib/temporal_tables/whodunnit.rb +5 -3
  31. data/lib/temporal_tables.rb +48 -33
  32. data/spec/basic_history_spec.rb +65 -35
  33. data/spec/internal/app/models/broom.rb +2 -0
  34. data/spec/internal/app/models/cat.rb +5 -0
  35. data/spec/internal/app/models/cat_life.rb +5 -0
  36. data/spec/internal/app/models/coven.rb +2 -0
  37. data/spec/internal/app/models/flying_machine.rb +2 -0
  38. data/spec/internal/app/models/person.rb +2 -0
  39. data/spec/internal/app/models/rocket_broom.rb +2 -0
  40. data/spec/internal/app/models/wart.rb +3 -1
  41. data/spec/internal/config/database.ci.yml +12 -0
  42. data/spec/internal/db/schema.rb +28 -5
  43. data/spec/spec_helper.rb +39 -5
  44. data/spec/support/database.rb +10 -6
  45. data/temporal_tables.gemspec +31 -18
  46. metadata +108 -39
  47. data/CHANGELOG.md +0 -73
  48. data/gemfiles/Gemfile.5.0.mysql +0 -16
  49. data/gemfiles/Gemfile.5.0.mysql.lock +0 -147
  50. data/gemfiles/Gemfile.5.0.pg +0 -16
  51. data/gemfiles/Gemfile.5.0.pg.lock +0 -147
  52. data/gemfiles/Gemfile.5.1.mysql.lock +0 -147
  53. data/gemfiles/Gemfile.5.1.pg +0 -16
  54. data/gemfiles/Gemfile.5.1.pg.lock +0 -147
  55. data/gemfiles/Gemfile.5.2.mysql +0 -16
  56. data/gemfiles/Gemfile.5.2.mysql.lock +0 -155
  57. data/gemfiles/Gemfile.5.2.pg.lock +0 -155
  58. data/lib/temporal_tables/join_extensions.rb +0 -20
  59. data/spec/extensions/combustion.rb +0 -9
  60. data/spec/internal/config/routes.rb +0 -3
@@ -1,67 +1,82 @@
1
- require "temporal_tables/temporal_adapter"
2
- require "temporal_tables/connection_adapters/mysql_adapter"
3
- require "temporal_tables/connection_adapters/postgresql_adapter"
4
- require "temporal_tables/whodunnit"
5
- require "temporal_tables/temporal_class"
6
- require "temporal_tables/history_hook"
7
- require "temporal_tables/relation_extensions"
8
- require "temporal_tables/association_extensions"
9
- require "temporal_tables/join_extensions"
10
- require "temporal_tables/preloader_extensions"
11
- require "temporal_tables/reflection_extensions"
12
- require "temporal_tables/arel_table"
13
- require "temporal_tables/version"
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporal_tables/temporal_adapter'
4
+ require 'temporal_tables/temporal_adapter_six_oh'
5
+ require 'temporal_tables/connection_adapters/mysql_adapter'
6
+ require 'temporal_tables/connection_adapters/postgresql_adapter'
7
+ require 'temporal_tables/whodunnit'
8
+ require 'temporal_tables/temporal_class'
9
+ require 'temporal_tables/history_hook'
10
+ require 'temporal_tables/relation_extensions'
11
+ require 'temporal_tables/association_extensions'
12
+ require 'temporal_tables/preloader_extensions'
13
+ require 'temporal_tables/reflection_extensions'
14
+ require 'temporal_tables/arel_table'
15
+ require 'temporal_tables/version'
14
16
 
15
17
  module TemporalTables
16
18
  class Railtie < ::Rails::Railtie
17
- initializer "temporal_tables.load" do
19
+ initializer 'temporal_tables.load' do
18
20
  # Iterating the subclasses will find any adapter implementations
19
21
  # which are in use by the rails app, and mixin the temporal functionality.
20
22
  # It's necessary to do this on the implementations in order for the
21
23
  # alias method chain hooks to work.
22
24
  ActiveRecord::ConnectionAdapters::AbstractAdapter.subclasses.each do |subclass|
23
- subclass.send :prepend, TemporalTables::TemporalAdapter
25
+ if ActiveRecord.version < ::Gem::Version.new('6.1')
26
+ subclass.send :prepend, TemporalTables::TemporalAdapterSixOh
27
+ else
28
+ subclass.send :prepend, TemporalTables::TemporalAdapter
29
+ end
30
+
31
+ module_name = subclass.name.split('::').last
32
+ next unless TemporalTables::ConnectionAdapters.const_defined?(module_name)
24
33
 
25
- module_name = subclass.name.split("::").last
26
- subclass.send :prepend, TemporalTables::ConnectionAdapters.const_get(module_name) if TemporalTables::ConnectionAdapters.const_defined?(module_name)
34
+ subclass.send(
35
+ :prepend,
36
+ TemporalTables::ConnectionAdapters.const_get(module_name)
37
+ )
27
38
  end
28
39
 
29
- ActiveRecord::Base.send :include, TemporalTables::Whodunnit
40
+ ActiveRecord::Base.include TemporalTables::Whodunnit
30
41
  end
31
42
  end
32
43
 
33
- @@create_by_default = false
44
+ @create_by_default = false
34
45
  def self.create_by_default
35
- @@create_by_default
46
+ @create_by_default
36
47
  end
48
+
37
49
  def self.create_by_default=(default)
38
- @@create_by_default = default
50
+ @create_by_default = default
39
51
  end
40
52
 
41
- @@skipped_temporal_tables = [:schema_migrations, :sessions, :ar_internal_metadata]
53
+ @skipped_temporal_tables = [:schema_migrations, :sessions, :ar_internal_metadata]
42
54
  def self.skip_temporal_table_for(*tables)
43
- @@skipped_temporal_tables += tables
55
+ @skipped_temporal_tables += tables
44
56
  end
57
+
45
58
  def self.skipped_temporal_tables
46
- @@skipped_temporal_tables.dup
59
+ @skipped_temporal_tables.dup
47
60
  end
48
61
 
49
- @@add_updated_by_field = false
50
- @@updated_by_type = :string
51
- @@updated_by_proc = nil
62
+ @add_updated_by_field = false
63
+ @updated_by_type = :string
64
+ @updated_by_proc = nil
52
65
  def self.updated_by_type
53
- @@updated_by_type
66
+ @updated_by_type
54
67
  end
68
+
55
69
  def self.updated_by_proc
56
- @@updated_by_proc
70
+ @updated_by_proc
57
71
  end
72
+
58
73
  def self.add_updated_by_field(type = :string, &block)
59
74
  if block_given?
60
- @@add_updated_by_field = true
61
- @@updated_by_type = type
62
- @@updated_by_proc = block
75
+ @add_updated_by_field = true
76
+ @updated_by_type = type
77
+ @updated_by_proc = block
63
78
  end
64
79
 
65
- @@add_updated_by_field
80
+ @add_updated_by_field
66
81
  end
67
82
  end
@@ -1,66 +1,68 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Person do
4
- let(:emily) { Person.create name: "Emily" }
6
+ let(:emily) { Person.create name: 'Emily' }
5
7
  let(:historical_emily) { emily.history.last }
6
8
 
7
9
  before do
8
10
  emily
9
- @init_time = Time.now
11
+ @init_time = Time.zone.now
10
12
  sleep 0.1
11
13
  end
12
14
 
13
- describe "upon making significant life changes" do
14
- let!(:coven) { Coven.create name: "Double Double Toil & Trouble" }
15
+ describe 'upon making significant life changes' do
16
+ let!(:coven) { Coven.create name: 'Double Double Toil & Trouble' }
15
17
  let!(:wart) { Wart.create person: emily, hairiness: 3 }
16
18
 
17
19
  before do
18
- emily.update name: "Grunthilda", coven: coven
20
+ emily.update name: 'Grunthilda', coven: coven
19
21
  sleep 0.1
20
22
  end
21
23
 
22
- describe "when affirming changes" do
23
- it "should have new name" do
24
- expect(emily.name).to eq("Grunthilda")
25
- expect(historical_emily.name).to eq("Grunthilda")
24
+ describe 'when affirming changes' do
25
+ it 'should have new name' do
26
+ expect(emily.name).to eq('Grunthilda')
27
+ expect(historical_emily.name).to eq('Grunthilda')
26
28
  end
27
29
 
28
- it "should belong to coven" do
30
+ it 'should belong to coven' do
29
31
  expect(emily.coven.name).to eq(coven.name)
30
32
  expect(historical_emily.coven.name).to eq(coven.name)
31
33
  end
32
34
 
33
- it "should have a wart" do
35
+ it 'should have a wart' do
34
36
  expect(emily.warts).to eq([wart])
35
- expect(emily.history.at(Time.now).last.warts).to eq([wart.history.last])
37
+ expect(emily.history.at(Time.zone.now).last.warts).to eq([wart.history.last])
36
38
  end
37
39
 
38
- it "should allow scopes on associations" do
40
+ it 'should allow scopes on associations' do
39
41
  expect(emily.warts.very_hairy).to eq([wart])
40
42
  expect(historical_emily.warts.very_hairy).to eq([wart.history.last])
41
43
  end
42
44
 
43
- it "should allow at value on class too" do
44
- expect(Wart.history.at(Time.now).where(person: emily).count).to eq(1)
45
+ it 'should allow at value on class too' do
46
+ expect(Wart.history.at(Time.zone.now).where(person: emily).count).to eq(1)
45
47
  expect(Wart.history.at(1.minute.ago).where(person: emily).count).to eq(0)
46
48
  end
47
49
  end
48
50
 
49
- describe "when reflecting on the past" do
51
+ describe 'when reflecting on the past' do
50
52
  let(:orig_emily) { emily.history.at(@init_time).last }
51
53
 
52
- it "should have historical name" do
53
- expect(orig_emily.name).to eq("Emily")
54
+ it 'should have historical name' do
55
+ expect(orig_emily.name).to eq('Emily')
54
56
  expect(orig_emily.at_value).to eq(@init_time)
55
57
  end
56
58
 
57
- it "should not belong to a coven or have warts" do
59
+ it 'should not belong to a coven or have warts' do
58
60
  expect(orig_emily.coven).to eq(nil)
59
61
  expect(orig_emily.warts.count).to eq(0)
60
62
  end
61
63
  end
62
64
 
63
- describe "when preloading associations" do
65
+ describe 'when preloading associations' do
64
66
  let(:orig_emily) { emily.history.at(@init_time).preload(:warts).first }
65
67
 
66
68
  it 'should preload the correct time' do
@@ -68,7 +70,7 @@ describe Person do
68
70
  end
69
71
  end
70
72
 
71
- describe "when eager_loading associations" do
73
+ describe 'when eager_loading associations' do
72
74
  let(:orig_emily) { emily.history.at(@init_time).eager_load(:warts).first }
73
75
 
74
76
  it 'should include the correct time' do
@@ -76,7 +78,14 @@ describe Person do
76
78
  end
77
79
 
78
80
  it 'should generate sensible sql' do
79
- sql = emily.history.at(@init_time).eager_load(:warts).where(Wart.history.arel_table[:hairiness].gteq(2)).to_sql.split(/(FROM)|(WHERE)|(ORDER)/)
81
+ sql =
82
+ emily
83
+ .history
84
+ .at(@init_time)
85
+ .eager_load(:warts)
86
+ .where(Wart.history.arel_table[:hairiness].gteq(2))
87
+ .to_sql
88
+ .split(/(FROM)|(WHERE)|(ORDER)/)
80
89
  from = sql[2]
81
90
  where = sql[4]
82
91
 
@@ -90,15 +99,15 @@ describe Person do
90
99
  end
91
100
  end
92
101
 
93
- describe "when checking simple code values" do
94
- it "should have correct class names" do
95
- expect(emily.class.name).to eq("Person")
96
- expect(historical_emily.class.name).to eq("PersonHistory")
102
+ describe 'when checking simple code values' do
103
+ it 'should have correct class names' do
104
+ expect(emily.class.name).to eq('Person')
105
+ expect(historical_emily.class.name).to eq('PersonHistory')
97
106
 
98
107
  expect(Person.history).to eq(PersonHistory)
99
108
  end
100
109
 
101
- it "should have correct class hierarchies" do
110
+ it 'should have correct class hierarchies' do
102
111
  expect(emily.is_a?(Person)).to eq(true)
103
112
  expect(emily.is_a?(PersonHistory)).to eq(false)
104
113
 
@@ -107,8 +116,8 @@ describe Person do
107
116
  end
108
117
  end
109
118
 
110
- describe "when checking current state" do
111
- it "should have correct information" do
119
+ describe 'when checking current state' do
120
+ it 'should have correct information' do
112
121
  # ie. we shouldn't break regular ActiveRecord behaviour
113
122
  expect(Person.count).to eq(1)
114
123
  expect(Wart.count).to eq(1)
@@ -123,20 +132,41 @@ describe Person do
123
132
  end
124
133
  end
125
134
 
126
- describe "when working with STI one level deep" do
127
- let!(:broom) { Broom.create person: emily, model: "Cackler 2000" }
135
+ describe 'when working with STI one level deep' do
136
+ let!(:broom) { Broom.create person: emily, model: 'Cackler 2000' }
128
137
 
129
- it "should initialize model correctly" do
138
+ it 'should initialize model correctly' do
130
139
  expect(emily.history.last.flying_machines).to eq([broom.history.last])
131
140
  end
132
141
  end
133
142
 
134
- describe "when working with STI two levels deep" do
135
- let!(:rocket_broom) { RocketBroom.create person: emily, model: "Pyrocackler 3000X" }
143
+ describe 'when working with STI two levels deep' do
144
+ let!(:rocket_broom) { RocketBroom.create person: emily, model: 'Pyrocackler 3000X' }
136
145
 
137
- it "should initialize model correctly" do
146
+ it 'should initialize model correctly' do
138
147
  expect(emily.history.last.flying_machines).to eq([rocket_broom.history.last])
139
148
  end
140
149
  end
141
150
  end
151
+
152
+ # The following only tests non-integer ids for postgres (see schema.rb)
153
+ describe 'when spawning and aging a creature with a non-integer id' do
154
+ let!(:cat) { Cat.create name: 'Mr. Mittens', color: 'black' }
155
+
156
+ before do
157
+ cat.lives.create started_at: 3.years.ago
158
+ @init_time = Time.zone.now
159
+ cat.update name: 'Old Mr. Mittens'
160
+ cat.lives.first.update ended_at: Time.zone.now, death_reason: 'fell into cauldron'
161
+ cat.lives.create started_at: Time.zone.now
162
+ end
163
+
164
+ it 'shows one life at the beginning' do
165
+ expect(cat.history.at(@init_time).last.lives.size).to eq(1)
166
+ end
167
+
168
+ it 'shows two lives at the end' do
169
+ expect(cat.history.last.lives.size).to eq(2)
170
+ end
171
+ end
142
172
  end
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Broom < FlyingMachine
2
4
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Cat < ActiveRecord::Base
4
+ has_many :lives, class_name: 'CatLife'
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CatLife < ActiveRecord::Base
4
+ belongs_to :cat
5
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Coven < ActiveRecord::Base
2
4
  has_many :people
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class FlyingMachine < ActiveRecord::Base
2
4
  belongs_to :person
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Person < ActiveRecord::Base
2
4
  belongs_to :coven
3
5
  has_many :warts
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class RocketBroom < Broom
2
4
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Wart < ActiveRecord::Base
2
4
  belongs_to :person
3
5
 
4
- scope :very_hairy, -> {
6
+ scope :very_hairy, lambda {
5
7
  where(arel_table[:hairiness].gteq(3))
6
8
  }
7
9
  end
@@ -0,0 +1,12 @@
1
+ mysql:
2
+ adapter: mysql2
3
+ database: temporal_tables_test
4
+ host: 127.0.0.1
5
+ username: root
6
+ password: password
7
+
8
+ postgresql:
9
+ adapter: postgresql
10
+ database: temporal_tables_test
11
+ user: postgres
12
+ password:
@@ -1,22 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ postgres = ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
5
+ rescue NameError
6
+ postgres = false
7
+ end
8
+
1
9
  ActiveRecord::Schema.define do
2
- create_table :people, temporal: true, force: true do |t|
3
- t.belongs_to :coven
4
- t.string :name
5
- end
10
+ enable_extension 'pgcrypto' if postgres
6
11
 
7
12
  create_table :covens, force: true do |t|
8
13
  t.string :name
9
14
  end
10
15
  add_temporal_table :covens
11
16
 
17
+ create_table :people, temporal: true, force: true do |t|
18
+ t.belongs_to :coven
19
+ t.string :name
20
+ end
21
+ add_index :people, :name, unique: true
22
+
12
23
  create_table :warts, temporal: true, force: true do |t|
13
24
  t.belongs_to :person
14
- t.integer :hairiness
15
25
  end
26
+ add_column :warts, :hairiness, :integer
16
27
 
17
28
  create_table :flying_machines, temporal: true, force: true do |t|
18
29
  t.belongs_to :person
19
30
  t.string :type
20
31
  t.string :model
21
32
  end
33
+
34
+ create_table :cats, id: (postgres ? :uuid : :integer), temporal: true, force: true do |t|
35
+ t.string :name
36
+ t.string :color
37
+ end
38
+
39
+ create_table :cat_lives, id: (postgres ? :uuid : :integer), temporal: true do |t|
40
+ t.belongs_to :cat, type: (postgres ? :uuid : :integer)
41
+ t.timestamp :started_at
42
+ t.timestamp :ended_at
43
+ t.string :death_reason
44
+ end
22
45
  end
data/spec/spec_helper.rb CHANGED
@@ -1,14 +1,48 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'gemika'
2
4
  require 'combustion'
5
+ require 'yaml'
3
6
 
4
- Dir["#{File.dirname(__FILE__)}/extensions/*.rb"].sort.each {|f| require f}
5
- Dir["#{File.dirname(__FILE__)}/support/*.rb"].sort.each {|f| require f}
7
+ Dir["#{File.dirname(__FILE__)}/extensions/*.rb"].sort.each { |f| require f }
8
+ Dir["#{File.dirname(__FILE__)}/support/*.rb"].sort.each { |f| require f }
9
+ READ_DATABASE_CONFIG_LOCATION = 'spec/internal/config/database.ci.yml'
10
+ WRITE_DATABASE_CONFIG_LOCATION = 'spec/internal/config/database.yml'
6
11
 
7
- Combustion.initialize! :active_record do
8
- Rails.env = TemporalTables::DatabaseAdapter.adapter_name
12
+ def adapter_name
13
+ if Gemika::Env.gem?('mysql2')
14
+ 'mysql'
15
+ else
16
+ 'postgresql'
17
+ end
9
18
  end
10
19
 
11
- DatabaseCleaner.strategy = :deletion
20
+ def database_config_from_gems(file_location)
21
+ config = YAML.load_file(file_location)
22
+ data = config.slice(adapter_name)
23
+ { Rails.env.to_s => data }
24
+ end
25
+
26
+ original_env = Rails.env
27
+
28
+ puts database_config_from_gems(READ_DATABASE_CONFIG_LOCATION)
29
+ File.write(
30
+ WRITE_DATABASE_CONFIG_LOCATION,
31
+ database_config_from_gems(READ_DATABASE_CONFIG_LOCATION).to_yaml
32
+ )
33
+
34
+ Rails.env = adapter_name
35
+ database = Gemika::Database.new
36
+ database.connect
37
+
38
+ Gemika::RSpec.configure_clean_database_before_example
39
+ Rails.env = original_env
40
+
41
+ begin
42
+ Combustion.initialize! :active_record
43
+ rescue ActiveRecord::RecordNotUnique
44
+ # noop
45
+ end
12
46
 
13
47
  RSpec.configure do |config|
14
48
  config.before(:each) do
@@ -1,9 +1,13 @@
1
- module TemporalTables::DatabaseAdapter
2
- def self.adapter_name
3
- if Gemika::Env.gem?('pg')
4
- "postgresql"
5
- elsif Gemika::Env.gem?('mysql2')
6
- "mysql"
1
+ # frozen_string_literal: true
2
+
3
+ module TemporalTables
4
+ module DatabaseAdapter
5
+ def self.adapter_name
6
+ if Gemika::Env.gem?('pg')
7
+ 'postgresql'
8
+ elsif Gemika::Env.gem?('mysql2')
9
+ 'mysql'
10
+ end
7
11
  end
8
12
  end
9
13
  end
@@ -1,24 +1,37 @@
1
- # -*- encoding: utf-8 -*-
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ lib = File.expand_path('lib', __dir__)
3
5
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
6
  require 'temporal_tables/version'
5
7
 
6
- Gem::Specification.new do |gem|
7
- gem.name = "temporal_tables"
8
- gem.version = TemporalTables::VERSION
9
- gem.authors = ["Brent Kroeker"]
10
- gem.email = ["brent@bkroeker.com"]
11
- gem.description = %q{Easily recall what your data looked like at any point in the past! TemporalTables sets up and maintains history tables to track all temporal changes to to your data.}
12
- gem.summary = %q{Tracks all history of changes to a table automatically in a history table.}
13
- gem.homepage = ""
8
+ Gem::Specification.new do |gem| # rubocop:disable Metrics/BlockLength
9
+ gem.name = 'temporal_tables'
10
+ gem.version = TemporalTables::VERSION
11
+ gem.authors = ['Brent Kroeker']
12
+ gem.email = ['brent@bkroeker.com']
13
+ gem.description = <<-DESC
14
+ Easily recall what your data looked like at any point in the past!
15
+ TemporalTables sets up and maintains history tables to track all temporal changes to to your data.
16
+ DESC
17
+ gem.summary = 'Tracks all history of changes to a table automatically in a history table.'
18
+ gem.homepage = ''
14
19
 
15
- gem.files = `git ls-files`.split($/)
16
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
- gem.require_paths = ["lib"]
20
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
21
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
22
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
23
+ gem.require_paths = ['lib']
24
+ gem.required_ruby_version = '>= 2.5.0'
25
+ gem.metadata = { 'rubygems_mfa_required' => 'true' }
19
26
 
20
- gem.add_dependency "rails", ">= 5.0", "< 6.1"
21
- gem.add_development_dependency "rspec", "~> 3.4"
22
- gem.add_development_dependency "combustion", "~> 0.9.1"
23
- gem.add_development_dependency "gemika"
27
+ gem.add_dependency 'rails', '>= 6.0', '< 7.1'
28
+ gem.add_development_dependency 'combustion', '~> 1'
29
+ gem.add_development_dependency 'database_cleaner'
30
+ gem.add_development_dependency 'gemika', '~> 0.6'
31
+ gem.add_development_dependency 'mysql2'
32
+ gem.add_development_dependency 'pg'
33
+ gem.add_development_dependency 'pry'
34
+ gem.add_development_dependency 'rspec', '~> 3.4'
35
+ gem.add_development_dependency 'rubocop'
36
+ gem.metadata['rubygems_mfa_required'] = 'true'
24
37
  end