peak_flow_utils 0.1.12 → 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: 27b80fe472694a76875b46c4bc5e393e6bf02acb13e4ff2b194e5ce4b61698b7
4
- data.tar.gz: 4e4bd827165ad878970df19990af1d80ab229188f7d590464faadb772945b324
3
+ metadata.gz: c01a814a1aad134f668f7144f6addea59f8b8afdc75a2ae8ff2280dbe3a9867c
4
+ data.tar.gz: b6746741a1ff204c7b2a31319409d84a4529c14a55f8e9cfbf7b7fcad37eacea
5
5
  SHA512:
6
- metadata.gz: 11cc3ae6a4bed2cec6209846fbe5463e2133c4a5ff3873a63ef93bec83fbf1a58c90282aa279307dc090e3d1ac30d27bcb93193dc7489914b500939d8a389bb1
7
- data.tar.gz: 2b20aedf4ba6b0ae8ad2a6882e8f8664c9212310ce4cd2f555ac6c6436cb5943b6096d23e64502deaf6ea3c02f105e7c941614e1b549e767878a08fe9a4d0080
6
+ metadata.gz: 5a82e952b87453cfde4fbcb42fd43a0f2bca93f43dc2b3dbdca3109c6d25a21dd587745c602680d89c446cda1c1be251167b46248b98bc1e5354f408ae9f2758
7
+ data.tar.gz: d2020073122505feaa6cb3923f79a76ff6f8608ac45af16625cae83f79c6232a9f4ac677e834faa015bfd1d9e287946b4cc0662557507dcfab89ea7c3faf665e
@@ -3,6 +3,7 @@ class PeakFlowUtils::Pings::PostgresConnectionsController < PeakFlowUtils::Appli
3
3
  postgres_connections_count = ActiveRecord::Base.connection.execute("SELECT SUM(numbackends) AS connections_count FROM pg_stat_database").to_a.first
4
4
 
5
5
  render json: {
6
+ check_json_status: "OK",
6
7
  postgres_connections_count: postgres_connections_count.fetch("connections_count")
7
8
  }
8
9
  end
@@ -5,6 +5,7 @@ class PeakFlowUtils::Pings::SidekiqController < PeakFlowUtils::ApplicationContro
5
5
  sidekiq_queue = Sidekiq::Queue.new
6
6
 
7
7
  render json: {
8
+ check_json_status: "OK",
8
9
  latency: sidekiq_queue.latency,
9
10
  queue_size: sidekiq_queue.size
10
11
  }
@@ -1,6 +1,6 @@
1
1
  class PeakFlowUtils::DatabaseInitializerService < PeakFlowUtils::ApplicationService
2
- def execute
3
- path = File.realpath("#{File.dirname(__FILE__)}/../../../lib/peak_flow_utils/migrations")
2
+ def perform
3
+ path = File.realpath("#{__dir__}/../../../lib/peak_flow_utils/migrations")
4
4
  create_schema_table unless schema_table_exists?
5
5
 
6
6
  Dir["#{path}/[0-9]*_*.rb"].sort.map do |filename|
@@ -22,7 +22,7 @@ class PeakFlowUtils::DatabaseInitializerService < PeakFlowUtils::ApplicationServ
22
22
  private
23
23
 
24
24
  def create_schema_table
25
- PeakFlowUtils::ApplicationRecord.connection.execute("CREATE TABLE schema_migrations (version VARCHAT)")
25
+ PeakFlowUtils::ApplicationRecord.connection.execute("CREATE TABLE schema_migrations (version VARCHAR)")
26
26
  end
27
27
 
28
28
  def register_migration_migrated(version)
@@ -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
@@ -1,8 +1,8 @@
1
1
  class PeakFlowUtils::HandlersFinderService < PeakFlowUtils::ApplicationService
2
- def execute
2
+ def perform
3
3
  handlers = []
4
4
 
5
- Dir.foreach("#{File.dirname(__FILE__)}/../../handlers/peak_flow_utils") do |file|
5
+ Dir.foreach("#{__dir__}/../../handlers/peak_flow_utils") do |file|
6
6
  match = file.match(/\A(.+)_handler\.rb\Z/)
7
7
  next unless match
8
8
 
@@ -1,7 +1,7 @@
1
1
  class PeakFlowUtils::TranslationsParserService < PeakFlowUtils::ApplicationService
2
2
  attr_reader :db
3
3
 
4
- def execute
4
+ def perform
5
5
  PeakFlowUtils::DatabaseInitializerService.execute!
6
6
 
7
7
  cache_translations_in_dir(Rails.root.join("config/locales"))
@@ -103,11 +103,11 @@ private
103
103
  puts message.to_s if @debug # rubocop:disable Rails/Output
104
104
  end
105
105
 
106
- def execute_migrations
106
+ def perform_migrations
107
107
  require "baza_migrations"
108
108
 
109
109
  executor = BazaMigrations::MigrationsExecutor.new(db: @db)
110
- executor.add_dir "#{File.dirname(__FILE__)}/../../db/baza_translations_migrations"
110
+ executor.add_dir "#{__dir__}/../../db/baza_translations_migrations"
111
111
  executor.execute_migrations
112
112
  end
113
113
 
@@ -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,37 +2,88 @@ 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.notify(*args)
18
- PeakFlowUtils::Notifier.current.notify(*args)
15
+ def self.notify(*args, **opts, &blk)
16
+ PeakFlowUtils::Notifier.current&.notify(*args, **opts, &blk)
17
+ end
18
+
19
+ def self.reset_parameters
20
+ ::PeakFlowUtils::Notifier.current&.instance_variable_set(:@parameters, ::PeakFlowUtils::InheritedLocalVar.new({}))
21
+ end
22
+
23
+ def self.with_parameters(parameters)
24
+ return yield unless ::PeakFlowUtils::Notifier.current
25
+
26
+ random_id = ::SecureRandom.hex(16)
27
+
28
+ ::PeakFlowUtils::Notifier.current.mutex.synchronize do
29
+ raise "'parameters' was nil?" if ::PeakFlowUtils::Notifier.current.parameters.value.nil?
30
+
31
+ parameters_with = ::PeakFlowUtils::Notifier.current.parameters.value.clone
32
+ parameters_with[random_id] = parameters
33
+
34
+ ::PeakFlowUtils::Notifier.current.parameters.value = parameters_with
35
+ end
36
+
37
+ begin
38
+ yield
39
+ ensure
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
46
+ end
19
47
  end
20
48
 
21
49
  def initialize(auth_token:)
22
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
23
75
  end
24
76
 
25
77
  def notify(error:, environment: nil, parameters: nil)
26
- error_parser = PeakFlowUtils::NotifierErrorParser.new(
78
+ error_parser = ::PeakFlowUtils::NotifierErrorParser.new(
27
79
  backtrace: error.backtrace,
28
80
  environment: environment,
29
81
  error: error
30
82
  )
31
83
 
32
- uri = URI("https://www.peakflow.io/errors/reports")
84
+ merged_parameters = current_parameters(parameters: parameters)
33
85
 
34
- https = Net::HTTP.new(uri.host, uri.port)
35
- https.use_ssl = true
86
+ uri = URI("https://www.peakflow.io/errors/reports")
36
87
 
37
88
  data = {
38
89
  auth_token: auth_token,
@@ -43,25 +94,32 @@ class PeakFlowUtils::Notifier
43
94
  file_path: error_parser.file_path,
44
95
  line_number: error_parser.line_number,
45
96
  message: error.message,
46
- parameters: parameters,
97
+ parameters: merged_parameters,
47
98
  remote_ip: error_parser.remote_ip,
48
99
  url: error_parser.url,
49
100
  user_agent: error_parser.user_agent
50
101
  }
51
102
  }
52
103
 
53
- 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)
54
112
  request["Content-Type"] = "application/json"
55
- request.body = JSON.generate(data)
113
+ request.body = ::JSON.generate(data)
56
114
 
57
115
  response = https.request(request)
58
116
 
59
117
  raise FailedToReportError, error_message_from_response(response) unless response.code == "200"
60
118
 
61
- response_data = JSON.parse(response.body)
119
+ response_data = ::JSON.parse(response.body)
62
120
 
63
121
  # Data not always present so dont use fetch
64
- PeakFlowUtils::NotifierResponse.new(
122
+ ::PeakFlowUtils::NotifierResponse.new(
65
123
  bug_report_id: response_data["bug_report_id"],
66
124
  bug_report_instance_id: response_data["bug_report_instance_id"],
67
125
  project_id: response_data["project_id"],
@@ -69,15 +127,4 @@ class PeakFlowUtils::Notifier
69
127
  url: response_data["url"]
70
128
  )
71
129
  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
130
  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.notify(
15
- environment: env,
16
- error: e,
17
- parameters: parameters
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
@@ -1,3 +1,3 @@
1
1
  module PeakFlowUtils
2
- VERSION = "0.1.12".freeze
2
+ VERSION = "0.1.16".freeze
3
3
  end
@@ -4,16 +4,19 @@ require "array_enumerator"
4
4
  require "service_pattern"
5
5
 
6
6
  module PeakFlowUtils
7
- path = "#{File.dirname(__FILE__)}/peak_flow_utils"
8
- models_path = "#{File.dirname(__FILE__)}/peak_flow_utils/models"
7
+ path = "#{__dir__}/peak_flow_utils"
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"
13
17
  autoload :NotifierRails, "#{path}/notifier_rails"
14
18
  autoload :NotifierResponse, "#{path}/notifier_response"
15
19
  autoload :NotifierSidekiq, "#{path}/notifier_sidekiq"
16
- autoload :RspecHelper, "#{path}/rspec_helper"
17
20
  autoload :HandlerHelper, "#{path}/handler_helper"
18
21
 
19
22
  autoload :ApplicationRecord, "#{models_path}/application_record"
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.12
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: 2020-12-06 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
@@ -58,9 +58,37 @@ dependencies:
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: 1.0.5
62
62
  type: :runtime
63
63
  prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
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
64
92
  version_requirements: !ruby/object:Gem::Requirement
65
93
  requirements:
66
94
  - - ">="
@@ -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:
@@ -135,6 +162,7 @@ files:
135
162
  - app/services/peak_flow_utils/attribute_service.rb
136
163
  - app/services/peak_flow_utils/configuration_service.rb
137
164
  - app/services/peak_flow_utils/database_initializer_service.rb
165
+ - app/services/peak_flow_utils/deep_merger.rb
138
166
  - app/services/peak_flow_utils/erb_inspector.rb
139
167
  - app/services/peak_flow_utils/erb_inspector/file_inspector.rb
140
168
  - app/services/peak_flow_utils/erb_inspector/translation_inspector.rb
@@ -143,14 +171,14 @@ files:
143
171
  - app/services/peak_flow_utils/model_inspector.rb
144
172
  - app/services/peak_flow_utils/translation_service.rb
145
173
  - app/services/peak_flow_utils/translations_parser_service.rb
146
- - bin/peak_flow_rspec_files
147
174
  - config/routes.rb
148
175
  - lib/peak_flow_utils.rb
149
176
  - lib/peak_flow_utils/engine.rb
150
177
  - lib/peak_flow_utils/handler_helper.rb
178
+ - lib/peak_flow_utils/inherited_local_var.rb
151
179
  - lib/peak_flow_utils/migrations/20150902155200_create_translation_keys.rb
180
+ - lib/peak_flow_utils/migrations/20150907070908_create_handlers.rb
152
181
  - lib/peak_flow_utils/migrations/20150907070909_create_groups.rb
153
- - lib/peak_flow_utils/migrations/20150907090900_create_handlers.rb
154
182
  - lib/peak_flow_utils/migrations/20150908085500_create_translation_values.rb
155
183
  - lib/peak_flow_utils/migrations/20150908090800_create_handler_texts.rb
156
184
  - lib/peak_flow_utils/migrations/20160411190500_create_scanned_files.rb
@@ -167,7 +195,7 @@ files:
167
195
  - lib/peak_flow_utils/notifier_rails.rb
168
196
  - lib/peak_flow_utils/notifier_response.rb
169
197
  - lib/peak_flow_utils/notifier_sidekiq.rb
170
- - lib/peak_flow_utils/rspec_helper.rb
198
+ - lib/peak_flow_utils/thread_callbacks_patch.rb
171
199
  - lib/peak_flow_utils/version.rb
172
200
  - lib/tasks/peak_flow_utils_tasks.rake
173
201
  homepage: https://github.com/kaspernj/peak_flow_utils
@@ -189,7 +217,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
189
217
  - !ruby/object:Gem::Version
190
218
  version: '0'
191
219
  requirements: []
192
- rubygems_version: 3.0.6
220
+ rubygems_version: 3.2.32
193
221
  signing_key:
194
222
  specification_version: 4
195
223
  summary: Utilities to be used with PeakFlow.
@@ -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 "#{File.dirname(__FILE__)}/../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,195 +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_command
139
- command = "bundle exec rspec --dry-run --format json"
140
-
141
- tags&.each do |tag|
142
- command << " --tag #{tag}"
143
- end
144
-
145
- command
146
- end
147
-
148
- def dry_result
149
- require "json"
150
- @dry_result ||= ::JSON.parse(`#{dry_result_command}`)
151
- end
152
-
153
- def dry_file(path)
154
- files.fetch(path)
155
- end
156
-
157
- def files
158
- @files ||= begin
159
- result = {}
160
- dry_result.fetch("examples").each do |example|
161
- file_path = example.fetch("file_path")
162
- file_path = file_path[2, file_path.length]
163
- type = type_from_path(file_path)
164
- points = points_from_type(type)
165
-
166
- next if ignore_type?(type)
167
-
168
- result[file_path] = {examples: 0, path: file_path, points: 0, type: type} unless result.key?(file_path)
169
- result[file_path][:examples] += 1
170
- result[file_path][:points] += points
171
- end
172
-
173
- result
174
- end
175
- end
176
-
177
- def ignore_type?(type)
178
- only_types && !only_types.include?(type) # rubocop:disable Rails/NegateInclude:, Style/SafeNavigation
179
- end
180
-
181
- def type_from_path(file_path)
182
- match = file_path.match(/^spec\/(.+?)\//)
183
- match[1] if match
184
- end
185
-
186
- def points_from_type(type)
187
- if type == "feature" || type == "system"
188
- 10
189
- elsif type == "controllers"
190
- 3
191
- else
192
- 1
193
- end
194
- end
195
- end