hospital 0.5.0 → 0.7.0

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: 5c2d250697eb93225f8674b4374369688af80a6d32efcbfc70406ae2130d0bba
4
- data.tar.gz: bb30e0f93629c922d9723c943061e9dddad28fcab7406ab24496cd5f893a5b6d
3
+ metadata.gz: c474d0a653f97f57a2e281a594c0e9b45d3e03bd17bd0b4ea80140e79b8d6572
4
+ data.tar.gz: 24c2232ea33222d4c470c2c736a6bf93745049d332fa07c44970c4f654bcc5eb
5
5
  SHA512:
6
- metadata.gz: 6ca63659e83bd3a662612b41b2a645776ca06132241fd5033c169f8fb4be1547445fb34e2160731adced86f7ba6a8995f92a6d1fa573751002ce6d2c85869c70
7
- data.tar.gz: 91f8b05eddcbfc0fbbc1cb44b7d9a0c340312207267bd4589237480f9fd95a5473af493d0c274d4231c6aaa914fb2820b7e3985039b1ad089eb523764d5ce89c
6
+ metadata.gz: 05540eb19dbdd3277ef4d76bca4f8692e69c6eba340e0a8f91a8c554dd81bf654fa478c7f9396925eed5e2deca3ac28d75b872cec652b38128b84291a65d3b02
7
+ data.tar.gz: 0eb85a41d00fe122217b07eef2d6a69f5d8f6dadbbfcd95b21991d0e055c2be35e2aeec4c9d4f4a86d133d50b960f7697746a26cb08e7fb584389905491d5fb3
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hospital
4
+ class Checkup
5
+ attr_reader :code, :condition, :diagnosis, :group, :skipped, :klass, :precondition
6
+
7
+ def initialize klass, code, title: nil, condition: -> { true }, precondition: false
8
+ @klass = klass
9
+ @code = code
10
+ @condition = condition
11
+ @diagnosis = Hospital::Diagnosis.new([klass.to_s, title].compact.join(' - '))
12
+ @precondition = precondition
13
+ @group = nil
14
+ end
15
+
16
+ def reset_diagnosis
17
+ diagnosis.reset
18
+ end
19
+
20
+ def check verbose: false
21
+ diagnosis.reset
22
+
23
+ if condition.nil? || condition.call
24
+ @skipped = false
25
+ code.call(diagnosis)
26
+ diagnosis
27
+ else
28
+ @skipped = true
29
+ nil
30
+ end
31
+ rescue StandardError => e
32
+ diagnosis.add_error "Unrescued exception in #{klass}.checkup:\n#{e.full_message}.\nThis is a bug inside the checkup method that should be fixed!"
33
+ end
34
+
35
+ def success?
36
+ diagnosis.success?
37
+ end
38
+
39
+ def set_group group
40
+ @group = group
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,55 @@
1
+ require_relative "string_formatter"
2
+
3
+ using StringFormatter
4
+
5
+ module Hospital
6
+ class CheckupGroup
7
+ attr_reader :name, :checkups, :precondition_checkups, :skipped
8
+
9
+ def initialize name
10
+ @name = name
11
+ @precondition_checkups = []
12
+ @checkups = []
13
+ @skipped = false
14
+ end
15
+
16
+ def all_checkups
17
+ @precondition_checkups + @checkups
18
+ end
19
+
20
+ def header
21
+ "#{name.to_s.capitalize.gsub(/_/, ' ')} checks"
22
+ end
23
+
24
+ def add_checkup checkup
25
+ checkup.set_group self
26
+
27
+ if checkup.precondition
28
+ @precondition_checkups << checkup
29
+ else
30
+ @checkups << checkup
31
+ end
32
+ end
33
+
34
+ def run_checkups verbose: false
35
+ run_precondition_checkups verbose: verbose
36
+
37
+ unless @skipped
38
+ run_dependent_checkups verbose: verbose
39
+ end
40
+ end
41
+
42
+ def run_precondition_checkups verbose: false
43
+ @precondition_checkups.each do |checkup|
44
+ checkup.check verbose: verbose
45
+ @skipped = true unless checkup.success?
46
+ end
47
+ end
48
+
49
+ def run_dependent_checkups verbose: false
50
+ @checkups.each do |checkup|
51
+ checkup.check verbose: verbose
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,5 +1,9 @@
1
- class Hospital::Diagnosis
2
- attr_reader :infos, :warnings, :errors, :name, :results
1
+ require_relative "string_formatter"
2
+
3
+ using StringFormatter
4
+
5
+ class Hospital::Diagnosis
6
+ attr_reader :infos, :warnings, :skips, :errors, :name, :results
3
7
 
4
8
  def initialize name
5
9
  @name = name.to_s
@@ -9,6 +13,7 @@ class Hospital::Diagnosis
9
13
  def reset
10
14
  @infos = []
11
15
  @warnings = []
16
+ @skips = []
12
17
  @errors = []
13
18
  @results = []
14
19
  end
@@ -44,8 +49,8 @@ class Hospital::Diagnosis
44
49
  "#{prefix} #{message.gsub(/\n\z/, '').gsub(/\n/, prefix ? "\n " : "\n")}"
45
50
  end
46
51
 
47
- def put
48
- puts output
52
+ def put out
53
+ out.put_diagnosis_result output
49
54
  end
50
55
  end
51
56
 
@@ -54,11 +59,15 @@ class Hospital::Diagnosis
54
59
  end
55
60
 
56
61
  class Warning < Result
57
- def prefix; '🟠'; end
62
+ def prefix; '🟠' end
63
+ end
64
+
65
+ class Skip < Result
66
+ def prefix; '🟠' end
58
67
  end
59
68
 
60
69
  class Error < Result
61
- def prefix; '🔴'; end
70
+ def prefix; '🔴' end
62
71
  end
63
72
 
64
73
  def add_info message
@@ -73,13 +82,31 @@ class Hospital::Diagnosis
73
82
  @results << warning
74
83
  end
75
84
 
85
+ def add_skip message
86
+ skip = Skip.new message
87
+ @skips << skip
88
+ @results << skip
89
+ end
90
+
76
91
  def add_error message
77
92
  error = Error.new message
78
93
  @errors << error
79
94
  @results << error
80
95
  end
81
96
 
82
- def put_results
83
- results.each &:put
97
+ def put_results out
98
+ results.each do |result|
99
+ result.put out
100
+ end
101
+ end
102
+
103
+ def success?
104
+ errors.count == 0 && skips.count == 0
105
+ end
106
+
107
+ def on_success_message message
108
+ if success?
109
+ add_info message
110
+ end
84
111
  end
85
112
  end
@@ -0,0 +1,11 @@
1
+ module Hospital
2
+ module Formatter
3
+ class Base
4
+ attr_reader :buffer
5
+
6
+ def initialize
7
+ @buffer = ""
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ require_relative "base"
2
+
3
+ using StringFormatter
4
+
5
+ module Hospital
6
+ module Formatter
7
+ class Pre < Base
8
+ def put_group_header text
9
+ @buffer << "\n\n### #{text}"
10
+ end
11
+
12
+ def put_diagnosis_header text
13
+ @buffer << "\n\n## #{text}"
14
+ end
15
+
16
+ def put_summary errors_count, warnings_count
17
+ @buffer << <<~END
18
+ \n\n
19
+ #### Summary:
20
+ Errors: #{errors_count}
21
+ Warnings: #{warnings_count}
22
+ END
23
+ end
24
+
25
+ def put_diagnosis_result text
26
+ @buffer << "\n#{text}"
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ require_relative "base"
2
+
3
+ using StringFormatter
4
+
5
+ module Hospital
6
+ module Formatter
7
+ class Shell < Base
8
+ def put_group_header text
9
+ @buffer << "\n### #{text}".h1
10
+ end
11
+
12
+ def put_diagnosis_header text
13
+ @buffer << "\n#{text.h2.indented}"
14
+ end
15
+
16
+ def put_summary errors_count, warnings_count
17
+ @buffer << <<~END
18
+
19
+ #{"Summary:".h1}
20
+ #{"Errors: #{errors_count}".red}
21
+ #{"Warnings: #{warnings_count}".yellow}
22
+ END
23
+ end
24
+
25
+ def put_diagnosis_result text
26
+ @buffer << "\n#{text.indented}"
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ module StringFormatter
2
+ refine String do
3
+ def bold
4
+ "\e[1m#{self}\e[0m"
5
+ end
6
+
7
+ def underline
8
+ "\e[4m#{self}\e[0m"
9
+ end
10
+
11
+ def h1
12
+ "\n#{self}".underline.bold
13
+ end
14
+
15
+ def h2
16
+ "\n#{self}".underline
17
+ end
18
+
19
+ def red
20
+ "\e[31m#{self}\e[0m"
21
+ end
22
+
23
+ def yellow
24
+ "\e[33m#{self}\e[0m"
25
+ end
26
+
27
+ def indented
28
+ "#{self}"
29
+ end
30
+ end
31
+ end
@@ -2,17 +2,12 @@
2
2
 
3
3
  require_relative '../../hospital'
4
4
 
5
+ # usage:
6
+ # rake doctor
7
+ # rake doctor[true]
8
+
5
9
  desc 'Check system setup sanity.'
6
10
  task :doctor, [:verbose] => :environment do |t, args|
7
- # at_exit { Rake::Task['doctor:summary'].invoke if $!.nil? }
8
-
9
- ActiveRecord::Base.connection_pool.disconnect!
10
- ActiveSupport.on_load(:active_record) do
11
- config = ActiveRecord::Base.configurations[Rails.env]
12
- config['pool'] = 100
13
- ActiveRecord::Base.establish_connection(config)
14
- end
15
-
16
11
  verbose = args[:verbose] == "true"
17
12
 
18
13
  # silence warnings about double constant definitions
@@ -20,7 +15,7 @@ task :doctor, [:verbose] => :environment do |t, args|
20
15
  p "eager load all classes" if verbose
21
16
  Rails.application.eager_load!
22
17
  end
23
-
18
+
24
19
  p "start checkup" if verbose
25
- Hospital.do_checkup_all verbose: verbose
20
+ puts Hospital::Runner.new(verbose: verbose).do_checkup_all
26
21
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hospital
4
- VERSION = "0.5.0"
4
+ VERSION = "0.7.0"
5
5
  end
data/lib/hospital.rb CHANGED
@@ -1,114 +1,104 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "byebug"
4
+ require 'require_all'
3
5
  require_relative "hospital/version"
6
+ require_relative "hospital/checkup"
7
+ require_relative "hospital/checkup_group"
4
8
  require_relative "hospital/diagnosis"
5
- require_relative "hospital/formatter"
9
+ require_relative "hospital/string_formatter"
10
+ require_relative "hospital/formatter/shell"
11
+ require_relative "hospital/formatter/pre"
6
12
 
7
- using Formatter
13
+ using StringFormatter
8
14
 
9
15
  module Hospital
10
16
  require_relative 'railtie' if defined?(Rails)
11
17
 
12
18
  class Error < StandardError; end
13
19
 
14
- class Checkup
15
- attr_reader :code, :condition, :diagnosis, :group, :skipped, :klass
20
+ @@groups = []
16
21
 
17
- def initialize klass, code, group: :general, title: nil, condition: -> { true }
18
- @klass = klass
19
- @code = code
20
- @group = group
21
- @condition = condition
22
- @diagnosis = Hospital::Diagnosis.new([klass.to_s, title].compact.join(' - '))
23
- end
22
+ class << self
24
23
 
25
- def reset_diagnosis
26
- diagnosis.reset
24
+ def included(klass)
25
+ raise Hospital::Error.new("Cannot include Hospital, please extend instead.")
27
26
  end
28
27
 
29
- def check verbose: false
30
- diagnosis.reset
28
+ # used to call the checkup for a specific class directly (in specs)
29
+ def do_checkup(klass, verbose: false)
30
+ @@groups.map(&:all_checkups).flatten.select{|cu| cu.klass == klass }.map do |cu|
31
+ cu.check verbose: verbose
32
+ end
33
+ end
31
34
 
32
- if condition.nil? || condition.call
33
- @skipped = false
34
- code.call(diagnosis)
35
- diagnosis
36
- else
37
- @skipped = true
38
- nil
35
+ def find_or_create_checkup_group name
36
+ unless checkup_group = @@groups.detect{|g| g.name == name }
37
+ checkup_group = CheckupGroup.new name
38
+ @@groups << checkup_group
39
39
  end
40
- rescue StandardError => e
41
- diagnosis.add_error "Unrescued exception in #{klass}.checkup:\n#{e.inspect}.\nThis is a bug inside the checkup method that should be fixed!"
40
+ checkup_group
41
+ end
42
+
43
+ def groups
44
+ @@groups
42
45
  end
43
46
  end
44
47
 
45
- @@checkups = {}
48
+ def checkup if: -> { true }, group: :general, title: nil, precondition: false, &code
49
+ checkup_group = Hospital.find_or_create_checkup_group group
50
+ checkup = Checkup.new(
51
+ self,
52
+ code,
53
+ title: title,
54
+ condition: binding.local_variable_get('if'),
55
+ precondition: precondition
56
+ )
57
+
58
+ # p "adding #{checkup.inspect} to #{group_name}"
59
+ checkup_group.add_checkup checkup
60
+ end
46
61
 
47
- class << self
62
+ class Runner
63
+ attr_reader :verbose
48
64
 
49
- def included(klass)
50
- raise Hospital::Error.new("Cannot include Hospital, please extend instead.")
51
- end
65
+ def initialize verbose: false, formatter: :shell
66
+ @verbose = verbose
67
+ @formatter = case formatter
68
+ when :pre then Formatter::Pre
69
+ else Formatter::Shell
70
+ end
52
71
 
53
- def extended(klass)
54
- # only relevant if the class does not yet define a real checkup method
55
- @@checkups[klass] = Checkup.new klass, -> (diagnosis) do
56
- diagnosis.add_warning("#{klass}: No checks defined! Please call checkup with a lambda.")
57
- end, group: :general
72
+ @out = @formatter.new
73
+ # @out.put_group_header "using formatter #{@formatter}"
58
74
  end
59
75
 
60
- def do_checkup_all verbose: false
76
+ def do_checkup_all
61
77
  errcount = 0
62
78
  warcount = 0
63
79
 
64
- threads = @@checkups.map do |klass, checkup|
65
- Thread.new do
66
- Thread.current.report_on_exception = false
67
- checkup.check(verbose: verbose)
68
- end
69
- end
70
-
71
- threads.each &:join
80
+ Hospital.groups.each do |group|
81
+ @out.put_group_header group.header
82
+ group.run_checkups verbose: verbose
72
83
 
73
- @@checkups.group_by{|klass, checkup| checkup.group}.map do |group, checkups|
74
- puts group_header(group)
75
- first = false
76
-
77
- checkups.each do |klass, checkup|
84
+ group.all_checkups.each do |checkup|
78
85
  if diagnosis = checkup.diagnosis
79
86
  errcount += diagnosis.errors.count
80
87
  warcount += diagnosis.warnings.count
81
88
 
82
- if !checkup.skipped
83
- puts "Checking #{diagnosis.name}:".h2
84
- diagnosis.put_results
89
+ if !checkup.skipped && (!checkup.group.skipped || checkup.precondition)
90
+ @out.put_diagnosis_header "Checking #{diagnosis.name}:"
91
+ diagnosis.put_results @out
85
92
  elsif verbose
86
- puts "Skipped #{diagnosis.name}.".h2
93
+ @out.put_diagnosis_header "Skipped #{diagnosis.name}."
87
94
  end
88
95
  end
89
96
  end
90
97
  end
91
98
 
92
- puts <<~END
99
+ @out.put_summary errcount, warcount
93
100
 
94
- #{"Summary:".h1}
95
- #{"Errors: #{errcount}".red}
96
- #{"Warnings: #{warcount}".yellow}
97
- END
98
- end
99
-
100
- # used to call the checkup for a specific class directly (in specs)
101
- def do_checkup(klass)
102
- @@checkups[klass].check
103
- end
104
-
105
- def group_header group
106
- "### #{group.to_s.capitalize.gsub(/_/, ' ')} checks".h1
101
+ @out.buffer
107
102
  end
108
103
  end
109
-
110
- def checkup if: -> { true }, group: :general, title: nil, &code
111
- @@checkups[self] = Checkup.new self, code, group: group, title: title, condition: binding.local_variable_get('if')
112
- end
113
-
114
104
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hospital
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-12-26 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2024-03-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: byebug
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: require_all
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
13
41
  description: Imagine a team of little doctors creating diagnoses and creating a useful
14
42
  report.
15
43
  email:
@@ -21,8 +49,13 @@ files:
21
49
  - LICENSE.txt
22
50
  - README.md
23
51
  - lib/hospital.rb
52
+ - lib/hospital/checkup.rb
53
+ - lib/hospital/checkup_group.rb
24
54
  - lib/hospital/diagnosis.rb
25
- - lib/hospital/formatter.rb
55
+ - lib/hospital/formatter/base.rb
56
+ - lib/hospital/formatter/pre.rb
57
+ - lib/hospital/formatter/shell.rb
58
+ - lib/hospital/string_formatter.rb
26
59
  - lib/hospital/tasks/checkup.rake
27
60
  - lib/hospital/version.rb
28
61
  - lib/railtie.rb
@@ -1,19 +0,0 @@
1
- module Formatter
2
- refine String do
3
- def h1
4
- "\n\e[4m\e[1m#{self}\e[0m"
5
- end
6
-
7
- def h2
8
- "\n\e[4m#{self}\e[0m"
9
- end
10
-
11
- def red
12
- "\e[31m#{self}\e[0m"
13
- end
14
-
15
- def yellow
16
- "\e[33m#{self}\e[0m"
17
- end
18
- end
19
- end