gem_activity_tracker 1.0.0 → 1.0.2
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 +4 -4
- data/lib/gem_activity_tracker/railtie.rb +1 -1
- data/lib/gem_activity_tracker/tracker.rb +111 -85
- data/lib/gem_activity_tracker/version.rb +1 -1
- metadata +6 -4
- data/gem_activity_tracker.gemspec +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41af2cc25b92505945ab8ca305644b17b7df8e13b94e0b17ef88caaad2b14a4f
|
4
|
+
data.tar.gz: aac8db37f891708a2f7c21768657c87b8ed98e1b09be8839afa2c825578ae883
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13b648d787fc0ad59444df78e924c290b226f4bbf4325400e6665e90554cb26be7cb594047ddc3fbb38c42765562449b648834a6eafa97792c5926fec704eec5
|
7
|
+
data.tar.gz: 12eb408462c35e041cabba7ca4629f5a541d168f96529dfd17eb23c1321239519be0619046821245789b698cca486477632bfe13d3b16159bdc6adb358983fe1
|
@@ -6,7 +6,7 @@ module GemActivityTracker
|
|
6
6
|
Rails.application.config.after_initialize do
|
7
7
|
# Delay the execution slightly to avoid blocking boot
|
8
8
|
Thread.new do
|
9
|
-
sleep(
|
9
|
+
sleep(3) # Give Rails a moment to fully start
|
10
10
|
|
11
11
|
begin
|
12
12
|
path = Rails.root.to_s
|
@@ -3,6 +3,7 @@ require 'json'
|
|
3
3
|
require 'fileutils'
|
4
4
|
require 'csv'
|
5
5
|
require 'digest'
|
6
|
+
require 'erb'
|
6
7
|
require 'active_support/all'
|
7
8
|
require 'active_record'
|
8
9
|
require 'active_support/dependencies'
|
@@ -13,7 +14,6 @@ module GemActivityTracker
|
|
13
14
|
puts "🔍 Tracking project at: #{path}"
|
14
15
|
|
15
16
|
if !defined?(Rails) || Rails.root.to_s != path
|
16
|
-
# Only require environment if we're not already inside a Rails app
|
17
17
|
$LOAD_PATH.unshift(path)
|
18
18
|
ENV['RAILS_ENV'] ||= 'development'
|
19
19
|
require File.join(path, 'config', 'environment')
|
@@ -21,16 +21,30 @@ module GemActivityTracker
|
|
21
21
|
|
22
22
|
data = collect_data(path)
|
23
23
|
FileUtils.mkdir_p("#{path}/activity_tracker")
|
24
|
-
File.write("#{path}/activity_tracker/report.yml", data.to_yaml)
|
25
|
-
|
24
|
+
File.write("#{path}/activity_tracker/report.yml", data.to_yaml(line_width: -1))
|
26
25
|
puts "✅ Report generated at: #{path}/activity_tracker/report.yml"
|
27
26
|
end
|
28
|
-
|
27
|
+
|
28
|
+
def self.safe_yaml_load(file_path)
|
29
|
+
content = ERB.new(File.read(file_path)).result
|
30
|
+
if YAML.respond_to?(:safe_load)
|
31
|
+
begin
|
32
|
+
YAML.safe_load(content, permitted_classes: [Symbol], permitted_symbols: [], aliases: true)
|
33
|
+
rescue ArgumentError
|
34
|
+
YAML.load(content)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
YAML.load(content)
|
38
|
+
end
|
39
|
+
rescue => e
|
40
|
+
puts "❌ YAML Load Error: #{e.message}"
|
41
|
+
{}
|
42
|
+
end
|
43
|
+
|
29
44
|
def self.collect_data(path)
|
30
45
|
{
|
31
46
|
project_name: File.basename(path),
|
32
47
|
ruby_version: `ruby -v`.strip,
|
33
|
-
# rails_version: get_rails_version(path),
|
34
48
|
rails_version: Rails.version,
|
35
49
|
database: detect_database(path),
|
36
50
|
models: analyze_models(path),
|
@@ -40,39 +54,36 @@ module GemActivityTracker
|
|
40
54
|
services: list_and_count_files(path, "app/services"),
|
41
55
|
migrations: migration_changes(path),
|
42
56
|
schema_hash: schema_hash(path),
|
43
|
-
routes: get_routes(path),
|
57
|
+
# routes: get_routes(path),
|
44
58
|
git_log: get_git_log(path)
|
45
59
|
}
|
46
60
|
end
|
47
61
|
|
48
62
|
def self.report
|
49
63
|
file = "activity_tracker/report.yml"
|
50
|
-
puts File.exist?(file) ?
|
64
|
+
puts File.exist?(file) ? safe_yaml_load(file).to_yaml(line_width: -1) : "❌ No report found."
|
51
65
|
end
|
52
66
|
|
53
67
|
def self.list_and_count_files(base, dir)
|
54
68
|
path = File.join(base, dir)
|
69
|
+
return { count: 0, files: [] } unless Dir.exist?(path)
|
70
|
+
|
55
71
|
files = Dir.glob("#{path}/**/*.rb").map { |f| f.gsub(base + '/', '') }
|
56
72
|
{ count: files.count, files: files }
|
57
73
|
end
|
58
74
|
|
59
|
-
def self.get_rails_version(path)
|
60
|
-
gemfile = File.join(path, 'Gemfile.lock')
|
61
|
-
File.exist?(gemfile) ? File.read(gemfile)[/rails \((.*?)\)/, 1] : 'Not a Rails project'
|
62
|
-
end
|
63
|
-
|
64
75
|
def self.detect_database(path)
|
65
76
|
db_file = File.join(path, "config/database.yml")
|
66
77
|
return 'Unknown' unless File.exist?(db_file)
|
67
78
|
|
68
|
-
config =
|
79
|
+
config = safe_yaml_load(db_file)
|
69
80
|
config["development"]["adapter"] rescue "Unknown"
|
70
81
|
end
|
71
82
|
|
72
|
-
def self.get_routes(path)
|
73
|
-
|
74
|
-
|
75
|
-
end
|
83
|
+
# def self.get_routes(path)
|
84
|
+
# output = `cd #{path} && RAILS_ENV=development bundle exec rails routes 2>/dev/null`
|
85
|
+
# output.empty? ? "No routes found or Rails not installed" : output
|
86
|
+
# end
|
76
87
|
|
77
88
|
def self.migration_changes(path)
|
78
89
|
files = Dir.glob("#{path}/db/migrate/*.rb")
|
@@ -89,87 +100,102 @@ module GemActivityTracker
|
|
89
100
|
end
|
90
101
|
|
91
102
|
def self.get_git_log(path)
|
103
|
+
return [] unless File.exist?(File.join(path, ".git"))
|
92
104
|
Dir.chdir(path) do
|
93
|
-
log = `git log --pretty=format:'%h - %an (%ad): %s' --date=short -n 20`
|
105
|
+
log = `git log --pretty=format:'%h - %an (%ad): %s' --date=short -n 20 2>/dev/null`
|
94
106
|
log.split("\n")
|
95
107
|
end
|
108
|
+
rescue => e
|
109
|
+
["Error reading git log: #{e.message}"]
|
96
110
|
end
|
97
111
|
|
98
|
-
|
99
|
-
|
112
|
+
def self.analyze_models(path)
|
113
|
+
result = { count: 0, files: [], detailed: {} }
|
100
114
|
|
101
|
-
|
115
|
+
ActiveSupport::Dependencies.autoload_paths += Dir["#{path}/app/models/**/"]
|
102
116
|
|
103
|
-
|
104
|
-
|
105
|
-
model_name = relative_path.sub('app/models/', '').sub('.rb', '').camelize
|
117
|
+
Dir.glob("#{path}/app/models/**/*.rb").each do |file|
|
118
|
+
next unless File.exist?(file)
|
106
119
|
|
107
|
-
|
108
|
-
|
109
|
-
model_class = model_name.constantize
|
110
|
-
next unless model_class < ActiveRecord::Base
|
111
|
-
|
112
|
-
result[:count] += 1
|
113
|
-
result[:files] << relative_path
|
114
|
-
|
115
|
-
# Validations
|
116
|
-
validations = Hash.new { |hash, key| hash[key] = [] }
|
117
|
-
model_class.validators.each do |validator|
|
118
|
-
validator.attributes.each do |attr|
|
119
|
-
validations[attr] << validator.class.name.demodulize.underscore
|
120
|
-
end
|
121
|
-
end
|
120
|
+
relative_path = file.gsub("#{path}/", '')
|
121
|
+
model_name = relative_path.sub('app/models/', '').sub('.rb', '').camelize
|
122
122
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
123
|
+
begin
|
124
|
+
require_dependency file
|
125
|
+
|
126
|
+
begin
|
127
|
+
model_class = model_name.safe_constantize
|
128
|
+
rescue => e
|
129
|
+
result[:detailed][model_name] = { error: "safe_constantize failed: #{e.class} - #{e.message}" }
|
130
|
+
next
|
131
|
+
end
|
131
132
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
class_methods = (model_class.methods(false) - ActiveRecord::Base.methods)
|
146
|
-
instance_methods = (model_class.instance_methods(false) - ActiveRecord::Base.instance_methods).map(&:to_s)
|
147
|
-
|
148
|
-
# # Comments
|
149
|
-
# comment = File.foreach(file).first if File.read(file).lines.first.strip.start_with?('#')
|
150
|
-
|
151
|
-
result[:detailed][model_class.name] = {
|
152
|
-
table_name: model_class.table_name,
|
153
|
-
file: relative_path,
|
154
|
-
# comment: comment&.strip,
|
155
|
-
attributes: model_class.columns.map(&:name),
|
156
|
-
associations: associations,
|
157
|
-
validations: validations,
|
158
|
-
enums: enums,
|
159
|
-
callbacks: callbacks,
|
160
|
-
scopes: scopes.map(&:to_s),
|
161
|
-
class_methods: class_methods.map(&:to_s),
|
162
|
-
instance_methods: instance_methods,
|
163
|
-
methods_count: model_class.instance_methods(false).count
|
164
|
-
}
|
165
|
-
|
166
|
-
rescue => e
|
167
|
-
result[:detailed][model_name] = { error: "Failed to load: #{e.message}" }
|
133
|
+
unless model_class && model_class < ActiveRecord::Base
|
134
|
+
result[:detailed][model_name] = { error: "Not an ActiveRecord model or constant not found." }
|
135
|
+
next
|
136
|
+
end
|
137
|
+
|
138
|
+
result[:count] += 1
|
139
|
+
result[:files] << relative_path
|
140
|
+
|
141
|
+
# Validations
|
142
|
+
validations = Hash.new { |hash, key| hash[key] = [] }
|
143
|
+
model_class.validators.each do |validator|
|
144
|
+
validator.attributes.each do |attr|
|
145
|
+
validations[attr] << validator.class.name.demodulize.underscore
|
168
146
|
end
|
169
147
|
end
|
170
148
|
|
171
|
-
|
149
|
+
# Callbacks
|
150
|
+
callbacks = {}
|
151
|
+
ActiveRecord::Callbacks::CALLBACKS.each do |cb|
|
152
|
+
if model_class.respond_to?("_#{cb}_callbacks")
|
153
|
+
methods = model_class.send("_#{cb}_callbacks").map(&:filter).uniq
|
154
|
+
callbacks[cb] = methods.map(&:to_s) if methods.any?
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Enums
|
159
|
+
enums = model_class.defined_enums.transform_values { |v| v.keys }
|
160
|
+
|
161
|
+
# Associations with polymorphic check
|
162
|
+
raw_associations = model_class.reflect_on_all_associations
|
163
|
+
associations = raw_associations.group_by(&:macro).transform_values { |a| a.map(&:name) }
|
164
|
+
polymorphic_associations = raw_associations.select { |a| a.options[:polymorphic] }.map(&:name)
|
165
|
+
|
166
|
+
# Scopes
|
167
|
+
scopes = model_class.methods(false).select do |method|
|
168
|
+
model_class.method(method).source_location&.first&.include?('/models/')
|
169
|
+
end.select { |m| model_class.respond_to?(m) && model_class.send(m).is_a?(ActiveRecord::Relation) rescue false }
|
170
|
+
|
171
|
+
# Class & Instance methods
|
172
|
+
class_methods = (model_class.methods(false) - ActiveRecord::Base.methods)
|
173
|
+
instance_methods = (model_class.instance_methods(false) - ActiveRecord::Base.instance_methods).map(&:to_s)
|
174
|
+
|
175
|
+
result[:detailed][model_class.name] = {
|
176
|
+
table_name: model_class.table_name,
|
177
|
+
file: relative_path,
|
178
|
+
attributes: model_class.columns.map(&:name),
|
179
|
+
attribute_defaults: model_class.columns.each_with_object({}) { |col, hash| hash[col.name] = col.default },
|
180
|
+
sti: model_class.columns.any? { |col| col.name == model_class.inheritance_column },
|
181
|
+
polymorphic_associations: polymorphic_associations,
|
182
|
+
associations: associations,
|
183
|
+
validations: validations,
|
184
|
+
enums: enums,
|
185
|
+
callbacks: callbacks,
|
186
|
+
scopes: scopes.map(&:to_s),
|
187
|
+
class_methods: class_methods.map(&:to_s),
|
188
|
+
instance_methods: instance_methods,
|
189
|
+
methods_count: model_class.instance_methods(false).count
|
190
|
+
}
|
191
|
+
|
192
|
+
rescue => e
|
193
|
+
result[:detailed][model_name] = { error: "Failed to load model: #{e.class} - #{e.message}" }
|
172
194
|
end
|
195
|
+
end
|
196
|
+
|
197
|
+
result
|
198
|
+
end
|
173
199
|
|
174
200
|
|
175
201
|
|
@@ -177,7 +203,7 @@ module GemActivityTracker
|
|
177
203
|
file = "#{path}/activity_tracker/report.yml"
|
178
204
|
return puts "❌ Report not found." unless File.exist?(file)
|
179
205
|
|
180
|
-
data =
|
206
|
+
data = safe_yaml_load(file)
|
181
207
|
|
182
208
|
case format
|
183
209
|
when :json
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gem_activity_tracker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Atul Yadav
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.0'
|
27
27
|
description: 'A CLI gem to analyze Ruby/Rails projects and track activity: models,
|
28
|
-
migrations,
|
28
|
+
migrations, Git, schema,STI, assocation,methods, acope, validation and more.'
|
29
29
|
email:
|
30
30
|
- atulyadav9039@gmail.com
|
31
31
|
executables:
|
@@ -35,7 +35,6 @@ extra_rdoc_files: []
|
|
35
35
|
files:
|
36
36
|
- README.md
|
37
37
|
- exe/gem_activity_tracker
|
38
|
-
- gem_activity_tracker.gemspec
|
39
38
|
- lib/gem_activity_tracker.rb
|
40
39
|
- lib/gem_activity_tracker/railtie.rb
|
41
40
|
- lib/gem_activity_tracker/tracker.rb
|
@@ -53,7 +52,10 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
53
52
|
requirements:
|
54
53
|
- - ">="
|
55
54
|
- !ruby/object:Gem::Version
|
56
|
-
version:
|
55
|
+
version: '2.5'
|
56
|
+
- - "<"
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '4.0'
|
57
59
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
60
|
requirements:
|
59
61
|
- - ">="
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require_relative "lib/gem_activity_tracker/version"
|
3
|
-
|
4
|
-
Gem::Specification.new do |spec|
|
5
|
-
spec.name = "gem_activity_tracker"
|
6
|
-
spec.version = GemActivityTracker::VERSION
|
7
|
-
spec.authors = ["Atul Yadav"]
|
8
|
-
spec.email = ["atulyadav9039@gmail.com"]
|
9
|
-
|
10
|
-
spec.summary = "Track gem and project activity in Ruby/Rails projects"
|
11
|
-
spec.description = "A CLI gem to analyze Ruby/Rails projects and track activity: models, migrations, routes, Git, schema, and more."
|
12
|
-
spec.homepage = "https://github.com/atul13055/gem_activity_tracker"
|
13
|
-
spec.license = "MIT"
|
14
|
-
|
15
|
-
spec.files = Dir["lib/**/*.rb", "exe/*", "README.md", "LICENSE.txt", "*.gemspec"]
|
16
|
-
spec.bindir = "exe"
|
17
|
-
spec.executables = ["gem_activity_tracker"]
|
18
|
-
spec.require_paths = ["lib"]
|
19
|
-
spec.required_ruby_version = ">= 3.1.0"
|
20
|
-
|
21
|
-
spec.add_dependency "listen", "~> 3.0"
|
22
|
-
end
|