annotate 2.4.0

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/History.txt ADDED
@@ -0,0 +1,37 @@
1
+ == 2.1 2009-10-18
2
+
3
+ * New options
4
+ * -R to require additional files before loading the models
5
+ * -i to show database indexes in annotations
6
+ * -e to exclude annotating tests or fixtures
7
+ * -m to include the migration version number in the annotation
8
+ * --model-dir to annotate model files stored a different place than app/models
9
+ * Ignore unknown macros ('acts_as_whatever')
10
+
11
+ == 2.0 2009-02-03
12
+
13
+ * Add annotate_models plugin fork additions
14
+ * Annotates Rspec and Test Unit models
15
+ * Annotates Object Daddy exemplars
16
+ * Annotates geometrical columns
17
+ * Add AnnotateRoutes rake task
18
+ * Up gem structure to newgem defaults
19
+
20
+ == 1.0.4 2008-09-04
21
+
22
+ * Only update modified models since last run, thanks to sant0sk1
23
+
24
+ == 1.0.3 2008-05-02
25
+
26
+ * Add misc changes from Dustin Sallings and Henrik N
27
+ * Remove trailing whitespace
28
+ * More intuitive info messages
29
+ * Update README file with update-to-date example
30
+
31
+ == 1.0.2 2008-03-22
32
+
33
+ * Add contributions from Michael Bumann (http://github.com/bumi)
34
+ * added an option "position" to choose to put the annotation,
35
+ * spec/fixtures now also get annotated
36
+ * added a task to remove the annotations
37
+ * these options can be specified from command line as -d and -p [before|after]
data/README.rdoc ADDED
@@ -0,0 +1,138 @@
1
+ == Annotate (aka AnnotateModels)
2
+
3
+ Add a comment summarizing the current schema to the top or bottom of each of your...
4
+
5
+ * ActiveRecord models
6
+ * Fixture files
7
+ * Tests and Specs
8
+ * Object Daddy exemplars
9
+ * Machinist blueprints
10
+
11
+ The schema comment looks like this:
12
+
13
+ # == Schema Info
14
+ #
15
+ # Table name: line_items
16
+ #
17
+ # id :integer(11) not null, primary key
18
+ # quantity :integer(11) not null
19
+ # product_id :integer(11) not null
20
+ # unit_price :float
21
+ # order_id :integer(11)
22
+ #
23
+
24
+ class LineItem < ActiveRecord::Base
25
+ belongs_to :product
26
+ . . .
27
+
28
+ It also annotates geometrical columns, geom type and srid, when using SpatialAdapter or PostgisAdapter:
29
+
30
+ # == Schema Info
31
+ #
32
+ # Table name: trips
33
+ #
34
+ # local :geometry point, 4326
35
+ # path :geometry line_string, 4326
36
+
37
+ Also, if you pass the -r option, it'll annotate routes.rb with the output of "rake routes".
38
+
39
+ == INSTALL
40
+
41
+ From rubyforge:
42
+
43
+ sudo gem install annotate
44
+
45
+ From github:
46
+
47
+ git clone git://github.com/ctran/annotate_models.git annotate
48
+ cd annotate
49
+ rake gem
50
+ sudo gem install pkg/annotate-*.gem
51
+
52
+ == USAGE
53
+
54
+ To annotate all your models, tests, fixtures, etc.:
55
+
56
+ cd /path/to/app
57
+ annotate
58
+
59
+ To annotate your models and tests:
60
+
61
+ annotate --exclude fixtures
62
+
63
+ To annotate just your models:
64
+
65
+ annotate --exclude tests,fixtures
66
+
67
+ To annotate routes.rb:
68
+
69
+ annotate -r
70
+
71
+ To automatically annotate after running 'rake db:migrate':
72
+
73
+ [needs more clarity] unpack the gem into vendor/plugins, or maybe vendor/gems, or maybe just require tasks/migrate.rake.
74
+
75
+ If you install annotate_models as a plugin, it will automatically
76
+ adjust your <tt>rake db:migrate</tt> tasks so that they update the
77
+ annotations in your model files for you once the migration is
78
+ completed.
79
+
80
+ == OPTIONS
81
+
82
+ Usage: annotate [options] [model_file]*
83
+ -d, --delete Remove annotations from all model files
84
+ -p, --position [before|after] Place the annotations at the top (before) or the bottom (after) of the model file
85
+ -r, --routes Annotate routes.rb with the output of 'rake routes'
86
+ -v, --version Show the current version of this gem
87
+ -m, --show-migration Include the migration version number in the annotation
88
+ -i, --show-indexes List the table's database indexes in the annotation
89
+ -s, --simple-indexes Concat the column's related indexes in the annotation
90
+ --model-dir dir Annotate model files stored in dir rather than app/models
91
+ -R, --require path Additional files to require before loading models
92
+ -e, --exclude [tests,fixtures] Do not annotate fixtures, test files, or both
93
+
94
+
95
+ == WARNING
96
+
97
+ Note that this code will blow away the initial/final comment
98
+ block in your models if it looks like it was previously added
99
+ by annotate models, so you don't want to add additional text
100
+ to an automatically created comment block.
101
+
102
+ * * Back up your model files before using... * *
103
+
104
+ == LINKS
105
+
106
+ * Factory Girl => http://github.com/thoughtbot/factory_girl (NOT IMPLEMENTED)
107
+ * Object Daddy => http://github.com/flogic/object_daddy
108
+ * SpatialAdapter => http://github.com/pdeffendol/spatial_adapter
109
+ * PostgisAdapter => http://github.com/nofxx/postgis_adapter
110
+
111
+ == LICENSE:
112
+
113
+ Released under the same license as Ruby. No Support. No Warranty.
114
+
115
+ == AUTHOR:
116
+
117
+ Original code by: Dave Thomas -- Pragmatic Programmers, LLC
118
+ Overhauled by: Alex Chaffee
119
+ Gemmed by: Cuong Tran
120
+ Maintained by: Alex Chaffee and Cuong Tran
121
+
122
+ Modifications by:
123
+
124
+ - Alex Chaffee - http://github.com/alexch - alex@pivotallabs.com
125
+ - Cuong Tran - http://github.com/ctran - ctran@pragmaquest.com
126
+ - Jack Danger - http://github.com/JackDanger
127
+ - Michael Bumann - http://github.com/bumi
128
+ - Henrik Nyh - http://github.com/henrik
129
+ - Marcos Piccinini - http://github.com/nofxx
130
+ - Neal Clark - http://github.com/nclark
131
+ - Jacqui Maher - http://github.com/jacqui
132
+ - Nick Plante - http://github.com/zapnap - http://blog.zerosum.org
133
+ - Pedro Visintin - http://github.com/peterpunk - http://www.pedrovisintin.com
134
+ - Bob Potter - http://github.com/bpot
135
+ - Gavin Montague - http://github.com/govan/
136
+ - Alexander Semyonov - http://github.com/rotuka/
137
+
138
+ and many others that I may have missed to add.
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 2
3
+ :minor: 4
4
+ :patch: 0
data/bin/annotate ADDED
@@ -0,0 +1,70 @@
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] [model_file]*"
10
+
11
+ opts.on('-d', '--delete',
12
+ "Remove annotations from all model files") do
13
+ task = :remove_annotation
14
+ end
15
+
16
+ opts.on('-p', '--position [before|after]', ['before', 'after'],
17
+ "Place the annotations at the top (before) or the bottom (after) of the model file") do |p|
18
+ ENV['position'] = p
19
+ end
20
+
21
+ opts.on('-r', '--routes',
22
+ "Annotate routes.rb with the output of 'rake routes'") do
23
+ task = :annotate_routes
24
+ end
25
+
26
+ opts.on('-v', '--version',
27
+ "Show the current version of this gem") do
28
+ puts "annotate v#{Annotate.version}"; exit
29
+ end
30
+
31
+ opts.on('-m', '--show-migration',
32
+ "Include the migration version number in the annotation") do
33
+ ENV['include_version'] = "yes"
34
+ end
35
+
36
+ opts.on('-i', '--show-indexes',
37
+ "List the table's database indexes in the annotation") do
38
+ ENV['show_indexes'] = "yes"
39
+ end
40
+
41
+ opts.on('-s', '--simple-indexes',
42
+ "Concat the column's related indexes in the annotation") do
43
+ ENV['simple_indexes'] = "yes"
44
+ end
45
+
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
+
51
+ opts.on('-R', '--require path',
52
+ "Additional files to require before loading models") do |path|
53
+ if ENV['require']
54
+ ENV['require'] = ENV['require'] + ",#{path}"
55
+ else
56
+ ENV['require'] = path
57
+ end
58
+ end
59
+
60
+ opts.on('-e', '--exclude [tests,fixtures]', Array, "Do not annotate fixtures, test files, or both") do |exclusions|
61
+ exclusions.each { |exclusion| ENV["exclude_#{exclusion}"] = "yes" }
62
+ end
63
+
64
+ end.parse!
65
+
66
+ if Annotate.load_tasks
67
+ Rake::Task[task].invoke
68
+ else
69
+ STDERR.puts "Can't find Rakefile. Are we in a Rails folder?"
70
+ end
data/lib/annotate.rb ADDED
@@ -0,0 +1,24 @@
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.version
6
+ version_file = File.dirname(__FILE__) + "/../VERSION.yml"
7
+ if File.exist?(version_file)
8
+ config = YAML.load(File.read(version_file))
9
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
10
+ else
11
+ version = "0.0.0"
12
+ end
13
+ end
14
+
15
+ def self.load_tasks
16
+ if File.exists?('Rakefile')
17
+ load 'Rakefile'
18
+ Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')].each { |rake| load rake }
19
+ return true
20
+ else
21
+ return false
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,330 @@
1
+ module AnnotateModels
2
+ class << self
3
+ # Annotate Models plugin use this header
4
+ COMPAT_PREFIX = "== Schema Info"
5
+ PREFIX = "== Schema Information"
6
+
7
+ FIXTURE_DIRS = ["test/fixtures","spec/fixtures"]
8
+ # File.join for windows reverse bar compat?
9
+ # I dont use windows, can`t test
10
+ UNIT_TEST_DIR = File.join("test", "unit" )
11
+ SPEC_MODEL_DIR = File.join("spec", "models")
12
+ # Object Daddy http://github.com/flogic/object_daddy/tree/master
13
+ EXEMPLARS_TEST_DIR = File.join("test", "exemplars")
14
+ EXEMPLARS_SPEC_DIR = File.join("spec", "exemplars")
15
+ # Machinist http://github.com/notahat/machinist
16
+ BLUEPRINTS_DIR = File.join("test", "blueprints")
17
+
18
+ def model_dir
19
+ @model_dir || "app/models"
20
+ end
21
+
22
+ def model_dir=(dir)
23
+ @model_dir = dir
24
+ end
25
+
26
+ # Simple quoting for the default column value
27
+ def quote(value)
28
+ case value
29
+ when NilClass then "NULL"
30
+ when TrueClass then "TRUE"
31
+ when FalseClass then "FALSE"
32
+ when Float, Fixnum, Bignum then value.to_s
33
+ # BigDecimals need to be output in a non-normalized form and quoted.
34
+ when BigDecimal then value.to_s('F')
35
+ else
36
+ value.inspect
37
+ end
38
+ end
39
+
40
+ # Use the column information in an ActiveRecord class
41
+ # to create a comment block containing a line for
42
+ # each column. The line contains the column name,
43
+ # the type (and length), and any optional attributes
44
+ def get_schema_info(klass, header, options = {})
45
+ info = "# #{header}\n#\n"
46
+ info << "# Table name: #{klass.table_name}\n#\n"
47
+
48
+ max_size = klass.column_names.collect{|name| name.size}.max + 1
49
+ klass.columns.each do |col|
50
+ attrs = []
51
+ attrs << "default(#{quote(col.default)})" unless col.default.nil?
52
+ attrs << "not null" unless col.null
53
+ attrs << "primary key" if col.name == klass.primary_key
54
+
55
+ col_type = col.type.to_s
56
+ if col_type == "decimal"
57
+ col_type << "(#{col.precision}, #{col.scale})"
58
+ else
59
+ col_type << "(#{col.limit})" if col.limit
60
+ end
61
+
62
+ # Check out if we got a geometric column
63
+ # and print the type and SRID
64
+ if col.respond_to?(:geometry_type)
65
+ attrs << "#{col.geometry_type}, #{col.srid}"
66
+ end
67
+
68
+ # Check if the column has indices and print "indexed" if true
69
+ # If the indice include another colum, print it too.
70
+ if options[:simple_indexes] # Check out if this column is indexed
71
+ indices = klass.connection.indexes(klass.table_name)
72
+ if indices = indices.select { |ind| ind.columns.include? col.name }
73
+ indices.each do |ind|
74
+ ind = ind.columns.reject! { |i| i == col.name }
75
+ attrs << (ind.length == 0 ? "indexed" : "indexed => [#{ind.join(", ")}]")
76
+ end
77
+ end
78
+ end
79
+
80
+ info << sprintf("# %-#{max_size}.#{max_size}s:%-15.15s %s", col.name, col_type, attrs.join(", ")).rstrip + "\n"
81
+ end
82
+
83
+ if options[:show_indexes]
84
+ info << get_index_info(klass)
85
+ end
86
+
87
+ info << "#\n\n"
88
+ end
89
+
90
+ def get_index_info(klass)
91
+ index_info = "#\n# Indexes\n#\n"
92
+
93
+ indexes = klass.connection.indexes(klass.table_name)
94
+ return "" if indexes.empty?
95
+
96
+ max_size = indexes.collect{|index| index.name.size}.max + 1
97
+ indexes.each do |index|
98
+ index_info << sprintf("# %-#{max_size}.#{max_size}s %s %s", index.name, "(#{index.columns.join(",")})", index.unique ? "UNIQUE" : "").rstrip + "\n"
99
+ end
100
+ return index_info
101
+ end
102
+
103
+ # Add a schema block to a file. If the file already contains
104
+ # a schema info block (a comment starting with "== Schema Information"), check if it
105
+ # matches the block that is already there. If so, leave it be. If not, remove the old
106
+ # info block and write a new one.
107
+ # Returns true or false depending on whether the file was modified.
108
+ #
109
+ # === Options (opts)
110
+ # :position<Symbol>:: where to place the annotated section in fixture or model file,
111
+ # "before" or "after". Default is "before".
112
+ # :position_in_class<Symbol>:: where to place the annotated section in model file
113
+ # :position_in_fixture<Symbol>:: where to place the annotated section in fixture file
114
+ # :position_in_others<Symbol>:: where to place the annotated section in the rest of
115
+ # supported files
116
+ #
117
+ def annotate_one_file(file_name, info_block, options={})
118
+ if File.exist?(file_name)
119
+ old_content = File.read(file_name)
120
+
121
+ # Ignore the Schema version line because it changes with each migration
122
+ header = Regexp.new(/(^# Table name:.*?\n(#.*\n)*\n)/)
123
+ old_header = old_content.match(header).to_s
124
+ new_header = info_block.match(header).to_s
125
+
126
+ if old_header == new_header
127
+ false
128
+ else
129
+ # Remove old schema info
130
+ old_content.sub!(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n/, '')
131
+
132
+ # Write it back
133
+ new_content = options[:position] == 'before' ? (info_block + old_content) : (old_content + "\n" + info_block)
134
+
135
+ File.open(file_name, "wb") { |f| f.puts new_content }
136
+ true
137
+ end
138
+ end
139
+ end
140
+
141
+ def remove_annotation_of_file(file_name)
142
+ if File.exist?(file_name)
143
+ content = File.read(file_name)
144
+
145
+ content.sub!(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n/, '')
146
+
147
+ File.open(file_name, "wb") { |f| f.puts content }
148
+ end
149
+ end
150
+
151
+ # Given the name of an ActiveRecord class, create a schema
152
+ # info block (basically a comment containing information
153
+ # on the columns and their types) and put it at the front
154
+ # of the model and fixture source files.
155
+ # Returns true or false depending on whether the source
156
+ # files were modified.
157
+ def annotate(klass, file, header,options={})
158
+ info = get_schema_info(klass, header, options)
159
+ annotated = false
160
+ model_name = klass.name.underscore
161
+ model_file_name = File.join(model_dir, file)
162
+
163
+ if annotate_one_file(model_file_name, info, options_with_position(options, :position_in_class))
164
+ annotated = true
165
+ end
166
+
167
+ unless ENV['exclude_tests']
168
+ [
169
+ File.join(UNIT_TEST_DIR, "#{model_name}_test.rb"), # test
170
+ File.join(SPEC_MODEL_DIR, "#{model_name}_spec.rb"), # spec
171
+ ].each do |file|
172
+ # todo: add an option "position_in_test" -- or maybe just ask if anyone ever wants different positions for model vs. test vs. fixture
173
+ annotate_one_file(file, info, options_with_position(options, :position_in_fixture))
174
+ end
175
+ end
176
+
177
+ unless ENV['exclude_fixtures']
178
+ [
179
+ File.join(EXEMPLARS_TEST_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
180
+ File.join(EXEMPLARS_SPEC_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
181
+ File.join(BLUEPRINTS_DIR, "#{model_name}_blueprint.rb"), # Machinist Blueprints
182
+ ].each do |file|
183
+ annotate_one_file(file, info, options_with_position(options, :position_in_fixture))
184
+ end
185
+
186
+ FIXTURE_DIRS.each do |dir|
187
+ fixture_file_name = File.join(dir,klass.table_name + ".yml")
188
+ if File.exist?(fixture_file_name)
189
+ annotate_one_file(fixture_file_name, info, options_with_position(options, :position_in_fixture))
190
+ end
191
+ end
192
+ end
193
+
194
+ annotated
195
+ end
196
+
197
+ # position = :position_in_fixture or :position_in_class
198
+ def options_with_position(options, position_in)
199
+ options.merge(:position=>(options[position_in] || options[:position]))
200
+ end
201
+
202
+ # Return a list of the model files to annotate. If we have
203
+ # command line arguments, they're assumed to be either
204
+ # the underscore or CamelCase versions of model names.
205
+ # Otherwise we take all the model files in the
206
+ # model_dir directory.
207
+ def get_model_files
208
+ models = ARGV.dup
209
+ models.shift
210
+ models.reject!{|m| m.match(/^(.*)=/)}
211
+ if models.empty?
212
+ begin
213
+ Dir.chdir(model_dir) do
214
+ models = Dir["**/*.rb"]
215
+ end
216
+ rescue SystemCallError
217
+ puts "No models found in directory '#{model_dir}'."
218
+ puts "Either specify models on the command line, or use the --model-dir option."
219
+ puts "Call 'annotate --help' for more info."
220
+ exit 1;
221
+ end
222
+ end
223
+ models
224
+ end
225
+
226
+ # Retrieve the classes belonging to the model names we're asked to process
227
+ # Check for namespaced models in subdirectories as well as models
228
+ # in subdirectories without namespacing.
229
+ def get_model_class(file)
230
+ require File.expand_path("#{model_dir}/#{file}") # this is for non-rails projects, which don't get Rails auto-require magic
231
+ model = file.gsub(/\.rb$/, '').camelize
232
+ parts = model.split('::')
233
+ begin
234
+ parts.inject(Object) {|klass, part| klass.const_get(part) }
235
+ rescue LoadError, NameError
236
+ Object.const_get(parts.last)
237
+ end
238
+ end
239
+
240
+ # We're passed a name of things that might be
241
+ # ActiveRecord models. If we can find the class, and
242
+ # if its a subclass of ActiveRecord::Base,
243
+ # then pass it to the associated block
244
+ def do_annotations(options={})
245
+ if options[:require]
246
+ options[:require].each do |path|
247
+ require path
248
+ end
249
+ end
250
+
251
+ header = PREFIX.dup
252
+
253
+ if options[:include_version]
254
+ version = ActiveRecord::Migrator.current_version rescue 0
255
+ if version > 0
256
+ header << "\n# Schema version: #{version}"
257
+ end
258
+ end
259
+
260
+ if options[:model_dir]
261
+ self.model_dir = options[:model_dir]
262
+ end
263
+
264
+ annotated = []
265
+ get_model_files.each do |file|
266
+ begin
267
+ klass = get_model_class(file)
268
+ if klass < ActiveRecord::Base && !klass.abstract_class?
269
+ if annotate(klass, file, header, options)
270
+ annotated << klass
271
+ end
272
+ end
273
+ rescue Exception => e
274
+ puts "Unable to annotate #{file}: #{e.inspect}"
275
+ puts ""
276
+ # todo: check if all backtrace lines are in "gems" -- if so, it's an annotate bug, so print the whole stack trace.
277
+ # puts e.backtrace.join("\n\t")
278
+ end
279
+ end
280
+ if annotated.empty?
281
+ puts "Nothing annotated."
282
+ else
283
+ puts "Annotated (#{annotated.length}): #{annotated.join(', ')}"
284
+ end
285
+ end
286
+
287
+ def remove_annotations(options={})
288
+ if options[:model_dir]
289
+ puts "removing"
290
+ self.model_dir = options[:model_dir]
291
+ end
292
+ deannotated = []
293
+ get_model_files.each do |file|
294
+ begin
295
+ klass = get_model_class(file)
296
+ if klass < ActiveRecord::Base && !klass.abstract_class?
297
+ deannotated << klass
298
+
299
+ model_file_name = File.join(model_dir, file)
300
+ remove_annotation_of_file(model_file_name)
301
+
302
+ FIXTURE_DIRS.each do |dir|
303
+ fixture_file_name = File.join(dir,klass.table_name + ".yml")
304
+ remove_annotation_of_file(fixture_file_name) if File.exist?(fixture_file_name)
305
+ end
306
+
307
+ [ File.join(UNIT_TEST_DIR, "#{klass.name.underscore}_test.rb"),
308
+ File.join(SPEC_MODEL_DIR,"#{klass.name.underscore}_spec.rb")].each do |file|
309
+ remove_annotation_of_file(file) if File.exist?(file)
310
+ end
311
+
312
+ end
313
+ rescue Exception => e
314
+ puts "Unable to annotate #{file}: #{e.message}"
315
+ end
316
+ end
317
+ puts "Removed annotation from: #{deannotated.join(', ')}"
318
+ end
319
+ end
320
+ end
321
+
322
+ # monkey patches
323
+
324
+ module ::ActiveRecord
325
+ class Base
326
+ def self.method_missing(name, *args)
327
+ # ignore this, so unknown/unloaded macros won't cause parsing to fail
328
+ end
329
+ end
330
+ 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'] ? ENV['require'].split(',') : []
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,5 @@
1
+ desc "Prepends the route map to the top of routes.rb"
2
+ task :annotate_routes do
3
+ require 'annotate/annotate_routes'
4
+ AnnotateRoutes.do_annotate
5
+ 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
@@ -0,0 +1,9 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe Annotate do
4
+
5
+ it "should have a version" do
6
+ Annotate.version.should be_instance_of(String)
7
+ end
8
+
9
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'annotate'
@@ -0,0 +1,33 @@
1
+ # These tasks are added to the project if you install annotate as a Rails plugin.
2
+ # (They are not used to build annotate itself.)
3
+
4
+ # Append annotations to Rake tasks for ActiveRecord, so annotate automatically gets
5
+ # run after doing db:migrate.
6
+ # Unfortunately it relies on ENV for options; it'd be nice to be able to set options
7
+ # in a per-project config file so this task can read them.
8
+ namespace :db do
9
+ task :migrate do
10
+ Annotate::Migration.update_annotations
11
+ end
12
+
13
+ namespace :migrate do
14
+ [:up, :down, :reset, :redo].each do |t|
15
+ task t do
16
+ Annotate::Migration.update_annotations
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ module Annotate
23
+ class Migration
24
+ @@working = false
25
+
26
+ def self.update_annotations
27
+ unless @@working
28
+ @@working = true
29
+ Rake::Task['annotate_models'].invoke
30
+ end
31
+ end
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: annotate
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Cuong Tran
8
+ - Alex Chaffee
9
+ - Marcos Piccinini
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2009-12-13 00:00:00 -08:00
15
+ default_executable: annotate
16
+ dependencies: []
17
+
18
+ description: Annotates Rails Models, routes, fixtures, and others based on the database schema.
19
+ email:
20
+ - alex@stinky.com
21
+ - ctran@pragmaquest.com
22
+ - x@nofxx.com
23
+ executables:
24
+ - annotate
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README.rdoc
29
+ files:
30
+ - History.txt
31
+ - README.rdoc
32
+ - VERSION.yml
33
+ - bin/annotate
34
+ - lib/annotate.rb
35
+ - lib/annotate/annotate_models.rb
36
+ - lib/annotate/annotate_routes.rb
37
+ - lib/tasks/annotate_models.rake
38
+ - lib/tasks/annotate_routes.rake
39
+ - spec/annotate/annotate_models_spec.rb
40
+ - spec/annotate/annotate_routes_spec.rb
41
+ - spec/annotate_spec.rb
42
+ - spec/spec.opts
43
+ - spec/spec_helper.rb
44
+ - tasks/migrate.rake
45
+ has_rdoc: true
46
+ homepage: http://github.com/ctran/annotate
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --charset=UTF-8
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project: annotate
69
+ rubygems_version: 1.3.5
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Annotates Rails Models, routes, fixtures, and others based on the database schema.
73
+ test_files:
74
+ - spec/annotate/annotate_models_spec.rb
75
+ - spec/annotate/annotate_routes_spec.rb
76
+ - spec/annotate_spec.rb
77
+ - spec/spec_helper.rb