When I use an ORM like Sequel, the columns of a Model are defined by the connected database. How can I get a documentation for the columns of the table?
Example (Sequel, but I think the principle is the same for AR):
I create a table with this migration:
Sequel.migration do
up do
create_table(:person) do
primary_key :id
String :name, :null=>false
end
end
down do
drop_table(:person)
end
end
There is no way to add a column description to the database (or is there one? Please no DB-specific solution).
The corresponding Model is defined as
class Person < Sequel::Model
end
When I generate my documentation with rdoc, the documentation for Person is "empty", I get no description of the columns.
I can add a getter-method for a column like here:
class Person < Sequel::Model
#Name of the person
attr_reader :name
end
I get a description with rdoc, but there are two problems:
Person#name
is always nil, the connection to the DB is lostI could add a list of all columns:
#Columns:
# * name: Name of the person
# ...
class Person < Sequel::Model
end
But again:
I founds the annotate-gem. But this gem deletes previous comments and it adds only technical informations. I can not add comments.
Some other model frameworks such as datamapper, mongomaper and mongoid directly define the attributes in the models. (source) But I don't want to change my ORM.
So my question: What is the best-practice to store descriptions of the columns?
Up to now no answer - perhaps there is no best-practice.
In meantime I had an idea with a modifies the idea of the annotate-gem.
The attached script creates a new ruby file with the documentation for the models. This ruby file may not be added to your application (it will break the models), but you can add it to your rdoc-call.
Add the following code att the end of the following script:
require 'your_application' #Get your model definitions
#Define ModelDocumentation with comments for the different fields
doc = ModelDoc.new(
#reference with table.field
'tables.field1' => 'field one of table tables',
#reference with model.field
'Table#field2' => "field two of table, referenced by model Table",
)
doc.save('my_doc.rb')
And now the script:
#encoding: utf-8
=begin rdoc
Generate documentation file for Model,
enriched with data from docdata
=end
=begin rdoc
Get all subclasses from a class.
Code from http://stackoverflow.com/a/436724/676874
=end
class Class
def subclasses
ObjectSpace.each_object(Class).select { |klass| klass < self }
end
end
=begin rdoc
Class to generate a Model-documentation
==Usage:
Write a script like this:
require 'my_application'
require 'model_doc' #this script
doc = ModelDoc.new(
#reference with table.field
'tables.field1' => 'field one of table tables',
#reference with model.field
'Table#field2' => "field two of table, referenced by model Table",
)
doc.save('my_doc.rb')
my_doc.rb can be included to your library.
Don't load it to your application!
Only add it to your documentation with rdoc.
=end
class ModelDoc
=begin rdoc
Class to generate a Model-documentation.
Comments are a hash with.
Keys may be:
* Modelname#field
* tablename.field
=end
def initialize( comments = {} )
@comments = comments
end
def save(filename)
docfile = File.new(filename, 'w')
puts "(Re-)Generate #{docfile.path}"
docfile << <<doc
=begin
This file is generated by #{__FILE__},
based on data from #{Sequel::Model.db.inspect}
Don't use this code in your implementation!
This code will overwrite your real model.
This is only for usage with rdoc.
=end
doc
Sequel::Model.subclasses.each{|model|
docfile << "\n\n#\n"
docfile << "#Model for table '#{model.table_name}'\n"
docfile << "#\n"
docfile << "class #{model} < Sequel::Model\n"
model.columns.each{|column|
comment = @comments[[model, column].join('#')]
comment = @comments[[model.table_name, column].join('.')] unless comment
docfile << " #\n #table field #{field_description(model.table_name, column, model.db_schema[column]).join("\n #* ")}\n"
if comment
docfile << comment.gsub(/^/, " #")
docfile << "\n"
end
#~ docfile << " #\n#accessor generated by Model\n"
docfile << " attr_reader #{column.inspect}\n"
}
docfile << "end\n\n"
}
docfile.close
end
def field_description(table_name, column, db_schema)
[
"#{table_name}.#{column} (#{db_schema[:type]}/#{db_schema[:db_type]})",
db_schema[:primary_key] ? "This is a key field" : nil,
"Default is #{db_schema[:default] || 'undefined'}",
#fixme: ruby_default
"Null-values are #{'not ' unless db_schema[:allow_null]}allowed",
].compact
end
end #ModelDoc
This script works only for Sequel, but I think it can be adapted for AR.