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 +5 -5
- data/.travis.yml +11 -17
- data/README.md +15 -0
- data/Rakefile +0 -10
- data/activerecord-rescue_from_duplicate.gemspec +10 -10
- data/docker-compose.yml +18 -0
- data/lib/rescue_from_duplicate/active_record/extension.rb +1 -1
- data/lib/rescue_from_duplicate/active_record/version.rb +1 -1
- data/spec/rescue_from_duplicate_spec.rb +14 -18
- data/spec/rescuer_spec.rb +4 -8
- data/spec/spec_helper.rb +0 -2
- data/spec/support/model.rb +45 -43
- data/spec/validator_spec.rb +10 -13
- metadata +10 -14
- data/gemfiles/Gemfile.ar-3.2 +0 -10
- data/gemfiles/Gemfile.ar-4.0 +0 -10
- data/gemfiles/Gemfile.ar-4.1 +0 -10
- data/gemfiles/Gemfile.ar-4.2 +0 -10
- data/gemfiles/Gemfile.ar-edge +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 57e38eb0a3e8bc47bc4c1e7e299c4ca6b30a4ffa14d9982db17d88086f3dc0ae
|
4
|
+
data.tar.gz: d963ba4a9a08bd3c6ba193bff5ca84f57074e3e0f68afd7a43ff6c02b02accb3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
-
-
|
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(
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
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
|
21
|
+
spec.add_dependency "activerecord", ">= 5.2"
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler"
|
23
|
+
spec.add_development_dependency "bundler"
|
24
24
|
spec.add_development_dependency "rake"
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
29
|
-
spec.add_development_dependency
|
30
|
-
spec.add_development_dependency
|
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
|
data/docker-compose.yml
ADDED
@@ -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
|
@@ -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
|
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
|
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.
|
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.
|
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.
|
35
|
-
Rescuable.
|
36
|
-
Base.
|
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
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
39
|
-
|
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
|
-
|
45
|
-
|
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
data/spec/support/model.rb
CHANGED
@@ -1,18 +1,30 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
34
|
-
|
35
|
-
recreate_table(:mysql_models)
|
36
|
-
end
|
45
|
+
ActiveRecord::Base.establish_connection(CONNECTIONS.fetch(:test_mysql))
|
46
|
+
recreate_table(:mysql_models)
|
37
47
|
|
38
|
-
|
39
|
-
|
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(
|
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
|
-
|
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
|
-
|
59
|
-
|
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
|
-
|
64
|
-
|
65
|
-
include TestModel
|
69
|
+
class MysqlModel < ActiveRecord::Base
|
70
|
+
include TestModel
|
66
71
|
|
67
|
-
|
68
|
-
end
|
72
|
+
establish_connection(CONNECTIONS.fetch(:test_mysql))
|
69
73
|
end
|
70
74
|
|
71
|
-
|
72
|
-
|
73
|
-
include TestModel
|
75
|
+
class PostgresqlModel < ActiveRecord::Base
|
76
|
+
include TestModel
|
74
77
|
|
75
|
-
|
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
|
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|
|
data/spec/validator_spec.rb
CHANGED
@@ -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
|
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.
|
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
|
-
|
92
|
-
|
93
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
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.
|
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:
|
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: '
|
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: '
|
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: '
|
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: '
|
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
|
-
-
|
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.
|
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
|
data/gemfiles/Gemfile.ar-3.2
DELETED
data/gemfiles/Gemfile.ar-4.0
DELETED
data/gemfiles/Gemfile.ar-4.1
DELETED
data/gemfiles/Gemfile.ar-4.2
DELETED
data/gemfiles/Gemfile.ar-edge
DELETED
@@ -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
|