peak_flow_utils 0.1.14 → 0.1.17
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 +4 -4
- data/README.md +12 -0
- data/app/services/peak_flow_utils/active_job_parameters_logging.rb +13 -0
- data/app/services/peak_flow_utils/deep_merger.rb +62 -0
- data/app/services/peak_flow_utils/sidekiq_parameters_logging.rb +16 -0
- data/lib/peak_flow_utils/inherited_local_var.rb +84 -0
- data/lib/peak_flow_utils/notifier.rb +84 -26
- data/lib/peak_flow_utils/notifier_rack.rb +6 -6
- data/lib/peak_flow_utils/thread_callbacks_patch.rb +23 -0
- data/lib/peak_flow_utils/version.rb +1 -1
- data/lib/peak_flow_utils.rb +7 -1
- metadata +39 -9
- data/bin/peak_flow_rspec_files +0 -21
- data/lib/peak_flow_utils/rspec_helper.rb +0 -209
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3917057ab2d29651eec619ccb60c5a6c671ce092ada22ff80269ce399a8a7bd6
|
4
|
+
data.tar.gz: 25738f4241971584cfbbc9ea80cbf07f426c5dac8b6394398eb92212c8818be1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 144f08cb6bb1af54806406e1a9ce16ade04bea01e07cf8c16537764236f9e58d672956a9346ddc6a5088f1ed8e8a3c87ff8d02ff797f230b74e47fa685590005
|
7
|
+
data.tar.gz: 324f5e6834f219cb4a51dd2c3a1df5321f4912e6586ea9a67c90d05554aeae231466b7da5dd23148cef8426aae93dc02862c91faf56c58a7fa1a1125c01b9e61
|
data/README.md
CHANGED
@@ -39,6 +39,13 @@ Add this to `config/peakflow.rb`:
|
|
39
39
|
PeakFlowUtils::NotifierRails.configure
|
40
40
|
```
|
41
41
|
|
42
|
+
### Reporting ActiveJob errors in Rails:
|
43
|
+
|
44
|
+
If you want the job name and its arguments logged in parameters you can execute this service:
|
45
|
+
```ruby
|
46
|
+
PeakFlowUtils::ActiveJobParametersLogging.execute!
|
47
|
+
```
|
48
|
+
|
42
49
|
### Reporting Sidekiq errors in Rails:
|
43
50
|
|
44
51
|
Add this to `config/peakflow.rb`:
|
@@ -46,6 +53,11 @@ Add this to `config/peakflow.rb`:
|
|
46
53
|
PeakFlowUtils::NotifierSidekiq.configure
|
47
54
|
```
|
48
55
|
|
56
|
+
If you want the job name and its arguments logged in parameters you can execute this service:
|
57
|
+
```ruby
|
58
|
+
PeakFlowUtils::SidekiqParametersLogging.execute!
|
59
|
+
```
|
60
|
+
|
49
61
|
## Contributing
|
50
62
|
Contribution directions go here.
|
51
63
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class PeakFlowUtils::ActiveJobParametersLogging < PeakFlowUtils::ApplicationService
|
2
|
+
def perform
|
3
|
+
ActiveJob::Base.class_eval do
|
4
|
+
around_perform do |job, block|
|
5
|
+
PeakFlowUtils::Notifier.with_parameters(active_job: {job_name: job.class.name, job_arguments: job.arguments}) do
|
6
|
+
block.call
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
succeed!
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class PeakFlowUtils::DeepMerger < PeakFlowUtils::ApplicationService
|
2
|
+
attr_reader :hashes, :object_mappings
|
3
|
+
|
4
|
+
def initialize(hashes:, object_mappings: {})
|
5
|
+
@hashes = hashes
|
6
|
+
@object_mappings = object_mappings
|
7
|
+
end
|
8
|
+
|
9
|
+
def perform
|
10
|
+
merged = {}
|
11
|
+
|
12
|
+
hashes.each do |hash|
|
13
|
+
merge_hash(hash, merged)
|
14
|
+
end
|
15
|
+
|
16
|
+
succeed! merged
|
17
|
+
end
|
18
|
+
|
19
|
+
def clone_something(object)
|
20
|
+
if object.is_a?(Hash)
|
21
|
+
new_hash = {}
|
22
|
+
merge_hash(object, new_hash)
|
23
|
+
new_hash
|
24
|
+
elsif object.is_a?(Array)
|
25
|
+
new_array = []
|
26
|
+
merge_array(object, new_array)
|
27
|
+
new_array
|
28
|
+
else
|
29
|
+
object
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def merge_something(object, merged)
|
34
|
+
if object.is_a?(Array)
|
35
|
+
merge_array(object, merged)
|
36
|
+
elsif object.is_a?(Hash)
|
37
|
+
merge_hash(object, merged)
|
38
|
+
else
|
39
|
+
raise "Unknown object: #{object.class.name}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def merge_array(array, merged)
|
44
|
+
array.each do |value|
|
45
|
+
merged << clone_something(value)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def merge_hash(hash, merged)
|
50
|
+
hash.each do |key, value|
|
51
|
+
if value.is_a?(Array)
|
52
|
+
merged[key] = []
|
53
|
+
merge_array(value, merged[key])
|
54
|
+
elsif value.is_a?(Hash)
|
55
|
+
merged[key] ||= {}
|
56
|
+
merge_hash(value, merged[key])
|
57
|
+
else
|
58
|
+
merged[key] = clone_something(value)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class PeakFlowUtils::SidekiqParametersLogging < PeakFlowUtils::ApplicationService
|
2
|
+
def perform
|
3
|
+
require "sidekiq"
|
4
|
+
require "sidekiq/processor"
|
5
|
+
|
6
|
+
Sidekiq::Processor.class_eval do
|
7
|
+
def execute_job(worker, cloned_args)
|
8
|
+
PeakFlowUtils::Notifier.with_parameters(sidekiq: {worker_class_name: worker.class.name, cloned_args: cloned_args}) do
|
9
|
+
worker.perform(*cloned_args)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
succeed!
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "monitor"
|
2
|
+
require_relative "thread_callbacks_patch"
|
3
|
+
|
4
|
+
Thread.on_initialize do |parent:, thread:|
|
5
|
+
thread.instance_variable_set(:@_inherited_local_vars, parent.instance_variable_get(:@_inherited_local_vars))
|
6
|
+
end
|
7
|
+
|
8
|
+
Thread.class_eval do
|
9
|
+
def self.inherited_local_vars_mutex
|
10
|
+
@inherited_local_vars_mutex ||= Mutex.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def self._inherited_local_vars
|
14
|
+
Thread.current.instance_variable_set(:@_inherited_local_vars, {}) unless Thread.current.instance_variable_get(:@_inherited_local_vars)
|
15
|
+
Thread.current.instance_variable_get(:@_inherited_local_vars)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.inherited_local_vars_reset
|
19
|
+
ObjectSpace.each_object(Thread) do |thread|
|
20
|
+
inherited_local_vars_mutex.synchronize do
|
21
|
+
thread.instance_variable_set(:@_inherited_local_vars, nil)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.inherited_local_vars_delete(key)
|
27
|
+
inherited_local_vars_mutex.synchronize do
|
28
|
+
raise "Key didn't exist: #{key}" unless _inherited_local_vars.key?(key)
|
29
|
+
|
30
|
+
_inherited_local_vars.delete(key)
|
31
|
+
end
|
32
|
+
rescue ThreadError # This can happen when process is closing down
|
33
|
+
_inherited_local_vars.delete(key)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.inherited_local_vars_fetch(key)
|
37
|
+
inherited_local_vars_mutex.synchronize do
|
38
|
+
return _inherited_local_vars.fetch(key)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.inherited_local_vars_get(key)
|
43
|
+
inherited_local_vars_mutex.synchronize do
|
44
|
+
return _inherited_local_vars[key]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.inherited_local_vars_set(values)
|
49
|
+
inherited_local_vars_mutex.synchronize do
|
50
|
+
current_vars = _inherited_local_vars
|
51
|
+
new_vars = PeakFlowUtils::DeepMerger.execute!(hashes: [current_vars, values])
|
52
|
+
Thread.current.instance_variable_set(:@_inherited_local_vars, new_vars)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class PeakFlowUtils::InheritedLocalVar
|
58
|
+
attr_reader :identifier
|
59
|
+
|
60
|
+
def self.finalize(inherited_local_var_object_id)
|
61
|
+
Thread.inherited_local_vars_delete("inherited_local_var_#{inherited_local_var_object_id}")
|
62
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
63
|
+
puts e.inspect # rubocop:disable Rails/Output
|
64
|
+
puts e.backtrace # rubocop:disable Rails/Output
|
65
|
+
|
66
|
+
raise e
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize(new_value = nil)
|
70
|
+
ObjectSpace.define_finalizer(self, PeakFlowUtils::InheritedLocalVar.method(:finalize))
|
71
|
+
|
72
|
+
@identifier = "inherited_local_var_#{__id__}"
|
73
|
+
|
74
|
+
Thread.inherited_local_vars_set(identifier => new_value)
|
75
|
+
end
|
76
|
+
|
77
|
+
def value
|
78
|
+
Thread.inherited_local_vars_fetch(identifier)
|
79
|
+
end
|
80
|
+
|
81
|
+
def value=(new_value)
|
82
|
+
Thread.inherited_local_vars_set(identifier => new_value)
|
83
|
+
end
|
84
|
+
end
|
@@ -1,38 +1,94 @@
|
|
1
1
|
class PeakFlowUtils::Notifier
|
2
2
|
class FailedToReportError < RuntimeError; end
|
3
3
|
class NotConfiguredError < RuntimeError; end
|
4
|
+
class NotifyMessageError < RuntimeError; end
|
4
5
|
|
5
|
-
attr_reader :auth_token
|
6
|
+
attr_reader :auth_token, :mutex, :parameters
|
6
7
|
|
7
8
|
def self.configure(auth_token:)
|
8
9
|
@current = PeakFlowUtils::Notifier.new(auth_token: auth_token)
|
9
10
|
end
|
10
11
|
|
11
|
-
def self.current
|
12
|
-
raise PeakFlowUtils::Notifier::NotConfiguredError, "Hasn't been configured" if !@current && Rails.env.test?
|
13
|
-
|
12
|
+
def self.current # rubocop:disable Style/TrivialAccessors
|
14
13
|
@current
|
15
14
|
end
|
16
15
|
|
17
|
-
def self.notify(*args)
|
18
|
-
PeakFlowUtils::Notifier.current
|
16
|
+
def self.notify(*args, **opts, &blk)
|
17
|
+
PeakFlowUtils::Notifier.current&.notify(*args, **opts, &blk)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.notify_message(*args, **opts, &blk)
|
21
|
+
PeakFlowUtils::Notifier.current&.notify_message(*args, **opts, &blk)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.reset_parameters
|
25
|
+
::PeakFlowUtils::Notifier.current&.instance_variable_set(:@parameters, ::PeakFlowUtils::InheritedLocalVar.new({}))
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.with_parameters(parameters)
|
29
|
+
return yield unless ::PeakFlowUtils::Notifier.current
|
30
|
+
|
31
|
+
random_id = ::SecureRandom.hex(16)
|
32
|
+
|
33
|
+
::PeakFlowUtils::Notifier.current.mutex.synchronize do
|
34
|
+
raise "'parameters' was nil?" if ::PeakFlowUtils::Notifier.current.parameters.value.nil?
|
35
|
+
|
36
|
+
parameters_with = ::PeakFlowUtils::Notifier.current.parameters.value.clone
|
37
|
+
parameters_with[random_id] = parameters
|
38
|
+
|
39
|
+
::PeakFlowUtils::Notifier.current.parameters.value = parameters_with
|
40
|
+
end
|
41
|
+
|
42
|
+
begin
|
43
|
+
yield
|
44
|
+
ensure
|
45
|
+
::PeakFlowUtils::Notifier.current.mutex.synchronize do
|
46
|
+
parameters_without = ::PeakFlowUtils::Notifier.current.parameters.value.clone
|
47
|
+
parameters_without.delete(random_id)
|
48
|
+
|
49
|
+
::PeakFlowUtils::Notifier.current.parameters.value = parameters_without
|
50
|
+
end
|
51
|
+
end
|
19
52
|
end
|
20
53
|
|
21
54
|
def initialize(auth_token:)
|
22
55
|
@auth_token = auth_token
|
56
|
+
@mutex = ::Mutex.new
|
57
|
+
@parameters = ::PeakFlowUtils::InheritedLocalVar.new({})
|
58
|
+
end
|
59
|
+
|
60
|
+
def current_parameters(parameters: nil)
|
61
|
+
hashes = current_parameters_hashes
|
62
|
+
hashes << parameters if parameters
|
63
|
+
|
64
|
+
::PeakFlowUtils::DeepMerger.execute!(hashes: hashes)
|
65
|
+
end
|
66
|
+
|
67
|
+
def current_parameters_hashes
|
68
|
+
parameters.value.values
|
69
|
+
end
|
70
|
+
|
71
|
+
def error_message_from_response(response)
|
72
|
+
message = "Couldn't report error to Peakflow (code #{response.code})"
|
73
|
+
|
74
|
+
if response["content-type"]&.starts_with?("application/json")
|
75
|
+
response_data = ::JSON.parse(response.body)
|
76
|
+
message << ": #{response_data.fetch("errors").join(". ")}" if response_data["errors"]
|
77
|
+
end
|
78
|
+
|
79
|
+
message
|
23
80
|
end
|
24
81
|
|
25
82
|
def notify(error:, environment: nil, parameters: nil)
|
26
|
-
error_parser = PeakFlowUtils::NotifierErrorParser.new(
|
83
|
+
error_parser = ::PeakFlowUtils::NotifierErrorParser.new(
|
27
84
|
backtrace: error.backtrace,
|
28
85
|
environment: environment,
|
29
86
|
error: error
|
30
87
|
)
|
31
88
|
|
32
|
-
|
89
|
+
merged_parameters = current_parameters(parameters: parameters)
|
33
90
|
|
34
|
-
|
35
|
-
https.use_ssl = true
|
91
|
+
uri = URI("https://www.peakflow.io/errors/reports")
|
36
92
|
|
37
93
|
data = {
|
38
94
|
auth_token: auth_token,
|
@@ -43,25 +99,38 @@ class PeakFlowUtils::Notifier
|
|
43
99
|
file_path: error_parser.file_path,
|
44
100
|
line_number: error_parser.line_number,
|
45
101
|
message: error.message,
|
46
|
-
parameters:
|
102
|
+
parameters: merged_parameters,
|
47
103
|
remote_ip: error_parser.remote_ip,
|
48
104
|
url: error_parser.url,
|
49
105
|
user_agent: error_parser.user_agent
|
50
106
|
}
|
51
107
|
}
|
52
108
|
|
53
|
-
|
109
|
+
send_notify_request(data: data, uri: uri)
|
110
|
+
end
|
111
|
+
|
112
|
+
def notify_message(message, **opts)
|
113
|
+
raise NotifyMessageError, message
|
114
|
+
rescue NotifyMessageError => e
|
115
|
+
notify(error: e, **opts)
|
116
|
+
end
|
117
|
+
|
118
|
+
def send_notify_request(data:, uri:)
|
119
|
+
https = ::Net::HTTP.new(uri.host, uri.port)
|
120
|
+
https.use_ssl = true
|
121
|
+
|
122
|
+
request = ::Net::HTTP::Post.new(uri.path)
|
54
123
|
request["Content-Type"] = "application/json"
|
55
|
-
request.body = JSON.generate(data)
|
124
|
+
request.body = ::JSON.generate(data)
|
56
125
|
|
57
126
|
response = https.request(request)
|
58
127
|
|
59
128
|
raise FailedToReportError, error_message_from_response(response) unless response.code == "200"
|
60
129
|
|
61
|
-
response_data = JSON.parse(response.body)
|
130
|
+
response_data = ::JSON.parse(response.body)
|
62
131
|
|
63
132
|
# Data not always present so dont use fetch
|
64
|
-
PeakFlowUtils::NotifierResponse.new(
|
133
|
+
::PeakFlowUtils::NotifierResponse.new(
|
65
134
|
bug_report_id: response_data["bug_report_id"],
|
66
135
|
bug_report_instance_id: response_data["bug_report_instance_id"],
|
67
136
|
project_id: response_data["project_id"],
|
@@ -69,15 +138,4 @@ class PeakFlowUtils::Notifier
|
|
69
138
|
url: response_data["url"]
|
70
139
|
)
|
71
140
|
end
|
72
|
-
|
73
|
-
def error_message_from_response(response)
|
74
|
-
message = "Couldn't report error to Peakflow (code #{response.code})"
|
75
|
-
|
76
|
-
if response["content-type"]&.starts_with?("application/json")
|
77
|
-
response_data = JSON.parse(response.body)
|
78
|
-
message << ": #{response_data.fetch("errors").join(". ")}" if response_data["errors"]
|
79
|
-
end
|
80
|
-
|
81
|
-
message
|
82
|
-
end
|
83
141
|
end
|
@@ -9,13 +9,13 @@ class PeakFlowUtils::NotifierRack
|
|
9
9
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
10
10
|
controller = env["action_controller.instance"]
|
11
11
|
request = controller&.request
|
12
|
-
parameters = {}.merge(request.GET).merge(request.POST)
|
13
12
|
|
14
|
-
PeakFlowUtils::Notifier.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
PeakFlowUtils::Notifier.with_parameters(rack: {get: request.GET, post: request.POST}) do
|
14
|
+
PeakFlowUtils::Notifier.notify(
|
15
|
+
environment: env,
|
16
|
+
error: e
|
17
|
+
)
|
18
|
+
end
|
19
19
|
|
20
20
|
raise e
|
21
21
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Thread
|
2
|
+
alias_method :_initialize, :initialize # rubocop:disable Style/Alias
|
3
|
+
|
4
|
+
def self.on_initialize(&callback)
|
5
|
+
@@on_initialize_count = 0 if @on_initialize_count.nil? # rubocop:disable Style/ClassVars
|
6
|
+
count_to_use = @@on_initialize_count
|
7
|
+
@@on_initialize_count += 1 # rubocop:disable Style/ClassVars
|
8
|
+
|
9
|
+
@@on_initialize_callbacks ||= {} # rubocop:disable Style/ClassVars
|
10
|
+
@@on_initialize_callbacks[count_to_use] = callback
|
11
|
+
|
12
|
+
count_to_use
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(*args, &block)
|
16
|
+
@@on_initialize_callbacks ||= {} # rubocop:disable Style/ClassVars
|
17
|
+
@@on_initialize_callbacks.each_value do |callback|
|
18
|
+
callback.call(parent: Thread.current, thread: self)
|
19
|
+
end
|
20
|
+
|
21
|
+
_initialize(*args, &block)
|
22
|
+
end
|
23
|
+
end
|
data/lib/peak_flow_utils.rb
CHANGED
@@ -6,14 +6,15 @@ require "service_pattern"
|
|
6
6
|
module PeakFlowUtils
|
7
7
|
path = "#{__dir__}/peak_flow_utils"
|
8
8
|
models_path = "#{__dir__}/peak_flow_utils/models"
|
9
|
+
services_path = File.realpath("#{__dir__}/../app/services/peak_flow_utils")
|
9
10
|
|
11
|
+
autoload :InheritedLocalVar, "#{path}/inherited_local_var"
|
10
12
|
autoload :Notifier, "#{path}/notifier"
|
11
13
|
autoload :NotifierErrorParser, "#{path}/notifier_error_parser"
|
12
14
|
autoload :NotifierRack, "#{path}/notifier_rack"
|
13
15
|
autoload :NotifierRails, "#{path}/notifier_rails"
|
14
16
|
autoload :NotifierResponse, "#{path}/notifier_response"
|
15
17
|
autoload :NotifierSidekiq, "#{path}/notifier_sidekiq"
|
16
|
-
autoload :RspecHelper, "#{path}/rspec_helper"
|
17
18
|
autoload :HandlerHelper, "#{path}/handler_helper"
|
18
19
|
|
19
20
|
autoload :ApplicationRecord, "#{models_path}/application_record"
|
@@ -23,4 +24,9 @@ module PeakFlowUtils
|
|
23
24
|
autoload :ScannedFile, "#{models_path}/scanned_file"
|
24
25
|
autoload :TranslationKey, "#{models_path}/translation_key"
|
25
26
|
autoload :TranslationValue, "#{models_path}/translation_value"
|
27
|
+
|
28
|
+
autoload :ActiveJobParametersLogging, "#{services_path}/active_job_parameters_logging"
|
29
|
+
autoload :ApplicationService, "#{services_path}/application_service"
|
30
|
+
autoload :SidekiqParametersLogging, "#{services_path}/sidekiq_parameters_logging"
|
31
|
+
autoload :DeepMerger, "#{services_path}/deep_merger"
|
26
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: peak_flow_utils
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.17
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kaspernj
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -58,14 +58,42 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.0.
|
61
|
+
version: 1.0.5
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.0.
|
68
|
+
version: 1.0.5
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: appraisal
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry-rails
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
98
|
name: redis
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -111,8 +139,7 @@ dependencies:
|
|
111
139
|
description: Utilities to be used with PeakFlow.
|
112
140
|
email:
|
113
141
|
- kaspernj@gmail.com
|
114
|
-
executables:
|
115
|
-
- peak_flow_rspec_files
|
142
|
+
executables: []
|
116
143
|
extensions: []
|
117
144
|
extra_rdoc_files: []
|
118
145
|
files:
|
@@ -130,24 +157,27 @@ files:
|
|
130
157
|
- app/handlers/peak_flow_utils/simple_form_handler.rb
|
131
158
|
- app/handlers/peak_flow_utils/validations_handler.rb
|
132
159
|
- app/handlers/peak_flow_utils/will_paginate_handler.rb
|
160
|
+
- app/services/peak_flow_utils/active_job_parameters_logging.rb
|
133
161
|
- app/services/peak_flow_utils/application_migration.rb
|
134
162
|
- app/services/peak_flow_utils/application_service.rb
|
135
163
|
- app/services/peak_flow_utils/attribute_service.rb
|
136
164
|
- app/services/peak_flow_utils/configuration_service.rb
|
137
165
|
- app/services/peak_flow_utils/database_initializer_service.rb
|
166
|
+
- app/services/peak_flow_utils/deep_merger.rb
|
138
167
|
- app/services/peak_flow_utils/erb_inspector.rb
|
139
168
|
- app/services/peak_flow_utils/erb_inspector/file_inspector.rb
|
140
169
|
- app/services/peak_flow_utils/erb_inspector/translation_inspector.rb
|
141
170
|
- app/services/peak_flow_utils/group_service.rb
|
142
171
|
- app/services/peak_flow_utils/handlers_finder_service.rb
|
143
172
|
- app/services/peak_flow_utils/model_inspector.rb
|
173
|
+
- app/services/peak_flow_utils/sidekiq_parameters_logging.rb
|
144
174
|
- app/services/peak_flow_utils/translation_service.rb
|
145
175
|
- app/services/peak_flow_utils/translations_parser_service.rb
|
146
|
-
- bin/peak_flow_rspec_files
|
147
176
|
- config/routes.rb
|
148
177
|
- lib/peak_flow_utils.rb
|
149
178
|
- lib/peak_flow_utils/engine.rb
|
150
179
|
- lib/peak_flow_utils/handler_helper.rb
|
180
|
+
- lib/peak_flow_utils/inherited_local_var.rb
|
151
181
|
- lib/peak_flow_utils/migrations/20150902155200_create_translation_keys.rb
|
152
182
|
- lib/peak_flow_utils/migrations/20150907070908_create_handlers.rb
|
153
183
|
- lib/peak_flow_utils/migrations/20150907070909_create_groups.rb
|
@@ -167,7 +197,7 @@ files:
|
|
167
197
|
- lib/peak_flow_utils/notifier_rails.rb
|
168
198
|
- lib/peak_flow_utils/notifier_response.rb
|
169
199
|
- lib/peak_flow_utils/notifier_sidekiq.rb
|
170
|
-
- lib/peak_flow_utils/
|
200
|
+
- lib/peak_flow_utils/thread_callbacks_patch.rb
|
171
201
|
- lib/peak_flow_utils/version.rb
|
172
202
|
- lib/tasks/peak_flow_utils_tasks.rake
|
173
203
|
homepage: https://github.com/kaspernj/peak_flow_utils
|
@@ -189,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
219
|
- !ruby/object:Gem::Version
|
190
220
|
version: '0'
|
191
221
|
requirements: []
|
192
|
-
rubygems_version: 3.
|
222
|
+
rubygems_version: 3.2.32
|
193
223
|
signing_key:
|
194
224
|
specification_version: 4
|
195
225
|
summary: Utilities to be used with PeakFlow.
|
data/bin/peak_flow_rspec_files
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# This task detects and prints out the RSpec files for the current build group
|
4
|
-
|
5
|
-
require "#{__dir__}/../lib/peak_flow_utils"
|
6
|
-
|
7
|
-
args = {}
|
8
|
-
ARGV.each do |arg|
|
9
|
-
if (match = arg.match(/\A--(.+?)=(.+)\Z/))
|
10
|
-
args[match[1]] = match[2]
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
rspec_helper = PeakFlowUtils::RspecHelper.new(
|
15
|
-
groups: args.fetch("groups").to_i,
|
16
|
-
group_number: args.fetch("group-number").to_i,
|
17
|
-
only_types: args["only-types"]&.split(","),
|
18
|
-
tags: args["tags"]&.split(",")
|
19
|
-
)
|
20
|
-
|
21
|
-
print rspec_helper.group_files.map { |group_file| group_file.fetch(:path) }.join(" ")
|
@@ -1,209 +0,0 @@
|
|
1
|
-
class PeakFlowUtils::RspecHelper
|
2
|
-
attr_reader :only_types, :tags
|
3
|
-
|
4
|
-
def initialize(groups:, group_number:, only_types: nil, tags: nil)
|
5
|
-
@groups = groups
|
6
|
-
@group_number = group_number
|
7
|
-
@example_data_exists = File.exist?("spec/examples.txt")
|
8
|
-
@only_types = only_types
|
9
|
-
@tags = tags
|
10
|
-
end
|
11
|
-
|
12
|
-
def example_data_exists?
|
13
|
-
@example_data_exists
|
14
|
-
end
|
15
|
-
|
16
|
-
def example_data
|
17
|
-
@example_data ||= begin
|
18
|
-
raw_data = File.read("spec/examples.txt")
|
19
|
-
|
20
|
-
result = []
|
21
|
-
raw_data.scan(/^\.\/(.+)\[(.+?)\]\s+\|\s+(.+?)\s+\|\s+((.+?) seconds|)\s+\|$/) do |match|
|
22
|
-
file_path = match[0]
|
23
|
-
spec_result = match[1]
|
24
|
-
seconds = match[4]&.to_f
|
25
|
-
|
26
|
-
spec_data = {
|
27
|
-
file_path: file_path,
|
28
|
-
spec_result: spec_result,
|
29
|
-
seconds: seconds
|
30
|
-
}
|
31
|
-
|
32
|
-
result << spec_data
|
33
|
-
end
|
34
|
-
|
35
|
-
result
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def example_files
|
40
|
-
@example_files ||= begin
|
41
|
-
files = {}
|
42
|
-
example_data.each do |spec_data|
|
43
|
-
file_path = spec_data.fetch(:file_path)
|
44
|
-
seconds = spec_data.fetch(:seconds)
|
45
|
-
|
46
|
-
files[file_path] ||= {examples: 0, seconds: 0.0}
|
47
|
-
files[file_path][:examples] += 1
|
48
|
-
files[file_path][:seconds] += seconds if seconds
|
49
|
-
end
|
50
|
-
|
51
|
-
files
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def example_file(path)
|
56
|
-
example_files[path]
|
57
|
-
end
|
58
|
-
|
59
|
-
def group_files
|
60
|
-
return @group_files if @group_files
|
61
|
-
|
62
|
-
sorted_files.each do |file|
|
63
|
-
file_path = file.fetch(:path)
|
64
|
-
file_data = example_file(file_path) if example_data_exists?
|
65
|
-
|
66
|
-
if file_data
|
67
|
-
examples = file_data.fetch(:examples)
|
68
|
-
seconds = file_data.fetch(:seconds)
|
69
|
-
else
|
70
|
-
examples = file.fetch(:examples)
|
71
|
-
end
|
72
|
-
|
73
|
-
group = group_with_least
|
74
|
-
group[:examples] += examples
|
75
|
-
group[:files] << file
|
76
|
-
group[:seconds] += seconds if seconds
|
77
|
-
end
|
78
|
-
|
79
|
-
@group_files = group_orders[@group_number - 1].fetch(:files)
|
80
|
-
end
|
81
|
-
|
82
|
-
def group_orders
|
83
|
-
@group_orders ||= begin
|
84
|
-
group_orders = []
|
85
|
-
@groups.times do
|
86
|
-
group_orders << {
|
87
|
-
examples: 0,
|
88
|
-
files: [],
|
89
|
-
seconds: 0.0
|
90
|
-
}
|
91
|
-
end
|
92
|
-
group_orders
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def group_with_least
|
97
|
-
group_orders.min do |group1, group2|
|
98
|
-
if example_data_exists? && group1.fetch(:seconds) != 0.0 && group2.fetch(:seconds) != 0.0
|
99
|
-
group1.fetch(:seconds) <=> group2.fetch(:seconds)
|
100
|
-
else
|
101
|
-
group1.fetch(:examples) <=> group2.fetch(:examples)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# Sort them so that they are sorted by file path in three groups so each group have an equal amount of controller specs, features specs and so on
|
107
|
-
def sorted_files
|
108
|
-
files.values.sort do |file1, file2|
|
109
|
-
file1_path = file1.fetch(:path)
|
110
|
-
file2_path = file2.fetch(:path)
|
111
|
-
|
112
|
-
file1_data = example_file(file1_path) if example_data_exists?
|
113
|
-
file2_data = example_file(file2_path) if example_data_exists?
|
114
|
-
|
115
|
-
if file1_data && file2_data && file1_data.fetch(:seconds) != 0.0 && file2_data.fetch(:seconds) != 0.0
|
116
|
-
value1 = file1_data[:seconds]
|
117
|
-
else
|
118
|
-
value1 = file1.fetch(:points)
|
119
|
-
end
|
120
|
-
|
121
|
-
if file2_data && file1_data && file2_data.fetch(:seconds) != 0.0 && file2_data.fetch(:seconds) != 0.0
|
122
|
-
value2 = file2_data[:seconds]
|
123
|
-
else
|
124
|
-
value2 = file2.fetch(:points)
|
125
|
-
end
|
126
|
-
|
127
|
-
if value1 == value2
|
128
|
-
value2 = file1_path
|
129
|
-
value1 = file2_path
|
130
|
-
end
|
131
|
-
|
132
|
-
value2 <=> value1
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
private
|
137
|
-
|
138
|
-
def dry_result
|
139
|
-
@dry_result ||= begin
|
140
|
-
require "json"
|
141
|
-
require "rspec/core"
|
142
|
-
|
143
|
-
output_capture = StringIO.new
|
144
|
-
RSpec::Core::Runner.run(rspec_options, $stderr, output_capture)
|
145
|
-
|
146
|
-
result = ::JSON.parse(output_capture.string)
|
147
|
-
|
148
|
-
raise "No examples were found" if result.fetch("examples").empty?
|
149
|
-
|
150
|
-
result
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
def dry_file(path)
|
155
|
-
files.fetch(path)
|
156
|
-
end
|
157
|
-
|
158
|
-
def files
|
159
|
-
@files ||= begin
|
160
|
-
result = {}
|
161
|
-
dry_result.fetch("examples").each do |example|
|
162
|
-
file_path = example.fetch("file_path")
|
163
|
-
file_path = file_path[2, file_path.length]
|
164
|
-
type = type_from_path(file_path)
|
165
|
-
points = points_from_type(type)
|
166
|
-
|
167
|
-
next if ignore_type?(type)
|
168
|
-
|
169
|
-
result[file_path] = {examples: 0, path: file_path, points: 0, type: type} unless result.key?(file_path)
|
170
|
-
result[file_path][:examples] += 1
|
171
|
-
result[file_path][:points] += points
|
172
|
-
end
|
173
|
-
|
174
|
-
result
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
def ignore_type?(type)
|
179
|
-
only_types && !only_types.include?(type) # rubocop:disable Rails/NegateInclude:, Style/SafeNavigation
|
180
|
-
end
|
181
|
-
|
182
|
-
def points_from_type(type)
|
183
|
-
if type == "feature" || type == "system"
|
184
|
-
10
|
185
|
-
elsif type == "controllers"
|
186
|
-
3
|
187
|
-
else
|
188
|
-
1
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
def rspec_options
|
193
|
-
rspec_options = ["--dry-run", "--format", "json"]
|
194
|
-
|
195
|
-
tags&.each do |tag|
|
196
|
-
rspec_options += ["--tag", tag]
|
197
|
-
end
|
198
|
-
|
199
|
-
# Add the folder with all the specs, which is required when running programmatically
|
200
|
-
rspec_options << "spec"
|
201
|
-
|
202
|
-
rspec_options
|
203
|
-
end
|
204
|
-
|
205
|
-
def type_from_path(file_path)
|
206
|
-
match = file_path.match(/^spec\/(.+?)\//)
|
207
|
-
match[1] if match
|
208
|
-
end
|
209
|
-
end
|