eltesla 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []