greenroom 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,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Greenroom
4
+ # The seam between the parts of this gem that produce events (the recorder,
5
+ # the experiment behavior) and wherever those events actually go. Kept as
6
+ # an abstract base plus two concrete subclasses here, and any real delivery
7
+ # implementation in its own file, so the core of the gem never has to load
8
+ # a delivery library it does not use.
9
+ class Reporter
10
+ # Greenroom's own bound, chosen rather than mirrored. Measured against
11
+ # the New Relic Ruby agent at version 10.7.1, that agent truncates an
12
+ # attribute name at 255 characters and an attribute value at 4095, so
13
+ # this cap is stricter than the value limit of the one delivery target
14
+ # written so far.
15
+ #
16
+ # It is stricter on purpose. Every attribute this gem sends is meant to
17
+ # be short: a digest, a class name, a count, an identifier. A string
18
+ # long enough to reach this cap is a sign that something a host supplied
19
+ # -- an experiment name, a row identifier -- is carrying more than a
20
+ # label, and cutting it keeps one such value from bloating every event
21
+ # of a run.
22
+ MAX_ATTRIBUTE_LENGTH = 255
23
+
24
+ def record(event_type, attributes)
25
+ raise NotImplementedError, "#{self.class} must implement #record"
26
+ end
27
+
28
+ private
29
+
30
+ def truncate(attributes)
31
+ attributes.transform_values do |value|
32
+ value.is_a?(String) ? value[0, MAX_ATTRIBUTE_LENGTH] : value
33
+ end
34
+ end
35
+ end
36
+
37
+ # The default reporter. A host that never configures `Greenroom.reporter`
38
+ # gets silence rather than a NotImplementedError, so requiring this gem
39
+ # alone never breaks a caller that has not wired up delivery yet.
40
+ class Reporter::Null < Reporter
41
+ def record(event_type, attributes)
42
+ end
43
+ end
44
+
45
+ # Accumulates every recorded event in memory instead of sending it
46
+ # anywhere. Exists for tests: a spec can install this reporter and then
47
+ # assert on `#events` instead of stubbing a real delivery library.
48
+ class Reporter::Memory < Reporter
49
+ Event = Struct.new(:type, :attributes)
50
+
51
+ attr_reader :events
52
+
53
+ def initialize
54
+ @events = []
55
+ end
56
+
57
+ def record(event_type, attributes)
58
+ @events << Event.new(event_type, truncate(attributes))
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Greenroom
4
+ VERSION = "0.1.0"
5
+ end
data/lib/greenroom.rb ADDED
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "scientist"
4
+
5
+ require_relative "greenroom/version"
6
+ require_relative "greenroom/digest"
7
+ require_relative "greenroom/reporter"
8
+ require_relative "greenroom/recorder"
9
+ require_relative "greenroom/experiment_behavior"
10
+ require_relative "greenroom/census/run"
11
+
12
+ module Greenroom
13
+ class Error < StandardError; end
14
+
15
+ # Raised by `assert_registered!` when no host class has claimed
16
+ # `Scientist::Experiment`'s one registration slot yet.
17
+ class NotRegisteredError < Error; end
18
+
19
+ # Raised by `Census::Job#build_enumerator` when the host's Sidekiq server
20
+ # middleware chain does not include `Census::Middleware` yet. Without that
21
+ # middleware, an error escaping `each_iteration` leaves the thread-local
22
+ # recorder installed (Sidekiq skips `on_stop` in that case), and the next,
23
+ # unrelated job Sidekiq happens to run on the same thread would find an
24
+ # experiment already turned on. Checking at the start of every run turns
25
+ # that silent leak into a startup-time failure instead.
26
+ #
27
+ # Defined here, alongside `NotRegisteredError`, rather than in
28
+ # `census/middleware.rb`: this file is the one place that never requires
29
+ # Sidekiq, so every error this gem can raise stays nameable and
30
+ # rescuable from `require "greenroom"` alone, without also pulling in
31
+ # the census machinery.
32
+ class MiddlewareNotRegisteredError < Error; end
33
+
34
+ class << self
35
+ # Where recorded events go. Defaults to `Reporter::Null`, so a host that
36
+ # has not configured delivery yet gets silence instead of a load-time
37
+ # error.
38
+ attr_accessor :reporter
39
+
40
+ # Meant to run at boot. A host that includes only `ExperimentBehavior`
41
+ # in its experiment class, and forgets `Scientist::Experiment`, gets no
42
+ # error and no candidate ever runs: `enabled?` and `publish` both exist,
43
+ # but `Scientist::Experiment.new` still returns a `Scientist::Default`
44
+ # (`enabled?` always false), so every experiment stays quietly inert
45
+ # forever. Probing at boot turns that silent misconfiguration into a
46
+ # startup failure instead.
47
+ def assert_registered!
48
+ return unless Scientist::Experiment.new("greenroom-probe").instance_of?(Scientist::Default)
49
+
50
+ raise NotRegisteredError,
51
+ "No class includes Scientist::Experiment yet. Add `include Scientist::Experiment` " \
52
+ "to the class that runs your experiments."
53
+ end
54
+ end
55
+
56
+ self.reporter = Reporter::Null.new
57
+ end
data/sig/greenroom.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Greenroom
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: greenroom
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - meganemura
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: scientist
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.6'
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 1.6.5
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '1.6'
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 1.6.5
32
+ - !ruby/object:Gem::Dependency
33
+ name: rubocop-ast
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '1.50'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '1.50'
46
+ description: Greenroom gives a Rails application the parts that turn a Scientist experiment
47
+ into evidence. An experiment stays inactive until a census job starts a recorder,
48
+ so a team can merge agent-written code early and run nothing new. The recorder counts
49
+ the rows, the comparisons, the mismatches, and the candidate errors. At the end
50
+ of the run it sends one aggregate event and a limited number of sample events. A
51
+ sample carries an identifier and a digest, and never the value itself.
52
+ email:
53
+ - meganemura@users.noreply.github.com
54
+ executables:
55
+ - greenroom
56
+ extensions: []
57
+ extra_rdoc_files: []
58
+ files:
59
+ - CHANGELOG.md
60
+ - LICENSE.txt
61
+ - README.md
62
+ - Rakefile
63
+ - exe/greenroom
64
+ - lib/greenroom.rb
65
+ - lib/greenroom/census/cursor.rb
66
+ - lib/greenroom/census/job.rb
67
+ - lib/greenroom/census/middleware.rb
68
+ - lib/greenroom/census/run.rb
69
+ - lib/greenroom/cli.rb
70
+ - lib/greenroom/cli/check.rb
71
+ - lib/greenroom/cli/judge.rb
72
+ - lib/greenroom/digest.rb
73
+ - lib/greenroom/experiment_behavior.rb
74
+ - lib/greenroom/recorder.rb
75
+ - lib/greenroom/reporter.rb
76
+ - lib/greenroom/reporter/new_relic.rb
77
+ - lib/greenroom/version.rb
78
+ - sig/greenroom.rbs
79
+ homepage: https://github.com/meganemura/greenroom
80
+ licenses:
81
+ - MIT
82
+ metadata:
83
+ allowed_push_host: https://rubygems.org
84
+ homepage_uri: https://github.com/meganemura/greenroom
85
+ source_code_uri: https://github.com/meganemura/greenroom
86
+ changelog_uri: https://github.com/meganemura/greenroom/blob/main/gems/greenroom/CHANGELOG.md
87
+ rubygems_mfa_required: 'true'
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: 3.3.0
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubygems_version: 4.0.16
103
+ specification_version: 4
104
+ summary: Judge a Scientist experiment by a nightly full-table census.
105
+ test_files: []