sequel-annotate 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: eb5fcadf130d2d23412b4597e7e0b1fa4c2ef7c0
4
+ data.tar.gz: efa509bb8630ad280e036c47105d4f3fd84a8a54
5
+ SHA512:
6
+ metadata.gz: 9ea01f3487452deeb82a3b34b5c2469c47d7e10e843dc66d60b68e4a762e841d21cbe73611ff3f400006dc5acbbc57db1a47598082200f5a132b1c2abd103e42
7
+ data.tar.gz: 945371efe3340ddd3a41f887572d6214b6940659a16cb9602cce9cec9cfd0f1ec104ef077a87b4658dff725bbd709cc131d09fda282e655cd441b794b948dffc
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ === 1.0.0 (2014-09-15)
2
+
3
+ * Initial Public Release
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2015 Jeremy Evans
2
+ Copyright (c) 2013-2015 Kenny Meyer
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,73 @@
1
+ = sequel-annotate
2
+
3
+ sequel-annotate annotates Sequel models with schema information. By
4
+ default, it includes information on columns, indexes, and foreign key
5
+ constraints for the current table.
6
+
7
+ On PostgreSQL, this includes more advanced information, including
8
+ check constraints, triggers, and foreign keys constraints for other
9
+ tables that reference the current table.
10
+
11
+ == Example
12
+
13
+ The schema comments are kept at the end of the file, using a format similar to:
14
+
15
+ class Item < Sequel::Model
16
+ end
17
+
18
+ # Table: items
19
+ # Columns:
20
+ # id | integer | PRIMARY KEY DEFAULT nextval('items_id_seq'::regclass)
21
+ # category_id | integer | NOT NULL
22
+ # manufacturer_name | character varying(50) |
23
+ # manufacturer_location | text |
24
+ # in_stock | boolean | DEFAULT false
25
+ # name | text | DEFAULT 'John'::text
26
+ # price | double precision | DEFAULT 0
27
+ # Indexes:
28
+ # items_pkey | PRIMARY KEY btree (id)
29
+ # name | UNIQUE btree (manufacturer_name, manufacturer_location)
30
+ # manufacturer_name | btree (manufacturer_name)
31
+ # Check constraints:
32
+ # pos_id | (id > 0)
33
+ # Foreign key constraints:
34
+ # items_category_id_fkey | (category_id) REFERENCES categories(id)
35
+ # items_manufacturer_name_fkey | (manufacturer_name, manufacturer_location) REFERENCES manufacturers(name, location)
36
+ # Triggers:
37
+ # valid_price | BEFORE INSERT OR UPDATE ON items FOR EACH ROW EXECUTE PROCEDURE valid_price()
38
+
39
+ == Install
40
+
41
+ gem install sequel-annotate
42
+
43
+ == Usage
44
+
45
+ After loading the models:
46
+
47
+ require 'sequel/annotate'
48
+ Sequel::Annotate.annotate(Dir['models/*.rb'])
49
+
50
+ That will append or replace the schema comment in each of the files. It's best
51
+ to run this when the repository is clean, and then use your source control
52
+ tools (e.g. git diff) to see the changes it makes.
53
+
54
+ In some cases, sequel-annotate may not be able to correctly guess the
55
+ model for the file. In that case, you may need to create an instance manually:
56
+
57
+ sa = Sequel::Annotate.new(Item)
58
+ sa.annotate('models/item.rb')
59
+
60
+ If you want to get the schema comment for a model without appending it to
61
+ a file:
62
+
63
+ sa.schema_comment
64
+
65
+ == License
66
+
67
+ MIT
68
+
69
+ == Author
70
+
71
+ Jeremy Evans <code@jeremyevans.net>
72
+
73
+ Based on [annotate-sequel]{https://github.com/kennym/annotate-sequel} by Kenny Meyer.
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ require "rake"
2
+ require "rake/clean"
3
+
4
+ CLEAN.include ["sequel-annotate-*.gem", "rdoc"]
5
+
6
+ desc "Build sequel-annotate gem"
7
+ task :package=>[:clean] do |p|
8
+ sh %{#{FileUtils::RUBY} -S gem build sequel-annotate.gemspec}
9
+ end
10
+
11
+ ### Specs
12
+
13
+ desc "Run specs"
14
+ task :spec do
15
+ sh "#{FileUtils::RUBY} -rubygems -I lib spec/sequel-annotate_spec.rb"
16
+ end
17
+
18
+ task :default => :spec
19
+
20
+ ### RDoc
21
+
22
+ RDOC_DEFAULT_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', 'sequel-annotate: Annotate Sequel models with schema information']
23
+
24
+ begin
25
+ gem 'rdoc'
26
+ gem 'hanna-nouveau'
27
+ RDOC_DEFAULT_OPTS.concat(['-f', 'hanna'])
28
+ rescue Gem::LoadError
29
+ end
30
+
31
+ rdoc_task_class = begin
32
+ require "rdoc/task"
33
+ RDoc::Task
34
+ rescue LoadError
35
+ require "rake/rdoctask"
36
+ Rake::RDocTask
37
+ end
38
+
39
+ RDOC_OPTS = RDOC_DEFAULT_OPTS + ['--main', 'README.rdoc']
40
+
41
+ rdoc_task_class.new do |rdoc|
42
+ rdoc.rdoc_dir = "rdoc"
43
+ rdoc.options += RDOC_OPTS
44
+ rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/**/*.rb"
45
+ end
46
+
@@ -0,0 +1,211 @@
1
+ require 'sequel'
2
+
3
+ module Sequel
4
+ class Annotate
5
+ # Append/replace the schema comment for all of the given files.
6
+ # Attempts to guess the model for each file using a regexp match of
7
+ # the file's content, if this doesn't work, you'll need to create
8
+ # an instance manually and pass in the model and path. Example:
9
+ #
10
+ # Sequel::Annotate.annotate(Dir['models/*.rb'])
11
+ def self.annotate(paths)
12
+ Sequel.extension :inflector
13
+ paths.each do |path|
14
+ if match = File.read(path).match(/class (\S+)\s*<\s*Sequel::Model/)
15
+ new(match[1].constantize).annotate(path)
16
+ end
17
+ end
18
+ end
19
+
20
+ # The model to annotate
21
+ attr_reader :model
22
+
23
+ # Store the model to annotate
24
+ def initialize(model)
25
+ @model = model
26
+ end
27
+
28
+ # Append the schema comment (or replace it if one already exists) to
29
+ # the file at the given path.
30
+ def annotate(path)
31
+ orig = current = File.read(path).rstrip
32
+
33
+ if m = current.reverse.match(/#{"#{$/}# Table: ".reverse}/m)
34
+ offset = current.length - m.end(0) + 1
35
+ unless current[offset..-1].match(/^[^#]/)
36
+ # If Table: comment exists, and there are no
37
+ # uncommented lines between it and the end of the file
38
+ # then replace current comment instead of appending it
39
+ current = current[0...offset].rstrip
40
+ end
41
+ end
42
+
43
+ current += "#{$/}#{$/}#{schema_comment}"
44
+
45
+ if orig != current
46
+ File.open(path, "wb") do |f|
47
+ f.puts current
48
+ end
49
+ end
50
+ end
51
+
52
+ # The schema comment to use for this model.
53
+ # For all databases, includes columns, indexes, and foreign
54
+ # key constraints in this table referencing other tables.
55
+ # On PostgreSQL, also includes check constraints, triggers,
56
+ # and foreign key constraints in other tables referencing this table.
57
+ def schema_comment
58
+ output = []
59
+ output << "# Table: #{model.table_name}"
60
+
61
+ meth = :"_schema_comment_#{model.db.database_type}"
62
+ if respond_to?(meth, true)
63
+ send(meth, output)
64
+ else
65
+ schema_comment_columns(output)
66
+ schema_comment_indexes(output)
67
+ schema_comment_foreign_keys(output)
68
+ end
69
+
70
+ output.join($/)
71
+ end
72
+
73
+ private
74
+
75
+ # Returns an array of strings for each array of array, such that
76
+ # each string is aligned and commented appropriately. Example:
77
+ #
78
+ # align([['abcdef', '1'], ['g', '123456']])
79
+ # # => ["# abcdef 1", "# g 123456"]
80
+ def align(rows)
81
+ cols = rows.first.length
82
+ lengths = [0] * cols
83
+
84
+ cols.times do |i|
85
+ rows.each do |r|
86
+ lengths[i] = r[i].length if r[i].length > lengths[i]
87
+ end
88
+ end
89
+
90
+ rows.map do |r|
91
+ "# #{r.zip(lengths).map{|c, l| c.ljust(l)}.join(' | ')}".strip
92
+ end
93
+ end
94
+
95
+ # Use the standard columns schema output, but use PostgreSQL specific
96
+ # code for additional schema information.
97
+ def _schema_comment_postgres(output)
98
+ schema_comment_columns(output)
99
+ oid = model.db.send(:regclass_oid, model.table_name)
100
+
101
+ # These queries below are all based on the queries that psql
102
+ # uses, captured using the -E option to psql.
103
+
104
+ rows = model.db.fetch(<<SQL, :oid=>oid).all
105
+ SELECT c2.relname, i.indisprimary, i.indisunique, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true)
106
+ FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
107
+ LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))
108
+ WHERE c.oid = :oid AND c.oid = i.indrelid AND i.indexrelid = c2.oid AND indisvalid
109
+ ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname;
110
+ SQL
111
+ unless rows.empty?
112
+ output << "# Indexes:"
113
+ rows = rows.map do |r|
114
+ [r[:relname], "#{"PRIMARY KEY " if r[:indisprimary]}#{"UNIQUE " if r[:indisunique] && !r[:indisprimary]}#{r[:pg_get_indexdef].match(/USING (.+)\z/)[1]}"]
115
+ end
116
+ output.concat(align(rows))
117
+ end
118
+
119
+ rows = model.db.fetch(<<SQL, :oid=>oid).all
120
+ SELECT r.conname, pg_catalog.pg_get_constraintdef(r.oid, true)
121
+ FROM pg_catalog.pg_constraint r
122
+ WHERE r.conrelid = :oid AND r.contype = 'c'
123
+ ORDER BY 1;
124
+ SQL
125
+ unless rows.empty?
126
+ output << "# Check constraints:"
127
+ rows = rows.map do |r|
128
+ [r[:conname], r[:pg_get_constraintdef].match(/CHECK (.+)\z/)[1]]
129
+ end
130
+ output.concat(align(rows))
131
+ end
132
+
133
+ rows = model.db.fetch(<<SQL, :oid=>oid).all
134
+ SELECT conname,
135
+ pg_catalog.pg_get_constraintdef(r.oid, true) as condef
136
+ FROM pg_catalog.pg_constraint r
137
+ WHERE r.conrelid = :oid AND r.contype = 'f' ORDER BY 1;
138
+ SQL
139
+ unless rows.empty?
140
+ output << "# Foreign key constraints:"
141
+ rows = rows.map do |r|
142
+ [r[:conname], r[:condef].match(/FOREIGN KEY (.+)\z/)[1]]
143
+ end
144
+ output.concat(align(rows))
145
+ end
146
+
147
+ rows = model.db.fetch(<<SQL, :oid=>oid).all
148
+ SELECT conname, conrelid::pg_catalog.regclass,
149
+ pg_catalog.pg_get_constraintdef(c.oid, true) as condef
150
+ FROM pg_catalog.pg_constraint c
151
+ WHERE c.confrelid = :oid AND c.contype = 'f' ORDER BY 2, 1;
152
+ SQL
153
+ unless rows.empty?
154
+ output << "# Referenced By:"
155
+ rows = rows.map do |r|
156
+ [r[:conrelid], r[:conname], r[:condef].match(/FOREIGN KEY (.+)\z/)[1]]
157
+ end
158
+ output.concat(align(rows))
159
+ end
160
+
161
+ rows = model.db.fetch(<<SQL, :oid=>oid).all
162
+ SELECT t.tgname, pg_catalog.pg_get_triggerdef(t.oid, true), t.tgenabled, t.tgisinternal
163
+ FROM pg_catalog.pg_trigger t
164
+ WHERE t.tgrelid = :oid AND (NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D'))
165
+ ORDER BY 1;
166
+ SQL
167
+ unless rows.empty?
168
+ output << "# Triggers:"
169
+ rows = rows.map do |r|
170
+ [r[:tgname], r[:pg_get_triggerdef].match(/((?:BEFORE|AFTER) .+)\z/)[1]]
171
+ end
172
+ output.concat(align(rows))
173
+ end
174
+ end
175
+
176
+ # The standard column schema information to output.
177
+ def schema_comment_columns(output)
178
+ if cpk = model.primary_key.is_a?(Array)
179
+ output << "# Primary Key: (#{model.primary_key.join(', ')})"
180
+ end
181
+ output << "# Columns:"
182
+ rows = model.columns.map do |col|
183
+ sch = model.db_schema[col]
184
+ [col.to_s, sch[:db_type], "#{"PRIMARY KEY #{"AUTOINCREMENT " if sch[:auto_increment] && model.db.database_type != :postgres}" if sch[:primary_key] && !cpk}#{"NOT NULL " if sch[:allow_null] == false && !sch[:primary_key]}#{"DEFAULT #{sch[:default]}" if sch[:default]}"]
185
+ end
186
+ output.concat(align(rows))
187
+ end
188
+
189
+ # The standard index information to output.
190
+ def schema_comment_indexes(output)
191
+ unless (indexes = model.db.indexes(model.table_name)).empty?
192
+ output << "# Indexes:"
193
+ rows = indexes.map do |name, metadata|
194
+ [name.to_s, "#{'UNIQUE ' if metadata[:unique]}(#{metadata[:columns].join(', ')})"]
195
+ end
196
+ output.concat(align(rows).sort)
197
+ end
198
+ end
199
+
200
+ # The standard foreign key information to output.
201
+ def schema_comment_foreign_keys(output)
202
+ unless (fks = model.db.foreign_key_list(model.table_name)).empty?
203
+ output << "# Foreign key constraints:"
204
+ rows = fks.map do |fk|
205
+ ["(#{fk[:columns].join(', ')}) REFERENCES #{fk[:table]}#{"(#{fk[:key].join(', ')})" if fk[:key]}"]
206
+ end
207
+ output.concat(align(rows).sort)
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,12 @@
1
+ class Category < Sequel::Model
2
+ end
3
+
4
+ # Table: categories
5
+ # Columns:
6
+ # id | integer | PRIMARY KEY DEFAULT nextval('categories_id_seq'::regclass)
7
+ # name | text | NOT NULL
8
+ # Indexes:
9
+ # categories_pkey | PRIMARY KEY btree (id)
10
+ # categories_name_key | UNIQUE btree (name)
11
+ # Referenced By:
12
+ # items | items_category_id_fkey | (category_id) REFERENCES categories(id)
@@ -0,0 +1,23 @@
1
+ class Item < Sequel::Model
2
+ end
3
+
4
+ # Table: items
5
+ # Columns:
6
+ # id | integer | PRIMARY KEY DEFAULT nextval('items_id_seq'::regclass)
7
+ # category_id | integer | NOT NULL
8
+ # manufacturer_name | character varying(50) |
9
+ # manufacturer_location | text |
10
+ # in_stock | boolean | DEFAULT false
11
+ # name | text | DEFAULT 'John'::text
12
+ # price | double precision | DEFAULT 0
13
+ # Indexes:
14
+ # items_pkey | PRIMARY KEY btree (id)
15
+ # name | UNIQUE btree (manufacturer_name, manufacturer_location)
16
+ # manufacturer_name | btree (manufacturer_name)
17
+ # Check constraints:
18
+ # pos_id | (id > 0)
19
+ # Foreign key constraints:
20
+ # items_category_id_fkey | (category_id) REFERENCES categories(id)
21
+ # items_manufacturer_name_fkey | (manufacturer_name, manufacturer_location) REFERENCES manufacturers(name, location)
22
+ # Triggers:
23
+ # valid_price | BEFORE INSERT OR UPDATE ON items FOR EACH ROW EXECUTE PROCEDURE valid_price()
@@ -0,0 +1,12 @@
1
+ class Manufacturer < Sequel::Model
2
+ end
3
+
4
+ # Table: manufacturers
5
+ # Primary Key: (name, location)
6
+ # Columns:
7
+ # name | text |
8
+ # location | text |
9
+ # Indexes:
10
+ # manufacturers_pkey | PRIMARY KEY btree (name, location)
11
+ # Referenced By:
12
+ # items | items_manufacturer_name_fkey | (manufacturer_name, manufacturer_location) REFERENCES manufacturers(name, location)
@@ -0,0 +1,7 @@
1
+ class SCategory < Sequel::Model(SDB[:categories])
2
+ end
3
+
4
+ # Table: categories
5
+ # Columns:
6
+ # id | integer | PRIMARY KEY AUTOINCREMENT
7
+ # name | varchar(255) | NOT NULL
@@ -0,0 +1,18 @@
1
+ class SItem < Sequel::Model(SDB[:items])
2
+ end
3
+
4
+ # Table: items
5
+ # Columns:
6
+ # id | integer | PRIMARY KEY AUTOINCREMENT
7
+ # category_id | integer | NOT NULL
8
+ # manufacturer_name | varchar(50) |
9
+ # manufacturer_location | varchar(255) |
10
+ # in_stock | boolean | DEFAULT 0
11
+ # name | varchar(255) | DEFAULT 'John'
12
+ # price | double precision | DEFAULT 0
13
+ # Indexes:
14
+ # manufacturer_name | (manufacturer_name)
15
+ # name | UNIQUE (manufacturer_name, manufacturer_location)
16
+ # Foreign key constraints:
17
+ # (category_id) REFERENCES categories
18
+ # (manufacturer_name, manufacturer_location) REFERENCES manufacturers
@@ -0,0 +1,8 @@
1
+ class SManufacturer < Sequel::Model(SDB[:manufacturers])
2
+ end
3
+
4
+ # Table: manufacturers
5
+ # Primary Key: (name, location)
6
+ # Columns:
7
+ # name | varchar(255) |
8
+ # location | varchar(255) |
@@ -0,0 +1,181 @@
1
+ require 'rubygems'
2
+ require 'fileutils'
3
+ require File.join(File.dirname(File.expand_path(__FILE__)), '../lib/sequel/annotate')
4
+ require 'minitest/autorun'
5
+
6
+ pg_user = ENV['PGUSER'] || 'postgres'
7
+ db_name = ENV['SEQUEL_ANNOTATE_DB'] || 'sequel-annotate_spec'
8
+ system("dropdb", "-U", pg_user, db_name)
9
+ system("createdb", "-U", pg_user, db_name)
10
+ DB = Sequel.postgres(db_name, :user=>pg_user)
11
+ SDB = Sequel.sqlite
12
+
13
+ [DB, SDB].each do |db|
14
+ db.create_table :categories do
15
+ primary_key :id
16
+ String :name, :unique=>true, :null=>false
17
+ end
18
+
19
+ db.create_table :manufacturers do
20
+ String :name
21
+ String :location
22
+ primary_key [:name, :location]
23
+ end
24
+
25
+ db.create_table :items do
26
+ primary_key :id
27
+ foreign_key :category_id, :categories, :null => false
28
+ foreign_key [:manufacturer_name, :manufacturer_location], :manufacturers
29
+
30
+ String :manufacturer_name, :size => 50
31
+ String :manufacturer_location
32
+ TrueClass :in_stock, :default => false
33
+ String :name, :default => "John"
34
+ Float :price, :default => 0
35
+
36
+ constraint :pos_id, Sequel.expr(:id) > 0
37
+
38
+ index [:manufacturer_name, :manufacturer_location], :name=>:name, :unique=>true
39
+ index [:manufacturer_name], :name=>:manufacturer_name
40
+ end
41
+ end
42
+
43
+ DB.run <<SQL
44
+ CREATE FUNCTION valid_price() RETURNS trigger AS $emp_stamp$
45
+ BEGIN
46
+ -- Check that empname and salary are given
47
+ IF NEW.price > 1000000 THEN
48
+ RAISE EXCEPTION 'price is too high';
49
+ END IF;
50
+ RETURN NEW;
51
+ END;
52
+ $emp_stamp$ LANGUAGE plpgsql;
53
+ SQL
54
+
55
+ DB.run <<SQL
56
+ CREATE TRIGGER valid_price BEFORE INSERT OR UPDATE ON items
57
+ FOR EACH ROW EXECUTE PROCEDURE valid_price();
58
+ SQL
59
+
60
+ class ::Item < Sequel::Model; end
61
+ class ::Category < Sequel::Model; end
62
+ class ::Manufacturer < Sequel::Model; end
63
+ class ::SItem < Sequel::Model(SDB[:items]); end
64
+ class ::SCategory < Sequel::Model(SDB[:categories]); end
65
+ class ::SManufacturer < Sequel::Model(SDB[:manufacturers]); end
66
+
67
+
68
+ describe Sequel::Annotate do
69
+ before do
70
+ File.mkdir('spec/tmp') unless File.directory?('spec/tmp')
71
+ end
72
+ after do
73
+ Dir['spec/tmp/*.rb'].each{|f| File.delete(f)}
74
+ end
75
+
76
+ it "#schema_info should return the model schema comment" do
77
+ Sequel::Annotate.new(Item).schema_comment.must_equal((<<OUTPUT).chomp)
78
+ # Table: items
79
+ # Columns:
80
+ # id | integer | PRIMARY KEY DEFAULT nextval('items_id_seq'::regclass)
81
+ # category_id | integer | NOT NULL
82
+ # manufacturer_name | character varying(50) |
83
+ # manufacturer_location | text |
84
+ # in_stock | boolean | DEFAULT false
85
+ # name | text | DEFAULT 'John'::text
86
+ # price | double precision | DEFAULT 0
87
+ # Indexes:
88
+ # items_pkey | PRIMARY KEY btree (id)
89
+ # name | UNIQUE btree (manufacturer_name, manufacturer_location)
90
+ # manufacturer_name | btree (manufacturer_name)
91
+ # Check constraints:
92
+ # pos_id | (id > 0)
93
+ # Foreign key constraints:
94
+ # items_category_id_fkey | (category_id) REFERENCES categories(id)
95
+ # items_manufacturer_name_fkey | (manufacturer_name, manufacturer_location) REFERENCES manufacturers(name, location)
96
+ # Triggers:
97
+ # valid_price | BEFORE INSERT OR UPDATE ON items FOR EACH ROW EXECUTE PROCEDURE valid_price()
98
+ OUTPUT
99
+
100
+ Sequel::Annotate.new(Category).schema_comment.must_equal((<<OUTPUT).chomp)
101
+ # Table: categories
102
+ # Columns:
103
+ # id | integer | PRIMARY KEY DEFAULT nextval('categories_id_seq'::regclass)
104
+ # name | text | NOT NULL
105
+ # Indexes:
106
+ # categories_pkey | PRIMARY KEY btree (id)
107
+ # categories_name_key | UNIQUE btree (name)
108
+ # Referenced By:
109
+ # items | items_category_id_fkey | (category_id) REFERENCES categories(id)
110
+ OUTPUT
111
+
112
+ Sequel::Annotate.new(Manufacturer).schema_comment.must_equal((<<OUTPUT).chomp)
113
+ # Table: manufacturers
114
+ # Primary Key: (name, location)
115
+ # Columns:
116
+ # name | text |
117
+ # location | text |
118
+ # Indexes:
119
+ # manufacturers_pkey | PRIMARY KEY btree (name, location)
120
+ # Referenced By:
121
+ # items | items_manufacturer_name_fkey | (manufacturer_name, manufacturer_location) REFERENCES manufacturers(name, location)
122
+ OUTPUT
123
+
124
+ Sequel::Annotate.new(SItem).schema_comment.must_equal((<<OUTPUT).chomp)
125
+ # Table: items
126
+ # Columns:
127
+ # id | integer | PRIMARY KEY AUTOINCREMENT
128
+ # category_id | integer | NOT NULL
129
+ # manufacturer_name | varchar(50) |
130
+ # manufacturer_location | varchar(255) |
131
+ # in_stock | boolean | DEFAULT 0
132
+ # name | varchar(255) | DEFAULT 'John'
133
+ # price | double precision | DEFAULT 0
134
+ # Indexes:
135
+ # manufacturer_name | (manufacturer_name)
136
+ # name | UNIQUE (manufacturer_name, manufacturer_location)
137
+ # Foreign key constraints:
138
+ # (category_id) REFERENCES categories
139
+ # (manufacturer_name, manufacturer_location) REFERENCES manufacturers
140
+ OUTPUT
141
+
142
+ Sequel::Annotate.new(SCategory).schema_comment.must_equal((<<OUTPUT).chomp)
143
+ # Table: categories
144
+ # Columns:
145
+ # id | integer | PRIMARY KEY AUTOINCREMENT
146
+ # name | varchar(255) | NOT NULL
147
+ OUTPUT
148
+
149
+ Sequel::Annotate.new(SManufacturer).schema_comment.must_equal((<<OUTPUT).chomp)
150
+ # Table: manufacturers
151
+ # Primary Key: (name, location)
152
+ # Columns:
153
+ # name | varchar(255) |
154
+ # location | varchar(255) |
155
+ OUTPUT
156
+ end
157
+
158
+ it "#annotate should annotate the file comment" do
159
+ FileUtils.cp(Dir['spec/unannotated/*.rb'], 'spec/tmp')
160
+
161
+ [Item, Category, Manufacturer, SItem, SCategory, SManufacturer].each do |model|
162
+ filename = model.name.downcase
163
+ 2.times do
164
+ Sequel::Annotate.new(model).annotate("spec/tmp/#{filename}.rb")
165
+ File.read("spec/tmp/#{filename}.rb").must_equal File.read("spec/annotated/#{filename}.rb")
166
+ end
167
+ end
168
+ end
169
+
170
+ it ".annotate should annotate all files given" do
171
+ FileUtils.cp(Dir['spec/unannotated/*.rb'], 'spec/tmp')
172
+
173
+ 2.times do
174
+ Sequel::Annotate.annotate(Dir["spec/tmp/*.rb"])
175
+ [Item, Category, Manufacturer, SItem, SCategory, SManufacturer].each do |model|
176
+ filename = model.name.downcase
177
+ File.read("spec/tmp/#{filename}.rb").must_equal File.read("spec/annotated/#{filename}.rb")
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,2 @@
1
+ class Category < Sequel::Model
2
+ end
@@ -0,0 +1,3 @@
1
+ class Item < Sequel::Model
2
+ end
3
+
@@ -0,0 +1,2 @@
1
+ class Manufacturer < Sequel::Model
2
+ end
@@ -0,0 +1,2 @@
1
+ class SCategory < Sequel::Model(SDB[:categories])
2
+ end
@@ -0,0 +1,3 @@
1
+ class SItem < Sequel::Model(SDB[:items])
2
+ end
3
+
@@ -0,0 +1,2 @@
1
+ class SManufacturer < Sequel::Model(SDB[:manufacturers])
2
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sequel-annotate
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jeremy Evans
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sequel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pg
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: |
70
+ sequel-annotate annotates Sequel models with schema information. By
71
+ default, it includes information on columns, indexes, and foreign key
72
+ constraints for the current table.
73
+
74
+ On PostgreSQL, this includes more advanced information, including
75
+ check constraints, triggers, and foreign keys constraints for other
76
+ tables that reference the current table.
77
+ email: code@jeremyevans.net
78
+ executables: []
79
+ extensions: []
80
+ extra_rdoc_files:
81
+ - README.rdoc
82
+ - CHANGELOG
83
+ - MIT-LICENSE
84
+ files:
85
+ - CHANGELOG
86
+ - MIT-LICENSE
87
+ - README.rdoc
88
+ - Rakefile
89
+ - lib/sequel/annotate.rb
90
+ - spec/annotated/category.rb
91
+ - spec/annotated/item.rb
92
+ - spec/annotated/manufacturer.rb
93
+ - spec/annotated/scategory.rb
94
+ - spec/annotated/sitem.rb
95
+ - spec/annotated/smanufacturer.rb
96
+ - spec/sequel-annotate_spec.rb
97
+ - spec/unannotated/category.rb
98
+ - spec/unannotated/item.rb
99
+ - spec/unannotated/manufacturer.rb
100
+ - spec/unannotated/scategory.rb
101
+ - spec/unannotated/sitem.rb
102
+ - spec/unannotated/smanufacturer.rb
103
+ homepage: http://github.com/jeremyevans/sequel-annotate
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options:
109
+ - "--quiet"
110
+ - "--line-numbers"
111
+ - "--inline-source"
112
+ - "--title"
113
+ - 'sequel-annotate: Annotate Sequel models with schema information'
114
+ - "--main"
115
+ - README.rdoc
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 1.8.7
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubyforge_project:
130
+ rubygems_version: 2.4.5.1
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: Annotate Sequel models with schema information
134
+ test_files: []