peak_flow_utils 0.1.15 → 0.1.16

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: b4fb7a276f751a58ba19b74e73955323e0f1b89215312b50ee3e3b9cf89a85f4
4
- data.tar.gz: 29edd9b2f4225cf2ea74c67d4fccd1b21a0149781f2cacc168309ef8dd3082c6
3
+ metadata.gz: c01a814a1aad134f668f7144f6addea59f8b8afdc75a2ae8ff2280dbe3a9867c
4
+ data.tar.gz: b6746741a1ff204c7b2a31319409d84a4529c14a55f8e9cfbf7b7fcad37eacea
5
5
  SHA512:
6
- metadata.gz: 18328934879ae9fbb148e812926d68b5dca82837f929e5c3f3228bc4bc34bb140e8d42013333ef68976931819960d45110b26a8bf8bb2b37e363b8a519bf0ee9
7
- data.tar.gz: 4251f1f1213fc0e9dfa426c7c4a1e75c53dbd23dff37ce09213f857ff7b7fba56251875d17a7baed74273d744f29b03bd9fa3e41f7a877319ada3cf9760bf9b4
6
+ metadata.gz: 5a82e952b87453cfde4fbcb42fd43a0f2bca93f43dc2b3dbdca3109c6d25a21dd587745c602680d89c446cda1c1be251167b46248b98bc1e5354f408ae9f2758
7
+ data.tar.gz: d2020073122505feaa6cb3923f79a76ff6f8608ac45af16625cae83f79c6232a9f4ac677e834faa015bfd1d9e287946b4cc0662557507dcfab89ea7c3faf665e
@@ -1,18 +1,35 @@
1
1
  class PeakFlowUtils::DeepMerger < PeakFlowUtils::ApplicationService
2
- def initialize(hashes:)
2
+ attr_reader :hashes, :object_mappings
3
+
4
+ def initialize(hashes:, object_mappings: {})
3
5
  @hashes = hashes
6
+ @object_mappings = object_mappings
4
7
  end
5
8
 
6
9
  def perform
7
10
  merged = {}
8
11
 
9
- @hashes.each do |hash|
12
+ hashes.each do |hash|
10
13
  merge_hash(hash, merged)
11
14
  end
12
15
 
13
16
  succeed! merged
14
17
  end
15
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
+
16
33
  def merge_something(object, merged)
17
34
  if object.is_a?(Array)
18
35
  merge_array(object, merged)
@@ -25,20 +42,20 @@ class PeakFlowUtils::DeepMerger < PeakFlowUtils::ApplicationService
25
42
 
26
43
  def merge_array(array, merged)
27
44
  array.each do |value|
28
- merged << value
45
+ merged << clone_something(value)
29
46
  end
30
47
  end
31
48
 
32
49
  def merge_hash(hash, merged)
33
50
  hash.each do |key, value|
34
- if !merged.key?(key)
35
- merged[key] = value
36
- elsif value.is_a?(Array)
51
+ if value.is_a?(Array)
52
+ merged[key] = []
37
53
  merge_array(value, merged[key])
38
54
  elsif value.is_a?(Hash)
55
+ merged[key] ||= {}
39
56
  merge_hash(value, merged[key])
40
57
  else
41
- raise "Unknown object: #{object.class.name}"
58
+ merged[key] = clone_something(value)
42
59
  end
43
60
  end
44
61
  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
@@ -2,73 +2,89 @@ class PeakFlowUtils::Notifier
2
2
  class FailedToReportError < RuntimeError; end
3
3
  class NotConfiguredError < RuntimeError; end
4
4
 
5
- attr_reader :auth_token
5
+ attr_reader :auth_token, :mutex, :parameters
6
6
 
7
7
  def self.configure(auth_token:)
8
8
  @current = PeakFlowUtils::Notifier.new(auth_token: auth_token)
9
9
  end
10
10
 
11
- def self.current
12
- raise PeakFlowUtils::Notifier::NotConfiguredError, "Hasn't been configured" if !@current && Rails.env.test?
13
-
11
+ def self.current # rubocop:disable Style/TrivialAccessors
14
12
  @current
15
13
  end
16
14
 
17
- def self.current_parameters(parameters: nil)
18
- hashes = PeakFlowUtils::Notifier.current_parameters_hashes
19
- hashes << parameters if parameters
20
-
21
- PeakFlowUtils::DeepMerger.execute!(hashes: hashes)
15
+ def self.notify(*args, **opts, &blk)
16
+ PeakFlowUtils::Notifier.current&.notify(*args, **opts, &blk)
22
17
  end
23
18
 
24
- def self.current_parameters_hashes
25
- hashes = []
19
+ def self.reset_parameters
20
+ ::PeakFlowUtils::Notifier.current&.instance_variable_set(:@parameters, ::PeakFlowUtils::InheritedLocalVar.new({}))
21
+ end
26
22
 
27
- Thread.current[:peakflow_utils] ||= {}
28
- Thread.current[:peakflow_utils].dig(:notifier, :with_parameters)&.each_value do |more_parameters|
29
- hashes << more_parameters
30
- end
23
+ def self.with_parameters(parameters)
24
+ return yield unless ::PeakFlowUtils::Notifier.current
31
25
 
32
- hashes
33
- end
26
+ random_id = ::SecureRandom.hex(16)
34
27
 
35
- def self.notify(*args, **opts, &blk)
36
- PeakFlowUtils::Notifier.current.notify(*args, **opts, &blk)
37
- end
28
+ ::PeakFlowUtils::Notifier.current.mutex.synchronize do
29
+ raise "'parameters' was nil?" if ::PeakFlowUtils::Notifier.current.parameters.value.nil?
38
30
 
39
- def self.with_parameters(parameters)
40
- random_id = SecureRandom.hex(16)
31
+ parameters_with = ::PeakFlowUtils::Notifier.current.parameters.value.clone
32
+ parameters_with[random_id] = parameters
41
33
 
42
- Thread.current[:peakflow_utils] ||= {}
43
- Thread.current[:peakflow_utils][:notifier] ||= {}
44
- Thread.current[:peakflow_utils][:notifier][:with_parameters] ||= {}
45
- Thread.current[:peakflow_utils][:notifier][:with_parameters][random_id] = parameters
34
+ ::PeakFlowUtils::Notifier.current.parameters.value = parameters_with
35
+ end
46
36
 
47
37
  begin
48
38
  yield
49
39
  ensure
50
- Thread.current[:peakflow_utils][:notifier][:with_parameters].delete(random_id)
40
+ ::PeakFlowUtils::Notifier.current.mutex.synchronize do
41
+ parameters_without = ::PeakFlowUtils::Notifier.current.parameters.value.clone
42
+ parameters_without.delete(random_id)
43
+
44
+ ::PeakFlowUtils::Notifier.current.parameters.value = parameters_without
45
+ end
51
46
  end
52
47
  end
53
48
 
54
49
  def initialize(auth_token:)
55
50
  @auth_token = auth_token
51
+ @mutex = ::Mutex.new
52
+ @parameters = ::PeakFlowUtils::InheritedLocalVar.new({})
53
+ end
54
+
55
+ def current_parameters(parameters: nil)
56
+ hashes = current_parameters_hashes
57
+ hashes << parameters if parameters
58
+
59
+ ::PeakFlowUtils::DeepMerger.execute!(hashes: hashes)
60
+ end
61
+
62
+ def current_parameters_hashes
63
+ parameters.value.values
64
+ end
65
+
66
+ def error_message_from_response(response)
67
+ message = "Couldn't report error to Peakflow (code #{response.code})"
68
+
69
+ if response["content-type"]&.starts_with?("application/json")
70
+ response_data = ::JSON.parse(response.body)
71
+ message << ": #{response_data.fetch("errors").join(". ")}" if response_data["errors"]
72
+ end
73
+
74
+ message
56
75
  end
57
76
 
58
77
  def notify(error:, environment: nil, parameters: nil)
59
- error_parser = PeakFlowUtils::NotifierErrorParser.new(
78
+ error_parser = ::PeakFlowUtils::NotifierErrorParser.new(
60
79
  backtrace: error.backtrace,
61
80
  environment: environment,
62
81
  error: error
63
82
  )
64
83
 
65
- merged_parameters = PeakFlowUtils::Notifier.current_parameters(parameters: parameters)
84
+ merged_parameters = current_parameters(parameters: parameters)
66
85
 
67
86
  uri = URI("https://www.peakflow.io/errors/reports")
68
87
 
69
- https = Net::HTTP.new(uri.host, uri.port)
70
- https.use_ssl = true
71
-
72
88
  data = {
73
89
  auth_token: auth_token,
74
90
  error: {
@@ -85,18 +101,25 @@ class PeakFlowUtils::Notifier
85
101
  }
86
102
  }
87
103
 
88
- request = Net::HTTP::Post.new(uri.path)
104
+ send_notify_request(data: data, uri: uri)
105
+ end
106
+
107
+ def send_notify_request(data:, uri:)
108
+ https = ::Net::HTTP.new(uri.host, uri.port)
109
+ https.use_ssl = true
110
+
111
+ request = ::Net::HTTP::Post.new(uri.path)
89
112
  request["Content-Type"] = "application/json"
90
- request.body = JSON.generate(data)
113
+ request.body = ::JSON.generate(data)
91
114
 
92
115
  response = https.request(request)
93
116
 
94
117
  raise FailedToReportError, error_message_from_response(response) unless response.code == "200"
95
118
 
96
- response_data = JSON.parse(response.body)
119
+ response_data = ::JSON.parse(response.body)
97
120
 
98
121
  # Data not always present so dont use fetch
99
- PeakFlowUtils::NotifierResponse.new(
122
+ ::PeakFlowUtils::NotifierResponse.new(
100
123
  bug_report_id: response_data["bug_report_id"],
101
124
  bug_report_instance_id: response_data["bug_report_instance_id"],
102
125
  project_id: response_data["project_id"],
@@ -104,15 +127,4 @@ class PeakFlowUtils::Notifier
104
127
  url: response_data["url"]
105
128
  )
106
129
  end
107
-
108
- def error_message_from_response(response)
109
- message = "Couldn't report error to Peakflow (code #{response.code})"
110
-
111
- if response["content-type"]&.starts_with?("application/json")
112
- response_data = JSON.parse(response.body)
113
- message << ": #{response_data.fetch("errors").join(". ")}" if response_data["errors"]
114
- end
115
-
116
- message
117
- end
118
130
  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
@@ -1,3 +1,3 @@
1
1
  module PeakFlowUtils
2
- VERSION = "0.1.15".freeze
2
+ VERSION = "0.1.16".freeze
3
3
  end
@@ -6,7 +6,11 @@ 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 :ApplicationService, "#{services_path}/application_service"
12
+ autoload :DeepMerger, "#{services_path}/deep_merger"
13
+ autoload :InheritedLocalVar, "#{path}/inherited_local_var"
10
14
  autoload :Notifier, "#{path}/notifier"
11
15
  autoload :NotifierErrorParser, "#{path}/notifier_error_parser"
12
16
  autoload :NotifierRack, "#{path}/notifier_rack"
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.15
4
+ version: 0.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - kaspernj
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-10 00:00:00.000000000 Z
11
+ date: 2022-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -66,6 +66,34 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
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
@@ -147,6 +175,7 @@ files:
147
175
  - lib/peak_flow_utils.rb
148
176
  - lib/peak_flow_utils/engine.rb
149
177
  - lib/peak_flow_utils/handler_helper.rb
178
+ - lib/peak_flow_utils/inherited_local_var.rb
150
179
  - lib/peak_flow_utils/migrations/20150902155200_create_translation_keys.rb
151
180
  - lib/peak_flow_utils/migrations/20150907070908_create_handlers.rb
152
181
  - lib/peak_flow_utils/migrations/20150907070909_create_groups.rb
@@ -166,6 +195,7 @@ files:
166
195
  - lib/peak_flow_utils/notifier_rails.rb
167
196
  - lib/peak_flow_utils/notifier_response.rb
168
197
  - lib/peak_flow_utils/notifier_sidekiq.rb
198
+ - lib/peak_flow_utils/thread_callbacks_patch.rb
169
199
  - lib/peak_flow_utils/version.rb
170
200
  - lib/tasks/peak_flow_utils_tasks.rake
171
201
  homepage: https://github.com/kaspernj/peak_flow_utils