bonobot 0.0.4 → 0.0.6

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: 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