webpacker_uploader 0.3.0 → 0.7.0

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.
@@ -1,23 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class WebpackerUploader::Instance
4
- cattr_accessor(:logger) { ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT)) }
5
-
6
4
  attr_writer :config
7
5
 
6
+ # @private
8
7
  def manifest
9
8
  @manifest ||= WebpackerUploader::Manifest.new
10
9
  end
11
10
 
11
+ # Returns the global WebpackerUploader::Configuration object.
12
+ # Use this to set and retrieve specific configuration options.
13
+ #
14
+ # @return [WebpackerUploader::Configuration]
15
+ #
16
+ # @example Disable log output
17
+ # WebpackerUploader.config.log_output = false
18
+ #
19
+ # @example Get the list of excluded file extension
20
+ # puts WebpackerUploader.config.ignored_extensions
21
+ #
22
+ # @see WebpackerUploader::Configuration
12
23
  def config
13
24
  @config ||= WebpackerUploader::Configuration.new
14
25
  end
15
26
 
27
+ # Yields the global configuration to a block. Use this to
28
+ # configure the base gem features.
29
+ #
30
+ # @example
31
+ # WebpackerUploader.configure do |config|
32
+ # config.ignored_extensions = [".png", ".jpg", ".webp"]
33
+ # config.log_output = false
34
+ # config.public_manifest_path = "path/to/manifest.json"
35
+ # config.public_path = "path/to/public/dir"
36
+ # end
37
+ #
38
+ # @see WebpackerUploader::Configuration
16
39
  def configure
17
40
  yield config
18
41
  end
19
42
 
20
- def upload!(provider, prefix: nil)
43
+ # Uploads assets using the supplied provider. Currently only AWS S3 is implemented.
44
+ #
45
+ # @return [void]
46
+ # @param provider [WebpackerUploader::Providers::Aws] A provider to use for file uploading.
47
+ # @param prefix [String] Used to prefix the remote file paths.
48
+ # @param cache_control [String] Used to add cache-control header to files.
49
+ def upload!(provider, prefix: nil, cache_control: nil)
21
50
  manifest.assets.each do |name, js_path|
22
51
  path = js_path[1..-1]
23
52
 
@@ -31,13 +60,13 @@ class WebpackerUploader::Instance
31
60
  file_path = config.public_path.join(path)
32
61
 
33
62
  if name.end_with?(*config.ignored_extensions)
34
- logger.info("Skipping #{file_path}") if config.log_output?
63
+ config.logger.info("Skipping #{file_path}") if config.log_output?
35
64
  else
36
65
  content_type = WebpackerUploader::Mime.mime_type(path)
37
66
 
38
- logger.info("Processing #{file_path} as #{content_type}") if config.log_output?
67
+ config.logger.info("Processing #{file_path} as #{content_type}") if config.log_output?
39
68
 
40
- provider.upload!(remote_path, file_path, content_type)
69
+ provider.upload!(remote_path, file_path, content_type, cache_control)
41
70
  end
42
71
  end
43
72
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class WebpackerUploader::Manifest
3
+ class WebpackerUploader::Manifest # @private
4
4
  attr_reader :assets
5
5
 
6
6
  def initialize
@@ -3,6 +3,12 @@
3
3
  require "mime-types"
4
4
 
5
5
  module WebpackerUploader::Mime
6
+ # Returns the mime type for the given file in the filesystem.
7
+ # If it's unable to detect the mime type, it returns +application/octet-stream+
8
+ # as a fallback.
9
+ #
10
+ # @param file_path [String] A file path in the local filesystem.
11
+ # @return [String] The file mime type.
6
12
  def mime_type(file_path)
7
13
  fallback = MIME::Types.type_for(file_path).first&.content_type || "application/octet-stream"
8
14
  Rack::Mime.mime_type(File.extname(file_path), fallback)
@@ -3,34 +3,105 @@
3
3
  require "aws-sdk-s3"
4
4
 
5
5
  module WebpackerUploader
6
+ # Namespace for the upload providers.
6
7
  module Providers
8
+ # AWS provider uploads files to AWS S3. It uses the +aws-sdk-s3+ gem.
7
9
  class Aws
8
- attr_reader :client
9
- attr_reader :resource
10
- attr_reader :bucket
10
+ # AWS provider error class. Raised when the provided AWS client credentials options are wrong.
11
+ class CredentialsError < StandardError; end
11
12
 
13
+ # @param [Hash] options
14
+ # * :region (String) The S3 region name.
15
+ # * :bucket (String) The S3 bucket name.
16
+ # * :credentials (Hash) credential options for the AWS provider:
17
+ # * :profile_name (String) use a named profile configured in ~/.aws/credentials
18
+ # * :instance_profile (Boolean) use an instance profile from an EC2
19
+ # * :access_key_id (String) the AWS credentials access id.
20
+ # * :secret_access_key (String) the AWS credentials secret access key.
21
+ #
22
+ # @note Any unknown options will be passed directly to the +Aws::S3::Client+ class
23
+ # during initialization.
24
+ #
25
+ # @raise [CredentialsError] if the credential options Hash is not correct.
26
+ #
27
+ # @example Initialize using a named profile:
28
+ #
29
+ # provider_options = {
30
+ # credentials: { profile_name: "staging" },
31
+ # region: "eu-central-1",
32
+ # bucket: "application-assets-20200929124523451600000001"
33
+ # }
34
+ # provider = WebpackerUploader::Providers::Aws.new(provider_options)
35
+ #
36
+ # @example Initialize using IAM keys
37
+ #
38
+ # provider_options = {
39
+ # credentials: { access_key_id: "KEY_ID", secret_access_key: "ACCESS_KEY" },
40
+ # region: "eu-central-1",
41
+ # bucket: "application-assets-20200929124523451600000001"
42
+ # }
43
+ # provider = WebpackerUploader::Providers::Aws.new(provider_options)
44
+ #
45
+ # @example Initialize using an EC2 instance profile
46
+ #
47
+ # provider_options = {
48
+ # credentials: { instance_profile: true },
49
+ # region: "eu-central-1",
50
+ # bucket: "application-assets-20200929124523451600000001"
51
+ # }
52
+ # provider = WebpackerUploader::Providers::Aws.new(provider_options)
12
53
  def initialize(options)
13
- @client = ::Aws::S3::Client.new(credentials: credentials(options[:credentials]), region: options[:region])
14
- @resource = ::Aws::S3::Resource.new(client: @client)
15
- @bucket = @resource.bucket(options[:bucket])
54
+ @region = options.delete(:region)
55
+ @bucket_name = options.delete(:bucket)
56
+ @credentials = credentials(options.delete(:credentials))
57
+ @aws_options = options
58
+ @resource = ::Aws::S3::Resource.new(client: client)
16
59
  end
17
60
 
18
- def upload!(object_key, file, content_type = "")
19
- object = @bucket.object(object_key)
20
- object.upload_file(file, content_type: content_type)
61
+ # Uploads a file to AWS S3.
62
+ #
63
+ # @param object_key [String] Is the remote path name for the S3 object.
64
+ # @param file [Pathname] Path of the local file.
65
+ # @param content_type [String] The content type that will be set to the S3 object.
66
+ # @return [void]
67
+ def upload!(object_key, file, content_type = "", cache_control = "")
68
+ object = @resource.bucket(@bucket_name).object(object_key)
69
+ object.upload_file(file, content_type: content_type, cache_control: cache_control)
21
70
  end
22
71
 
23
72
  private
24
-
25
73
  def credentials(options)
26
- if options[:profile_name].present?
27
- ::Aws::SharedCredentials.new(profile_name: options[:profile_name])
74
+ if options.key?(:profile_name)
75
+ { profile: options[:profile_name] }
28
76
  elsif options.key?(:instance_profile) && options[:instance_profile]
29
77
  ::Aws::InstanceProfileCredentials.new
30
- else
78
+ elsif options.key?(:access_key_id) && options.key?(:secret_access_key)
31
79
  ::Aws::Credentials.new(options[:access_key_id], options[:secret_access_key])
80
+ else
81
+ raise CredentialsError, "Wrong AWS provider credentials options."
32
82
  end
33
83
  end
84
+
85
+ def profile?
86
+ @credentials.is_a?(Hash) && @credentials.key?(:profile)
87
+ end
88
+
89
+ def credentials_object?
90
+ !profile?
91
+ end
92
+
93
+ def client
94
+ ::Aws::S3::Client.new(client_options)
95
+ end
96
+
97
+ def client_options
98
+ opts = {}
99
+ opts.merge!(@aws_options)
100
+ opts[:region] = @region
101
+ opts.merge!(@credentials) if profile?
102
+ opts[:credentials] = @credentials if credentials_object?
103
+ opts
104
+ end
34
105
  end
35
106
  end
36
107
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebpackerUploader
2
- VERSION = "0.3.0"
4
+ VERSION = "0.7.0"
3
5
  end
@@ -1,21 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/module"
3
4
  require "active_support/core_ext/object/blank"
4
- require "active_support/logger"
5
- require "active_support/tagged_logging"
6
5
 
7
6
  module WebpackerUploader
8
7
  extend self
9
8
 
9
+ # @private
10
10
  def instance=(instance)
11
11
  @instance = instance
12
12
  end
13
13
 
14
+ # @private
14
15
  def instance
15
16
  @instance ||= WebpackerUploader::Instance.new
16
17
  end
17
18
 
18
- delegate :logger, :logger=, :configure, :config, :upload!, to: :instance
19
+ # @!attribute [rw] config
20
+ # @see Instance#config
21
+ # @!scope class
22
+ # @!method configure
23
+ # @see Instance#configure
24
+ # @!scope class
25
+ # @!method upload!
26
+ # @return [void]
27
+ # @see Instance#upload!
28
+ # @!scope class
29
+ delegate :configure, :config, :upload!, to: :instance
19
30
  end
20
31
 
21
32
  require "webpacker_uploader/configuration"
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class ConfigurationTest < Minitest::Test
6
+ def setup
7
+ @config = WebpackerUploader::Configuration.new
8
+ end
9
+
10
+ def teardown
11
+ WebpackerUploader.reset!
12
+ end
13
+
14
+ def test_default_config_options
15
+ assert_empty @config.ignored_extensions
16
+
17
+ assert_instance_of ActiveSupport::Logger, @config.logger
18
+
19
+ assert @config.log_output
20
+ assert @config.log_output?
21
+
22
+ public_manifest_path = Pathname.new(File.expand_path("test_app/public/packs/manifest.json", __dir__))
23
+ assert_equal public_manifest_path, @config.public_manifest_path
24
+
25
+ public_path = Pathname.new(File.expand_path("test_app/public/", __dir__))
26
+ assert_equal public_path, @config.public_path
27
+ end
28
+
29
+ def test_changing_config_options
30
+ @config.ignored_extensions = [".css", ".js"]
31
+ assert_equal [".css", ".js"], @config.ignored_extensions
32
+
33
+ @config.logger = Logger.new(STDOUT)
34
+ assert_instance_of Logger, @config.logger
35
+
36
+ @config.log_output = false
37
+ refute @config.log_output
38
+ refute @config.log_output?
39
+
40
+ @config.public_manifest_path = "test_app/manifest.json"
41
+ assert_equal "test_app/manifest.json", @config.public_manifest_path.to_s
42
+
43
+ @config.public_path = "test_app"
44
+ assert_equal "test_app", @config.public_path.to_s
45
+ end
46
+
47
+ def test_configure_block
48
+ WebpackerUploader.configure do |c|
49
+ c.ignored_extensions = [".js"]
50
+ c.logger = Logger.new(STDOUT)
51
+ c.log_output = false
52
+ c.public_manifest_path = "path/to/manifest.json"
53
+ c.public_path = "path/to/public/dir"
54
+ end
55
+
56
+ assert_equal [".js"], WebpackerUploader.config.ignored_extensions
57
+ assert_instance_of Logger, WebpackerUploader.config.logger
58
+ refute WebpackerUploader.config.log_output
59
+ refute WebpackerUploader.config.log_output?
60
+ assert_equal "path/to/manifest.json", WebpackerUploader.config.public_manifest_path.to_s
61
+ assert_equal "path/to/public/dir", WebpackerUploader.config.public_path.to_s
62
+
63
+ assert_raises(NoMethodError) do
64
+ WebpackerUploader.configure do |c|
65
+ c.unknown = true
66
+ end
67
+ end
68
+ end
69
+
70
+ def test_pathname_casting
71
+ WebpackerUploader.config do |c|
72
+ c.public_manifest_path = "path/to/manifest.json"
73
+ c.public_path = "path/to/public/dir"
74
+ end
75
+
76
+ assert_instance_of Pathname, WebpackerUploader.config.public_manifest_path
77
+ assert_instance_of Pathname, WebpackerUploader.config.public_path
78
+
79
+ WebpackerUploader.configure do |c|
80
+ c.public_manifest_path = Pathname.new("path/to/manifest.json")
81
+ c.public_path = Pathname.new("path/to/public/dir")
82
+ end
83
+
84
+ assert_instance_of Pathname, WebpackerUploader.config.public_manifest_path
85
+ assert_instance_of Pathname, WebpackerUploader.config.public_path
86
+ end
87
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class ManifestTest < Minitest::Test
6
+ def setup
7
+ @manifest = ::WebpackerUploader::Manifest.new
8
+ end
9
+
10
+ def teardown
11
+ WebpackerUploader.reset!
12
+ end
13
+
14
+ def test_assets
15
+ assert_includes @manifest.assets, "application.css"
16
+ assert_includes @manifest.assets, "application.js"
17
+ assert_includes @manifest.assets, "application.png"
18
+ refute_includes @manifest.assets, "entrypoints"
19
+ end
20
+
21
+ def test_missing_manifest
22
+ WebpackerUploader.config.public_manifest_path = "missing.json"
23
+ @empty_manifest = WebpackerUploader::Manifest.new
24
+
25
+ assert_empty @empty_manifest.assets
26
+ end
27
+ end
data/test/mime_test.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class MimeTest < Minitest::Test
6
+ def test_for_webp
7
+ # Rack < 3.x does not support this so we have a fallback mechanism in place
8
+ assert_equal "image/webp", WebpackerUploader::Mime.mime_type("image-dd6b1cd38bfa093df600.webp")
9
+ end
10
+
11
+ def test_for_sourcemap
12
+ assert_equal "application/octet-stream", WebpackerUploader::Mime.mime_type("application-dd6b1cd38bfa093df600.css.map")
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+ require "webpacker_uploader/providers/aws"
5
+
6
+ class AwsTest < Minitest::Test
7
+ def test_credentials_initialization
8
+ assert_raises(WebpackerUploader::Providers::Aws::CredentialsError) do
9
+ WebpackerUploader::Providers::Aws.new(credentials: {})
10
+ end
11
+
12
+ assert_raises(WebpackerUploader::Providers::Aws::CredentialsError) do
13
+ WebpackerUploader::Providers::Aws.new(credentials: { access_key_id: "test" })
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,103 @@
1
+ # Note: You must restart bin/webpack-dev-server for changes to take effect
2
+
3
+ default: &default
4
+ source_path: app/javascript
5
+ source_entry_path: packs
6
+ public_root_path: public
7
+ public_output_path: packs
8
+ cache_path: tmp/cache/webpacker
9
+ webpack_compile_output: false
10
+
11
+ # Additional paths webpack should lookup modules
12
+ # ['app/assets', 'engine/foo/app/assets']
13
+ additional_paths:
14
+ - app/assets
15
+ - /etc/yarn
16
+
17
+ # This configuration option is deprecated and is only here for testing, to
18
+ # ensure backwards-compatibility. Please use `additional_paths`.
19
+ resolved_paths:
20
+ - app/elm
21
+
22
+ # Reload manifest.json on all requests so we reload latest compiled packs
23
+ cache_manifest: false
24
+
25
+ # Extract and emit a css file
26
+ extract_css: false
27
+
28
+ static_assets_extensions:
29
+ - .jpg
30
+ - .jpeg
31
+ - .png
32
+ - .gif
33
+ - .tiff
34
+ - .ico
35
+ - .svg
36
+
37
+ extensions:
38
+ - .mjs
39
+ - .js
40
+ - .sass
41
+ - .scss
42
+ - .css
43
+ - .module.sass
44
+ - .module.scss
45
+ - .module.css
46
+ - .png
47
+ - .svg
48
+ - .gif
49
+ - .jpeg
50
+ - .jpg
51
+ - .elm
52
+
53
+ development:
54
+ <<: *default
55
+ compile: true
56
+
57
+ # Reference: https://webpack.js.org/configuration/dev-server/
58
+ dev_server:
59
+ https: false
60
+ host: localhost
61
+ port: 3035
62
+ public: localhost:3035
63
+ hmr: false
64
+ # Inline should be set to true if using HMR
65
+ inline: true
66
+ overlay: true
67
+ disable_host_check: true
68
+ use_local_ip: false
69
+ pretty: false
70
+
71
+ test:
72
+ <<: *default
73
+ compile: true
74
+
75
+ # Compile test packs to a separate directory
76
+ public_output_path: packs-test
77
+
78
+ production:
79
+ <<: *default
80
+
81
+ # Production depends on precompilation of packs prior to booting for performance.
82
+ compile: false
83
+
84
+ # Extract and emit a css file
85
+ extract_css: true
86
+
87
+ # Cache manifest.json for performance
88
+ cache_manifest: true
89
+
90
+ staging:
91
+ <<: *default
92
+
93
+ # Production depends on precompilation of packs prior to booting for performance.
94
+ compile: false
95
+
96
+ # Extract and emit a css file
97
+ extract_css: true
98
+
99
+ # Cache manifest.json for performance
100
+ cache_manifest: true
101
+
102
+ # Compile staging packs to a separate directory
103
+ public_output_path: packs-staging
@@ -0,0 +1,33 @@
1
+ {
2
+ "bootstrap.css": "/packs/bootstrap-c38deda30895059837cf.css",
3
+ "application.css": "/packs/application-dd6b1cd38bfa093df600.css",
4
+ "application.css.map": "/packs/application-dd6b1cd38bfa093df600.css.map",
5
+ "bootstrap.js": "/packs/bootstrap-300631c4f0e0f9c865bc.js",
6
+ "application.js": "/packs/application-k344a6d59eef8632c9d1.js",
7
+ "application.png": "/packs/application-k344a6d59eef8632c9d1.png",
8
+ "fonts/fa-regular-400.woff2": "/packs/fonts/fa-regular-400-944fb546bd7018b07190a32244f67dc9.woff2",
9
+ "media/images/image.jpg": "/packs/media/images/image-c38deda30895059837cf.jpg",
10
+ "media/images/image-2x.jpg": "/packs/media/images/image-2x-7cca48e6cae66ec07b8e.jpg",
11
+ "media/images/nested/image.jpg": "/packs/media/images/nested/image-c38deda30895059837cf.jpg",
12
+ "media/images/mb-icon.png": "/packs/media/images/mb-icon-c38deda30895059837cf.png",
13
+ "media/images/nested/mb-icon.png": "/packs/media/images/nested/mb-icon-c38deda30895059837cf.png",
14
+ "entrypoints": {
15
+ "application": {
16
+ "js": [
17
+ "/packs/vendors~application~bootstrap-c20632e7baf2c81200d3.chunk.js",
18
+ "/packs/vendors~application-e55f2aae30c07fb6d82a.chunk.js",
19
+ "/packs/application-k344a6d59eef8632c9d1.js"
20
+ ],
21
+ "css": [
22
+ "/packs/1-c20632e7baf2c81200d3.chunk.css",
23
+ "/packs/application-k344a6d59eef8632c9d1.chunk.css"
24
+ ]
25
+ },
26
+ "hello_stimulus": {
27
+ "css": [
28
+ "/packs/1-c20632e7baf2c81200d3.chunk.css",
29
+ "/packs/hello_stimulus-k344a6d59eef8632c9d1.chunk.css"
30
+ ]
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require "rails"
5
+ require "rails/test_help"
6
+ require "webpacker"
7
+ require "webpacker_uploader"
8
+
9
+ module TestApp
10
+ class Application < ::Rails::Application
11
+ config.root = File.join(File.dirname(__FILE__), "test_app")
12
+ config.eager_load = true
13
+ end
14
+ end
15
+
16
+ TestApp::Application.initialize!
17
+
18
+ module WebpackerUploader::Providers
19
+ class TestProvider
20
+ def initialize(asset_objects)
21
+ @asset_objects = asset_objects
22
+ end
23
+
24
+ def upload!(object_key, file, content_type = "", cache_control = "")
25
+ @asset_objects << { object_key: object_key, file: file, content_type: content_type, cache_control: cache_control }
26
+ end
27
+ end
28
+ end
29
+
30
+ module WebpackerUploader
31
+ def reset!
32
+ WebpackerUploader.instance.config = nil
33
+ end
34
+ module_function :reset!
35
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class WebpackerUploaderTest < Minitest::Test
6
+ def setup
7
+ @asset_objects = []
8
+ @provider = WebpackerUploader::Providers::TestProvider.new(@asset_objects)
9
+
10
+ WebpackerUploader.config.log_output = false
11
+ end
12
+
13
+ def teardown
14
+ WebpackerUploader.reset!
15
+ end
16
+
17
+ def test_upload
18
+ WebpackerUploader.upload!(@provider)
19
+
20
+ assert_equal "packs/bootstrap-c38deda30895059837cf.css", @asset_objects.first[:object_key]
21
+ assert_instance_of Pathname, @asset_objects.first[:file]
22
+ assert_equal "text/css", @asset_objects.first[:content_type]
23
+ end
24
+
25
+ def test_upload_with_prefix
26
+ WebpackerUploader.upload!(@provider, prefix: "prefix")
27
+
28
+ assert_equal "prefix/packs/bootstrap-c38deda30895059837cf.css", @asset_objects.first[:object_key]
29
+ assert_instance_of Pathname, @asset_objects.first[:file]
30
+ assert_equal "text/css", @asset_objects.first[:content_type]
31
+ end
32
+
33
+ def test_upload_with_cache_control
34
+ WebpackerUploader.upload!(@provider, cache_control: "cache_control")
35
+ assert_equal "cache_control", @asset_objects.first[:cache_control]
36
+ end
37
+ end
@@ -20,20 +20,16 @@ Gem::Specification.new do |s|
20
20
  "source_code_uri" => "https://github.com/tlatsas/webpacker_uploader/tree/v#{s.version}"
21
21
  }
22
22
 
23
- # Specify which files should be added to the gem when it is released.
24
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
- s.files = Dir.chdir(File.expand_path("..", __FILE__)) do
26
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
- end
28
-
29
- s.bindir = "exe"
30
- s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ s.files = `git ls-files`.split("\n").reject { |f| f.match(%r{^(bin|test|integration|.github)/}) }
24
+ s.test_files = `git ls-files -- test/*`.split("\n")
31
25
  s.require_paths = ["lib"]
32
26
 
33
27
  s.add_dependency "webpacker", ">= 5.1"
34
28
  s.add_dependency "mime-types"
29
+ s.add_dependency "rack", "~> 2.0"
35
30
 
36
31
  s.add_development_dependency "bundler", ">= 1.3.0"
37
- s.add_development_dependency "rubocop", "< 0.69"
32
+ s.add_development_dependency "rubocop", "1.11.0"
38
33
  s.add_development_dependency "rubocop-performance"
34
+ s.add_development_dependency "rubocop-minitest"
39
35
  end