miyucy-annotate 2.4.3

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