annotato 0.1.6 → 0.1.8

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: abbd50b99b491161e60be9f141a6e5315d56602dbf4b361f2f0a9d0fe15ad49b
4
- data.tar.gz: b4f5fb6095559edb538bc3f4fe9f8b6ffd723c3e34a75c9259f2b460474aeed8
3
+ metadata.gz: c083f5bdc6340dbd160b2392a98776b88540333ed1ab48cb7d9cf10065d5299e
4
+ data.tar.gz: 415a2f70e589b162364d16b4132b58cb9899f85f1147214756e3f81fdda89cb3
5
5
  SHA512:
6
- metadata.gz: 05ed566b4eba95b988ec2873588d2f6ac10d650d1807a54ca0374f2657d8f5e29dea50daf8b255f55c7ac58d6e553a1b646a1cef71f0ad31d809331f59eda612
7
- data.tar.gz: 2203994c5c48cff744ad5e1d6583eec04211cd664b9d60aafbbec1cee02ff74dc1ae9a67350736655388e9d419c54c7bf46f897c6b66474805908c103bee4e97
6
+ metadata.gz: 0c5713da93df6b7dfa3bd4cc9524a80e503a2edc5da17268b73e5ee0de762222aa376cd26a7bed31ef9feba87b05e1e0701160987442ea55699033ff8d1341aa
7
+ data.tar.gz: be1a839e36ef7f8066376087108177472bab0750a912c04c687177a1a3856f6da338c8eb5c9893026fc372992325c8219a3bbbbe1bcb4deae962e59a82fb4462
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- annotato (0.1.6)
4
+ annotato (0.1.8)
5
5
  rails (>= 6.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -9,15 +9,15 @@
9
9
 
10
10
  ## Features
11
11
 
12
- - Annotates Rails models with:
13
- - Columns and types
14
- - Default values and constraints
15
- - Enums with values
16
- - Indexes (only if present)
17
- - Triggers (only if present)
18
- - Skips unchanged annotations
19
- - Replaces legacy `annotate`/`annotate_models` blocks
20
- - Smart formatting with aligned columns
12
+ * Annotates Rails models with:
13
+
14
+ * Columns and types
15
+ * Default values and constraints
16
+ * Enums with values
17
+ * Indexes (only if present)
18
+ * Triggers (only if present)
19
+ * Skips unchanged annotations
20
+ * Smart formatting with aligned columns
21
21
 
22
22
  Example:
23
23
 
@@ -57,23 +57,38 @@ bundle install
57
57
 
58
58
  ## Usage
59
59
 
60
- To annotate all models:
60
+ ### Annotate All Models
61
+
62
+ To annotate all models, run:
61
63
 
62
64
  ```bash
63
65
  bundle exec rake annotato:models
64
66
  ```
65
67
 
68
+ ### Annotate Specific Models
69
+
70
+ You can now pass one or multiple model names to the task.
71
+
72
+ ```bash
73
+ # Annotate only the User model
74
+ rake annotato:models[User]
75
+
76
+ # Annotate multiple models (User and Admin::Account)
77
+ rake annotato:models["User,Admin::Account"]
78
+ ```
79
+
66
80
  ---
67
81
 
68
82
  ## Rake Task
69
83
 
70
84
  ```bash
71
- rake annotato:models
85
+ rake annotato:models[MODEL]
72
86
  ```
73
87
 
74
- - Automatically loads all models via `Rails.application.eager_load!`
75
- - Modifies model files in place
76
- - Existing Annotato and Annotate blocks will be replaced
88
+ * Automatically loads all models via `Rails.application.eager_load!`
89
+ * Modifies model files in place
90
+ * Replace existing Annotato and legacy annotate blocks
91
+ * **Pass `MODEL` argument (single or comma-separated) to target specific models**
77
92
 
78
93
  ---
79
94
 
@@ -1,76 +1,97 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
3
+ require "json"
4
4
 
5
5
  module Annotato
6
6
  class ColumnFormatter
7
+ # Main entry: returns an array of comment lines per column, flattened.
7
8
  def self.format(model, connection)
8
- table_name = model.table_name
9
- primary_key = model.primary_key
10
- enums = model.defined_enums
9
+ table_name = model.table_name
10
+ primary_key = model.primary_key
11
11
  unique_indexes = connection.indexes(table_name).select(&:unique)
12
- columns = connection.columns(table_name)
12
+ enums = model.defined_enums
13
+ columns = connection.columns(table_name)
13
14
 
14
- raw_data = columns.map do |col|
15
+ # Compute max widths for name and sql_type so that the table lines up.
16
+ name_width = columns.map(&:name).map(&:length).max
17
+ type_width = columns.map(&:sql_type).map(&:length).max
18
+
19
+ columns.flat_map do |col|
15
20
  name = col.name
16
21
  type = col.sql_type
17
- default = col.default
18
- opts = []
19
-
20
- opts << "default(#{default.inspect})" unless default.nil?
21
- opts << "not null" unless col.null
22
- opts << "primary key" if name == primary_key
23
- opts << "is an Array" if type.end_with?("[]")
24
- opts << "unique" if unique_indexes.any? { |idx| idx.columns == [name] }
25
- opts << "enum" if enums.key?(name)
26
-
27
- [name, type, default, opts]
28
- end
29
-
30
- name_width = raw_data.map { |name, *_| name.length }.max
31
- type_width = raw_data.map { |_, type, *_| type.length }.max
32
22
 
33
- raw_data.flat_map do |name, type, default, opts|
34
- base_line = "# %-#{name_width}s :%-#{type_width}s" % [name, type]
35
- indent = ' ' * (base_line.length + 1)
23
+ # require "pry" if name == "allowed_statuses"
24
+ # binding.pry if name == "allowed_statuses"
25
+ # Build the left-hand side and calculate indent.
26
+ left = "# %-#{name_width}s :%-#{type_width}s" % [name, type]
27
+ # " default(" is 9 chars; +2 gives the extra gap.
28
+ indent_size = left.length + 1
29
+ indent_str = " " * indent_size
30
+ closing_indent_str = " " * (indent_size - 2)
36
31
 
37
- if multiline_default?(default)
38
- formatted_defaults = format_multiline_default(default)
39
- remaining_opts = opts.reject { |o| o.start_with?("default(") }
32
+ # Gather all options, pulling out default_lines if multiline.
33
+ opts = []
34
+ default_block = build_default_block(col.default)
35
+ if default_block
36
+ opts << "__MULTILINE__" # placeholder
37
+ elsif col.default
38
+ opts << "default(#{col.default.inspect})"
39
+ end
40
+ opts << "not null" unless col.null
41
+ opts << "primary key" if name == primary_key
42
+ opts << "is an Array" if type.end_with?("[]")
43
+ opts << "unique" if unique_indexes.any? { |idx| idx.columns == [name] }
44
+ opts << "enum" if enums.key?(name)
40
45
 
41
- lines = []
42
- lines << "#{base_line} default(["
43
- formatted_defaults.each do |v|
44
- lines << "#{'#' + indent}#{v}"
45
- end
46
- closing = "#{'#' + indent}]),"
47
- closing += " #{remaining_opts.join(', ')}" unless remaining_opts.empty?
48
- lines << closing.rstrip
46
+ # Emit either a multiline block or a single line.
47
+ if default_block
48
+ # 1) opening line
49
+ lines = ["#{left} default(#{col.default[0]}"]
50
+ # 2) each interior line, prefixed by "# " + indent_str
51
+ lines += default_block.map { |l| "# #{indent_str}#{l}" }
52
+ # 3) closing line with trailing options
53
+ closing = "# #{closing_indent_str}#{col.default[-1]})"
54
+ trailing = opts.reject { |o| o == "__MULTILINE__" }
55
+ closing += ", #{trailing.join(', ')}" unless trailing.empty?
56
+ lines << closing
49
57
  lines
50
58
  else
51
- line = base_line
59
+ # single-line comment
60
+ line = left
52
61
  line += " #{opts.join(', ')}" unless opts.empty?
53
- line.rstrip
62
+ [line.rstrip]
54
63
  end
55
64
  end
56
65
  end
57
66
 
58
- def self.multiline_default?(value)
59
- value.is_a?(String) && value.strip.start_with?("[") && value.strip.end_with?("]") && value.include?(",")
60
- end
67
+ # Returns nil (no default) or an Array of un-indented lines:
68
+ # ["[", "\"A\",", "\"B\"", "]"] or ["{", "\"k\":v,", ... , "}"]
69
+ # If the value is empty array or hash, returns ["[]"] or ["{}"].
70
+ def self.build_default_block(value)
71
+ return nil if value.nil?
72
+ s = value.strip
73
+ return nil unless s.start_with?("[") || s.start_with?("{")
61
74
 
62
- def self.format_multiline_default(value)
63
- parsed = JSON.parse(value)
64
- return [value] unless parsed.is_a?(Array)
75
+ parsed = JSON.parse(s) rescue nil
76
+ return nil unless parsed.is_a?(Array) || parsed.is_a?(Hash)
65
77
 
66
- items = parsed.is_a?(Array) && parsed[0].is_a?(Array) ? parsed[0] : parsed
78
+ if parsed.is_a?(Array)
79
+ return if parsed.empty? # empty array → ["[]"]
67
80
 
68
- items.map.with_index do |item, idx|
69
- comma = idx == items.size - 1 ? "" : ","
70
- "#{item.to_json}#{comma}"
81
+ # Only a JSON array of strings?
82
+ parsed.map.with_index do |e, i|
83
+ comma = i == parsed.size - 1 ? "" : ","
84
+ %Q{"#{e}"#{comma}}
85
+ end
86
+ else
87
+ return if parsed.empty? # empty hash → ["{}"]
88
+
89
+ # JSON hash → key/value pairs
90
+ parsed.map.with_index do |(k, v), i|
91
+ comma = i == parsed.size - 1 ? "" : ","
92
+ %Q{"#{k}": #{v.inspect}#{comma}}
93
+ end
71
94
  end
72
- rescue JSON::ParserError
73
- [value]
74
95
  end
75
96
  end
76
97
  end
@@ -10,8 +10,9 @@ module Annotato
10
10
  @output = output
11
11
  end
12
12
 
13
- def run
14
- models.each do |model|
13
+ def run(one_model = nil)
14
+ mtd = one_model ? [one_model] : models
15
+ mtd.each do |model|
15
16
  next unless model.table_exists?
16
17
 
17
18
  annotation = AnnotationBuilder.build(model)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Annotato
4
- VERSION = "0.1.6"
4
+ VERSION = "0.1.8"
5
5
  end
@@ -1,8 +1,23 @@
1
+ # lib/tasks/annotato.rake
2
+
1
3
  require "annotato/model_annotator"
2
4
 
3
5
  namespace :annotato do
4
- desc "Annotate models with schema info"
5
- task models: :environment do
6
- Annotato::ModelAnnotator.new.run
6
+ desc "Annotate models with schema info. Pass MODEL=name or comma-separated list"
7
+ task :models, [:MODEL] => :environment do |t, args|
8
+ # Parse MODEL argument into an array of model names, if given
9
+ model_list = args[:MODEL]&.split(",")&.map(&:strip)
10
+
11
+ annotator = Annotato::ModelAnnotator.new
12
+
13
+ if model_list && model_list.any?
14
+ # Constantize each name and annotate only those classes
15
+ model_list.map(&:constantize).each do |klass|
16
+ annotator.run(klass)
17
+ end
18
+ else
19
+ # No MODEL passed → annotate all models
20
+ annotator.run
21
+ end
7
22
  end
8
23
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: annotato
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Serhii Bodnaruk
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-05-16 00:00:00.000000000 Z
10
+ date: 2025-05-17 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails