selleo-annotate 2.3.2
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.
- data/.gitignore +8 -0
- data/History.txt +27 -0
- data/README.rdoc +166 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/bin/annotate +64 -0
- data/lib/annotate.rb +14 -0
- data/lib/annotate/annotate_models.rb +293 -0
- data/lib/annotate/annotate_routes.rb +41 -0
- data/lib/tasks/annotate_models.rake +21 -0
- data/lib/tasks/annotate_routes.rake +5 -0
- data/lib/tasks/automatic_annotation.rake +23 -0
- data/spec/annotate/annotate_models_spec.rb +83 -0
- data/spec/annotate/annotate_routes_spec.rb +47 -0
- data/spec/annotate_spec.rb +8 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- metadata +75 -0
data/.gitignore
ADDED
data/History.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
== 2.0 2009-02-03
|
2
|
+
|
3
|
+
* Add annotate_models plugin fork additions
|
4
|
+
* Annotates Rspec and Test Unit models
|
5
|
+
* Annotates Object Daddy exemplars
|
6
|
+
* Annotates geometrical columns
|
7
|
+
* Add AnnotateRoutes rake task
|
8
|
+
* Up gem structure to newgem defaults
|
9
|
+
|
10
|
+
== 1.0.4 2008-09-04
|
11
|
+
|
12
|
+
* Only update modified models since last run, thanks to sant0sk1
|
13
|
+
|
14
|
+
== 1.0.3 2008-05-02
|
15
|
+
|
16
|
+
* Add misc changes from Dustin Sallings and Henrik N
|
17
|
+
* Remove trailing whitespace
|
18
|
+
* More intuitive info messages
|
19
|
+
* Update README file with update-to-date example
|
20
|
+
|
21
|
+
== 1.0.2 2008-03-22
|
22
|
+
|
23
|
+
* Add contributions from Michael Bumann (http://github.com/bumi)
|
24
|
+
* added an option "position" to choose to put the annotation,
|
25
|
+
* spec/fixtures now also get annotated
|
26
|
+
* added a task to remove the annotations
|
27
|
+
* these options can be specified from command line as -d and -p [before|after]
|
data/README.rdoc
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
_
|
2
|
+
/_| _/__/_
|
3
|
+
( |/)/)()/(//(-
|
4
|
+
________________
|
5
|
+
|
6
|
+
|
7
|
+
Add a comment summarizing the current schema to the bottom of each
|
8
|
+
ActiveRecord model, fixture file.
|
9
|
+
|
10
|
+
If you are using Object Daddy, it`ll annotate your example files too.
|
11
|
+
|
12
|
+
# == Schema Info
|
13
|
+
#
|
14
|
+
# Table name: line_items
|
15
|
+
#
|
16
|
+
# id :integer(11) not null, primary key
|
17
|
+
# quantity :integer(11) not null
|
18
|
+
# product_id :integer(11) not null
|
19
|
+
# unit_price :float
|
20
|
+
# order_id :integer(11)
|
21
|
+
#
|
22
|
+
|
23
|
+
class LineItem < ActiveRecord::Base
|
24
|
+
belongs_to :product
|
25
|
+
. . .
|
26
|
+
|
27
|
+
Annotates geometrical columns, geom type and srid, when using SpatialAdapter or PostgisAdapter:
|
28
|
+
|
29
|
+
# == Schema Info
|
30
|
+
#
|
31
|
+
# Table name: trips
|
32
|
+
#
|
33
|
+
# local :geometry point, 4326
|
34
|
+
# path :geometry line_string, 4326
|
35
|
+
|
36
|
+
|
37
|
+
== INSTALL
|
38
|
+
|
39
|
+
sudo gem install nofxx-annotate
|
40
|
+
|
41
|
+
|
42
|
+
== HOW TO USE:
|
43
|
+
=======
|
44
|
+
== Warning
|
45
|
+
|
46
|
+
Note that this code will blow away the initial/final comment
|
47
|
+
block in your models if it looks like it was previously added
|
48
|
+
by annotate models, so you don't want to add additional text
|
49
|
+
to an automatically created comment block.
|
50
|
+
|
51
|
+
* * Back up your model files before using... * *
|
52
|
+
|
53
|
+
== Install
|
54
|
+
|
55
|
+
From rubyforge:
|
56
|
+
|
57
|
+
sudo gem install annotate
|
58
|
+
|
59
|
+
From github:
|
60
|
+
|
61
|
+
gem sources -a http://gems.github.com
|
62
|
+
sudo gem install ctran-annotate
|
63
|
+
|
64
|
+
== Usage
|
65
|
+
|
66
|
+
To annotate all your models:
|
67
|
+
|
68
|
+
cd /path/to/app
|
69
|
+
annotate
|
70
|
+
|
71
|
+
To annotate routes.rb:
|
72
|
+
|
73
|
+
annotate -r
|
74
|
+
|
75
|
+
More:
|
76
|
+
|
77
|
+
annotate -h
|
78
|
+
|
79
|
+
|
80
|
+
To migrate & annotate (TODO):
|
81
|
+
|
82
|
+
|
83
|
+
Options:
|
84
|
+
|
85
|
+
Annotate on the head of the file:
|
86
|
+
|
87
|
+
annotate -p [before|after]
|
88
|
+
|
89
|
+
|
90
|
+
== WARNING
|
91
|
+
|
92
|
+
Note that this code will blow away the initial/final comment
|
93
|
+
block in your models if it looks like it was previously added
|
94
|
+
by annotate models, so you don't want to add additional text
|
95
|
+
to an automatically created comment block.
|
96
|
+
|
97
|
+
* * Back up your model files before using... * *
|
98
|
+
|
99
|
+
== LINKS
|
100
|
+
|
101
|
+
* Factory Girl => http://github.com/thoughtbot/factory_girl (NOT IMPLEMENTED)
|
102
|
+
* Object Daddy => http://github.com/flogic/object_daddy
|
103
|
+
|
104
|
+
* SpatialAdapter => http://github.com/pdeffendol/spatial_adapter
|
105
|
+
* PostgisAdapter => http://github.com/nofxx/postgis_adapter
|
106
|
+
|
107
|
+
|
108
|
+
== TODO
|
109
|
+
|
110
|
+
* Spec
|
111
|
+
|
112
|
+
|
113
|
+
== LICENSE:
|
114
|
+
=======
|
115
|
+
More options:
|
116
|
+
|
117
|
+
Usage: annotate [options]
|
118
|
+
-d, --delete Remove annotations from all model files
|
119
|
+
-p, --position [before|after] Place the annotations at the top (before) or the bottom (after) of the model file
|
120
|
+
-r, --routes Annotate routes.rb with the output of 'rake routes'
|
121
|
+
-v, --version Show the current version of this gem
|
122
|
+
-m, --show-migration Include the migration version number in the annotation
|
123
|
+
-i, --show-indexes List the table's database indexes in the annotation
|
124
|
+
--model-dir dir Annotate model files stored in dir rather than app/models
|
125
|
+
|
126
|
+
== LICENSE:
|
127
|
+
|
128
|
+
Released under the same license as Ruby. No Support. No Warranty.
|
129
|
+
|
130
|
+
== Author:
|
131
|
+
|
132
|
+
Original code by:
|
133
|
+
|
134
|
+
Dave Thomas -- Pragmatic Programmers, LLC
|
135
|
+
|
136
|
+
AnnotateModels mods by:
|
137
|
+
|
138
|
+
Alexander Semyonov ( http://github.com/rotuka/annotate_models )
|
139
|
+
|
140
|
+
AnnotateRoutes originally by:
|
141
|
+
|
142
|
+
Gavin Montague ( http://github.com/govan/annotate-routes )
|
143
|
+
|
144
|
+
|
145
|
+
== Forked from:
|
146
|
+
|
147
|
+
http://github.com/ctran/annotate_models
|
148
|
+
|
149
|
+
|
150
|
+
== Modifications
|
151
|
+
- alex@pivotallabs.com
|
152
|
+
=======
|
153
|
+
Modifications by:
|
154
|
+
|
155
|
+
- Alex Chaffee - http://github.com/alexch - alex@pivotallabs.com
|
156
|
+
- Cuong Tran - http://github.com/ctran
|
157
|
+
- Jack Danger - http://github.com/JackDanger
|
158
|
+
- Michael Bumann - http://github.com/bumi
|
159
|
+
- Henrik Nyh - http://github.com/henrik
|
160
|
+
- Marcos Piccinini - http://github.com/nofxx
|
161
|
+
|
162
|
+
and many others that I may have missed to add.
|
163
|
+
|
164
|
+
== This fork
|
165
|
+
|
166
|
+
Marcos Piccinini ( http://github.com/nofxx/annotate )
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "selleo-annotate"
|
8
|
+
gem.summary = "Annotates Rails Models, routes, automatic annotation and others"
|
9
|
+
gem.email = "x@selleo.com"
|
10
|
+
gem.homepage = "http://github.com/selleo/annotate"
|
11
|
+
gem.authors = ['Cuong Tran',"Marcos Piccinini"]
|
12
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
13
|
+
end
|
14
|
+
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'spec/rake/spectask'
|
20
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
21
|
+
spec.libs << 'lib' << 'spec'
|
22
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
23
|
+
end
|
24
|
+
|
25
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
26
|
+
spec.libs << 'lib' << 'spec'
|
27
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
28
|
+
spec.rcov = true
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
task :default => :spec
|
33
|
+
|
34
|
+
require 'rake/rdoctask'
|
35
|
+
Rake::RDocTask.new do |rdoc|
|
36
|
+
if File.exist?('VERSION.yml')
|
37
|
+
config = YAML.load(File.read('VERSION.yml'))
|
38
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
39
|
+
else
|
40
|
+
version = ""
|
41
|
+
end
|
42
|
+
|
43
|
+
rdoc.rdoc_dir = 'rdoc'
|
44
|
+
rdoc.title = "annotate #{version}"
|
45
|
+
rdoc.rdoc_files.include('README*')
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
47
|
+
end
|
48
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.2
|
data/bin/annotate
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'annotate'
|
5
|
+
|
6
|
+
task = :annotate_models
|
7
|
+
|
8
|
+
OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: annotate [options]"
|
10
|
+
opts.on('-d', '--delete',
|
11
|
+
"Remove annotations from all model files") do
|
12
|
+
task = :remove_annotation
|
13
|
+
end
|
14
|
+
opts.on('-p', '--position [before|after]', ['before', 'after'],
|
15
|
+
"Place the annotations at the top (before) or the bottom (after) of the model file") do |p|
|
16
|
+
ENV['position'] = p
|
17
|
+
end
|
18
|
+
opts.on('-r', '--routes',
|
19
|
+
"Annotate routes.rb with the output of 'rake routes'") do
|
20
|
+
task = :annotate_routes
|
21
|
+
end
|
22
|
+
opts.on('-A', '--automatic_annotate',
|
23
|
+
"Annotate each time when server boots in development mode") do
|
24
|
+
task = :automatic_annotate
|
25
|
+
end
|
26
|
+
opts.on('-D', '--remove_automatic_annotation',
|
27
|
+
"Remove automatic annotate when server boots") do
|
28
|
+
task = :remove_automatic_annotation
|
29
|
+
end
|
30
|
+
opts.on('-v', '--version',
|
31
|
+
"Show the current version of this gem") do
|
32
|
+
puts "Annotate v#{Annotate::VERSION}"; exit
|
33
|
+
end
|
34
|
+
opts.on('-m', '--show-migration',
|
35
|
+
"Include the migration version number in the annotation") do
|
36
|
+
ENV['include_version'] = "yes"
|
37
|
+
end
|
38
|
+
opts.on('-i', '--show-indexes',
|
39
|
+
"List the table's database indexes in the annotation") do
|
40
|
+
ENV['show_indexes'] = "yes"
|
41
|
+
end
|
42
|
+
opts.on('-s', '--simple-indexes',
|
43
|
+
"Concat the column's related indexes in the annotation") do
|
44
|
+
ENV['simple_indexes'] = "yes"
|
45
|
+
end
|
46
|
+
opts.on('--model-dir dir',
|
47
|
+
"Annotate model files stored in dir rather than app/models") do |dir|
|
48
|
+
ENV['model_dir'] = dir
|
49
|
+
end
|
50
|
+
opts.on('-R', '--require path',
|
51
|
+
"Additional files to require before loading models") do |path|
|
52
|
+
if ENV['require']
|
53
|
+
ENV['require'] = ENV['require'] + ",#{path}"
|
54
|
+
else
|
55
|
+
ENV['require'] = path
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end.parse!
|
59
|
+
|
60
|
+
if Annotate.load_tasks
|
61
|
+
Rake::Task[task].invoke
|
62
|
+
else
|
63
|
+
STDERR.puts "Can't find Rakefile. Are we in a Rails folder?"
|
64
|
+
end
|
data/lib/annotate.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
module Annotate
|
5
|
+
def self.load_tasks
|
6
|
+
if File.exists?('Rakefile')
|
7
|
+
load 'Rakefile'
|
8
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')].each { |rake| load rake }
|
9
|
+
return true
|
10
|
+
else
|
11
|
+
return false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,293 @@
|
|
1
|
+
module AnnotateModels
|
2
|
+
class << self
|
3
|
+
# Annotate Models plugin use this header
|
4
|
+
COMPAT_PREFIX = "== Schema Info"
|
5
|
+
PREFIX = "== Schema Information"
|
6
|
+
FIXTURE_DIRS = ["test/fixtures","spec/fixtures"]
|
7
|
+
# File.join for windows reverse bar compat?
|
8
|
+
# I dont use windows, can`t test
|
9
|
+
UNIT_TEST_DIR = File.join("test", "unit" )
|
10
|
+
SPEC_MODEL_DIR = File.join("spec", "models")
|
11
|
+
EXEMPLARS_TEST_DIR = File.join("test", "exemplars")
|
12
|
+
# Object Daddy http://github.com/flogic/object_daddy/tree/master
|
13
|
+
EXEMPLARS_SPEC_DIR = File.join("spec", "exemplars")
|
14
|
+
# Machinist http://github.com/notahat/machinist
|
15
|
+
BLUEPRINTS_DIR = File.join("test", "blueprints")
|
16
|
+
|
17
|
+
def model_dir
|
18
|
+
@model_dir || "app/models"
|
19
|
+
end
|
20
|
+
|
21
|
+
def model_dir=(dir)
|
22
|
+
@model_dir = dir
|
23
|
+
end
|
24
|
+
|
25
|
+
# Simple quoting for the default column value
|
26
|
+
def quote(value)
|
27
|
+
case value
|
28
|
+
when NilClass then "NULL"
|
29
|
+
when TrueClass then "TRUE"
|
30
|
+
when FalseClass then "FALSE"
|
31
|
+
when Float, Fixnum, Bignum then value.to_s
|
32
|
+
# BigDecimals need to be output in a non-normalized form and quoted.
|
33
|
+
when BigDecimal then value.to_s('F')
|
34
|
+
else
|
35
|
+
value.inspect
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Use the column information in an ActiveRecord class
|
40
|
+
# to create a comment block containing a line for
|
41
|
+
# each column. The line contains the column name,
|
42
|
+
# the type (and length), and any optional attributes
|
43
|
+
def get_schema_info(klass, header, options = {})
|
44
|
+
info = "# #{header}\n#\n"
|
45
|
+
info << "# Table name: #{klass.table_name}\n#\n"
|
46
|
+
|
47
|
+
max_size = klass.column_names.collect{|name| name.size}.max + 1
|
48
|
+
klass.columns.each do |col|
|
49
|
+
attrs = []
|
50
|
+
attrs << "default(#{quote(col.default)})" unless col.default.nil?
|
51
|
+
attrs << "not null" unless col.null
|
52
|
+
attrs << "primary key" if col.name == klass.primary_key
|
53
|
+
|
54
|
+
col_type = col.type.to_s
|
55
|
+
if col_type == "decimal"
|
56
|
+
col_type << "(#{col.precision}, #{col.scale})"
|
57
|
+
else
|
58
|
+
col_type << "(#{col.limit})" if col.limit
|
59
|
+
end
|
60
|
+
|
61
|
+
# Check out if we got a geometric column
|
62
|
+
# and print the type and SRID
|
63
|
+
if col.respond_to?(:geometry_type)
|
64
|
+
attrs << "#{col.geometry_type}, #{col.srid}"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Check if the column has indices and print "indexed" if true
|
68
|
+
# If the indice include another colum, print it too.
|
69
|
+
if options[:simple_indexes] # Check out if this column is indexed
|
70
|
+
indices = klass.connection.indexes(klass.table_name)
|
71
|
+
if indices = indices.select { |ind| ind.columns.include? col.name }
|
72
|
+
indices.each do |ind|
|
73
|
+
ind = ind.columns.reject! { |i| i == col.name }
|
74
|
+
attrs << (ind.length == 0 ? "indexed" : "indexed => [#{ind.join(", ")}]")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
info << sprintf("# %-#{max_size}.#{max_size}s:%-15.15s %s", col.name, col_type, attrs.join(", ")).rstrip + "\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
if options[:show_indexes]
|
82
|
+
info << get_index_info(klass)
|
83
|
+
end
|
84
|
+
|
85
|
+
info << "#\n\n"
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_index_info(klass)
|
89
|
+
index_info = "#\n# Indexes\n#\n"
|
90
|
+
|
91
|
+
indexes = klass.connection.indexes(klass.table_name)
|
92
|
+
return "" if indexes.empty?
|
93
|
+
|
94
|
+
max_size = indexes.collect{|index| index.name.size}.max + 1
|
95
|
+
indexes.each do |index|
|
96
|
+
index_info << sprintf("# %-#{max_size}.#{max_size}s %s %s", index.name, "(#{index.columns.join(",")})", index.unique ? "UNIQUE" : "").rstrip + "\n"
|
97
|
+
end
|
98
|
+
return index_info
|
99
|
+
end
|
100
|
+
|
101
|
+
# Add a schema block to a file. If the file already contains
|
102
|
+
# a schema info block (a comment starting with "== Schema Information"), check if it
|
103
|
+
# matches the block that is already there. If so, leave it be. If not, remove the old
|
104
|
+
# info block and write a new one.
|
105
|
+
# Returns true or false depending on whether the file was modified.
|
106
|
+
#
|
107
|
+
# === Options (opts)
|
108
|
+
# :position<Symbol>:: where to place the annotated section in fixture or model file,
|
109
|
+
# "before" or "after". Default is "before".
|
110
|
+
# :position_in_class<Symbol>:: where to place the annotated section in model file
|
111
|
+
# :position_in_fixture<Symbol>:: where to place the annotated section in fixture file
|
112
|
+
# :position_in_others<Symbol>:: where to place the annotated section in the rest of
|
113
|
+
# supported files
|
114
|
+
#
|
115
|
+
def annotate_one_file(file_name, info_block, options={})
|
116
|
+
if File.exist?(file_name)
|
117
|
+
old_content = File.read(file_name)
|
118
|
+
|
119
|
+
# Ignore the Schema version line because it changes with each migration
|
120
|
+
header = Regexp.new(/(^# Table name:.*?\n(#.*\n)*\n)/)
|
121
|
+
old_header = old_content.match(header).to_s
|
122
|
+
new_header = info_block.match(header).to_s
|
123
|
+
|
124
|
+
if old_header == new_header
|
125
|
+
false
|
126
|
+
else
|
127
|
+
# Remove old schema info
|
128
|
+
old_content.sub!(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n/, '')
|
129
|
+
|
130
|
+
# Write it back
|
131
|
+
new_content = options[:position] == 'before' ? (info_block + old_content) : (old_content + "\n" + info_block)
|
132
|
+
|
133
|
+
File.open(file_name, "wb") { |f| f.puts new_content }
|
134
|
+
true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def remove_annotation_of_file(file_name)
|
140
|
+
if File.exist?(file_name)
|
141
|
+
content = File.read(file_name)
|
142
|
+
|
143
|
+
content.sub!(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n/, '')
|
144
|
+
|
145
|
+
File.open(file_name, "wb") { |f| f.puts content }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Given the name of an ActiveRecord class, create a schema
|
150
|
+
# info block (basically a comment containing information
|
151
|
+
# on the columns and their types) and put it at the front
|
152
|
+
# of the model and fixture source files.
|
153
|
+
# Returns true or false depending on whether the source
|
154
|
+
# files were modified.
|
155
|
+
|
156
|
+
def annotate(klass, file, header,options={})
|
157
|
+
info = get_schema_info(klass, header, options)
|
158
|
+
annotated = false
|
159
|
+
model_name = klass.name.underscore
|
160
|
+
model_file_name = File.join(model_dir, file)
|
161
|
+
if annotate_one_file(model_file_name, info, options.merge(
|
162
|
+
:position=>(options[:position_in_class] || options[:position])))
|
163
|
+
annotated = true
|
164
|
+
end
|
165
|
+
|
166
|
+
[
|
167
|
+
File.join(UNIT_TEST_DIR, "#{model_name}_test.rb"), # test
|
168
|
+
File.join(SPEC_MODEL_DIR, "#{model_name}_spec.rb"), # spec
|
169
|
+
File.join(EXEMPLARS_TEST_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
|
170
|
+
File.join(EXEMPLARS_SPEC_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
|
171
|
+
File.join(BLUEPRINTS_DIR, "#{model_name}_blueprint.rb"), # Machinist Blueprints
|
172
|
+
].each { |file| annotate_one_file(file, info) }
|
173
|
+
|
174
|
+
FIXTURE_DIRS.each do |dir|
|
175
|
+
fixture_file_name = File.join(dir,klass.table_name + ".yml")
|
176
|
+
annotate_one_file(fixture_file_name, info, options.merge(:position=>(options[:position_in_fixture] || options[:position]))) if File.exist?(fixture_file_name)
|
177
|
+
end
|
178
|
+
annotated
|
179
|
+
end
|
180
|
+
|
181
|
+
# Return a list of the model files to annotate. If we have
|
182
|
+
# command line arguments, they're assumed to be either
|
183
|
+
# the underscore or CamelCase versions of model names.
|
184
|
+
# Otherwise we take all the model files in the
|
185
|
+
# model_dir directory.
|
186
|
+
def get_model_files
|
187
|
+
models = ARGV.dup
|
188
|
+
models.shift
|
189
|
+
models.reject!{|m| m.starts_with?("position=")}
|
190
|
+
if models.empty?
|
191
|
+
Dir.chdir(model_dir) do
|
192
|
+
models = Dir["**/*.rb"]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
models
|
196
|
+
end
|
197
|
+
|
198
|
+
# Retrieve the classes belonging to the model names we're asked to process
|
199
|
+
# Check for namespaced models in subdirectories as well as models
|
200
|
+
# in subdirectories without namespacing.
|
201
|
+
def get_model_class(file)
|
202
|
+
require File.expand_path("#{model_dir}/#{file}") # this is for non-rails projects, which don't get Rails auto-require magic
|
203
|
+
model = file.gsub(/\.rb$/, '').camelize
|
204
|
+
parts = model.split('::')
|
205
|
+
begin
|
206
|
+
parts.inject(Object) {|klass, part| klass.const_get(part) }
|
207
|
+
rescue LoadError
|
208
|
+
Object.const_get(parts.last)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# We're passed a name of things that might be
|
213
|
+
# ActiveRecord models. If we can find the class, and
|
214
|
+
# if its a subclass of ActiveRecord::Base,
|
215
|
+
# then pass it to the associated block
|
216
|
+
def do_annotations(options={})
|
217
|
+
if options[:require]
|
218
|
+
options[:require].each do |path|
|
219
|
+
require path
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
header = PREFIX.dup
|
224
|
+
|
225
|
+
if options[:include_version]
|
226
|
+
version = ActiveRecord::Migrator.current_version rescue 0
|
227
|
+
if version > 0
|
228
|
+
header << "\n# Schema version: #{version}"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
if options[:model_dir]
|
233
|
+
self.model_dir = options[:model_dir]
|
234
|
+
end
|
235
|
+
|
236
|
+
annotated = []
|
237
|
+
get_model_files.each do |file|
|
238
|
+
begin
|
239
|
+
klass = get_model_class(file)
|
240
|
+
if klass < ActiveRecord::Base && !klass.abstract_class?
|
241
|
+
if annotate(klass, file, header,options)
|
242
|
+
annotated << klass
|
243
|
+
end
|
244
|
+
end
|
245
|
+
rescue Exception => e
|
246
|
+
puts "Unable to annotate #{file}: #{e.message} (#{e.backtrace.first})"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
if annotated.empty?
|
250
|
+
puts "Nothing annotated."
|
251
|
+
else
|
252
|
+
puts "Annotated (#{annotated.length}): #{annotated.join(', ')}"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def remove_annotations(options={})
|
257
|
+
if options[:model_dir]
|
258
|
+
puts "removing"
|
259
|
+
self.model_dir = options[:model_dir]
|
260
|
+
end
|
261
|
+
deannotated = []
|
262
|
+
get_model_files.each do |file|
|
263
|
+
begin
|
264
|
+
klass = get_model_class(file)
|
265
|
+
if klass < ActiveRecord::Base && !klass.abstract_class?
|
266
|
+
deannotated << klass
|
267
|
+
|
268
|
+
model_file_name = File.join(model_dir, file)
|
269
|
+
remove_annotation_of_file(model_file_name)
|
270
|
+
|
271
|
+
FIXTURE_DIRS.each do |dir|
|
272
|
+
fixture_file_name = File.join(dir,klass.table_name + ".yml")
|
273
|
+
remove_annotation_of_file(fixture_file_name) if File.exist?(fixture_file_name)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
rescue Exception => e
|
277
|
+
puts "Unable to annotate #{file}: #{e.message}"
|
278
|
+
end
|
279
|
+
end
|
280
|
+
puts "Removed annotation from: #{deannotated.join(', ')}"
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# monkey patches
|
286
|
+
|
287
|
+
module ::ActiveRecord
|
288
|
+
class Base
|
289
|
+
def self.method_missing(name, *args)
|
290
|
+
# ignore this, so unknown/unloaded macros won't cause parsing to fail
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# == Annotate Routes
|
2
|
+
#
|
3
|
+
# Based on:
|
4
|
+
#
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# Prepends the output of "rake routes" to the top of your routes.rb file.
|
8
|
+
# Yes, it's simple but I'm thick and often need a reminder of what my routes mean.
|
9
|
+
#
|
10
|
+
# Running this task will replace any exising route comment generated by the task.
|
11
|
+
# Best to back up your routes file before running:
|
12
|
+
#
|
13
|
+
# Author:
|
14
|
+
# Gavin Montague
|
15
|
+
# gavin@leftbrained.co.uk
|
16
|
+
#
|
17
|
+
# Released under the same license as Ruby. No Support. No Warranty.module AnnotateRoutes
|
18
|
+
#
|
19
|
+
module AnnotateRoutes
|
20
|
+
PREFIX = "#== Route Map"
|
21
|
+
|
22
|
+
def self.do_annotate
|
23
|
+
routes_rb = File.join("config", "routes.rb")
|
24
|
+
header = PREFIX + "\n# Generated on #{Time.now.strftime("%d %b %Y %H:%M")}\n#"
|
25
|
+
if File.exists? routes_rb
|
26
|
+
routes_map = `rake routes`
|
27
|
+
routes_map = routes_map.split("\n")
|
28
|
+
routes_map.shift # remove the first line of rake routes which is just a file path
|
29
|
+
routes_map = routes_map.inject(header){|sum, line| sum<<"\n# "<<line}
|
30
|
+
content = File.read(routes_rb)
|
31
|
+
content, old = content.split(/^#== Route .*?\n/)
|
32
|
+
File.open(routes_rb, "wb") do |f|
|
33
|
+
f.puts content.sub!(/\n?\z/, "\n") + routes_map
|
34
|
+
end
|
35
|
+
puts "Route file annotated."
|
36
|
+
else
|
37
|
+
puts "Can`t find routes.rb"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
desc "Add schema information (as comments) to model and fixture files"
|
2
|
+
task :annotate_models => :environment do
|
3
|
+
require 'annotate/annotate_models'
|
4
|
+
options={}
|
5
|
+
options[:position_in_class] = ENV['position_in_class'] || ENV['position'] || :before
|
6
|
+
options[:position_in_fixture] = ENV['position_in_fixture'] || ENV['position'] || :before
|
7
|
+
options[:show_indexes] = ENV['show_indexes']
|
8
|
+
options[:simple_indexes] = ENV['simple_indexes']
|
9
|
+
options[:model_dir] = ENV['model_dir']
|
10
|
+
options[:include_version] = ENV['include_version']
|
11
|
+
options[:require] = ENV['require'].split(',') rescue []
|
12
|
+
AnnotateModels.do_annotations(options)
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Remove schema information from model and fixture files"
|
16
|
+
task :remove_annotation => :environment do
|
17
|
+
require 'annotate/annotate_models'
|
18
|
+
options={}
|
19
|
+
options[:model_dir] = ENV['model_dir']
|
20
|
+
AnnotateModels.remove_annotations(options)
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
RAILS_INITIALIZERS_PATH = File.join(RAILS_ROOT, "config", "initializers")
|
2
|
+
|
3
|
+
desc "Add automatic annotation to project to invoke annotate each time when server boots in development mode"
|
4
|
+
task :automatic_annotate => :environment do
|
5
|
+
unless File.exists?(File.join RAILS_INITIALIZERS_PATH, "automatic_annotation.rb")
|
6
|
+
f = File.open(File.join(RAILS_INITIALIZERS_PATH, "automatic_annotation.rb"), "w")
|
7
|
+
f.write <<-CODE
|
8
|
+
if RAILS_ENV == "development"
|
9
|
+
require 'annotate'
|
10
|
+
if Annotate.load_tasks
|
11
|
+
Rake::Task[:annotate_models].invoke
|
12
|
+
end
|
13
|
+
end
|
14
|
+
CODE
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Remove automatic annotation"
|
19
|
+
task :remove_automatic_annotation => :environment do
|
20
|
+
if File.exists?(f = File.join(RAILS_INITIALIZERS_PATH, "automatic_annotation.rb"))
|
21
|
+
File.delete(f)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
require 'annotate/annotate_models'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'activesupport'
|
5
|
+
|
6
|
+
describe AnnotateModels do
|
7
|
+
|
8
|
+
def mock_klass(stubs={})
|
9
|
+
@mock_file ||= mock("Klass", stubs)
|
10
|
+
end
|
11
|
+
|
12
|
+
def mock_column(stubs={})
|
13
|
+
@mock_column ||= mock("Column", stubs)
|
14
|
+
end
|
15
|
+
|
16
|
+
it { AnnotateModels.quote(nil).should eql("NULL") }
|
17
|
+
it { AnnotateModels.quote(true).should eql("TRUE") }
|
18
|
+
it { AnnotateModels.quote(false).should eql("FALSE") }
|
19
|
+
it { AnnotateModels.quote(25).should eql("25") }
|
20
|
+
it { AnnotateModels.quote(25.6).should eql("25.6") }
|
21
|
+
it { AnnotateModels.quote(1e-20).should eql("1.0e-20") }
|
22
|
+
|
23
|
+
it "should get schema info" do
|
24
|
+
|
25
|
+
AnnotateModels.get_schema_info(mock_klass(
|
26
|
+
:connection => mock("Conn", :indexes => []),
|
27
|
+
:table_name => "users",
|
28
|
+
:primary_key => "id",
|
29
|
+
:column_names => ["id","login"],
|
30
|
+
:columns => [
|
31
|
+
mock_column(:type => "integer", :default => nil, :null => false, :name => "id", :limit => nil),
|
32
|
+
mock_column(:type => "string", :default => nil, :null => false, :name => "name", :limit => 50)
|
33
|
+
]), "Schema Info").should eql(<<-EOS)
|
34
|
+
# Schema Info
|
35
|
+
#
|
36
|
+
# Table name: users
|
37
|
+
#
|
38
|
+
# id :integer not null, primary key
|
39
|
+
# id :integer not null, primary key
|
40
|
+
#
|
41
|
+
|
42
|
+
EOS
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#get_model_class" do
|
47
|
+
module ::ActiveRecord
|
48
|
+
class Base
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def create(file, body="hi")
|
53
|
+
File.open(@dir + '/' + file, "w") do |f|
|
54
|
+
f.puts(body)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
before :all do
|
59
|
+
require "tmpdir"
|
60
|
+
@dir = Dir.tmpdir + "/#{Time.now.to_i}" + "/annotate_models"
|
61
|
+
FileUtils.mkdir_p(@dir)
|
62
|
+
AnnotateModels.model_dir = @dir
|
63
|
+
create('foo.rb', <<-EOS)
|
64
|
+
class Foo < ActiveRecord::Base
|
65
|
+
end
|
66
|
+
EOS
|
67
|
+
create('foo_with_macro.rb', <<-EOS)
|
68
|
+
class FooWithMacro < ActiveRecord::Base
|
69
|
+
acts_as_awesome :yah
|
70
|
+
end
|
71
|
+
EOS
|
72
|
+
end
|
73
|
+
it "should work" do
|
74
|
+
klass = AnnotateModels.get_model_class("foo.rb")
|
75
|
+
klass.name.should == "Foo"
|
76
|
+
end
|
77
|
+
it "should not care about unknown macros" do
|
78
|
+
klass = AnnotateModels.get_model_class("foo_with_macro.rb")
|
79
|
+
klass.name.should == "FooWithMacro"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
require 'annotate/annotate_routes'
|
3
|
+
|
4
|
+
describe AnnotateRoutes do
|
5
|
+
|
6
|
+
def mock_file(stubs={})
|
7
|
+
@mock_file ||= mock(File, stubs)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "Annotate Job" do
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
File.should_receive(:join).with("config", "routes.rb").and_return("config/routes.rb")
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should check if routes.rb exists" do
|
17
|
+
File.should_receive(:exists?).with("config/routes.rb").and_return(false)
|
18
|
+
AnnotateRoutes.should_receive(:puts).with("Can`t find routes.rb")
|
19
|
+
AnnotateRoutes.do_annotate
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "When Annotating" do
|
23
|
+
|
24
|
+
before(:each) do
|
25
|
+
File.should_receive(:exists?).with("config/routes.rb").and_return(true)
|
26
|
+
AnnotateRoutes.should_receive(:`).with("rake routes").and_return("bad line\ngood line")
|
27
|
+
File.should_receive(:open).with("config/routes.rb", "wb").and_yield(mock_file)
|
28
|
+
AnnotateRoutes.should_receive(:puts).with("Route file annotated.")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should annotate and add a newline!" do
|
32
|
+
File.should_receive(:read).with("config/routes.rb").and_return("ActionController::Routing...\nfoo")
|
33
|
+
@mock_file.should_receive(:puts).with(/ActionController::Routing...\nfoo\n#== Route Map\n# Generated on .*\n#\n# good line/)
|
34
|
+
AnnotateRoutes.do_annotate
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should not add a newline if there are empty lines" do
|
38
|
+
File.should_receive(:read).with("config/routes.rb").and_return("ActionController::Routing...\nfoo\n")
|
39
|
+
@mock_file.should_receive(:puts).with(/ActionController::Routing...\nfoo\n#== Route Map\n# Generated on .*\n#\n# good line/)
|
40
|
+
AnnotateRoutes.do_annotate
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: selleo-annotate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.3.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cuong Tran
|
8
|
+
- Marcos Piccinini
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-10-23 00:00:00 +02:00
|
14
|
+
default_executable: annotate
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description:
|
18
|
+
email: x@selleo.com
|
19
|
+
executables:
|
20
|
+
- annotate
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files:
|
24
|
+
- README.rdoc
|
25
|
+
files:
|
26
|
+
- .gitignore
|
27
|
+
- History.txt
|
28
|
+
- README.rdoc
|
29
|
+
- Rakefile
|
30
|
+
- VERSION
|
31
|
+
- bin/annotate
|
32
|
+
- lib/annotate.rb
|
33
|
+
- lib/annotate/annotate_models.rb
|
34
|
+
- lib/annotate/annotate_routes.rb
|
35
|
+
- lib/tasks/annotate_models.rake
|
36
|
+
- lib/tasks/annotate_routes.rake
|
37
|
+
- lib/tasks/automatic_annotation.rake
|
38
|
+
- spec/annotate/annotate_models_spec.rb
|
39
|
+
- spec/annotate/annotate_routes_spec.rb
|
40
|
+
- spec/annotate_spec.rb
|
41
|
+
- spec/spec.opts
|
42
|
+
- spec/spec_helper.rb
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://github.com/selleo/annotate
|
45
|
+
licenses: []
|
46
|
+
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options:
|
49
|
+
- --charset=UTF-8
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.3.5
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: Annotates Rails Models, routes, automatic annotation and others
|
71
|
+
test_files:
|
72
|
+
- spec/annotate_spec.rb
|
73
|
+
- spec/annotate/annotate_routes_spec.rb
|
74
|
+
- spec/annotate/annotate_models_spec.rb
|
75
|
+
- spec/spec_helper.rb
|