Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for foreign key annotations #241

Merged
merged 1 commit into from Mar 27, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions README.rdoc
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions bin/annotate
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion lib/annotate.rb
Expand Up @@ -26,7 +26,7 @@ module Annotate
: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
Expand Down
32 changes: 29 additions & 3 deletions lib/annotate/annotate_models.rb
Expand Up @@ -193,6 +193,10 @@ def get_schema_info(klass, header, options = {})
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"
Expand Down Expand Up @@ -223,6 +227,28 @@ def get_index_info(klass, options={})
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
Expand Down Expand Up @@ -350,9 +376,9 @@ def options_with_position(options, position_in)
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 = []
Expand All @@ -364,7 +390,7 @@ def get_model_files(options)
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
Expand Down
Expand Up @@ -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",
Expand Down
60 changes: 52 additions & 8 deletions spec/annotate/annotate_models_spec.rb
Expand Up @@ -4,15 +4,32 @@
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]
}]
}

Expand Down Expand Up @@ -127,6 +144,33 @@ def mock_column(name, type, options={})
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),
Expand Down
Expand Up @@ -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",
Expand Down
Expand Up @@ -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",
Expand Down