eltesla 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6d5a5688dc14f783b38a944c3e29088ee53343beab09b3a7dd0a4d59a54eadcc
4
+ data.tar.gz: 39b89807a086e30b1b0d680975a29a3f657591e82e192b9709fc4c91388d3487
5
+ SHA512:
6
+ metadata.gz: b263b136234020ac7b6e88b7493131b2f815120409b36b1c6bd51d1d873adae29a4456e05a6e1613150fe4fbbb72d6c9f65e49be6a49e54963673749fe6a3036
7
+ data.tar.gz: 1b935e5420b4720c16be076184c13a32ba98f597043bc32b9dd4fbecaf74dcd99a9670ba7765354908e86864db976380c038beec17bb346e3ff4bed2f455b91a
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'json'
6
+
7
+ module Eltesla
8
+ # Client for the Eltesla backend
9
+ class Client
10
+ if ENV.fetch('ELTESLA_DEBUG', false)
11
+ PROTOCOL = 'http'
12
+ DOMAIN = 'localhost:3000'
13
+ QUEUE_SIZE_THRESHOLD = 1
14
+ QUEUE_SIZE_LIMIT = 2
15
+ else
16
+ PROTOCOL = 'https'
17
+ DOMAIN = 'eltesla.dev'
18
+ QUEUE_SIZE_THRESHOLD = 10
19
+ QUEUE_SIZE_LIMIT = 20
20
+ end
21
+ ENDPOINT = URI.parse("#{PROTOCOL}://#{DOMAIN}/metrics")
22
+
23
+ class << self
24
+ attr_writer :queue
25
+
26
+ def queue
27
+ @queue ||= []
28
+ end
29
+
30
+ def send(result)
31
+ raise "Could not process #{result}" unless result.is_a?(Eltesla::Result)
32
+
33
+ queue << result
34
+ flush! if queue.size >= QUEUE_SIZE_THRESHOLD
35
+ end
36
+
37
+ def flush!
38
+ return if queue.empty?
39
+
40
+ http_post!
41
+ queue.clear
42
+ rescue StandardError => e
43
+ warn "[Eltesla] Could not send payload: #{e.message}"
44
+ if queue.size > QUEUE_SIZE_LIMIT
45
+ warn("[Eltesla] Dropping #{queue.size - QUEUE_SIZE_LIMIT} results from the queue")
46
+ self.queue = queue[-QUEUE_SIZE_LIMIT..]
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def http_post!
53
+ http = Net::HTTP.new(ENDPOINT.host, ENDPOINT.port)
54
+ request = Net::HTTP::Post.new(
55
+ ENDPOINT.request_uri,
56
+ { 'Content-Type': 'application/json' }
57
+ )
58
+ request.body = { results: queue }.to_json
59
+
60
+ http.request(request).value
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ # rubocop:disable Style/GlobalVars
67
+ $eltesla_exit_callback ||= at_exit { Eltesla::Client.flush! }
68
+ # rubocop:enable Style/GlobalVars
69
+
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'scientist/experiment'
4
+ require 'eltesla/result'
5
+
6
+ module Eltesla
7
+ # Github scientist experiment implementation for Eltesla
8
+ class Experiment
9
+ include Scientist::Experiment
10
+
11
+ attr_reader :name, :file, :line
12
+
13
+ def initialize(name)
14
+ location_parts = clean_trace(caller).first.split(/:/)
15
+ @file = location_parts.first
16
+ @line = location_parts[1].to_i
17
+ @name = name || location_parts.last[4..-2]
18
+ end
19
+
20
+ def enabled?
21
+ # FIXME : Add activation policies
22
+ true
23
+ end
24
+
25
+ def raised(operation, error)
26
+ # see "In a Scientist callback" below
27
+ p "Operation '#{operation}' failed with error '#{error.inspect}'"
28
+ super # will re-raise
29
+ end
30
+
31
+ def publish(result)
32
+ Eltesla::Client.send(result.to_eltesla)
33
+ end
34
+
35
+ def clean_trace(backtrace)
36
+ return backtrace unless defined?(Rails.backtrace_cleaner)
37
+
38
+ Rails.backtrace_cleaner.clean(backtrace)
39
+ end
40
+
41
+ def self.run(name = nil)
42
+ raise 'Missing block' unless block_given?
43
+
44
+ experiment = Eltesla::Experiment.new(name)
45
+ yield(experiment)
46
+ experiment.run
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'scientist'
5
+
6
+ module Eltesla
7
+ # rubocop:disable Naming/ConstantName
8
+ OBSERVATION_ATTRIBUTES = %i[
9
+ name
10
+ value
11
+ duration
12
+ exception_class
13
+ exception_message
14
+ exception_backtrace
15
+ ].freeze
16
+
17
+ Observation =
18
+ if defined?(Data.define)
19
+ Data.define(*OBSERVATION_ATTRIBUTES)
20
+ else
21
+ Struct.new(*OBSERVATION_ATTRIBUTES, keyword_init: true)
22
+ end
23
+
24
+ Scientist::Observation.define_method(:to_eltesla) do
25
+ Eltesla::Observation.new(
26
+ exception_class: exception&.class&.name,
27
+ exception_message: exception&.message,
28
+ exception_backtrace: exception&.backtrace,
29
+ name:,
30
+ value:,
31
+ duration:
32
+ )
33
+ end
34
+ # rubocop:enable Naming/ConstantName
35
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'scientist'
5
+ require_relative 'observation'
6
+
7
+ module Eltesla
8
+ # rubocop:disable Naming/ConstantName
9
+ RESULT_ATTRIBUTES = %i[
10
+ observations_attributes
11
+ context
12
+ matched
13
+ executed_at
14
+ experiment_name
15
+ experiment_file
16
+ experiment_line
17
+ ].freeze
18
+
19
+ Result =
20
+ if defined?(Data.define)
21
+ Data.define(*RESULT_ATTRIBUTES)
22
+ else
23
+ Struct.new(*RESULT_ATTRIBUTES, keyword_init: true)
24
+ end
25
+
26
+ Scientist::Result.define_method(:to_eltesla) do
27
+ Eltesla::Result.new(
28
+ experiment_name: experiment.name,
29
+ experiment_file: experiment.file,
30
+ experiment_line: experiment.line,
31
+ context: context.empty? ? nil : context,
32
+ matched: matched?,
33
+ executed_at: Time.now,
34
+ observations_attributes: observations.map(&:to_eltesla)
35
+ )
36
+ end
37
+ # rubocop:enable Naming/ConstantName
38
+ end
@@ -0,0 +1,3 @@
1
+ module Eltesla
2
+ VERSION = '0.0.1'
3
+ end
data/lib/eltesla.rb ADDED
@@ -0,0 +1,10 @@
1
+
2
+ require 'scientist'
3
+ require 'eltesla/observation'
4
+ require 'eltesla/result'
5
+ require 'eltesla/experiment'
6
+ require 'eltesla/client'
7
+
8
+ module Eltesla
9
+ warn 'Eltesla is loaded, and rebuilt!'
10
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eltesla
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexandre Ignjatovic
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-03-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: scientist
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ description: Client for the Eltesla service, for easier refactoring
28
+ email: alexandre.ignjatovic@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/eltesla.rb
34
+ - lib/eltesla/client.rb
35
+ - lib/eltesla/experiment.rb
36
+ - lib/eltesla/observation.rb
37
+ - lib/eltesla/result.rb
38
+ - lib/eltesla/version.rb
39
+ homepage: https://eltes.la
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '2.3'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubygems_version: 3.5.6
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: 'Eltesla: GitHub Scientist as a Service.'
62
+ test_files: []