annotate 2.5.0 → 2.6.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|