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 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,7 @@
1
+ module ActiveRecord::Comments::ColumnExt
2
+ attr_reader :table_name
3
+
4
+ def comment
5
+ ActiveRecord::Base.column_comment name, table_name
6
+ end
7
+ 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
@@ -0,0 +1,6 @@
1
+ ---
2
+ adapter: mysql
3
+ database: activerecord_comments_test_db
4
+ host: localhost
5
+ username: root
6
+ password:
@@ -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
+