falkor 0.1.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.
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Falkor
4
+ module Extract
5
+ class Gem
6
+ def initialize(file_name)
7
+ @file_name = file_name
8
+ end
9
+
10
+ def extract
11
+ ::Gem::Package::FileSource.new(file_name).with_read_io do |io|
12
+ ::Gem::Package::TarReader.new(io).each do |entry|
13
+ next if entry.full_name != "data.tar.gz"
14
+
15
+ write_tar_file(entry.read)
16
+
17
+ remove_file
18
+ break # ignore further entries
19
+ end
20
+ end
21
+
22
+ tar_file_name
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :file_name
28
+
29
+ def write_tar_file(contents)
30
+ File.open(tar_file_name, "wb") do |file|
31
+ file.write contents
32
+ end
33
+ end
34
+
35
+ def tar_file_name
36
+ File.join(
37
+ File.dirname(file_name), File.basename(file_name, ".gem") + ".tar.gz"
38
+ )
39
+ end
40
+
41
+ def remove_file
42
+ FileUtils.rm file_name
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems/package"
4
+ require "falkor/concerns/trackable_progress"
5
+
6
+ module Falkor
7
+ module Extract
8
+ class TarGz
9
+ include TrackableProgress
10
+
11
+ TAR_LONGLINK = "././@LongLink"
12
+
13
+ def initialize(file_name, has_root_dir: false)
14
+ @file_name = file_name
15
+ @extraction_destination =
16
+ if has_root_dir
17
+ File.dirname(file_name)
18
+ else
19
+ source_destination
20
+ end
21
+ end
22
+
23
+ def extract
24
+ return source_destination if Dir.exist? source_destination
25
+
26
+ FileUtils.mkdir_p extraction_destination
27
+
28
+ block = block_given? ? Proc.new : proc {}
29
+
30
+ report_progress(:write_each_tarfile, open_file(&:count), &block)
31
+ FileUtils.rm file_name
32
+ source_destination
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :file_name, :extraction_destination
38
+
39
+ def source_destination
40
+ @source_destination ||= File.join(
41
+ File.dirname(file_name), File.basename(file_name, ".tar.gz")
42
+ )
43
+ end
44
+
45
+ def write_each_tarfile
46
+ open_file do |tar|
47
+ current_destination = nil
48
+ tar.each do |tarfile|
49
+ current_destination =
50
+ handle_longlink_or_write(tarfile, current_destination)
51
+ yield(1)
52
+ end
53
+ end
54
+ end
55
+
56
+ def open_file
57
+ File.open(file_name, "rb") do |file|
58
+ return_value = nil
59
+ Zlib::GzipReader.wrap(file) do |gz|
60
+ ::Gem::Package::TarReader.new(gz) do |tar|
61
+ return_value = yield tar
62
+ end
63
+ end
64
+ return_value
65
+ end
66
+ end
67
+
68
+ def write_tarfile(tarfile, current_destination)
69
+ current_destination ||=
70
+ File.join extraction_destination, tarfile.full_name
71
+
72
+ if directory?(tarfile)
73
+ write_directory(tarfile, current_destination)
74
+ elsif file?(tarfile)
75
+ write_file(tarfile, current_destination)
76
+ elsif tarfile.header.typeflag == "2" # Symlink
77
+ File.symlink tarfile.header.linkname, current_destination
78
+ end
79
+ nil
80
+ end
81
+
82
+ def directory?(file)
83
+ file.directory? || file.full_name.end_with?("/")
84
+ end
85
+
86
+ def file?(file)
87
+ file.file? || !file.full_name.end_with?("/")
88
+ end
89
+
90
+ def write_directory(file, dest)
91
+ File.delete dest if File.file? dest
92
+ FileUtils.mkdir_p dest, mode: file.header.mode, verbose: false
93
+ end
94
+
95
+ def write_file(file, dest)
96
+ if File.directory? dest
97
+ FileUtils.rm_rf dest
98
+ else
99
+ FileUtils.mkdir_p(File.dirname(dest))
100
+ end
101
+
102
+ File.open dest, "wb" do |f|
103
+ f.print file.read
104
+ end
105
+ FileUtils.chmod file.header.mode, dest, verbose: false
106
+ end
107
+
108
+ def handle_longlink_or_write(tarfile, dest)
109
+ if tarfile.full_name == TAR_LONGLINK
110
+ File.join extraction_destination, tarfile.read.strip
111
+ else
112
+ write_tarfile(tarfile, dest)
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "falkor/concerns/installable"
4
+ require "gems"
5
+
6
+ module Falkor
7
+ class Gem
8
+ include Installable
9
+
10
+ attr_reader :name, :info, :version, :created_at, :project_uri
11
+
12
+ class << self
13
+ def search(query)
14
+ return [] if query.nil? || query.empty?
15
+
16
+ Gems.search(query).map do |gem|
17
+ new(**gem.transform_keys(&:to_sym))
18
+ end
19
+ end
20
+
21
+ def find(query, version = nil)
22
+ gem_info =
23
+ if version.nil?
24
+ Gems.info(query)
25
+ else
26
+ rubygems_v2_info(query, version)
27
+ end
28
+
29
+ new(**gem_info.transform_keys(&:to_sym))
30
+ end
31
+
32
+ private
33
+
34
+ def rubygems_v2_info(gem_name, version)
35
+ response = Gems::Client.new.get(
36
+ "/api/v2/rubygems/#{gem_name}/versions/#{version}.json"
37
+ )
38
+ JSON.parse(response)
39
+ rescue JSON::ParserError
40
+ {}
41
+ end
42
+ end
43
+
44
+ def initialize(**attrs)
45
+ @name = attrs[:name]
46
+ @info = attrs[:info]
47
+ @created_at = attrs[:created_at] && Time.parse(attrs[:created_at])
48
+ @project_uri = attrs[:homepage_uri]
49
+ @version = ::Gem::Version.new(attrs[:version])
50
+ end
51
+
52
+ def other_versions
53
+ @other_versions ||= Gems.versions(name).map do |payload|
54
+ next if payload["number"] == version
55
+
56
+ self.class.new(
57
+ name: name,
58
+ info: payload["summary"],
59
+ version: payload["number"],
60
+ created_at: payload["created_at"]
61
+ )
62
+ end.compact
63
+ end
64
+
65
+ %i(root class method module constant classvariable macro).each do |type|
66
+ define_method("#{type}_objects") do
67
+ store.values_for_type(type)
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def file_name
74
+ "#{name}-#{version}.gem"
75
+ end
76
+
77
+ def url
78
+ "https://rubygems.org/gems/#{file_name}"
79
+ end
80
+
81
+ def yard_filepath
82
+ File.join(Dir.pwd, "tmp", File.basename(file_name, ".gem") + ".falkor")
83
+ end
84
+
85
+ def extract(file_path)
86
+ Falkor::Extract::TarGz.new(
87
+ Falkor::Extract::Gem.new(file_path).extract
88
+ ).extract do |progress|
89
+ yield :extracting, progress
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Falkor
4
+ class Progress
5
+ def initialize(total)
6
+ @total = total
7
+ @current = 0
8
+ @previous = 0
9
+ end
10
+
11
+ def increment!(amount, description)
12
+ self.previous, self.current = current, current + amount
13
+
14
+ return if percentage(previous) == percentage(current) && !description
15
+
16
+ yield percentage(current), description
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :total
22
+ attr_accessor :current, :previous
23
+
24
+ def percentage(amount)
25
+ (amount.to_f / total * 100).to_i
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "falkor/concerns/installable"
4
+
5
+ module Falkor
6
+ class Ruby
7
+ class NotFound < StandardError; end
8
+
9
+ include Installable
10
+
11
+ RELEASES = "https://raw.githubusercontent.com/ruby/www.ruby-lang.org/"\
12
+ "master/_data/releases.yml"
13
+
14
+ class << self
15
+ def search(query)
16
+ versions.keys.map do |version|
17
+ new(version) if version.include? query
18
+ end.compact
19
+ end
20
+
21
+ alias :find :new
22
+
23
+ def versions
24
+ YAML.
25
+ load_file(Download.new(RELEASES, "ruby_releases.yml").download {}).
26
+ select { |release| release.dig("url", "gz") }.
27
+ map { |release| [release["version"], release.dig("url", "gz")] }.
28
+ to_h
29
+ end
30
+ end
31
+
32
+ def initialize(version)
33
+ raise NotFound if self.class.versions[version].nil?
34
+
35
+ @version = version
36
+ end
37
+
38
+ def other_versions
39
+ self.class.versions.keys.map do |number|
40
+ next if number == version
41
+
42
+ self.class.new(number)
43
+ end.compact
44
+ end
45
+
46
+ %i(root class method module constant classvariable macro).each do |type|
47
+ define_method("#{type}_objects") do
48
+ store.values_for_type(type)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ attr_reader :version
55
+
56
+ def file_name
57
+ "ruby-#{version}.tar.gz"
58
+ end
59
+
60
+ def url
61
+ self.class.versions[version]
62
+ end
63
+
64
+ def yard_filepath
65
+ File.join(Dir.pwd, "tmp", File.basename(file_name, ".tar.gz") + ".falkor")
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Falkor
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "falkor/concerns/trackable_progress"
4
+ require "falkor/yard/parser"
5
+ require "yard"
6
+
7
+ module Falkor
8
+ module Yard
9
+ class Documentation
10
+ include TrackableProgress
11
+
12
+ FILE_GLOB =
13
+ YARD::Parser::SourceParser::DEFAULT_PATH_GLOB + ["*.c", "ext/**/*.rb"]
14
+
15
+ def initialize(source_dir, yardoc_file = ".yardoc")
16
+ @source_dir = source_dir
17
+ @yardoc_file = yardoc_file
18
+ end
19
+
20
+ def generate
21
+ in_source_dir do
22
+ with_yardoc_file do
23
+ YARD::Registry.lock_for_writing do
24
+ report_progress(:parse_files, files.size, &Proc.new)
25
+ YARD::Registry.save(true)
26
+ end
27
+ end
28
+ end
29
+
30
+ yardoc_file
31
+ end
32
+
33
+ private
34
+
35
+ attr_accessor :source_dir, :yardoc_file
36
+
37
+ def files
38
+ @files ||=
39
+ FILE_GLOB.map do |path|
40
+ path = "#{path}/**/*.{rb,c,cc,cxx,cpp}" if File.directory?(path)
41
+ path = Dir[path].sort_by { |d| [d.length, d] } if path.include?("*")
42
+
43
+ path
44
+ end.flatten.select { |p| File.file?(p) }
45
+ end
46
+
47
+ def in_source_dir
48
+ Dir.chdir(source_dir) { yield }
49
+ end
50
+
51
+ def with_yardoc_file
52
+ YARD::Registry.clear
53
+ YARD::Logger.instance.io = IO.new(IO.sysopen("/dev/null", "w+"))
54
+ YARD::Registry.yardoc_file = yardoc_file
55
+ yield
56
+ YARD::Registry.yardoc_file = nil
57
+ YARD::Logger.instance.io = STDOUT
58
+ end
59
+
60
+ def parse_files
61
+ Parser.new(nil, files).parse do |count, description|
62
+ yield count, description
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "falkor/yard/parser/global_state"
4
+ require "yard"
5
+
6
+ module Falkor
7
+ module Yard
8
+ class Parser
9
+ attr_accessor :files, :global_state
10
+
11
+ def initialize(global_state, files)
12
+ @global_state = global_state || GlobalState.new(self, 0)
13
+
14
+ @files = files.dup
15
+ end
16
+
17
+ def parse
18
+ global_state.block = Proc.new if block_given?
19
+
20
+ until files.empty?
21
+ file = files.shift
22
+ YARD::Parser::SourceParser.new(
23
+ YARD::Parser::SourceParser.parser_type, global_state
24
+ ).parse(file)
25
+
26
+ global_state.processed(file)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end