gem_activity_tracker 1.0.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dbf3e335476faecbfe09e7b427ec0706ad1cbfc397ceadd74ff8bd2dd68d56ae
4
- data.tar.gz: 83d525f61caafca1a605244dca22be4859045739a04ea984a7edd9267361af51
3
+ metadata.gz: 41af2cc25b92505945ab8ca305644b17b7df8e13b94e0b17ef88caaad2b14a4f
4
+ data.tar.gz: aac8db37f891708a2f7c21768657c87b8ed98e1b09be8839afa2c825578ae883
5
5
  SHA512:
6
- metadata.gz: 55066b038228ba20827e13f17fd962d0e4da8c9056ca4d5c67d4b3b414fe01ac4e6f067c4652169089cff9d13407819e997203e28dfb7ec2c5eb5e10ace8960a
7
- data.tar.gz: 354d740e41817c937009444eec07ffd896e0f41918896c1eb4725025b0592447459bd95d809e4673eff13c688328904b33dbb4af1bc7af2c7fe36bb7074e609e
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(2) # Give Rails a moment to fully start
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) ? YAML.load_file(file).to_yaml : "❌ No report found."
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 = YAML.load_file(db_file, aliases: true)
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
- output = `cd #{path} && RAILS_ENV=development bundle exec rails routes 2>/dev/null`
74
- output.empty? ? "No routes found or Rails not installed" : output
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
- def self.analyze_models(path)
99
- result = { count: 0, files: [], detailed: {} }
112
+ def self.analyze_models(path)
113
+ result = { count: 0, files: [], detailed: {} }
100
114
 
101
- ActiveSupport::Dependencies.autoload_paths += Dir["#{path}/app/models/**/"]
115
+ ActiveSupport::Dependencies.autoload_paths += Dir["#{path}/app/models/**/"]
102
116
 
103
- Dir.glob("#{path}/app/models/**/*.rb").each do |file|
104
- relative_path = file.gsub("#{path}/", '')
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
- begin
108
- require_dependency file
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
- # Callbacks
124
- callbacks = {}
125
- ActiveRecord::Callbacks::CALLBACKS.each do |cb|
126
- if model_class.respond_to?("_#{cb}_callbacks")
127
- methods = model_class.send("_#{cb}_callbacks").map(&:filter).uniq
128
- callbacks[cb] = methods.map(&:to_s) if methods.any?
129
- end
130
- end
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
- # Enums
133
- # debugger
134
- enums = model_class.defined_enums.transform_values { |v| v.keys }
135
-
136
- # Associations
137
- associations = model_class.reflect_on_all_associations.group_by(&:macro).transform_values { |a| a.map(&:name) }
138
-
139
- # Scopes
140
- scopes = model_class.methods(false).select do |method|
141
- model_class.method(method).source_location&.first&.include?('/models/')
142
- end.select { |m| model_class.respond_to?(m) && model_class.send(m).is_a?(ActiveRecord::Relation) rescue false }
143
-
144
- # Class methods
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
- result
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 = YAML.load_file(file)
206
+ data = safe_yaml_load(file)
181
207
 
182
208
  case format
183
209
  when :json
@@ -1,3 +1,3 @@
1
1
  module GemActivityTracker
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
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.1
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, routes, Git, schema, and more.'
28
+ migrations, Git, schema,STI, assocation,methods, acope, validation and more.'
29
29
  email:
30
30
  - atulyadav9039@gmail.com
31
31
  executables: