annotaterb 4.24.0 → 4.25.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: ff11e0177eddc61744806ec5749c7a5d234023e0e7d75d46bd5fc21e4d0998fc
4
- data.tar.gz: f8c87d260bf6cafbeb120568ecda6131f6bd335ef2c3882630a45cc2e6631a05
3
+ metadata.gz: e76c98c2c18b7021f7844189a076f4d5cb52b61777c5200bff9888497d3924d2
4
+ data.tar.gz: ddf7481ef3fdb08071855c9131a7dfca2426d29ad0c7dfdeff9f891d510a867b
5
5
  SHA512:
6
- metadata.gz: 6e2edbfc22db4f8b1bd150313e795993e3f5366ac4594084fec2f7ee9f1d2650d98cef0933bdc92a0ff0a4e52af4bb77b35672cbd0049fed9c8e6c256743e00e
7
- data.tar.gz: ddf3161a49b9c041083ce42c9264823da16e23703a8f013b98098f24dc4f7d46642a659621f0d0612572865fec1099a91e7622139acc3a1c689e7df1d96e81bf
6
+ metadata.gz: 38dd0ae99e1a1e21dd68963b5e6f85bb72d620955ab001df8136dc4d40122196d4bb88dd107bfe95fdd93bcb17befe907f4ebe001ad05f364067b17d40c839e3
7
+ data.tar.gz: be47e90cad90aefdd1ea96d494783731270b26c5a8f24843912eea6f418a1375bbbc7bdc3550181f763eef47eb46eb595d1e54fc2bc06ede1eb26abfbfeb917b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## [v4.24.0](https://github.com/drwl/annotaterb/tree/v4.24.0) (2026-07-24)
4
+
5
+ [Full Changelog](https://github.com/drwl/annotaterb/compare/v4.23.0...v4.24.0)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Annotate postgres enums [\#309](https://github.com/drwl/annotaterb/issues/309)
10
+ - `--frozen` option returns a non-zero error code when database is inaccessible [\#240](https://github.com/drwl/annotaterb/issues/240)
11
+ - Add support for printing enum types [\#176](https://github.com/drwl/annotaterb/issues/176)
12
+
13
+ **Fixed bugs:**
14
+
15
+ - Annotation placement for namespaced models is inconsistent — anchor shifts with file shape \(doc comment presence\) [\#366](https://github.com/drwl/annotaterb/issues/366)
16
+ - Fixture files that start with \<% \(erb\) insert doc inside the erb block [\#345](https://github.com/drwl/annotaterb/issues/345)
17
+ - Schema information inserted every run into some fixture files. [\#344](https://github.com/drwl/annotaterb/issues/344)
18
+
19
+ **Closed issues:**
20
+
21
+ - Routes are not annotated after migration tasks [\#251](https://github.com/drwl/annotaterb/issues/251)
22
+
23
+ **Merged pull requests:**
24
+
25
+ - Bump version to v4.24.0 [\#371](https://github.com/drwl/annotaterb/pull/371) ([drwl](https://github.com/drwl))
26
+ - Revert "Release v4.24.0" [\#370](https://github.com/drwl/annotaterb/pull/370) ([drwl](https://github.com/drwl))
27
+ - Show DEFERRABLE INITIALLY on foreign keys [\#365](https://github.com/drwl/annotaterb/pull/365) ([kamipo](https://github.com/kamipo))
28
+ - Annotate PostgreSQL unique and exclusion constraints [\#364](https://github.com/drwl/annotaterb/pull/364) ([kamipo](https://github.com/kamipo))
29
+ - Fix `--without-comment` help text to say exclude [\#363](https://github.com/drwl/annotaterb/pull/363) ([OdenTakashi](https://github.com/OdenTakashi))
30
+ - Fix NoMethodError when handling malformed annotations [\#362](https://github.com/drwl/annotaterb/pull/362) ([OdenTakashi](https://github.com/OdenTakashi))
31
+ - Remove empty TODO comment from Runner [\#361](https://github.com/drwl/annotaterb/pull/361) ([OdenTakashi](https://github.com/OdenTakashi))
32
+ - Move ignore\_database\_name check into AnnotationBuilder [\#360](https://github.com/drwl/annotaterb/pull/360) ([OdenTakashi](https://github.com/OdenTakashi))
33
+ - Add YAML configuration option reference to the README. [\#359](https://github.com/drwl/annotaterb/pull/359) ([OdenTakashi](https://github.com/OdenTakashi))
34
+ - Respect DB column defaults over `attribute :foo, default: X` overrides [\#358](https://github.com/drwl/annotaterb/pull/358) ([kamipo](https://github.com/kamipo))
35
+ - added :ignore\_database\_name option [\#357](https://github.com/drwl/annotaterb/pull/357) ([HoneyryderChuck](https://github.com/HoneyryderChuck))
36
+ - Add Markdown annotation idempotency test [\#356](https://github.com/drwl/annotaterb/pull/356) ([aouxwoux](https://github.com/aouxwoux))
37
+ - Fix schema\_like? to recognize markdown-formatted annotation rows [\#354](https://github.com/drwl/annotaterb/pull/354) ([nashirox](https://github.com/nashirox))
38
+ - Keep fixture annotations out of ERB blocks \(\#345\) [\#353](https://github.com/drwl/annotaterb/pull/353) ([Halvanhelv](https://github.com/Halvanhelv))
39
+ - Stop tracking generated secondary DB artifacts in dummyapp [\#352](https://github.com/drwl/annotaterb/pull/352) ([OdenTakashi](https://github.com/OdenTakashi))
40
+ - Add Rails version matrix to CI [\#351](https://github.com/drwl/annotaterb/pull/351) ([OdenTakashi](https://github.com/OdenTakashi))
41
+ - Create release script [\#349](https://github.com/drwl/annotaterb/pull/349) ([drwl](https://github.com/drwl))
42
+ - Generate changelog for v4.23.0 [\#348](https://github.com/drwl/annotaterb/pull/348) ([drwl](https://github.com/drwl))
43
+
3
44
  ## [v4.23.0](https://github.com/drwl/annotaterb/tree/v4.23.0) (2026-06-25)
4
45
 
5
46
  [Full Changelog](https://github.com/drwl/annotaterb/compare/v4.22.0...v4.23.0)
data/README.md CHANGED
@@ -22,16 +22,17 @@ The schema comment looks like this:
22
22
  ```ruby
23
23
  # == Schema Information
24
24
  #
25
- # Table name: tasks
25
+ # Table name: users
26
26
  #
27
- # id :integer not null, primary key
28
- # content :string
29
- # count :integer
30
- # status :boolean
31
- # created_at :datetime not null
32
- # updated_at :datetime not null
27
+ # id :integer not null, primary key
28
+ # name :string
29
+ # email :string
30
+ # sign_in_count :integer default(0), not null
31
+ # admin :boolean default(FALSE), not null
32
+ # created_at :datetime not null
33
+ # updated_at :datetime not null
33
34
  #
34
- class Task < ApplicationRecord
35
+ class User < ApplicationRecord
35
36
  ...
36
37
  ```
37
38
 
@@ -68,7 +69,7 @@ This will copy a rake task into your Rails project's `lib/tasks` directory that
68
69
  $ bin/rails db:migrate
69
70
  # ...
70
71
  # Annotating models
71
- # Annotated (1): app/models/task.rb
72
+ # Annotated (1): app/models/user.rb
72
73
  ```
73
74
 
74
75
  To skip the automatic annotation that happens after a db task, pass the environment variable `ANNOTATERB_SKIP_ON_DB_TASKS=1` before your command.
@@ -158,6 +159,8 @@ Annotate model options:
158
159
  --without-column-comments exclude column comments in model annotations
159
160
  --position-of-column-comment [with_name|rightmost_column]
160
161
  set the position, in the annotation block, of the column comment
162
+ --enum-default-format [label|raw|both]
163
+ set how defaults of enum backed columns are shown
161
164
  --with-table-comments include table comments in model annotations
162
165
  --without-table-comments exclude table comments in model annotations
163
166
  --classes-default-to-s class Custom classes to be represented with `to_s`, may be used multiple times
@@ -267,6 +270,7 @@ Keys use snake_case and match the gem defaults in `AnnotateRb::Options`. CLI fla
267
270
  | `simple_indexes` | `false` | Concat related indexes onto each column line. |
268
271
  | `show_check_constraints` | `false` | List check constraints. |
269
272
  | `show_enums` | `false` | Show PostgreSQL enum types. |
273
+ | `enum_default_format` | `label` | Default shown for enum backed columns: `label` (`default("idnow")`), `raw` (`default(0)`) or `both` (`default(0: "idnow")`). |
270
274
  | `show_virtual_columns` | `false` | Show virtual/generated columns. |
271
275
  | `include_version` | `false` | Include the migration version number. |
272
276
  | `with_comment` | `true` | Include database comments (fallback for column/table comment flags). |
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.24.0
1
+ 4.25.0
@@ -37,6 +37,7 @@ module AnnotateRb
37
37
  when Float, Integer then value.to_s
38
38
  # BigDecimals need to be output in a non-normalized form and quoted.
39
39
  when BigDecimal then value.to_s("F")
40
+ when EnumDefault then "#{quote(value.raw)}: #{quote(value.label)}"
40
41
  when String then value.inspect
41
42
  else
42
43
  value.inspect
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnnotateRb
4
+ module ModelAnnotator
5
+ module ColumnAnnotation
6
+ # Pairs the raw DB default of an enum backed column with its label, so
7
+ # that annotations can show both.
8
+ EnumDefault = Struct.new(:raw, :label)
9
+ end
10
+ end
11
+ end
@@ -8,6 +8,7 @@ module AnnotateRb
8
8
  autoload :ColumnWrapper, "annotate_rb/model_annotator/column_annotation/column_wrapper"
9
9
  autoload :AnnotationBuilder, "annotate_rb/model_annotator/column_annotation/annotation_builder"
10
10
  autoload :DefaultValueBuilder, "annotate_rb/model_annotator/column_annotation/default_value_builder"
11
+ autoload :EnumDefault, "annotate_rb/model_annotator/column_annotation/enum_default"
11
12
  autoload :ColumnComponent, "annotate_rb/model_annotator/column_annotation/column_component"
12
13
  end
13
14
  end
@@ -31,7 +31,7 @@ module AnnotateRb
31
31
  def parse_comments
32
32
  # Adds 0-indexed line numbers
33
33
  @input.split($/).each_with_index do |line, line_no|
34
- if line.strip.starts_with?("#")
34
+ if line.strip.start_with?("#")
35
35
  @comments << [line, line_no]
36
36
  end
37
37
  end
@@ -13,7 +13,7 @@ module AnnotateRb
13
13
 
14
14
  def formatted_name
15
15
  @formatted_name ||= if foreign_key.name.blank?
16
- foreign_key.column
16
+ stringified_columns
17
17
  else
18
18
  @options[:show_complete_foreign_keys] ? foreign_key.name : foreign_key.name.gsub(/(?<=^fk_rails_)[0-9a-f]{10}$/, "...")
19
19
  end
@@ -136,13 +136,23 @@ module AnnotateRb
136
136
 
137
137
  def columns_info
138
138
  Array(index.columns).map do |col|
139
- if index.try(:orders) && index.orders[col.to_s]
140
- "#{col} #{index.orders[col.to_s].upcase}"
141
- else
142
- col.to_s.gsub("\r", '\r').gsub("\n", '\n')
143
- end
139
+ column = col.to_s.gsub("\r", '\r').gsub("\n", '\n')
140
+ opclass = column_option(:opclasses, col)
141
+ order = column_option(:orders, col)
142
+
143
+ [column, opclass, order&.upcase].compact.join(" ")
144
144
  end
145
145
  end
146
+
147
+ # ActiveRecord condenses per column index options (`opclasses`, `orders`)
148
+ # into a single value when every column shares the same one, so the
149
+ # option can either be a Hash keyed by column or a bare value.
150
+ def column_option(option, col)
151
+ value = index.try(option)
152
+ value = value[col.to_s] if value.is_a?(Hash)
153
+
154
+ value.presence&.to_s
155
+ end
146
156
  end
147
157
  end
148
158
  end
@@ -73,14 +73,17 @@ module AnnotateRb
73
73
  # `Model#column_defaults` reflects `attribute :foo, default: X` overrides,
74
74
  # which would incorrectly show the Ruby-side default in annotations
75
75
  # instead of the DB schema default. To preserve model-level decorations
76
- # such as `TimeZoneConverter` on datetime columns, we start from
77
- # `column_defaults` and only substitute the DB schema value when a
78
- # difference indicates an attribute-level override.
76
+ # such as enum labels or `TimeZoneConverter` on datetime columns, we start
77
+ # from `column_defaults` and only substitute the DB schema value for
78
+ # attributes whose raw default was replaced.
79
79
  def column_defaults
80
80
  @column_defaults ||= @klass.column_defaults.each_with_object({}) do |(name, value), result|
81
81
  column = @klass.columns_hash[name]
82
- schema_value = schema_default_for(column)
83
- result[name] = (value == schema_value) ? value : schema_value
82
+ result[name] = if attribute_default_overridden?(name, column)
83
+ schema_default_for(column)
84
+ else
85
+ enum_default(name, column, value)
86
+ end
84
87
  end
85
88
  end
86
89
 
@@ -278,6 +281,33 @@ module AnnotateRb
278
281
 
279
282
  private
280
283
 
284
+ # `attribute :foo, default: X` replaces the raw default Rails read from
285
+ # the DB column, so comparing the two raw values detects the override.
286
+ # Comparing cast values instead would misreport decorated attribute types
287
+ # (an enum casts the DB default `0` into its label) as overrides.
288
+ def attribute_default_overridden?(name, column)
289
+ return false if column.nil?
290
+
291
+ @klass._default_attributes[name].value_before_type_cast != column.default
292
+ end
293
+
294
+ # An enum attribute type casts the raw DB default into its label, which is
295
+ # what `column_defaults` reports and what annotations have historically
296
+ # shown. `enum_default_format` picks between that label, the raw value,
297
+ # and both.
298
+ def enum_default(name, column, label)
299
+ return label unless @klass.defined_enums.key?(name)
300
+
301
+ raw = schema_default_for(column)
302
+ return label if raw == label
303
+
304
+ case @options[:enum_default_format]
305
+ when :raw then raw
306
+ when :both then ColumnAnnotation::EnumDefault.new(raw, label)
307
+ else label
308
+ end
309
+ end
310
+
281
311
  def schema_default_for(column)
282
312
  return nil if column.nil? || column.default.nil? || column.default_function
283
313
  cast_type_for(column).deserialize(column.default)
@@ -43,7 +43,9 @@ module AnnotateRb
43
43
 
44
44
  klass.reset_column_information
45
45
  model_name = klass.name.underscore
46
- table_name = klass.table_name
46
+ # Match annotation behavior so a secondary model with a duplicate table
47
+ # name cannot remove annotations from the primary database's fixture.
48
+ table_name = klass.table_name if klass.connection_specification_name == ActiveRecord::Base.name
47
49
 
48
50
  model_instruction = SingleFileRemoveAnnotationInstruction.new(file, @options)
49
51
  instructions << model_instruction
@@ -88,8 +88,33 @@ module AnnotateRb
88
88
 
89
89
  patterns
90
90
  .map { |f| FileNameResolver.call(f, @model_name, @table_name) }
91
- .map { |f| Dir.glob(f) }
92
- .flatten
91
+ .flat_map { |f| Dir.glob(f) }
92
+ .select { |f| owning_root_dir(f) == model_root_dir }
93
+ end
94
+
95
+ def model_root_dir
96
+ return @model_root_dir if defined?(@model_root_dir)
97
+
98
+ @model_root_dir = owning_root_dir(@file)
99
+ end
100
+
101
+ # Patterns are expanded for every `root_dir` and resolved by model name alone, so models sharing a
102
+ # file basename across root directories (e.g. packwerk packs) glob the same related files. Root
103
+ # directories can be nested, so a file belongs to the most specific one containing it, and only
104
+ # models from that same root directory may annotate it. Returns nil for the project root.
105
+ def owning_root_dir(file)
106
+ expanded_file = File.expand_path(file)
107
+
108
+ expanded_root_dirs
109
+ .select { |dir| expanded_file.start_with?("#{dir}/") }
110
+ .max_by(&:length)
111
+ end
112
+
113
+ def expanded_root_dirs
114
+ @expanded_root_dirs ||= Array(@options[:root_dir])
115
+ .reject { |root_dir| root_dir.to_s.empty? }
116
+ .flat_map { |root_dir| Dir.glob(root_dir) }
117
+ .map { |root_dir| File.expand_path(root_dir) }
93
118
  end
94
119
 
95
120
  def add_related_test_files
@@ -63,7 +63,8 @@ module AnnotateRb
63
63
  with_comment: true, # ModelAnnotator
64
64
  with_column_comments: nil, # ModelAnnotator
65
65
  with_table_comments: nil, # ModelAnnotator
66
- position_of_column_comment: :with_name # ModelAnnotator
66
+ position_of_column_comment: :with_name, # ModelAnnotator
67
+ enum_default_format: :label # ModelAnnotator
67
68
  }.freeze
68
69
 
69
70
  OTHER_OPTIONS = {
@@ -84,7 +85,6 @@ module AnnotateRb
84
85
  ignore_multi_database_name: false, # ModelAnnotator
85
86
  ignore_routes: nil, # RouteAnnotator
86
87
  models: true, # Core
87
- routes: false, # Core
88
88
  skip_on_db_migrate: false, # Core
89
89
  auto_annotate_routes_after_migrate: false, # Core
90
90
  target_action: :do_annotations, # Core; Possible values: :do_annotations, :remove_annotations
@@ -141,7 +141,8 @@ module AnnotateRb
141
141
  :with_comment,
142
142
  :with_column_comments,
143
143
  :with_table_comments,
144
- :position_of_column_comment
144
+ :position_of_column_comment,
145
+ :enum_default_format
145
146
  ].freeze
146
147
 
147
148
  OTHER_OPTION_KEYS = [
@@ -155,7 +156,6 @@ module AnnotateRb
155
156
  :ignore_routes,
156
157
  :ignore_multi_database_name,
157
158
  :models,
158
- :routes,
159
159
  :skip_on_db_migrate,
160
160
  :auto_annotate_routes_after_migrate,
161
161
  :target_action,
@@ -224,6 +224,7 @@ module AnnotateRb
224
224
  @options[:with_column_comments] = @options[:with_comment] if @options[:with_column_comments].nil?
225
225
  @options[:with_table_comments] = @options[:with_comment] if @options[:with_table_comments].nil?
226
226
  @options[:position_of_column_comment] = @options[:position_of_column_comment].to_sym
227
+ @options[:enum_default_format] = @options[:enum_default_format].to_sym
227
228
 
228
229
  self
229
230
  end
@@ -268,6 +268,11 @@ module AnnotateRb
268
268
  @options[:position_of_column_comment] = value.to_sym
269
269
  end
270
270
 
271
+ option_parser.on("--enum-default-format [label|raw|both]",
272
+ "set how defaults of enum backed columns are shown") do |value|
273
+ @options[:enum_default_format] = value.to_sym
274
+ end
275
+
271
276
  option_parser.on("--with-table-comments",
272
277
  "include table comments in model annotations") do
273
278
  @options[:with_table_comments] = true
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: annotaterb
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.24.0
4
+ version: 4.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew W. Lee
@@ -88,6 +88,7 @@ files:
88
88
  - lib/annotate_rb/model_annotator/column_annotation/column_component.rb
89
89
  - lib/annotate_rb/model_annotator/column_annotation/column_wrapper.rb
90
90
  - lib/annotate_rb/model_annotator/column_annotation/default_value_builder.rb
91
+ - lib/annotate_rb/model_annotator/column_annotation/enum_default.rb
91
92
  - lib/annotate_rb/model_annotator/column_annotation/type_builder.rb
92
93
  - lib/annotate_rb/model_annotator/components.rb
93
94
  - lib/annotate_rb/model_annotator/enum_annotation.rb