rubocop-schema-gen 0.1.1 → 0.1.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d13b27e5b62cc1a35d64acf5a81f2c6443aae1d0e9597207634bb7defa2a6b34
4
- data.tar.gz: 78ac1aadadf7a5f23700615d717262fa0601e54ac772f8a5f2a790fe3dcd37bf
3
+ metadata.gz: ad2887f7e9c6ed387de3ea9c44a785c2c846c351681226752d334bad5e42ac42
4
+ data.tar.gz: dd3787e2db023d2dfc5a391bbc628e9796dc63d4578e604d2bd0eb2f9fb86ca6
5
5
  SHA512:
6
- metadata.gz: 4293f202f9132881733e84b280e1b4ba93df2ab1b9567efb7ae48703835dc4d3e5aebb8a797f05453766e7929a3efe5c2c150b8d575611c6c959deeefc290a5c
7
- data.tar.gz: b61fcf727e203d2ca162eba0bd081c9bf008d8606774e8c16cd6727bd35c9e3f144606a375efdce1c115ae35001a04411cf70aebe09ecddd4cbf046b9ced3b6d
6
+ metadata.gz: 0cb67d04acc4d37ba1c8d4e05fb269b6f22be79b0928666c5e1639cb0d7840caacc3140c0320cead6fe311fe9aae3fd6cf9ff6474854f65ff3f07d28849204ab
7
+ data.tar.gz: 9f663797d40199ab878106996f4d6272c57c67f698ab9bb55fb222330c047330f49985eedd66cbcbd15acc0330e8da47dba38463f6039332d12d6fae2bc8514c
@@ -2,9 +2,13 @@ require 'pathname'
2
2
  require 'uri'
3
3
  require 'net/http'
4
4
 
5
+ require 'rubocop/schema/helpers'
6
+
5
7
  module RuboCop
6
8
  module Schema
7
9
  class CachedHTTPClient
10
+ include Helpers
11
+
8
12
  def initialize(cache_dir, &event_handler)
9
13
  @cache_dir = Pathname(cache_dir)
10
14
  @event_handler = event_handler
@@ -18,11 +22,9 @@ module RuboCop
18
22
  return path.read if path.readable?
19
23
 
20
24
  path.parent.mkpath
21
- @event_handler&.call Event.new(type: :request)
25
+ Event.dispatch type: :request, &@event_handler
22
26
 
23
- res = Net::HTTP.get_response(url)
24
- res.body = '' unless res.is_a? Net::HTTPOK
25
- res.body.force_encoding(Encoding::UTF_8).tap(&path.method(:write))
27
+ http_get(url).tap(&path.method(:write))
26
28
  end
27
29
 
28
30
  private
@@ -5,6 +5,7 @@ require 'rubocop/schema/document_loader'
5
5
  require 'rubocop/schema/cached_http_client'
6
6
  require 'rubocop/schema/generator'
7
7
  require 'rubocop/schema/extension_spec'
8
+ require 'rubocop/schema/repo'
8
9
 
9
10
  module RuboCop
10
11
  module Schema
@@ -22,19 +23,14 @@ module RuboCop
22
23
  @args = args
23
24
  @out_file = out_file
24
25
  @log_file = log_file
25
- @out_path = args.first
26
26
 
27
- raise ArgumentError, 'Cannot accept an out_file and an argument' if @out_file && @out_path
27
+ raise ArgumentError, 'Cannot accept an out_file and an argument' if @out_file && args.first
28
28
  end
29
29
 
30
30
  def run
31
- lockfile_path = @working_dir + 'Gemfile.lock'
32
- fail "Cannot read #{lockfile_path}" unless lockfile_path.readable?
31
+ read_flag while @args.first&.start_with?('--')
32
+ assign_outfile
33
33
 
34
- spec = ExtensionSpec.from_lockfile(lockfile_path)
35
- fail 'RuboCop is not part of this project' if spec.empty?
36
-
37
- assign_outfile(spec)
38
34
  print "Generating #{@out_path} … " if @out_path
39
35
 
40
36
  schema = report_duration(lowercase: @out_path) { Generator.new(spec.specs, document_loader).schema }
@@ -43,18 +39,55 @@ module RuboCop
43
39
 
44
40
  private
45
41
 
46
- def assign_outfile(spec)
42
+ def read_flag
43
+ case @args.shift
44
+ when '--version'
45
+ info VERSION
46
+ when '--spec'
47
+ info spec
48
+ when /\A--spec=(\S+)/
49
+ @spec = ExtensionSpec.from_string($1)
50
+ when /\A--build-repo=(.+)/
51
+ build_repo $1
52
+ end
53
+ end
54
+
55
+ def build_repo(dir)
56
+ Repo.new(dir, document_loader, &method(:handle_event)).build
57
+ exit
58
+ end
59
+
60
+ def spec
61
+ @spec ||=
62
+ begin
63
+ lockfile_path = @working_dir + 'Gemfile.lock'
64
+ fail "Cannot read #{lockfile_path}" unless lockfile_path.readable?
65
+
66
+ spec = ExtensionSpec.from_lockfile(lockfile_path)
67
+ fail 'RuboCop is not part of this project' if spec.empty?
68
+
69
+ spec
70
+ end
71
+ end
72
+
73
+ def assign_outfile
47
74
  return if @out_file
48
75
 
49
- case @out_path
76
+ @out_path = path_from_arg(@args.first)
77
+
78
+ @out_file ||= File.open(@out_path, 'w') # rubocop:disable Naming/MemoizedInstanceVariableName
79
+ end
80
+
81
+ def path_from_arg(arg)
82
+ case arg
50
83
  when '-'
51
84
  @out_file = $stdout
52
- @out_path = nil
85
+ nil
53
86
  when nil
54
- @out_path = "#{spec}-config-schema.json"
87
+ "#{spec}-config-schema.json"
88
+ else
89
+ arg
55
90
  end
56
-
57
- @out_file ||= File.open(@out_path, 'w') # rubocop:disable Naming/MemoizedInstanceVariableName
58
91
  end
59
92
 
60
93
  def report_duration(lowercase: false)
@@ -79,6 +112,11 @@ module RuboCop
79
112
  end
80
113
  end
81
114
 
115
+ def info(msg)
116
+ $stdout.puts msg
117
+ exit
118
+ end
119
+
82
120
  def fail(msg)
83
121
  @log_file.puts msg.to_s
84
122
  exit 1
@@ -0,0 +1,67 @@
1
+ require 'rubocop/schema/helpers'
2
+
3
+ module RuboCop
4
+ module Schema
5
+ class Diff
6
+ include Helpers
7
+
8
+ class << self
9
+ def instance
10
+ @instance ||= new
11
+ end
12
+
13
+ def diff(old, new)
14
+ instance.diff old, new
15
+ end
16
+
17
+ def apply(old, diff)
18
+ instance.apply old, diff
19
+ end
20
+ end
21
+
22
+ def diff(old, new)
23
+ return diff_hashes old, new if old.is_a?(Hash) && new.is_a?(Hash)
24
+
25
+ new
26
+ end
27
+
28
+ def apply(old, diff)
29
+ return apply_hash(old, diff) if old.is_a?(Hash) && diff.is_a?(Hash)
30
+
31
+ diff
32
+ end
33
+
34
+ private
35
+
36
+ def diff_hashes(old, new)
37
+ (old.keys - new.keys).map { |k| [k, nil] }.to_h.tap do |result|
38
+ new.each do |k, v|
39
+ if old.key? k
40
+ result[k] = diff(old[k], v) unless old[k] == v
41
+ else
42
+ result[k] = v
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def apply_hash(old, diff)
49
+ deep_dup(old).tap do |result|
50
+ diff.each do |k, v|
51
+ apply_hash_pair result, k, v
52
+ end
53
+ end
54
+ end
55
+
56
+ def apply_hash_pair(hash, key, value)
57
+ if value.nil?
58
+ hash.delete key
59
+ elsif hash.key? key
60
+ hash[key] = apply(hash[key], value)
61
+ else
62
+ hash[key] = value
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -1,10 +1,19 @@
1
+ require 'asciidoctor'
2
+
1
3
  module RuboCop
2
4
  module Schema
3
5
  class DocumentLoader
4
6
  DOCS_URL_TEMPLATE =
5
- -'https://raw.githubusercontent.com/rubocop/%s/v%s/docs/modules/ROOT/pages/cops%s.adoc'
7
+ -'https://raw.githubusercontent.com/rubocop/%s/%s/docs/modules/ROOT/pages/cops%s.adoc'
6
8
  DEFAULTS_URL_TEMPLATE =
7
- -'https://raw.githubusercontent.com/rubocop/%s/v%s/config/default.yml'
9
+ -'https://raw.githubusercontent.com/rubocop/%s/%s/config/default.yml'
10
+
11
+ CORRECTIONS = {
12
+ 'rubocop' => {
13
+ # Fixes a typo that causes Asciidoctor to crash
14
+ '1.10.0' => '174bda389c2c23cffb17e9d6128f5e6bdbc0e8a0'
15
+ }
16
+ }.freeze
8
17
 
9
18
  # @param [CachedHTTPClient] http_client
10
19
  def initialize(http_client)
@@ -30,11 +39,15 @@ module RuboCop
30
39
  private
31
40
 
32
41
  def url_for_doc(spec, department)
33
- format DOCS_URL_TEMPLATE, spec.name, spec.version, department && "_#{department.to_s.downcase}"
42
+ format DOCS_URL_TEMPLATE, spec.name, correct_version(spec), department && "_#{department.to_s.downcase}"
34
43
  end
35
44
 
36
45
  def url_for_defaults(spec)
37
- format DEFAULTS_URL_TEMPLATE, spec.name, spec.version
46
+ format DEFAULTS_URL_TEMPLATE, spec.name, correct_version(spec)
47
+ end
48
+
49
+ def correct_version(spec)
50
+ CORRECTIONS.dig(spec.name, spec.version) || "v#{spec.version}"
38
51
  end
39
52
  end
40
53
  end
@@ -28,16 +28,24 @@ module RuboCop
28
28
  end.compact)
29
29
  end
30
30
 
31
+ # @param [Pathname] lockfile
31
32
  def self.from_lockfile(lockfile)
32
- new(Bundler::LockfileParser.new(lockfile.to_s).sources.flat_map do |source|
33
- source.specs.map do |stub|
34
- next unless KNOWN_GEMS.include? stub.name
35
-
36
- Spec.new(
37
- name: stub.name,
38
- version: stub.version.to_s
39
- )
40
- end.compact
33
+ new(lockfile.readlines.map do |line|
34
+ next unless line =~ /\A\s+(rubocop(?:-\w+)?) \((\d+(?:\.\d+)+)\)\s*\z/
35
+ next unless KNOWN_GEMS.include? $1
36
+
37
+ Spec.new(name: $1, version: $2)
38
+ end.compact)
39
+ end
40
+
41
+ def self.from_string(string)
42
+ new(string.split('-').each_slice(2).map do |(name, version)|
43
+ name = "rubocop-#{name}" unless name == 'rubocop'
44
+
45
+ raise ArgumentError, "Unknown gem '#{name}'" unless KNOWN_GEMS.include? name
46
+ raise ArgumentError, "Invalid version '#{version}'" unless version&.match? /\A\d+(?:\.\d+)+\z/
47
+
48
+ Spec.new(name: name, version: version)
41
49
  end)
42
50
  end
43
51
 
@@ -1,6 +1,3 @@
1
- require 'asciidoctor'
2
- require 'nokogiri'
3
-
4
1
  require 'rubocop/schema/value_objects'
5
2
  require 'rubocop/schema/cop_schema'
6
3
  require 'rubocop/schema/helpers'
@@ -32,6 +29,7 @@ module RuboCop
32
29
 
33
30
  def generate
34
31
  @specs.each &method(:generate_spec)
32
+ @props.delete 'AllCops' unless @specs.any? { |s| s.name == 'rubocop' }
35
33
  end
36
34
 
37
35
  def generate_spec(spec)
@@ -1,3 +1,6 @@
1
+ require 'nokogiri'
2
+ require 'uri'
3
+
1
4
  module RuboCop
2
5
  module Schema
3
6
  module Helpers
@@ -46,6 +49,13 @@ module RuboCop
46
49
  def strip_html(str)
47
50
  Nokogiri::HTML(str).text
48
51
  end
52
+
53
+ def http_get(url)
54
+ url = URI(url)
55
+ res = Net::HTTP.get_response(url)
56
+ res.body = '' unless res.is_a? Net::HTTPOK
57
+ res.body.force_encoding Encoding::UTF_8
58
+ end
49
59
  end
50
60
  end
51
61
  end
@@ -0,0 +1,79 @@
1
+ require 'json'
2
+
3
+ require 'rubocop/schema/helpers'
4
+ require 'rubocop/schema/diff'
5
+
6
+ module RuboCop
7
+ module Schema
8
+ class Repo
9
+ include Helpers
10
+
11
+ TAGS_URL_TEMPLATE = -'https://api.github.com/repos/rubocop/%s/tags'
12
+
13
+ def initialize(dir, loader, &event_handler)
14
+ @dir = Pathname(dir)
15
+ @loader = loader
16
+ @event_handler = event_handler
17
+ @dir.mkpath
18
+ end
19
+
20
+ def build
21
+ ExtensionSpec::KNOWN_GEMS.each &method(:build_for_gem)
22
+ Event.dispatch message: "Repo updated: #{@dir}", &@event_handler
23
+ end
24
+
25
+ private
26
+
27
+ def build_for_gem(name)
28
+ existing = read(name).map { |h| [h['version'], h['diff']] }.to_h
29
+ previous = nil
30
+ body = versions_of(name).map do |version|
31
+ previous, diff = fetch_for_spec(Spec.new(name: name, version: version), existing, previous)
32
+ {
33
+ 'version' => version,
34
+ 'diff' => diff
35
+ }
36
+ end
37
+ write name, body.compact
38
+ end
39
+
40
+ def fetch_for_spec(spec, existing, previous)
41
+ if existing.key? spec.version
42
+ diff = existing[spec.version]
43
+ schema = Diff.apply(previous, diff)
44
+ else
45
+ schema = build_for_spec(spec)
46
+ diff = Diff.diff(previous, schema)
47
+ end
48
+ [schema, diff]
49
+ end
50
+
51
+ def build_for_spec(spec)
52
+ Event.dispatch message: "Generating: #{spec}", &@event_handler
53
+ Generator.new([spec], @loader).schema
54
+ end
55
+
56
+ def write(name, body)
57
+ path_for(name).binwrite JSON.pretty_generate body
58
+ end
59
+
60
+ def read(name)
61
+ path = path_for(name)
62
+ return [] unless path.exist?
63
+
64
+ JSON.parse path.read
65
+ end
66
+
67
+ def path_for(name)
68
+ @dir.join("#{name}.json")
69
+ end
70
+
71
+ def versions_of(name)
72
+ json = http_get(format(TAGS_URL_TEMPLATE, name))
73
+ raise "No tags available for #{name}" if json == ''
74
+
75
+ JSON.parse(json).reverse.map { |obj| obj['name'].to_s[/(?<=\Av)\d.+/] }.compact
76
+ end
77
+ end
78
+ end
79
+ end
@@ -2,8 +2,14 @@ module RuboCop
2
2
  module Schema
3
3
  CopInfo = Struct.new(:name, :description, :attributes, :supports_autocorrect, :enabled_by_default)
4
4
  Attribute = Struct.new(:name, :type, :default)
5
- Event = Struct.new(:type, :message)
6
- Spec = Struct.new(:name, :version) do
5
+
6
+ Event = Struct.new(:type, :message) do
7
+ def self.dispatch(**kwargs)
8
+ yield new(**kwargs) if block_given?
9
+ end
10
+ end
11
+
12
+ Spec = Struct.new(:name, :version) do
7
13
  def short_name
8
14
  return nil if name == 'rubocop'
9
15
 
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module Schema
3
- VERSION = -'0.1.1'
3
+ VERSION = -'0.1.2'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-schema-gen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil E. Pearson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-04-30 00:00:00.000000000 Z
11
+ date: 2021-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -60,21 +60,10 @@ executables:
60
60
  extensions: []
61
61
  extra_rdoc_files: []
62
62
  files:
63
- - ".gitignore"
64
- - ".rspec"
65
- - ".rubocop.yml"
66
- - ".ruby-version"
67
- - ".travis.yml"
68
- - CODE_OF_CONDUCT.md
69
- - Gemfile
70
63
  - LICENSE
71
- - LICENSE.txt
72
64
  - README.md
73
- - Rakefile
74
65
  - assets/templates/cop_schema.yml
75
66
  - assets/templates/schema.yml
76
- - bin/console
77
- - bin/setup
78
67
  - exe/rubocop-schema-gen
79
68
  - lib/rubocop/schema.rb
80
69
  - lib/rubocop/schema/ascii_doc/base.rb
@@ -87,14 +76,14 @@ files:
87
76
  - lib/rubocop/schema/cop_info_merger.rb
88
77
  - lib/rubocop/schema/cop_schema.rb
89
78
  - lib/rubocop/schema/defaults_ripper.rb
79
+ - lib/rubocop/schema/diff.rb
90
80
  - lib/rubocop/schema/document_loader.rb
91
81
  - lib/rubocop/schema/extension_spec.rb
92
82
  - lib/rubocop/schema/generator.rb
93
83
  - lib/rubocop/schema/helpers.rb
84
+ - lib/rubocop/schema/repo.rb
94
85
  - lib/rubocop/schema/value_objects.rb
95
86
  - lib/rubocop/schema/version.rb
96
- - rubocop-schema.gemspec
97
- - rubocop-schema.json
98
87
  homepage: https://github.com/hx/rubocop-schema
99
88
  licenses:
100
89
  - MIT
@@ -117,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
106
  - !ruby/object:Gem::Version
118
107
  version: '0'
119
108
  requirements: []
120
- rubygems_version: 3.2.3
109
+ rubygems_version: 3.0.8
121
110
  signing_key:
122
111
  specification_version: 4
123
112
  summary: Generate JSON schemas for IDE integration with RuboCop