bonobot 0.0.5 → 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: 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