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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +165 -0
- data/Rakefile +10 -0
- data/exe/greenroom +6 -0
- data/lib/greenroom/census/cursor.rb +48 -0
- data/lib/greenroom/census/job.rb +118 -0
- data/lib/greenroom/census/middleware.rb +30 -0
- data/lib/greenroom/census/run.rb +59 -0
- data/lib/greenroom/cli/check.rb +454 -0
- data/lib/greenroom/cli/judge.rb +273 -0
- data/lib/greenroom/cli.rb +30 -0
- data/lib/greenroom/digest.rb +28 -0
- data/lib/greenroom/experiment_behavior.rb +82 -0
- data/lib/greenroom/recorder.rb +219 -0
- data/lib/greenroom/reporter/new_relic.rb +15 -0
- data/lib/greenroom/reporter.rb +61 -0
- data/lib/greenroom/version.rb +5 -0
- data/lib/greenroom.rb +57 -0
- data/sig/greenroom.rbs +4 -0
- metadata +105 -0
|
@@ -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
|
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
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: []
|