annotate 2.5.0 → 2.6.0.beta2
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 +7 -0
- data/{History.txt → CHANGELOG.rdoc} +62 -6
- data/README.rdoc +129 -39
- data/TODO.rdoc +12 -0
- data/annotate.gemspec +38 -0
- data/bin/annotate +66 -26
- data/lib/annotate.rb +143 -10
- data/lib/annotate/active_record_patch.rb +1 -1
- data/lib/annotate/annotate_models.rb +152 -98
- data/lib/annotate/annotate_routes.rb +126 -20
- data/lib/annotate/version.rb +1 -1
- data/lib/generators/{annotate_models → annotate}/USAGE +1 -1
- data/lib/generators/{annotate_models → annotate}/install_generator.rb +2 -2
- data/lib/generators/annotate/templates/auto_annotate_models.rake +34 -0
- data/lib/tasks/annotate_models.rake +21 -16
- data/lib/tasks/annotate_routes.rake +17 -2
- metadata +42 -29
- data/lib/generators/annotate_models/templates/auto_annotate_models.rake +0 -20
data/lib/annotate.rb
CHANGED
@@ -1,17 +1,150 @@
|
|
1
|
-
|
2
|
-
require
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
require 'annotate/version'
|
3
|
+
require 'annotate/annotate_models'
|
4
|
+
require 'annotate/annotate_routes'
|
5
|
+
|
6
|
+
begin
|
7
|
+
# ActiveSupport 3.x...
|
8
|
+
require 'active_support/hash_with_indifferent_access'
|
9
|
+
rescue Exception => e
|
10
|
+
# ActiveSupport 2.x...
|
11
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
12
|
+
end
|
3
13
|
|
4
14
|
module Annotate
|
15
|
+
##
|
16
|
+
# The set of available options to customize the behavior of Annotate.
|
17
|
+
#
|
18
|
+
POSITION_OPTIONS=[
|
19
|
+
:position_in_routes, :position_in_class, :position_in_test,
|
20
|
+
:position_in_fixture, :position_in_factory, :position,
|
21
|
+
]
|
22
|
+
FLAG_OPTIONS=[
|
23
|
+
:show_indexes, :simple_indexes, :include_version, :exclude_tests,
|
24
|
+
:exclude_fixtures, :exclude_factories, :ignore_model_sub_dir,
|
25
|
+
:format_bare, :format_rdoc, :format_markdown, :sort, :force, :trace,
|
26
|
+
]
|
27
|
+
OTHER_OPTIONS=[
|
28
|
+
:model_dir,
|
29
|
+
]
|
30
|
+
PATH_OPTIONS=[
|
31
|
+
:require,
|
32
|
+
]
|
33
|
+
|
34
|
+
|
35
|
+
##
|
36
|
+
# Set default values that can be overridden via environment variables.
|
37
|
+
#
|
38
|
+
def self.set_defaults(options = {})
|
39
|
+
return if(@has_set_defaults)
|
40
|
+
@has_set_defaults = true
|
41
|
+
options = HashWithIndifferentAccess.new(options)
|
42
|
+
[POSITION_OPTIONS, FLAG_OPTIONS, PATH_OPTIONS].flatten.each do |key|
|
43
|
+
if(options.has_key?(key))
|
44
|
+
default_value = if(options[key].is_a?(Array))
|
45
|
+
options[key].join(",")
|
46
|
+
else
|
47
|
+
options[key]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
default_value = ENV[key.to_s] if(!ENV[key.to_s].blank?)
|
51
|
+
ENV[key.to_s] = default_value.to_s
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
TRUE_RE = /^(true|t|yes|y|1)$/i
|
56
|
+
def self.setup_options(options = {})
|
57
|
+
POSITION_OPTIONS.each do |key|
|
58
|
+
options[key] = fallback(ENV[key.to_s], ENV['position'], 'before')
|
59
|
+
end
|
60
|
+
FLAG_OPTIONS.each do |key|
|
61
|
+
options[key] = true?(ENV[key.to_s])
|
62
|
+
end
|
63
|
+
OTHER_OPTIONS.each do |key|
|
64
|
+
options[key] = (!ENV[key.to_s].blank?) ? ENV[key.to_s] : nil
|
65
|
+
end
|
66
|
+
PATH_OPTIONS.each do |key|
|
67
|
+
options[key] = (!ENV[key.to_s].blank?) ? ENV[key.to_s].split(',') : []
|
68
|
+
end
|
69
|
+
|
70
|
+
if(!options[:model_dir])
|
71
|
+
options[:model_dir] = 'app/models'
|
72
|
+
end
|
73
|
+
|
74
|
+
return options
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.skip_on_migration?
|
78
|
+
ENV['skip_on_db_migrate'] =~ TRUE_RE
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.loaded_tasks=(val); @loaded_tasks = val; end
|
82
|
+
def self.loaded_tasks; return @loaded_tasks; end
|
83
|
+
|
5
84
|
def self.load_tasks
|
6
|
-
if
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
85
|
+
return if(self.loaded_tasks)
|
86
|
+
self.loaded_tasks = true
|
87
|
+
|
88
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')].each { |rake| load rake }
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.load_requires(options)
|
92
|
+
options[:require].each { |path| require path } if options[:require].count > 0
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.eager_load(options)
|
96
|
+
self.load_requires(options)
|
97
|
+
require "annotate/active_record_patch"
|
98
|
+
|
99
|
+
if(defined?(Rails))
|
100
|
+
if(Rails.version.split('.').first.to_i < 3)
|
101
|
+
Rails.configuration.eager_load_paths.each do |load_path|
|
102
|
+
matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/
|
103
|
+
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
|
104
|
+
require_dependency file.sub(matcher, '\1')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
else
|
108
|
+
klass = Rails::Application.send(:subclasses).first
|
109
|
+
klass.eager_load!
|
110
|
+
end
|
13
111
|
else
|
14
|
-
|
112
|
+
FileList["#{options[:model_dir]}/**/*.rb"].each do |fname|
|
113
|
+
require File.expand_path(fname)
|
114
|
+
end
|
15
115
|
end
|
16
116
|
end
|
117
|
+
|
118
|
+
def self.bootstrap_rake
|
119
|
+
begin
|
120
|
+
require 'rake/dsl_definition'
|
121
|
+
rescue Exception => e
|
122
|
+
# We might just be on an old version of Rake...
|
123
|
+
end
|
124
|
+
require 'rake'
|
125
|
+
|
126
|
+
if File.exists?('./Rakefile')
|
127
|
+
load './Rakefile'
|
128
|
+
end
|
129
|
+
Rake::Task[:environment].invoke rescue nil
|
130
|
+
if(!defined?(Rails))
|
131
|
+
# Not in a Rails project, so time to load up the parts of
|
132
|
+
# ActiveSupport we need.
|
133
|
+
require 'active_support'
|
134
|
+
require 'active_support/core_ext/class/subclasses'
|
135
|
+
require 'active_support/core_ext/string/inflections'
|
136
|
+
end
|
137
|
+
self.load_tasks
|
138
|
+
Rake::Task[:set_annotation_options].invoke
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.fallback(*args)
|
142
|
+
return args.detect { |arg| !arg.blank? }
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.true?(val)
|
146
|
+
return false if(val.blank?)
|
147
|
+
return false unless(val =~ TRUE_RE)
|
148
|
+
return true
|
149
|
+
end
|
17
150
|
end
|
@@ -9,11 +9,10 @@ module AnnotateModels
|
|
9
9
|
|
10
10
|
# File.join for windows reverse bar compat?
|
11
11
|
# I dont use windows, can`t test
|
12
|
-
UNIT_TEST_DIR = File.join("test", "unit"
|
12
|
+
UNIT_TEST_DIR = File.join("test", "unit")
|
13
13
|
SPEC_MODEL_DIR = File.join("spec", "models")
|
14
14
|
FIXTURE_TEST_DIR = File.join("test", "fixtures")
|
15
15
|
FIXTURE_SPEC_DIR = File.join("spec", "fixtures")
|
16
|
-
FIXTURE_DIRS = ["test/fixtures","spec/fixtures"]
|
17
16
|
|
18
17
|
# Object Daddy http://github.com/flogic/object_daddy/tree/master
|
19
18
|
EXEMPLARS_TEST_DIR = File.join("test", "exemplars")
|
@@ -31,6 +30,29 @@ module AnnotateModels
|
|
31
30
|
FABRICATORS_TEST_DIR = File.join("test", "fabricators")
|
32
31
|
FABRICATORS_SPEC_DIR = File.join("spec", "fabricators")
|
33
32
|
|
33
|
+
TEST_PATTERNS = [
|
34
|
+
[UNIT_TEST_DIR, "%MODEL_NAME%_test.rb"],
|
35
|
+
[SPEC_MODEL_DIR, "%MODEL_NAME%_spec.rb"],
|
36
|
+
]
|
37
|
+
|
38
|
+
FIXTURE_PATTERNS = [
|
39
|
+
File.join(FIXTURE_TEST_DIR, "%TABLE_NAME%.yml"),
|
40
|
+
File.join(FIXTURE_SPEC_DIR, "%TABLE_NAME%.yml"),
|
41
|
+
]
|
42
|
+
|
43
|
+
FACTORY_PATTERNS = [
|
44
|
+
File.join(EXEMPLARS_TEST_DIR, "%MODEL_NAME%_exemplar.rb"),
|
45
|
+
File.join(EXEMPLARS_SPEC_DIR, "%MODEL_NAME%_exemplar.rb"),
|
46
|
+
File.join(BLUEPRINTS_TEST_DIR, "%MODEL_NAME%_blueprint.rb"),
|
47
|
+
File.join(BLUEPRINTS_SPEC_DIR, "%MODEL_NAME%_blueprint.rb"),
|
48
|
+
File.join(FACTORY_GIRL_TEST_DIR, "%MODEL_NAME%_factory.rb"), # (old style)
|
49
|
+
File.join(FACTORY_GIRL_SPEC_DIR, "%MODEL_NAME%_factory.rb"), # (old style)
|
50
|
+
File.join(FACTORY_GIRL_TEST_DIR, "%TABLE_NAME%.rb"), # (new style)
|
51
|
+
File.join(FACTORY_GIRL_SPEC_DIR, "%TABLE_NAME%.rb"), # (new style)
|
52
|
+
File.join(FABRICATORS_TEST_DIR, "%MODEL_NAME%_fabricator.rb"),
|
53
|
+
File.join(FABRICATORS_SPEC_DIR, "%MODEL_NAME%_fabricator.rb"),
|
54
|
+
]
|
55
|
+
|
34
56
|
# Don't show limit (#) on these column types
|
35
57
|
# Example: show "integer" instead of "integer(4)"
|
36
58
|
NO_LIMIT_COL_TYPES = ["integer", "boolean"]
|
@@ -39,7 +61,7 @@ module AnnotateModels
|
|
39
61
|
def model_dir
|
40
62
|
@model_dir || "app/models"
|
41
63
|
end
|
42
|
-
|
64
|
+
|
43
65
|
def model_dir=(dir)
|
44
66
|
@model_dir = dir
|
45
67
|
end
|
@@ -65,15 +87,24 @@ module AnnotateModels
|
|
65
87
|
def get_schema_info(klass, header, options = {})
|
66
88
|
info = "# #{header}\n"
|
67
89
|
info<< "#\n"
|
68
|
-
|
90
|
+
if(options[:format_markdown])
|
91
|
+
info<< "# Table name: `#{klass.table_name}`\n"
|
92
|
+
info<< "#\n"
|
93
|
+
info<< "# ### Columns\n"
|
94
|
+
else
|
95
|
+
info<< "# Table name: #{klass.table_name}\n"
|
96
|
+
end
|
69
97
|
info<< "#\n"
|
70
98
|
|
71
99
|
max_size = klass.column_names.map{|name| name.size}.max || 0
|
72
100
|
max_size += options[:format_rdoc] ? 5 : 1
|
101
|
+
md_names_overhead = 6
|
102
|
+
md_type_allowance = 18
|
103
|
+
bare_type_allowance = 16
|
73
104
|
|
74
105
|
if(options[:format_markdown])
|
75
|
-
info<< sprintf( "# %-#{max_size +
|
76
|
-
info<< "# #{ '-' * ( max_size +
|
106
|
+
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' )
|
107
|
+
info<< "# #{ '-' * ( max_size + md_names_overhead ) } | #{'-' * md_type_allowance} | #{ '-' * 27 }\n"
|
77
108
|
end
|
78
109
|
|
79
110
|
cols = klass.columns
|
@@ -82,7 +113,7 @@ module AnnotateModels
|
|
82
113
|
attrs = []
|
83
114
|
attrs << "default(#{quote(col.default)})" unless col.default.nil?
|
84
115
|
attrs << "not null" unless col.null
|
85
|
-
attrs << "primary key" if klass.primary_key && col.name.to_sym == klass.primary_key.to_sym
|
116
|
+
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)
|
86
117
|
|
87
118
|
col_type = (col.type || col.sql_type).to_s
|
88
119
|
if col_type == "decimal"
|
@@ -92,7 +123,7 @@ module AnnotateModels
|
|
92
123
|
col_type << "(#{col.limit})" unless NO_LIMIT_COL_TYPES.include?(col_type)
|
93
124
|
end
|
94
125
|
end
|
95
|
-
|
126
|
+
|
96
127
|
# Check out if we got a geometric column
|
97
128
|
# and print the type and SRID
|
98
129
|
if col.respond_to?(:geometry_type)
|
@@ -114,14 +145,16 @@ module AnnotateModels
|
|
114
145
|
if options[:format_rdoc]
|
115
146
|
info << sprintf("# %-#{max_size}.#{max_size}s<tt>%s</tt>", "*#{col.name}*::", attrs.unshift(col_type).join(", ")).rstrip + "\n"
|
116
147
|
elsif options[:format_markdown]
|
117
|
-
|
148
|
+
name_remainder = max_size - col.name.length
|
149
|
+
type_remainder = (md_type_allowance - 2) - col_type.length
|
150
|
+
info << (sprintf("# **`%s`**%#{name_remainder}s | `%s`%#{type_remainder}s | `%s`", col.name, " ", col_type, " ", attrs.join(", ").rstrip)).gsub('``', ' ').rstrip + "\n"
|
118
151
|
else
|
119
|
-
info << sprintf("# %-#{max_size}.#{max_size}s
|
152
|
+
info << sprintf("# %-#{max_size}.#{max_size}s:%-#{bare_type_allowance}.#{bare_type_allowance}s %s", col.name, col_type, attrs.join(", ")).rstrip + "\n"
|
120
153
|
end
|
121
154
|
end
|
122
155
|
|
123
156
|
if options[:show_indexes] && klass.table_exists?
|
124
|
-
info << get_index_info(klass)
|
157
|
+
info << get_index_info(klass, options)
|
125
158
|
end
|
126
159
|
|
127
160
|
if options[:format_rdoc]
|
@@ -133,15 +166,23 @@ module AnnotateModels
|
|
133
166
|
end
|
134
167
|
end
|
135
168
|
|
136
|
-
def get_index_info(klass)
|
137
|
-
|
169
|
+
def get_index_info(klass, options={})
|
170
|
+
if(options[:format_markdown])
|
171
|
+
index_info = "#\n# ### Indexes\n#\n"
|
172
|
+
else
|
173
|
+
index_info = "#\n# Indexes\n#\n"
|
174
|
+
end
|
138
175
|
|
139
176
|
indexes = klass.connection.indexes(klass.table_name)
|
140
177
|
return "" if indexes.empty?
|
141
178
|
|
142
179
|
max_size = indexes.collect{|index| index.name.size}.max + 1
|
143
180
|
indexes.sort_by{|index| index.name}.each do |index|
|
144
|
-
|
181
|
+
if(options[:format_markdown])
|
182
|
+
index_info << sprintf("# * `%s`%s:\n# * **`%s`**\n", index.name, index.unique ? " (_unique_)" : "", index.columns.join("`**\n# * **`"))
|
183
|
+
else
|
184
|
+
index_info << sprintf("# %-#{max_size}.#{max_size}s %s %s", index.name, "(#{index.columns.join(",")})", index.unique ? "UNIQUE" : "").rstrip + "\n"
|
185
|
+
end
|
145
186
|
end
|
146
187
|
return index_info
|
147
188
|
end
|
@@ -153,14 +194,11 @@ module AnnotateModels
|
|
153
194
|
# Returns true or false depending on whether the file was modified.
|
154
195
|
#
|
155
196
|
# === Options (opts)
|
156
|
-
# :
|
157
|
-
#
|
158
|
-
#
|
159
|
-
# :position_in_fixture<Symbol>:: where to place the annotated section in fixture file
|
160
|
-
# :position_in_others<Symbol>:: where to place the annotated section in the rest of
|
161
|
-
# supported files
|
197
|
+
# :force<Symbol>:: whether to update the file even if it doesn't seem to need it.
|
198
|
+
# :position_in_*<Symbol>:: where to place the annotated section in fixture or model file,
|
199
|
+
# :before or :after. Default is :before.
|
162
200
|
#
|
163
|
-
def annotate_one_file(file_name, info_block, options={})
|
201
|
+
def annotate_one_file(file_name, info_block, position, options={})
|
164
202
|
if File.exist?(file_name)
|
165
203
|
old_content = File.read(file_name)
|
166
204
|
return false if(old_content =~ /# -\*- SkipSchemaAnnotations.*\n/)
|
@@ -178,9 +216,9 @@ module AnnotateModels
|
|
178
216
|
encoding_header = old_content.match(encoding).to_s
|
179
217
|
|
180
218
|
if old_columns == new_columns && !options[:force]
|
181
|
-
false
|
219
|
+
return false
|
182
220
|
else
|
183
|
-
|
221
|
+
|
184
222
|
# todo: figure out if we need to extract any logic from this merge chunk
|
185
223
|
# <<<<<<< HEAD
|
186
224
|
# # Replace the old schema info with the new schema info
|
@@ -197,22 +235,30 @@ module AnnotateModels
|
|
197
235
|
# Strip the old schema info, and insert new schema info.
|
198
236
|
old_content.sub!(encoding, '')
|
199
237
|
old_content.sub!(PATTERN, '')
|
200
|
-
|
201
|
-
new_content =
|
238
|
+
|
239
|
+
new_content = options[position].to_s == 'after' ?
|
202
240
|
(encoding_header + (old_content.rstrip + "\n\n" + info_block)) :
|
203
241
|
(encoding_header + info_block + old_content)
|
204
242
|
|
205
243
|
File.open(file_name, "wb") { |f| f.puts new_content }
|
206
|
-
true
|
244
|
+
return true
|
207
245
|
end
|
246
|
+
else
|
247
|
+
return false
|
208
248
|
end
|
209
249
|
end
|
210
|
-
|
250
|
+
|
211
251
|
def remove_annotation_of_file(file_name)
|
212
252
|
if File.exist?(file_name)
|
213
253
|
content = File.read(file_name)
|
254
|
+
|
214
255
|
content.sub!(PATTERN, '')
|
256
|
+
|
215
257
|
File.open(file_name, "wb") { |f| f.puts content }
|
258
|
+
|
259
|
+
return true
|
260
|
+
else
|
261
|
+
return false
|
216
262
|
end
|
217
263
|
end
|
218
264
|
|
@@ -222,48 +268,55 @@ module AnnotateModels
|
|
222
268
|
# of the model and fixture source files.
|
223
269
|
# Returns true or false depending on whether the source
|
224
270
|
# files were modified.
|
271
|
+
#
|
272
|
+
# === Options (opts)
|
273
|
+
# :position_in_class<Symbol>:: where to place the annotated section in model file
|
274
|
+
# :position_in_test<Symbol>:: where to place the annotated section in test/spec file(s)
|
275
|
+
# :position_in_fixture<Symbol>:: where to place the annotated section in fixture file
|
276
|
+
# :position_in_factory<Symbol>:: where to place the annotated section in factory file
|
277
|
+
# :exclude_tests<Symbol>:: whether to skip modification of test/spec files
|
278
|
+
# :exclude_fixtures<Symbol>:: whether to skip modification of fixture files
|
279
|
+
# :exclude_factories<Symbol>:: whether to skip modification of factory files
|
280
|
+
#
|
225
281
|
def annotate(klass, file, header, options={})
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
282
|
+
begin
|
283
|
+
info = get_schema_info(klass, header, options)
|
284
|
+
did_annotate = false
|
285
|
+
model_name = klass.name.underscore
|
286
|
+
table_name = klass.table_name
|
287
|
+
model_file_name = File.join(model_dir, file)
|
288
|
+
|
289
|
+
if annotate_one_file(model_file_name, info, :position_in_class, options_with_position(options, :position_in_class))
|
290
|
+
did_annotate = true
|
291
|
+
end
|
230
292
|
|
231
|
-
|
232
|
-
|
233
|
-
|
293
|
+
unless options[:exclude_tests]
|
294
|
+
did_annotate = TEST_PATTERNS.
|
295
|
+
map { |pat| [pat[0], resolve_filename(pat[1], model_name, table_name)] }.
|
296
|
+
map { |pat| find_test_file(*pat) }.
|
297
|
+
map { |file| annotate_one_file(file, info, :position_in_test, options_with_position(options, :position_in_test)) }.
|
298
|
+
detect { |result| result } || did_annotate
|
299
|
+
end
|
234
300
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
# todo: add an option "position_in_test" -- or maybe just ask if anyone ever wants different positions for model vs. test vs. fixture
|
241
|
-
if annotate_one_file(file, info, options_with_position(options, :position_in_fixture))
|
242
|
-
annotated = true
|
243
|
-
end
|
301
|
+
unless options[:exclude_fixtures]
|
302
|
+
did_annotate = FIXTURE_PATTERNS.
|
303
|
+
map { |file| resolve_filename(file, model_name, table_name) }.
|
304
|
+
map { |file| annotate_one_file(file, info, :position_in_fixture, options_with_position(options, :position_in_fixture)) }.
|
305
|
+
detect { |result| result } || did_annotate
|
244
306
|
end
|
245
|
-
end
|
246
307
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
File.join(EXEMPLARS_SPEC_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
|
253
|
-
File.join(BLUEPRINTS_TEST_DIR, "#{model_name}_blueprint.rb"), # Machinist Blueprints
|
254
|
-
File.join(BLUEPRINTS_SPEC_DIR, "#{model_name}_blueprint.rb"), # Machinist Blueprints
|
255
|
-
File.join(FACTORY_GIRL_TEST_DIR, "#{model_name}_factory.rb"), # Factory Girl Factories
|
256
|
-
File.join(FACTORY_GIRL_SPEC_DIR, "#{model_name}_factory.rb"), # Factory Girl Factories
|
257
|
-
File.join(FABRICATORS_TEST_DIR, "#{model_name}_fabricator.rb"), # Fabrication Fabricators
|
258
|
-
File.join(FABRICATORS_SPEC_DIR, "#{model_name}_fabricator.rb"), # Fabrication Fabricators
|
259
|
-
].each do |file|
|
260
|
-
if annotate_one_file(file, info, options_with_position(options, :position_in_fixture))
|
261
|
-
annotated = true
|
262
|
-
end
|
308
|
+
unless options[:exclude_factories]
|
309
|
+
did_annotate = FACTORY_PATTERNS.
|
310
|
+
map { |file| resolve_filename(file, model_name, table_name) }.
|
311
|
+
map { |file| annotate_one_file(file, info, :position_in_factory, options_with_position(options, :position_in_factory)) }.
|
312
|
+
detect { |result| result } || did_annotate
|
263
313
|
end
|
264
|
-
end
|
265
314
|
|
266
|
-
|
315
|
+
return did_annotate
|
316
|
+
rescue Exception => e
|
317
|
+
puts "Unable to annotate #{file}: #{e.message}"
|
318
|
+
puts "\t" + e.backtrace.join("\n\t") if options[:trace]
|
319
|
+
end
|
267
320
|
end
|
268
321
|
|
269
322
|
# position = :position_in_fixture or :position_in_class
|
@@ -297,19 +350,18 @@ module AnnotateModels
|
|
297
350
|
puts "No models found in directory '#{model_dir}'."
|
298
351
|
puts "Either specify models on the command line, or use the --model-dir option."
|
299
352
|
puts "Call 'annotate --help' for more info."
|
300
|
-
exit 1
|
353
|
+
exit 1
|
301
354
|
end
|
302
355
|
end
|
303
356
|
models
|
304
357
|
end
|
305
|
-
|
358
|
+
|
306
359
|
# Retrieve the classes belonging to the model names we're asked to process
|
307
360
|
# Check for namespaced models in subdirectories as well as models
|
308
361
|
# in subdirectories without namespacing.
|
309
362
|
def get_model_class(file)
|
310
363
|
# this is for non-rails projects, which don't get Rails auto-require magic
|
311
|
-
require File.expand_path("#{model_dir}/#{file}")
|
312
|
-
|
364
|
+
require File.expand_path("#{model_dir}/#{file}") unless Module.const_defined?(:Rails)
|
313
365
|
model_path = file.gsub(/\.rb$/, '')
|
314
366
|
get_loaded_model(model_path) || get_loaded_model(model_path.split('/').last)
|
315
367
|
end
|
@@ -320,7 +372,7 @@ module AnnotateModels
|
|
320
372
|
select do |c|
|
321
373
|
Class === c and # note: we use === to avoid a bug in activesupport 2.3.14 OptionMerger vs. is_a?
|
322
374
|
c.ancestors.respond_to?(:include?) and # to fix FactoryGirl bug, see https://github.com/ctran/annotate_models/pull/82
|
323
|
-
c.ancestors.include?(ActiveRecord::Base)
|
375
|
+
c.ancestors.include?(ActiveRecord::Base)
|
324
376
|
end.
|
325
377
|
detect { |c| ActiveSupport::Inflector.underscore(c) == model_path }
|
326
378
|
end
|
@@ -330,21 +382,15 @@ module AnnotateModels
|
|
330
382
|
# if its a subclass of ActiveRecord::Base,
|
331
383
|
# then pass it to the associated block
|
332
384
|
def do_annotations(options={})
|
333
|
-
if options[:require]
|
334
|
-
options[:require].each do |path|
|
335
|
-
require path
|
336
|
-
end
|
337
|
-
end
|
338
|
-
|
339
385
|
header = options[:format_markdown] ? PREFIX_MD.dup : PREFIX.dup
|
340
386
|
|
341
387
|
if options[:include_version]
|
342
388
|
version = ActiveRecord::Migrator.current_version rescue 0
|
343
389
|
if version > 0
|
344
390
|
header << "\n# Schema version: #{version}"
|
345
|
-
end
|
391
|
+
end
|
346
392
|
end
|
347
|
-
|
393
|
+
|
348
394
|
self.model_dir = options[:model_dir] if options[:model_dir]
|
349
395
|
|
350
396
|
annotated = []
|
@@ -373,46 +419,54 @@ module AnnotateModels
|
|
373
419
|
end
|
374
420
|
|
375
421
|
def remove_annotations(options={})
|
422
|
+
|
376
423
|
self.model_dir = options[:model_dir] if options[:model_dir]
|
377
424
|
deannotated = []
|
425
|
+
deannotated_klass = false
|
378
426
|
get_model_files(options).each do |file|
|
379
427
|
begin
|
380
428
|
klass = get_model_class(file)
|
381
429
|
if klass < ActiveRecord::Base && !klass.abstract_class?
|
382
|
-
deannotated << klass
|
383
|
-
|
384
430
|
model_name = klass.name.underscore
|
431
|
+
table_name = klass.table_name
|
385
432
|
model_file_name = File.join(model_dir, file)
|
386
|
-
remove_annotation_of_file(model_file_name)
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
433
|
+
deannotated_klass = true if(remove_annotation_of_file(model_file_name))
|
434
|
+
|
435
|
+
TEST_PATTERNS.
|
436
|
+
map { |pat| [pat[0], resolve_filename(pat[1], model_name, table_name)]}.
|
437
|
+
map { |pat| find_test_file(*pat) }.each do |file|
|
438
|
+
if(File.exist?(file))
|
439
|
+
remove_annotation_of_file(file)
|
440
|
+
deannotated_klass = true
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
(FIXTURE_PATTERNS + FACTORY_PATTERNS).
|
445
|
+
map { |file| resolve_filename(file, model_name, table_name) }.
|
446
|
+
each do |file|
|
447
|
+
if File.exist?(file)
|
448
|
+
remove_annotation_of_file(file)
|
449
|
+
deannotated_klass = true
|
450
|
+
end
|
451
|
+
end
|
405
452
|
end
|
453
|
+
deannotated << klass if(deannotated_klass)
|
406
454
|
rescue Exception => e
|
407
455
|
puts "Unable to deannotate #{file}: #{e.message}"
|
408
|
-
puts "\t" + e.backtrace.join("\n\t") if options[:trace]
|
456
|
+
puts "\t" + e.backtrace.join("\n\t") if options[:trace]
|
409
457
|
end
|
410
458
|
end
|
411
|
-
puts "Removed
|
459
|
+
puts "Removed annotations from: #{deannotated.join(', ')}"
|
412
460
|
end
|
413
461
|
|
414
462
|
def find_test_file(dir, file_name)
|
415
463
|
Dir.glob(File.join(dir, "**", file_name)).first || File.join(dir, file_name)
|
416
464
|
end
|
465
|
+
|
466
|
+
def resolve_filename(filename_template, model_name, table_name)
|
467
|
+
return filename_template.
|
468
|
+
gsub('%MODEL_NAME%', model_name).
|
469
|
+
gsub('%TABLE_NAME%', table_name || model_name.pluralize)
|
470
|
+
end
|
417
471
|
end
|
418
472
|
end
|