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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +53 -0
- data/.rubocop.yml +158 -0
- data/.ruby-version +1 -1
- data/.travis.yml +5 -7
- data/Gemfile +2 -0
- data/README.md +15 -5
- data/Rakefile +7 -2
- data/config.ru +2 -0
- data/gemfiles/Gemfile.6.0.mysql.lock +84 -84
- data/gemfiles/Gemfile.6.0.pg.lock +103 -98
- data/gemfiles/Gemfile.6.1.mysql.lock +180 -0
- data/gemfiles/Gemfile.6.1.pg.lock +180 -0
- data/gemfiles/{Gemfile.5.1.mysql → Gemfile.7.0.mysql} +2 -2
- data/gemfiles/Gemfile.7.0.mysql.lock +173 -0
- data/gemfiles/{Gemfile.5.2.pg → Gemfile.7.0.pg} +1 -1
- data/gemfiles/Gemfile.7.0.pg.lock +173 -0
- data/lib/temporal_tables/arel_table.rb +10 -9
- data/lib/temporal_tables/association_extensions.rb +2 -0
- data/lib/temporal_tables/connection_adapters/mysql_adapter.rb +8 -3
- data/lib/temporal_tables/connection_adapters/postgresql_adapter.rb +5 -3
- data/lib/temporal_tables/history_hook.rb +8 -5
- data/lib/temporal_tables/preloader_extensions.rb +2 -0
- data/lib/temporal_tables/reflection_extensions.rb +11 -14
- data/lib/temporal_tables/relation_extensions.rb +11 -24
- data/lib/temporal_tables/temporal_adapter.rb +93 -97
- data/lib/temporal_tables/temporal_adapter_six_oh.rb +187 -0
- data/lib/temporal_tables/temporal_class.rb +29 -28
- data/lib/temporal_tables/version.rb +3 -1
- data/lib/temporal_tables/whodunnit.rb +5 -3
- data/lib/temporal_tables.rb +48 -33
- data/spec/basic_history_spec.rb +65 -35
- data/spec/internal/app/models/broom.rb +2 -0
- data/spec/internal/app/models/cat.rb +5 -0
- data/spec/internal/app/models/cat_life.rb +5 -0
- data/spec/internal/app/models/coven.rb +2 -0
- data/spec/internal/app/models/flying_machine.rb +2 -0
- data/spec/internal/app/models/person.rb +2 -0
- data/spec/internal/app/models/rocket_broom.rb +2 -0
- data/spec/internal/app/models/wart.rb +3 -1
- data/spec/internal/config/database.ci.yml +12 -0
- data/spec/internal/db/schema.rb +28 -5
- data/spec/spec_helper.rb +39 -5
- data/spec/support/database.rb +10 -6
- data/temporal_tables.gemspec +31 -18
- metadata +108 -39
- data/CHANGELOG.md +0 -73
- data/gemfiles/Gemfile.5.0.mysql +0 -16
- data/gemfiles/Gemfile.5.0.mysql.lock +0 -147
- data/gemfiles/Gemfile.5.0.pg +0 -16
- data/gemfiles/Gemfile.5.0.pg.lock +0 -147
- data/gemfiles/Gemfile.5.1.mysql.lock +0 -147
- data/gemfiles/Gemfile.5.1.pg +0 -16
- data/gemfiles/Gemfile.5.1.pg.lock +0 -147
- data/gemfiles/Gemfile.5.2.mysql +0 -16
- data/gemfiles/Gemfile.5.2.mysql.lock +0 -155
- data/gemfiles/Gemfile.5.2.pg.lock +0 -155
- data/lib/temporal_tables/join_extensions.rb +0 -20
- data/spec/extensions/combustion.rb +0 -9
- data/spec/internal/config/routes.rb +0 -3
data/lib/temporal_tables.rb
CHANGED
@@ -1,67 +1,82 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
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
|
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
|
-
|
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
|
-
|
26
|
-
|
34
|
+
subclass.send(
|
35
|
+
:prepend,
|
36
|
+
TemporalTables::ConnectionAdapters.const_get(module_name)
|
37
|
+
)
|
27
38
|
end
|
28
39
|
|
29
|
-
ActiveRecord::Base.
|
40
|
+
ActiveRecord::Base.include TemporalTables::Whodunnit
|
30
41
|
end
|
31
42
|
end
|
32
43
|
|
33
|
-
|
44
|
+
@create_by_default = false
|
34
45
|
def self.create_by_default
|
35
|
-
|
46
|
+
@create_by_default
|
36
47
|
end
|
48
|
+
|
37
49
|
def self.create_by_default=(default)
|
38
|
-
|
50
|
+
@create_by_default = default
|
39
51
|
end
|
40
52
|
|
41
|
-
|
53
|
+
@skipped_temporal_tables = [:schema_migrations, :sessions, :ar_internal_metadata]
|
42
54
|
def self.skip_temporal_table_for(*tables)
|
43
|
-
|
55
|
+
@skipped_temporal_tables += tables
|
44
56
|
end
|
57
|
+
|
45
58
|
def self.skipped_temporal_tables
|
46
|
-
|
59
|
+
@skipped_temporal_tables.dup
|
47
60
|
end
|
48
61
|
|
49
|
-
|
50
|
-
|
51
|
-
|
62
|
+
@add_updated_by_field = false
|
63
|
+
@updated_by_type = :string
|
64
|
+
@updated_by_proc = nil
|
52
65
|
def self.updated_by_type
|
53
|
-
|
66
|
+
@updated_by_type
|
54
67
|
end
|
68
|
+
|
55
69
|
def self.updated_by_proc
|
56
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
75
|
+
@add_updated_by_field = true
|
76
|
+
@updated_by_type = type
|
77
|
+
@updated_by_proc = block
|
63
78
|
end
|
64
79
|
|
65
|
-
|
80
|
+
@add_updated_by_field
|
66
81
|
end
|
67
82
|
end
|
data/spec/basic_history_spec.rb
CHANGED
@@ -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:
|
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
|
14
|
-
let!(:coven) { Coven.create name:
|
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:
|
20
|
+
emily.update name: 'Grunthilda', coven: coven
|
19
21
|
sleep 0.1
|
20
22
|
end
|
21
23
|
|
22
|
-
describe
|
23
|
-
it
|
24
|
-
expect(emily.name).to eq(
|
25
|
-
expect(historical_emily.name).to eq(
|
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
|
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
|
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
|
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
|
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
|
51
|
+
describe 'when reflecting on the past' do
|
50
52
|
let(:orig_emily) { emily.history.at(@init_time).last }
|
51
53
|
|
52
|
-
it
|
53
|
-
expect(orig_emily.name).to eq(
|
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
|
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
|
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
|
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 =
|
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
|
94
|
-
it
|
95
|
-
expect(emily.class.name).to eq(
|
96
|
-
expect(historical_emily.class.name).to eq(
|
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
|
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
|
111
|
-
it
|
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
|
127
|
-
let!(:broom) { Broom.create person: emily, model:
|
135
|
+
describe 'when working with STI one level deep' do
|
136
|
+
let!(:broom) { Broom.create person: emily, model: 'Cackler 2000' }
|
128
137
|
|
129
|
-
it
|
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
|
135
|
-
let!(:rocket_broom) { RocketBroom.create person: emily, model:
|
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
|
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
|
data/spec/internal/db/schema.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
8
|
-
|
12
|
+
def adapter_name
|
13
|
+
if Gemika::Env.gem?('mysql2')
|
14
|
+
'mysql'
|
15
|
+
else
|
16
|
+
'postgresql'
|
17
|
+
end
|
9
18
|
end
|
10
19
|
|
11
|
-
|
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
|
data/spec/support/database.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
data/temporal_tables.gemspec
CHANGED
@@ -1,24 +1,37 @@
|
|
1
|
-
#
|
2
|
-
|
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
|
8
|
-
gem.version
|
9
|
-
gem.authors
|
10
|
-
gem.email
|
11
|
-
gem.description
|
12
|
-
|
13
|
-
|
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
|
16
|
-
gem.executables
|
17
|
-
gem.test_files
|
18
|
-
gem.require_paths = [
|
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
|
21
|
-
gem.add_development_dependency
|
22
|
-
gem.add_development_dependency
|
23
|
-
gem.add_development_dependency
|
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
|