remi-activerecord-comments 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+