blood_contracts-instrumentation 0.1.0 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a1ff83954224dfbb24664768832c7db0e4b621364a92de707c3b1774fbc9e30
4
- data.tar.gz: e271b417bdd7082067d91faf6826d91c0f13f5e2f3f01068ddba0a23af3edf8c
3
+ metadata.gz: 1799124ca3d91ee88f459ab3052a28317a65d84175f551ea48bedec9a2fc2d5c
4
+ data.tar.gz: b50de2922484d0e36dac0a72f4f92a4a895504c1f31cc56e42444ce213533122
5
5
  SHA512:
6
- metadata.gz: d6c19029329fbf97adbea0fe440b0951c2ac6f9dd251ba4bf0c2e93d40421e9a823e9f5445c3483f4b78a4f3f23a5827bfa42406757d27f8d82264dbb660a66d
7
- data.tar.gz: bdb1e16379fd159cff96592bff34fd0ed43182bf8da38720acc3a0bf4e759ee0fe78ba746f84a7fcdf7f5dea2f19463c32f68b11e1c5589c776b8abcb56a51f3
6
+ metadata.gz: 1fecb3bc14257121f2fb7f183e0db21184b60af47b7aa30511c927ad2404e9bc98738a89c354779bb27fc2dab5d17ddd7e7ff5393443e6758167ef4135aac079
7
+ data.tar.gz: f829a2f03ea9ebfc23e0b4394604aa867e59521fa3f5f42600b3a0d97ae371b22c7feba29b02dc0f6743e5122c6e487deb3dc27968519639b9fae2ff5ad1d77a
data/CHANGELOG.md CHANGED
@@ -5,8 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
+ ## [0.1.1] - [2019-07-25]
9
+
10
+ Fixes NoMethodError in Instrument.build
11
+
12
+ Adds specs
13
+
8
14
  ## [0.1.0] - [2019-07-05]
9
15
 
10
16
  This is a first public release marked in change log with features extracted from production app.
11
17
  Includes:
12
- TBD
18
+ - Configuration for instruments (proc or a callable object)
19
+ - Automatic detection of existing Refined types
20
+ - Assignment of instruments to types
21
+ - #match wrapper to handle before, after and finalize of call
22
+ - 3 strategies of finalization (basic, threads and fibers)
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = 'blood_contracts-instrumentation'
5
- gem.version = '0.1.0'
5
+ gem.version = '0.1.1'
6
6
  gem.authors = ['Sergey Dolganov (sclinede)']
7
7
  gem.email = ['sclinede@evilmartians.com']
8
8
 
@@ -56,6 +56,14 @@ module BloodContracts
56
56
  @config ||= Config.new
57
57
  end
58
58
 
59
+ # Resets current instance of Config
60
+ #
61
+ # @return [Nothig]
62
+ #
63
+ def reset_config!
64
+ @config = nil
65
+ end
66
+
59
67
  require_relative "./instrumentation/failed_match.rb"
60
68
  require_relative "./instrumentation/session.rb"
61
69
 
@@ -29,7 +29,7 @@ module BloodContracts
29
29
  instance = instrument_from_proc(proto)
30
30
 
31
31
  if before.respond_to?(:call)
32
- inst.define_singleton_method(:before, &before)
32
+ instance.define_singleton_method(:before, &before)
33
33
  end
34
34
 
35
35
  define_stub(instance, :before)
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe BloodContracts::Instrumentation::Config 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
+ subject { described_class.new }
28
+
29
+ describe "defaults on new instance" do
30
+ let(:default_pool_size) { BCI::SessionFinalizer::DEFAULT_POOL_SIZE }
31
+
32
+ it do
33
+ expect(subject.instruments).to be_empty
34
+ expect(subject.types).to be_empty
35
+ expect(subject.finalizer_pool_size).to eq(default_pool_size)
36
+ expect(subject.session_finalizer).to eq(:basic)
37
+ end
38
+ end
39
+
40
+ describe "#instrument" do
41
+ subject { BCI.configure { |config| config.send(:reset_types!) } }
42
+
43
+ before do
44
+ subject.types << Test::EmailType << Test::JsonType
45
+
46
+ subject.instrument "Json", lambda { |session|
47
+ puts "[SID:#{session.id}] #{session.result_type_name}"
48
+ }
49
+ end
50
+
51
+ let(:instruments) { { /Json/i => [kind_of(BCI::Instrument)] } }
52
+ let(:json_instruments) { [kind_of(BCI::Instrument)] }
53
+
54
+ it do
55
+ expect(subject.instruments).to match(instruments)
56
+ expect(subject.types).to match_array([Test::EmailType, Test::JsonType])
57
+ expect(Test::EmailType.instruments).to be_empty
58
+ expect(Test::JsonType.instruments).to match_array(json_instruments)
59
+ end
60
+ end
61
+
62
+ describe "#finalizer_pool_size=" do
63
+ before do
64
+ # Pool is only relevant for Fibers finalizer right now
65
+ subject.session_finalizer = :fibers
66
+ old_finalizer
67
+
68
+ subject.finalizer_pool_size = new_pool_size
69
+ new_finalizer
70
+ end
71
+
72
+ let(:old_finalizer) { BCI::SessionFinalizer.instance }
73
+ let(:new_finalizer) { BCI::SessionFinalizer.instance }
74
+ let(:new_pool_size) { 2 }
75
+
76
+ it do
77
+ expect(old_finalizer).not_to eq(new_finalizer)
78
+ expect(new_finalizer.fibers.size).to eq(new_pool_size)
79
+ end
80
+ end
81
+
82
+ describe "#session_finalizer=" do
83
+ before do
84
+ # Pool is only relevant for Fibers finalizer right now
85
+ config = BCI::Config.new
86
+ default_finalizer
87
+
88
+ config.session_finalizer = :fibers
89
+ fibers_finalizer
90
+
91
+ config.session_finalizer = :threads
92
+ threads_finalizer
93
+ end
94
+
95
+ let(:default_finalizer) { BCI::SessionFinalizer.instance }
96
+ let(:fibers_finalizer) { BCI::SessionFinalizer.instance }
97
+ let(:threads_finalizer) { BCI::SessionFinalizer.instance }
98
+
99
+ it do
100
+ expect(default_finalizer).to eq(BCI::SessionFinalizer::Basic)
101
+ expect(fibers_finalizer).to match(kind_of(BCI::SessionFinalizer::Fibers))
102
+ expect(threads_finalizer).to eq(BCI::SessionFinalizer::Threads)
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe BloodContracts::Instrumentation::FailedMatch do
4
+ subject do
5
+ begin
6
+ 1 / 0
7
+ rescue StandardError => ex
8
+ described_class.new(ex, context: validation_context)
9
+ end
10
+ end
11
+
12
+ let(:validation_context) do
13
+ { some: "data", exception: kind_of(ZeroDivisionError) }
14
+ end
15
+
16
+ it do
17
+ is_expected.to be_invalid
18
+ expect(subject.match).to eq(subject)
19
+ expect(subject.exception).to match(kind_of(ZeroDivisionError))
20
+ expect(subject.context).to match(validation_context)
21
+ end
22
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe BloodContracts::Instrumentation::Instrument do
4
+ before do
5
+ module Test
6
+ Instrument = BloodContracts::Instrumentation::Instrument.build(
7
+ ->(session) { "[SID:#{session.id}] #{session.result_type_name}" },
8
+ before: ->(session) { session.extras[:started] = true },
9
+ after: ->(session) { session.extras[:finished] = true }
10
+ )
11
+
12
+ class CustomInstrument
13
+ def call(session)
14
+ "[SID:#{session.id}] #{session.result_type_name}"
15
+ end
16
+
17
+ def before(session)
18
+ session.extras[:started] = true
19
+ end
20
+
21
+ def after(session)
22
+ session.extras[:finished] = true
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ let!(:session) { BloodContracts::Instrumentation::Session.new("SomeType") }
29
+
30
+ describe ".build" do
31
+ # FIXME: add case when after and before are not defined on custom instrument
32
+ subject { BCI::Instrument.build(Test::CustomInstrument.new) }
33
+
34
+ let(:message) do
35
+ "[SID:#{session.id}] BloodContracts::Core::ContractFailure"
36
+ end
37
+
38
+ it do
39
+ expect(subject).to match(kind_of(Test::CustomInstrument))
40
+
41
+ session.start
42
+ subject.before(session)
43
+ expect(session.extras).to include(started: true)
44
+
45
+ session.finish(BC::ContractFailure.new({ SomeType: :damn }))
46
+ subject.after(session)
47
+
48
+ expect(session.extras).to include(finished: true)
49
+
50
+ expect(subject.call(session)).to eq(message)
51
+ end
52
+ end
53
+
54
+ describe "#before" do
55
+ before do
56
+ session.start
57
+ Test::Instrument.before(session)
58
+ end
59
+
60
+ it do
61
+ expect(session.extras).to include(started: true)
62
+ expect(session.extras).not_to include(finished: true)
63
+ end
64
+ end
65
+
66
+ describe "#after" do
67
+ before do
68
+ session.start
69
+ Test::Instrument.before(session)
70
+
71
+ session.finish(BC::ContractFailure.new({ SomeType: :damn }))
72
+ Test::Instrument.after(session)
73
+ end
74
+
75
+ it do
76
+ expect(session.extras).to include(started: true)
77
+ expect(session.extras).to include(finished: true)
78
+ end
79
+ end
80
+
81
+ describe "#call" do
82
+ before do
83
+ session.start
84
+ Test::Instrument.before(session)
85
+
86
+ session.finish(BC::ContractFailure.new({ SomeType: :damn }))
87
+ Test::Instrument.after(session)
88
+ end
89
+
90
+ subject { Test::Instrument.call(session) }
91
+
92
+ let(:message) do
93
+ "[SID:#{session.id}] BloodContracts::Core::ContractFailure"
94
+ end
95
+
96
+ it do
97
+ is_expected.to eq(message)
98
+ expect(session.extras).to include(started: true)
99
+ expect(session.extras).to include(finished: true)
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe BloodContracts::Instrumentation::SessionFinalizer do
4
+ before do
5
+ end
6
+
7
+ describe "defaults on new instance" do
8
+ end
9
+
10
+ describe "Basic finalizer" do
11
+ end
12
+
13
+ describe "Threads finalizer" do
14
+ end
15
+
16
+ describe "Fibers finalizer" do
17
+ end
18
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe BloodContracts::Instrumentation::SessionRecording do
4
+ before do
5
+ BCI.reset_config!
6
+ module Test
7
+ class PhoneType < BC::Refined
8
+ REGEX = /\A(\+7|8)(9|8)\d{9}\z/i
9
+ def match
10
+ context[:phone_input] = value.to_s
11
+ return failure(:invalid_phone) if context[:phone_input] !~ REGEX
12
+ context[:phone] = context[:phone_input]
13
+ self
14
+ end
15
+ end
16
+
17
+ class CustomInstrument
18
+ def call(session)
19
+ "[SID:#{session.id}] #{session.result_type_name}"
20
+ end
21
+
22
+ def before(session)
23
+ session.extras[:started] = true
24
+ end
25
+
26
+ def after(session)
27
+ session.extras[:finished] = true
28
+ end
29
+ end
30
+ end
31
+
32
+ BCI.configure { |cfg| cfg.instrument "Phone", custom_instrument }
33
+ end
34
+
35
+ let(:custom_instrument) { Test::CustomInstrument.new }
36
+
37
+ it do
38
+ expect(Test::PhoneType.instruments).to match_array([custom_instrument])
39
+ end
40
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe BloodContracts::Instrumentation::Session do
4
+ before do
5
+ module Test
6
+ class PhoneType < BC::Refined
7
+ REGEX = /\A(\+7|8)(9|8)\d{9}\z/i
8
+ def match
9
+ context[:phone_input] = value.to_s
10
+ return failure(:invalid_phone) if context[:phone_input] !~ REGEX
11
+ context[:phone] = context[:phone_input]
12
+ self
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ subject { described_class.new(Test::PhoneType.name) }
19
+
20
+ describe "defaults on new instance" do
21
+ it do
22
+ expect(subject.extras).to be_empty
23
+
24
+ expect(subject.context).to be_empty
25
+ expect(subject.matcher_type_name).to eq(Test::PhoneType.name)
26
+
27
+ expect(subject.result_type_name).to be_nil
28
+ expect(subject.id.size).to eq(20)
29
+
30
+ expect(subject.started_at).to be_nil
31
+ expect(subject.finished_at).to be_nil
32
+
33
+ expect(subject.scope).to be_nil
34
+ expect(subject.path).to be_nil
35
+ end
36
+ end
37
+
38
+ describe "#start" do
39
+ before { subject.start }
40
+
41
+ it do
42
+ expect(subject.extras).to be_empty
43
+ expect(subject.context).to be_empty
44
+
45
+ expect(subject.matcher_type_name).to eq(Test::PhoneType.name)
46
+ expect(subject.result_type_name).to be_nil
47
+
48
+ expect(subject.id.size).to eq(20)
49
+
50
+ expect(subject.started_at).to match(kind_of(Time))
51
+ expect(subject.finished_at).to be_nil
52
+
53
+ expect(subject.scope).to be_nil
54
+ expect(subject.path).to be_nil
55
+ end
56
+ end
57
+
58
+ describe "#finish" do
59
+ before do
60
+ subject.start
61
+ subject.finish(invalid_match)
62
+ end
63
+
64
+ let(:invalid_match) { Test::PhoneType.match("asdasdsdas") }
65
+
66
+ it do
67
+ expect(subject.extras).to be_empty
68
+ expect(subject.context).to match(invalid_match.context)
69
+
70
+ expect(subject.matcher_type_name).to eq(Test::PhoneType.name)
71
+ expect(subject.result_type_name).to eq(BC::ContractFailure.name)
72
+
73
+ expect(subject.id.size).to eq(20)
74
+
75
+ expect(subject.started_at).to match(kind_of(Time))
76
+ expect(subject.finished_at).to match(kind_of(Time))
77
+
78
+ expect(subject.scope).to eq(described_class::NO_SCOPE)
79
+ expect(subject.path).to eq(described_class::NO_VALIDATION_PATH)
80
+ end
81
+ end
82
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  RSpec.describe BloodContracts::Instrumentation do
4
4
  before do
5
+ BCI.configure { |config| config.send(:reset_types!) }
6
+
5
7
  module Test
6
8
  require "json"
7
9
 
@@ -57,7 +59,9 @@ RSpec.describe BloodContracts::Instrumentation do
57
59
  context "when type matches instruments condition" do
58
60
  let(:type) { Test::JsonType.name }
59
61
 
60
- it { is_expected.to match_array([kind_of(described_class::Instrument)]) }
62
+ it do
63
+ is_expected.to match_array([kind_of(BCI::Instrument)])
64
+ end
61
65
  end
62
66
 
63
67
  context "when type doesn't match instruments condition" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blood_contracts-instrumentation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Dolganov (sclinede)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-11 00:00:00.000000000 Z
11
+ date: 2019-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: blood_contracts-core
@@ -128,6 +128,12 @@ files:
128
128
  - lib/blood_contracts/instrumentation/session_finalizer/fibers.rb
129
129
  - lib/blood_contracts/instrumentation/session_finalizer/threads.rb
130
130
  - lib/blood_contracts/instrumentation/session_recording.rb
131
+ - spec/blood_contracts/instrumentation/config_spec.rb
132
+ - spec/blood_contracts/instrumentation/failed_match_spec.rb
133
+ - spec/blood_contracts/instrumentation/instrument_spec.rb
134
+ - spec/blood_contracts/instrumentation/session_finalizer_spec.rb
135
+ - spec/blood_contracts/instrumentation/session_recording_spec.rb
136
+ - spec/blood_contracts/instrumentation/session_spec.rb
131
137
  - spec/blood_contracts/instrumentation_spec.rb
132
138
  - spec/spec_helper.rb
133
139
  homepage: https://github.com/sclinede/blood_contracts-core
@@ -154,5 +160,11 @@ signing_key:
154
160
  specification_version: 4
155
161
  summary: Adds instrumentation to BloodContracts refinement types
156
162
  test_files:
163
+ - spec/blood_contracts/instrumentation/config_spec.rb
164
+ - spec/blood_contracts/instrumentation/failed_match_spec.rb
165
+ - spec/blood_contracts/instrumentation/instrument_spec.rb
166
+ - spec/blood_contracts/instrumentation/session_finalizer_spec.rb
167
+ - spec/blood_contracts/instrumentation/session_recording_spec.rb
168
+ - spec/blood_contracts/instrumentation/session_spec.rb
157
169
  - spec/blood_contracts/instrumentation_spec.rb
158
170
  - spec/spec_helper.rb