wordmove 5.2.2 → 6.0.0.alpha.1

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +12 -5
  3. data/.rubocop.yml +2 -5
  4. data/.ruby-version +1 -1
  5. data/.yardopts +1 -0
  6. data/CONTRIBUTING.md +15 -0
  7. data/Rakefile +4 -3
  8. data/exe/wordmove +2 -1
  9. data/lib/wordmove/actions/adapt_local_db.rb +95 -0
  10. data/lib/wordmove/actions/adapt_remote_db.rb +87 -0
  11. data/lib/wordmove/actions/backup_local_db.rb +54 -0
  12. data/lib/wordmove/actions/delete_local_file.rb +34 -0
  13. data/lib/wordmove/actions/delete_remote_file.rb +43 -0
  14. data/lib/wordmove/actions/filter_and_setup_tasks_to_run.rb +42 -0
  15. data/lib/wordmove/actions/ftp/backup_remote_db.rb +54 -0
  16. data/lib/wordmove/actions/ftp/cleanup_after_adapt.rb +70 -0
  17. data/lib/wordmove/actions/ftp/download_remote_db.rb +69 -0
  18. data/lib/wordmove/actions/ftp/get_directory.rb +67 -0
  19. data/lib/wordmove/actions/ftp/helpers.rb +91 -0
  20. data/lib/wordmove/actions/ftp/pull_wordpress.rb +56 -0
  21. data/lib/wordmove/actions/ftp/push_wordpress.rb +54 -0
  22. data/lib/wordmove/actions/ftp/put_and_import_dump_remotely.rb +74 -0
  23. data/lib/wordmove/actions/ftp/put_directory.rb +67 -0
  24. data/lib/wordmove/actions/get_file.rb +38 -0
  25. data/lib/wordmove/actions/helpers.rb +142 -0
  26. data/lib/wordmove/actions/put_file.rb +48 -0
  27. data/lib/wordmove/actions/run_after_pull_hook.rb +26 -0
  28. data/lib/wordmove/actions/run_after_push_hook.rb +26 -0
  29. data/lib/wordmove/actions/run_before_pull_hook.rb +26 -0
  30. data/lib/wordmove/actions/run_before_push_hook.rb +26 -0
  31. data/lib/wordmove/actions/run_local_command.rb +34 -0
  32. data/lib/wordmove/actions/setup_context_for_db.rb +79 -0
  33. data/lib/wordmove/actions/ssh/backup_remote_db.rb +49 -0
  34. data/lib/wordmove/actions/ssh/cleanup_after_adapt.rb +42 -0
  35. data/lib/wordmove/actions/ssh/download_remote_db.rb +76 -0
  36. data/lib/wordmove/actions/ssh/get_directory.rb +76 -0
  37. data/lib/wordmove/actions/ssh/helpers.rb +128 -0
  38. data/lib/wordmove/actions/ssh/pull_wordpress.rb +56 -0
  39. data/lib/wordmove/actions/ssh/push_wordpress.rb +54 -0
  40. data/lib/wordmove/actions/ssh/put_and_import_dump_remotely.rb +70 -0
  41. data/lib/wordmove/actions/ssh/put_directory.rb +76 -0
  42. data/lib/wordmove/actions/ssh/run_remote_command.rb +39 -0
  43. data/lib/wordmove/assets/dump.php.erb +6 -6
  44. data/lib/wordmove/assets/import.php.erb +7 -7
  45. data/lib/wordmove/assets/wordmove_schema_global.yml +2 -0
  46. data/lib/wordmove/cli.rb +152 -91
  47. data/lib/wordmove/db_paths_config.rb +44 -0
  48. data/lib/wordmove/doctor/movefile.rb +8 -8
  49. data/lib/wordmove/doctor/mysql.rb +18 -15
  50. data/lib/wordmove/doctor/rsync.rb +2 -2
  51. data/lib/wordmove/doctor/ssh.rb +3 -3
  52. data/lib/wordmove/doctor/wpcli.rb +4 -4
  53. data/lib/wordmove/environments_list.rb +4 -4
  54. data/lib/wordmove/exceptions.rb +13 -0
  55. data/lib/wordmove/generators/movefile.rb +7 -5
  56. data/lib/wordmove/generators/movefile_adapter.rb +11 -5
  57. data/lib/wordmove/guardian.rb +5 -5
  58. data/lib/wordmove/hook.rb +13 -14
  59. data/lib/wordmove/logger.rb +11 -10
  60. data/lib/wordmove/movefile.rb +63 -59
  61. data/lib/wordmove/organizers/ftp/pull.rb +52 -0
  62. data/lib/wordmove/organizers/ftp/push.rb +53 -0
  63. data/lib/wordmove/organizers/ssh/pull.rb +52 -0
  64. data/lib/wordmove/organizers/ssh/push.rb +53 -0
  65. data/lib/wordmove/version.rb +1 -1
  66. data/lib/wordmove/wordpress_directory.rb +76 -8
  67. data/lib/wordmove/wpcli.rb +85 -0
  68. data/lib/wordmove.rb +33 -11
  69. data/wordmove.gemspec +37 -30
  70. metadata +139 -35
  71. data/lib/wordmove/deployer/base.rb +0 -193
  72. data/lib/wordmove/deployer/ftp.rb +0 -160
  73. data/lib/wordmove/deployer/ssh/default_sql_adapter.rb +0 -47
  74. data/lib/wordmove/deployer/ssh/wpcli_sql_adapter.rb +0 -55
  75. data/lib/wordmove/deployer/ssh.rb +0 -169
  76. data/lib/wordmove/sql_adapter/default.rb +0 -68
  77. data/lib/wordmove/sql_adapter/wpcli.rb +0 -54
  78. data/lib/wordmove/wordpress_directory/path.rb +0 -11
@@ -0,0 +1,52 @@
1
+ module Wordmove
2
+ module Organizers
3
+ module Ssh
4
+ class Pull
5
+ extend ::LightService::Organizer
6
+ include Wordmove::Actions::Helpers
7
+ include Wordmove::Actions::Ssh::Helpers
8
+
9
+ def self.call(cli_options:, movefile:)
10
+ logger = Logger.new($stdout, movefile.secrets).tap { |l| l.level = Logger::DEBUG }
11
+ remote_options = movefile.options[movefile.environment]
12
+ ssh_opts = ssh_options(remote_options: remote_options, simulate: cli_options[:simulate])
13
+
14
+ LightService::Configuration.logger = ::Logger.new($stdout) if cli_options[:debug]
15
+
16
+ with(
17
+ cli_options: cli_options,
18
+ global_options: movefile.options[:global],
19
+ local_options: movefile.options[:local],
20
+ remote_options: remote_options,
21
+ movefile: movefile,
22
+ guardian: Wordmove::Guardian.new(cli_options: cli_options, action: :pull),
23
+ logger: logger,
24
+ photocopier: Photocopier::SSH
25
+ .new(ssh_opts)
26
+ .tap { |c| c.logger = logger }
27
+ ).reduce(actions)
28
+ end
29
+
30
+ def self.actions
31
+ [
32
+ Wordmove::Actions::RunBeforePullHook,
33
+ Wordmove::Actions::FilterAndSetupTasksToRun,
34
+ reduce_if(
35
+ ->(ctx) { ctx.wordpress_task },
36
+ [Wordmove::Actions::Ssh::PullWordpress]
37
+ ),
38
+ iterate(:folder_tasks, [Wordmove::Actions::Ssh::GetDirectory])
39
+ ].concat [
40
+ Wordmove::Actions::SetupContextForDb,
41
+ Wordmove::Actions::BackupLocalDb,
42
+ Wordmove::Actions::Ssh::DownloadRemoteDb,
43
+ Wordmove::Actions::AdaptRemoteDb,
44
+ Wordmove::Actions::Ssh::CleanupAfterAdapt
45
+ ].concat [
46
+ Wordmove::Actions::RunAfterPullHook
47
+ ]
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,53 @@
1
+ module Wordmove
2
+ module Organizers
3
+ module Ssh
4
+ class Push
5
+ extend ::LightService::Organizer
6
+ include Wordmove::Actions::Helpers
7
+ include Wordmove::Actions::Ssh::Helpers
8
+
9
+ def self.call(cli_options:, movefile:)
10
+ logger = Logger.new($stdout, movefile.secrets).tap { |l| l.level = Logger::DEBUG }
11
+ remote_options = movefile.options[movefile.environment]
12
+ ssh_opts = ssh_options(remote_options: remote_options, simulate: cli_options[:simulate])
13
+
14
+ LightService::Configuration.logger = ::Logger.new($stdout) if cli_options[:debug]
15
+
16
+ with(
17
+ cli_options: cli_options,
18
+ global_options: movefile.options[:global],
19
+ local_options: movefile.options[:local],
20
+ remote_options: remote_options,
21
+ movefile: movefile,
22
+ guardian: Wordmove::Guardian.new(cli_options: cli_options, action: :push),
23
+ logger: logger,
24
+ photocopier: Photocopier::SSH
25
+ .new(ssh_opts)
26
+ .tap { |c| c.logger = logger }
27
+ ).reduce(actions)
28
+ end
29
+
30
+ def self.actions
31
+ [
32
+ Wordmove::Actions::RunBeforePushHook,
33
+ Wordmove::Actions::FilterAndSetupTasksToRun,
34
+ reduce_if(
35
+ ->(ctx) { ctx.wordpress_task },
36
+ [Wordmove::Actions::Ssh::PushWordpress]
37
+ ),
38
+ iterate(:folder_tasks, [Wordmove::Actions::Ssh::PutDirectory])
39
+ ].concat [
40
+ Wordmove::Actions::SetupContextForDb,
41
+ Wordmove::Actions::Ssh::DownloadRemoteDb,
42
+ Wordmove::Actions::Ssh::BackupRemoteDb,
43
+ Wordmove::Actions::AdaptLocalDb,
44
+ Wordmove::Actions::Ssh::PutAndImportDumpRemotely,
45
+ Wordmove::Actions::Ssh::CleanupAfterAdapt
46
+ ].concat [
47
+ Wordmove::Actions::RunAfterPushHook
48
+ ]
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,3 +1,3 @@
1
1
  module Wordmove
2
- VERSION = "5.2.2".freeze
2
+ VERSION = '6.0.0.alpha.1'.freeze
3
3
  end
@@ -1,13 +1,21 @@
1
- require 'wordmove/wordpress_directory/path'
2
-
3
1
  class WordpressDirectory
4
- attr_accessor :type, :options
2
+ attr_reader :folder, :options
5
3
 
6
- def initialize(type, options)
7
- @type = type
4
+ def initialize(folder, options)
5
+ @folder = folder
8
6
  @options = options
9
7
  end
10
8
 
9
+ module Path
10
+ WP_CONTENT = :wp_content
11
+ WP_CONFIG = :wp_config
12
+ PLUGINS = :plugins
13
+ MU_PLUGINS = :mu_plugins
14
+ THEMES = :themes
15
+ UPLOADS = :uploads
16
+ LANGUAGES = :languages
17
+ end
18
+
11
19
  DEFAULT_PATHS = {
12
20
  Path::WP_CONTENT => 'wp-content',
13
21
  Path::WP_CONFIG => 'wp-config.php',
@@ -31,11 +39,71 @@ class WordpressDirectory
31
39
  end
32
40
 
33
41
  def relative_path(*args)
34
- path = if options[:paths] && options[:paths][type]
35
- options[:paths][type]
42
+ path = if options[:paths] && options[:paths][folder]
43
+ options[:paths][folder]
36
44
  else
37
- DEFAULT_PATHS[type]
45
+ DEFAULT_PATHS[folder]
38
46
  end
39
47
  File.join(path, *args)
40
48
  end
49
+
50
+ module RemoteHelperMethods
51
+ extend ActiveSupport::Concern
52
+
53
+ class_methods do
54
+ def remote_wp_content_dir(remote_options:)
55
+ WordpressDirectory.new(:wp_content, remote_options)
56
+ end
57
+
58
+ def remote_plugins_dir(remote_options:)
59
+ WordpressDirectory.new(:plugins, remote_options)
60
+ end
61
+
62
+ def remote_mu_plugins_dir(remote_options:)
63
+ WordpressDirectory.new(:mu_plugins, remote_options)
64
+ end
65
+
66
+ def remote_themes_dir(remote_options:)
67
+ WordpressDirectory.new(:themes, remote_options)
68
+ end
69
+
70
+ def remote_uploads_dir(remote_options:)
71
+ WordpressDirectory.new(:uploads, remote_options)
72
+ end
73
+
74
+ def remote_languages_dir(remote_options:)
75
+ WordpressDirectory.new(:languages, remote_options)
76
+ end
77
+ end
78
+ end
79
+
80
+ module LocalHelperMethods
81
+ extend ActiveSupport::Concern
82
+
83
+ class_methods do
84
+ def local_wp_content_dir(local_options:)
85
+ WordpressDirectory.new(:wp_content, local_options)
86
+ end
87
+
88
+ def local_plugins_dir(local_options:)
89
+ WordpressDirectory.new(:plugins, local_options)
90
+ end
91
+
92
+ def local_mu_plugins_dir(local_options:)
93
+ WordpressDirectory.new(:mu_plugins, local_options)
94
+ end
95
+
96
+ def local_themes_dir(local_options:)
97
+ WordpressDirectory.new(:themes, local_options)
98
+ end
99
+
100
+ def local_uploads_dir(local_options:)
101
+ WordpressDirectory.new(:uploads, local_options)
102
+ end
103
+
104
+ def local_languages_dir(local_options:)
105
+ WordpressDirectory.new(:languages, local_options)
106
+ end
107
+ end
108
+ end
41
109
  end
@@ -0,0 +1,85 @@
1
+ module Wordmove
2
+ # This class is a sort of mini-wrapper around the wp-cli executable.
3
+ # It's responsible to run or produce wp-cli commands.
4
+ module Wpcli
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ private_class_method :load_from_wpcli, :load_from_yml
9
+ end
10
+
11
+ class_methods do # rubocop:disable Metrics/BlockLength
12
+ # Checks if `wp` command is in your shell `$PATH`
13
+ #
14
+ # @return [Boolean]
15
+ # @!scope class
16
+ def wp_in_path?
17
+ system('which wp > /dev/null 2>&1')
18
+ end
19
+
20
+ # Compose and returns the search-replace command. It's intended to be
21
+ # used from a +LightService::Action+
22
+ #
23
+ # @param context [LightService::Context] The context of an action
24
+ # @param config_key [:vhost, :wordpress_path] Determines what will be replaced in DB
25
+ # @return [String]
26
+ # @!scope class
27
+ def wpcli_search_replace_command(context, config_key)
28
+ unless %i[vhost wordpress_path].include?(config_key)
29
+ raise ArgumentError, "Unexpected `config_key` #{config_key}.:vhost" \
30
+ 'or :wordpress_path expected'
31
+ end
32
+
33
+ [
34
+ 'wp search-replace',
35
+ "--path=#{wpcli_config_path(context)}",
36
+ context.remote_options[config_key],
37
+ context.local_options[config_key],
38
+ '--quiet',
39
+ '--skip-columns=guid',
40
+ '--all-tables',
41
+ '--allow-root'
42
+ ].join(' ')
43
+ end
44
+
45
+ # Returns the wordpress path from wp-cli (with precedence) or from movefile
46
+ #
47
+ # It's intended to be used from a +LightService::Action+
48
+ #
49
+ # @param context [LightService::Context] The context of an action
50
+ # @return [String]
51
+ # @!scope class
52
+ def wpcli_config_path(context)
53
+ load_from_yml(context) || load_from_wpcli || context.local_options[:wordpress_path]
54
+ end
55
+
56
+ # If wordpress installation brings a `wp-cli.yml` file in its root folder,
57
+ # reads it and returns the `path` yaml key configured there
58
+ #
59
+ # @return [String, nil] The `path` configuration or `nil`
60
+ # @!scope class
61
+ # @!visibility private
62
+ def load_from_yml(context)
63
+ yml_path = File.join(context.local_options[:wordpress_path], 'wp-cli.yml')
64
+
65
+ return unless File.exist?(yml_path)
66
+
67
+ YAML.load_file(yml_path).with_indifferent_access['path']
68
+ end
69
+
70
+ # Returns the wordpress path as per wp-cli configuration.
71
+ # A possible scenario is that the used wpcli command could return an empty
72
+ # string: we thus rescue parse errors in order to ignore this config source
73
+ #
74
+ # @return [String, nil] The wordpress path as per wp-cli configuration or nil
75
+ # @!scope class
76
+ # @!visibility private
77
+ def load_from_wpcli
78
+ wpcli_config = JSON.parse(`wp cli param-dump --with-values`, symbolize_names: true)
79
+ wpcli_config.dig(:path, :current)
80
+ rescue JSON::ParserError => _e
81
+ nil
82
+ end
83
+ end
84
+ end
85
+ end
data/lib/wordmove.rb CHANGED
@@ -4,13 +4,15 @@ require 'active_support'
4
4
  require 'active_support/core_ext'
5
5
  require 'colorize'
6
6
  require 'dotenv'
7
+ require 'dry/cli'
8
+ require 'dry-configurable'
9
+ require 'dry/files'
7
10
  require 'erb'
8
11
  require 'kwalify'
12
+ require 'light-service'
9
13
  require 'logger'
10
14
  require 'open-uri'
11
15
  require 'ostruct'
12
- require 'thor'
13
- require 'thor/group'
14
16
  require 'yaml'
15
17
 
16
18
  require 'photocopier'
@@ -27,20 +29,40 @@ require 'wordmove/guardian'
27
29
  require 'wordmove/hook'
28
30
  require 'wordmove/logger'
29
31
  require 'wordmove/movefile'
30
- require 'wordmove/sql_adapter/default'
31
- require 'wordmove/sql_adapter/wpcli'
32
32
  require 'wordmove/wordpress_directory'
33
- require "wordmove/version"
34
- require "wordmove/environments_list"
33
+ require 'wordmove/version'
34
+ require 'wordmove/environments_list'
35
+ require 'wordmove/wpcli'
35
36
 
36
37
  require 'wordmove/generators/movefile_adapter'
37
38
  require 'wordmove/generators/movefile'
38
39
 
39
- require 'wordmove/deployer/base'
40
- require 'wordmove/deployer/ftp'
41
- require 'wordmove/deployer/ssh'
42
- require 'wordmove/deployer/ssh/default_sql_adapter'
43
- require 'wordmove/deployer/ssh/wpcli_sql_adapter'
40
+ require 'wordmove/db_paths_config'
41
+
42
+ require 'wordmove/actions/helpers'
43
+ require 'wordmove/actions/ssh/helpers'
44
+ require 'wordmove/actions/ftp/helpers'
45
+ Dir[File.join(__dir__, 'wordmove/actions/**/*.rb')].sort.each { |file| require file }
46
+ Dir[File.join(__dir__, 'wordmove/organizers/**/*.rb')].sort.each { |file| require file }
44
47
 
45
48
  module Wordmove
49
+ # Interactors' namespce. Interactors are called "Actions", following the LightService convention.
50
+ # In this namespace there are two kinds of "Actions":
51
+ # * local environment actions
52
+ # * protocol agnostic remote environment actions
53
+ # @see https://github.com/adomokos/light-service/blob/master/README.md LightService README
54
+ module Actions
55
+ # Ssh actions' namespace. Here are SSH protocol specific actions and organizers
56
+ # for remote environments
57
+ module Ssh
58
+ end
59
+ end
60
+
61
+ # Organizers are responsible of running organizer procedures putting together Actions
62
+ # following business logic requirements.
63
+ module Organizers
64
+ # Organizers for the Ssh protocol
65
+ module Ssh
66
+ end
67
+ end
46
68
  end
data/wordmove.gemspec CHANGED
@@ -3,48 +3,55 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
  require 'wordmove/version'
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = "wordmove"
6
+ spec.name = 'wordmove'
7
7
  spec.version = Wordmove::VERSION
8
+ spec.metadata = { 'rubygems_mfa_required' => 'true' }
8
9
  spec.authors = [
9
- "Stefano Verna", "Ju Liu", "Fabrizio Monti", "Alessandro Fazzi", "Filippo Gangi Dino"
10
+ 'Stefano Verna', 'Ju Liu', 'Fabrizio Monti', 'Alessandro Fazzi', 'Filippo Gangi Dino'
10
11
  ]
11
12
  spec.email = [
12
- "stefano.verna@welaika.com",
13
- "ju.liu@welaika.com",
14
- "fabrizio.monti@welaika.com",
15
- "alessandro.fazzi@welaika.com",
16
- "filippo.gangidino@welaika.com"
13
+ 'stefano.verna@welaika.com',
14
+ 'ju.liu@welaika.com',
15
+ 'fabrizio.monti@welaika.com',
16
+ 'alessandro.fazzi@welaika.com',
17
+ 'filippo.gangidino@welaika.com'
17
18
  ]
18
19
 
19
- spec.summary = "Wordmove, Capistrano for Wordpress"
20
- spec.description = "Wordmove deploys your WordPress websites at the speed of light."
21
- spec.homepage = "https://github.com/welaika/wordmove"
22
- spec.license = "MIT"
20
+ spec.summary = 'Wordmove, Capistrano for Wordpress'
21
+ spec.description = 'Wordmove deploys your WordPress websites at the speed of light.'
22
+ spec.homepage = 'https://github.com/welaika/wordmove'
23
+ spec.license = 'MIT'
23
24
 
24
25
  spec.files = `git ls-files -z`
25
26
  .split("\x0")
26
27
  .reject { |f| f.match(%r{^(test|spec|features)/}) }
27
28
 
28
- spec.bindir = "exe"
29
+ spec.bindir = 'exe'
29
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
- spec.require_paths = ["lib"]
31
-
32
- spec.add_runtime_dependency "activesupport", '~> 6.1'
33
- spec.add_runtime_dependency "colorize", "~> 0.8.1"
34
- spec.add_runtime_dependency "dotenv", "~> 2.7.5"
35
- spec.add_runtime_dependency "kwalify", "~> 0"
36
- spec.add_runtime_dependency "photocopier", "~> 1.4", ">= 1.4.0"
37
- spec.add_runtime_dependency "thor", "~> 0.20.3"
38
-
39
- spec.required_ruby_version = ">= 2.6.0"
40
-
41
- spec.add_development_dependency "bundler", "~> 2.0"
42
- spec.add_development_dependency "priscilla", "~> 1.0"
43
- spec.add_development_dependency "pry-byebug", "~> 3.1"
44
- spec.add_development_dependency "rake", "~> 13.0.1"
45
- spec.add_development_dependency "rspec", "~> 3.9"
46
- spec.add_development_dependency "rubocop", "~> 0.76.0"
47
- spec.add_development_dependency "simplecov", "~> 0.17.1"
31
+ spec.require_paths = ['lib']
32
+
33
+ spec.add_runtime_dependency 'activesupport', '~> 6.1'
34
+ spec.add_runtime_dependency 'colorize', '~> 0.8.1'
35
+ spec.add_runtime_dependency 'dotenv', '~> 2.7.5'
36
+ spec.add_runtime_dependency 'dry-configurable', '~> 0.13.0'
37
+ spec.add_runtime_dependency 'kwalify', '~> 0.7.2'
38
+ spec.add_runtime_dependency 'light-service', '~> 0.17.0'
39
+ spec.add_runtime_dependency 'photocopier', '~> 1.4', '>= 1.4.0'
40
+ # spec.add_runtime_dependency 'thor', '~> 0.20.3'
41
+ spec.add_runtime_dependency 'dry-cli', '~> 0.7.0'
42
+ spec.add_runtime_dependency 'dry-files', '~> 0.1.0'
43
+
44
+ spec.required_ruby_version = '>= 2.6.0' # rubocop:disable Gemspec/RequiredRubyVersion
45
+
46
+ spec.add_development_dependency 'bundler', '~> 2.3.3'
47
+ spec.add_development_dependency 'pry-byebug', '~> 3.1'
48
+ spec.add_development_dependency 'rake', '~> 13.0.1'
49
+ spec.add_development_dependency 'rspec', '~> 3.9'
50
+ spec.add_development_dependency 'rubocop', '~> 1.24.0'
51
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.6.0'
52
+ spec.add_development_dependency 'simplecov', '~> 0.21.2'
53
+ spec.add_development_dependency 'yard'
54
+ spec.add_development_dependency 'yard-activesupport-concern'
48
55
 
49
56
  spec.post_install_message = <<-RAINBOW
50
57
  Starting from version 3.0.0 `database.charset` option is no longer accepted.