rails_lens 0.3.0 → 0.5.1
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/CHANGELOG.md +18 -0
- data/lib/rails_lens/commands.rb +3 -3
- data/lib/rails_lens/configuration.rb +6 -1
- data/lib/rails_lens/extension_loader.rb +5 -4
- data/lib/rails_lens/model_source.rb +72 -0
- data/lib/rails_lens/model_source_loader.rb +117 -0
- data/lib/rails_lens/model_sources/active_record_source.rb +89 -0
- data/lib/rails_lens/schema/annotation_manager.rb +93 -2
- data/lib/rails_lens/tasks/annotate.rake +55 -2
- data/lib/rails_lens/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1bd96547bfc5ae90c3971f787bbb03cad57e8912ed25c4ea0005ab3652401408
|
|
4
|
+
data.tar.gz: 95f1bcfa75b796a22c1fd5fb861c79e41e048608648388d1d9b255b4a3466157
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 960195b484708f9904f1d96d5f52e5fca70580431deffdc9a5cb56c99ab57af3509c33ad88eb7cdfa84c75067ea04f1d542568152cc60ab6dbcb162b036d1b61
|
|
7
|
+
data.tar.gz: da71bed238e3fa5742039fa0f931d4fc625f52a6d1df3cc831678bd070b65b38e6f3ea4b01b998c7e163409764db0aebfaab83af9a10733081d99513f6a80893
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.1](https://github.com/seuros/rails_lens/compare/rails_lens/v0.5.0...rails_lens/v0.5.1) (2025-12-07)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* add by_source ([f6693f3](https://github.com/seuros/rails_lens/commit/f6693f384761de505eac0e525c7b70f4c053d14c))
|
|
9
|
+
|
|
10
|
+
## [0.5.0](https://github.com/seuros/rails_lens/compare/rails_lens/v0.3.0...rails_lens/v0.5.0) (2025-12-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### ⚠ BREAKING CHANGES
|
|
14
|
+
|
|
15
|
+
* refactor the extension modules
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* refactor the extension modules ([de80a63](https://github.com/seuros/rails_lens/commit/de80a638f968cc24e1b8c7906054dbb2292df772))
|
|
20
|
+
|
|
3
21
|
## [0.3.0](https://github.com/seuros/rails_lens/compare/rails_lens/v0.2.13...rails_lens/v0.3.0) (2025-11-29)
|
|
4
22
|
|
|
5
23
|
|
data/lib/rails_lens/commands.rb
CHANGED
|
@@ -211,14 +211,14 @@ module RailsLens
|
|
|
211
211
|
File.write(rake_file, rake_task_template)
|
|
212
212
|
|
|
213
213
|
output.say "Created rake task at #{rake_file}", :green
|
|
214
|
-
output.say ''
|
|
214
|
+
output.say ''
|
|
215
215
|
output.say 'The following task has been installed:', :blue
|
|
216
216
|
output.say ' • rails_lens:annotate - Annotate models after migrations', :green
|
|
217
|
-
output.say ''
|
|
217
|
+
output.say ''
|
|
218
218
|
output.say 'Configuration options in lib/tasks/rails_lens.rake:', :blue
|
|
219
219
|
output.say ' • AUTO_ANNOTATE (default: true in development)', :cyan
|
|
220
220
|
output.say ' • RAILS_LENS_ENV (default: development)', :cyan
|
|
221
|
-
output.say ''
|
|
221
|
+
output.say ''
|
|
222
222
|
output.say 'Disable auto-annotation:', :blue
|
|
223
223
|
output.say ' export AUTO_ANNOTATE=false', :cyan
|
|
224
224
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module RailsLens
|
|
4
4
|
class Config
|
|
5
5
|
attr_accessor :verbose, :debug, :raise_on_error, :logger,
|
|
6
|
-
:annotations, :erd, :schema, :extensions, :routes, :mailers
|
|
6
|
+
:annotations, :erd, :schema, :extensions, :routes, :mailers, :model_sources
|
|
7
7
|
|
|
8
8
|
def initialize
|
|
9
9
|
@verbose = false
|
|
@@ -76,6 +76,11 @@ module RailsLens
|
|
|
76
76
|
pattern: '**/*_mailer.rb',
|
|
77
77
|
exclusion_pattern: 'vendor/**/*_mailer.rb'
|
|
78
78
|
}
|
|
79
|
+
|
|
80
|
+
@model_sources = {
|
|
81
|
+
enabled: true, # Enable gem-provided model sources
|
|
82
|
+
error_reporting: :warn # :silent, :warn, :verbose
|
|
83
|
+
}
|
|
79
84
|
end
|
|
80
85
|
end
|
|
81
86
|
|
|
@@ -138,13 +138,14 @@ module RailsLens
|
|
|
138
138
|
# Check each loaded gem for RailsLens extensions
|
|
139
139
|
|
|
140
140
|
Gem.loaded_specs.each_key do |gem_name|
|
|
141
|
-
# Try to find extension in the gem
|
|
142
|
-
gem_constant_name = gem_name.gsub('-', '_').split('_').map(&:capitalize).join
|
|
143
|
-
extension_constant_name = "#{gem_constant_name}::RailsLensExtension"
|
|
144
|
-
|
|
145
141
|
# Skip gems that are likely to cause autoload issues
|
|
146
142
|
next if %w[digest openssl uri net json].include?(gem_name)
|
|
147
143
|
|
|
144
|
+
# Try to find extension in the gem
|
|
145
|
+
# Use ActiveSupport's camelize for proper Rails-style conversion (e.g., 'activecypher' -> 'ActiveCypher')
|
|
146
|
+
gem_constant_name = gem_name.gsub('-', '_').camelize
|
|
147
|
+
extension_constant_name = "#{gem_constant_name}::RailsLensExtension"
|
|
148
|
+
|
|
148
149
|
# First check if the gem constant exists without triggering autoload
|
|
149
150
|
next unless Object.const_defined?(gem_constant_name, false)
|
|
150
151
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsLens
|
|
4
|
+
# Base class for model sources
|
|
5
|
+
# Model sources provide pluggable discovery of different model types
|
|
6
|
+
# (e.g., ActiveRecord, ActiveCypher, etc.)
|
|
7
|
+
#
|
|
8
|
+
# Gems can register their own model source by defining:
|
|
9
|
+
# GemName::RailsLensModelSource < RailsLens::ModelSource
|
|
10
|
+
#
|
|
11
|
+
# Example:
|
|
12
|
+
# module MyOrm
|
|
13
|
+
# class ModelSource < ::RailsLens::ModelSource
|
|
14
|
+
# def self.models(options = {})
|
|
15
|
+
# # Return array of model classes
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# def self.file_patterns
|
|
19
|
+
# ['app/my_models/**/*.rb']
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# def self.annotate_model(model, options = {})
|
|
23
|
+
# # Return { status: :annotated/:skipped/:failed, model: name, ... }
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# def self.remove_annotation(model)
|
|
27
|
+
# # Return { status: :removed/:skipped, model: name, ... }
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# RailsLensModelSource = ModelSource
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
class ModelSource
|
|
35
|
+
class << self
|
|
36
|
+
# Return array of model classes to annotate
|
|
37
|
+
# @param options [Hash] Options passed from CLI
|
|
38
|
+
# @return [Array<Class>] Array of model classes
|
|
39
|
+
def models(_options = {})
|
|
40
|
+
raise NotImplementedError, "#{name} must implement .models"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Return file patterns for annotation removal
|
|
44
|
+
# Used when removing annotations by filesystem scan
|
|
45
|
+
# @return [Array<String>] Glob patterns relative to Rails.root
|
|
46
|
+
def file_patterns
|
|
47
|
+
raise NotImplementedError, "#{name} must implement .file_patterns"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Annotate a single model
|
|
51
|
+
# @param model [Class] The model class to annotate
|
|
52
|
+
# @param options [Hash] Options passed from CLI
|
|
53
|
+
# @return [Hash] Result with :status (:annotated, :skipped, :failed), :model, :file, :message
|
|
54
|
+
def annotate_model(_model, _options = {})
|
|
55
|
+
raise NotImplementedError, "#{name} must implement .annotate_model"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Remove annotation from a single model
|
|
59
|
+
# @param model [Class] The model class
|
|
60
|
+
# @return [Hash] Result with :status (:removed, :skipped), :model, :file
|
|
61
|
+
def remove_annotation(_model)
|
|
62
|
+
raise NotImplementedError, "#{name} must implement .remove_annotation"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Human-readable name for this source (used in logging)
|
|
66
|
+
# @return [String]
|
|
67
|
+
def source_name
|
|
68
|
+
name.demodulize.sub(/Source$/, '').underscore.humanize
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsLens
|
|
4
|
+
# Discovers and loads model sources from gems
|
|
5
|
+
# Gems can register sources in two ways:
|
|
6
|
+
# 1. Define GemName::RailsLensModelSource (auto-discovery)
|
|
7
|
+
# 2. Call RailsLens::ModelSourceLoader.register(SourceClass) explicitly
|
|
8
|
+
class ModelSourceLoader
|
|
9
|
+
@registered_sources = []
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
# Register a model source explicitly
|
|
13
|
+
# Use this when gem naming doesn't follow conventions
|
|
14
|
+
# @param source [Class] Model source class
|
|
15
|
+
def register(source)
|
|
16
|
+
return unless valid_source?(source)
|
|
17
|
+
|
|
18
|
+
@registered_sources ||= []
|
|
19
|
+
@registered_sources << source unless @registered_sources.include?(source)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Load all available model sources
|
|
23
|
+
# @return [Array<Class>] Array of model source classes
|
|
24
|
+
def load_sources
|
|
25
|
+
sources = []
|
|
26
|
+
|
|
27
|
+
# Always include ActiveRecord source first
|
|
28
|
+
sources << ModelSources::ActiveRecordSource
|
|
29
|
+
|
|
30
|
+
# Include explicitly registered sources
|
|
31
|
+
@registered_sources ||= []
|
|
32
|
+
sources.concat(@registered_sources)
|
|
33
|
+
|
|
34
|
+
# Load gem-provided sources via auto-discovery if enabled
|
|
35
|
+
sources.concat(load_gem_sources) if config_enabled?
|
|
36
|
+
|
|
37
|
+
# Deduplicate in case a source was both registered and auto-discovered
|
|
38
|
+
sources.uniq
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# List all loaded sources (for debugging/info)
|
|
42
|
+
# @return [Array<Hash>] Source info with name and class
|
|
43
|
+
def list_sources
|
|
44
|
+
load_sources.map do |source|
|
|
45
|
+
{
|
|
46
|
+
name: source.source_name,
|
|
47
|
+
class: source.name,
|
|
48
|
+
patterns: source.file_patterns
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def config_enabled?
|
|
56
|
+
config = RailsLens.config.model_sources
|
|
57
|
+
config && config[:enabled]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def load_gem_sources
|
|
61
|
+
sources = []
|
|
62
|
+
|
|
63
|
+
Gem.loaded_specs.each_key do |gem_name|
|
|
64
|
+
source = find_source_for_gem(gem_name)
|
|
65
|
+
sources << source if source && valid_source?(source)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
sources
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def find_source_for_gem(gem_name)
|
|
72
|
+
# Skip gems that might cause autoload issues
|
|
73
|
+
return nil if %w[digest openssl uri net json].include?(gem_name)
|
|
74
|
+
|
|
75
|
+
# Convert gem name to constant (e.g., 'activecypher' -> 'ActiveCypher')
|
|
76
|
+
# Use ActiveSupport's camelize for proper Rails-style conversion
|
|
77
|
+
gem_constant_name = gem_name.gsub('-', '_').camelize
|
|
78
|
+
|
|
79
|
+
# Check if gem constant exists without triggering autoload
|
|
80
|
+
return nil unless Object.const_defined?(gem_constant_name, false)
|
|
81
|
+
|
|
82
|
+
gem_constant = Object.const_get(gem_constant_name)
|
|
83
|
+
return nil unless gem_constant.is_a?(Module)
|
|
84
|
+
|
|
85
|
+
# Check if it has a RailsLensModelSource
|
|
86
|
+
return nil unless gem_constant.const_defined?('RailsLensModelSource', false)
|
|
87
|
+
|
|
88
|
+
gem_constant.const_get('RailsLensModelSource')
|
|
89
|
+
rescue NameError
|
|
90
|
+
nil
|
|
91
|
+
rescue StandardError => e
|
|
92
|
+
log_error("Error loading model source from #{gem_name}: #{e.message}")
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def valid_source?(klass)
|
|
97
|
+
return false unless klass.is_a?(Class)
|
|
98
|
+
|
|
99
|
+
required_methods = %i[models file_patterns annotate_model remove_annotation]
|
|
100
|
+
required_methods.all? { |m| klass.respond_to?(m) }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def log_error(message)
|
|
104
|
+
error_reporting = RailsLens.config.model_sources[:error_reporting] || :warn
|
|
105
|
+
|
|
106
|
+
case error_reporting
|
|
107
|
+
when :silent
|
|
108
|
+
# Do nothing
|
|
109
|
+
when :warn
|
|
110
|
+
RailsLens.logger.warn "[RailsLens ModelSources] #{message}"
|
|
111
|
+
when :verbose
|
|
112
|
+
RailsLens.logger.error "[RailsLens ModelSources] #{message}"
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsLens
|
|
4
|
+
module ModelSources
|
|
5
|
+
# Built-in model source for ActiveRecord models
|
|
6
|
+
class ActiveRecordSource < ModelSource
|
|
7
|
+
class << self
|
|
8
|
+
def models(options = {})
|
|
9
|
+
# Convert models option to include option for ModelDetector
|
|
10
|
+
opts = options.dup
|
|
11
|
+
opts[:include] = opts.delete(:models) if opts[:models]
|
|
12
|
+
|
|
13
|
+
models = ModelDetector.detect_models(opts)
|
|
14
|
+
|
|
15
|
+
# Filter abstract classes based on options
|
|
16
|
+
if opts[:include_abstract]
|
|
17
|
+
# Include all models
|
|
18
|
+
elsif opts[:abstract_only]
|
|
19
|
+
models = models.select(&:abstract_class?)
|
|
20
|
+
else
|
|
21
|
+
# Default: exclude abstract classes
|
|
22
|
+
models = models.reject(&:abstract_class?)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
models
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def file_patterns
|
|
29
|
+
['app/models/**/*.rb']
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def annotate_model(model, options = {})
|
|
33
|
+
# Use the optimized connection-pooled annotation
|
|
34
|
+
results = { annotated: [], skipped: [], failed: [] }
|
|
35
|
+
|
|
36
|
+
# Group this single model by connection pool for consistency
|
|
37
|
+
begin
|
|
38
|
+
pool = model.connection_pool
|
|
39
|
+
pool.with_connection do |connection|
|
|
40
|
+
Schema::AnnotationManager.process_model_with_connection(model, connection, results, options)
|
|
41
|
+
end
|
|
42
|
+
rescue StandardError
|
|
43
|
+
# Fallback without connection management
|
|
44
|
+
Schema::AnnotationManager.process_model_with_connection(model, nil, results, options)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if results[:annotated].include?(model.name)
|
|
48
|
+
{ status: :annotated, model: model.name, file: model_file_path(model) }
|
|
49
|
+
elsif results[:failed].any? { |f| f[:model] == model.name }
|
|
50
|
+
failure = results[:failed].find { |f| f[:model] == model.name }
|
|
51
|
+
{ status: :failed, model: model.name, message: failure[:error] }
|
|
52
|
+
else
|
|
53
|
+
{ status: :skipped, model: model.name }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def remove_annotation(model)
|
|
58
|
+
manager = Schema::AnnotationManager.new(model)
|
|
59
|
+
if manager.remove_annotations
|
|
60
|
+
{ status: :removed, model: model.name, file: model_file_path(model) }
|
|
61
|
+
else
|
|
62
|
+
{ status: :skipped, model: model.name }
|
|
63
|
+
end
|
|
64
|
+
rescue StandardError => e
|
|
65
|
+
{ status: :failed, model: model.name, message: e.message }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def source_name
|
|
69
|
+
'ActiveRecord'
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def model_file_path(model)
|
|
75
|
+
return nil unless model.name
|
|
76
|
+
|
|
77
|
+
const_source_location = Object.const_source_location(model.name)
|
|
78
|
+
return const_source_location.first if const_source_location
|
|
79
|
+
|
|
80
|
+
if defined?(Rails.root)
|
|
81
|
+
Rails.root.join('app', 'models', "#{model.name.underscore}.rb").to_s
|
|
82
|
+
end
|
|
83
|
+
rescue StandardError
|
|
84
|
+
nil
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -148,6 +148,54 @@ module RailsLens
|
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
def self.annotate_all(options = {})
|
|
151
|
+
results = { annotated: [], skipped: [], failed: [], by_source: {} }
|
|
152
|
+
|
|
153
|
+
# Iterate through all model sources
|
|
154
|
+
ModelSourceLoader.load_sources.each do |source|
|
|
155
|
+
puts "Annotating #{source.source_name} models..." if options[:verbose]
|
|
156
|
+
source_results = annotate_source(source, options)
|
|
157
|
+
results[:by_source][source.source_name] = source_results[:annotated].length
|
|
158
|
+
merge_results(results, source_results)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
results
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Annotate models from a specific source
|
|
165
|
+
def self.annotate_source(source, options = {})
|
|
166
|
+
results = { annotated: [], skipped: [], failed: [] }
|
|
167
|
+
|
|
168
|
+
begin
|
|
169
|
+
models = source.models(options)
|
|
170
|
+
puts " Found #{models.size} #{source.source_name} models" if options[:verbose]
|
|
171
|
+
|
|
172
|
+
models.each do |model|
|
|
173
|
+
result = source.annotate_model(model, options)
|
|
174
|
+
case result[:status]
|
|
175
|
+
when :annotated
|
|
176
|
+
results[:annotated] << result[:model]
|
|
177
|
+
when :skipped
|
|
178
|
+
results[:skipped] << result[:model]
|
|
179
|
+
when :failed
|
|
180
|
+
results[:failed] << { model: result[:model], error: result[:message] }
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
rescue StandardError => e
|
|
184
|
+
puts " Error processing #{source.source_name} source: #{e.message}" if options[:verbose]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
results
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Merge source results into main results
|
|
191
|
+
def self.merge_results(main, source)
|
|
192
|
+
main[:annotated].concat(source[:annotated] || [])
|
|
193
|
+
main[:skipped].concat(source[:skipped] || [])
|
|
194
|
+
main[:failed].concat(source[:failed] || [])
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Original ActiveRecord-specific annotation logic (used by ActiveRecordSource)
|
|
198
|
+
def self.annotate_active_record_models(options = {})
|
|
151
199
|
# Convert models option to include option for ModelDetector
|
|
152
200
|
if options[:models]
|
|
153
201
|
options[:include] = options[:models]
|
|
@@ -265,10 +313,53 @@ module RailsLens
|
|
|
265
313
|
end
|
|
266
314
|
|
|
267
315
|
def self.remove_all(options = {})
|
|
268
|
-
|
|
269
|
-
|
|
316
|
+
results = { removed: [], skipped: [], failed: [], by_source: {} }
|
|
317
|
+
|
|
318
|
+
# Iterate through all model sources
|
|
319
|
+
ModelSourceLoader.load_sources.each do |source|
|
|
320
|
+
puts "Removing annotations from #{source.source_name} models..." if options[:verbose]
|
|
321
|
+
source_results = remove_source(source, options)
|
|
322
|
+
results[:by_source][source.source_name] = source_results[:removed].length
|
|
323
|
+
merge_remove_results(results, source_results)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
results
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Remove annotations from a specific source
|
|
330
|
+
def self.remove_source(source, options = {})
|
|
331
|
+
results = { removed: [], skipped: [], failed: [] }
|
|
332
|
+
|
|
333
|
+
begin
|
|
334
|
+
models = source.models(options.merge(include_abstract: true))
|
|
335
|
+
puts " Found #{models.size} #{source.source_name} models" if options[:verbose]
|
|
336
|
+
|
|
337
|
+
models.each do |model|
|
|
338
|
+
result = source.remove_annotation(model)
|
|
339
|
+
case result[:status]
|
|
340
|
+
when :removed
|
|
341
|
+
results[:removed] << result[:model]
|
|
342
|
+
when :skipped
|
|
343
|
+
results[:skipped] << result[:model]
|
|
344
|
+
when :failed
|
|
345
|
+
results[:failed] << { model: result[:model], error: result[:message] }
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
rescue StandardError => e
|
|
349
|
+
puts " Error removing from #{source.source_name} source: #{e.message}" if options[:verbose]
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
results
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Merge removal results into main results
|
|
356
|
+
def self.merge_remove_results(main, source)
|
|
357
|
+
main[:removed].concat(source[:removed] || [])
|
|
358
|
+
main[:skipped].concat(source[:skipped] || [])
|
|
359
|
+
main[:failed].concat(source[:failed] || [])
|
|
270
360
|
end
|
|
271
361
|
|
|
362
|
+
# Original filesystem-based removal (kept for backwards compatibility)
|
|
272
363
|
def self.remove_all_by_filesystem(options = {})
|
|
273
364
|
base_path = options[:models_path] || default_models_path
|
|
274
365
|
results = { removed: [], skipped: [], failed: [] }
|
|
@@ -11,7 +11,13 @@ namespace :rails_lens do
|
|
|
11
11
|
|
|
12
12
|
results = RailsLens::Schema::AnnotationManager.annotate_all(options)
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
if results[:by_source]&.any?
|
|
15
|
+
results[:by_source].each do |source_name, count|
|
|
16
|
+
puts "Annotated #{count} #{source_name} models" if count.positive?
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
puts "Annotated #{results[:annotated].length} models"
|
|
20
|
+
end
|
|
15
21
|
puts "Skipped #{results[:skipped].length} models" if results[:skipped].any?
|
|
16
22
|
if results[:failed].any?
|
|
17
23
|
puts "Failed to annotate #{results[:failed].length} models:"
|
|
@@ -21,9 +27,44 @@ namespace :rails_lens do
|
|
|
21
27
|
end
|
|
22
28
|
end
|
|
23
29
|
|
|
30
|
+
desc 'Remove all annotations from models'
|
|
31
|
+
task remove: :environment do
|
|
32
|
+
require 'rails_lens/schema/annotation_manager'
|
|
33
|
+
|
|
34
|
+
results = RailsLens::Schema::AnnotationManager.remove_all
|
|
35
|
+
|
|
36
|
+
if results[:by_source]&.any?
|
|
37
|
+
results[:by_source].each do |source_name, count|
|
|
38
|
+
puts "Removed annotations from #{count} #{source_name} models" if count.positive?
|
|
39
|
+
end
|
|
40
|
+
elsif results[:removed].any?
|
|
41
|
+
puts "Removed annotations from #{results[:removed].length} models"
|
|
42
|
+
end
|
|
43
|
+
puts "Skipped #{results[:skipped].length} models (no annotations)" if results[:skipped].any?
|
|
44
|
+
if results[:failed].any?
|
|
45
|
+
puts "Failed to remove annotations from #{results[:failed].length} models:"
|
|
46
|
+
results[:failed].each do |failure|
|
|
47
|
+
puts " - #{failure[:model]}: #{failure[:error]}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
desc 'List registered model sources'
|
|
53
|
+
task sources: :environment do
|
|
54
|
+
require 'rails_lens'
|
|
55
|
+
|
|
56
|
+
puts 'Registered model sources:'
|
|
57
|
+
RailsLens::ModelSourceLoader.list_sources.each do |source|
|
|
58
|
+
puts " - #{source[:name]} (#{source[:class]})"
|
|
59
|
+
source[:patterns].each do |pattern|
|
|
60
|
+
puts " #{pattern}"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
24
65
|
desc 'Annotate all Rails files (models, routes, and mailers)'
|
|
25
66
|
task all: :environment do
|
|
26
|
-
# Annotate models
|
|
67
|
+
# Annotate models (includes all registered model sources)
|
|
27
68
|
Rake::Task['rails_lens:annotate'].invoke
|
|
28
69
|
|
|
29
70
|
# Annotate routes
|
|
@@ -32,4 +73,16 @@ namespace :rails_lens do
|
|
|
32
73
|
# Annotate mailers
|
|
33
74
|
Rake::Task['rails_lens:mailers:annotate'].invoke
|
|
34
75
|
end
|
|
76
|
+
|
|
77
|
+
desc 'Remove all annotations from Rails files (models, routes, and mailers)'
|
|
78
|
+
task remove_all: :environment do
|
|
79
|
+
# Remove model annotations (includes all registered model sources)
|
|
80
|
+
Rake::Task['rails_lens:remove'].invoke
|
|
81
|
+
|
|
82
|
+
# Remove route annotations
|
|
83
|
+
Rake::Task['rails_lens:routes:remove'].invoke
|
|
84
|
+
|
|
85
|
+
# Remove mailer annotations
|
|
86
|
+
Rake::Task['rails_lens:mailers:remove'].invoke
|
|
87
|
+
end
|
|
35
88
|
end
|
data/lib/rails_lens/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_lens
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
@@ -198,6 +198,9 @@ files:
|
|
|
198
198
|
- lib/rails_lens/mailer/annotator.rb
|
|
199
199
|
- lib/rails_lens/mailer/extractor.rb
|
|
200
200
|
- lib/rails_lens/model_detector.rb
|
|
201
|
+
- lib/rails_lens/model_source.rb
|
|
202
|
+
- lib/rails_lens/model_source_loader.rb
|
|
203
|
+
- lib/rails_lens/model_sources/active_record_source.rb
|
|
201
204
|
- lib/rails_lens/note_codes.rb
|
|
202
205
|
- lib/rails_lens/parsers.rb
|
|
203
206
|
- lib/rails_lens/parsers/class_info.rb
|