remi-activerecord-comments 0.1.0
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.
- data/README.markdown +79 -0
- data/lib/activerecord-comments/base_ext.rb +111 -0
- data/lib/activerecord-comments/column_ext.rb +7 -0
- data/lib/activerecord-comments/mysql_adapter.rb +72 -0
- data/lib/activerecord-comments.rb +25 -0
- data/spec/mysql_comments_spec.rb +76 -0
- data/spec/spec_database.yml +6 -0
- data/spec/spec_helper.rb +26 -0
- metadata +63 -0
data/README.markdown
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
activerecord-comments
|
2
|
+
=====================
|
3
|
+
|
4
|
+
Super-duper simple gem for getting table/column comments defined in the database.
|
5
|
+
|
6
|
+
Background
|
7
|
+
----------
|
8
|
+
|
9
|
+
I wanted a way to easily get to the comments defined in the database, via ActiveRecord.
|
10
|
+
While the underlying implementation may change to become faster and more database agnostic,
|
11
|
+
the public API should remain the same.
|
12
|
+
|
13
|
+
Install
|
14
|
+
-------
|
15
|
+
|
16
|
+
$ sudo gem install remi-activerecord-comments -s http://gems.github.com
|
17
|
+
|
18
|
+
Usage
|
19
|
+
-----
|
20
|
+
|
21
|
+
>> require 'activerecord-comments'
|
22
|
+
|
23
|
+
>> Fox.comment
|
24
|
+
=> "Represents a Fox, a creature that craves chunky bacon"
|
25
|
+
|
26
|
+
>> ActiveRecord::Base.comment :foxes
|
27
|
+
=> "Represents a Fox, a creature that craves chunky bacon"
|
28
|
+
|
29
|
+
>> Fox.columns
|
30
|
+
=> [#<ActiveRecord::...>, #<ActiveRecord::...>]
|
31
|
+
|
32
|
+
>> Fox.column_names
|
33
|
+
=> ["id", "name", "has_had_truck_stolen"]
|
34
|
+
|
35
|
+
>> Fox.column_comments
|
36
|
+
=> ["Primary Key", "Fox's name", "Whether or not this Fox has had his/her truck stolen"]
|
37
|
+
|
38
|
+
>> Fox.columns.first.name
|
39
|
+
=> "id"
|
40
|
+
|
41
|
+
>> Fox.columns.first.comment
|
42
|
+
=> "Primary Key"
|
43
|
+
|
44
|
+
>> Fox.column_comment :id
|
45
|
+
=> "Primary Key"
|
46
|
+
|
47
|
+
>> ActiveRecord::Base.column_comment :id, :foxes
|
48
|
+
=> "Primary Key"
|
49
|
+
|
50
|
+
|
51
|
+
Database Support
|
52
|
+
----------------
|
53
|
+
|
54
|
+
For right now, I'm just supporting MySQL as it's the only database I'm currently using
|
55
|
+
that supports database comments.
|
56
|
+
|
57
|
+
If you want to extend activerecord-comments to support comments for your favorite database,
|
58
|
+
the gem is coded in such a way that it should be really easy to extend.
|
59
|
+
|
60
|
+
See [mysql_adapter.rb][mysql_adapter] for an example of the methods your database adapter
|
61
|
+
needs to support (just `#comment(table)` and `#column_comment(column,table)`).
|
62
|
+
|
63
|
+
|
64
|
+
SQL
|
65
|
+
---
|
66
|
+
|
67
|
+
If you're unsure how to add comments to your MySQL tables/columns, most MySQL GUIs support
|
68
|
+
this, or you can add comments to your `CREATE TABLE` declarations ...
|
69
|
+
|
70
|
+
CREATE TABLE foo (
|
71
|
+
id INT COMMENT 'i am the primary key',
|
72
|
+
foo VARCHAR(100) COMMENT 'foo!'
|
73
|
+
) COMMENT 'this table rocks'
|
74
|
+
|
75
|
+
for more MySQL examples, see [spec/mysql_comments_spec.rb][mysql_spec]
|
76
|
+
|
77
|
+
|
78
|
+
[mysql_adapter]: http://github.com/remi/activerecord-comments/tree/master/lib/activerecord-comments/mysql_adapter.rb
|
79
|
+
[mysql_spec]: http://github.com/remi/activerecord-comments/tree/master/spec/mysql_comments_spec.rb
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module ActiveRecord::Comments::BaseExt
|
2
|
+
|
3
|
+
def self.included base
|
4
|
+
base.extend ClassMethods
|
5
|
+
|
6
|
+
base.instance_eval {
|
7
|
+
class << self
|
8
|
+
alias_method_chain :columns, :table_name # this is evil!!! how to fix? column needs to know its table :(
|
9
|
+
end
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
|
15
|
+
# Get the database comment (if any) defined for a table
|
16
|
+
#
|
17
|
+
# ==== Parameters
|
18
|
+
# table<~to_s>::
|
19
|
+
# The name of the table to get the comment for, default is
|
20
|
+
# the #table_name of the ActiveRecord::Base class this is
|
21
|
+
# being called on, eg. +User.comment+
|
22
|
+
#
|
23
|
+
# ==== Returns
|
24
|
+
# String:: The comment for the given table (or nil if no comment)
|
25
|
+
#
|
26
|
+
# :api: public
|
27
|
+
def comment table = self.table_name
|
28
|
+
adapter = connection.adapter_name.downcase
|
29
|
+
database_specific_method_name = "#{ adapter }_comment"
|
30
|
+
|
31
|
+
if self.respond_to? database_specific_method_name
|
32
|
+
send database_specific_method_name, table.to_s
|
33
|
+
else
|
34
|
+
|
35
|
+
# try requiring 'activerecord-comments/[name-of-adapter]_adapter'
|
36
|
+
begin
|
37
|
+
|
38
|
+
# see if there right method exists after requiring
|
39
|
+
require "activerecord-comments/#{ adapter }_adapter"
|
40
|
+
if self.respond_to? database_specific_method_name
|
41
|
+
send database_specific_method_name, table.to_s
|
42
|
+
else
|
43
|
+
raise ActiveRecord::Comments::UnsupportedDatabase.new("#{adapter} unsupported by ActiveRecord::Comments")
|
44
|
+
end
|
45
|
+
|
46
|
+
rescue LoadError
|
47
|
+
raise ActiveRecord::Comments::UnsupportedDatabase.new("#{adapter} unsupported by ActiveRecord::Comments")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get the database comment (if any) defined for a column
|
53
|
+
#
|
54
|
+
# ==== Parameters
|
55
|
+
# column<~to_s>::
|
56
|
+
# The name of the column to get the comment for
|
57
|
+
#
|
58
|
+
# table<~to_s>::
|
59
|
+
# The name of the table to get the column comment for, default is
|
60
|
+
# the #table_name of the ActiveRecord::Base class this is
|
61
|
+
# being called on, eg. +User.column_comment 'username'+
|
62
|
+
#
|
63
|
+
# ==== Returns
|
64
|
+
# String:: The comment for the given column (or nil if no comment)
|
65
|
+
#
|
66
|
+
# :api: public
|
67
|
+
def column_comment column, table = self.table_name
|
68
|
+
adapter = connection.adapter_name.downcase
|
69
|
+
database_specific_method_name = "#{ adapter }_column_comment"
|
70
|
+
|
71
|
+
if self.respond_to? database_specific_method_name
|
72
|
+
send database_specific_method_name, column.to_s, table.to_s
|
73
|
+
else
|
74
|
+
|
75
|
+
# try requiring 'activerecord-comments/[name-of-adapter]_adapter'
|
76
|
+
begin
|
77
|
+
|
78
|
+
# see if there right method exists after requiring
|
79
|
+
require "activerecord-comments/#{ adapter }_adapter"
|
80
|
+
if self.respond_to? database_specific_method_name
|
81
|
+
send database_specific_method_name, column.to_s, table.to_s
|
82
|
+
else
|
83
|
+
raise ActiveRecord::Comments::UnsupportedDatabase.new("#{adapter} unsupported by ActiveRecord::Comments")
|
84
|
+
end
|
85
|
+
|
86
|
+
rescue LoadError
|
87
|
+
raise ActiveRecord::Comments::UnsupportedDatabase.new("#{adapter} unsupported by ActiveRecord::Comments")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Extends ActiveRecord::Base#columns, setting @table_name as an instance variable
|
93
|
+
# on each of the column instances that are returned
|
94
|
+
#
|
95
|
+
# ==== Returns
|
96
|
+
# Array[ActiveRecord::ConnectionAdapters::Column]::
|
97
|
+
# Returns an Array of column objects, each with @table_name set
|
98
|
+
#
|
99
|
+
# :api: private
|
100
|
+
def columns_with_table_name *args
|
101
|
+
columns = columns_without_table_name *args
|
102
|
+
table = self.table_name # make table_name available as variable in instanve_eval closure
|
103
|
+
columns.each do |column|
|
104
|
+
column.instance_eval { @table_name = table }
|
105
|
+
end
|
106
|
+
columns
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module ActiveRecord::Comments::MysqlAdapter
|
2
|
+
|
3
|
+
def self.included base
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
# MySQL implementation of ActiveRecord::Comments::BaseExt#comment
|
10
|
+
def mysql_comment table
|
11
|
+
table_options = create_table_sql(table).split("\n").last
|
12
|
+
if table_options =~ /COMMENT='/
|
13
|
+
/COMMENT='(.*)'/.match(table_options).captures.first
|
14
|
+
else
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# MySQL implementation of ActiveRecord::Comments::BaseExt#column_comment
|
20
|
+
def mysql_column_comment column, table
|
21
|
+
column_creation_sql = create_column_sql(column, table)
|
22
|
+
if column_creation_sql =~ /COMMENT '/
|
23
|
+
/COMMENT '(.*)'/.match(column_creation_sql).captures.first
|
24
|
+
else
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Returns the SQL used to create the given table
|
32
|
+
#
|
33
|
+
# ==== Parameters
|
34
|
+
# table<~to_s>::
|
35
|
+
# The name of the table to get the 'CREATE TABLE' SQL for
|
36
|
+
#
|
37
|
+
# ==== Returns
|
38
|
+
# String:: the SQL used to create the table
|
39
|
+
#
|
40
|
+
# :api: private
|
41
|
+
def create_table_sql table = table_name
|
42
|
+
connection.execute("show create table `#{ table }`").all_hashes.first['Create Table']
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the SQL used to create the given column for the given table
|
46
|
+
#
|
47
|
+
# ==== Parameters
|
48
|
+
# column<~to_s>::
|
49
|
+
# The name of the column to get the creation SQL for
|
50
|
+
#
|
51
|
+
# table<~to_s>::
|
52
|
+
# The name of the table to get the 'CREATE TABLE' SQL for
|
53
|
+
#
|
54
|
+
# ==== Returns
|
55
|
+
# String:: the SQL used to create the column
|
56
|
+
#
|
57
|
+
# :api: private
|
58
|
+
def create_column_sql column, table = table_name
|
59
|
+
full_table_create_sql = create_table_sql(table)
|
60
|
+
parts = full_table_create_sql.split("\n")
|
61
|
+
create_table = parts.shift # take off the first CREATE TABLE part
|
62
|
+
create_table_options = parts.pop # take off the last options for the table, leaving just the columns
|
63
|
+
sql_for_this_column = parts.find {|str| str =~ /^ *`#{ column }`/ }
|
64
|
+
sql_for_this_column.strip! if sql_for_this_column
|
65
|
+
sql_for_this_column
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
ActiveRecord::Base.send :include, ActiveRecord::Comments::MysqlAdapter
|
@@ -0,0 +1,25 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
if defined?(ActiveRecord)
|
4
|
+
|
5
|
+
# Main class for ActiveRecord::Comments ActiveRecord extension gem
|
6
|
+
#
|
7
|
+
# Used for global configuration options / etc for the extension
|
8
|
+
class ActiveRecord::Comments
|
9
|
+
end
|
10
|
+
|
11
|
+
class ActiveRecord::Comments::UnsupportedDatabase < Exception; end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
if defined?(ActiveRecord::Base) && defined?(ActiveRecord::ConnectionAdapters::Column)
|
16
|
+
|
17
|
+
# require and include our modules which add the 'comment' functionality to ActiveRecord
|
18
|
+
|
19
|
+
require 'activerecord-comments/base_ext'
|
20
|
+
require 'activerecord-comments/column_ext'
|
21
|
+
|
22
|
+
ActiveRecord::Base.send :include, ActiveRecord::Comments::BaseExt
|
23
|
+
ActiveRecord::ConnectionAdapters::Column.send :include, ActiveRecord::Comments::ColumnExt
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRecord::Comments, 'MySQL support' do
|
4
|
+
|
5
|
+
# HELPERS
|
6
|
+
|
7
|
+
def connection
|
8
|
+
ActiveRecord::Base.connection
|
9
|
+
end
|
10
|
+
|
11
|
+
def drop_table
|
12
|
+
connection.execute 'DROP TABLE IF EXISTS foxes;'
|
13
|
+
end
|
14
|
+
|
15
|
+
before do
|
16
|
+
drop_table
|
17
|
+
@foxes = Class.new(ActiveRecord::Base){ set_table_name 'foxes' }
|
18
|
+
end
|
19
|
+
|
20
|
+
# EXAMPLES
|
21
|
+
|
22
|
+
it "should create table OK" do
|
23
|
+
connection.execute "CREATE TABLE foxes( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY );"
|
24
|
+
@foxes.count.should == 0
|
25
|
+
end
|
26
|
+
|
27
|
+
it "Model#comment should return nil for a model that doesn't have a database comment" do
|
28
|
+
connection.execute "CREATE TABLE foxes( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY );"
|
29
|
+
@foxes.comment.should be_nil
|
30
|
+
ActiveRecord::Base.comment(:foxes).should be_nil
|
31
|
+
ActiveRecord::Base.comment('foxes').should be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "Model#comment should return the database comment for a model that has a database comment" do
|
35
|
+
connection.execute "CREATE TABLE foxes( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY ) COMMENT 'foxes Rule';"
|
36
|
+
@foxes.comment.should == 'foxes Rule'
|
37
|
+
ActiveRecord::Base.comment(:foxes).should == 'foxes Rule'
|
38
|
+
ActiveRecord::Base.comment('foxes').should == 'foxes Rule'
|
39
|
+
end
|
40
|
+
|
41
|
+
it "Model#comment should return the database comment for a model that has a database comment in different formats" do
|
42
|
+
pending
|
43
|
+
#[ 'foxes Rule', 'i has Numbers123', 'i have " double " quotes', "i have ' single quotes'" ].each do
|
44
|
+
# connection.execute "CREATE TABLE foxes( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY ) COMMENT 'foxes Rule';"
|
45
|
+
#@foxes.comment.should == 'foxes Rule'
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'Model#column_comment should return the comment for a column' do
|
49
|
+
connection.execute "CREATE TABLE foxes( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'i am the ID column' );"
|
50
|
+
@foxes.column_comment('id').should == 'i am the ID column'
|
51
|
+
@foxes.column_comment(:id).should == 'i am the ID column'
|
52
|
+
ActiveRecord::Base.column_comment(:id, :foxes).should == 'i am the ID column'
|
53
|
+
ActiveRecord::Base.column_comment(:id, 'foxes').should == 'i am the ID column'
|
54
|
+
ActiveRecord::Base.column_comment('id', 'foxes').should == 'i am the ID column'
|
55
|
+
end
|
56
|
+
|
57
|
+
it "@column#comment should return nil for a column that doesn't have a database comment" do
|
58
|
+
connection.execute "CREATE TABLE foxes( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY );"
|
59
|
+
@foxes.columns.first.name.should == 'id'
|
60
|
+
@foxes.columns.first.comment.should be_nil
|
61
|
+
end
|
62
|
+
|
63
|
+
# need to add this extension (tho it's yucky) so a column can easily find its comment (needs its table name)
|
64
|
+
it "@column should know its #table_name" do
|
65
|
+
connection.execute "CREATE TABLE foxes( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY );"
|
66
|
+
@foxes.columns.length.should == 1
|
67
|
+
@foxes.columns.first.table_name.should == 'foxes'
|
68
|
+
end
|
69
|
+
|
70
|
+
it "@column#comment should return the database comment for a column that has a database comment" do
|
71
|
+
connection.execute "CREATE TABLE foxes( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'i am the ID column' );"
|
72
|
+
@foxes.columns.first.name.should == 'id'
|
73
|
+
@foxes.columns.first.comment.should == 'i am the ID column'
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
require 'yaml'
|
4
|
+
begin
|
5
|
+
require 'activerecord'
|
6
|
+
rescue LoadError
|
7
|
+
raise "dependency ActiveRecord not found. try: $ sudo gem install activerecord"
|
8
|
+
end
|
9
|
+
|
10
|
+
require File.dirname(__FILE__) + '/../lib/activerecord-comments'
|
11
|
+
|
12
|
+
# right now, we run all tests against MySQL (I would also do sqlite but I don't think it supports comments!)
|
13
|
+
database_hash = YAML::load File.read(File.dirname(__FILE__) + '/spec_database.yml')
|
14
|
+
ActiveRecord::Base.establish_connection database_hash
|
15
|
+
|
16
|
+
begin
|
17
|
+
# touch the connection to see if it's OK
|
18
|
+
ActiveRecord::Base.connection
|
19
|
+
rescue Mysql::Error => ex
|
20
|
+
if ex.to_s =~ /unknown database/i
|
21
|
+
db = database_hash['database']
|
22
|
+
raise "\n\nMySQL database not found: #{db}.\ntry: $ mysqladmin create #{db}\n\n"
|
23
|
+
else
|
24
|
+
raise ex
|
25
|
+
end
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: remi-activerecord-comments
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- remi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-02 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Provides an easy to access database table/column comments from ActiveRecord
|
17
|
+
email: remi@remitaylor.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- VERSION.yml
|
26
|
+
- README.markdown
|
27
|
+
- lib/activerecord-comments
|
28
|
+
- lib/activerecord-comments/column_ext.rb
|
29
|
+
- lib/activerecord-comments/mysql_adapter.rb
|
30
|
+
- lib/activerecord-comments/base_ext.rb
|
31
|
+
- lib/activerecord-comments.rb
|
32
|
+
- spec/mysql_comments_spec.rb
|
33
|
+
- spec/spec_database.yml
|
34
|
+
- spec/spec_helper.rb
|
35
|
+
has_rdoc: true
|
36
|
+
homepage: http://github.com/remi/activerecord-comments
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options:
|
39
|
+
- --inline-source
|
40
|
+
- --charset=UTF-8
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: "0"
|
48
|
+
version:
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 1.2.0
|
59
|
+
signing_key:
|
60
|
+
specification_version: 2
|
61
|
+
summary: Provides an easy to access database table/column comments from ActiveRecord
|
62
|
+
test_files: []
|
63
|
+
|