activerecord-rescue_from_duplicate 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ad1109d6b48c9d16624a06e7262f80571d261b5e
4
- data.tar.gz: 7fb9e9970bc575794559358c82be53d8667c5c92
2
+ SHA256:
3
+ metadata.gz: 57e38eb0a3e8bc47bc4c1e7e299c4ca6b30a4ffa14d9982db17d88086f3dc0ae
4
+ data.tar.gz: d963ba4a9a08bd3c6ba193bff5ca84f57074e3e0f68afd7a43ff6c02b02accb3
5
5
  SHA512:
6
- metadata.gz: 7c77fd19ff2d451ad3351c7c2058e76048df727dc5932648668687449960299fe9fa98ce44c1cf5ef0d36bc7955553810fc02240a3e43ccb31e5a87a37403d28
7
- data.tar.gz: 52cc5da74eb6c2993042645e679af11209011a86392c4e2863e33af4919dcc1ffb087d5bcdfcad31f0874534c780a80f83288aa34fb5bf66851898e406a211a4
6
+ metadata.gz: e703b89e8bcb6a4b722ca14fda626f837fe49cfca11cd1ced6023b2d54b0fde559e26e87e808ae5918ab9c180e27142ada04be181c1690122a16774906bee28f
7
+ data.tar.gz: 3c87f3a1f4f8c5e3233a2a603075bf62f9c0e49dcf6f9257c0e8b98de4bf16c5078b9c718537dd6aea0de704712381d833b7a3b1e544c87eed3a57103426524f
data/.travis.yml CHANGED
@@ -1,22 +1,16 @@
1
1
  sudo: false
2
2
  rvm:
3
- - 2.1
4
- - 2.2
5
- - 2.3.1
6
- gemfile:
7
- - gemfiles/Gemfile.ar-3.2
8
- - gemfiles/Gemfile.ar-4.0
9
- - gemfiles/Gemfile.ar-4.1
10
- - gemfiles/Gemfile.ar-4.2
11
- - gemfiles/Gemfile.ar-edge
12
-
13
- before_script:
14
- - mysql -e 'create database rescue_from_duplicate;'
15
- - psql -c 'create database rescue_from_duplicate;' -U postgres
3
+ - 2.5.0
16
4
 
17
5
  env:
18
- - POSTGRES=1 MYSQL=1
6
+ - DOCKER_COMPOSE_VERSION=1.20.1
7
+
8
+ before_script:
9
+ - docker-compose --version
10
+ - docker-compose pull
11
+ - docker-compose build
12
+ - docker-compose up --no-start
13
+ - docker-compose start
14
+ - sleep 30 # Wait for databases to come online
15
+ - docker ps
19
16
 
20
- matrix:
21
- allow_failures:
22
- - gemfile: gemfiles/Gemfile.ar-edge
data/README.md CHANGED
@@ -69,6 +69,21 @@ And then execute:
69
69
  Or install it yourself as:
70
70
 
71
71
  $ gem install activerecord-rescue_from_duplicate
72
+
73
+ ## Development Setup
74
+
75
+ Install:
76
+
77
+ - Have Docker installed
78
+ - Clone the repo
79
+ - `docker-compose up -d`
80
+
81
+ Run tests:
82
+
83
+ ```
84
+ rspec
85
+ ```
86
+
72
87
 
73
88
  ## Contributing
74
89
 
data/Rakefile CHANGED
@@ -5,13 +5,3 @@ RSpec::Core::RakeTask.new(:spec)
5
5
 
6
6
  task :default => :spec
7
7
 
8
- namespace :spec do
9
- task :all do
10
- Dir[File.expand_path('../gemfiles/*.lock', __FILE__)].each { |f| File.delete(f) }
11
- Dir[File.expand_path('../gemfiles/*', __FILE__)].each do |gemfile|
12
- env = {'BUNDLE_GEMFILE' => gemfile, 'MYSQL' => '1'}
13
- system(env, 'bundle install')
14
- system(env, 'bundle exec rspec')
15
- end
16
- end
17
- end
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
2
+ lib = File.expand_path("../lib", __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'rescue_from_duplicate/active_record/version'
4
+ require "rescue_from_duplicate/active_record/version"
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "activerecord-rescue_from_duplicate"
@@ -18,14 +18,14 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency 'activerecord', '>= 3.2'
21
+ spec.add_dependency "activerecord", ">= 5.2"
22
22
 
23
- spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "bundler"
24
24
  spec.add_development_dependency "rake"
25
- spec.add_development_dependency 'simplecov'
26
- spec.add_development_dependency 'sqlite3'
27
- spec.add_development_dependency 'mysql2'
28
- spec.add_development_dependency 'rspec'
29
- spec.add_development_dependency 'pg'
30
- spec.add_development_dependency 'pry'
25
+ spec.add_development_dependency "simplecov"
26
+ spec.add_development_dependency "sqlite3"
27
+ spec.add_development_dependency "mysql2"
28
+ spec.add_development_dependency "rspec"
29
+ spec.add_development_dependency "pg"
30
+ spec.add_development_dependency "pry"
31
31
  end
@@ -0,0 +1,18 @@
1
+ mysql:
2
+ image: mysql:5.7
3
+ environment:
4
+ MYSQL_USER: "test"
5
+ MYSQL_PASSWORD: "test"
6
+ MYSQL_ALLOW_EMPTY_PASSWORD: "true"
7
+ MYSQL_DATABASE: "rescue_from_duplicate"
8
+ ports:
9
+ - "29291:3306"
10
+
11
+ postgres:
12
+ image: postgres:10.3
13
+ environment:
14
+ POSTGRES_USER: "test"
15
+ POSTGRES_PASSWORD: "test"
16
+ POSTGRES_DB: "rescue_from_duplicate"
17
+ ports:
18
+ - "29292:5432"
@@ -45,7 +45,7 @@ module RescueFromDuplicate::ActiveRecord
45
45
  def exception_handler(exception)
46
46
  columns = exception_columns(exception)
47
47
  return unless columns
48
- columns.sort!
48
+ columns = columns.sort
49
49
 
50
50
  self.class._rescue_from_duplicate_handlers.detect do |handler|
51
51
  handler.rescue? && columns == handler.columns
@@ -1,5 +1,5 @@
1
1
  module Activerecord
2
2
  module RescueFromDuplicate
3
- VERSION = '0.1.2'
3
+ VERSION = '0.1.3'
4
4
  end
5
5
  end
@@ -2,16 +2,16 @@ require 'spec_helper'
2
2
  include RescueFromDuplicate
3
3
 
4
4
  shared_examples 'database error rescuing' do
5
- let(:uniqueness_exception) { ::ActiveRecord::RecordNotUnique.new(message, nil) }
5
+ let(:uniqueness_exception) { ::ActiveRecord::RecordNotUnique.new(message) }
6
6
 
7
7
  subject { Rescuable.new }
8
8
 
9
9
  before do
10
- allow(Rescuable).to receive(:connection).and_return(double(indexes: [Rescuable.index]))
10
+ allow(Rescuable).to(receive(:connection).and_return(double(indexes: [Rescuable.index])))
11
11
  end
12
12
 
13
13
  describe "#create_or_update when the validation fails" do
14
- before { Base.stub(exception: uniqueness_exception) }
14
+ before { allow(Base).to(receive(:exception).and_return(uniqueness_exception)) }
15
15
 
16
16
  context "when the validator is present" do
17
17
  it "adds an error to the model" do
@@ -21,19 +21,19 @@ shared_examples 'database error rescuing' do
21
21
  end
22
22
 
23
23
  context "when the validator is not present" do
24
- before { Rescuable.stub(validators: [Rescuable.presence_validator]) }
24
+ before { allow(Rescuable).to(receive(:validators).and_return([Rescuable.presence_validator])) }
25
25
 
26
26
  it "raises an exception" do
27
- expect{ subject.create_or_update }.to raise_error(ActiveRecord::RecordNotUnique)
27
+ expect { subject.create_or_update }.to raise_error(ActiveRecord::RecordNotUnique)
28
28
  end
29
29
  end
30
30
  end
31
31
 
32
32
  describe "#create_or_update when using rescuer without validation" do
33
33
  before {
34
- Rescuable.stub(_validators: {})
35
- Rescuable.stub(_rescue_from_duplicates: [Rescuable.uniqueness_rescuer])
36
- Base.stub(exception: uniqueness_exception)
34
+ allow(Rescuable).to(receive(:_validators).and_return({}))
35
+ allow(Rescuable).to(receive(:_rescue_from_duplicates).and_return([Rescuable.uniqueness_rescuer]))
36
+ allow(Base).to(receive(:exception).and_return(uniqueness_exception))
37
37
  }
38
38
 
39
39
  it "adds an error to the model" do
@@ -44,18 +44,14 @@ shared_examples 'database error rescuing' do
44
44
  end
45
45
 
46
46
  describe RescueFromDuplicate::ActiveRecord do
47
- if defined?(MysqlModel)
48
- context 'mysql' do
49
- let(:message) { "Duplicate entry '1-Rescuable-toto' for key 'index_rescuable_on_shop_id_and_type_and_name'" }
50
- it_behaves_like 'database error rescuing'
51
- end
47
+ context 'mysql' do
48
+ let(:message) { "Duplicate entry '1-Rescuable-toto' for key 'index_rescuable_on_shop_id_and_type_and_name'" }
49
+ it_behaves_like 'database error rescuing'
52
50
  end
53
51
 
54
- if defined?(PostgresqlModel)
55
- context 'pgsql' do
56
- let(:message) { "PG::UniqueViolation: ERROR: duplicate key value violates unique constraint \"index_rescuable_on_shop_id_and_type_and_name\"\nDETAIL: Key (shop_id, type, name)=(1, Rescuable, toto) already exists.\n: INSERT INTO \"postgresql_models\" (\"shop_id\", \"type\", \"name\") VALUES ($1, $2, $3) RETURNING \"id\"" }
57
- it_behaves_like 'database error rescuing'
58
- end
52
+ context 'pgsql' do
53
+ let(:message) { "PG::UniqueViolation: ERROR: duplicate key value violates unique constraint \"index_rescuable_on_shop_id_and_type_and_name\"\nDETAIL: Key (shop_id, type, name)=(1, Rescuable, toto) already exists.\n: INSERT INTO \"postgresql_models\" (\"shop_id\", \"type\", \"name\") VALUES ($1, $2, $3) RETURNING \"id\"" }
54
+ it_behaves_like 'database error rescuing'
59
55
  end
60
56
 
61
57
  context 'sqlite3' do
data/spec/rescuer_spec.rb CHANGED
@@ -35,14 +35,10 @@ describe Sqlite3Model do
35
35
  it_behaves_like 'a model with rescued unique error without validator'
36
36
  end
37
37
 
38
- if defined?(MysqlModel)
39
- describe MysqlModel do
40
- it_behaves_like 'a model with rescued unique error without validator'
41
- end
38
+ describe MysqlModel do
39
+ it_behaves_like 'a model with rescued unique error without validator'
42
40
  end
43
41
 
44
- if defined?(PostgresqlModel)
45
- describe PostgresqlModel do
46
- it_behaves_like 'a model with rescued unique error without validator'
47
- end
42
+ describe PostgresqlModel do
43
+ it_behaves_like 'a model with rescued unique error without validator'
48
44
  end
data/spec/spec_helper.rb CHANGED
@@ -83,8 +83,6 @@ module RescueFromDuplicate
83
83
  "index_rescuable_on_shop_id_and_type_and_name",
84
84
  true,
85
85
  ["shop_id", "type", "name"],
86
- [nil, nil, nil],
87
- nil
88
86
  )
89
87
  end
90
88
  end
@@ -1,18 +1,30 @@
1
- require 'active_record'
2
- require 'json'
3
- require 'yaml'
4
-
5
- AR_VERSION = Gem::Version.new(ActiveRecord::VERSION::STRING)
6
- AR_4_0 = Gem::Version.new('4.0')
7
- AR_4_1 = Gem::Version.new('4.1.0.beta')
8
-
9
- ActiveRecord::Base.configurations = {
10
- 'test_sqlite3' => {adapter: 'sqlite3', database: "/tmp/rescue_from_duplicate.db"},
11
- 'test_postgresql' => {adapter: 'postgresql', database: 'rescue_from_duplicate', username: 'postgres'},
12
- 'test_mysql' => {adapter: 'mysql2', database: 'rescue_from_duplicate', username: 'travis'},
1
+ require "active_record"
2
+ require "json"
3
+ require "yaml"
4
+
5
+ CONNECTIONS = {
6
+ test_sqlite3: {
7
+ adapter: "sqlite3",
8
+ database: "/tmp/rescue_from_duplicate.db",
9
+ },
10
+ test_postgresql: {
11
+ adapter: "postgresql",
12
+ database: "rescue_from_duplicate",
13
+ username: "test",
14
+ password: "test",
15
+ host: "127.0.0.1",
16
+ port: "29292",
17
+ },
18
+ test_mysql: {
19
+ adapter: "mysql2",
20
+ database: "rescue_from_duplicate",
21
+ username: "test",
22
+ password: "test",
23
+ host: "127.0.0.1",
24
+ port: "29291",
25
+ },
13
26
  }
14
-
15
- class CreateAllTables < ActiveRecord::Migration
27
+ class CreateAllTables < ActiveRecord::Migration[5.2]
16
28
  def self.recreate_table(name, *args, &block)
17
29
  execute "drop table if exists #{name}"
18
30
 
@@ -30,17 +42,13 @@ class CreateAllTables < ActiveRecord::Migration
30
42
  end
31
43
 
32
44
  def self.up
33
- if ENV['MYSQL']
34
- ActiveRecord::Base.establish_connection('test_mysql')
35
- recreate_table(:mysql_models)
36
- end
45
+ ActiveRecord::Base.establish_connection(CONNECTIONS.fetch(:test_mysql))
46
+ recreate_table(:mysql_models)
37
47
 
38
- if ENV['POSTGRES']
39
- ActiveRecord::Base.establish_connection(ENV['POSTGRES_URL'] || 'test_postgresql')
40
- recreate_table(:postgresql_models)
41
- end
48
+ ActiveRecord::Base.establish_connection(CONNECTIONS.fetch(:test_postgresql))
49
+ recreate_table(:postgresql_models)
42
50
 
43
- ActiveRecord::Base.establish_connection('test_sqlite3')
51
+ ActiveRecord::Base.establish_connection(CONNECTIONS.fetch(:test_sqlite3))
44
52
  recreate_table(:sqlite3_models)
45
53
  end
46
54
  end
@@ -50,43 +58,37 @@ CreateAllTables.up
50
58
 
51
59
 
52
60
  module TestModel
53
- extend ActiveSupport::Concern
54
-
55
- included do
56
- rescue_from_duplicate :handle, scope: :relation_id, message: "handle must be unique for this relation"
61
+ def self.included(base)
62
+ base.rescue_from_duplicate(:handle, scope: :relation_id, message: "handle must be unique for this relation")
57
63
 
58
- validates_uniqueness_of :name, rescue_from_duplicate: true, allow_nil: true
59
- validates_uniqueness_of :size, allow_nil: true
64
+ base.validates(:name, uniqueness: { rescue_from_duplicate: true }, allow_nil: true)
65
+ base.validates(:size, uniqueness: { rescue_from_duplicate: true }, allow_nil: true)
60
66
  end
61
67
  end
62
68
 
63
- if ENV['MYSQL']
64
- class MysqlModel < ActiveRecord::Base
65
- include TestModel
69
+ class MysqlModel < ActiveRecord::Base
70
+ include TestModel
66
71
 
67
- establish_connection 'test_mysql'
68
- end
72
+ establish_connection(CONNECTIONS.fetch(:test_mysql))
69
73
  end
70
74
 
71
- if ENV['POSTGRES']
72
- class PostgresqlModel < ActiveRecord::Base
73
- include TestModel
75
+ class PostgresqlModel < ActiveRecord::Base
76
+ include TestModel
74
77
 
75
- establish_connection ENV['POSTGRES_URL'] || 'test_postgresql'
76
- end
78
+ establish_connection(CONNECTIONS.fetch(:test_postgresql))
77
79
  end
78
80
 
79
81
  class Sqlite3Model < ActiveRecord::Base
80
82
  include TestModel
81
83
 
82
- establish_connection 'test_sqlite3'
84
+ establish_connection(CONNECTIONS.fetch(:test_sqlite3))
83
85
  end
84
86
 
85
87
  Models = [
86
- Sqlite3Model
88
+ Sqlite3Model,
89
+ MysqlModel,
90
+ PostgresqlModel,
87
91
  ]
88
- Models << MysqlModel if defined?(MysqlModel)
89
- Models << PostgresqlModel if defined?(PostgresqlModel)
90
92
 
91
93
 
92
94
  RSpec.configure do |config|
@@ -5,7 +5,8 @@ shared_examples 'a model with rescued uniqueness validator' do
5
5
  context 'when catching a race condition' do
6
6
 
7
7
  before(:each) {
8
- ActiveRecord::Validations::UniquenessValidator.any_instance.stub(validate_each: nil)
8
+ allow_any_instance_of(ActiveRecord::Validations::UniquenessValidator)
9
+ .to(receive(:validate_each)).and_return(nil)
9
10
  described_class.create!(name: 'toto', size: 5)
10
11
  }
11
12
 
@@ -55,7 +56,7 @@ shared_examples 'missing index finding' do
55
56
 
56
57
  context 'indexes are missing' do
57
58
  before {
58
- described_class.stub(_rescue_from_duplicate_handlers: [
59
+ allow(described_class).to(receive(:_rescue_from_duplicate_handlers).and_return([
59
60
  RescueFromDuplicate::UniquenessRescuer.new(
60
61
  ::ActiveRecord::Validations::UniquenessValidator.new(
61
62
  attributes: [:name],
@@ -63,7 +64,7 @@ shared_examples 'missing index finding' do
63
64
  )
64
65
  ),
65
66
  RescueFromDuplicate::Rescuer.new(:name, scope: [:hello])
66
- ])
67
+ ]))
67
68
  }
68
69
 
69
70
  it 'returns the missing indexes' do
@@ -88,16 +89,12 @@ describe Sqlite3Model do
88
89
  it_behaves_like 'missing index finding'
89
90
  end
90
91
 
91
- if defined?(MysqlModel)
92
- describe MysqlModel do
93
- it_behaves_like 'a model with rescued uniqueness validator'
94
- it_behaves_like 'missing index finding'
95
- end
92
+ describe MysqlModel do
93
+ it_behaves_like 'a model with rescued uniqueness validator'
94
+ it_behaves_like 'missing index finding'
96
95
  end
97
96
 
98
- if defined?(PostgresqlModel)
99
- describe PostgresqlModel do
100
- it_behaves_like 'a model with rescued uniqueness validator'
101
- it_behaves_like 'missing index finding'
102
- end
97
+ describe PostgresqlModel do
98
+ it_behaves_like 'a model with rescued uniqueness validator'
99
+ it_behaves_like 'missing index finding'
103
100
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-rescue_from_duplicate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guillaume Malette
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-22 00:00:00.000000000 Z
11
+ date: 2019-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '3.2'
19
+ version: '5.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '3.2'
26
+ version: '5.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '1.3'
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '1.3'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -151,11 +151,7 @@ files:
151
151
  - README.md
152
152
  - Rakefile
153
153
  - activerecord-rescue_from_duplicate.gemspec
154
- - gemfiles/Gemfile.ar-3.2
155
- - gemfiles/Gemfile.ar-4.0
156
- - gemfiles/Gemfile.ar-4.1
157
- - gemfiles/Gemfile.ar-4.2
158
- - gemfiles/Gemfile.ar-edge
154
+ - docker-compose.yml
159
155
  - lib/activerecord-rescue_from_duplicate.rb
160
156
  - lib/rescue_from_duplicate/active_record.rb
161
157
  - lib/rescue_from_duplicate/active_record/extension.rb
@@ -190,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
190
186
  version: '0'
191
187
  requirements: []
192
188
  rubyforge_project:
193
- rubygems_version: 2.5.1
189
+ rubygems_version: 2.7.6
194
190
  signing_key:
195
191
  specification_version: 4
196
192
  summary: Rescue from MySQL and Sqlite duplicate errors when trying to insert records
@@ -1,10 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'activerecord', '~> 3.2.15'
4
- gem 'bundler', '~> 1.3'
5
- gem 'rake'
6
- gem 'rspec'
7
- gem 'sqlite3'
8
- gem 'pg', '~> 0.11'
9
- gem 'mysql2', '~> 0.3.20'
10
- gem 'simplecov', require: false
@@ -1,10 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'activerecord', '~> 4.0.0'
4
- gem 'bundler', '~> 1.3'
5
- gem 'rake'
6
- gem 'rspec'
7
- gem 'sqlite3'
8
- gem 'pg', '~> 0.11'
9
- gem 'mysql2', '~> 0.3.20'
10
- gem 'simplecov', require: false
@@ -1,10 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'activerecord', '~> 4.1.0'
4
- gem 'bundler', '~> 1.3'
5
- gem 'rake'
6
- gem 'rspec'
7
- gem 'sqlite3'
8
- gem 'pg', '~> 0.11'
9
- gem 'mysql2', '~> 0.3.20'
10
- gem 'simplecov', require: false
@@ -1,10 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'activerecord', '~> 4.2.0'
4
- gem 'bundler', '~> 1.3'
5
- gem 'rake'
6
- gem 'rspec'
7
- gem 'sqlite3'
8
- gem 'pg', '~> 0.11'
9
- gem 'mysql2', '~> 0.3.20'
10
- gem 'simplecov', require: false
@@ -1,12 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'rails', github: 'rails/rails'
4
- gem 'arel', github: 'rails/arel'
5
-
6
- gem 'bundler', '~> 1.3'
7
- gem 'rake'
8
- gem 'rspec'
9
- gem 'sqlite3'
10
- gem 'pg', '~> 0.11'
11
- gem 'mysql2', '~> 0.3.20'
12
- gem 'simplecov', require: false