annotate 2.7.0 → 2.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 ()