annotate 2.7.0 → 2.7.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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ::ActiveRecord
4
4
  class Base
5
- def self.method_missing(name, *args)
5
+ def self.method_missing(_name, *_args)
6
6
  # ignore this, so unknown/unloaded macros won't cause parsing to fail
7
7
  end
8
8
  end
@@ -1,13 +1,14 @@
1
1
  require 'bigdecimal'
2
2
 
3
3
  module AnnotateModels
4
+ TRUE_RE = /^(true|t|yes|y|1)$/i
5
+
4
6
  # Annotate Models plugin use this header
5
7
  COMPAT_PREFIX = "== Schema Info"
6
8
  COMPAT_PREFIX_MD = "## Schema Info"
7
9
  PREFIX = "== Schema Information"
8
10
  PREFIX_MD = "## Schema Information"
9
11
  END_MARK = "== Schema Information End"
10
- PATTERN = /^\r?\n?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?\r?\n(#.*\r?\n)*(\r?\n)*/
11
12
 
12
13
  MATCHED_TYPES = %w(test fixture factory serializer scaffold controller helper)
13
14
 
@@ -49,83 +50,111 @@ module AnnotateModels
49
50
  # Controller files
50
51
  CONTROLLER_DIR = File.join("app", "controllers")
51
52
 
53
+ # Active admin registry files
54
+ ACTIVEADMIN_DIR = File.join("app", "admin")
55
+
52
56
  # Helper files
53
57
  HELPER_DIR = File.join("app", "helpers")
54
58
 
55
59
  # Don't show limit (#) on these column types
56
60
  # Example: show "integer" instead of "integer(4)"
57
- NO_LIMIT_COL_TYPES = ["integer", "boolean"]
61
+ NO_LIMIT_COL_TYPES = %w(integer boolean)
62
+
63
+ # Don't show default value for these column types
64
+ NO_DEFAULT_COL_TYPES = %w(json jsonb)
58
65
 
59
66
  class << self
60
- def model_dir
61
- @model_dir.is_a?(Array) ? @model_dir : [@model_dir || "app/models"]
67
+ def annotate_pattern(options = {})
68
+ if options[:wrapper_open]
69
+ return /(?:^\n?# (?:#{options[:wrapper_open]}).*\n?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?\n(#.*\n)*\n*)|^\n?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?\n(#.*\n)*\n*/
70
+ end
71
+ /^\n?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?\n(#.*\n)*\n*/
62
72
  end
63
73
 
64
- def model_dir=(dir)
65
- @model_dir = dir
74
+ def model_dir
75
+ @model_dir.is_a?(Array) ? @model_dir : [@model_dir || 'app/models']
66
76
  end
67
77
 
78
+ attr_writer :model_dir
79
+
68
80
  def root_dir
69
- @root_dir.is_a?(Array) ? @root_dir : [@root_dir || ""]
81
+ @root_dir.is_a?(Array) ? @root_dir : [@root_dir || '']
82
+ end
83
+
84
+ attr_writer :root_dir
85
+
86
+ def test_files(root_directory)
87
+ [
88
+ File.join(root_directory, UNIT_TEST_DIR, "%MODEL_NAME%_test.rb"),
89
+ File.join(root_directory, MODEL_TEST_DIR, "%MODEL_NAME%_test.rb"),
90
+ File.join(root_directory, SPEC_MODEL_DIR, "%MODEL_NAME%_spec.rb")
91
+ ]
92
+ end
93
+
94
+ def fixture_files(root_directory)
95
+ [
96
+ File.join(root_directory, FIXTURE_TEST_DIR, "%TABLE_NAME%.yml"),
97
+ File.join(root_directory, FIXTURE_SPEC_DIR, "%TABLE_NAME%.yml"),
98
+ File.join(root_directory, FIXTURE_TEST_DIR, "%PLURALIZED_MODEL_NAME%.yml"),
99
+ File.join(root_directory, FIXTURE_SPEC_DIR, "%PLURALIZED_MODEL_NAME%.yml")
100
+ ]
101
+ end
102
+
103
+ def scaffold_files(root_directory)
104
+ [
105
+ File.join(root_directory, CONTROLLER_TEST_DIR, "%PLURALIZED_MODEL_NAME%_controller_test.rb"),
106
+ File.join(root_directory, CONTROLLER_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_controller_spec.rb"),
107
+ File.join(root_directory, REQUEST_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_spec.rb"),
108
+ File.join(root_directory, ROUTING_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_routing_spec.rb")
109
+ ]
70
110
  end
71
111
 
72
- def root_dir=(dir)
73
- @root_dir = dir
112
+ def factory_files(root_directory)
113
+ [
114
+ File.join(root_directory, EXEMPLARS_TEST_DIR, "%MODEL_NAME%_exemplar.rb"),
115
+ File.join(root_directory, EXEMPLARS_SPEC_DIR, "%MODEL_NAME%_exemplar.rb"),
116
+ File.join(root_directory, BLUEPRINTS_TEST_DIR, "%MODEL_NAME%_blueprint.rb"),
117
+ File.join(root_directory, BLUEPRINTS_SPEC_DIR, "%MODEL_NAME%_blueprint.rb"),
118
+ File.join(root_directory, FACTORY_GIRL_TEST_DIR, "%MODEL_NAME%_factory.rb"), # (old style)
119
+ File.join(root_directory, FACTORY_GIRL_SPEC_DIR, "%MODEL_NAME%_factory.rb"), # (old style)
120
+ File.join(root_directory, FACTORY_GIRL_TEST_DIR, "%TABLE_NAME%.rb"), # (new style)
121
+ File.join(root_directory, FACTORY_GIRL_SPEC_DIR, "%TABLE_NAME%.rb"), # (new style)
122
+ File.join(root_directory, FABRICATORS_TEST_DIR, "%MODEL_NAME%_fabricator.rb"),
123
+ File.join(root_directory, FABRICATORS_SPEC_DIR, "%MODEL_NAME%_fabricator.rb")
124
+ ]
74
125
  end
75
126
 
76
- def get_patterns(pattern_types=MATCHED_TYPES)
127
+ def serialize_files(root_directory)
128
+ [
129
+ File.join(root_directory, SERIALIZERS_DIR, "%MODEL_NAME%_serializer.rb"),
130
+ File.join(root_directory, SERIALIZERS_TEST_DIR, "%MODEL_NAME%_serializer_spec.rb"),
131
+ File.join(root_directory, SERIALIZERS_SPEC_DIR, "%MODEL_NAME%_serializer_spec.rb")
132
+ ]
133
+ end
134
+
135
+ def files_by_pattern(root_directory, pattern_type)
136
+ case pattern_type
137
+ when 'test' then test_files(root_directory)
138
+ when 'fixture' then fixture_files(root_directory)
139
+ when 'scaffold' then scaffold_files(root_directory)
140
+ when 'factory' then factory_files(root_directory)
141
+ when 'serializer' then serialize_files(root_directory)
142
+ when 'controller'
143
+ [File.join(root_directory, CONTROLLER_DIR, "%PLURALIZED_MODEL_NAME%_controller.rb")]
144
+ when 'admin'
145
+ [File.join(root_directory, ACTIVEADMIN_DIR, "%MODEL_NAME%.rb")]
146
+ when 'helper'
147
+ [File.join(root_directory, HELPER_DIR, "%PLURALIZED_MODEL_NAME%_helper.rb")]
148
+ else
149
+ []
150
+ end
151
+ end
152
+
153
+ def get_patterns(pattern_types=[])
77
154
  current_patterns = []
78
155
  root_dir.each do |root_directory|
79
156
  Array(pattern_types).each do |pattern_type|
80
- current_patterns += case pattern_type
81
- when 'test'
82
- [
83
- File.join(root_directory, UNIT_TEST_DIR, "%MODEL_NAME%_test.rb"),
84
- File.join(root_directory, MODEL_TEST_DIR, "%MODEL_NAME%_test.rb"),
85
- File.join(root_directory, SPEC_MODEL_DIR, "%MODEL_NAME%_spec.rb"),
86
- ]
87
- when 'fixture'
88
- [
89
- File.join(root_directory, FIXTURE_TEST_DIR, "%TABLE_NAME%.yml"),
90
- File.join(root_directory, FIXTURE_SPEC_DIR, "%TABLE_NAME%.yml"),
91
- File.join(root_directory, FIXTURE_TEST_DIR, "%PLURALIZED_MODEL_NAME%.yml"),
92
- File.join(root_directory, FIXTURE_SPEC_DIR, "%PLURALIZED_MODEL_NAME%.yml"),
93
- ]
94
- when 'scaffold'
95
- [
96
- File.join(root_directory, CONTROLLER_TEST_DIR, "%PLURALIZED_MODEL_NAME%_controller_test.rb"),
97
- File.join(root_directory, CONTROLLER_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_controller_spec.rb"),
98
- File.join(root_directory, REQUEST_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_spec.rb"),
99
- File.join(root_directory, ROUTING_SPEC_DIR, "%PLURALIZED_MODEL_NAME%_routing_spec.rb"),
100
- ]
101
- when 'factory'
102
- [
103
- File.join(root_directory, EXEMPLARS_TEST_DIR, "%MODEL_NAME%_exemplar.rb"),
104
- File.join(root_directory, EXEMPLARS_SPEC_DIR, "%MODEL_NAME%_exemplar.rb"),
105
- File.join(root_directory, BLUEPRINTS_TEST_DIR, "%MODEL_NAME%_blueprint.rb"),
106
- File.join(root_directory, BLUEPRINTS_SPEC_DIR, "%MODEL_NAME%_blueprint.rb"),
107
- File.join(root_directory, FACTORY_GIRL_TEST_DIR, "%MODEL_NAME%_factory.rb"), # (old style)
108
- File.join(root_directory, FACTORY_GIRL_SPEC_DIR, "%MODEL_NAME%_factory.rb"), # (old style)
109
- File.join(root_directory, FACTORY_GIRL_TEST_DIR, "%TABLE_NAME%.rb"), # (new style)
110
- File.join(root_directory, FACTORY_GIRL_SPEC_DIR, "%TABLE_NAME%.rb"), # (new style)
111
- File.join(root_directory, FABRICATORS_TEST_DIR, "%MODEL_NAME%_fabricator.rb"),
112
- File.join(root_directory, FABRICATORS_SPEC_DIR, "%MODEL_NAME%_fabricator.rb"),
113
- ]
114
- when 'serializer'
115
- [
116
- File.join(root_directory, SERIALIZERS_DIR, "%MODEL_NAME%_serializer.rb"),
117
- File.join(root_directory, SERIALIZERS_TEST_DIR, "%MODEL_NAME%_serializer_spec.rb"),
118
- File.join(root_directory, SERIALIZERS_SPEC_DIR, "%MODEL_NAME%_serializer_spec.rb")
119
- ]
120
- when 'controller'
121
- [
122
- File.join(root_directory, CONTROLLER_DIR, "%PLURALIZED_MODEL_NAME%_controller.rb")
123
- ]
124
- when 'helper'
125
- [
126
- File.join(root_directory, HELPER_DIR, "%PLURALIZED_MODEL_NAME%_helper.rb")
127
- ]
128
- end
157
+ current_patterns += files_by_pattern(root_directory, pattern_type)
129
158
  end
130
159
  end
131
160
  current_patterns.map{ |p| p.sub(/^[\/]*/, '') }
@@ -134,9 +163,9 @@ module AnnotateModels
134
163
  # Simple quoting for the default column value
135
164
  def quote(value)
136
165
  case value
137
- when NilClass then "NULL"
138
- when TrueClass then "TRUE"
139
- when FalseClass then "FALSE"
166
+ when NilClass then 'NULL'
167
+ when TrueClass then 'TRUE'
168
+ when FalseClass then 'FALSE'
140
169
  when Float, Fixnum, Bignum then value.to_s
141
170
  # BigDecimals need to be output in a non-normalized form and quoted.
142
171
  when BigDecimal then value.to_s('F')
@@ -157,7 +186,7 @@ module AnnotateModels
157
186
  def get_schema_info(klass, header, options = {})
158
187
  info = "# #{header}\n"
159
188
  info<< "#\n"
160
- if(options[:format_markdown])
189
+ if options[:format_markdown]
161
190
  info<< "# Table name: `#{klass.table_name}`\n"
162
191
  info<< "#\n"
163
192
  info<< "# ### Columns\n"
@@ -166,13 +195,13 @@ module AnnotateModels
166
195
  end
167
196
  info<< "#\n"
168
197
 
169
- max_size = klass.column_names.map{|name| name.size}.max || 0
198
+ max_size = klass.column_names.map(&:size).max || 0
170
199
  max_size += options[:format_rdoc] ? 5 : 1
171
200
  md_names_overhead = 6
172
201
  md_type_allowance = 18
173
202
  bare_type_allowance = 16
174
203
 
175
- if(options[:format_markdown])
204
+ if options[:format_markdown]
176
205
  info<< sprintf( "# %-#{max_size + md_names_overhead}.#{max_size + md_names_overhead}s | %-#{md_type_allowance}.#{md_type_allowance}s | %s\n", 'Name', 'Type', 'Attributes' )
177
206
  info<< "# #{ '-' * ( max_size + md_names_overhead ) } | #{'-' * md_type_allowance} | #{ '-' * 27 }\n"
178
207
  end
@@ -185,20 +214,20 @@ module AnnotateModels
185
214
  klass.columns
186
215
  end
187
216
 
188
- cols = cols.sort_by(&:name) if(options[:sort])
189
- cols = classified_sort(cols) if(options[:classified_sort])
217
+ cols = cols.sort_by(&:name) if options[:sort]
218
+ cols = classified_sort(cols) if options[:classified_sort]
190
219
  cols.each do |col|
191
220
  col_type = (col.type || col.sql_type).to_s
192
221
 
193
222
  attrs = []
194
- attrs << "default(#{schema_default(klass, col)})" unless col.default.nil? || col_type == "jsonb"
195
- attrs << "not null" unless col.null
196
- attrs << "primary key" if klass.primary_key && (klass.primary_key.is_a?(Array) ? klass.primary_key.collect{|c|c.to_sym}.include?(col.name.to_sym) : col.name.to_sym == klass.primary_key.to_sym)
223
+ attrs << "default(#{schema_default(klass, col)})" unless col.default.nil? || NO_DEFAULT_COL_TYPES.include?(col_type)
224
+ attrs << 'not null' unless col.null
225
+ attrs << 'primary key' if klass.primary_key && (klass.primary_key.is_a?(Array) ? klass.primary_key.collect(&:to_sym).include?(col.name.to_sym) : col.name.to_sym == klass.primary_key.to_sym)
197
226
 
198
- if col_type == "decimal"
227
+ if col_type == 'decimal'
199
228
  col_type << "(#{col.precision}, #{col.scale})"
200
- elsif col_type != "spatial"
201
- if (col.limit)
229
+ elsif col_type != 'spatial'
230
+ if col.limit
202
231
  if col.limit.is_a? Array
203
232
  attrs << "(#{col.limit.join(', ')})"
204
233
  else
@@ -208,15 +237,13 @@ module AnnotateModels
208
237
  end
209
238
 
210
239
  # Check out if we got an array column
211
- if col.respond_to?(:array) && col.array
212
- attrs << "is an Array"
213
- end
240
+ attrs << 'is an Array' if col.respond_to?(:array) && col.array
214
241
 
215
242
  # Check out if we got a geometric column
216
243
  # and print the type and SRID
217
244
  if col.respond_to?(:geometry_type)
218
245
  attrs << "#{col.geometry_type}, #{col.srid}"
219
- elsif col.respond_to?(:geometric_type) and col.geometric_type.present?
246
+ elsif col.respond_to?(:geometric_type) && col.geometric_type.present?
220
247
  attrs << "#{col.geometric_type.to_s.downcase}, #{col.srid}"
221
248
  end
222
249
 
@@ -225,9 +252,9 @@ module AnnotateModels
225
252
  if options[:simple_indexes] && klass.table_exists?# Check out if this column is indexed
226
253
  indices = klass.connection.indexes(klass.table_name)
227
254
  if indices = indices.select { |ind| ind.columns.include? col.name }
228
- indices.sort_by{|ind| ind.name}.each do |ind|
255
+ indices.sort_by(&:name).each do |ind|
229
256
  ind = ind.columns.reject! { |i| i == col.name }
230
- attrs << (ind.length == 0 ? "indexed" : "indexed => [#{ind.join(", ")}]")
257
+ attrs << (ind.empty? ? "indexed" : "indexed => [#{ind.join(", ")}]")
231
258
  end
232
259
  end
233
260
  end
@@ -261,28 +288,29 @@ module AnnotateModels
261
288
  end
262
289
 
263
290
  def get_index_info(klass, options={})
264
- if(options[:format_markdown])
291
+ if options[:format_markdown]
265
292
  index_info = "#\n# ### Indexes\n#\n"
266
293
  else
267
294
  index_info = "#\n# Indexes\n#\n"
268
295
  end
269
296
 
270
297
  indexes = klass.connection.indexes(klass.table_name)
271
- return "" if indexes.empty?
298
+ return '' if indexes.empty?
272
299
 
273
300
  max_size = indexes.collect{|index| index.name.size}.max + 1
274
- indexes.sort_by{|index| index.name}.each do |index|
275
- if(options[:format_markdown])
301
+ indexes.sort_by(&:name).each do |index|
302
+ if options[:format_markdown]
276
303
  index_info << sprintf("# * `%s`%s:\n# * **`%s`**\n", index.name, index.unique ? " (_unique_)" : "", index.columns.join("`**\n# * **`"))
277
304
  else
278
305
  index_info << sprintf("# %-#{max_size}.#{max_size}s %s %s", index.name, "(#{index.columns.join(",")})", index.unique ? "UNIQUE" : "").rstrip + "\n"
279
306
  end
280
307
  end
281
- return index_info
308
+
309
+ index_info
282
310
  end
283
311
 
284
312
  def hide_limit?(col_type, options)
285
- excludes =
313
+ excludes =
286
314
  if options[:hide_limit_column_types].blank?
287
315
  NO_LIMIT_COL_TYPES
288
316
  else
@@ -293,34 +321,42 @@ module AnnotateModels
293
321
  end
294
322
 
295
323
  def get_foreign_key_info(klass, options={})
296
- if(options[:format_markdown])
324
+ if options[:format_markdown]
297
325
  fk_info = "#\n# ### Foreign Keys\n#\n"
298
326
  else
299
327
  fk_info = "#\n# Foreign Keys\n#\n"
300
328
  end
301
329
 
302
- return "" unless klass.connection.supports_foreign_keys? && klass.connection.respond_to?(:foreign_keys)
330
+ return '' unless klass.connection.respond_to?(:supports_foreign_keys?) &&
331
+ klass.connection.supports_foreign_keys? && klass.connection.respond_to?(:foreign_keys)
303
332
 
304
333
  foreign_keys = klass.connection.foreign_keys(klass.table_name)
305
- return "" if foreign_keys.empty?
334
+ return '' if foreign_keys.empty?
306
335
 
307
336
  max_size = foreign_keys.collect{|fk| fk.name.size}.max + 1
308
- foreign_keys.sort_by{|fk| fk.name}.each do |fk|
337
+ foreign_keys.sort_by(&:name).each do |fk|
309
338
  ref_info = "#{fk.column} => #{fk.to_table}.#{fk.primary_key}"
310
- if(options[:format_markdown])
311
- fk_info << sprintf("# * `%s`:\n# * **`%s`**\n", fk.name, ref_info)
339
+ constraints_info = ''
340
+ constraints_info += "ON DELETE => #{fk.on_delete} " if fk.on_delete
341
+ constraints_info += "ON UPDATE => #{fk.on_update} " if fk.on_update
342
+ constraints_info.strip!
343
+ if options[:format_markdown]
344
+ fk_info << sprintf("# * `%s`%s:\n# * **`%s`**\n", fk.name, constraints_info.blank? ? '' : " (_#{constraints_info}_)", ref_info)
312
345
  else
313
- fk_info << sprintf("# %-#{max_size}.#{max_size}s %s", fk.name, "(#{ref_info})").rstrip + "\n"
346
+ fk_info << sprintf("# %-#{max_size}.#{max_size}s %s %s", fk.name, "(#{ref_info})", constraints_info).rstrip + "\n"
314
347
  end
315
348
  end
316
- return fk_info
349
+
350
+ fk_info
317
351
  end
318
352
 
319
353
  # Add a schema block to a file. If the file already contains
320
354
  # a schema info block (a comment starting with "== Schema Information"), check if it
321
355
  # matches the block that is already there. If so, leave it be. If not, remove the old
322
356
  # info block and write a new one.
323
- # Returns true or false depending on whether the file was modified.
357
+ #
358
+ # == Returns:
359
+ # true or false depending on whether the file was modified.
324
360
  #
325
361
  # === Options (opts)
326
362
  # :force<Symbol>:: whether to update the file even if it doesn't seem to need it.
@@ -330,7 +366,7 @@ module AnnotateModels
330
366
  def annotate_one_file(file_name, info_block, position, options={})
331
367
  if File.exist?(file_name)
332
368
  old_content = File.read(file_name)
333
- return false if(old_content =~ /# -\*- SkipSchemaAnnotations.*\n/)
369
+ return false if old_content =~ /# -\*- SkipSchemaAnnotations.*\n/
334
370
 
335
371
  # Ignore the Schema version line because it changes with each migration
336
372
  header_pattern = /(^# Table name:.*?\n(#.*[\r]?\n)*[\r]?)/
@@ -348,10 +384,10 @@ module AnnotateModels
348
384
  return false
349
385
  else
350
386
  # Replace inline the old schema info with the new schema info
351
- new_content = old_content.sub(PATTERN, info_block + "\n")
387
+ new_content = old_content.sub(annotate_pattern(options), info_block + "\n")
352
388
 
353
389
  if new_content.end_with?(info_block + "\n")
354
- new_content = old_content.sub(PATTERN, "\n" + info_block)
390
+ new_content = old_content.sub(annotate_pattern(options), "\n" + info_block)
355
391
  end
356
392
 
357
393
  wrapper_open = options[:wrapper_open] ? "# #{options[:wrapper_open]}\n" : ""
@@ -361,41 +397,48 @@ module AnnotateModels
361
397
  # we simply need to insert it in correct position
362
398
  if new_content == old_content || options[:force]
363
399
  old_content.sub!(magic_comment_matcher, '')
364
- old_content.sub!(PATTERN, '')
400
+ old_content.sub!(annotate_pattern(options), '')
365
401
 
366
- new_content = %w(after bottom).include?(options[position].to_s) ?
367
- (magic_comments.join + (old_content.rstrip + "\n\n" + wrapped_info_block)) :
368
- (magic_comments.join + wrapped_info_block + "\n" + old_content)
402
+ if %w(after bottom).include?(options[position].to_s)
403
+ new_content = magic_comments.join + (old_content.rstrip + "\n\n" + wrapped_info_block)
404
+ else
405
+ new_content = magic_comments.join + wrapped_info_block + "\n" + old_content
406
+ end
369
407
  end
370
408
 
371
- File.open(file_name, "wb") { |f| f.puts new_content }
409
+ File.open(file_name, 'wb') { |f| f.puts new_content }
372
410
  return true
373
411
  end
374
412
  else
375
- return false
413
+ false
376
414
  end
377
415
  end
378
416
 
379
- def remove_annotation_of_file(file_name)
417
+ def remove_annotation_of_file(file_name, options={})
380
418
  if File.exist?(file_name)
381
419
  content = File.read(file_name)
420
+ wrapper_open = options[:wrapper_open] ? "# #{options[:wrapper_open]}\n" : ""
421
+ content.sub!(/(#{wrapper_open})?#{annotate_pattern(options)}/, '')
382
422
 
383
- content.sub!(PATTERN, '')
423
+ File.open(file_name, 'wb') { |f| f.puts content }
384
424
 
385
- File.open(file_name, "wb") { |f| f.puts content }
386
-
387
- return true
425
+ true
388
426
  else
389
- return false
427
+ false
390
428
  end
391
429
  end
392
430
 
431
+ def matched_types(options)
432
+ types = MATCHED_TYPES
433
+ types << 'admin' if options[:active_admin] =~ TRUE_RE && !types.include?('admin')
434
+
435
+ types
436
+ end
437
+
393
438
  # Given the name of an ActiveRecord class, create a schema
394
439
  # info block (basically a comment containing information
395
440
  # on the columns and their types) and put it at the front
396
441
  # of the model and fixture source files.
397
- # Returns true or false depending on whether the source
398
- # files were modified.
399
442
  #
400
443
  # === Options (opts)
401
444
  # :position_in_class<Symbol>:: where to place the annotated section in model file
@@ -411,40 +454,53 @@ module AnnotateModels
411
454
  # :exclude_controllers<Symbol>:: whether to skip modification of controller files
412
455
  # :exclude_helpers<Symbol>:: whether to skip modification of helper files
413
456
  #
457
+ # == Returns:
458
+ # an array of file names that were annotated.
459
+ #
414
460
  def annotate(klass, file, header, options={})
415
461
  begin
462
+ klass.reset_column_information
416
463
  info = get_schema_info(klass, header, options)
417
- did_annotate = false
418
464
  model_name = klass.name.underscore
419
465
  table_name = klass.table_name
420
466
  model_file_name = File.join(file)
467
+ annotated = []
421
468
 
422
469
  if annotate_one_file(model_file_name, info, :position_in_class, options_with_position(options, :position_in_class))
423
- did_annotate = true
470
+ annotated << model_file_name
424
471
  end
425
472
 
426
- MATCHED_TYPES.each do |key|
473
+ matched_types(options).each do |key|
427
474
  exclusion_key = "exclude_#{key.pluralize}".to_sym
428
475
  position_key = "position_in_#{key}".to_sym
429
476
 
477
+ # Same options for active_admin models
478
+ if key == 'admin'
479
+ exclusion_key = 'exclude_class'.to_sym
480
+ position_key = 'position_in_class'.to_sym
481
+ end
482
+
430
483
  unless options[exclusion_key]
431
- did_annotate = self.get_patterns(key).
484
+ self.get_patterns(key).
432
485
  map { |f| resolve_filename(f, model_name, table_name) }.
433
- map { |f| annotate_one_file(f, info, position_key, options_with_position(options, position_key)) }.
434
- detect { |result| result } || did_annotate
486
+ each { |f|
487
+ if annotate_one_file(f, info, position_key, options_with_position(options, position_key))
488
+ annotated << f
489
+ end
490
+ }
435
491
  end
436
492
  end
437
-
438
- return did_annotate
439
493
  rescue Exception => e
440
494
  puts "Unable to annotate #{file}: #{e.message}"
441
495
  puts "\t" + e.backtrace.join("\n\t") if options[:trace]
442
496
  end
497
+
498
+ annotated
443
499
  end
444
500
 
445
501
  # position = :position_in_fixture or :position_in_class
446
502
  def options_with_position(options, position_in)
447
- options.merge(:position=>(options[position_in] || options[:position]))
503
+ options.merge(position: (options[position_in] || options[:position]))
448
504
  end
449
505
 
450
506
  # Return a list of the model files to annotate.
@@ -453,7 +509,7 @@ module AnnotateModels
453
509
  # in the model_dir directory.
454
510
  def get_model_files(options)
455
511
  models = []
456
- if(!options[:is_rake])
512
+ if !options[:is_rake]
457
513
  models = ARGV.dup.reject{|m| m.match(/^(.*)=/)}
458
514
  end
459
515
 
@@ -488,13 +544,13 @@ module AnnotateModels
488
544
  model_path = file.gsub(/\.rb$/, '')
489
545
  model_dir.each { |dir| model_path = model_path.gsub(/^#{dir}/, '').gsub(/^\//, '') }
490
546
  begin
491
- get_loaded_model(model_path) or raise BadModelFileError.new
547
+ get_loaded_model(model_path) || raise(BadModelFileError.new)
492
548
  rescue LoadError
493
549
  # this is for non-rails projects, which don't get Rails auto-require magic
494
550
  file_path = File.expand_path(file)
495
551
  if File.file?(file_path) && silence_warnings { Kernel.require(file_path) }
496
552
  retry
497
- elsif model_path.match(/\//)
553
+ elsif model_path =~ /\//
498
554
  model_path = model_path.split('/')[1..-1].join('/').to_s
499
555
  retry
500
556
  else
@@ -511,37 +567,39 @@ module AnnotateModels
511
567
  # Revert to the old way but it is not really robust
512
568
  ObjectSpace.each_object(::Class).
513
569
  select do |c|
514
- Class === c and # note: we use === to avoid a bug in activesupport 2.3.14 OptionMerger vs. is_a?
515
- c.ancestors.respond_to?(:include?) and # to fix FactoryGirl bug, see https://github.com/ctran/annotate_models/pull/82
570
+ Class === c && # note: we use === to avoid a bug in activesupport 2.3.14 OptionMerger vs. is_a?
571
+ c.ancestors.respond_to?(:include?) && # to fix FactoryGirl bug, see https://github.com/ctran/annotate_models/pull/82
516
572
  c.ancestors.include?(ActiveRecord::Base)
517
573
  end.
518
574
  detect { |c| ActiveSupport::Inflector.underscore(c.to_s) == model_path }
519
575
  end
520
576
  end
521
577
 
578
+ def parse_options(options={})
579
+ self.model_dir = options[:model_dir] if options[:model_dir]
580
+ self.root_dir = options[:root_dir] if options[:root_dir]
581
+ end
582
+
522
583
  # We're passed a name of things that might be
523
584
  # ActiveRecord models. If we can find the class, and
524
585
  # if its a subclass of ActiveRecord::Base,
525
586
  # then pass it to the associated block
526
587
  def do_annotations(options={})
527
- header = options[:format_markdown] ? PREFIX_MD.dup : PREFIX.dup
588
+ parse_options(options)
528
589
 
529
- if options[:include_version]
530
- version = ActiveRecord::Migrator.current_version rescue 0
531
- if version > 0
532
- header << "\n# Schema version: #{version}"
533
- end
590
+ header = options[:format_markdown] ? PREFIX_MD.dup : PREFIX.dup
591
+ version = ActiveRecord::Migrator.current_version rescue 0
592
+ if options[:include_version] && version > 0
593
+ header << "\n# Schema version: #{version}"
534
594
  end
535
595
 
536
- self.model_dir = options[:model_dir] if options[:model_dir]
537
- self.root_dir = options[:root_dir] if options[:root_dir]
538
-
539
596
  annotated = []
540
- get_model_files(options).each do |file|
541
- annotate_model_file(annotated, File.join(file), header, options)
597
+ get_model_files(options).each do |path, filename|
598
+ annotate_model_file(annotated, File.join(path, filename), header, options)
542
599
  end
600
+
543
601
  if annotated.empty?
544
- puts "Model files unchanged."
602
+ puts 'Model files unchanged.'
545
603
  else
546
604
  puts "Annotated (#{annotated.length}): #{annotated.join(', ')}"
547
605
  end
@@ -549,12 +607,10 @@ module AnnotateModels
549
607
 
550
608
  def annotate_model_file(annotated, file, header, options)
551
609
  begin
552
- return false if (/# -\*- SkipSchemaAnnotations.*/ =~ (File.exist?(file) ? File.read(file) : '') )
610
+ return false if /# -\*- SkipSchemaAnnotations.*/ =~ (File.exist?(file) ? File.read(file) : '')
553
611
  klass = get_model_class(file)
554
612
  if klass && klass < ActiveRecord::Base && !klass.abstract_class? && klass.table_exists?
555
- if annotate(klass, file, header, options)
556
- annotated << file
557
- end
613
+ annotated.concat(annotate(klass, file, header, options))
558
614
  end
559
615
  rescue BadModelFileError => e
560
616
  unless options[:ignore_unknown_models]
@@ -568,8 +624,8 @@ module AnnotateModels
568
624
  end
569
625
 
570
626
  def remove_annotations(options={})
571
- self.model_dir = options[:model_dir] if options[:model_dir]
572
- self.root_dir = options[:root_dir] if options[:root_dir]
627
+ parse_options(options)
628
+
573
629
  deannotated = []
574
630
  deannotated_klass = false
575
631
  get_model_files(options).each do |file|
@@ -580,18 +636,18 @@ module AnnotateModels
580
636
  model_name = klass.name.underscore
581
637
  table_name = klass.table_name
582
638
  model_file_name = file
583
- deannotated_klass = true if(remove_annotation_of_file(model_file_name))
639
+ deannotated_klass = true if remove_annotation_of_file(model_file_name, options)
584
640
 
585
- get_patterns.
641
+ get_patterns(matched_types(options)).
586
642
  map { |f| resolve_filename(f, model_name, table_name) }.
587
643
  each do |f|
588
644
  if File.exist?(f)
589
- remove_annotation_of_file(f)
645
+ remove_annotation_of_file(f, options)
590
646
  deannotated_klass = true
591
647
  end
592
648
  end
593
649
  end
594
- deannotated << klass if(deannotated_klass)
650
+ deannotated << klass if deannotated_klass
595
651
  rescue Exception => e
596
652
  puts "Unable to deannotate #{File.join(file)}: #{e.message}"
597
653
  puts "\t" + e.backtrace.join("\n\t") if options[:trace]
@@ -601,10 +657,10 @@ module AnnotateModels
601
657
  end
602
658
 
603
659
  def resolve_filename(filename_template, model_name, table_name)
604
- return filename_template.
605
- gsub('%MODEL_NAME%', model_name).
606
- gsub('%PLURALIZED_MODEL_NAME%', model_name.pluralize).
607
- gsub('%TABLE_NAME%', table_name || model_name.pluralize)
660
+ filename_template.
661
+ gsub('%MODEL_NAME%', model_name).
662
+ gsub('%PLURALIZED_MODEL_NAME%', model_name.pluralize).
663
+ gsub('%TABLE_NAME%', table_name || model_name.pluralize)
608
664
  end
609
665
 
610
666
  def classified_sort(cols)
@@ -613,12 +669,12 @@ module AnnotateModels
613
669
  associations = []
614
670
  id = nil
615
671
 
616
- cols = cols.each do |c|
617
- if c.name.eql?("id")
672
+ cols.each do |c|
673
+ if c.name.eql?('id')
618
674
  id = c
619
- elsif (c.name.eql?("created_at") || c.name.eql?("updated_at"))
675
+ elsif c.name.eql?('created_at') || c.name.eql?('updated_at')
620
676
  timestamps << c
621
- elsif c.name[-3,3].eql?("_id")
677
+ elsif c.name[-3,3].eql?('_id')
622
678
  associations << c
623
679
  else
624
680
  rest_cols << c
@@ -626,7 +682,7 @@ module AnnotateModels
626
682
  end
627
683
  [rest_cols, timestamps, associations].each {|a| a.sort_by!(&:name) }
628
684
 
629
- return ([id] << rest_cols << timestamps << associations).flatten
685
+ return ([id] << rest_cols << timestamps << associations).flatten.compact
630
686
  end
631
687
 
632
688
  # Ignore warnings for the duration of the block ()