bonobot 0.0.4 → 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: 60d190ca5c05e4d29f7622276e6f387e53b06b87de3bfed7ed31446ac28b2fd9
4
- data.tar.gz: 8a900dfeb52e73e177d02363e98ce429e0012a94f0d184048208968d9841dbbe
3
+ metadata.gz: c6014e196b254d2d08a4b6054cd1aba997d2fb9158ebe26cfbb897b1c29ef81c
4
+ data.tar.gz: 829215850c62ecb9409838362a463f6518120bc9f3209ea6ab059967c5c9b0b6
5
5
  SHA512:
6
- metadata.gz: 5c72443158105126ae949850fcb70a588517837344c1b6cc4da90400da3e227dc44acb2bed7caf614fb64b9455739091882ae07177783b2a41aea6afbd5a9bd8
7
- data.tar.gz: 31a93680b3b218ebda245eeeb1b97717bdc92e95727042523f85ae599b559635305bc030f996525bc70c76341e12304c2fd3b1a8ff42a4eee74a331c5d1a0540
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).
data/Rakefile CHANGED
@@ -7,6 +7,7 @@ rescue LoadError
7
7
  end
8
8
 
9
9
  require "rdoc/task"
10
+ require "rspec/core/rake_task"
10
11
 
11
12
  RDoc::Task.new(:rdoc) do |rdoc|
12
13
  rdoc.rdoc_dir = "rdoc"
@@ -20,10 +21,8 @@ require "bundler/gem_tasks"
20
21
 
21
22
  require "rake/testtask"
22
23
 
23
- Rake::TestTask.new(:test) do |t|
24
- t.libs << "test"
25
- t.pattern = "test/**/*_test.rb"
26
- t.verbose = false
24
+ Rake::TestTask.new(:test) do |_t|
25
+ RSpec::Core::RakeTask.new(:spec)
27
26
  end
28
27
 
29
28
  task default: :test
@@ -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,126 +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: rails_files, engines_files: engines_files, overloads: overloads }))
14
+ File.write("status.json", status_json)
13
15
  puts File.expand_path("status.json")
14
16
  puts "-----"
15
17
 
16
- unless up_to_date.empty?
17
- puts "🥳 Up to date fingerprint count: #{up_to_date.count}"
18
- puts "-> Up to date fingerprint: #{present(up_to_date)}"
19
- puts ""
20
- end
21
-
22
- unless out_of_date.empty?
23
- puts "😱 Out of date fingerprint count: #{out_of_date.count}"
24
- puts "-> Out of date fingerprint: #{present(out_of_date)}"
25
- puts ""
26
- end
27
-
28
- unless missing.empty?
29
- puts "🤬 Files missing fingerprint count: #{missing.count}"
30
- puts "-> Missing fingerprint: #{present(missing)}"
31
- puts ""
18
+ STATUS.each do |status, emoji|
19
+ generate_status(status, emoji)
32
20
  end
33
21
 
34
22
  puts "-----"
35
- out_of_date.empty? && 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")
42
-
43
- "\n#{entries}"
44
- end
45
-
46
- def self.out_of_date
47
- overloads.fetch(:out_of_date, [])
48
- end
49
-
50
- def self.up_to_date
51
- overloads.fetch(:up_to_date, [])
52
- end
53
-
54
- def self.missing
55
- overloads.fetch(:missing, [])
56
- end
57
-
58
- def self.overloads
59
- @overloads ||= rails_files.each_with_object({}) do |(path, fingerprint), hash|
60
- engines_files.keys.each do |engine_name|
61
- next unless path.include? engine_name
62
-
63
- source_path = engines_files[engine_name].fetch(path, nil)
64
- next unless source_path
65
-
66
- result = [engine_name, source_path.merge(short_path: path)]
67
-
68
- key = status_key(source_path[:fingerprint], fingerprint)
69
- if hash[key].nil?
70
- hash[key] = [result]
71
- else
72
- hash[key] << result
73
- end
74
- end
75
- end
76
30
  end
77
31
 
78
- def self.rails_files
79
- @rails_files ||= Dir.glob(Rails.root.join("app", "**", "*.{erb,rb}")).map { |path| path.sub("#{Rails.root}/", "") }.each_with_object({}) do |path, hash|
80
- hash[path] = read_annotation(path)
81
- end
82
- end
32
+ def self.generate_status(status, emoji)
33
+ return if OverloadsRegistry.find_by(status: status).empty?
83
34
 
84
- def self.engines_files
85
- @engine_files ||= ::Rails::Engine.subclasses.each_with_object({}) do |klass, hash|
86
- paths = Dir.glob("#{klass.instance.root}/app/**/*.{erb,rb}")
87
- next if paths.empty?
35
+ overload_status = OverloadsRegistry.find_by(status: status)
36
+ status_to_text = status.to_s.capitalize.gsub("_", " ")
88
37
 
89
- hash[engine_to_gem(klass)] = engine_paths(paths)
90
- end
38
+ puts "-> #{emoji} #{status_to_text} fingerprint (#{overload_status.count}):"
39
+ puts present(OverloadsRegistry.find_by(status: status))
40
+ puts ""
91
41
  end
92
42
 
93
- def self.engine_to_gem(engine_class)
94
- if engine_class.respond_to?(:railtie_namespace) && engine_class.railtie_namespace
95
- engine_class.railtie_namespace.to_s.split("::").map(&:underscore).join("/")
96
- else
97
- engine_class.engine_name.sub("_engine", "")
98
- end
99
- end
100
-
101
- def self.engine_paths(paths)
102
- paths.each_with_object({}) do |path, hash|
103
- _name, *short_path = path.sub("#{gems_dir}/gems/", "").split("/")
104
- hash[short_path.join("/")] = { path: path, fingerprint: fingerprint(path) }
105
- end
106
- end
107
-
108
- def self.fingerprint(path)
109
- Digest::MD5.hexdigest(File.read(path))
110
- end
111
-
112
- def self.gems_dir
113
- @gems_dir ||= Bundler.rubygems.gem_dir
114
- end
115
-
116
- def self.read_annotation(path)
117
- File.readlines(path).map do |line|
118
- line.sub(/# bonobot_fingerprint:/, "").sub("<%", "").sub("%>", "").strip if line.match?(/# bonobot_fingerprint:/) || line.match?(/<%# bonobot_fingerprint:/)
119
- end.compact.first
120
- end
121
-
122
- def self.status_key(source_fingerprint, target_fingerprint)
123
- return :up_to_date if source_fingerprint == target_fingerprint
124
- return :missing if target_fingerprint.nil?
125
-
126
- :out_of_date
43
+ def self.status_json
44
+ JSON.pretty_generate({
45
+ rails_files: LocalFilesRegistry.output,
46
+ engines_files: EnginesFilesRegistry.output,
47
+ overloads: OverloadsRegistry.output
48
+ })
127
49
  end
128
50
  end
129
51
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bonobot
4
- VERSION = "0.0.4"
4
+ VERSION = "0.0.6"
5
5
  end
data/lib/bonobot.rb CHANGED
@@ -3,4 +3,11 @@
3
3
  require "bonobot/railtie"
4
4
 
5
5
  module Bonobot
6
+ autoload :Status, "bonobot/status"
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"
6
13
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bonobot/status"
4
-
5
3
  namespace :bonobot do
6
4
  desc "Generate status"
7
5
  task status: :environment do
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.4
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-05 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,6 +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
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.12.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.12.0
41
83
  - !ruby/object:Gem::Dependency
42
84
  name: rubocop
43
85
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +122,34 @@ dependencies:
80
122
  - - "~>"
81
123
  - !ruby/object:Gem::Version
82
124
  version: 2.11.1
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
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
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 2.1.0
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 2.1.0
83
153
  description: Description of Bonobot.
84
154
  email:
85
155
  - fardeauarmand@gmail.com
@@ -91,6 +161,12 @@ files:
91
161
  - README.md
92
162
  - Rakefile
93
163
  - lib/bonobot.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
94
170
  - lib/bonobot/railtie.rb
95
171
  - lib/bonobot/status.rb
96
172
  - lib/bonobot/version.rb