annotate 2.4.1.beta1 → 2.5.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +75 -1
- data/README.rdoc +80 -57
- data/bin/annotate +46 -19
- data/lib/annotate.rb +4 -14
- data/lib/annotate/active_record_patch.rb +9 -0
- data/lib/annotate/annotate_models.rb +188 -112
- data/lib/annotate/annotate_routes.rb +11 -11
- data/lib/annotate/tasks.rb +6 -0
- data/lib/annotate/version.rb +5 -0
- data/lib/generators/annotate_models/USAGE +4 -0
- data/lib/generators/annotate_models/install_generator.rb +14 -0
- data/lib/generators/annotate_models/templates/auto_annotate_models.rake +20 -0
- data/lib/tasks/annotate_models.rake +29 -9
- data/lib/tasks/annotate_routes.rake +3 -2
- data/tasks/migrate.rake +4 -4
- metadata +31 -29
- data/Rakefile +0 -51
- data/VERSION.yml +0 -5
- data/annotate.gemspec +0 -58
- data/spec/annotate/annotate_models_spec.rb +0 -92
- data/spec/annotate/annotate_routes_spec.rb +0 -47
- data/spec/annotate_spec.rb +0 -9
- data/spec/spec.opts +0 -1
- data/spec/spec_helper.rb +0 -10
- data/todo.txt +0 -5
data/History.txt
CHANGED
@@ -1,3 +1,77 @@
|
|
1
|
+
== 2.5.0
|
2
|
+
|
3
|
+
* Fixed that schema kept prepending additional newlines
|
4
|
+
* Updates to make annotate smarter about when to touch a model
|
5
|
+
* Recognize column+type, and don't change a file unless the column+type combination of the new schema are different than that of the old (i.e., don't regenerate if columns happen to be in a different order. That's just how life is sometimes)
|
6
|
+
* Grab old specification even if it has \r\n as line endings rather than pure \ns
|
7
|
+
* Various warning and specification fixes
|
8
|
+
* Fix "no such file to load -- annotate/annotate_models (MissingSourceFile)" error (require statements in tasks now use full path to lib files)
|
9
|
+
* warn about macros, to mitigate when we're included during a production run, not just a rakefile run -- possibly at the expense of too much noise
|
10
|
+
* Adding rake as a runtime dependency
|
11
|
+
* If the schema is already in the model file, it will be replaced into the same location. If it didn't previously exist, it'll be placed according to the "position", as before.
|
12
|
+
* Allow task loading from Rakefile for gems (plugin installation already auto-detects).
|
13
|
+
* Add skip_on_db_migrate option as well for people that don't want it
|
14
|
+
* Fix options parsing to convert strings to proper booleans. Change annotate to use opt
|
15
|
+
ions hash instead of ENV.
|
16
|
+
* Add support for Fabrication fabricators
|
17
|
+
* Leave magic encoding comment intact
|
18
|
+
* Fix issue #14 - RuntimeError: Already memoized
|
19
|
+
* Count a model as 'annotated' if any of its tests/fixtures are annotated
|
20
|
+
* Support FactoryGirl
|
21
|
+
* Support :change migrations (Rails 3.1)
|
22
|
+
* Allow models with non-standard capitalization
|
23
|
+
* Widen type column so we can handle longtexts with chopping things off.
|
24
|
+
* Skip trying to get list of models from commandline when running via Rake (was
|
25
|
+
preventing the use of multiple rake tasks in one command if one of them was
|
26
|
+
db:migrate).
|
27
|
+
* Add ability to skip annotations for a model by adding
|
28
|
+
'# -*- SkipSchemaAnnotations' anywhere in the file.
|
29
|
+
* Don't show column limits for integer and boolean types.
|
30
|
+
* Add sorting for columns and indexes. (Helpful for out-of-order migration
|
31
|
+
execution, but use --no-sort if you don't want this.)
|
32
|
+
* Annotate unit tests in subfolders.
|
33
|
+
* Add generator to install rakefile that automatically annotates on db:migrate.
|
34
|
+
* Correct Gemfile to clarify which environments need which gems.
|
35
|
+
* Add an .rvmrc to facilitate clean development.
|
36
|
+
* Refactor out ActiveRecord monkey-patch to permit extending without
|
37
|
+
side-effects.
|
38
|
+
* Use ObjectSpace to locate models to facilitate handling of models with
|
39
|
+
non-standard capitalization.
|
40
|
+
Note that this still requires that the inflector be configured to understand
|
41
|
+
the special case.
|
42
|
+
* Shore up test cases a bit.
|
43
|
+
* Merge against many of the older branches on Github whose functionality is
|
44
|
+
already reflected to reduce confusion about what is and is not implemented
|
45
|
+
here.
|
46
|
+
* Accept String or Symbol for :position (et al) options.
|
47
|
+
* Rename "annotate" bin to "annotate_models" to avoid conflicting with
|
48
|
+
ImageMagick.
|
49
|
+
* Add RDoc output formatting as an option.
|
50
|
+
* Add Markdown output formatting as an option.
|
51
|
+
* Add option to force annotation regeneration.
|
52
|
+
* Add new configuration option for controlling where info is placed in
|
53
|
+
fixtures/factories.
|
54
|
+
* Fix for models without tables.
|
55
|
+
* Fix gemspec generation now that Jeweler looks at Gemfile.
|
56
|
+
* Fix warning: `NOTE: Gem::Specification#default_executable= is deprecated with
|
57
|
+
no replacement. It will be removed on or after 2011-10-01.`
|
58
|
+
* Fix handling of files with no trailing newline when putting annotations at
|
59
|
+
the end of the file.
|
60
|
+
|
61
|
+
== 2.4.2 2009-11-21
|
62
|
+
|
63
|
+
* Annotates (spec|test)/factories/<model>_factory.rb files
|
64
|
+
|
65
|
+
== 2.4.1 2009-11-20
|
66
|
+
|
67
|
+
* Annotates thoughtbot's factory_girl factories (test/factories/<model>_factory.rb)
|
68
|
+
* Move default annotation position back to top
|
69
|
+
|
70
|
+
== 2.4.0 2009-12-13
|
71
|
+
|
72
|
+
* Incorporated lots of patches from the Github community, including support for Blueprints fixtures
|
73
|
+
* Several bug fixes
|
74
|
+
|
1
75
|
== 2.1 2009-10-18
|
2
76
|
|
3
77
|
* New options
|
@@ -5,7 +79,7 @@
|
|
5
79
|
* -i to show database indexes in annotations
|
6
80
|
* -e to exclude annotating tests or fixtures
|
7
81
|
* -m to include the migration version number in the annotation
|
8
|
-
* --model-dir to annotate model files stored a different place than app/models
|
82
|
+
* --model-dir to annotate model files stored a different place than app/models
|
9
83
|
* Ignore unknown macros ('acts_as_whatever')
|
10
84
|
|
11
85
|
== 2.0 2009-02-03
|
data/README.rdoc
CHANGED
@@ -2,11 +2,14 @@
|
|
2
2
|
|
3
3
|
Add a comment summarizing the current schema to the top or bottom of each of your...
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
- ActiveRecord models
|
6
|
+
- Fixture files
|
7
|
+
- Tests and Specs
|
8
|
+
- Object Daddy exemplars
|
9
|
+
- Machinist blueprints
|
10
|
+
- Fabrication fabricators
|
11
|
+
- Thoughtbot's factory_girl factories, i.e. the (spec|test)/factories/<model>_factory.rb files
|
12
|
+
- routes.rb file
|
10
13
|
|
11
14
|
The schema comment looks like this:
|
12
15
|
|
@@ -40,20 +43,18 @@ Also, if you pass the -r option, it'll annotate routes.rb with the output of "ra
|
|
40
43
|
|
41
44
|
Into Gemfile from Github:
|
42
45
|
|
43
|
-
gem 'annotate', :git => 'git://github.com/
|
44
|
-
|
46
|
+
gem 'annotate', :git => 'git://github.com/MrJoy/annotate_models.git'
|
45
47
|
|
46
|
-
Into environment gems From
|
48
|
+
Into environment gems From rubygems.org:
|
47
49
|
|
48
|
-
|
50
|
+
gem install annotate
|
49
51
|
|
50
52
|
Into environment gems from Github checkout:
|
51
53
|
|
52
|
-
git clone git://github.com/
|
53
|
-
cd
|
54
|
-
rake
|
55
|
-
|
56
|
-
|
54
|
+
git clone git://github.com/MrJoy/annotate_models.git annotate_models
|
55
|
+
cd annotate_models
|
56
|
+
rake build
|
57
|
+
gem install pkg/annotate-*.gem
|
57
58
|
|
58
59
|
== USAGE
|
59
60
|
|
@@ -62,43 +63,61 @@ Into environment gems from Github checkout:
|
|
62
63
|
To annotate all your models, tests, fixtures, etc.:
|
63
64
|
|
64
65
|
cd /path/to/app
|
65
|
-
|
66
|
+
annotate_models
|
66
67
|
|
67
68
|
To annotate your models and tests:
|
68
69
|
|
69
|
-
|
70
|
+
annotate_models --exclude fixtures
|
70
71
|
|
71
72
|
To annotate just your models:
|
72
73
|
|
73
|
-
|
74
|
+
annotate_models --exclude tests,fixtures
|
74
75
|
|
75
76
|
To annotate routes.rb:
|
76
77
|
|
77
|
-
|
78
|
+
annotate_models -r
|
79
|
+
|
80
|
+
To remove annotations:
|
81
|
+
|
82
|
+
annotate_models -d
|
83
|
+
|
84
|
+
To automatically annotate after running 'rake db:migrate', ensure you've added
|
85
|
+
annotate_models to your Rails project's Gemfile, and run this:
|
86
|
+
|
87
|
+
rails g annotate_models:install
|
78
88
|
|
79
|
-
|
80
|
-
|
81
|
-
|
89
|
+
This will produce a .rake file that will ensure annotation happens after
|
90
|
+
migration (but only in development mode), and provide configuration options
|
91
|
+
you can use to tailor the output.
|
82
92
|
|
83
|
-
If you
|
84
|
-
|
85
|
-
|
86
|
-
|
93
|
+
If you want to always skip annotations on a particular model, add this string
|
94
|
+
anywhere in the file:
|
95
|
+
|
96
|
+
# -*- SkipSchemaAnnotations
|
87
97
|
|
88
98
|
== OPTIONS
|
89
99
|
|
90
|
-
Usage:
|
100
|
+
Usage: annotate_models [options] [model_file]*
|
91
101
|
-d, --delete Remove annotations from all model files
|
92
102
|
-p, --position [before|after] Place the annotations at the top (before) or the bottom (after) of the model file
|
93
103
|
-r, --routes Annotate routes.rb with the output of 'rake routes'
|
94
104
|
-v, --version Show the current version of this gem
|
95
105
|
-m, --show-migration Include the migration version number in the annotation
|
96
|
-
-i, --show-indexes List the
|
97
|
-
-s, --simple-indexes
|
106
|
+
-i, --show-indexes List the indexes for the table in the annotation
|
107
|
+
-s, --simple-indexes Include information about indexes inline with the relevant column
|
98
108
|
--model-dir dir Annotate model files stored in dir rather than app/models
|
109
|
+
--ignore-model-subdirs Ignore sub-directories of the models directory.
|
99
110
|
-R, --require path Additional files to require before loading models
|
100
|
-
-e
|
111
|
+
-e [tests,fixtures] Skip annotation of fixtures/factories/test files
|
112
|
+
--exclude
|
113
|
+
-n --no-sort Sort by column creation order rather than alphabetical order
|
114
|
+
|
115
|
+
== SORTING
|
101
116
|
|
117
|
+
By default, columns will be sorted alphabetically so that the results of
|
118
|
+
annotation are consistent regardless of what order migrations are executed in.
|
119
|
+
|
120
|
+
If you prefer the old behavior, use --no-sort.
|
102
121
|
|
103
122
|
== WARNING
|
104
123
|
|
@@ -107,40 +126,44 @@ block in your models if it looks like it was previously added
|
|
107
126
|
by annotate models, so you don't want to add additional text
|
108
127
|
to an automatically created comment block.
|
109
128
|
|
110
|
-
|
129
|
+
BACK UP YOUR MODELS BEFORE USING THIS TOOL!
|
111
130
|
|
112
131
|
== LINKS
|
113
132
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
133
|
+
- Factory Girl: http://github.com/thoughtbot/factory_girl
|
134
|
+
- Object Daddy: http://github.com/flogic/object_daddy
|
135
|
+
- Machinist: http://github.com/notahat/machinist
|
136
|
+
- Fabrication: http://github.com/paulelliott/fabrication
|
137
|
+
- SpatialAdapter: http://github.com/pdeffendol/spatial_adapter
|
138
|
+
- PostgisAdapter: http://github.com/nofxx/postgis_adapter
|
118
139
|
|
119
140
|
== LICENSE:
|
120
141
|
|
121
142
|
Released under the same license as Ruby. No Support. No Warranty.
|
122
143
|
|
123
|
-
==
|
124
|
-
|
125
|
-
Original code by: Dave Thomas -- Pragmatic Programmers, LLC
|
126
|
-
Overhauled by: Alex Chaffee
|
127
|
-
Gemmed by: Cuong Tran
|
128
|
-
Maintained by: Alex Chaffee and Cuong Tran
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
144
|
+
== AUTHORS:
|
145
|
+
|
146
|
+
- Original code by: Dave Thomas -- Pragmatic Programmers, LLC <http://agilewebdevelopment.com/plugins/annotate_models>
|
147
|
+
- Overhauled by: Alex Chaffee <http://alexch.github.com> alex@stinky.com
|
148
|
+
- Gemmed by: Cuong Tran <http://github.com/ctran> ctran@pragmaquest.com
|
149
|
+
- Maintained by: Alex Chaffee and Cuong Tran
|
150
|
+
- Homepage: http://github.com/ctran/annotate_models
|
151
|
+
|
152
|
+
With help from:
|
153
|
+
|
154
|
+
- Jack Danger - http://github.com/JackDanger
|
155
|
+
- Michael Bumann - http://github.com/bumi
|
156
|
+
- Henrik Nyh - http://github.com/henrik
|
157
|
+
- Marcos Piccinini - http://github.com/nofxx
|
158
|
+
- Neal Clark - http://github.com/nclark
|
159
|
+
- Jacqui Maher - http://github.com/jacqui
|
160
|
+
- Nick Plante - http://github.com/zapnap - http://blog.zerosum.org
|
161
|
+
- Pedro Visintin - http://github.com/peterpunk - http://www.pedrovisintin.com
|
162
|
+
- Bob Potter - http://github.com/bpot
|
163
|
+
- Gavin Montague - http://github.com/govan
|
164
|
+
- Alexander Semyonov - http://github.com/rotuka
|
165
|
+
- Nathan Brazil - http://github.com/bitaxis
|
166
|
+
- Ian Duggan http://github.com/ijcd
|
167
|
+
- Jon Frisby http://github.com/mrjoy
|
168
|
+
|
146
169
|
and many others that I may have missed to add.
|
data/bin/annotate
CHANGED
@@ -1,55 +1,73 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
2
|
require 'optparse'
|
4
|
-
require '
|
3
|
+
require 'rake/dsl_definition'
|
4
|
+
require 'rake'
|
5
|
+
begin
|
6
|
+
require "annotate"
|
7
|
+
rescue LoadError
|
8
|
+
here = File.expand_path(File.dirname __FILE__)
|
9
|
+
$:<< "#{here}/../lib"
|
10
|
+
require "annotate"
|
11
|
+
end
|
5
12
|
|
6
13
|
task = :annotate_models
|
7
14
|
|
8
15
|
OptionParser.new do |opts|
|
9
|
-
opts.banner = "Usage:
|
16
|
+
opts.banner = "Usage: annotate_models [options] [model_file]*"
|
10
17
|
|
11
|
-
opts.on('-d', '--delete',
|
12
|
-
|
18
|
+
opts.on('-d', '--delete',
|
19
|
+
"Remove annotations from all model files") do
|
13
20
|
task = :remove_annotation
|
14
21
|
end
|
15
22
|
|
16
|
-
|
17
|
-
|
23
|
+
ENV['position'] = 'before' # hack: make sure default position is "before"
|
24
|
+
opts.on('-p', '--position [before|after]', ['before', 'after'],
|
25
|
+
"Place the annotations at the top (before) or the bottom (after) of the model file") do |p|
|
18
26
|
ENV['position'] = p
|
19
27
|
end
|
20
28
|
|
21
|
-
opts.on('-r', '--routes',
|
22
|
-
|
29
|
+
opts.on('-r', '--routes',
|
30
|
+
"Annotate routes.rb with the output of 'rake routes'") do
|
23
31
|
task = :annotate_routes
|
24
32
|
end
|
25
33
|
|
26
|
-
opts.on('-v', '--version',
|
27
|
-
|
34
|
+
opts.on('-v', '--version',
|
35
|
+
"Show the current version of this gem") do
|
28
36
|
puts "annotate v#{Annotate.version}"; exit
|
29
37
|
end
|
30
38
|
|
31
|
-
opts.on('-m', '--show-migration',
|
32
|
-
|
39
|
+
opts.on('-m', '--show-migration',
|
40
|
+
"Include the migration version number in the annotation") do
|
33
41
|
ENV['include_version'] = "yes"
|
34
42
|
end
|
35
43
|
|
36
|
-
opts.on('-i', '--show-indexes',
|
37
|
-
|
44
|
+
opts.on('-i', '--show-indexes',
|
45
|
+
"List the table's database indexes in the annotation") do
|
38
46
|
ENV['show_indexes'] = "yes"
|
39
47
|
end
|
40
48
|
|
41
49
|
opts.on('-s', '--simple-indexes',
|
42
|
-
|
50
|
+
"Concat the column's related indexes in the annotation") do
|
43
51
|
ENV['simple_indexes'] = "yes"
|
44
52
|
end
|
45
53
|
|
46
|
-
opts.on('--model-dir dir',
|
47
|
-
|
54
|
+
opts.on('--model-dir dir',
|
55
|
+
"Annotate model files stored in dir rather than app/models") do |dir|
|
48
56
|
ENV['model_dir'] = dir
|
49
57
|
end
|
50
58
|
|
59
|
+
opts.on('--ignore-model-subdirects',
|
60
|
+
"Ignore subdirectories of the models directory") do |dir|
|
61
|
+
ENV['ignore_model_sub_dir'] = "yes"
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on('-n', '--no-sort',
|
65
|
+
"Sort columns in creation order rather than alphabetically") do |dir|
|
66
|
+
ENV['no_sort'] = "yes"
|
67
|
+
end
|
68
|
+
|
51
69
|
opts.on('-R', '--require path',
|
52
|
-
|
70
|
+
"Additional files to require before loading models") do |path|
|
53
71
|
if ENV['require']
|
54
72
|
ENV['require'] = ENV['require'] + ",#{path}"
|
55
73
|
else
|
@@ -61,8 +79,17 @@ OptionParser.new do |opts|
|
|
61
79
|
exclusions.each { |exclusion| ENV["exclude_#{exclusion}"] = "yes" }
|
62
80
|
end
|
63
81
|
|
82
|
+
opts.on('-f', '--format [bare|rdoc|markdown]', ['bare', 'rdoc', 'markdown'], 'Render Schema Infomation as plain/RDoc/Markdown') do |fmt|
|
83
|
+
ENV["format_#{fmt}"] = 'yes'
|
84
|
+
end
|
85
|
+
|
86
|
+
opts.on('--force', 'Force new annotations even if there are no changes.') do |force|
|
87
|
+
ENV['force'] = 'yes'
|
88
|
+
end
|
89
|
+
|
64
90
|
end.parse!
|
65
91
|
|
92
|
+
ENV['is_cli'] = '1'
|
66
93
|
if Annotate.load_tasks
|
67
94
|
Rake::Task[task].invoke
|
68
95
|
else
|
data/lib/annotate.rb
CHANGED
@@ -1,23 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require 'yaml'
|
1
|
+
here = File.dirname __FILE__
|
2
|
+
require "#{here}/annotate/version"
|
5
3
|
|
6
4
|
module Annotate
|
7
|
-
def self.version
|
8
|
-
version_file = File.dirname(__FILE__) + "/../VERSION.yml"
|
9
|
-
if File.exist?(version_file)
|
10
|
-
config = YAML.load(File.read(version_file))
|
11
|
-
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
12
|
-
else
|
13
|
-
version = "0.0.0"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
5
|
def self.load_tasks
|
18
6
|
if File.exists?('Rakefile')
|
19
7
|
require 'rake'
|
20
8
|
load 'Rakefile'
|
9
|
+
# Rails 3 wants to load our .rake files for us.
|
10
|
+
# TODO: selectively do this require on Rails 2.x?
|
21
11
|
Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')].each { |rake| load rake }
|
22
12
|
return true
|
23
13
|
else
|
@@ -1,20 +1,41 @@
|
|
1
1
|
module AnnotateModels
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
2
|
+
# Annotate Models plugin use this header
|
3
|
+
COMPAT_PREFIX = "== Schema Info"
|
4
|
+
COMPAT_PREFIX_MD = "## Schema Info"
|
5
|
+
PREFIX = "== Schema Information"
|
6
|
+
PREFIX_MD = "## Schema Information"
|
7
|
+
END_MARK = "== Schema Information End"
|
8
|
+
PATTERN = /^\n?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?\n(#.*\n)*\n/
|
9
|
+
|
10
|
+
# File.join for windows reverse bar compat?
|
11
|
+
# I dont use windows, can`t test
|
12
|
+
UNIT_TEST_DIR = File.join("test", "unit" )
|
13
|
+
SPEC_MODEL_DIR = File.join("spec", "models")
|
14
|
+
FIXTURE_TEST_DIR = File.join("test", "fixtures")
|
15
|
+
FIXTURE_SPEC_DIR = File.join("spec", "fixtures")
|
16
|
+
FIXTURE_DIRS = ["test/fixtures","spec/fixtures"]
|
17
|
+
|
18
|
+
# Object Daddy http://github.com/flogic/object_daddy/tree/master
|
19
|
+
EXEMPLARS_TEST_DIR = File.join("test", "exemplars")
|
20
|
+
EXEMPLARS_SPEC_DIR = File.join("spec", "exemplars")
|
21
|
+
|
22
|
+
# Machinist http://github.com/notahat/machinist
|
23
|
+
BLUEPRINTS_TEST_DIR = File.join("test", "blueprints")
|
24
|
+
BLUEPRINTS_SPEC_DIR = File.join("spec", "blueprints")
|
25
|
+
|
26
|
+
# Factory Girl http://github.com/thoughtbot/factory_girl
|
27
|
+
FACTORY_GIRL_TEST_DIR = File.join("test", "factories")
|
28
|
+
FACTORY_GIRL_SPEC_DIR = File.join("spec", "factories")
|
29
|
+
|
30
|
+
# Fabrication https://github.com/paulelliott/fabrication.git
|
31
|
+
FABRICATORS_TEST_DIR = File.join("test", "fabricators")
|
32
|
+
FABRICATORS_SPEC_DIR = File.join("spec", "fabricators")
|
33
|
+
|
34
|
+
# Don't show limit (#) on these column types
|
35
|
+
# Example: show "integer" instead of "integer(4)"
|
36
|
+
NO_LIMIT_COL_TYPES = ["integer", "boolean"]
|
17
37
|
|
38
|
+
class << self
|
18
39
|
def model_dir
|
19
40
|
@model_dir || "app/models"
|
20
41
|
end
|
@@ -26,14 +47,14 @@ module AnnotateModels
|
|
26
47
|
# Simple quoting for the default column value
|
27
48
|
def quote(value)
|
28
49
|
case value
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
50
|
+
when NilClass then "NULL"
|
51
|
+
when TrueClass then "TRUE"
|
52
|
+
when FalseClass then "FALSE"
|
53
|
+
when Float, Fixnum, Bignum then value.to_s
|
33
54
|
# BigDecimals need to be output in a non-normalized form and quoted.
|
34
|
-
|
35
|
-
|
36
|
-
|
55
|
+
when BigDecimal then value.to_s('F')
|
56
|
+
else
|
57
|
+
value.inspect
|
37
58
|
end
|
38
59
|
end
|
39
60
|
|
@@ -42,21 +63,34 @@ module AnnotateModels
|
|
42
63
|
# each column. The line contains the column name,
|
43
64
|
# the type (and length), and any optional attributes
|
44
65
|
def get_schema_info(klass, header, options = {})
|
45
|
-
info = "# #{header}\n
|
46
|
-
info
|
66
|
+
info = "# #{header}\n"
|
67
|
+
info<< "#\n"
|
68
|
+
info<< "# Table name: #{klass.table_name}\n"
|
69
|
+
info<< "#\n"
|
70
|
+
|
71
|
+
max_size = klass.column_names.map{|name| name.size}.max || 0
|
72
|
+
max_size += options[:format_rdoc] ? 5 : 1
|
47
73
|
|
48
|
-
|
49
|
-
|
74
|
+
if(options[:format_markdown])
|
75
|
+
info<< sprintf( "# %-#{max_size + 4}.#{max_size + 4}s | %-18.18s | %s\n", 'Field', 'Type', 'Attributes' )
|
76
|
+
info<< "# #{ '-' * ( max_size + 4 ) } | #{'-' * 18} | #{ '-' * 25 }\n"
|
77
|
+
end
|
78
|
+
|
79
|
+
cols = klass.columns
|
80
|
+
cols = cols.sort_by(&:name) unless(options[:no_sort])
|
81
|
+
cols.each do |col|
|
50
82
|
attrs = []
|
51
83
|
attrs << "default(#{quote(col.default)})" unless col.default.nil?
|
52
84
|
attrs << "not null" unless col.null
|
53
|
-
attrs << "primary key" if col.name == klass.primary_key
|
85
|
+
attrs << "primary key" if col.name.to_sym == klass.primary_key.to_sym
|
54
86
|
|
55
|
-
col_type = col.type.to_s
|
87
|
+
col_type = (col.type || col.sql_type).to_s
|
56
88
|
if col_type == "decimal"
|
57
89
|
col_type << "(#{col.precision}, #{col.scale})"
|
58
90
|
else
|
59
|
-
|
91
|
+
if (col.limit)
|
92
|
+
col_type << "(#{col.limit})" unless NO_LIMIT_COL_TYPES.include?(col_type)
|
93
|
+
end
|
60
94
|
end
|
61
95
|
|
62
96
|
# Check out if we got a geometric column
|
@@ -66,8 +100,8 @@ module AnnotateModels
|
|
66
100
|
end
|
67
101
|
|
68
102
|
# Check if the column has indices and print "indexed" if true
|
69
|
-
# If the
|
70
|
-
if options[:simple_indexes]
|
103
|
+
# If the index includes another column, print it too.
|
104
|
+
if options[:simple_indexes] && klass.table_exists?# Check out if this column is indexed
|
71
105
|
indices = klass.connection.indexes(klass.table_name)
|
72
106
|
if indices = indices.select { |ind| ind.columns.include? col.name }
|
73
107
|
indices.each do |ind|
|
@@ -77,14 +111,26 @@ module AnnotateModels
|
|
77
111
|
end
|
78
112
|
end
|
79
113
|
|
80
|
-
|
114
|
+
if options[:format_rdoc]
|
115
|
+
info << sprintf("# %-#{max_size}.#{max_size}s<tt>%s</tt>", "*#{col.name}*::", attrs.unshift(col_type).join(", ")).rstrip + "\n"
|
116
|
+
elsif options[:format_markdown]
|
117
|
+
info << sprintf("# **%-#{max_size}.#{max_size}s** | `%-16.16s` | `%s`", col.name, col_type, attrs.join(", ").rstrip) + "\n"
|
118
|
+
else
|
119
|
+
info << sprintf("# %-#{max_size}.#{max_size}s:%-16.16s %s", col.name, col_type, attrs.join(", ")).rstrip + "\n"
|
120
|
+
end
|
81
121
|
end
|
82
122
|
|
83
|
-
if options[:show_indexes]
|
123
|
+
if options[:show_indexes] && klass.table_exists?
|
84
124
|
info << get_index_info(klass)
|
85
125
|
end
|
86
126
|
|
87
|
-
|
127
|
+
if options[:format_rdoc]
|
128
|
+
info << "#--\n"
|
129
|
+
info << "# #{END_MARK}\n"
|
130
|
+
info << "#++\n\n"
|
131
|
+
else
|
132
|
+
info << "#\n\n"
|
133
|
+
end
|
88
134
|
end
|
89
135
|
|
90
136
|
def get_index_info(klass)
|
@@ -94,7 +140,7 @@ module AnnotateModels
|
|
94
140
|
return "" if indexes.empty?
|
95
141
|
|
96
142
|
max_size = indexes.collect{|index| index.name.size}.max + 1
|
97
|
-
indexes.each do |index|
|
143
|
+
indexes.sort_by{|index| index.name}.each do |index|
|
98
144
|
index_info << sprintf("# %-#{max_size}.#{max_size}s %s %s", index.name, "(#{index.columns.join(",")})", index.unique ? "UNIQUE" : "").rstrip + "\n"
|
99
145
|
end
|
100
146
|
return index_info
|
@@ -108,7 +154,7 @@ module AnnotateModels
|
|
108
154
|
#
|
109
155
|
# === Options (opts)
|
110
156
|
# :position<Symbol>:: where to place the annotated section in fixture or model file,
|
111
|
-
#
|
157
|
+
# :before or :after. Default is :before.
|
112
158
|
# :position_in_class<Symbol>:: where to place the annotated section in model file
|
113
159
|
# :position_in_fixture<Symbol>:: where to place the annotated section in fixture file
|
114
160
|
# :position_in_others<Symbol>:: where to place the annotated section in the rest of
|
@@ -117,26 +163,44 @@ module AnnotateModels
|
|
117
163
|
def annotate_one_file(file_name, info_block, options={})
|
118
164
|
if File.exist?(file_name)
|
119
165
|
old_content = File.read(file_name)
|
166
|
+
return false if(old_content =~ /# -\*- SkipSchemaAnnotations.*\n/)
|
120
167
|
|
121
168
|
# Ignore the Schema version line because it changes with each migration
|
122
|
-
|
123
|
-
old_header = old_content.match(
|
124
|
-
new_header = info_block.match(
|
169
|
+
header_pattern = /(^# Table name:.*?\n(#.*[\r]?\n)*[\r]?\n)/
|
170
|
+
old_header = old_content.match(header_pattern).to_s
|
171
|
+
new_header = info_block.match(header_pattern).to_s
|
125
172
|
|
126
|
-
|
127
|
-
|
173
|
+
column_pattern = /^#[\t ]+\w+[\t ]+.+$/
|
174
|
+
old_columns = old_header && old_header.scan(column_pattern).sort
|
175
|
+
new_columns = new_header && new_header.scan(column_pattern).sort
|
128
176
|
|
129
|
-
|
177
|
+
encoding = Regexp.new(/(^#\s*encoding:.*\n)|(^# coding:.*\n)|(^# -\*- coding:.*\n)/)
|
178
|
+
encoding_header = old_content.match(encoding).to_s
|
179
|
+
|
180
|
+
if old_columns == new_columns && !options[:force]
|
130
181
|
false
|
131
182
|
else
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
183
|
+
|
184
|
+
# todo: figure out if we need to extract any logic from this merge chunk
|
185
|
+
# <<<<<<< HEAD
|
186
|
+
# # Replace the old schema info with the new schema info
|
187
|
+
# new_content = old_content.sub(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n*/, info_block)
|
188
|
+
# # But, if there *was* no old schema info, we simply need to insert it
|
189
|
+
# if new_content == old_content
|
190
|
+
# old_content.sub!(encoding, '')
|
191
|
+
# new_content = options[:position] == 'after' ?
|
192
|
+
# (encoding_header + (old_content =~ /\n$/ ? old_content : old_content + "\n") + info_block) :
|
193
|
+
# (encoding_header + info_block + old_content)
|
194
|
+
# end
|
195
|
+
# =======
|
196
|
+
|
197
|
+
# Strip the old schema info, and insert new schema info.
|
198
|
+
old_content.sub!(encoding, '')
|
199
|
+
old_content.sub!(PATTERN, '')
|
200
|
+
|
201
|
+
new_content = (options[:position] || 'before').to_s == 'after' ?
|
202
|
+
(encoding_header + (old_content.rstrip + "\n\n" + info_block)) :
|
203
|
+
(encoding_header + info_block + old_content)
|
140
204
|
|
141
205
|
File.open(file_name, "wb") { |f| f.puts new_content }
|
142
206
|
true
|
@@ -148,7 +212,7 @@ module AnnotateModels
|
|
148
212
|
if File.exist?(file_name)
|
149
213
|
content = File.read(file_name)
|
150
214
|
|
151
|
-
content.sub!(
|
215
|
+
content.sub!(PATTERN, '')
|
152
216
|
|
153
217
|
File.open(file_name, "wb") { |f| f.puts content }
|
154
218
|
end
|
@@ -160,7 +224,7 @@ module AnnotateModels
|
|
160
224
|
# of the model and fixture source files.
|
161
225
|
# Returns true or false depending on whether the source
|
162
226
|
# files were modified.
|
163
|
-
def annotate(klass, file, header,options={})
|
227
|
+
def annotate(klass, file, header, options={})
|
164
228
|
info = get_schema_info(klass, header, options)
|
165
229
|
annotated = false
|
166
230
|
model_name = klass.name.underscore
|
@@ -169,37 +233,41 @@ module AnnotateModels
|
|
169
233
|
if annotate_one_file(model_file_name, info, options_with_position(options, :position_in_class))
|
170
234
|
annotated = true
|
171
235
|
end
|
172
|
-
|
173
|
-
unless
|
236
|
+
|
237
|
+
unless options[:exclude_tests]
|
174
238
|
[
|
175
|
-
|
176
|
-
|
177
|
-
].each do |file|
|
239
|
+
find_test_file(UNIT_TEST_DIR, "#{model_name}_test.rb"), # test
|
240
|
+
find_test_file(SPEC_MODEL_DIR, "#{model_name}_spec.rb"), # spec
|
241
|
+
].each do |file|
|
178
242
|
# todo: add an option "position_in_test" -- or maybe just ask if anyone ever wants different positions for model vs. test vs. fixture
|
179
|
-
annotate_one_file(file, info, options_with_position(options, :position_in_fixture))
|
243
|
+
if annotate_one_file(file, info, options_with_position(options, :position_in_fixture))
|
244
|
+
annotated = true
|
245
|
+
end
|
180
246
|
end
|
181
247
|
end
|
182
248
|
|
183
|
-
unless
|
249
|
+
unless options[:exclude_fixtures]
|
184
250
|
[
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
251
|
+
File.join(FIXTURE_TEST_DIR, "#{klass.table_name}.yml"), # fixture
|
252
|
+
File.join(FIXTURE_SPEC_DIR, "#{klass.table_name}.yml"), # fixture
|
253
|
+
File.join(EXEMPLARS_TEST_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
|
254
|
+
File.join(EXEMPLARS_SPEC_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
|
255
|
+
File.join(BLUEPRINTS_TEST_DIR, "#{model_name}_blueprint.rb"), # Machinist Blueprints
|
256
|
+
File.join(BLUEPRINTS_SPEC_DIR, "#{model_name}_blueprint.rb"), # Machinist Blueprints
|
257
|
+
File.join(FACTORY_GIRL_TEST_DIR, "#{model_name}_factory.rb"), # Factory Girl Factories
|
258
|
+
File.join(FACTORY_GIRL_SPEC_DIR, "#{model_name}_factory.rb"), # Factory Girl Factories
|
259
|
+
File.join(FABRICATORS_TEST_DIR, "#{model_name}_fabricator.rb"), # Fabrication Fabricators
|
260
|
+
File.join(FABRICATORS_SPEC_DIR, "#{model_name}_fabricator.rb"), # Fabrication Fabricators
|
261
|
+
].each do |file|
|
262
|
+
if annotate_one_file(file, info, options_with_position(options, :position_in_fixture))
|
263
|
+
annotated = true
|
196
264
|
end
|
197
265
|
end
|
198
266
|
end
|
199
|
-
|
267
|
+
|
200
268
|
annotated
|
201
269
|
end
|
202
|
-
|
270
|
+
|
203
271
|
# position = :position_in_fixture or :position_in_class
|
204
272
|
def options_with_position(options, position_in)
|
205
273
|
options.merge(:position=>(options[position_in] || options[:position]))
|
@@ -210,14 +278,22 @@ module AnnotateModels
|
|
210
278
|
# the underscore or CamelCase versions of model names.
|
211
279
|
# Otherwise we take all the model files in the
|
212
280
|
# model_dir directory.
|
213
|
-
def get_model_files
|
214
|
-
|
215
|
-
|
281
|
+
def get_model_files(options)
|
282
|
+
if(!options[:is_rake])
|
283
|
+
models = ARGV.dup
|
284
|
+
models.shift
|
285
|
+
else
|
286
|
+
models = []
|
287
|
+
end
|
216
288
|
models.reject!{|m| m.match(/^(.*)=/)}
|
217
289
|
if models.empty?
|
218
290
|
begin
|
219
291
|
Dir.chdir(model_dir) do
|
220
|
-
models =
|
292
|
+
models = if options[:ignore_model_sub_dir]
|
293
|
+
Dir["*.rb"]
|
294
|
+
else
|
295
|
+
Dir["**/*.rb"]
|
296
|
+
end
|
221
297
|
end
|
222
298
|
rescue SystemCallError
|
223
299
|
puts "No models found in directory '#{model_dir}'."
|
@@ -233,18 +309,18 @@ module AnnotateModels
|
|
233
309
|
# Check for namespaced models in subdirectories as well as models
|
234
310
|
# in subdirectories without namespacing.
|
235
311
|
def get_model_class(file)
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
312
|
+
# this is for non-rails projects, which don't get Rails auto-require magic
|
313
|
+
require File.expand_path("#{model_dir}/#{file}")
|
314
|
+
|
315
|
+
model_path = file.gsub(/\.rb$/, '')
|
316
|
+
get_loaded_model(model_path) || get_loaded_model(model_path.split('/').last)
|
317
|
+
end
|
318
|
+
|
319
|
+
# Retrieve loaded model class by path to the file where it's supposed to be defined.
|
320
|
+
def get_loaded_model(model_path)
|
321
|
+
ObjectSpace.each_object.
|
322
|
+
select { |c| c.is_a?(Class) && c.ancestors.include?(ActiveRecord::Base) }.
|
323
|
+
detect { |c| ActiveSupport::Inflector.underscore(c) == model_path }
|
248
324
|
end
|
249
325
|
|
250
326
|
# We're passed a name of things that might be
|
@@ -258,7 +334,7 @@ module AnnotateModels
|
|
258
334
|
end
|
259
335
|
end
|
260
336
|
|
261
|
-
header = PREFIX.dup
|
337
|
+
header = options[:format_markdown] ? PREFIX_MD.dup : PREFIX.dup
|
262
338
|
|
263
339
|
if options[:include_version]
|
264
340
|
version = ActiveRecord::Migrator.current_version rescue 0
|
@@ -272,19 +348,17 @@ module AnnotateModels
|
|
272
348
|
end
|
273
349
|
|
274
350
|
annotated = []
|
275
|
-
get_model_files.each do |file|
|
351
|
+
get_model_files(options).each do |file|
|
276
352
|
begin
|
277
353
|
klass = get_model_class(file)
|
278
|
-
if klass < ActiveRecord::Base && !klass.abstract_class?
|
354
|
+
if klass && klass < ActiveRecord::Base && !klass.abstract_class?
|
279
355
|
if annotate(klass, file, header, options)
|
280
356
|
annotated << klass
|
281
357
|
end
|
282
358
|
end
|
283
359
|
rescue Exception => e
|
284
|
-
|
285
|
-
puts ""
|
286
|
-
# todo: check if all backtrace lines are in "gems" -- if so, it's an annotate bug, so print the whole stack trace.
|
287
|
-
# puts e.backtrace.join("\n\t")
|
360
|
+
# todo: check if all backtrace lines are in "gems" -- if so, it's an annotate bug, so print the whole stack trace.
|
361
|
+
puts "Unable to annotate #{file}: #{e.message} (#{e.backtrace.first})"
|
288
362
|
end
|
289
363
|
end
|
290
364
|
if annotated.empty?
|
@@ -300,25 +374,33 @@ module AnnotateModels
|
|
300
374
|
self.model_dir = options[:model_dir]
|
301
375
|
end
|
302
376
|
deannotated = []
|
303
|
-
get_model_files.each do |file|
|
377
|
+
get_model_files(options).each do |file|
|
304
378
|
begin
|
305
379
|
klass = get_model_class(file)
|
306
380
|
if klass < ActiveRecord::Base && !klass.abstract_class?
|
307
381
|
deannotated << klass
|
308
382
|
|
383
|
+
model_name = klass.name.underscore
|
309
384
|
model_file_name = File.join(model_dir, file)
|
310
385
|
remove_annotation_of_file(model_file_name)
|
311
386
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
387
|
+
[
|
388
|
+
File.join(UNIT_TEST_DIR, "#{model_name}_test.rb"),
|
389
|
+
File.join(SPEC_MODEL_DIR, "#{model_name}_spec.rb"),
|
390
|
+
File.join(FIXTURE_TEST_DIR, "#{klass.table_name}.yml"), # fixture
|
391
|
+
File.join(FIXTURE_SPEC_DIR, "#{klass.table_name}.yml"), # fixture
|
392
|
+
File.join(EXEMPLARS_TEST_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
|
393
|
+
File.join(EXEMPLARS_SPEC_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
|
394
|
+
File.join(BLUEPRINTS_TEST_DIR, "#{model_name}_blueprint.rb"), # Machinist Blueprints
|
395
|
+
File.join(BLUEPRINTS_SPEC_DIR, "#{model_name}_blueprint.rb"), # Machinist Blueprints
|
396
|
+
File.join(FACTORY_GIRL_TEST_DIR, "#{model_name}_factory.rb"), # Factory Girl Factories
|
397
|
+
File.join(FACTORY_GIRL_SPEC_DIR, "#{model_name}_factory.rb"), # Factory Girl Factories
|
398
|
+
File.join(FABRICATORS_TEST_DIR, "#{model_name}_fabricator.rb"), # Fabrication Fabricators
|
399
|
+
File.join(FABRICATORS_SPEC_DIR, "#{model_name}_fabricator.rb"), # Fabrication Fabricators
|
400
|
+
].each do |file|
|
319
401
|
remove_annotation_of_file(file) if File.exist?(file)
|
320
402
|
end
|
321
|
-
|
403
|
+
|
322
404
|
end
|
323
405
|
rescue Exception => e
|
324
406
|
puts "Unable to annotate #{file}: #{e.message}"
|
@@ -326,15 +408,9 @@ module AnnotateModels
|
|
326
408
|
end
|
327
409
|
puts "Removed annotation from: #{deannotated.join(', ')}"
|
328
410
|
end
|
329
|
-
end
|
330
|
-
end
|
331
|
-
|
332
|
-
# monkey patches
|
333
411
|
|
334
|
-
|
335
|
-
|
336
|
-
def self.method_missing(name, *args)
|
337
|
-
# ignore this, so unknown/unloaded macros won't cause parsing to fail
|
412
|
+
def find_test_file(dir, file_name)
|
413
|
+
Dir.glob(File.join(dir, "**", file_name)).first || File.join(dir, file_name)
|
338
414
|
end
|
339
415
|
end
|
340
416
|
end
|