codeape-annotate 2.0.0.20090212001

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.
@@ -0,0 +1,28 @@
1
+ == 2.0 2009-02-03
2
+
3
+ * Add annotate_models plugin fork additions
4
+ * Annotates Rspec and Test Unit models
5
+ * Annotates Object Daddy exemplars
6
+ * Annotates geometrical columns
7
+ * Add AnnotateRoutes rake task
8
+ * Up gem structure to newgem defaults
9
+
10
+ == 1.0.4 2008-09-04
11
+
12
+ * Only update modified models since last run, thanks to sant0sk1
13
+
14
+ == 1.0.3 2008-05-02
15
+
16
+ * Add misc changes from Dustin Sallings and Henrik N
17
+ * Remove trailing whitespace
18
+ * More intuitive info messages
19
+ * Update README file with update-to-date example
20
+
21
+ == 1.0.2 2008-03-22
22
+
23
+ * Add contributions from Michael Bumann (http://github.com/bumi)
24
+ * added an option "position" to choose to put the annotation,
25
+ * spec/fixtures now also get annotated
26
+ * added a task to remove the annotations
27
+ * these options can be specified from command line as -d and -p [before|after]
28
+
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2008 Dave Thomas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,22 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ annotate_models.gemspec
7
+ bin/annotate
8
+ lib/annotate.rb
9
+ lib/annotate/annotate_models.rb
10
+ lib/annotate/annotate_routes.rb
11
+ lib/tasks/annotate_models.rake
12
+ lib/tasks/annotate_routes.rake
13
+ script/console
14
+ script/destroy
15
+ script/generate
16
+ setup.rb
17
+ spec/annotate/annotate_models_spec.rb
18
+ spec/annotate/annotate_routes_spec.rb
19
+ spec/annotate_spec.rb
20
+ spec/spec.opts
21
+ spec/spec_helper.rb
22
+ tasks/rspec.rake
@@ -0,0 +1,95 @@
1
+ == AnnotateModels
2
+
3
+ Add a comment summarizing the current schema to the bottom of each
4
+ ActiveRecord model, fixture file.
5
+
6
+ If you are using Object Daddy, it`ll annotate your example files too.
7
+
8
+ # == Schema Info
9
+ #
10
+ # Table name: line_items
11
+ #
12
+ # id :integer(11) not null, primary key
13
+ # quantity :integer(11) not null
14
+ # product_id :integer(11) not null
15
+ # unit_price :float
16
+ # order_id :integer(11)
17
+ #
18
+
19
+ class LineItem < ActiveRecord::Base
20
+ belongs_to :product
21
+ . . .
22
+
23
+ Annotates geometrical columns, geom type and srid, when using SpatialAdapter or PostgisAdapter:
24
+
25
+ # == Schema Info
26
+ #
27
+ # Table name: trips
28
+ #
29
+ # local :geometry point, 4326
30
+ # path :geometry line_string, 4326
31
+
32
+ == Warning
33
+
34
+ Note that this code will blow away the initial/final comment
35
+ block in your models if it looks like it was previously added
36
+ by annotate models, so you don't want to add additional text
37
+ to an automatically created comment block.
38
+
39
+ * * Back up your model files before using... * *
40
+
41
+ == Install
42
+
43
+ From rubyforge:
44
+
45
+ sudo gem install annotate
46
+
47
+ From github:
48
+
49
+ gem sources -a http://gems.github.com
50
+ sudo gem install ctran-annotate
51
+
52
+ == Usage
53
+
54
+ To annotate all your models:
55
+
56
+ cd /path/to/app
57
+ annotate
58
+
59
+ To annotate routes.rb:
60
+
61
+ annotate -r
62
+
63
+ More:
64
+
65
+ annotate -h
66
+
67
+ Add annotation at the beginning or end of the file:
68
+
69
+ annotate -p [before|after]
70
+
71
+ Remove annotations:
72
+
73
+ annotate -d
74
+
75
+ == LICENSE:
76
+
77
+ Released under the same license as Ruby. No Support. No Warranty.
78
+
79
+ == Author:
80
+
81
+ Original code by:
82
+
83
+ Dave Thomas -- Pragmatic Programmers, LLC
84
+
85
+ Modifications by:
86
+
87
+ - alex@pivotallabs.com
88
+ - Cuong Tran - http://github.com/ctran
89
+ - Jack Danger - http://github.com/JackDanger
90
+ - Michael Bumann - http://github.com/bumi
91
+ - Henrik Nyh - http://github.com/henrik
92
+ - Marcos Piccinini - http://github.com/nofxx
93
+
94
+ and many others that I may have missed to add.
95
+
@@ -0,0 +1,28 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require 'lib/annotate'
3
+
4
+ # Generate all the Rake tasks
5
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
+ $hoe = Hoe.new('annotate', Annotate::VERSION) do |p|
7
+ p.developer('Cuong Tran', 'ctran@pragmaquest.com')
8
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
9
+ p.rubyforge_name = 'annotate-models'
10
+ p.url = "http://github.com/ctran/annotate_models"
11
+ p.summary = "Annotates Rails Models, routes, and others"
12
+ p.description = "Annotates Rails Models, routes, and others"
13
+
14
+ p.extra_dev_deps = [
15
+ ['newgem', ">= #{::Newgem::VERSION}"]
16
+ ]
17
+
18
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
19
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
20
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
21
+ p.rsync_args = '-av --delete --ignore-errors'
22
+ end
23
+
24
+ require 'newgem/tasks'
25
+ Dir['tasks/**/*.rake'].each { |t| load t }
26
+
27
+ # TODO - want other tests/tasks run by default? Add them to the list
28
+ # task :default => [:spec, :features]
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{annotate}
5
+ s.version = "2.0.0.20090212001"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Dan Cheail", "Cuong Tran"]
9
+ s.date = %q{2009-02-03}
10
+ s.default_executable = %q{annotate}
11
+ s.description = %q{Annotates Rails Models, routes, and others}
12
+ s.email = ["ctran@pragmaquest.com"]
13
+ s.executables = ["annotate"]
14
+ s.extra_rdoc_files = ["History.txt", "License.txt", "Manifest.txt", "README.rdoc"]
15
+ s.files = ["History.txt", "License.txt", "Manifest.txt", "README.rdoc", "Rakefile", "annotate_models.gemspec", "bin/annotate", "lib/annotate.rb", "lib/annotate/annotate_models.rb", "lib/annotate/annotate_routes.rb", "lib/tasks/annotate_models.rake", "lib/tasks/annotate_routes.rake", "script/console", "script/destroy", "script/generate", "setup.rb", "spec/annotate/annotate_models_spec.rb", "spec/annotate/annotate_routes_spec.rb", "spec/annotate_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/rspec.rake"]
16
+ s.has_rdoc = true
17
+ s.homepage = %q{http://github.com/ctran/annotate_models}
18
+ s.rdoc_options = ["--main", "README.rdoc"]
19
+ s.require_paths = ["lib"]
20
+ s.rubyforge_project = %q{annotate}
21
+ s.rubygems_version = %q{1.3.1}
22
+ s.summary = %q{Annotates Rails Models, routes, and others}
23
+
24
+ if s.respond_to? :specification_version then
25
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
26
+ s.specification_version = 2
27
+
28
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
+ s.add_development_dependency(%q<newgem>, [">= 1.2.3"])
30
+ s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
31
+ else
32
+ s.add_dependency(%q<newgem>, [">= 1.2.3"])
33
+ s.add_dependency(%q<hoe>, [">= 1.8.0"])
34
+ end
35
+ else
36
+ s.add_dependency(%q<newgem>, [">= 1.2.3"])
37
+ s.add_dependency(%q<hoe>, [">= 1.8.0"])
38
+ end
39
+ end
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'annotate'
5
+
6
+ task = :annotate_models
7
+
8
+ OptionParser.new do |opts|
9
+ opts.banner = "Usage: annotate [options]"
10
+ opts.on('-d', '--delete') { task = :remove_annotation }
11
+ opts.on('-p', '--position [before|after]', ['before', 'after']) { |p| ENV['position'] = p }
12
+ opts.on('-r', '--routes') { task = :annotate_routes }
13
+ opts.on('-v', '--version') { puts "Annotate v#{Annotate::VERSION}"; exit }
14
+ opts.on('-m', '--show-migration') { ENV['include_version'] = "yes" }
15
+ end.parse!
16
+
17
+ begin
18
+ Rake::Task[task].invoke
19
+ #TODO: rescue only rake error
20
+ rescue NameError => e
21
+ puts "Can`t find Rake. Are we in a Rails folder?"
22
+ end
@@ -0,0 +1,14 @@
1
+ unless defined?(Annotate)
2
+ $:.unshift(File.dirname(__FILE__))
3
+
4
+ module Annotate
5
+ VERSION = '2.0.1'
6
+ end
7
+
8
+ begin
9
+ load 'Rakefile'
10
+ Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')].each { |rake| load rake }
11
+ rescue LoadError => e
12
+ nil
13
+ end
14
+ end
@@ -0,0 +1,222 @@
1
+ module AnnotateModels
2
+ class << self
3
+ # Annotate Models plugin use this header
4
+ COMPAT_PREFIX = "== Schema Info"
5
+ PREFIX = "== Schema Information"
6
+
7
+ MODEL_DIR = "app/models"
8
+ FIXTURE_DIRS = ["test/fixtures","spec/fixtures"]
9
+ # File.join for windows reverse bar compat?
10
+ # I dont use windows, can`t test
11
+ UNIT_TEST_DIR = File.join("test", "unit" )
12
+ SPEC_MODEL_DIR = File.join("spec", "models")
13
+ # Object Daddy http://github.com/flogic/object_daddy/tree/master
14
+ EXEMPLARS_DIR = File.join("spec", "exemplars")
15
+
16
+
17
+ # Simple quoting for the default column value
18
+ def quote(value)
19
+ case value
20
+ when NilClass then "NULL"
21
+ when TrueClass then "TRUE"
22
+ when FalseClass then "FALSE"
23
+ when Float, Fixnum, Bignum then value.to_s
24
+ # BigDecimals need to be output in a non-normalized form and quoted.
25
+ when BigDecimal then value.to_s('F')
26
+ else
27
+ value.inspect
28
+ end
29
+ end
30
+
31
+ # Use the column information in an ActiveRecord class
32
+ # to create a comment block containing a line for
33
+ # each column. The line contains the column name,
34
+ # the type (and length), and any optional attributes
35
+ def get_schema_info(klass, header)
36
+ info = "# #{header}\n#\n"
37
+ info << "# Table name: #{klass.table_name}\n#\n"
38
+
39
+ max_size = klass.column_names.collect{|name| name.size}.max + 1
40
+ klass.columns.each do |col|
41
+ attrs = []
42
+ attrs << "default(#{quote(col.default)})" if col.default
43
+ attrs << "not null" unless col.null
44
+ attrs << "primary key" if col.name == klass.primary_key
45
+
46
+ col_type = col.type.to_s
47
+ if col_type == "decimal"
48
+ col_type << "(#{col.precision}, #{col.scale})"
49
+ else
50
+ col_type << "(#{col.limit})" if col.limit
51
+ end
52
+
53
+ # Check out if we got a geometric column
54
+ # and print the type and SRID
55
+ if col.respond_to?(:geometry_type)
56
+ attrs << "#{col.geometry_type}, #{col.srid}"
57
+ end
58
+
59
+ info << sprintf("# %-#{max_size}.#{max_size}s:%-15.15s %s", col.name, col_type, attrs.join(", ")).rstrip + "\n"
60
+ end
61
+
62
+ info << "#\n\n"
63
+ end
64
+
65
+ # Add a schema block to a file. If the file already contains
66
+ # a schema info block (a comment starting with "== Schema Information"), check if it
67
+ # matches the block that is already there. If so, leave it be. If not, remove the old
68
+ # info block and write a new one.
69
+ # Returns true or false depending on whether the file was modified.
70
+ #
71
+ # === Options (opts)
72
+ # :position<Symbol>:: where to place the annotated section in fixture or model file,
73
+ # "before" or "after". Default is "before".
74
+ # :position_in_class<Symbol>:: where to place the annotated section in model file
75
+ # :position_in_fixture<Symbol>:: where to place the annotated section in fixture file
76
+ #
77
+ def annotate_one_file(file_name, info_block, options={})
78
+ if File.exist?(file_name)
79
+ old_content = File.read(file_name)
80
+
81
+ # Ignore the Schema version line because it changes with each migration
82
+ header = Regexp.new(/(^# Table name:.*?\n(#.*\n)*\n)/)
83
+ old_header = old_content.match(header).to_s
84
+ new_header = info_block.match(header).to_s
85
+
86
+ if old_header == new_header
87
+ false
88
+ else
89
+ # Remove old schema info
90
+ old_content.sub!(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n/, '')
91
+
92
+ # Write it back
93
+ new_content = ((options[:position] || :before).to_sym == :before) ? (info_block + old_content) : (old_content + "\n" + info_block)
94
+
95
+ File.open(file_name, "wb") { |f| f.puts new_content }
96
+ true
97
+ end
98
+ end
99
+ end
100
+
101
+ def remove_annotation_of_file(file_name)
102
+ if File.exist?(file_name)
103
+ content = File.read(file_name)
104
+
105
+ content.sub!(/^# #{COMPAT_PREFIX}.*?\n(#.*\n)*\n/, '')
106
+
107
+ File.open(file_name, "wb") { |f| f.puts content }
108
+ end
109
+ end
110
+
111
+ # Given the name of an ActiveRecord class, create a schema
112
+ # info block (basically a comment containing information
113
+ # on the columns and their types) and put it at the front
114
+ # of the model and fixture source files.
115
+ # Returns true or false depending on whether the source
116
+ # files were modified.
117
+
118
+ def annotate(klass, file, header,options={})
119
+ info = get_schema_info(klass, header)
120
+ annotated = false
121
+ model_name = klass.name.underscore
122
+ model_file_name = File.join(MODEL_DIR, file)
123
+ if annotate_one_file(model_file_name, info, options.merge(
124
+ :position=>(options[:position_in_class] || options[:position])))
125
+ annotated = true
126
+ end
127
+
128
+ [
129
+ File.join(UNIT_TEST_DIR, "#{model_name}_test.rb"), # test
130
+ File.join(SPEC_MODEL_DIR, "#{model_name}_spec.rb"), # spec
131
+ File.join(EXEMPLARS_DIR, "#{model_name}_exemplar.rb"), # Object Daddy
132
+ ].each { |file| annotate_one_file(file, info) }
133
+
134
+ FIXTURE_DIRS.each do |dir|
135
+ fixture_file_name = File.join(dir,klass.table_name + ".yml")
136
+ annotate_one_file(fixture_file_name, info, options.merge(:position=>(options[:position_in_fixture] || options[:position]))) if File.exist?(fixture_file_name)
137
+ end
138
+ annotated
139
+ end
140
+
141
+ # Return a list of the model files to annotate. If we have
142
+ # command line arguments, they're assumed to be either
143
+ # the underscore or CamelCase versions of model names.
144
+ # Otherwise we take all the model files in the
145
+ # app/models directory.
146
+ def get_model_files
147
+ models = ARGV.dup
148
+ models.shift
149
+ models.reject!{|m| m.starts_with?("position=")}
150
+ if models.empty?
151
+ Dir.chdir(MODEL_DIR) do
152
+ models = Dir["**/*.rb"]
153
+ end
154
+ end
155
+ models
156
+ end
157
+
158
+ # Retrieve the classes belonging to the model names we're asked to process
159
+ # Check for namespaced models in subdirectories as well as models
160
+ # in subdirectories without namespacing.
161
+ def get_model_class(file)
162
+ file.gsub(/\.rb$/, '').camelize.constantize
163
+ end
164
+
165
+ # We're passed a name of things that might be
166
+ # ActiveRecord models. If we can find the class, and
167
+ # if its a subclass of ActiveRecord::Base,
168
+ # then pas it to the associated block
169
+ def do_annotations(options={})
170
+ header = PREFIX.dup
171
+
172
+ if options[:include_version]
173
+ version = ActiveRecord::Migrator.current_version rescue 0
174
+ if version > 0
175
+ header << "\n# Schema version: #{version}"
176
+ end
177
+ end
178
+
179
+ annotated = []
180
+ get_model_files.each do |file|
181
+ begin
182
+ klass = get_model_class(file)
183
+ if klass < ActiveRecord::Base && !klass.abstract_class?
184
+ if annotate(klass, file, header,options)
185
+ annotated << klass
186
+ end
187
+ end
188
+ rescue Exception => e
189
+ puts "Unable to annotate #{file}: #{e.message} (#{e.backtrace.first})"
190
+ end
191
+ end
192
+ if annotated.empty?
193
+ puts "Nothing annotated!"
194
+ else
195
+ puts "Annotated (#{annotated.length}): #{annotated.join(', ')}"
196
+ end
197
+ end
198
+
199
+ def remove_annotations
200
+ deannotated = []
201
+ get_model_files.each do |file|
202
+ begin
203
+ klass = get_model_class(file)
204
+ if klass < ActiveRecord::Base && !klass.abstract_class?
205
+ deannotated << klass
206
+
207
+ model_file_name = File.join(MODEL_DIR, file)
208
+ remove_annotation_of_file(model_file_name)
209
+
210
+ FIXTURE_DIRS.each do |dir|
211
+ fixture_file_name = File.join(dir,klass.table_name + ".yml")
212
+ remove_annotation_of_file(fixture_file_name) if File.exist?(fixture_file_name)
213
+ end
214
+ end
215
+ rescue Exception => e
216
+ puts "Unable to annotate #{file}: #{e.message}"
217
+ end
218
+ end
219
+ puts "Removed annotation from: #{deannotated.join(', ')}"
220
+ end
221
+ end
222
+ end