annotate 2.4.0

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