type_fusion 0.0.3 → 0.0.5

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: 7fd6c97b4b4bb6eeecea4ff23e108c24a7564ac3d85e2231352824cd27bf160e
4
- data.tar.gz: a438ca7330e335d8d1922c967a24ef46a23403d6825e0e7a4740aafa6e93dcf6
3
+ metadata.gz: ba90818fd8e50f24a1b0049c0a8076a22b2e168a0fcafff53fb425b858b6323a
4
+ data.tar.gz: '011790f72f4d7e17fe12a8a1443f71e4bbdc42d4308f02c8110d5b07a3e91825'
5
5
  SHA512:
6
- metadata.gz: 48d5ea9c6b6c52e8fe44bd8b1ea295f96ff5e48e737985b7c75d21b5c8379d2934b8dfe6e0fa41acfc85aa4104427a7e86354ebed07823d81e37c85a5060d7e3
7
- data.tar.gz: 54d0eae75e1b154a249ddf5eecf6facf7694d3fc43d0ea3e3c022e7225a0510f7e2d58758fbbba885f6b45c16f35236653054e3e1d9ea199b6dfb16696b88fa3
6
+ metadata.gz: 23e4e3de6448d1f0f1a982ed90d73fec09b74d10b89e037bb6c477c24c8576bb42087a3dc112b612e41f81029e364472ebe498fd83258dc7da4f368c44866a26
7
+ data.tar.gz: 933b4a204122078dcc8a939daa35aae1648cb73ca06ec08231aa982be790adabc8075725ac715782873c84e8c657a3b6f12db9759b1fe32008dbde3557f70148
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.0.5] - 2023-08-25
4
+
5
+ - Introduce `TypeFusion::Processor` for processing samples
6
+ - Add support for running TypeFusion in the test environment with Minitest
7
+ - Rescue and log when job failed to enqueue
8
+ - Add Rake task for manually processing enqueued jobs
9
+
10
+ ## [0.0.4] - 2023-08-16
11
+
12
+ - Implement sample collection by sending samples to gem.sh endpoint
13
+ - Introduce Litejob to enqueue samples to be collected async
14
+ - Updates to `SampleCall`
15
+ - Split up `gem_and_version` to `gem` and `version` in `SampleCall`
16
+ - Also collect `application_name` and `type_fusion_version` with `SampleCall`
17
+ - Change `Tracepoint` API from `:call` to `:return` to also collect `return_value` in `SampleCall`
18
+ - Introduce `endpoint` and `application_name` configs
19
+
3
20
  ## [0.0.3] - 2023-08-13
4
21
 
5
22
  - Introduce `TypeFusion::Middleware` to allow type-sampling in Rack-powered apps
data/Gemfile CHANGED
@@ -6,6 +6,4 @@ gemspec
6
6
 
7
7
  gem "minitest", "~> 5.0"
8
8
  gem "rake", "~> 13.0"
9
- gem "rubocop", "~> 1.21"
10
-
11
- gem "litestack", github: "marcoroth/litestack"
9
+ gem "rubocop", "~> 1.56"
data/Gemfile.lock CHANGED
@@ -1,54 +1,69 @@
1
- GIT
2
- remote: https://github.com/marcoroth/litestack.git
3
- revision: 1911d5ef35f474425fb4e17a11a7d40deb4008a8
4
- specs:
5
- litestack (0.2.6)
6
- erubi
7
- hanami-router
8
- oj
9
- rack
10
- sqlite3
11
- tilt
12
-
13
1
  PATH
14
2
  remote: .
15
3
  specs:
16
- type_fusion (0.0.3)
17
- litestack
4
+ type_fusion (0.0.5)
5
+ lhc (~> 15.2)
6
+ litejob (~> 0.2.3)
18
7
 
19
8
  GEM
20
9
  remote: https://rubygems.org/
21
10
  specs:
11
+ activesupport (7.0.7.2)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (>= 1.6, < 2)
14
+ minitest (>= 5.1)
15
+ tzinfo (~> 2.0)
16
+ addressable (2.8.5)
17
+ public_suffix (>= 2.0.2, < 6.0)
22
18
  ast (2.4.2)
23
- erubi (1.12.0)
24
- hanami-router (0.6.2)
25
- hanami-utils (~> 0.7)
26
- http_router (~> 0.11)
27
- hanami-utils (0.9.2)
28
- http_router (0.11.2)
29
- rack (>= 1.0.0)
30
- url_mount (~> 0.2.1)
19
+ base64 (0.1.1)
20
+ concurrent-ruby (1.2.2)
21
+ ethon (0.16.0)
22
+ ffi (>= 1.15.0)
23
+ ffi (1.15.5)
24
+ i18n (1.14.1)
25
+ concurrent-ruby (~> 1.0)
31
26
  json (2.6.3)
27
+ language_server-protocol (3.17.0.3)
28
+ lhc (15.2.1)
29
+ activesupport (>= 5.2)
30
+ addressable
31
+ local_uri
32
+ typhoeus (>= 0.11)
33
+ litedb (0.2.1)
34
+ litescheduler (>= 0.2.0)
35
+ sqlite3 (>= 1.5.0)
36
+ litejob (0.2.3)
37
+ litequeue (>= 0.2.1)
38
+ litescheduler (>= 0.2.1)
39
+ litequeue (0.2.1)
40
+ litedb (>= 0.2.1)
41
+ litescheduler (0.2.1)
42
+ local_uri (1.2.0)
43
+ activesupport
44
+ rack
32
45
  minitest (5.19.0)
33
- oj (3.15.1)
34
46
  parallel (1.23.0)
35
47
  parser (3.2.2.3)
36
48
  ast (~> 2.4.1)
37
49
  racc
50
+ public_suffix (5.0.3)
38
51
  racc (1.7.1)
39
52
  rack (3.0.8)
40
53
  rainbow (3.1.1)
41
54
  rake (13.0.6)
42
55
  regexp_parser (2.8.1)
43
56
  rexml (3.2.6)
44
- rubocop (1.48.1)
57
+ rubocop (1.56.1)
58
+ base64 (~> 0.1.1)
45
59
  json (~> 2.3)
60
+ language_server-protocol (>= 3.17.0)
46
61
  parallel (~> 1.10)
47
- parser (>= 3.2.0.0)
62
+ parser (>= 3.2.2.3)
48
63
  rainbow (>= 2.2.2, < 4.0)
49
64
  regexp_parser (>= 1.8, < 3.0)
50
65
  rexml (>= 3.2.5, < 4.0)
51
- rubocop-ast (>= 1.26.0, < 2.0)
66
+ rubocop-ast (>= 1.28.1, < 2.0)
52
67
  ruby-progressbar (~> 1.7)
53
68
  unicode-display_width (>= 2.4.0, < 3.0)
54
69
  rubocop-ast (1.29.0)
@@ -56,20 +71,20 @@ GEM
56
71
  ruby-progressbar (1.13.0)
57
72
  sqlite3 (1.6.3-x86_64-darwin)
58
73
  sqlite3 (1.6.3-x86_64-linux)
59
- tilt (2.2.0)
74
+ typhoeus (1.4.0)
75
+ ethon (>= 0.9.0)
76
+ tzinfo (2.0.6)
77
+ concurrent-ruby (~> 1.0)
60
78
  unicode-display_width (2.4.2)
61
- url_mount (0.2.1)
62
- rack
63
79
 
64
80
  PLATFORMS
65
81
  x86_64-darwin-22
66
82
  x86_64-linux
67
83
 
68
84
  DEPENDENCIES
69
- litestack!
70
85
  minitest (~> 5.0)
71
86
  rake (~> 13.0)
72
- rubocop (~> 1.21)
87
+ rubocop (~> 1.56)
73
88
  type_fusion!
74
89
 
75
90
  BUNDLED WITH
data/README.md CHANGED
@@ -27,6 +27,24 @@ use TypeFusion::Middleware
27
27
  Adding the gem to your applications Gemfile will automatically setup `type_fusion`.
28
28
 
29
29
 
30
+ #### Minitest
31
+
32
+ If you only want to run TypeFusion in your test environment you can require `type_fusion/minitest` in your `test_helper.rb`:
33
+
34
+ ```diff
35
+ # test/test_helper.rb
36
+
37
+ ENV["RAILS_ENV"] ||= "test"
38
+ require_relative "../config/environment"
39
+ require "rails/test_help"
40
+
41
+ +require "type_fusion/minitest"
42
+
43
+ class ActiveSupport::TestCase
44
+ # ...
45
+ end
46
+ ```
47
+
30
48
  ## Configuration
31
49
 
32
50
  Setup `TypeFusion` in an initializer
@@ -38,6 +56,27 @@ require "type_fusion"
38
56
 
39
57
  TypeFusion.config do |config|
40
58
 
59
+ # === application_name
60
+ #
61
+ # Set application_name to a string which is used to know where the samples
62
+ # came from. Set application_name to an empty string if you wish to not
63
+ # send the application name alongside the samples.
64
+ #
65
+ # Default: "TypeFusion"
66
+ # Default when using Rails: Rails.application.class.module_parent_name
67
+ #
68
+ # config.application_name = "YourApplication"
69
+
70
+
71
+ # === endpoint
72
+ #
73
+ # Set endpoint to an URL where TypeFusion should send the samples to.
74
+ #
75
+ # Default: "https://gem.sh/api/v1/types/samples"
76
+ #
77
+ # config.endpoint = "https://your-domain.com/api/v1/types/samples"
78
+
79
+
41
80
  # === type_sample_request
42
81
  #
43
82
  # Set type_sample_request to a lambda which resolves to true/false
@@ -54,13 +93,16 @@ TypeFusion.config do |config|
54
93
  # to true/false to check if a tracepoint_path should be sampled
55
94
  # or not.
56
95
  #
57
- # This can be useful when you only want to sample method calls for
96
+ # This can be useful when you want to only sample method calls for
58
97
  # certain gems or want to exclude a gem from being sampled.
59
98
  #
60
99
  # Example:
61
100
  # config.type_sample_tracepoint_path = ->(tracepoint_path) {
62
- # # only sample calls for the Nokogiri gem
63
- # tracepoint_path.include?("nokogiri")
101
+ # return false if tracepoint_path.include?("activerecord")
102
+ # return false if tracepoint_path.include?("sprockets")
103
+ # return false if tracepoint_path.include?("some-private-gem")
104
+ #
105
+ # true
64
106
  # }
65
107
  #
66
108
  # Default: ->(tracepoint_path) { true }
@@ -110,19 +152,20 @@ TypeFusion::Sampler.instance.samples
110
152
  TypeFusion::Sampler.instance.samples.first
111
153
 
112
154
  # => #<struct TypeFusion::SampleCall
113
- # gem_and_version="nokogiri-1.15.4-x86_64-darwin",
155
+ # gem_name="nokogiri"
156
+ # gem_version="1.15.4-x86_64-darwin",
114
157
  # receiver="Nokogiri",
115
158
  # method_name=:parse,
116
- # location=[
117
- # "/Users/marcoroth/.anyenv/envs/rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/nokogiri-1.15.4-x86_64-darwin/lib/nokogiri.rb",
118
- # 43
119
- # ],
159
+ # application_name="TypeFusion",
160
+ # location="/Users/marcoroth/.anyenv/envs/rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/nokogiri-1.15.4-x86_64-darwin/lib/nokogiri.rb:43",
161
+ # type_fusion_version="0.0.4",
120
162
  # parameters=[
121
163
  # [:string, :req, String],
122
164
  # [:url, :opt, NilClass],
123
165
  # [:encoding, :opt, NilClass],
124
166
  # [:options, :opt, NilClass]
125
- # ]
167
+ # ],
168
+ # return_value="Nokogiri::XML::Document"
126
169
  # >
127
170
  ```
128
171
 
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :type_fusion do
4
+ desc "Process enqueued type samples"
5
+ task :process do
6
+ TypeFusion.processor.process!
7
+ end
8
+ end
@@ -11,17 +11,20 @@ module TypeFusion
11
11
 
12
12
  @config
13
13
  end
14
+ alias configure config
14
15
  end
15
16
 
16
17
  class Config
17
18
  include Singleton
18
19
 
19
- attr_accessor :type_sample_call_rate, :type_sample_request, :type_sample_tracepoint_path
20
+ attr_accessor :type_sample_call_rate, :type_sample_request, :type_sample_tracepoint_path, :endpoint, :application_name
20
21
 
21
22
  def initialize
22
23
  @type_sample_call_rate = 0.001
23
24
  @type_sample_request = ->(_env) { [true, false, false, false].sample }
24
25
  @type_sample_tracepoint_path = ->(_tracepoint_path) { true }
26
+ @endpoint = "https://gem.sh/api/v1/types/samples"
27
+ @application_name = "TypeFusion"
25
28
  end
26
29
 
27
30
  def type_sample_request?(env)
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "litejob"
4
+
5
+ # Litequeue.configure do |config|
6
+ # config.path = "db/type_fusion/queue.sqlite3"
7
+ # end
8
+
9
+ Litejob.configure do |config|
10
+ config.logger = Class.new do
11
+ def self.info(*args)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ TypeFusion.start
4
+
5
+ Minitest.after_run do
6
+ TypeFusion.stop
7
+ TypeFusion.processor.process!
8
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypeFusion
4
+ class Processor
5
+ def initialize(amount = 4)
6
+ @amount = amount
7
+ @servers = []
8
+ end
9
+
10
+ def should_run?
11
+ enqueued_samples.positive?
12
+ end
13
+
14
+ def enqueued_samples
15
+ Litequeue.instance.count
16
+ end
17
+
18
+ def stop!
19
+ @servers.each do |server|
20
+ server.instance_variable_get(:@scheduler).context.kill
21
+ end
22
+ @servers = []
23
+ end
24
+
25
+ def start!
26
+ Signal.trap("INT") do
27
+ puts "\n[TypeFusion] Stop processing of TypeFusion samples..."
28
+ stop!
29
+ end
30
+
31
+ @amount.times do
32
+ @servers << Litejob::Server.new
33
+ end
34
+ end
35
+
36
+ def process!
37
+ puts "[TypeFusion] Start processing of #{enqueued_samples} samples..."
38
+
39
+ start!
40
+
41
+ while should_run?
42
+ puts "[TypeFusion] Remaining samples: #{Litequeue.instance.count}"
43
+ sleep 1
44
+ end
45
+
46
+ puts "[TypeFusion] Finished!"
47
+
48
+ puts "[TypeFusion] Congratulations! You just contributed samples to the following gems and helped making the Ruby ecosystem better for the whole community! Thank you!\n"
49
+ puts "- #{gems.join("\n- ")}"
50
+
51
+ stop!
52
+ end
53
+
54
+ private
55
+
56
+ def gems
57
+ TypeFusion::Sampler.instance.samples.map(&:gem_name).uniq.sort
58
+ end
59
+ end
60
+ end
@@ -6,6 +6,7 @@ module TypeFusion
6
6
  class Middleware
7
7
  def initialize(app)
8
8
  @app = app
9
+ TypeFusion.start_processing
9
10
  end
10
11
 
11
12
  def call(env)
@@ -5,6 +5,8 @@ module TypeFusion
5
5
  initializer "type_fusion.middleware" do |app|
6
6
  require "type_fusion/rack/middleware"
7
7
 
8
+ TypeFusion.config.application_name = app.class.module_parent_name
9
+
8
10
  app.config.middleware.use TypeFusion::Middleware
9
11
  end
10
12
  end
@@ -3,9 +3,13 @@
3
3
  require "json"
4
4
 
5
5
  module TypeFusion
6
- SampleCall = Struct.new(:gem_and_version, :receiver, :method_name, :location, :parameters) do
6
+ SampleCall = Struct.new(:gem_name, :gem_version, :receiver, :method_name, :application_name, :location, :type_fusion_version, :parameters, :return_value) do
7
7
  def to_s
8
8
  JSON.pretty_generate(to_h)
9
9
  end
10
+
11
+ def inspect
12
+ "#<TypeFusion::SampleCall receiver=#{receiver.inspect} method_name=#{method_name.inspect} gem_name=#{gem_name.inspect}>"
13
+ end
10
14
  end
11
15
  end
@@ -1,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "litestack"
3
+ require "lhc"
4
4
 
5
5
  module TypeFusion
6
6
  class SampleJob
7
7
  include Litejob
8
8
 
9
- self.queue = :default
9
+ queue_as :default
10
10
 
11
11
  def perform(sample)
12
- puts sample.inspect
12
+ LHC.json.post(TypeFusion.config.endpoint, body: { sample: JSON.parse(sample) })
13
+ rescue StandardError => e
14
+ puts e.inspect
13
15
  end
14
16
  end
15
17
  end
@@ -21,7 +21,7 @@ module TypeFusion
21
21
  end
22
22
 
23
23
  def trace
24
- @trace ||= TracePoint.trace(:call) do |tracepoint|
24
+ @trace ||= TracePoint.trace(:return) do |tracepoint|
25
25
  if sample?(tracepoint.path)
26
26
  receiver = begin
27
27
  tracepoint.binding.receiver.name
@@ -30,21 +30,32 @@ module TypeFusion
30
30
  end
31
31
 
32
32
  method_name = tracepoint.method_id
33
- location = tracepoint.binding.source_location
34
- gem_and_version = location.first.gsub(gem_path, "").split("/").first
35
- args = tracepoint.parameters.map(&:reverse).to_h
33
+ location = tracepoint.binding.source_location.join(":")
34
+ gem_and_version = location.gsub(gem_path, "").split("/").first
35
+ gem, version = gem_and_version_from(gem_and_version)
36
+ args = tracepoint.parameters.to_h(&:reverse)
36
37
  parameters = extract_parameters(args, tracepoint.binding)
38
+ return_value = type_for_object(tracepoint.return_value)
37
39
 
38
40
  sample = SampleCall.new(
39
- gem_and_version: gem_and_version,
41
+ gem_name: gem,
42
+ gem_version: version,
40
43
  receiver: receiver,
41
44
  method_name: method_name,
42
45
  location: location,
46
+ type_fusion_version: VERSION,
43
47
  parameters: parameters,
48
+ application_name: TypeFusion.config.application_name,
49
+ return_value: return_value,
44
50
  )
45
51
 
46
52
  samples << sample
47
- SampleJob.perform_async(sample)
53
+
54
+ begin
55
+ SampleJob.perform_async(sample)
56
+ rescue StandardError => e
57
+ puts "[TypeFusion] Couldn't enqueue sample: #{e.inspect}"
58
+ end
48
59
  end
49
60
  end.tap(&:disable)
50
61
  end
@@ -81,6 +92,31 @@ module TypeFusion
81
92
  end
82
93
  end
83
94
 
95
+ def gem_and_version_from(gem_and_version)
96
+ return [] if gem_and_version.nil?
97
+
98
+ splits = gem_and_version.split("-")
99
+
100
+ if splits.length == 1
101
+ [splits.first, nil]
102
+ elsif splits.length == 2
103
+ splits
104
+ else
105
+ *name, version = splits
106
+
107
+ # TODO: there must be a better way to do this
108
+ if ["darwin", "java", "linux", "ucrt", "mingw32"].include?(version)
109
+ amount = (version == "ucrt") ? 3 : 2
110
+
111
+ version = [*name.pop(amount), version].join("-")
112
+ end
113
+
114
+ gem = name.join("-")
115
+
116
+ [gem, version]
117
+ end
118
+ end
119
+
84
120
  def extract_parameters(args, binding)
85
121
  args.map do |name, kind|
86
122
  variable = name.to_s.gsub("*", "").gsub("&", "").to_sym
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TypeFusion
4
- VERSION = "0.0.3"
4
+ VERSION = "0.0.5"
5
5
  end
data/lib/type_fusion.rb CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  require_relative "type_fusion/version"
4
4
  require_relative "type_fusion/config"
5
+ require_relative "type_fusion/litejob"
5
6
  require_relative "type_fusion/sample_call"
6
7
  require_relative "type_fusion/sample_job"
7
8
  require_relative "type_fusion/sampler"
9
+ require_relative "type_fusion/processor"
8
10
 
9
11
  module TypeFusion
10
12
  class << self
@@ -22,6 +24,16 @@ module TypeFusion
22
24
  Sampler.instance.trace.disable
23
25
  end
24
26
 
27
+ def start_processing
28
+ puts "[TypeFusion] Start processing..."
29
+ processor.start!
30
+ end
31
+
32
+ def stop_processing
33
+ puts "[TypeFusion] Stop processing..."
34
+ processor.stop!
35
+ end
36
+
25
37
  def with_sampling
26
38
  start
27
39
 
@@ -29,6 +41,10 @@ module TypeFusion
29
41
 
30
42
  stop
31
43
  end
44
+
45
+ def processor
46
+ @processor ||= TypeFusion::Processor.new(4)
47
+ end
32
48
  end
33
49
  end
34
50
 
data/test.rb CHANGED
@@ -16,4 +16,4 @@ TypeFusion.with_sampling do
16
16
  end
17
17
 
18
18
  puts TypeFusion::Sampler.instance.samples
19
- puts TypeFusion::Sampler.instance.to_s
19
+ puts TypeFusion::Sampler.instance
metadata CHANGED
@@ -1,29 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: type_fusion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Roth
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-08-13 00:00:00.000000000 Z
11
+ date: 2023-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: litestack
14
+ name: lhc
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '15.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '15.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: litejob
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.3
27
41
  description: Community-contributed sample data for Ruby types
28
42
  email:
29
43
  - marco.roth@intergga.ch
@@ -39,8 +53,12 @@ files:
39
53
  - Gemfile.lock
40
54
  - README.md
41
55
  - Rakefile
56
+ - lib/tasks/type_fusion.rake
42
57
  - lib/type_fusion.rb
43
58
  - lib/type_fusion/config.rb
59
+ - lib/type_fusion/litejob.rb
60
+ - lib/type_fusion/minitest.rb
61
+ - lib/type_fusion/processor.rb
44
62
  - lib/type_fusion/rack/middleware.rb
45
63
  - lib/type_fusion/rails/railtie.rb
46
64
  - lib/type_fusion/sample_call.rb