goodcheck 2.6.1 → 2.7.0

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
2
  SHA256:
3
- metadata.gz: 883ed31ce90c345b08d6b9e12b89a13c802baa1f6836d986f440a2eac816ec41
4
- data.tar.gz: 196cc57241e9fc0f1505122225d4f165930c9fed34cf30f58146f415c53eaeef
3
+ metadata.gz: 8c2de636a6040f67abd7f2a24da8cd3519d76490724c0897ec4da3e8594e9a88
4
+ data.tar.gz: 690af43e3de713f7185fe3d080d8d99b647423e389b941981d18c2a84e049169
5
5
  SHA512:
6
- metadata.gz: 857d4fb3c2048887c3c4e2809c17357e22eb968ad895a125db39059b52e81ccb9f7fb956827e3c4d0f927c052b8deb1f277f4183c2d0542f09fb45b016f14aed
7
- data.tar.gz: f306f2e95696190574f577b141bccefd641bc62171008cb0a47c8bd7fe9966dba0a9c92881f91c186dfafab49c3173519537017bf377579aecce686e275e1890
6
+ metadata.gz: 8e6d05a4a287d13d41fcb42af646d259aa50b1cb0c8b0c39d14aa09ce96fa97839331dbf192f1048737f56b5a253ef590187add746f82251c3acb8e45ceaaa7a
7
+ data.tar.gz: aeec81a86feffac3321ad616c10f5f319a37dd6efce177a446e0a316c35db1fdc8de405d5c2afd5a7e450a9faafb359578ea92a1283411bfc988f78722c35eb5
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## HEAD
4
4
 
5
+ ## 2.7.0 (2020-12-02)
6
+
7
+ * Goodbye ActiveSupport [#155](https://github.com/sider/goodcheck/pull/155)
8
+ * Fix zero column [#160](https://github.com/sider/goodcheck/pull/160)
9
+ * Improve text reporter [#161](https://github.com/sider/goodcheck/pull/161)
10
+
5
11
  ## 2.6.1 (2020-11-19)
6
12
 
7
13
  * Add missing doc for importing glob feature [#151](https://github.com/sider/goodcheck/pull/151)
@@ -4,9 +4,7 @@ require "set"
4
4
  require "strong_json"
5
5
  require "yaml"
6
6
  require "json"
7
- require "active_support/core_ext/hash/indifferent_access"
8
- require "active_support/core_ext/integer/inflections"
9
- require "active_support/tagged_logging"
7
+ require "logger"
10
8
  require "rainbow"
11
9
  require "digest/sha2"
12
10
  require "net/http"
@@ -13,7 +13,7 @@ module Goodcheck
13
13
  def scan(&block)
14
14
  if block_given?
15
15
  if trigger.patterns.empty?
16
- yield Issue.new(buffer: buffer, range: nil, rule: rule, text: nil)
16
+ yield Issue.new(buffer: buffer, rule: rule)
17
17
  else
18
18
  var_pats, novar_pats = trigger.patterns.partition {|pat|
19
19
  pat.is_a?(Pattern::Token) && !pat.variables.empty?
@@ -44,9 +44,7 @@ module Goodcheck
44
44
  while true
45
45
  case
46
46
  when scanner.scan_until(regexp)
47
- text = scanner.matched
48
- range = (scanner.pos - text.bytesize) .. scanner.pos
49
- issues << Issue.new(buffer: buffer, range: range, rule: rule, text: text)
47
+ issues << new_issue_with_matched(scanner)
50
48
  else
51
49
  break
52
50
  end
@@ -55,7 +53,7 @@ module Goodcheck
55
53
  issues.each(&block)
56
54
  else
57
55
  unless regexp =~ buffer.content
58
- yield Issue.new(buffer: buffer, range: nil, rule: rule, text: nil)
56
+ yield Issue.new(buffer: buffer, rule: rule)
59
57
  end
60
58
  end
61
59
  end
@@ -68,9 +66,7 @@ module Goodcheck
68
66
  case
69
67
  when scanner.scan_until(pat.regexp)
70
68
  if pat.test_variables(scanner)
71
- text = scanner.matched
72
- range = (scanner.pos - text.bytesize) .. scanner.pos
73
- yield Issue.new(buffer: buffer, range: range, rule: rule, text: text)
69
+ yield new_issue_with_matched(scanner)
74
70
  end
75
71
  else
76
72
  break
@@ -84,11 +80,19 @@ module Goodcheck
84
80
  break
85
81
  end
86
82
  else
87
- yield Issue.new(buffer: buffer, range: nil, rule: rule, text: nil)
83
+ yield Issue.new(buffer: buffer, rule: rule)
88
84
  break
89
85
  end
90
86
  end
91
87
  end
92
88
  end
89
+
90
+ private
91
+
92
+ def new_issue_with_matched(scanner)
93
+ Issue.new(buffer: buffer, rule: rule,
94
+ text: scanner.matched,
95
+ text_begin_pos: scanner.pos - scanner.matched_size)
96
+ end
93
97
  end
94
98
  end
@@ -43,7 +43,7 @@ module Goodcheck
43
43
  start_position = 0
44
44
 
45
45
  content.split(/\n/, -1).each do |line|
46
- range = start_position...(start_position + line.bytesize)
46
+ range = start_position..(start_position + line.bytesize)
47
47
  @line_ranges << range
48
48
  start_position = range.end + 1
49
49
  end
@@ -51,10 +51,10 @@ module Goodcheck
51
51
 
52
52
  @line_ranges
53
53
  end
54
-
54
+
55
55
  def line_disabled?(line_number)
56
56
  if line_number > 1
57
- return true if DISABLE_NEXT_LINE_PATTERNS.any? { |pattern| line(line_number - 1).match?(pattern) }
57
+ return true if DISABLE_NEXT_LINE_PATTERNS.any? { |pattern| line(line_number - 1).match?(pattern) }
58
58
  end
59
59
 
60
60
  if line_number <= lines.length
@@ -70,7 +70,9 @@ module Goodcheck
70
70
  end
71
71
 
72
72
  if line_index
73
- [line_index + 1, position - line_ranges[line_index].begin]
73
+ line_number = line_index + 1
74
+ column_number = position - line_ranges[line_index].begin + 1
75
+ [line_number, column_number]
74
76
  end
75
77
  end
76
78
 
@@ -79,16 +81,7 @@ module Goodcheck
79
81
  end
80
82
 
81
83
  def line(line_number)
82
- lines[line_number-1]
83
- end
84
-
85
- def position_for_location(line, column)
86
- if (range = line_ranges[line-1])
87
- pos = range.begin + column
88
- if pos <= range.end
89
- pos
90
- end
91
- end
84
+ lines[line_number - 1]
92
85
  end
93
86
  end
94
87
  end
@@ -63,7 +63,7 @@ module Goodcheck
63
63
  rules = []
64
64
  formats = [:text, :json]
65
65
  format = :text
66
- loglevel = Logger::ERROR
66
+ loglevel = nil
67
67
  force_download = false
68
68
 
69
69
  OptionParser.new("Usage: goodcheck check [options] paths...") do |opts|
@@ -82,7 +82,7 @@ module Goodcheck
82
82
  end
83
83
  end.parse!(args)
84
84
 
85
- Goodcheck.logger.level = loglevel
85
+ Goodcheck.logger.level = loglevel if loglevel
86
86
 
87
87
  if args.empty?
88
88
  targets << Pathname(".")
@@ -57,6 +57,8 @@ module Goodcheck
57
57
  end
58
58
  end
59
59
 
60
+ reporter.summary
61
+
60
62
  issue_reported ? EXIT_MATCH : EXIT_SUCCESS
61
63
  end
62
64
  end
@@ -71,31 +73,27 @@ module Goodcheck
71
73
  def each_check
72
74
  targets.each do |target|
73
75
  Goodcheck.logger.info "Checking target: #{target}"
74
- Goodcheck.logger.tagged target.to_s do
75
- each_file target, immediate: true do |path|
76
- Goodcheck.logger.debug "Checking file: #{path}"
77
- Goodcheck.logger.tagged path.to_s do
78
- reporter.file(path) do
79
- buffers = {}
80
-
81
- config.rules_for_path(path, rules_filter: rules) do |rule, glob, trigger|
82
- Goodcheck.logger.debug "Checking rule: #{rule.id}"
83
- begin
84
- encoding = glob&.encoding || Encoding.default_external.name
85
-
86
- if buffers[encoding]
87
- buffer = buffers[encoding]
88
- else
89
- content = path.read(encoding: encoding).encode(Encoding.default_internal || Encoding::UTF_8)
90
- buffer = Buffer.new(path: path, content: content)
91
- buffers[encoding] = buffer
92
- end
93
-
94
- yield buffer, rule, trigger
95
- rescue ArgumentError => exn
96
- stderr.puts "#{path}: #{exn.inspect}"
97
- end
76
+ each_file target, immediate: true do |path|
77
+ Goodcheck.logger.debug "Checking file: #{path}"
78
+ reporter.file(path) do
79
+ buffers = {}
80
+
81
+ config.rules_for_path(path, rules_filter: rules) do |rule, glob, trigger|
82
+ Goodcheck.logger.debug "Checking rule: #{rule.id}"
83
+ begin
84
+ encoding = glob&.encoding || Encoding.default_external.name
85
+
86
+ if buffers[encoding]
87
+ buffer = buffers[encoding]
88
+ else
89
+ content = path.read(encoding: encoding).encode(Encoding.default_internal || Encoding::UTF_8)
90
+ buffer = Buffer.new(path: path, content: content)
91
+ buffers[encoding] = buffer
98
92
  end
93
+
94
+ yield buffer, rule, trigger
95
+ rescue ArgumentError => exn
96
+ stderr.puts "#{path}: #{exn.inspect}"
99
97
  end
100
98
  end
101
99
  end
@@ -112,7 +110,7 @@ module Goodcheck
112
110
  when DEFAULT_EXCLUSIONS.include?(path.basename.to_s)
113
111
  # noop
114
112
  when immediate || !excluded?(path)
115
- path.children.each do |child|
113
+ path.children.sort.each do |child|
116
114
  each_file(child, &block)
117
115
  end
118
116
  end
@@ -5,6 +5,7 @@ module Goodcheck
5
5
  attr_reader :path
6
6
 
7
7
  def initialize(path:)
8
+ super(path.to_s)
8
9
  @path = path
9
10
  end
10
11
  end
@@ -20,7 +21,7 @@ module Goodcheck
20
21
  end
21
22
 
22
23
  import_loader = ImportLoader.new(cache_path: cache_path, force_download: force_download, config_path: config_path)
23
- content = JSON.parse(JSON.dump(YAML.load(config_content, filename: config_path.to_s)), symbolize_names: true)
24
+ content = JSON.parse(JSON.dump(YAML.safe_load(config_content, filename: config_path.to_s)), symbolize_names: true)
24
25
  loader = ConfigLoader.new(path: config_path, content: content, stderr: stderr, import_loader: import_loader)
25
26
  @config = loader.load
26
27
  end
@@ -51,7 +51,8 @@ module Goodcheck
51
51
  true
52
52
  else
53
53
  count = duplicated_ids.size
54
- stdout.puts(Rainbow(" Found #{count} #{'duplication'.pluralize(count)}.😞").red)
54
+ duplication = count == 1 ? 'duplication' : 'duplications'
55
+ stdout.puts(Rainbow(" Found #{count} #{duplication}.😞").red)
55
56
  duplicated_ids.each do |id|
56
57
  stdout.puts " #{id}"
57
58
  end
@@ -76,7 +77,7 @@ module Goodcheck
76
77
  if trigger.by_pattern?
77
78
  stdout.puts " Testing pattern..."
78
79
  else
79
- stdout.puts " Testing #{(index+1).ordinalize} trigger..."
80
+ stdout.puts " #{index + 1}. Testing trigger..."
80
81
  end
81
82
 
82
83
  pass_errors = trigger.passes.each.with_index.select do |pass, _|
@@ -92,7 +93,7 @@ module Goodcheck
92
93
  rule_ok = false
93
94
 
94
95
  pass_errors.each do |_, index|
95
- stdout.puts " #{(index+1).ordinalize} #{Rainbow('pass').green} example matched.😱"
96
+ stdout.puts " #{index + 1}. #{Rainbow('pass').green} example matched.😱"
96
97
  failed_rule_ids << rule.id
97
98
  end
98
99
  end
@@ -102,7 +103,7 @@ module Goodcheck
102
103
  rule_ok = false
103
104
 
104
105
  fail_errors.each do |_, index|
105
- stdout.puts " #{(index+1).ordinalize} #{Rainbow('fail').red} example didn't match.😱"
106
+ stdout.puts " #{index + 1}. #{Rainbow('fail').red} example didn't match.😱"
106
107
  failed_rule_ids << rule.id
107
108
  end
108
109
  end
@@ -132,10 +133,11 @@ module Goodcheck
132
133
  end
133
134
 
134
135
  rule_count = success_count + failure_count
136
+ rule = rule_count == 1 ? "1 rule" : "#{rule_count} rules"
137
+ success = Rainbow(success_count == 1 ? "1 success" : "#{success_count} successes").green
138
+ failure = Rainbow(failure_count == 1 ? "1 failure" : "#{failure_count} failures").red
135
139
  stdout.puts ""
136
- stdout.puts ["Tested #{rule_count} #{'rule'.pluralize(rule_count)}",
137
- Rainbow("#{success_count} #{'success'.pluralize(success_count)}").green,
138
- Rainbow("#{failure_count} #{'failure'.pluralize(failure_count)}").red].join(", ")
140
+ stdout.puts "Tested #{rule}, #{success}, #{failure}"
139
141
 
140
142
  test_pass
141
143
  end
@@ -214,21 +214,19 @@ module Goodcheck
214
214
 
215
215
  def load
216
216
  Goodcheck.logger.info "Loading configuration: #{path}"
217
- Goodcheck.logger.tagged "#{path}" do
218
- Schema.config.coerce(content)
217
+ Schema.config.coerce(content)
219
218
 
220
- rules = []
219
+ rules = []
221
220
 
222
- load_rules(rules, array(content[:rules]))
221
+ load_rules(rules, array(content[:rules]))
223
222
 
224
- Array(content[:import]).each do |import|
225
- load_import rules, import
226
- end
223
+ Array(content[:import]).each do |import|
224
+ load_import rules, import
225
+ end
227
226
 
228
- exclude_paths = Array(content[:exclude])
227
+ exclude_paths = Array(content[:exclude])
229
228
 
230
- Config.new(rules: rules, exclude_paths: exclude_paths)
231
- end
229
+ Config.new(rules: rules, exclude_paths: exclude_paths)
232
230
  end
233
231
 
234
232
  def load_rules(rules, array)
@@ -240,13 +238,11 @@ module Goodcheck
240
238
  def load_import(rules, import)
241
239
  Goodcheck.logger.info "Importing rules from #{import}"
242
240
 
243
- Goodcheck.logger.tagged import do
244
- import_loader.load(import) do |content|
245
- json = JSON.parse(JSON.dump(YAML.load(content, filename: import)), symbolize_names: true)
241
+ import_loader.load(import) do |content|
242
+ json = JSON.parse(JSON.dump(YAML.load(content, filename: import)), symbolize_names: true)
246
243
 
247
- Schema.rules.coerce json
248
- load_rules(rules, json)
249
- end
244
+ Schema.rules.coerce json
245
+ load_rules(rules, json)
250
246
  end
251
247
  end
252
248
 
@@ -1,15 +1,15 @@
1
1
  module Goodcheck
2
2
  class Issue
3
3
  attr_reader :buffer
4
- attr_reader :range
5
4
  attr_reader :rule
6
5
  attr_reader :text
6
+ attr_reader :range
7
7
 
8
- def initialize(buffer:, range:, rule:, text:)
8
+ def initialize(buffer:, rule:, text: nil, text_begin_pos: nil)
9
9
  @buffer = buffer
10
- @range = range
11
10
  @rule = rule
12
11
  @text = text
12
+ @range = text ? text_begin_pos..(text_begin_pos + text.bytesize - 1) : nil
13
13
  @location = nil
14
14
  end
15
15
 
@@ -1,4 +1,18 @@
1
1
  module Goodcheck
2
+ # In the example below, each attribute is:
3
+ #
4
+ # - start_line: 2
5
+ # - start_column: 3
6
+ # - end_line: 2
7
+ # - end_column: 9
8
+ #
9
+ # @example
10
+ #
11
+ # 1 |
12
+ # 2 | A matched text
13
+ # 3 | ^~~~~~~
14
+ # 3456789
15
+ #
2
16
  class Location
3
17
  attr_reader :start_line
4
18
  attr_reader :start_column
@@ -12,6 +26,14 @@ module Goodcheck
12
26
  @end_column = end_column
13
27
  end
14
28
 
29
+ def one_line?
30
+ start_line == end_line
31
+ end
32
+
33
+ def column_size
34
+ end_column - start_column + 1
35
+ end
36
+
15
37
  def ==(other)
16
38
  other.is_a?(Location) &&
17
39
  other.start_line == start_line &&
@@ -19,5 +41,11 @@ module Goodcheck
19
41
  other.end_line == end_line &&
20
42
  other.end_column == end_column
21
43
  end
44
+
45
+ alias eql? ==
46
+
47
+ def hash
48
+ self.class.hash ^ start_line.hash ^ start_column.hash ^ end_line.hash ^ end_column.hash
49
+ end
22
50
  end
23
51
  end
@@ -1,8 +1,8 @@
1
1
  module Goodcheck
2
2
  def self.logger
3
- @logger ||= ActiveSupport::TaggedLogging.new(Logger.new(STDERR)).tap do |logger|
4
- logger.push_tags VERSION
5
- logger.level = Logger::ERROR
6
- end
3
+ @logger ||= Logger.new(
4
+ STDERR, level: Logger::ERROR,
5
+ formatter: ->(severity, time, progname, msg) { "[#{severity}] #{msg}\n" }
6
+ )
7
7
  end
8
8
  end
@@ -44,6 +44,10 @@ module Goodcheck
44
44
  def issue(issue)
45
45
  issues << issue
46
46
  end
47
+
48
+ def summary
49
+ # noop
50
+ end
47
51
  end
48
52
  end
49
53
  end
@@ -5,6 +5,8 @@ module Goodcheck
5
5
 
6
6
  def initialize(stdout:)
7
7
  @stdout = stdout
8
+ @file_count = 0
9
+ @issue_count = 0
8
10
  end
9
11
 
10
12
  def analysis
@@ -12,6 +14,7 @@ module Goodcheck
12
14
  end
13
15
 
14
16
  def file(path)
17
+ @file_count += 1
15
18
  yield
16
19
  end
17
20
 
@@ -20,21 +23,49 @@ module Goodcheck
20
23
  end
21
24
 
22
25
  def issue(issue)
26
+ @issue_count += 1
27
+
28
+ message = issue.rule.message.lines.first.chomp
29
+
23
30
  if issue.location
24
- line = issue.buffer.line(issue.location.start_line)
25
- end_column = if issue.location.start_line == issue.location.end_line
26
- issue.location.end_column
27
- else
28
- line.bytesize
29
- end
30
- colored_line = line.byteslice(0, issue.location.start_column) + Rainbow(line.byteslice(issue.location.start_column, end_column - issue.location.start_column)).red + line.byteslice(end_column, line.bytesize)
31
- stdout.puts "#{issue.path}:#{issue.location.start_line}:#{colored_line.chomp}:\t#{issue.rule.message.lines.first.chomp}"
31
+ start_line = issue.location.start_line
32
+ start_column = issue.location.start_column
33
+ start_column_index = start_column - 1
34
+ line = issue.buffer.line(start_line)
35
+ column_size = if issue.location.one_line?
36
+ issue.location.column_size
37
+ else
38
+ line.bytesize - start_column
39
+ end
40
+ stdout.puts "#{Rainbow(issue.path).cyan}:#{start_line}:#{start_column}: #{message}"
41
+ stdout.puts line.chomp
42
+ stdout.puts (" " * start_column_index) + Rainbow("^" + "~" * (column_size - 1)).yellow
32
43
  else
33
- line = issue.buffer.line(1)&.chomp
34
- line = line ? Rainbow(line).red : '-'
35
- stdout.puts "#{issue.path}:-:#{line}:\t#{issue.rule.message.lines.first.chomp}"
44
+ stdout.puts "#{Rainbow(issue.path).cyan}:-:-: #{message}"
36
45
  end
37
46
  end
47
+
48
+ def summary
49
+ files = case @file_count
50
+ when 0
51
+ "no files"
52
+ when 1
53
+ "1 file"
54
+ else
55
+ "#{@file_count} files"
56
+ end
57
+ issues = case @issue_count
58
+ when 0
59
+ Rainbow("no issues").green
60
+ when 1
61
+ Rainbow("1 issue").red
62
+ else
63
+ Rainbow("#{@issue_count} issues").red
64
+ end
65
+
66
+ stdout.puts ""
67
+ stdout.puts "#{files} inspected, #{issues} detected"
68
+ end
38
69
  end
39
70
  end
40
71
  end
@@ -1,3 +1,3 @@
1
1
  module Goodcheck
2
- VERSION = "2.6.1".freeze
2
+ VERSION = "2.7.0".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goodcheck
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.1
4
+ version: 2.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soutaro Matsumoto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-19 00:00:00.000000000 Z
11
+ date: 2020-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -28,64 +28,58 @@ dependencies:
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '13.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
40
  version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '5.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: minitest-reporters
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 1.4.2
61
+ version: '1.4'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: 1.4.2
68
+ version: '1.4'
69
69
  - !ruby/object:Gem::Dependency
70
- name: activesupport
70
+ name: simplecov
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '4.0'
76
- - - "<"
77
- - !ruby/object:Gem::Version
78
- version: '7.0'
79
- type: :runtime
75
+ version: '0.18'
76
+ type: :development
80
77
  prerelease: false
81
78
  version_requirements: !ruby/object:Gem::Requirement
82
79
  requirements:
83
80
  - - ">="
84
81
  - !ruby/object:Gem::Version
85
- version: '4.0'
86
- - - "<"
87
- - !ruby/object:Gem::Version
88
- version: '7.0'
82
+ version: '0.18'
89
83
  - !ruby/object:Gem::Dependency
90
84
  name: strong_json
91
85
  requirement: !ruby/object:Gem::Requirement
@@ -110,16 +104,22 @@ dependencies:
110
104
  name: rainbow
111
105
  requirement: !ruby/object:Gem::Requirement
112
106
  requirements:
113
- - - "~>"
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '3.0'
110
+ - - "<"
114
111
  - !ruby/object:Gem::Version
115
- version: 3.0.0
112
+ version: '4.0'
116
113
  type: :runtime
117
114
  prerelease: false
118
115
  version_requirements: !ruby/object:Gem::Requirement
119
116
  requirements:
120
- - - "~>"
117
+ - - ">="
121
118
  - !ruby/object:Gem::Version
122
- version: 3.0.0
119
+ version: '3.0'
120
+ - - "<"
121
+ - !ruby/object:Gem::Version
122
+ version: '4.0'
123
123
  - !ruby/object:Gem::Dependency
124
124
  name: psych
125
125
  requirement: !ruby/object:Gem::Requirement