sequel-annotate 1.0.0

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