bonobot 0.0.5 → 0.0.6

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: 06ccb8b5b500887ae4d039b56d507d9c4c3b949ebf82c0b9213f154689832354
4
- data.tar.gz: 725a0102e557cde864e780396d2ac713ac6446059ab049a9c8acd555c9ea4e00
3
+ metadata.gz: c6014e196b254d2d08a4b6054cd1aba997d2fb9158ebe26cfbb897b1c29ef81c
4
+ data.tar.gz: 829215850c62ecb9409838362a463f6518120bc9f3209ea6ab059967c5c9b0b6
5
5
  SHA512:
6
- metadata.gz: 108229c0e785c832dbc0cc727348b6586ca860d110bf06e8f300b3658b9774c1edc15de024e63f0877328846c682484697998db3633422ea8e95d9ee9f2f480d
7
- data.tar.gz: 749c8b17db41e4c317dbaf5adca0ea13d770f09345e8fc787c7121c34d50c76a6f2eb357c42d4483189170e30e92f8a1d686651cf4b73151f82dffd569b0f54e
6
+ metadata.gz: 451b94f8a0cffe4d1f05c7913055fa8446caaf3865d504dfd83f749e5e20413ea959db56e6ae4d1d9a066bc684cae5496bf7ca1d4e40ea4bd0f55db98226781b
7
+ data.tar.gz: 8401521e0421b536e30ef41683463d31544f1a9534461f90c7dc7a25b280a88053d0dddad1b4a09639dddeba2d9567aff9323a8d01035bee9c7650b043c56d66
data/README.md CHANGED
@@ -1,10 +1,17 @@
1
- # Bonobo
2
- Short description and motivation.
1
+ # Bonobot
2
+ ![ci_cd](https://github.com/armandfardeau/bonobot/actions/workflows/ci_cd.yml/badge.svg)
3
+ [![codecov](https://codecov.io/gh/armandfardeau/bonobot/branch/master/graph/badge.svg?token=274POQGBAK)](https://codecov.io/gh/armandfardeau/bonobot)
4
+
5
+ BonoBot is a Ruby gem that helps with Rails monkey patching.
3
6
 
4
7
  ## Usage
5
- How to use my plugin.
8
+
9
+ ```bash
10
+ bundle exec rake bonobot: status
11
+ ```
6
12
 
7
13
  ## Installation
14
+
8
15
  Add this line to your application's Gemfile:
9
16
 
10
17
  ```ruby
@@ -12,17 +19,22 @@ gem 'bonobot'
12
19
  ```
13
20
 
14
21
  And then execute:
22
+
15
23
  ```bash
16
- $ bundle
24
+ bundle exec rake bonobot:status
17
25
  ```
18
26
 
19
27
  Or install it yourself as:
28
+
20
29
  ```bash
21
- $ gem install bonobot
30
+ gem install bonobot
31
+ bundle exec rake bonobot:status
22
32
  ```
23
33
 
24
34
  ## Contributing
35
+
25
36
  Contribution directions go here.
26
37
 
27
38
  ## License
28
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
39
+
40
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bonobot
4
+ class EngineFile
5
+ attr_reader :path, :engine_name, :short_path, :root_path
6
+
7
+ def initialize(path, engine)
8
+ @path = path
9
+ @root_path = engine.instance.root
10
+ @engine_name = engine_to_name(engine)
11
+ @short_path = path.sub("#{@root_path}/", "")
12
+ end
13
+
14
+ def fingerprint
15
+ Digest::MD5.hexdigest(File.read(@path))
16
+ end
17
+
18
+ def to_hash
19
+ instance_values.merge({ "fingerprint" => fingerprint })
20
+ end
21
+
22
+ private
23
+
24
+ def engine_to_name(engine_class)
25
+ if engine_class.respond_to?(:railtie_namespace) && engine_class.railtie_namespace
26
+ engine_class.railtie_namespace.to_s.split("::").map(&:underscore).join("/")
27
+ else
28
+ engine_class.engine_name.sub("_engine", "")
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bonobot
4
+ class EnginesFilesRegistry
5
+ def self.all
6
+ @all ||= deduplicate(generate)
7
+ end
8
+
9
+ def self.generate
10
+ Parallel.flat_map(::Rails::Engine.subclasses) do |klass|
11
+ Dir.glob(root(klass.instance.root).join("**", "*.{erb,rb}")).map do |path|
12
+ EngineFile.new(path, klass)
13
+ end
14
+ end
15
+ end
16
+
17
+ def self.deduplicate(engine_files)
18
+ engine_files.group_by(&:path).map { |_, files| files.min_by(&:engine_name) }
19
+ end
20
+
21
+ def self.find_by(attributes)
22
+ all.select do |local_file|
23
+ attributes.all? do |key, value|
24
+ local_file.try(key) == value
25
+ end
26
+ end
27
+ end
28
+
29
+ def self.output
30
+ all.map(&:as_json)
31
+ end
32
+
33
+ def self.root(path)
34
+ Pathname.new(path).join("app")
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bonobot
4
+ class LocalFile
5
+ attr_reader :path
6
+
7
+ def initialize(path, root)
8
+ @path = path.sub("#{root}/", "")
9
+ end
10
+
11
+ def annotation
12
+ File.readlines(@path).map do |line|
13
+ line.sub(/# bonobot_fingerprint:/, "").sub("<%", "").sub("%>", "").strip if line.match?(/# bonobot_fingerprint:/) || line.match?(/<%# bonobot_fingerprint:/)
14
+ end.compact.first.presence
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parallel"
4
+
5
+ module Bonobot
6
+ class LocalFilesRegistry
7
+ def self.all
8
+ @all ||= Parallel.map(Dir.glob(root.join("**", "*.{erb,rb}"))) do |path|
9
+ LocalFile.new(path, rails_root)
10
+ end
11
+ end
12
+
13
+ def self.root
14
+ rails_root.join("app")
15
+ end
16
+
17
+ def self.rails_root
18
+ ::Rails.root
19
+ end
20
+
21
+ def self.output
22
+ all.map(&:as_json)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bonobot
4
+ class Overload
5
+ attr_reader :engine_file
6
+
7
+ def initialize(local_file, engine_file)
8
+ @local_file = local_file
9
+ @engine_file = engine_file
10
+ end
11
+
12
+ def path
13
+ @local_file.path
14
+ end
15
+
16
+ def status
17
+ return :missing if @local_file.annotation.nil?
18
+ return :up_to_date if @local_file.annotation == @engine_file.fingerprint
19
+
20
+ :out_of_date
21
+ end
22
+
23
+ def to_hash
24
+ instance_values.merge({ "status" => status, "path" => path })
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bonobot
4
+ module OverloadsRegistry
5
+ def self.all
6
+ @all ||= LocalFilesRegistry.all.flat_map do |local_file|
7
+ EnginesFilesRegistry.find_by(short_path: local_file.path).map do |engine_file|
8
+ Overload.new(local_file, engine_file)
9
+ end
10
+ end
11
+ end
12
+
13
+ # TODO: Extract to module
14
+ def self.find_by(attributes)
15
+ all.select do |item|
16
+ attributes.all? do |key, value|
17
+ item.try(key) == value
18
+ end
19
+ end
20
+ end
21
+
22
+ def self.output
23
+ all.map(&:as_json)
24
+ end
25
+ end
26
+ end
@@ -4,43 +4,48 @@ require "json"
4
4
 
5
5
  module Bonobot
6
6
  class Status
7
+ STATUS = { up_to_date: "🥳", out_of_date: "😱", missing: "🤬" }.freeze
8
+
7
9
  def self.generate
8
10
  puts "-----"
9
11
  puts "🙈 🙉 🙊 Bonobot 🙈 🙉 🙊"
10
12
  puts "-----"
11
13
  puts "🛠 Generating status"
12
- File.write("status.json", JSON.pretty_generate({ rails_files: LocalFiles.files, engines_files: EnginesFiles.files, overloads: Overloads.files }))
14
+ File.write("status.json", status_json)
13
15
  puts File.expand_path("status.json")
14
16
  puts "-----"
15
17
 
16
- unless Overloads.status(:up_to_date).empty?
17
- puts "🥳 Up to date fingerprint count: #{Overloads.status(:up_to_date).count}"
18
- puts "-> Up to date fingerprint: #{present(Overloads.status(:up_to_date))}"
19
- puts ""
20
- end
21
-
22
- unless Overloads.status(:out_of_date).empty?
23
- puts "😱 Out of date fingerprint count: #{Overloads.status(:out_of_date).count}"
24
- puts "-> Out of date fingerprint: #{present(Overloads.status(:out_of_date))}"
25
- puts ""
26
- end
27
-
28
- unless Overloads.status(:missing).empty?
29
- puts "🤬 Files missing fingerprint count: #{Overloads.status(:missing).count}"
30
- puts "-> Missing fingerprint: #{present(Overloads.status(:missing))}"
31
- puts ""
18
+ STATUS.each do |status, emoji|
19
+ generate_status(status, emoji)
32
20
  end
33
21
 
34
22
  puts "-----"
35
- Overloads.status(:out_of_date).empty? && Overloads.status(:missing).empty?
23
+ OverloadsRegistry.find_by(status: :out_of_date).empty? && OverloadsRegistry.find_by(status: :missing).empty?
36
24
  end
37
25
 
38
- def self.present(entry)
39
- entries = entry.map do |(engine_name, source_path)|
40
- " - #{engine_name}: #{source_path[:short_path]} (#{source_path[:fingerprint]})"
26
+ def self.present(entries)
27
+ entries.map do |entry|
28
+ " - #{entry.engine_file.engine_name}: #{entry.engine_file.short_path} (#{entry.engine_file.fingerprint})"
41
29
  end.join("\n")
30
+ end
31
+
32
+ def self.generate_status(status, emoji)
33
+ return if OverloadsRegistry.find_by(status: status).empty?
34
+
35
+ overload_status = OverloadsRegistry.find_by(status: status)
36
+ status_to_text = status.to_s.capitalize.gsub("_", " ")
37
+
38
+ puts "-> #{emoji} #{status_to_text} fingerprint (#{overload_status.count}):"
39
+ puts present(OverloadsRegistry.find_by(status: status))
40
+ puts ""
41
+ end
42
42
 
43
- "\n#{entries}"
43
+ def self.status_json
44
+ JSON.pretty_generate({
45
+ rails_files: LocalFilesRegistry.output,
46
+ engines_files: EnginesFilesRegistry.output,
47
+ overloads: OverloadsRegistry.output
48
+ })
44
49
  end
45
50
  end
46
51
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bonobot
4
- VERSION = "0.0.5"
4
+ VERSION = "0.0.6"
5
5
  end
data/lib/bonobot.rb CHANGED
@@ -4,7 +4,10 @@ require "bonobot/railtie"
4
4
 
5
5
  module Bonobot
6
6
  autoload :Status, "bonobot/status"
7
- autoload :LocalFiles, "bonobot/local_files"
8
- autoload :EnginesFiles, "bonobot/engines_files"
9
- autoload :Overloads, "bonobot/overloads"
7
+ autoload :LocalFilesRegistry, "bonobot/local_files_registry"
8
+ autoload :LocalFile, "bonobot/local_file"
9
+ autoload :EnginesFilesRegistry, "bonobot/engines_files_registry"
10
+ autoload :EngineFile, "bonobot/engine_file"
11
+ autoload :OverloadsRegistry, "bonobot/overloads_registry"
12
+ autoload :Overload, "bonobot/overload"
10
13
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bonobot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - armandfardeau
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-06 00:00:00.000000000 Z
11
+ date: 2023-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: parallel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.22.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.22.1
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rails
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,20 +52,34 @@ dependencies:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
54
  version: '11.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: factory_bot
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 6.2.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 6.2.1
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: rspec
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
- - - ">="
73
+ - - "~>"
46
74
  - !ruby/object:Gem::Version
47
- version: '0'
75
+ version: 3.12.0
48
76
  type: :development
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
- - - ">="
80
+ - - "~>"
53
81
  - !ruby/object:Gem::Version
54
- version: '0'
82
+ version: 3.12.0
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: rubocop
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -98,16 +126,30 @@ dependencies:
98
126
  name: simplecov
99
127
  requirement: !ruby/object:Gem::Requirement
100
128
  requirements:
101
- - - ">="
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.21.2
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.21.2
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov-cobertura
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
102
144
  - !ruby/object:Gem::Version
103
- version: '0'
145
+ version: 2.1.0
104
146
  type: :development
105
147
  prerelease: false
106
148
  version_requirements: !ruby/object:Gem::Requirement
107
149
  requirements:
108
- - - ">="
150
+ - - "~>"
109
151
  - !ruby/object:Gem::Version
110
- version: '0'
152
+ version: 2.1.0
111
153
  description: Description of Bonobot.
112
154
  email:
113
155
  - fardeauarmand@gmail.com
@@ -119,9 +161,12 @@ files:
119
161
  - README.md
120
162
  - Rakefile
121
163
  - lib/bonobot.rb
122
- - lib/bonobot/engines_files.rb
123
- - lib/bonobot/local_files.rb
124
- - lib/bonobot/overloads.rb
164
+ - lib/bonobot/engine_file.rb
165
+ - lib/bonobot/engines_files_registry.rb
166
+ - lib/bonobot/local_file.rb
167
+ - lib/bonobot/local_files_registry.rb
168
+ - lib/bonobot/overload.rb
169
+ - lib/bonobot/overloads_registry.rb
125
170
  - lib/bonobot/railtie.rb
126
171
  - lib/bonobot/status.rb
127
172
  - lib/bonobot/version.rb
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module EnginesFiles
4
- def self.files
5
- @files ||= ::Rails::Engine.subclasses.each_with_object({}) do |klass, hash|
6
- paths = Dir.glob("#{klass.instance.root}/app/**/*.{erb,rb}")
7
- next if paths.empty?
8
-
9
- hash[engine_to_name(klass)] = engine_paths(paths)
10
- end
11
- end
12
-
13
- def self.engine_to_name(engine_class)
14
- if engine_class.respond_to?(:railtie_namespace) && engine_class.railtie_namespace
15
- engine_class.railtie_namespace.to_s.split("::").map(&:underscore).join("/")
16
- else
17
- engine_class.engine_name.sub("_engine", "")
18
- end
19
- end
20
-
21
- def self.engine_paths(paths)
22
- paths.each_with_object({}) do |path, hash|
23
- _name, *short_path = path.sub(gems_dir, "").split("/")
24
- hash[short_path.join("/")] = { path: path, fingerprint: fingerprint(path) }
25
- end
26
- end
27
-
28
- def self.gems_dir
29
- @gems_dir ||= "#{Bundler.rubygems.gem_dir}/gems/"
30
- end
31
-
32
- def self.fingerprint(path)
33
- Digest::MD5.hexdigest(File.read(path))
34
- end
35
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module LocalFiles
4
- def self.files
5
- @files ||= Dir.glob(::Rails.root.join("app", "**", "*.{erb,rb}")).map { |path| path.sub("#{::Rails.root}/", "") }.each_with_object({}) do |path, hash|
6
- hash[path] = read_annotation(path)
7
- end
8
- end
9
-
10
- def self.read_annotation(path)
11
- File.readlines(path).map do |line|
12
- line.sub(/# bonobot_fingerprint:/, "").sub("<%", "").sub("%>", "").strip if line.match?(/# bonobot_fingerprint:/) || line.match?(/<%# bonobot_fingerprint:/)
13
- end.compact.first.presence
14
- end
15
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Overloads
4
- def self.files
5
- @files ||= LocalFiles.files.each_with_object({}) do |(path, fingerprint), hash|
6
- EnginesFiles.files.keys.each do |engine_name|
7
- next unless path.include? engine_name
8
-
9
- source_path = EnginesFiles.files[engine_name].fetch(path, nil)
10
- next unless source_path
11
-
12
- result = [engine_name, source_path.merge(short_path: path)]
13
-
14
- key = status_key(source_path[:fingerprint], fingerprint)
15
- if hash[key].nil?
16
- hash[key] = [result]
17
- else
18
- hash[key] << result
19
- end
20
- end
21
- end
22
- end
23
-
24
- def self.status_key(source_fingerprint, target_fingerprint)
25
- return :up_to_date if source_fingerprint == target_fingerprint
26
- return :missing if target_fingerprint.nil?
27
-
28
- :out_of_date
29
- end
30
-
31
- def self.status(status)
32
- files.fetch(status, [])
33
- end
34
- end