Commit 1de5bfbc by cuong.tran

Merge branch 'release/v2.6.9'

parents 52ffefcb 75a18367
language: ruby
rvm:
- 1.9.3
- 2.0
- 2.1
- 2.2
before_install:
- rvm @global do gem install bundler
== 2.6.9
* Support foreigh key (#241)
* Check if model has skip tag in annotate_model_file (#167)
* Fix issue where serializer-related flags weren't being honored (#246)
* Prefer SQL column type over normalized AR type (#231)
== 2.6.8
* Nothing annotated unless options[:model_dir] is specified, #234
......
......@@ -175,6 +175,7 @@ you can do so with a simple environment variable, instead of editing the
-v, --version Show the current version of this gem
-m, --show-migration Include the migration version number in the annotation
-i, --show-indexes List the table's database indexes in the annotation
-k, --show-foreign-keys List the table's foreign key constraints in the annotation
-s, --simple-indexes Concat the column's related indexes in the annotation
--model-dir dir Annotate model files stored in dir rather than app/models, separate multiple dirs with comas
--ignore-model-subdirects Ignore subdirectories of the models directory
......
......@@ -33,7 +33,7 @@ OptionParser.new do |opts|
end
opts.on('-p', '--position [before|top|after|bottom]', ['before', 'top', 'after', 'bottom'],
"Place the annotations at the top (before) or the bottom (after) of the model/test/fixture/factory/routes file(s)") do |p|
"Place the annotations at the top (before) or the bottom (after) of the model/test/fixture/factory/route/serializer file(s)") do |p|
ENV['position'] = p
[
'position_in_class','position_in_factory','position_in_fixture','position_in_test', 'position_in_routes', 'position_in_serializer'
......@@ -109,6 +109,11 @@ OptionParser.new do |opts|
ENV['include_version'] = "yes"
end
opts.on('-k', '--show-foreign-keys',
"List the table's foreign key constraints in the annotation") do
ENV['show_foreign_keys'] = "yes"
end
opts.on('-i', '--show-indexes',
"List the table's database indexes in the annotation") do
ENV['show_indexes'] = "yes"
......
......@@ -20,13 +20,13 @@ module Annotate
POSITION_OPTIONS=[
:position_in_routes, :position_in_class, :position_in_test,
:position_in_fixture, :position_in_factory, :position,
:position_in_serializer,
:position_in_serializer
]
FLAG_OPTIONS=[
:show_indexes, :simple_indexes, :include_version, :exclude_tests,
:exclude_fixtures, :exclude_factories, :ignore_model_sub_dir,
:format_bare, :format_rdoc, :format_markdown, :sort, :force, :trace,
:timestamp, :exclude_serializers, :classified_sort
:timestamp, :exclude_serializers, :classified_sort, :show_foreign_keys,
]
OTHER_OPTIONS=[
:ignore_columns
......
......@@ -7,7 +7,7 @@ module AnnotateModels
PREFIX = "== Schema Information"
PREFIX_MD = "## Schema Information"
END_MARK = "== Schema Information End"
PATTERN = /^\n?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?\n(#.*\n)*\n*/
PATTERN = /^\r?\n?# (?:#{COMPAT_PREFIX}|#{COMPAT_PREFIX_MD}).*?\r?\n(#.*\r?\n)*(\r?\n)*/
# File.join for windows reverse bar compat?
# I dont use windows, can`t test
......@@ -140,7 +140,7 @@ module AnnotateModels
attrs << "not null" unless col.null
attrs << "primary key" if klass.primary_key && (klass.primary_key.is_a?(Array) ? klass.primary_key.collect{|c|c.to_sym}.include?(col.name.to_sym) : col.name.to_sym == klass.primary_key.to_sym)
col_type = (col.type || col.sql_type).to_s
col_type = (col.sql_type || col.type).to_s
if col_type == "decimal"
col_type << "(#{col.precision}, #{col.scale})"
elsif col_type != "spatial"
......@@ -193,6 +193,10 @@ module AnnotateModels
info << get_index_info(klass, options)
end
if options[:show_foreign_keys] && klass.table_exists?
info << get_foreign_key_info(klass, options)
end
if options[:format_rdoc]
info << "#--\n"
info << "# #{END_MARK}\n"
......@@ -223,6 +227,28 @@ module AnnotateModels
return index_info
end
def get_foreign_key_info(klass, options={})
if(options[:format_markdown])
fk_info = "#\n# ### Foreign Keys\n#\n"
else
fk_info = "#\n# Foreign Keys\n#\n"
end
foreign_keys = klass.connection.respond_to?(:foreign_keys) ? klass.connection.foreign_keys(klass.table_name) : []
return "" if foreign_keys.empty?
max_size = foreign_keys.collect{|fk| fk.name.size}.max + 1
foreign_keys.sort_by{|fk| fk.name}.each do |fk|
ref_info = "#{fk.column} => #{fk.to_table}.#{fk.primary_key}"
if(options[:format_markdown])
fk_info << sprintf("# * `%s`:\n# * **`%s`**\n", fk.name, ref_info)
else
fk_info << sprintf("# %-#{max_size}.#{max_size}s %s", fk.name, "(#{ref_info})").rstrip + "\n"
end
end
return fk_info
end
# Add a schema block to a file. If the file already contains
# a schema info block (a comment starting with "== Schema Information"), check if it
# matches the block that is already there. If so, leave it be. If not, remove the old
......@@ -309,9 +335,11 @@ module AnnotateModels
# :position_in_test<Symbol>:: where to place the annotated section in test/spec file(s)
# :position_in_fixture<Symbol>:: where to place the annotated section in fixture file
# :position_in_factory<Symbol>:: where to place the annotated section in factory file
# :position_in_serializer<Symbol>:: where to place the annotated section in serializer file
# :exclude_tests<Symbol>:: whether to skip modification of test/spec files
# :exclude_fixtures<Symbol>:: whether to skip modification of fixture files
# :exclude_factories<Symbol>:: whether to skip modification of factory files
# :exclude_serializers<Symbol>:: whether to skip modification of serializer files
#
def annotate(klass, file, header, options={})
begin
......@@ -350,9 +378,9 @@ module AnnotateModels
options.merge(:position=>(options[position_in] || options[:position]))
end
# Return a list of the model files to annotate.
# Return a list of the model files to annotate.
# If we have command line arguments, they're assumed to the path
# of model files from root dir. Otherwise we take all the model files
# of model files from root dir. Otherwise we take all the model files
# in the model_dir directory.
def get_model_files(options)
models = []
......@@ -364,7 +392,7 @@ module AnnotateModels
begin
model_dir.each do |dir|
Dir.chdir(dir) do
lst =
lst =
if options[:ignore_model_sub_dir]
Dir["*.rb"].map{ |f| [dir, f] }
else
......@@ -451,6 +479,7 @@ module AnnotateModels
def annotate_model_file(annotated, file, header, options)
begin
return false if (/# -\*- SkipSchemaAnnotations.*/ =~ (File.exist?(file) ? File.read(file) : '') )
klass = get_model_class(file)
if klass && klass < ActiveRecord::Base && !klass.abstract_class? && klass.table_exists?
if annotate(klass, file, header, options)
......@@ -477,7 +506,7 @@ module AnnotateModels
model_file_name = file
deannotated_klass = true if(remove_annotation_of_file(model_file_name))
(TEST_PATTERNS + FIXTURE_PATTERNS + FACTORY_PATTERNS).
(TEST_PATTERNS + FIXTURE_PATTERNS + FACTORY_PATTERNS + SERIALIZER_PATTERNS).
map { |file| resolve_filename(file, model_name, table_name) }.
each do |file|
if File.exist?(file)
......
module Annotate
def self.version
'2.6.8'
'2.6.9'
end
end
......@@ -6,27 +6,30 @@ if Rails.env.development?
# You can override any of these by setting an environment variable of the
# same name.
Annotate.set_defaults({
'position_in_routes' => "before",
'position_in_class' => "before",
'position_in_test' => "before",
'position_in_fixture' => "before",
'position_in_factory' => "before",
'show_indexes' => "true",
'simple_indexes' => "false",
'model_dir' => "app/models",
'include_version' => "false",
'require' => "",
'exclude_tests' => "false",
'exclude_fixtures' => "false",
'exclude_factories' => "false",
'ignore_model_sub_dir' => "false",
'skip_on_db_migrate' => "false",
'format_bare' => "true",
'format_rdoc' => "false",
'format_markdown' => "false",
'sort' => "false",
'force' => "false",
'trace' => "false",
'position_in_routes' => "before",
'position_in_class' => "before",
'position_in_test' => "before",
'position_in_fixture' => "before",
'position_in_factory' => "before",
'position_in_serializer' => "before",
'show_foreign_keys' => "true",
'show_indexes' => "true",
'simple_indexes' => "false",
'model_dir' => "app/models",
'include_version' => "false",
'require' => "",
'exclude_tests' => "false",
'exclude_fixtures' => "false",
'exclude_factories' => "false",
'exclude_serializers' => "false",
'ignore_model_sub_dir' => "false",
'skip_on_db_migrate' => "false",
'format_bare' => "true",
'format_rdoc' => "false",
'format_markdown' => "false",
'sort' => "false",
'force' => "false",
'trace' => "false",
})
end
......
......@@ -16,6 +16,7 @@ task :annotate_models => :environment do
options[:position_in_fixture] = Annotate.fallback(ENV['position_in_fixture'], ENV['position'])
options[:position_in_factory] = Annotate.fallback(ENV['position_in_factory'], ENV['position'])
options[:position_in_test] = Annotate.fallback(ENV['position_in_test'], ENV['position'])
options[:position_in_serializer] = Annotate.fallback(ENV['position_in_serializer'], ENV['position'])
options[:show_indexes] = Annotate.true?(ENV['show_indexes'])
options[:simple_indexes] = Annotate.true?(ENV['simple_indexes'])
options[:model_dir] = ENV['model_dir'] ? ENV['model_dir'].split(',') : []
......@@ -24,6 +25,7 @@ task :annotate_models => :environment do
options[:exclude_tests] = Annotate.true?(ENV['exclude_tests'])
options[:exclude_factories] = Annotate.true?(ENV['exclude_factories'])
options[:exclude_fixtures] = Annotate.true?(ENV['exclude_fixtures'])
options[:exclude_serializers] = Annotate.true?(ENV['exclude_serializers'])
options[:ignore_model_sub_dir] = Annotate.true?(ENV['ignore_model_sub_dir'])
options[:format_bare] = Annotate.true?(ENV['format_bare'])
options[:format_rdoc] = Annotate.true?(ENV['format_rdoc'])
......
......@@ -4,15 +4,32 @@ require 'annotate/annotate_models'
require 'annotate/active_record_patch'
describe AnnotateModels do
def mock_class(table_name, primary_key, columns)
def mock_foreign_key(name, from_column, to_table, to_column = 'id')
double("ForeignKeyDefinition",
:name => name,
:column => from_column,
:to_table => to_table,
:primary_key => to_column,
)
end
def mock_connection(indexes = [], foreign_keys = [])
double("Conn",
:indexes => indexes,
:foreign_keys => foreign_keys,
)
end
def mock_class(table_name, primary_key, columns, foreign_keys = [])
options = {
:connection => double("Conn", :indexes => []),
:table_name => table_name,
:primary_key => primary_key,
:column_names => columns.map { |col| col.name.to_s },
:columns => columns,
:column_defaults => Hash[columns.map { |col|
[col.name, col.default]
:connection => mock_connection([], foreign_keys),
:table_exists? => true,
:table_name => table_name,
:primary_key => primary_key,
:column_names => columns.map { |col| col.name.to_s },
:columns => columns,
:column_defaults => Hash[columns.map { |col|
[col.name, col.default]
}]
}
......@@ -28,7 +45,7 @@ describe AnnotateModels do
stubs = default_options.dup
stubs.merge!(options)
stubs.merge!(:name => name, :type => type)
stubs.merge!(:name => name, :sql_type => type, :type => type)
double("Column", stubs)
end
......@@ -127,6 +144,33 @@ EOS
EOS
end
it "should get foreign key info" do
klass = mock_class(:users, :id, [
mock_column(:id, :integer),
mock_column(:foreign_thing_id, :integer),
],
[
mock_foreign_key(
'fk_rails_02e851e3b7',
'foreign_thing_id',
'foreign_things'
)
])
expect(AnnotateModels.get_schema_info(klass, "Schema Info", :show_foreign_keys => true)).to eql(<<-EOS)
# Schema Info
#
# Table name: users
#
# id :integer not null, primary key
# foreign_thing_id :integer not null
#
# Foreign Keys
#
# fk_rails_02e851e3b7 (foreign_thing_id => foreign_things.id)
#
EOS
end
it "should get schema info as RDoc" do
klass = mock_class(:users, :id, [
mock_column(:id, :integer),
......
......@@ -11,6 +11,7 @@ if Rails.env.development?
'position_in_test' => "before",
'position_in_fixture' => "before",
'position_in_factory' => "before",
'show_foreign_keys' => "true",
'show_indexes' => "true",
'simple_indexes' => "false",
'model_dir' => "app/models",
......
......@@ -11,6 +11,7 @@ if Rails.env.development?
'position_in_test' => "before",
'position_in_fixture' => "before",
'position_in_factory' => "before",
'show_foreign_keys' => "true",
'show_indexes' => "true",
'simple_indexes' => "false",
'model_dir' => "app/models",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment