peak_flow_utils 0.1.15 → 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
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