annotaterb 4.14.0 → 4.16.0

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: 915a9aee6e9396080e4b115bc34466f422cc866aa31f64e8563a409f4ed53753
4
- data.tar.gz: 30e4511ae77313d4d1f7348599286815a85e56f2c100f31ca73cc7a1feaa59d4
3
+ metadata.gz: 91c5e0b20f8c7f5a3160bd2eac0f1e96f9b05e9fd72ddd08fc18422d63472510
4
+ data.tar.gz: e7bfda05d5943c0b2c978dc6863151aad9c1934b6ff5551ec88292ab96dfc57d
5
5
  SHA512:
6
- metadata.gz: 8e1c00298956404ba517a54b803f0cae4235da530a1f2dfe6db35e58c3747ba4bc7304759375ecf51237870f2e4701b965192b235e1c430b08bd4d0340bf6dc1
7
- data.tar.gz: 63f51a4bbc1ebf2455aeeb8b3a7459a87787b309f5deae0a0e91df971bc28a36f77fa71f7b80a4fbe8f32d3660936fa41576f56569e72526fc2505a77c8fc6df
6
+ metadata.gz: 42f9849dd393c04606dcbd7ada4749bfb617180fe3992d19adb16f17f00a9e4af4ecae34d2a9305b5a2ab5b359c4762a87c83cd43c395302e14387b43538ccb2
7
+ data.tar.gz: 5cc6acd44ac8ae6c4aeb5895e776adf3ac75044fc67e4c2682527256bda5a36915670dbae92e3a478f55d883907c5c480e28c769b7f40c219c5d4a06fc64750f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # Changelog
2
2
 
3
+ ## [v4.15.0](https://github.com/drwl/annotaterb/tree/v4.15.0) (2025-05-30)
4
+
5
+ [Full Changelog](https://github.com/drwl/annotaterb/compare/v4.14.1...v4.15.0)
6
+
7
+ **Closed issues:**
8
+
9
+ - Feature request: packs-rails support [\#99](https://github.com/drwl/annotaterb/issues/99)
10
+ - Annotate models using Zeitwerk namespaces [\#82](https://github.com/drwl/annotaterb/issues/82)
11
+
12
+ **Merged pull requests:**
13
+
14
+ - Bump version to v4.15.0 [\#218](https://github.com/drwl/annotaterb/pull/218) ([drwl](https://github.com/drwl))
15
+ - Add debug logs for model annotation [\#217](https://github.com/drwl/annotaterb/pull/217) ([jarredhawkins](https://github.com/jarredhawkins))
16
+ - Cache retrieved indexes in ModelWrapper [\#215](https://github.com/drwl/annotaterb/pull/215) ([tr4b4nt](https://github.com/tr4b4nt))
17
+ - feat: identify unique indexes in simple\_indexes option [\#214](https://github.com/drwl/annotaterb/pull/214) ([amerritt14](https://github.com/amerritt14))
18
+ - fix: Handle case when table\_name\_prefix specified as symbol [\#208](https://github.com/drwl/annotaterb/pull/208) ([gururuby](https://github.com/gururuby))
19
+ - Support the glob pattern in `root_dir` and `model_dir` [\#198](https://github.com/drwl/annotaterb/pull/198) ([sinsoku](https://github.com/sinsoku))
20
+ - Fix `changelog_uri` in gemspec [\#192](https://github.com/drwl/annotaterb/pull/192) ([y-yagi](https://github.com/y-yagi))
21
+ - Generate changelog for v4.14.0 [\#191](https://github.com/drwl/annotaterb/pull/191) ([drwl](https://github.com/drwl))
22
+ - feat: add `timestamp_columns` config option [\#173](https://github.com/drwl/annotaterb/pull/173) ([pbernays](https://github.com/pbernays))
23
+
24
+ ## [v4.14.1](https://github.com/drwl/annotaterb/tree/v4.14.1) (2025-03-31)
25
+
26
+ [Full Changelog](https://github.com/drwl/annotaterb/compare/v4.14.0...v4.14.1)
27
+
28
+ **Closed issues:**
29
+
30
+ - Sort by database order [\#194](https://github.com/drwl/annotaterb/issues/194)
31
+ - “wrong number of arguments \(given 0, expected 1..2\)” when using enum in a Rails 8 model [\#184](https://github.com/drwl/annotaterb/issues/184)
32
+
33
+ ## [v4.14.0](https://github.com/drwl/annotaterb/tree/v4.14.0) (2025-02-17)
34
+
35
+ [Full Changelog](https://github.com/drwl/annotaterb/compare/v4.13.0...v4.14.0)
36
+
37
+ **Closed issues:**
38
+
39
+ - How do I annotate models but NOT routes? [\#178](https://github.com/drwl/annotaterb/issues/178)
40
+ - Model Annotation Not Updated When Modifying Table Columns Using change\_table. [\#169](https://github.com/drwl/annotaterb/issues/169)
41
+ - annotate\_rb:install failing on Rails 8 [\#168](https://github.com/drwl/annotaterb/issues/168)
42
+ - Annotations with enums changing between db:create db:migrate and then db:migrate [\#167](https://github.com/drwl/annotaterb/issues/167)
43
+ - JSON parse error [\#155](https://github.com/drwl/annotaterb/issues/155)
44
+ - Feature: Add Support for dynamic fixtures with ERB [\#149](https://github.com/drwl/annotaterb/issues/149)
45
+
46
+ **Merged pull requests:**
47
+
48
+ - Bump version to v4.14.0 [\#190](https://github.com/drwl/annotaterb/pull/190) ([drwl](https://github.com/drwl))
49
+ - Add expected file to automatically require [\#185](https://github.com/drwl/annotaterb/pull/185) ([drwl](https://github.com/drwl))
50
+ - Fix translation foreign key exclusion bug [\#181](https://github.com/drwl/annotaterb/pull/181) ([galori](https://github.com/galori))
51
+ - Lock `concurrent-ruby` gem to fix CI [\#180](https://github.com/drwl/annotaterb/pull/180) ([tagliala](https://github.com/tagliala))
52
+ - Chore: alert when multiple conmmands were selected [\#179](https://github.com/drwl/annotaterb/pull/179) ([OdenTakashi](https://github.com/OdenTakashi))
53
+ - Updated COLUMN\_PATTERN to handle optional metadata \(e.g., constraints or descriptions\) enclosed in parentheses. [\#170](https://github.com/drwl/annotaterb/pull/170) ([hatsu38](https://github.com/hatsu38))
54
+ - Opt-in for MFA requirement [\#166](https://github.com/drwl/annotaterb/pull/166) ([tagliala](https://github.com/tagliala))
55
+ - Fix typos [\#165](https://github.com/drwl/annotaterb/pull/165) ([tagliala](https://github.com/tagliala))
56
+ - Add support for virtual columns [\#163](https://github.com/drwl/annotaterb/pull/163) ([robbevp](https://github.com/robbevp))
57
+ - Generate changelog for v4.13.0 [\#160](https://github.com/drwl/annotaterb/pull/160) ([drwl](https://github.com/drwl))
58
+
3
59
  ## [v4.13.0](https://github.com/drwl/annotaterb/tree/v4.13.0) (2024-10-21)
4
60
 
5
61
  [Full Changelog](https://github.com/drwl/annotaterb/compare/v4.12.0...v4.13.0)
@@ -293,7 +349,7 @@
293
349
  - Fix the default behavior for model annotator [\#20](https://github.com/drwl/annotaterb/pull/20) ([drwl](https://github.com/drwl))
294
350
  - Refactor model annotator [\#19](https://github.com/drwl/annotaterb/pull/19) ([drwl](https://github.com/drwl))
295
351
  - Removed unused helper methods and `Env` class [\#18](https://github.com/drwl/annotaterb/pull/18) ([drwl](https://github.com/drwl))
296
- - Update dummy app depdencies [\#17](https://github.com/drwl/annotaterb/pull/17) ([drwl](https://github.com/drwl))
352
+ - Update dummy app dependencies [\#17](https://github.com/drwl/annotaterb/pull/17) ([drwl](https://github.com/drwl))
297
353
 
298
354
  ## [v4.0.0](https://github.com/drwl/annotaterb/tree/v4.0.0) (2023-05-03)
299
355
 
data/README.md CHANGED
@@ -134,6 +134,7 @@ Annotate model options:
134
134
  --ignore-unknown-models don't display warnings for bad model files
135
135
  -I, --ignore-columns REGEX don't annotate columns that match a given REGEX (i.e., `annotate -I '^(id|updated_at|created_at)'`
136
136
  --with-comment include database comments in model annotations
137
+ --with-column-comments include column comments in model annotations
137
138
 
138
139
  Annotate routes options:
139
140
  Usage: annotaterb routes [options]
@@ -149,7 +150,7 @@ Command options:
149
150
  Additional options that work for annotating models and routes
150
151
 
151
152
  --additional-file-patterns path1,path2,path3
152
- Additional file paths or globs to annotate, separated by commas (e.g. `/foo/bar/%model_name%/*.rb,/baz/%model_name%.rb`)
153
+ Additional file paths or globs to annotate, separated by commas (e.g. `/foo/bar/%MODEL_NAME%/*.rb,/baz/%MODEL_NAME%.rb`)
153
154
  -d, --delete Remove annotations from all model files or the routes.rb file
154
155
  --model-dir dir Annotate model files stored in dir rather than app/models, separate multiple dirs with commas
155
156
  --root-dir dir Annotate files stored within root dir projects, separate multiple dirs with commas
@@ -192,7 +193,7 @@ Previously in the [Annotate](https://github.com/ctran/annotate_models) you could
192
193
  position: after
193
194
  ```
194
195
 
195
- Annotaterb reads first from the configuration file, if it exists, then merges it with any options passed into the CLI.
196
+ Annotaterb reads first the configuration file, if it exists, passes its content through ERB, and merges the result with any options passed into the CLI.
196
197
 
197
198
  For further details visit the [section in the migration guide](MIGRATION_GUIDE.md#automatic-annotations-after-running-database-migration-commands).
198
199
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.14.0
1
+ 4.16.0
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "erb"
4
+
3
5
  module AnnotateRb
4
6
  # Raised when a configuration file is not found.
5
7
  class ConfigNotFoundError < StandardError
@@ -20,8 +22,9 @@ module AnnotateRb
20
22
  # Method from Rubocop::ConfigLoader
21
23
  def load_yaml_configuration(absolute_path)
22
24
  file_contents = read_file(absolute_path)
25
+ yaml_code = ERB.new(file_contents).result
23
26
 
24
- hash = yaml_safe_load(file_contents, absolute_path) || {}
27
+ hash = yaml_safe_load(yaml_code, absolute_path) || {}
25
28
 
26
29
  # TODO: Print config if debug flag/option is set
27
30
 
@@ -15,10 +15,9 @@ module AnnotateRb
15
15
  klass.eager_load!
16
16
  end
17
17
  else
18
- options[:model_dir].each do |dir|
19
- ::Rake::FileList["#{dir}/**/*.rb"].each do |fname|
20
- require File.expand_path(fname)
21
- end
18
+ model_files = ModelAnnotator::ModelFilesGetter.call(options)
19
+ model_files&.each do |model_file|
20
+ require model_file
22
21
  end
23
22
  end
24
23
  end
@@ -47,19 +47,46 @@ module AnnotateRb
47
47
  def content_annotated_before(parsed, content_without_annotations, write_position)
48
48
  same_write_position = @parsed_file.has_annotations? && @parsed_file.annotation_position.to_s == write_position
49
49
 
50
- # Could error if there's no class or module declaration
51
- _constant_name, line_number_before = parsed.starts.first
50
+ _constant_name, line_number_before = determine_annotation_position(parsed)
52
51
 
53
52
  content_with_annotations_written_before = []
54
53
  content_with_annotations_written_before << content_without_annotations.lines[0...line_number_before]
55
54
  content_with_annotations_written_before << $/ if @parsed_file.has_leading_whitespace? && same_write_position
56
- content_with_annotations_written_before << @new_wrapped_annotations.lines
55
+ content_with_annotations_written_before << formatted_annotations(content_without_annotations, line_number_before)
57
56
  content_with_annotations_written_before << $/ if @parsed_file.has_trailing_whitespace? && same_write_position
58
57
  content_with_annotations_written_before << content_without_annotations.lines[line_number_before..]
59
58
 
60
59
  content_with_annotations_written_before.join
61
60
  end
62
61
 
62
+ # Determines where to place the annotation based on the nested_position option.
63
+ # When nested_position is enabled, finds the most deeply nested class declaration
64
+ # to place annotations directly above nested classes instead of at the file top.
65
+ def determine_annotation_position(parsed)
66
+ return parsed.starts.first unless @options[:nested_position]
67
+
68
+ class_entries = parsed.starts.select { |name, _line| parsed.type_map[name] == :class }
69
+ class_entries.last || parsed.starts.first
70
+ end
71
+
72
+ # Formats annotations with appropriate indentation for consistent code structure.
73
+ # Applies the same indentation level as the target line to maintain proper
74
+ # code alignment when using nested positioning.
75
+ def formatted_annotations(content_without_annotations, line_number_before)
76
+ indentation = determine_indentation(content_without_annotations, line_number_before)
77
+ @new_wrapped_annotations.lines.map { |line| "#{indentation}#{line}" }
78
+ end
79
+
80
+ # Calculates the indentation string to apply to annotations for nested positioning.
81
+ # Extracts leading whitespace from the target line to preserve visual hierarchy
82
+ # and readability of nested code structures.
83
+ def determine_indentation(content_without_annotations, line_number_before)
84
+ return "" unless @options[:nested_position] && line_number_before > 0
85
+
86
+ target_line = content_without_annotations.lines[line_number_before]
87
+ target_line&.match(/^(\s*)/)&.[](1) || ""
88
+ end
89
+
63
90
  def content_annotated_after(parsed, content_without_annotations)
64
91
  _constant_name, line_number_after = parsed.ends.last
65
92
 
@@ -70,10 +70,16 @@ module AnnotateRb
70
70
  sorted_column_indices&.each do |index|
71
71
  indexed_columns = index.columns.reject { |i| i == @column.name }
72
72
 
73
- attrs << if indexed_columns.empty?
73
+ index_text = if index.unique
74
+ "uniquely indexed"
75
+ else
74
76
  "indexed"
77
+ end
78
+
79
+ attrs << if indexed_columns.empty?
80
+ index_text
75
81
  else
76
- "indexed => [#{indexed_columns.join(", ")}]"
82
+ "#{index_text} => [#{indexed_columns.join(", ")}]"
77
83
  end
78
84
  end
79
85
  end
@@ -13,7 +13,8 @@ module AnnotateRb
13
13
 
14
14
  return model_files if model_files.any?
15
15
 
16
- options[:model_dir].each do |dir|
16
+ model_dirs = options[:model_dir].flat_map { |model_dir| Dir[model_dir] }
17
+ model_dirs.each do |dir|
17
18
  Dir.chdir(dir) do
18
19
  list = if options[:ignore_model_sub_dir]
19
20
  Dir["*.rb"].map { |f| [dir, f] }
@@ -26,12 +27,14 @@ module AnnotateRb
26
27
  end
27
28
  end
28
29
 
29
- model_files
30
- rescue SystemCallError
31
- warn "No models found in directory '#{options[:model_dir].join("', '")}'."
32
- warn "Either specify models on the command line, or use the --model-dir option."
33
- warn "Call 'annotaterb --help' for more info."
34
- # exit 1 # TODO: Return exit code back to caller. Right now it messes up RSpec being able to run
30
+ if model_files.empty?
31
+ warn "No models found in directory '#{options[:model_dir].join("', '")}'."
32
+ warn "Either specify models on the command line, or use the --model-dir option."
33
+ warn "Call 'annotaterb --help' for more info."
34
+ # exit 1 # TODO: Return exit code back to caller. Right now it messes up RSpec being able to run
35
+ else
36
+ model_files
37
+ end
35
38
  end
36
39
 
37
40
  private
@@ -6,6 +6,8 @@ module AnnotateRb
6
6
  # Should be the wrapper for an ActiveRecord model that serves as the source of truth of the model
7
7
  # of the model that we're annotating
8
8
 
9
+ DEFAULT_TIMESTAMP_COLUMNS = %w[created_at updated_at]
10
+
9
11
  def initialize(klass, options)
10
12
  @klass = klass
11
13
  @options = options
@@ -107,6 +109,10 @@ module AnnotateRb
107
109
  end
108
110
 
109
111
  def retrieve_indexes_from_table
112
+ @indexes_from_table ||= _retrieve_indexes_from_table
113
+ end
114
+
115
+ def _retrieve_indexes_from_table
110
116
  table_name = @klass.table_name
111
117
  return [] unless table_name
112
118
 
@@ -114,7 +120,7 @@ module AnnotateRb
114
120
  return indexes if indexes.any? || !@klass.table_name_prefix
115
121
 
116
122
  # Try to search the table without prefix
117
- table_name_without_prefix = table_name.to_s.sub(@klass.table_name_prefix, "")
123
+ table_name_without_prefix = table_name.to_s.sub(@klass.table_name_prefix.to_s, "")
118
124
  begin
119
125
  @klass.connection.indexes(table_name_without_prefix)
120
126
  rescue ActiveRecord::StatementInvalid => _e
@@ -143,10 +149,13 @@ module AnnotateRb
143
149
  associations = []
144
150
  id = nil
145
151
 
152
+ # specs don't load defaults, so ensure we have defaults here
153
+ timestamp_columns = @options[:timestamp_columns] || DEFAULT_TIMESTAMP_COLUMNS
154
+
146
155
  cols.each do |c|
147
156
  if c.name.eql?("id")
148
157
  id = c
149
- elsif c.name.eql?("created_at") || c.name.eql?("updated_at")
158
+ elsif timestamp_columns.include?(c.name)
150
159
  timestamps << c
151
160
  elsif c.name[-3, 3].eql?("_id")
152
161
  associations << c
@@ -154,7 +163,10 @@ module AnnotateRb
154
163
  rest_cols << c
155
164
  end
156
165
  end
157
- [rest_cols, timestamps, associations].each { |a| a.sort_by!(&:name) }
166
+
167
+ timestamp_order = timestamp_columns.each_with_index.to_h
168
+ timestamps.sort_by! { |col| timestamp_order[col.name] }
169
+ [rest_cols, associations].each { |a| a.sort_by!(&:name) }
158
170
 
159
171
  ([id] << rest_cols << timestamps << associations).flatten.compact
160
172
  end
@@ -64,7 +64,10 @@ module AnnotateRb
64
64
  def get
65
65
  current_patterns = []
66
66
 
67
- @options[:root_dir].each do |root_directory|
67
+ root_dirs = @options[:root_dir].flat_map do |root_dir|
68
+ root_dir.empty? ? root_dir : Dir[root_dir]
69
+ end
70
+ root_dirs.each do |root_directory|
68
71
  Array(@pattern_types).each do |pattern_type|
69
72
  patterns = generate(root_directory, pattern_type)
70
73
 
@@ -34,6 +34,7 @@ module AnnotateRb
34
34
  private
35
35
 
36
36
  def build_instructions_for_file(file)
37
+ start = Time.now
37
38
  klass = ModelClassGetter.call(file, @options)
38
39
 
39
40
  instructions = []
@@ -52,6 +53,10 @@ module AnnotateRb
52
53
  end
53
54
  instructions.concat(related_file_instructions)
54
55
 
56
+ if @options[:debug]
57
+ puts "Built instructions for #{file} in #{Time.now - start}s"
58
+ end
59
+
55
60
  instructions
56
61
  end
57
62
 
@@ -69,6 +69,9 @@ module AnnotateRb
69
69
  # ModelAnnotator
70
70
  hide_limit_column_types: "",
71
71
 
72
+ # ModelAnnotator
73
+ timestamp_columns: ModelAnnotator::ModelWrapper::DEFAULT_TIMESTAMP_COLUMNS,
74
+
72
75
  ignore_columns: nil, # ModelAnnotator
73
76
  ignore_routes: nil, # RouteAnnotator
74
77
  ignore_unknown_models: false, # ModelAnnotator
@@ -130,6 +133,7 @@ module AnnotateRb
130
133
  :debug,
131
134
  :hide_default_column_types,
132
135
  :hide_limit_column_types,
136
+ :timestamp_columns,
133
137
  :ignore_columns,
134
138
  :ignore_routes,
135
139
  :ignore_unknown_models,
@@ -264,6 +264,11 @@ module AnnotateRb
264
264
  [klass]
265
265
  end
266
266
  end
267
+
268
+ option_parser.on("--nested-position",
269
+ "Place annotations directly above nested classes or modules instead of at the top of the file.") do
270
+ @options[:nested_position] = true
271
+ end
267
272
  end
268
273
 
269
274
  def add_route_options_to_parser(option_parser)
@@ -362,7 +367,7 @@ module AnnotateRb
362
367
 
363
368
  option_parser.on("--additional-file-patterns path1,path2,path3",
364
369
  Array,
365
- "Additional file paths or globs to annotate, separated by commas (e.g. `/foo/bar/%model_name%/*.rb,/baz/%model_name%.rb`)") do |additional_file_patterns|
370
+ "Additional file paths or globs to annotate, separated by commas (e.g. `/foo/bar/%MODEL_NAME%/*.rb,/baz/%MODEL_NAME%.rb`)") do |additional_file_patterns|
366
371
  @options[:additional_file_patterns] = additional_file_patterns
367
372
  end
368
373
 
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: annotaterb
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.14.0
4
+ version: 4.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew W. Lee
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-02-17 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2025-06-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 6.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 6.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 6.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 6.0.0
13
41
  description: Annotates Rails/ActiveRecord Models, routes, fixtures, and others based
14
42
  on the database schema.
15
43
  email:
@@ -120,7 +148,7 @@ licenses:
120
148
  metadata:
121
149
  homepage_uri: https://github.com/drwl/annotaterb
122
150
  source_code_uri: https://github.com/drwl/annotaterb
123
- changelog_uri: https://github.com/drwl/annotaterb/blob/master/CHANGELOG.md
151
+ changelog_uri: https://github.com/drwl/annotaterb/blob/main/CHANGELOG.md
124
152
  bug_tracker_uri: https://github.com/drwl/annotaterb/issues
125
153
  rubygems_mfa_required: 'true'
126
154
  post_install_message:
@@ -131,7 +159,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
131
159
  requirements:
132
160
  - - ">="
133
161
  - !ruby/object:Gem::Version
134
- version: 2.7.0
162
+ version: 3.0.0
135
163
  required_rubygems_version: !ruby/object:Gem::Requirement
136
164
  requirements:
137
165
  - - ">="