blood_contracts-instrumentation 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/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +31 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +12 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +51 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/blood_contracts-instrumentation.gemspec +27 -0
- data/lib/blood_contracts-instrumentation.rb +3 -0
- data/lib/blood_contracts/instrumentation.rb +72 -0
- data/lib/blood_contracts/instrumentation/config.rb +126 -0
- data/lib/blood_contracts/instrumentation/failed_match.rb +41 -0
- data/lib/blood_contracts/instrumentation/instrument.rb +88 -0
- data/lib/blood_contracts/instrumentation/session.rb +121 -0
- data/lib/blood_contracts/instrumentation/session_finalizer.rb +60 -0
- data/lib/blood_contracts/instrumentation/session_finalizer/basic.rb +27 -0
- data/lib/blood_contracts/instrumentation/session_finalizer/fibers.rb +75 -0
- data/lib/blood_contracts/instrumentation/session_finalizer/threads.rb +33 -0
- data/lib/blood_contracts/instrumentation/session_recording.rb +134 -0
- data/spec/blood_contracts/instrumentation_spec.rb +82 -0
- data/spec/spec_helper.rb +22 -0
- metadata +158 -0
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BloodContracts
|
4
|
+
module Instrumentation
|
5
|
+
# Prependable module for matching session recording
|
6
|
+
module SessionRecording
|
7
|
+
# Adds @session initialization to constructor
|
8
|
+
def initialize(*)
|
9
|
+
super
|
10
|
+
self.class.instruments
|
11
|
+
@session = self.class.session_klass.new(self.class.name)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Wrapper for BC::Refined#match call, to add instrumentaion
|
15
|
+
# Usage:
|
16
|
+
# class JsonType < BC::Refined
|
17
|
+
# # now during #match call you will have access to @session
|
18
|
+
# prepend BloodContracts::Instrumentation::Match
|
19
|
+
#
|
20
|
+
# def match
|
21
|
+
# context[:parsed] = JSON.parse(value.to_s)
|
22
|
+
# self
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
module Match
|
26
|
+
# Wraps original call in session start and finish call
|
27
|
+
# Note that @session.finish(result) is called even when
|
28
|
+
# exception was raised during the call
|
29
|
+
#
|
30
|
+
# @return [BC::Refined]
|
31
|
+
#
|
32
|
+
def match
|
33
|
+
@session.start
|
34
|
+
self.class.instruments.each { |i| i.before(@session) }
|
35
|
+
|
36
|
+
result = super
|
37
|
+
rescue StandardError => e
|
38
|
+
result = FailedMatch.new(e, context: @context)
|
39
|
+
raise e
|
40
|
+
ensure
|
41
|
+
finalize!(result)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Finish the matching session and delegate finalize to SessionFinalizer
|
45
|
+
#
|
46
|
+
# @param result [BC::Refined] result of type matching pipeline
|
47
|
+
#
|
48
|
+
# @return [Nothing]
|
49
|
+
#
|
50
|
+
def finalize!(result)
|
51
|
+
@session.finish(result)
|
52
|
+
self.class.instruments.each { |i| i.after(@session) }
|
53
|
+
SessionFinalizer.instance.finalize!(self.class.instruments, @session)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Modification to inheritance for the session recording
|
58
|
+
module Inheritance
|
59
|
+
# Register the inherited type and set the session klass same as parent
|
60
|
+
#
|
61
|
+
# @param child [BC::Refined] class to enhance
|
62
|
+
#
|
63
|
+
# @return [Nothing]
|
64
|
+
#
|
65
|
+
def inherited(child)
|
66
|
+
child.session_klass = session_klass
|
67
|
+
Instrumentation.register_type(child)
|
68
|
+
super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Modifications in singleton class of BC::Refined
|
73
|
+
#
|
74
|
+
# @param other [BC::Refined] class to enhance
|
75
|
+
#
|
76
|
+
# rubocop:disable Metrics/MethodLength
|
77
|
+
def self.prepended(other)
|
78
|
+
class << other
|
79
|
+
prepend Inheritance
|
80
|
+
|
81
|
+
# Class to use as a session (writer)
|
82
|
+
#
|
83
|
+
# @return [Session]
|
84
|
+
#
|
85
|
+
attr_writer :session_klass
|
86
|
+
|
87
|
+
# Class to use as a session
|
88
|
+
# By default is set to Session
|
89
|
+
#
|
90
|
+
# @return [Session]
|
91
|
+
#
|
92
|
+
def session_klass
|
93
|
+
@session_klass ||= Session
|
94
|
+
end
|
95
|
+
|
96
|
+
# Whether type anonymous or not
|
97
|
+
#
|
98
|
+
# @return [Boolean]
|
99
|
+
#
|
100
|
+
def anonymous?
|
101
|
+
name.nil?
|
102
|
+
end
|
103
|
+
|
104
|
+
# List of instruments for the type
|
105
|
+
#
|
106
|
+
# @return [Array<Instrument>]
|
107
|
+
#
|
108
|
+
def instruments
|
109
|
+
return @instruments if defined? @instruments
|
110
|
+
|
111
|
+
reset_instruments!
|
112
|
+
end
|
113
|
+
|
114
|
+
# Alias for instruments reader
|
115
|
+
# See #instruments
|
116
|
+
alias setup_instruments instruments
|
117
|
+
|
118
|
+
# Reset the List of instruments for the type
|
119
|
+
# Note, that if list of instruments is empty there is no need to
|
120
|
+
# init the session, so type is not prepended by Match wrapper
|
121
|
+
#
|
122
|
+
# @return [Array<Instrument>]
|
123
|
+
#
|
124
|
+
def reset_instruments!
|
125
|
+
@instruments = Instrumentation.select_instruments(name)
|
126
|
+
ensure
|
127
|
+
prepend(Match) unless @instruments.empty?
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
# rubocop:enable Metrics/MethodLength
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe BloodContracts::Instrumentation do
|
4
|
+
before do
|
5
|
+
module Test
|
6
|
+
require "json"
|
7
|
+
|
8
|
+
class EmailType < BC::Refined
|
9
|
+
REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
|
10
|
+
def match
|
11
|
+
context[:email_input] = value.to_s
|
12
|
+
return failure(:invalid_email) if context[:email_input] !~ REGEX
|
13
|
+
context[:email] = context[:email_input]
|
14
|
+
self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class JsonType < BC::Refined
|
19
|
+
def match
|
20
|
+
context[:parsed] = JSON.parse(value.to_s)
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe ".configure" do
|
28
|
+
subject do
|
29
|
+
described_class.configure { |cfg| cfg.finalizer_pool_size = 5 }
|
30
|
+
end
|
31
|
+
|
32
|
+
it do
|
33
|
+
is_expected.to eq(described_class.config)
|
34
|
+
expect(subject.finalizer_pool_size).to eq(5)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe ".register_type" do
|
39
|
+
before { described_class.register_type(Test::JsonType) }
|
40
|
+
|
41
|
+
subject { described_class.config }
|
42
|
+
|
43
|
+
it { expect(subject.types).to include(Test::JsonType) }
|
44
|
+
end
|
45
|
+
|
46
|
+
describe ".select_instruments" do
|
47
|
+
before do
|
48
|
+
described_class.configure do |cfg|
|
49
|
+
cfg.instrument "Json", lambda { |session|
|
50
|
+
puts "[SID:#{session.id}] #{session.result_type_name}"
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
subject { described_class.select_instruments(type) }
|
56
|
+
|
57
|
+
context "when type matches instruments condition" do
|
58
|
+
let(:type) { Test::JsonType.name }
|
59
|
+
|
60
|
+
it { is_expected.to match_array([kind_of(described_class::Instrument)]) }
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when type doesn't match instruments condition" do
|
64
|
+
let(:type) { Test::EmailType.name }
|
65
|
+
|
66
|
+
it { is_expected.to be_empty }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe ".reset_session_finalizer!" do
|
71
|
+
before do
|
72
|
+
described_class.configure { |cfg| cfg.session_finalizer = :fibers }
|
73
|
+
old_finalizer
|
74
|
+
end
|
75
|
+
|
76
|
+
let(:old_finalizer) { described_class::SessionFinalizer.instance }
|
77
|
+
|
78
|
+
subject { described_class.reset_session_finalizer! }
|
79
|
+
|
80
|
+
it { is_expected.not_to eq(old_finalizer) }
|
81
|
+
end
|
82
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "blood_contracts/instrumentation"
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
# Enable flags like --only-failures and --next-failure
|
8
|
+
config.example_status_persistence_file_path = ".rspec_status"
|
9
|
+
|
10
|
+
# Disable RSpec exposing methods globally on `Module` and `main`
|
11
|
+
config.disable_monkey_patching!
|
12
|
+
|
13
|
+
config.expect_with :rspec do |c|
|
14
|
+
c.syntax = :expect
|
15
|
+
end
|
16
|
+
|
17
|
+
config.around do |example|
|
18
|
+
module Test; end
|
19
|
+
example.run
|
20
|
+
Object.send(:remove_const, :Test)
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: blood_contracts-instrumentation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sergey Dolganov (sclinede)
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-07-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: blood_contracts-core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.49'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.49'
|
97
|
+
description: Adds instrumentation to BloodContracts refinement types
|
98
|
+
email:
|
99
|
+
- sclinede@evilmartians.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files:
|
103
|
+
- CODE_OF_CONDUCT.md
|
104
|
+
- README.md
|
105
|
+
- CHANGELOG.md
|
106
|
+
files:
|
107
|
+
- ".gitignore"
|
108
|
+
- ".rspec"
|
109
|
+
- ".rubocop.yml"
|
110
|
+
- ".travis.yml"
|
111
|
+
- CHANGELOG.md
|
112
|
+
- CODE_OF_CONDUCT.md
|
113
|
+
- Gemfile
|
114
|
+
- LICENSE.txt
|
115
|
+
- README.md
|
116
|
+
- Rakefile
|
117
|
+
- bin/console
|
118
|
+
- bin/setup
|
119
|
+
- blood_contracts-instrumentation.gemspec
|
120
|
+
- lib/blood_contracts-instrumentation.rb
|
121
|
+
- lib/blood_contracts/instrumentation.rb
|
122
|
+
- lib/blood_contracts/instrumentation/config.rb
|
123
|
+
- lib/blood_contracts/instrumentation/failed_match.rb
|
124
|
+
- lib/blood_contracts/instrumentation/instrument.rb
|
125
|
+
- lib/blood_contracts/instrumentation/session.rb
|
126
|
+
- lib/blood_contracts/instrumentation/session_finalizer.rb
|
127
|
+
- lib/blood_contracts/instrumentation/session_finalizer/basic.rb
|
128
|
+
- lib/blood_contracts/instrumentation/session_finalizer/fibers.rb
|
129
|
+
- lib/blood_contracts/instrumentation/session_finalizer/threads.rb
|
130
|
+
- lib/blood_contracts/instrumentation/session_recording.rb
|
131
|
+
- spec/blood_contracts/instrumentation_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
homepage: https://github.com/sclinede/blood_contracts-core
|
134
|
+
licenses:
|
135
|
+
- MIT
|
136
|
+
metadata: {}
|
137
|
+
post_install_message:
|
138
|
+
rdoc_options: []
|
139
|
+
require_paths:
|
140
|
+
- lib
|
141
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '2.4'
|
146
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
requirements: []
|
152
|
+
rubygems_version: 3.0.3
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: Adds instrumentation to BloodContracts refinement types
|
156
|
+
test_files:
|
157
|
+
- spec/blood_contracts/instrumentation_spec.rb
|
158
|
+
- spec/spec_helper.rb
|