rubocop-schema-gen 0.1.1 → 0.1.2

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: 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