migration_comments 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .idea/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in migration_comments.gemspec
4
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,81 @@
1
+ = MigrationComments
2
+
3
+ Comments for your migrations
4
+
5
+ Tested on Ruby 1.8.6 using Rails 2.3.x.
6
+
7
+ == Why?
8
+
9
+ Migrations are wonderful. They handle all your schema changes, and in a pinch they can bring
10
+ any database up to speed. However, database schemas can change rapidly while a project is
11
+ maturing, and it can be difficult to know (or remember) the purpose for each table and field.
12
+ As such, they deserve to be commented. These comments should be available for display wherever
13
+ those fields are found.
14
+
15
+ == Solution!
16
+
17
+ Using MigrationComments, you can simply add comments during your migrations. Or if you already
18
+ have existing data structures, just add the comments afterwards in a separate migration. And of
19
+ course you can always modify and delete these comments in later migrations.
20
+
21
+ So where are these comments used? Firstly, they will be included in your schema.rb dump which
22
+ is where your IDE (e.g. RubyMine) should be learning about your model structure. This means that
23
+ they'll be available at any point in your project. Additionally, if you are using the 'annotate'
24
+ gem, these comments will be added to the annotations that are generated within your model.rb
25
+ file.
26
+
27
+ == Examples
28
+
29
+ Want to add a comment to an existing structure...
30
+
31
+ self.up
32
+ add_table_comment :table_name, "A table comment"
33
+ comment_table :table_name, "A table comment" # does the same thing
34
+
35
+ add_column_comment :table_name, :column_name, "A column comment"
36
+ comment_column :table_name, :column_name, "A column comment" # does the same thing
37
+ end
38
+
39
+ Or you can use the change_table macro...
40
+
41
+ self.up
42
+ change_table :table_name do |t|
43
+ t.comment "A table comment"
44
+ t.change_comment :column_name, "A column comment"
45
+ end
46
+ end
47
+
48
+ Creating a new table?
49
+
50
+ self.up
51
+ create_table :table_name, :comment => "A table comment" do |t|
52
+ t.string :column_name, :comment => "A column comment"
53
+ end
54
+ end
55
+
56
+ You can also remove comments...
57
+
58
+ self.up
59
+ remove_table_comment :table_name
60
+ remove_column_comment :table_name, :column_name
61
+ end
62
+
63
+ Or you can combine these commands while modifying a table...
64
+
65
+ self.up
66
+ change_table :existing_table do |t|
67
+ t.comment nil # remove an existing table comment
68
+ t.string :new_column, :comment => "a new column" # add a new column with a comment
69
+ t.change_comment :existing_column, nil # remove a comment on an existing column
70
+ t.integer :another_existing_column, :comment => nil # remove a comment on an existing column while modifying the column type
71
+ t.boolean :column_with_comment # modify an existing column without altering the comment
72
+ end
73
+ end
74
+
75
+
76
+ == Requirements
77
+
78
+ You must be using a DBMS that supports COMMENTing (currently only PostgreSQL and MySQL).
79
+
80
+ If this isn't an option for you, check out the 'schema_comments' gem:
81
+ https://github.com/akm/schema_comments
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,41 @@
1
+ module MigrationComments::ActiveRecord::ConnectionAdapters
2
+ module AbstractAdapter
3
+ def add_table_comment(table_name, comment_text)
4
+ # SQL standard doesn't support schema commenting
5
+ raise "Table comments are not supported"
6
+ end
7
+ alias comment_table :add_table_comment
8
+
9
+ def add_column_comment(table_name, column_name, comment_text)
10
+ # SQL standard doesn't support schema commenting
11
+ raise "Column comments are not supported"
12
+ end
13
+ alias comment_column :add_column_comment
14
+
15
+ def comments_supported?
16
+ false
17
+ end
18
+
19
+ # Remove a comment on a table (if set)
20
+ def remove_table_comment(table_name)
21
+ add_table_comment(table_name, nil)
22
+ end
23
+
24
+ # Remove a comment on a column (if set)
25
+ def remove_column_comment(table_name, column_name)
26
+ add_column_comment(table_name, column_name, nil)
27
+ end
28
+
29
+ def retrieve_table_comment(table_name)
30
+ nil
31
+ end
32
+
33
+ def retrieve_column_comments(table_name, *column_names)
34
+ {}
35
+ end
36
+
37
+ def retrieve_column_comment(table_name, column_name)
38
+ retrieve_column_comments(table_name, column_name)[column_name]
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,5 @@
1
+ module MigrationComments::ActiveRecord::ConnectionAdapters
2
+ module ColumnDefinition
3
+ attr_accessor :comment
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ module MigrationComments::ActiveRecord::ConnectionAdapters
2
+ class CommentDefinition < Struct.new(:adapter, :table, :column_name, :comment_text)
3
+ def to_dump
4
+ table_comment? ?
5
+ "add_table_comment :#{table_name}, %{#{comment_text}}" :
6
+ "add_column_comment :#{table_name}, :#{column_name}, %{#{comment_text}}"
7
+ end
8
+
9
+ def to_sql
10
+ adapter.comment_sql(self)
11
+ end
12
+ alias to_s :to_sql
13
+
14
+ def table_comment?
15
+ column_name.blank?
16
+ end
17
+
18
+ def table_name
19
+ table.respond_to?(:name) ? table.name : table
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ module MigrationComments::ActiveRecord::ConnectionAdapters
2
+ module Mysql2Adapter
3
+ def self.included(base)
4
+ base.class_eval do
5
+ include MigrationComments::ActiveRecord::ConnectionAdapters::MysqlAdapter
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,97 @@
1
+ module MigrationComments::ActiveRecord::ConnectionAdapters
2
+ module MysqlAdapter
3
+ def self.included(base)
4
+ base.class_eval do
5
+ attr_accessor :database_name
6
+ alias_method_chain :create_table, :migration_comments
7
+ alias_method_chain :change_column, :migration_comments
8
+ end
9
+ end
10
+
11
+ def add_table_comment(table_name, comment)
12
+ execute "ALTER TABLE #{table_name} COMMENT #{escaped_comment(comment)}"
13
+ end
14
+
15
+ def add_column_comment(table_name, column_name, comment)
16
+ column = column_for(table_name, column_name)
17
+ change_column table_name, column_name, column.sql_type, :comment => comment
18
+ end
19
+
20
+ def retrieve_table_comment(table_name)
21
+ result = select_rows(table_comment_sql(table_name))
22
+ result[0].nil? || result[0][0].blank? ? nil : result[0][0]
23
+ end
24
+
25
+ def retrieve_column_comments(table_name, *column_names)
26
+ result = select_rows(column_comment_sql(table_name, *column_names))
27
+ return {} if result.nil?
28
+ return result.inject({}){|m, row| m[row[0].to_sym] = (row[1].blank? ? nil : row[1]); m}
29
+ end
30
+
31
+ def create_table_with_migration_comments(table_name, options={}, &block)
32
+ local_table_definition = nil
33
+ create_table_without_migration_comments(table_name, options) do |td|
34
+ local_table_definition = td
35
+ local_table_definition.comment options[:comment] if options.has_key?(:comment)
36
+ block.call(td)
37
+ end
38
+ comments = local_table_definition.collect_comments(table_name)
39
+ comments.each do |comment_definition|
40
+ execute_comment comment_definition
41
+ end
42
+ end
43
+
44
+ def change_column_with_migration_comments(table_name, column_name, type, options={})
45
+ unless options.keys.include?(:comment)
46
+ options.merge!(:comment => retrieve_column_comment(table_name, column_name))
47
+ end
48
+ change_column_without_migration_comments(table_name, column_name, type, options)
49
+ end
50
+
51
+ def add_column_options!(sql, options)
52
+ super(sql, options)
53
+ if options.keys.include?(:comment)
54
+ sql << " COMMENT #{escaped_comment(options[:comment])}"
55
+ end
56
+ end
57
+
58
+ def execute_comment(comment_definition)
59
+ if comment_definition.table_comment?
60
+ add_table_comment comment_definition.table_name, comment_definition.comment_text
61
+ else
62
+ add_column_comment comment_definition.table_name, comment_definition.column_name, comment_definition.comment_text
63
+ end
64
+ end
65
+
66
+ private
67
+ def escaped_comment(comment)
68
+ comment.nil? ? "''" : "'#{comment.gsub("'", "''").gsub("\\", "\\\\\\\\")}'"
69
+ end
70
+
71
+ def table_comment_sql(table_name)
72
+ ensure_database_name
73
+ <<SQL
74
+ SELECT table_comment FROM INFORMATION_SCHEMA.TABLES
75
+ WHERE table_schema = '#{database_name}'
76
+ AND table_name = '#{table_name}'
77
+ SQL
78
+ end
79
+
80
+ def column_comment_sql(table_name, *column_names)
81
+ ensure_database_name
82
+ col_matcher_sql = column_names.empty? ? "" : " AND column_name IN (#{column_names.map{|c_name| "'#{c_name}'"}.join(',')})"
83
+ <<SQL
84
+ SELECT column_name, column_comment FROM INFORMATION_SCHEMA.COLUMNS
85
+ WHERE table_schema = '#{database_name}'
86
+ AND table_name = '#{table_name}' #{col_matcher_sql}
87
+ SQL
88
+ end
89
+
90
+ def ensure_database_name
91
+ return if database_name
92
+ info = YAML::load(IO.read('config/database.yml'))
93
+ @database_name = info[ENV['DB'] || RAILS_ENV]["database"]
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,111 @@
1
+ module MigrationComments::ActiveRecord::ConnectionAdapters
2
+ module PostgreSQLAdapter
3
+ def self.included(base)
4
+ base.class_eval do
5
+ alias_method_chain :create_table, :migration_comments
6
+ alias_method_chain :add_column, :migration_comments
7
+ alias_method_chain :change_column, :migration_comments
8
+ end
9
+ end
10
+
11
+ def comments_supported?
12
+ true
13
+ end
14
+
15
+ # Set a comment on a table
16
+ def add_table_comment(table_name, comment_text)
17
+ execute CommentDefinition.new(self, table_name, nil, comment_text).to_sql
18
+ end
19
+
20
+ # Set a comment on a column
21
+ def add_column_comment(table_name, column_name, comment_text)
22
+ execute CommentDefinition.new(self, table_name, column_name, comment_text).to_sql
23
+ end
24
+
25
+ def retrieve_table_comment(table_name)
26
+ result = execute(table_comment_sql(table_name)).result
27
+ result[0].nil? ? nil : result[0][0]
28
+ end
29
+
30
+ def retrieve_column_comments(table_name, *column_names)
31
+ result = execute(column_comment_sql(table_name, *column_names)).result
32
+ return {} if result.nil?
33
+ return result.inject({}){|m, row| m[row[0].to_sym] = row[1]; m}
34
+ end
35
+
36
+ def create_table_with_migration_comments(table_name, options = {}, &block)
37
+ local_table_definition = nil
38
+ create_table_without_migration_comments(table_name, options) do |td|
39
+ local_table_definition = td
40
+ local_table_definition.comment options[:comment] if options.has_key?(:comment)
41
+ block.call(td)
42
+ end
43
+ comments = local_table_definition.collect_comments(table_name)
44
+ comments.each do |comment_definition|
45
+ execute comment_definition.to_sql
46
+ end
47
+ end
48
+
49
+ def add_column_with_migration_comments(table_name, column_name, type, options = {})
50
+ add_column_without_migration_comments(table_name, column_name, type, options)
51
+ if options[:comment]
52
+ add_column_comment(table_name, column_name, options[:comment])
53
+ end
54
+ end
55
+
56
+ def change_column_with_migration_comments(table_name, column_name, type, options = {})
57
+ change_column_without_migration_comments(table_name, column_name, type, options)
58
+ if options.keys.include?(:comment)
59
+ add_column_comment(table_name, column_name, options[:comment])
60
+ end
61
+ end
62
+
63
+ def comment_sql(comment_definition)
64
+ "COMMENT ON #{comment_target(comment_definition)} IS #{escaped_comment(comment_definition.comment_text)}"
65
+ end
66
+
67
+ private
68
+
69
+ private
70
+ def comment_target(comment_definition)
71
+ comment_definition.table_comment? ?
72
+ "TABLE #{quote_table_name(comment_definition.table_name)}" :
73
+ "COLUMN #{quote_table_name(comment_definition.table_name)}.#{quote_column_name(comment_definition.column_name)}"
74
+ end
75
+
76
+ def escaped_comment(comment)
77
+ comment.nil? ? 'NULL' : "'#{comment.gsub("'", "''")}'"
78
+ end
79
+
80
+ def table_comment_sql(table_name)
81
+ <<SQL
82
+ SELECT d.description FROM (
83
+ #{table_oids(table_name)}) tt
84
+ JOIN pg_catalog.pg_description d
85
+ ON tt.oid = d.objoid AND tt.tableoid = d.classoid AND d.objsubid = 0;
86
+ SQL
87
+ end
88
+
89
+ def column_comment_sql(table_name, *column_names)
90
+ col_matcher_sql = column_names.empty? ? "" : " a.attname IN (#{column_names.map{|c_name| "'#{c_name}'"}.join(',')}) AND "
91
+ <<SQL
92
+ SELECT a.attname, pg_catalog.col_description(a.attrelid, a.attnum)
93
+ FROM pg_catalog.pg_attribute a
94
+ JOIN (
95
+ #{table_oids(table_name)}) tt
96
+ ON tt.oid = a.attrelid
97
+ WHERE #{col_matcher_sql} a.attnum > 0 AND NOT a.attisdropped;
98
+ SQL
99
+ end
100
+
101
+ def table_oids(table_name)
102
+ <<SQL
103
+ SELECT c.oid, c.tableoid
104
+ FROM pg_catalog.pg_class c
105
+ WHERE c.relname = '#{table_name}'
106
+ AND c.relkind = 'r'
107
+ AND pg_catalog.pg_table_is_visible(c.oid)
108
+ SQL
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,12 @@
1
+ module MigrationComments::ActiveRecord::ConnectionAdapters
2
+ module Table
3
+ def change_comment(column_name, comment_text)
4
+ @base.add_column_comment(@table_name, column_name, comment_text)
5
+ end
6
+
7
+ def change_table_comment(comment_text)
8
+ @base.add_table_comment(@table_name, comment_text)
9
+ end
10
+ alias comment :change_table_comment
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ module MigrationComments::ActiveRecord::ConnectionAdapters
2
+ module TableDefinition
3
+
4
+ attr_accessor :table_comment
5
+ def self.included(base)
6
+ base.class_eval do
7
+ alias_method_chain :column, :migration_comments
8
+ end
9
+ end
10
+
11
+ def comment(text)
12
+ @table_comment = CommentDefinition.new(@base, nil, nil, text)
13
+ self
14
+ end
15
+
16
+ def column_with_migration_comments(name, type, options = {})
17
+ column_without_migration_comments(name, type, options)
18
+ col = self[name]
19
+ col.comment = CommentDefinition.new(@base, nil, name, options[:comment])
20
+ self
21
+ end
22
+
23
+ def collect_comments(table_name)
24
+ comments = []
25
+ comments << @table_comment << @columns.map(&:comment)
26
+ comments.flatten!.compact!
27
+ comments.each{|comment| comment.table = table_name}
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,54 @@
1
+ module MigrationComments::ActiveRecord
2
+ module SchemaDumper
3
+ def self.included(base)
4
+ base.class_eval do
5
+ alias_method_chain :table, :migration_comments
6
+ end
7
+ end
8
+
9
+ def table_with_migration_comments(table, stream)
10
+ tbl_stream = StringIO.new
11
+ table_without_migration_comments(table, tbl_stream)
12
+ tbl_stream.rewind
13
+ commented_stream = append_comments(table, tbl_stream)
14
+ tbl_stream.close
15
+ stream.print commented_stream.read
16
+ end
17
+
18
+ def append_comments(table, stream)
19
+ table_name = table.inspect.gsub('"', '')
20
+ table_comment = @connection.retrieve_table_comment(table_name)
21
+ column_comments = @connection.retrieve_column_comments(table_name)
22
+ comment_stream = StringIO.new
23
+ lines = []
24
+ table_line = 0
25
+ col_names = {}
26
+ while (line = stream.gets)
27
+ content = line.chomp
28
+ if content =~ /create_table\s/
29
+ table_line = lines.size
30
+ elsif content =~ /t\.\w+\s+"(\w+)"/
31
+ col_names[lines.size] = $1.to_sym
32
+ end
33
+ lines << content
34
+ end
35
+ len = col_names.keys.map{|index| lines[index]}.map(&:length).max + 2
36
+ lines.each_with_index do |line, index|
37
+ if table_line == index && table_comment.present?
38
+ block_init = " do |t|"
39
+ line.chomp!(block_init) << ", " << render_comment(table_comment) << block_init
40
+ elsif col_names[index]
41
+ comment = column_comments[col_names[index]]
42
+ line << ',' << ' ' * (len - line.length) << render_comment(comment) unless comment.blank?
43
+ end
44
+ comment_stream.puts line
45
+ end
46
+ comment_stream.rewind
47
+ comment_stream
48
+ end
49
+
50
+ def render_comment(comment)
51
+ ":comment => \"#{comment}\""
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,40 @@
1
+ module MigrationComments
2
+ module AnnotateModels
3
+ def self.included(base)
4
+ base.class_eval do
5
+ class << self
6
+ include ClassMethods
7
+ alias_method_chain :get_schema_info, :migration_comments
8
+ end
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+ def get_schema_info_with_migration_comments(*args)
14
+ info = get_schema_info_without_migration_comments(*args)
15
+ klass = args[0]
16
+ commented_info(klass, info)
17
+ end
18
+
19
+ def commented_info(klass, info)
20
+ table_name = klass.table_name
21
+ adapter = klass.connection
22
+ table_comment = adapter.retrieve_table_comment(table_name)
23
+ column_comments = adapter.retrieve_column_comments(table_name)
24
+ lines = []
25
+ info.each_line{|l| lines << l.chomp}
26
+ column_regex = /^#\s+(\w+)\s+:\w+/
27
+ len = lines.select{|l| l =~ column_regex}.map{|l| l.length}.max
28
+ lines.each do |line|
29
+ if line =~ /# Table name: |# table \+\w+\+ /
30
+ line << " # #{table_comment}" if table_comment
31
+ elsif line =~ column_regex
32
+ comment = column_comments[$1.to_sym]
33
+ line << " " * (len - line.length) << " # #{comment}" if comment
34
+ end
35
+ end
36
+ lines.join($/) + $/
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module MigrationComments
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,58 @@
1
+ require "migration_comments/version"
2
+
3
+ require 'migration_comments/active_record/schema_dumper'
4
+ require 'migration_comments/active_record/connection_adapters/comment_definition'
5
+ require 'migration_comments/active_record/connection_adapters/column_definition'
6
+ require 'migration_comments/active_record/connection_adapters/table'
7
+ require 'migration_comments/active_record/connection_adapters/table_definition'
8
+ require 'migration_comments/active_record/connection_adapters/abstract_adapter'
9
+ require 'migration_comments/active_record/connection_adapters/mysql_adapter'
10
+ require 'migration_comments/active_record/connection_adapters/mysql2_adapter'
11
+ require 'migration_comments/active_record/connection_adapters/postgresql_adapter'
12
+
13
+ module MigrationComments
14
+ def self.setup
15
+ base_names = %w(SchemaDumper) +
16
+ %w(ColumnDefinition Table TableDefinition AbstractAdapter).map{|name| "ConnectionAdapters::#{name}"}
17
+
18
+ base_names.each do |base_name|
19
+ ar_class = "ActiveRecord::#{base_name}".constantize
20
+ mc_class = "MigrationComments::ActiveRecord::#{base_name}".constantize
21
+ unless ar_class.ancestors.include?(mc_class)
22
+ ar_class.__send__(:include, mc_class)
23
+ end
24
+ end
25
+
26
+ %w(PostgreSQL Mysql Mysql2).each do |adapter|
27
+ begin
28
+ require("active_record/connection_adapters/#{adapter.downcase}_adapter")
29
+ adapter_class = ('ActiveRecord::ConnectionAdapters::' << "#{adapter}Adapter").constantize
30
+ mc_class = ('MigrationComments::ActiveRecord::ConnectionAdapters::' << "#{adapter}Adapter").constantize
31
+ adapter_class.module_eval do
32
+ adapter_class.__send__(:include, mc_class)
33
+ end
34
+ rescue Exception => ex
35
+ end
36
+ end
37
+
38
+ # annotations are not required for this gem, but if they exist they should be updated
39
+ begin
40
+ begin # first try to load from the 'annotate' gem
41
+ require 'annotate/annotate_models'
42
+ rescue Exception => ex
43
+ # continue as it may be already accessible through a plugin
44
+ end
45
+ gem_class = AnnotateModels
46
+ # don't require this until after the original AnnotateModels loads to avoid namespace confusion
47
+ require 'migration_comments/annotate_models'
48
+ mc_class = MigrationComments::AnnotateModels
49
+ unless gem_class.ancestors.include?(mc_class)
50
+ gem_class.__send__(:include, mc_class)
51
+ end
52
+ rescue Exception => ex
53
+ # if we got here, don't bother installing comments into annotations
54
+ end
55
+ end
56
+ end
57
+
58
+ MigrationComments.setup
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "migration_comments/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "migration_comments"
7
+ s.version = MigrationComments::VERSION
8
+ s.authors = ["Pinny"]
9
+ s.email = ["pinny@medwiztech.com"]
10
+ s.homepage = "https://github.com/pinnymz/migration_comments"
11
+ s.summary = %q{Comments for your migrations}
12
+ s.description = %q{Add schema comments in your migrations, see them in model annotations and db/schema.rb dump}
13
+
14
+ s.rubyforge_project = "migration_comments"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_runtime_dependency 'rails', '~> 2.3', '>= 2.3.2'
22
+
23
+ # for development, we are testing against the 'annotate' gem
24
+ # however, the comments should work with the original 'annotate_models' plugin as well at:
25
+ # http://repo.pragprog.com/svn/Public/plugins/annotate_models
26
+ # provided the environment is not loaded until _after_ the AnnotateModels module is declared
27
+ s.add_development_dependency 'annotate'
28
+
29
+ s.add_development_dependency 'postgres-pr' # replace with other adapter as needed
30
+ # s.add_development_dependency 'mysql'
31
+ # s.add_development_dependency 'mysql2'
32
+ end
@@ -0,0 +1,135 @@
1
+ require 'test_helper'
2
+
3
+ class AddCommentsTest < Test::Unit::TestCase
4
+ include TestHelper
5
+
6
+ def test_adding_a_table_comment
7
+ comment_text = "a comment on the sample table"
8
+ result = nil
9
+ ActiveRecord::Schema.define do
10
+ add_table_comment :sample, comment_text
11
+ result = retrieve_table_comment :sample
12
+ end
13
+ assert_equal comment_text, result
14
+ end
15
+
16
+ def test_adding_a_column_comment
17
+ comment_text = "a comment on the sample table in the column field"
18
+ result_field1 = nil
19
+ result_field2 = nil
20
+ ActiveRecord::Schema.define do
21
+ add_column_comment :sample, :field1, comment_text
22
+ result_field1 = retrieve_column_comment :sample, :field1
23
+ result_field2 = retrieve_column_comment :sample, :field2
24
+ end
25
+ assert_equal comment_text, result_field1
26
+ assert_nil result_field2
27
+ end
28
+
29
+ def test_creating_a_table_with_table_and_column_comments
30
+ table_comment = "a table comment"
31
+ column_comment = "a column comment"
32
+ result_table_comment = nil
33
+ result_column_comments = nil
34
+ ActiveRecord::Schema.define do
35
+ begin
36
+ create_table :sample2, :comment => table_comment do |t|
37
+ t.integer :field1, :comment => column_comment
38
+ t.string :field2
39
+ end
40
+ result_table_comment = retrieve_table_comment :sample2
41
+ result_column_comments = retrieve_column_comments :sample2
42
+ ensure
43
+ drop_table :sample2
44
+ end
45
+ end
46
+ assert_equal table_comment, result_table_comment
47
+ assert_equal column_comment, result_column_comments[:field1]
48
+ assert_nil result_column_comments[:field2]
49
+ end
50
+
51
+ def test_changing_a_table_with_new_comments
52
+ table_comment = "a table comment"
53
+ column_comment1 = "a column comment"
54
+ column_comment2 = "another column comment"
55
+ result_table_comment = nil
56
+ result_column_comments = nil
57
+ ActiveRecord::Schema.define do
58
+ change_table :sample do |t|
59
+ t.comment table_comment
60
+ t.change :field1, :string, :comment => column_comment1
61
+ t.change :field2, :integer
62
+ t.boolean :field3, :comment => column_comment2
63
+ end
64
+ result_table_comment = retrieve_table_comment :sample
65
+ result_column_comments = retrieve_column_comments :sample
66
+ end
67
+ assert_equal table_comment, result_table_comment
68
+ assert_equal column_comment1, result_column_comments[:field1]
69
+ assert_equal column_comment2, result_column_comments[:field3]
70
+ assert_nil result_column_comments[:field2]
71
+ end
72
+
73
+ def test_partially_modifying_comments_from_a_table
74
+ table_comment = "a table comment"
75
+ column_comment1 = "a column comment"
76
+ column_comment2 = "another column comment"
77
+ column_comment3 = "yet a third column comment"
78
+ modified_comment = "modified comment"
79
+ result_table_comment = nil
80
+ result_column_comments = nil
81
+ ActiveRecord::Schema.define do
82
+ change_table :sample do |t|
83
+ t.comment table_comment
84
+ t.change :field1, :string, :comment => column_comment1
85
+ t.change :field2, :integer, :comment => column_comment2
86
+ t.boolean :field3, :comment => column_comment3
87
+ end
88
+ change_table :sample do |t|
89
+ t.comment nil
90
+ t.change :field1, :string
91
+ t.change :field2, :integer, :comment => modified_comment
92
+ t.change :field3, :boolean, :comment => nil
93
+ end
94
+ result_table_comment = retrieve_table_comment :sample
95
+ result_column_comments = retrieve_column_comments :sample
96
+ end
97
+ assert_nil result_table_comment
98
+ assert_equal column_comment1, result_column_comments[:field1]
99
+ assert_equal modified_comment, result_column_comments[:field2]
100
+ assert_nil result_column_comments[:field3]
101
+ end
102
+
103
+ def test_removing_comments_from_a_table
104
+ comment_text = "a comment on the sample table"
105
+ result = nil
106
+ ActiveRecord::Schema.define do
107
+ add_table_comment :sample, comment_text
108
+ remove_table_comment :sample
109
+ result = retrieve_table_comment :sample
110
+ end
111
+ assert_nil result
112
+ end
113
+
114
+ def test_removing_comments_from_a_column
115
+ comment_text = "a comment on field1 of sample table"
116
+ result = nil
117
+ ActiveRecord::Schema.define do
118
+ add_column_comment :sample, :field1, comment_text
119
+ remove_column_comment :sample, :field1
120
+ result = retrieve_column_comment :sample, :field1
121
+ end
122
+ assert_nil result
123
+ end
124
+
125
+ def test_comment_text_is_escaped_properly
126
+ comment_text = "a \"comment\" \\ that ' needs; escaping''"
127
+ result = nil
128
+ ActiveRecord::Schema.define do
129
+ add_table_comment :sample, comment_text
130
+ result = retrieve_table_comment :sample
131
+ end
132
+ assert_equal comment_text, result
133
+ end
134
+
135
+ end
@@ -0,0 +1,53 @@
1
+ require 'test_helper'
2
+ gem 'annotate'
3
+ require 'annotate/annotate_models'
4
+
5
+ class Sample < ActiveRecord::Base
6
+ self.table_name = 'sample'
7
+ end
8
+
9
+ class AnnotateModelsTest < Test::Unit::TestCase
10
+ include TestHelper
11
+
12
+ TEST_PREFIX = "== Schema Information"
13
+
14
+ def test_annotate_includes_comments
15
+ db_type = :default
16
+ ActiveRecord::Schema.define do
17
+ db_type = :postgres if connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) rescue false
18
+
19
+ add_table_comment :sample, "a table comment"
20
+ add_column_comment :sample, :field1, "a \"comment\" \\ that ' needs; escaping''"
21
+ add_column :sample, :field3, :string, :null => false, :comment => "third column comment"
22
+ end
23
+
24
+ result = AnnotateModels.get_schema_info(Sample, TEST_PREFIX)
25
+ postgres_expected = <<EOS
26
+ # #{TEST_PREFIX}
27
+ #
28
+ # Table name: sample # a table comment
29
+ #
30
+ # id :integer not null, primary key
31
+ # field1 :string(255) # a "comment" \\ that ' needs; escaping''
32
+ # field2 :integer
33
+ # field3 :string(255) not null # third column comment
34
+ #
35
+
36
+ EOS
37
+ default_expected = <<EOS
38
+ # #{TEST_PREFIX}
39
+ #
40
+ # Table name: sample # a table comment
41
+ #
42
+ # id :integer(4) not null, primary key
43
+ # field1 :string(255) # a "comment" \\ that ' needs; escaping''
44
+ # field2 :integer(4)
45
+ # field3 :string(255) not null # third column comment
46
+ #
47
+
48
+ EOS
49
+ expected = instance_eval "#{db_type}_expected"
50
+ assert_equal expected, result
51
+ end
52
+ end
53
+
@@ -0,0 +1,12 @@
1
+ postgres:
2
+ adapter: postgresql
3
+ database: migration_comments_test
4
+ host: 127.0.0.1
5
+ username: postgres
6
+ password: postgres
7
+
8
+ mysql:
9
+ adapter: mysql # mysql2
10
+ database: migration_comments_test
11
+ user: root
12
+ password: password
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class SchemaDumperTest < Test::Unit::TestCase
4
+ include TestHelper
5
+
6
+ def test_dump
7
+ ActiveRecord::Schema.define do
8
+ add_table_comment :sample, "a table comment"
9
+ add_column_comment :sample, :field1, "a \"comment\" \\ that ' needs; escaping''"
10
+ add_column :sample, :field3, :string, :null => false, :comment => "third column comment"
11
+ end
12
+ dest = StringIO.new
13
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, dest)
14
+ dest.rewind
15
+ result = dest.read
16
+ expected = <<EOS
17
+ # This file is auto-generated from the current state of the database. Instead of editing this file,
18
+ # please use the migrations feature of Active Record to incrementally modify your database, and
19
+ # then regenerate this schema definition.
20
+ #
21
+ # Note that this schema.rb definition is the authoritative source for your database schema. If you need
22
+ # to create the application database on another system, you should be using db:schema:load, not running
23
+ # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
24
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
25
+ #
26
+ # It's strongly recommended to check this file into your version control system.
27
+
28
+ ActiveRecord::Schema.define(:version => 1) do
29
+
30
+ create_table "sample", :force => true, :comment => "a table comment" do |t|
31
+ t.string "field1", :comment => "a \"comment\" \\ that ' needs; escaping''"
32
+ t.integer "field2"
33
+ t.string "field3", :null => false, :comment => "third column comment"
34
+ end
35
+
36
+ end
37
+ EOS
38
+ assert_equal expected, result
39
+ end
40
+ end
@@ -0,0 +1,30 @@
1
+ require 'test/unit'
2
+
3
+ require 'rubygems'
4
+ gem 'rails', '>= 2.3.2'
5
+ require 'active_record'
6
+ require 'yaml'
7
+
8
+ CONFIGURATIONS = YAML::load(IO.read('config/database.yml'))
9
+
10
+ ActiveRecord::Base.establish_connection(CONFIGURATIONS[ENV['DB'] || 'postgres'])
11
+
12
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
13
+ require 'migration_comments'
14
+
15
+ module TestHelper
16
+ def setup
17
+ ActiveRecord::Schema.define(:version => 1) do
18
+ create_table :sample do |t|
19
+ t.string :field1
20
+ t.integer :field2
21
+ end
22
+ end
23
+ end
24
+
25
+ def teardown
26
+ ActiveRecord::Schema.define do
27
+ drop_table :sample
28
+ end
29
+ end
30
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: migration_comments
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Pinny
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-02-10 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rails
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 5
30
+ segments:
31
+ - 2
32
+ - 3
33
+ version: "2.3"
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ hash: 7
37
+ segments:
38
+ - 2
39
+ - 3
40
+ - 2
41
+ version: 2.3.2
42
+ type: :runtime
43
+ version_requirements: *id001
44
+ - !ruby/object:Gem::Dependency
45
+ name: annotate
46
+ prerelease: false
47
+ requirement: &id002 !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ hash: 3
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ type: :development
57
+ version_requirements: *id002
58
+ - !ruby/object:Gem::Dependency
59
+ name: postgres-pr
60
+ prerelease: false
61
+ requirement: &id003 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ type: :development
71
+ version_requirements: *id003
72
+ description: Add schema comments in your migrations, see them in model annotations and db/schema.rb dump
73
+ email:
74
+ - pinny@medwiztech.com
75
+ executables: []
76
+
77
+ extensions: []
78
+
79
+ extra_rdoc_files: []
80
+
81
+ files:
82
+ - .gitignore
83
+ - Gemfile
84
+ - README.rdoc
85
+ - Rakefile
86
+ - lib/migration_comments.rb
87
+ - lib/migration_comments/active_record/connection_adapters/abstract_adapter.rb
88
+ - lib/migration_comments/active_record/connection_adapters/column_definition.rb
89
+ - lib/migration_comments/active_record/connection_adapters/comment_definition.rb
90
+ - lib/migration_comments/active_record/connection_adapters/mysql2_adapter.rb
91
+ - lib/migration_comments/active_record/connection_adapters/mysql_adapter.rb
92
+ - lib/migration_comments/active_record/connection_adapters/postgresql_adapter.rb
93
+ - lib/migration_comments/active_record/connection_adapters/table.rb
94
+ - lib/migration_comments/active_record/connection_adapters/table_definition.rb
95
+ - lib/migration_comments/active_record/schema_dumper.rb
96
+ - lib/migration_comments/annotate_models.rb
97
+ - lib/migration_comments/version.rb
98
+ - migration_comments.gemspec
99
+ - test/add_comments_test.rb
100
+ - test/annotate_models_test.rb
101
+ - test/config/database.yml
102
+ - test/schema_dumper_test.rb
103
+ - test/test_helper.rb
104
+ has_rdoc: true
105
+ homepage: https://github.com/pinnymz/migration_comments
106
+ licenses: []
107
+
108
+ post_install_message:
109
+ rdoc_options: []
110
+
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ hash: 3
119
+ segments:
120
+ - 0
121
+ version: "0"
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ hash: 3
128
+ segments:
129
+ - 0
130
+ version: "0"
131
+ requirements: []
132
+
133
+ rubyforge_project: migration_comments
134
+ rubygems_version: 1.4.2
135
+ signing_key:
136
+ specification_version: 3
137
+ summary: Comments for your migrations
138
+ test_files: []
139
+