sequel 0.1.9.5 → 0.1.9.6
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/CHANGELOG +9 -1
- data/Rakefile +1 -1
- data/lib/sequel.rb +4 -4
- data/lib/sequel/database.rb +36 -7
- data/lib/sequel/dataset/dataset_convenience.rb +28 -5
- data/lib/sequel/dataset/dataset_sql.rb +4 -2
- data/lib/sequel/migration.rb +8 -2
- data/lib/sequel/mysql.rb +11 -0
- data/lib/sequel/postgres.rb +7 -1
- data/lib/sequel/schema.rb +4 -179
- data/lib/sequel/schema/schema_generator.rb +53 -0
- data/lib/sequel/schema/schema_sql.rb +85 -0
- data/lib/sequel/sqlite.rb +4 -0
- data/spec/database_spec.rb +4 -3
- data/spec/dataset_spec.rb +11 -0
- data/spec/schema_spec.rb +185 -0
- metadata +6 -2
data/CHANGELOG
CHANGED
@@ -1,4 +1,12 @@
|
|
1
|
-
===
|
1
|
+
=== 0.2 (2007-08-13)
|
2
|
+
|
3
|
+
* Refactored schema definition code. Gets rid of famous primary_key problem as well as other issues (e.g. issue #22).
|
4
|
+
|
5
|
+
* Added #pagination_record_count, #page_range and #current_page_record_range for paginated datasets.
|
6
|
+
|
7
|
+
* Changed MySQL adapter to automatically reconnect (issue #26).
|
8
|
+
|
9
|
+
* Changed Sequel() to acccept variable arity.
|
2
10
|
|
3
11
|
* Added :elements option to column definition, in order to support ENUM and SET types.
|
4
12
|
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
|
|
6
6
|
include FileUtils
|
7
7
|
|
8
8
|
NAME = "sequel"
|
9
|
-
VERS = "0.1.9.
|
9
|
+
VERS = "0.1.9.6"
|
10
10
|
CLEAN.include ['**/.*.sw?', 'pkg/*', '.config', 'doc/*', 'coverage/*']
|
11
11
|
RDOC_OPTS = ['--quiet', '--title', "Sequel: Concise ORM for Ruby",
|
12
12
|
"--opname", "index.html",
|
data/lib/sequel.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'metaid'
|
2
2
|
|
3
3
|
files = %w[
|
4
|
-
core_ext error database connection_pool pretty_table expressions
|
5
|
-
dataset
|
4
|
+
core_ext error schema database connection_pool pretty_table expressions
|
5
|
+
dataset migration model
|
6
6
|
]
|
7
7
|
dir = File.join(File.dirname(__FILE__), 'sequel')
|
8
8
|
files.each {|f| require(File.join(dir, f))}
|
@@ -31,7 +31,7 @@ module Sequel #:nodoc:
|
|
31
31
|
end
|
32
32
|
|
33
33
|
class Object
|
34
|
-
def Sequel(
|
35
|
-
Sequel.connect(
|
34
|
+
def Sequel(*args)
|
35
|
+
Sequel.connect(*args)
|
36
36
|
end
|
37
37
|
end
|
data/lib/sequel/database.rb
CHANGED
@@ -110,6 +110,38 @@ module Sequel
|
|
110
110
|
true
|
111
111
|
end
|
112
112
|
|
113
|
+
include Schema::SQL
|
114
|
+
|
115
|
+
NULL = "NULL".freeze
|
116
|
+
TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S'".freeze
|
117
|
+
DATE_FORMAT = "DATE '%Y-%m-%d'".freeze
|
118
|
+
TRUE = "'t'".freeze
|
119
|
+
FALSE = "'f'".freeze
|
120
|
+
|
121
|
+
# default literal implementation for use in schema definitions
|
122
|
+
def literal(v)
|
123
|
+
case v
|
124
|
+
when ExpressionString: v
|
125
|
+
when String: "'#{v.gsub(/'/, "''")}'"
|
126
|
+
when Integer, Float: v.to_s
|
127
|
+
when NilClass: NULL
|
128
|
+
when TrueClass: TRUE
|
129
|
+
when FalseClass: FALSE
|
130
|
+
when Symbol: v.to_field_name
|
131
|
+
when Array: v.empty? ? NULL : v.map {|i| literal(i)}.join(COMMA_SEPARATOR)
|
132
|
+
when Time: v.strftime(TIMESTAMP_FORMAT)
|
133
|
+
when Date: v.strftime(DATE_FORMAT)
|
134
|
+
when Dataset: "(#{v.sql})"
|
135
|
+
else
|
136
|
+
raise SequelError, "can't express #{v.inspect} as a SQL literal"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# default serial primary key definition. this should be overriden for each adapter.
|
141
|
+
def serial_primary_key_options
|
142
|
+
{:primary_key => true, :type => :integer, :auto_increment => true}
|
143
|
+
end
|
144
|
+
|
113
145
|
# Creates a table. The easiest way to use this method is to provide a
|
114
146
|
# block:
|
115
147
|
# DB.create_table :posts do
|
@@ -119,16 +151,13 @@ module Sequel
|
|
119
151
|
# index :title
|
120
152
|
# end
|
121
153
|
def create_table(name, &block)
|
122
|
-
|
123
|
-
|
124
|
-
schema.create(self)
|
154
|
+
g = Schema::Generator.new(self, name, &block)
|
155
|
+
execute(create_table_sql(*g.create_info))
|
125
156
|
end
|
126
157
|
|
127
158
|
# Drops a table.
|
128
159
|
def drop_table(*names)
|
129
|
-
|
130
|
-
execute(names.map {|n| Schema.drop_table_sql(n)}.join)
|
131
|
-
end
|
160
|
+
execute(names.map {|n| drop_table_sql(n)}.join)
|
132
161
|
end
|
133
162
|
|
134
163
|
# Performs a brute-force check for the existance of a table. This method is
|
@@ -171,7 +200,7 @@ module Sequel
|
|
171
200
|
end
|
172
201
|
end
|
173
202
|
end
|
174
|
-
|
203
|
+
|
175
204
|
@@adapters = Hash.new
|
176
205
|
|
177
206
|
# Sets the adapter scheme for the Database class. Call this method in
|
@@ -80,19 +80,27 @@ module Sequel
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
-
#
|
83
|
+
# Returns a paginated dataset. The resulting dataset also provides the
|
84
84
|
# total number of pages (Dataset#page_count) and the current page number
|
85
85
|
# (Dataset#current_page), as well as Dataset#prev_page and Dataset#next_page
|
86
86
|
# for implementing pagination controls.
|
87
87
|
def paginate(page_no, page_size)
|
88
|
-
|
88
|
+
record_count = count
|
89
|
+
total_pages = (record_count / page_size.to_f).ceil
|
89
90
|
paginated = limit(page_size, (page_no - 1) * page_size)
|
90
|
-
paginated.
|
91
|
-
paginated.page_count = total_pages
|
91
|
+
paginated.set_pagination_info(page_no, page_size, record_count)
|
92
92
|
paginated
|
93
93
|
end
|
94
|
+
|
95
|
+
# Sets the pagination info
|
96
|
+
def set_pagination_info(page_no, page_size, record_count)
|
97
|
+
@current_page = page_no
|
98
|
+
@page_size = page_size
|
99
|
+
@pagination_record_count = record_count
|
100
|
+
@page_count = (record_count / page_size.to_f).ceil
|
101
|
+
end
|
94
102
|
|
95
|
-
attr_accessor :page_count, :current_page
|
103
|
+
attr_accessor :page_size, :page_count, :current_page, :pagination_record_count
|
96
104
|
|
97
105
|
# Returns the previous page number or nil if the current page is the first
|
98
106
|
def prev_page
|
@@ -103,6 +111,21 @@ module Sequel
|
|
103
111
|
def next_page
|
104
112
|
current_page < page_count ? (current_page + 1) : nil
|
105
113
|
end
|
114
|
+
|
115
|
+
# Returns the page range
|
116
|
+
def page_range
|
117
|
+
1..page_count
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns the record range for the current page
|
121
|
+
def current_page_record_range
|
122
|
+
return nil if @current_page > @page_count
|
123
|
+
|
124
|
+
a = 1 + (@current_page - 1) * @page_size
|
125
|
+
b = a + @page_size - 1
|
126
|
+
b = @pagination_record_count if b > @pagination_record_count
|
127
|
+
a..b
|
128
|
+
end
|
106
129
|
|
107
130
|
# Returns the minimum value for the given field.
|
108
131
|
def min(field)
|
@@ -44,6 +44,8 @@ module Sequel
|
|
44
44
|
NULL = "NULL".freeze
|
45
45
|
TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S'".freeze
|
46
46
|
DATE_FORMAT = "DATE '%Y-%m-%d'".freeze
|
47
|
+
TRUE = "'t'".freeze
|
48
|
+
FALSE = "'f'".freeze
|
47
49
|
|
48
50
|
# Returns a literal representation of a value to be used as part
|
49
51
|
# of an SQL expression. The stock implementation supports literalization
|
@@ -64,8 +66,8 @@ module Sequel
|
|
64
66
|
when String: "'#{v.gsub(/'/, "''")}'"
|
65
67
|
when Integer, Float: v.to_s
|
66
68
|
when NilClass: NULL
|
67
|
-
when TrueClass:
|
68
|
-
when FalseClass:
|
69
|
+
when TrueClass: TRUE
|
70
|
+
when FalseClass: FALSE
|
69
71
|
when Symbol: v.to_field_name
|
70
72
|
when Array: v.empty? ? NULL : v.map {|i| literal(i)}.join(COMMA_SEPARATOR)
|
71
73
|
when Time: v.strftime(TIMESTAMP_FORMAT)
|
data/lib/sequel/migration.rb
CHANGED
@@ -106,6 +106,7 @@ module Sequel
|
|
106
106
|
target ||= latest_migration_version(directory)
|
107
107
|
raise SequelError, "No current version available" if current.nil?
|
108
108
|
raise SequelError, "No target version available" if target.nil?
|
109
|
+
|
109
110
|
direction = current < target ? :up : :down
|
110
111
|
|
111
112
|
classes = migration_classes(directory, target, current, direction)
|
@@ -121,9 +122,14 @@ module Sequel
|
|
121
122
|
def self.migration_classes(directory, target, current, direction)
|
122
123
|
range = direction == :up ?
|
123
124
|
(current + 1)..target : (target + 1)..current
|
124
|
-
|
125
|
-
#
|
125
|
+
|
126
|
+
# Remove class definitions
|
127
|
+
Migration.descendants.each do |c|
|
128
|
+
Object.send(:remove_const, c.to_s) rescue nil
|
129
|
+
end
|
126
130
|
Migration.descendants.clear # remove any defined migration classes
|
131
|
+
|
132
|
+
# load migration files
|
127
133
|
migration_files(directory, range).each {|fn| load(fn)}
|
128
134
|
|
129
135
|
# get migration classes
|
data/lib/sequel/mysql.rb
CHANGED
@@ -55,10 +55,21 @@ module Sequel
|
|
55
55
|
class Database < Sequel::Database
|
56
56
|
set_adapter_scheme :mysql
|
57
57
|
|
58
|
+
def serial_primary_key_options
|
59
|
+
{:primary_key => true, :type => :integer, :auto_increment => true}
|
60
|
+
end
|
61
|
+
|
62
|
+
AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
|
63
|
+
|
64
|
+
def auto_increment_sql
|
65
|
+
AUTO_INCREMENT
|
66
|
+
end
|
67
|
+
|
58
68
|
def connect
|
59
69
|
conn = Mysql.real_connect(@opts[:host], @opts[:user], @opts[:password],
|
60
70
|
@opts[:database], @opts[:port])
|
61
71
|
conn.query_with_result = false
|
72
|
+
conn.reconnect = true
|
62
73
|
conn
|
63
74
|
end
|
64
75
|
|
data/lib/sequel/postgres.rb
CHANGED
@@ -159,7 +159,6 @@ module Sequel
|
|
159
159
|
RELATION_FILTER = "(relkind = 'r') AND (relname !~ '^pg|sql')".freeze
|
160
160
|
SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
|
161
161
|
|
162
|
-
|
163
162
|
def tables
|
164
163
|
dataset(RELATION_QUERY).filter(RELATION_FILTER).map {|r| r[:relname].to_sym}
|
165
164
|
end
|
@@ -265,6 +264,13 @@ module Sequel
|
|
265
264
|
end
|
266
265
|
end
|
267
266
|
|
267
|
+
def serial_primary_key_options
|
268
|
+
{:primary_key => true, :type => :serial}
|
269
|
+
end
|
270
|
+
|
271
|
+
def drop_table_sql(name)
|
272
|
+
"DROP TABLE #{name} CASCADE;"
|
273
|
+
end
|
268
274
|
end
|
269
275
|
|
270
276
|
class Dataset < Sequel::Dataset
|
data/lib/sequel/schema.rb
CHANGED
@@ -1,183 +1,8 @@
|
|
1
1
|
module Sequel
|
2
|
-
|
3
|
-
class << self
|
4
|
-
include Dataset::SQL
|
5
|
-
end
|
6
|
-
|
7
|
-
COMMA_SEPARATOR = ', '.freeze
|
8
|
-
COLUMN_DEF = '%s %s'.freeze
|
9
|
-
COLUMN_MEMBERS_DEF = '(%d)'.freeze
|
10
|
-
UNIQUE = ' UNIQUE'.freeze
|
11
|
-
NOT_NULL = ' NOT NULL'.freeze
|
12
|
-
DEFAULT = ' DEFAULT %s'.freeze
|
13
|
-
PRIMARY_KEY = ' PRIMARY KEY'.freeze
|
14
|
-
REFERENCES = ' REFERENCES %s'.freeze
|
15
|
-
ON_DELETE = ' ON DELETE %s'.freeze
|
16
|
-
AUTOINCREMENT = ' AUTOINCREMENT'.freeze
|
17
|
-
|
18
|
-
RESTRICT = 'RESTRICT'.freeze
|
19
|
-
CASCADE = 'CASCADE'.freeze
|
20
|
-
NO_ACTION = 'NO ACTION'.freeze
|
21
|
-
SET_NULL = 'SET NULL'.freeze
|
22
|
-
SET_DEFAULT = 'SET DEFAULT'.freeze
|
23
|
-
|
24
|
-
TYPES = Hash.new {|h, k| k}
|
25
|
-
TYPES[:double] = 'double precision'
|
26
|
-
|
27
|
-
def self.on_delete_action(action)
|
28
|
-
case action
|
29
|
-
when :restrict: RESTRICT
|
30
|
-
when :cascade: CASCADE
|
31
|
-
when :set_null: SET_NULL
|
32
|
-
when :set_default: SET_DEFAULT
|
33
|
-
else NO_ACTION
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.column_definition(column)
|
38
|
-
c = COLUMN_DEF % [column[:name], TYPES[column[:type]]]
|
39
|
-
column[:size] ||= 255 if column[:type] == :varchar
|
40
|
-
atts = column[:size] || column[:elements]
|
41
|
-
c << COLUMN_MEMBERS_DEF % literal(atts) if atts
|
42
|
-
c << UNIQUE if column[:unique]
|
43
|
-
c << NOT_NULL if column[:null] == false
|
44
|
-
c << DEFAULT % literal(column[:default]) if column.include?(:default)
|
45
|
-
c << PRIMARY_KEY if column[:primary_key]
|
46
|
-
c << REFERENCES % column[:table] if column[:table]
|
47
|
-
c << ON_DELETE % on_delete_action(column[:on_delete]) if
|
48
|
-
column[:on_delete]
|
49
|
-
c << AUTOINCREMENT if column[:auto_increment]
|
50
|
-
c
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.create_table_column_list(columns)
|
54
|
-
columns.map {|c| column_definition(c)}.join(COMMA_SEPARATOR)
|
55
|
-
end
|
56
|
-
|
57
|
-
CREATE_INDEX = 'CREATE INDEX %s ON %s (%s);'.freeze
|
58
|
-
CREATE_UNIQUE_INDEX = 'CREATE UNIQUE INDEX %s ON %s (%s);'.freeze
|
59
|
-
INDEX_NAME = '%s_%s_index'.freeze
|
60
|
-
UNDERSCORE = '_'.freeze
|
61
|
-
|
62
|
-
def self.index_definition(table_name, index)
|
63
|
-
fields = index[:columns].join(COMMA_SEPARATOR)
|
64
|
-
index_name = index[:name] || INDEX_NAME %
|
65
|
-
[table_name, index[:columns].join(UNDERSCORE)]
|
66
|
-
(index[:unique] ? CREATE_UNIQUE_INDEX : CREATE_INDEX) %
|
67
|
-
[index_name, table_name, fields]
|
68
|
-
end
|
69
|
-
|
70
|
-
def self.create_indexes_sql(table_name, indexes)
|
71
|
-
indexes.map {|i| index_definition(table_name, i)}.join
|
72
|
-
end
|
73
|
-
|
74
|
-
CREATE_TABLE = "CREATE TABLE %s (%s);".freeze
|
75
|
-
|
76
|
-
def self.create_table_sql(name, columns, indexes = nil)
|
77
|
-
sql = CREATE_TABLE % [name, create_table_column_list(columns)]
|
78
|
-
sql << create_indexes_sql(name, indexes) if indexes && !indexes.empty?
|
79
|
-
sql
|
80
|
-
end
|
81
|
-
|
82
|
-
DROP_TABLE = "DROP TABLE %s CASCADE;".freeze
|
83
|
-
|
84
|
-
def self.drop_table_sql(name)
|
85
|
-
DROP_TABLE % name
|
86
|
-
end
|
87
|
-
|
88
|
-
class Generator
|
89
|
-
attr_reader :table_name
|
90
|
-
|
91
|
-
def initialize(table_name, auto_primary_key = nil, &block)
|
92
|
-
@table_name = table_name
|
93
|
-
@primary_key = auto_primary_key
|
94
|
-
@columns = []
|
95
|
-
@indexes = []
|
96
|
-
instance_eval(&block)
|
97
|
-
end
|
98
|
-
|
99
|
-
def method_missing(type, name = nil, opts = nil)
|
100
|
-
return super unless name
|
101
|
-
column(name, type, opts)
|
102
|
-
end
|
103
|
-
|
104
|
-
def primary_key(name, type = nil, opts = nil)
|
105
|
-
@primary_key = {
|
106
|
-
:name => name,
|
107
|
-
:type => type || :serial,
|
108
|
-
:primary_key => true
|
109
|
-
}.merge(opts || {})
|
110
|
-
end
|
111
|
-
|
112
|
-
def primary_key_name
|
113
|
-
@primary_key && @primary_key[:name]
|
114
|
-
end
|
115
|
-
|
116
|
-
def column(name, type, opts = nil)
|
117
|
-
@columns << {:name => name, :type => type}.merge(opts || {})
|
118
|
-
end
|
119
|
-
|
120
|
-
def foreign_key(name, opts)
|
121
|
-
@columns << {:name => name, :type => :integer}.merge(opts || {})
|
122
|
-
end
|
123
|
-
|
124
|
-
def has_column?(name)
|
125
|
-
@columns.each {|c| return true if c[:name] == name}
|
126
|
-
false
|
127
|
-
end
|
128
|
-
|
129
|
-
def index(columns, opts = nil)
|
130
|
-
columns = [columns] unless columns.is_a?(Array)
|
131
|
-
@indexes << {:columns => columns}.merge(opts || {})
|
132
|
-
end
|
133
|
-
|
134
|
-
def create_sql
|
135
|
-
if @primary_key && !has_column?(@primary_key[:name])
|
136
|
-
@columns.unshift(@primary_key)
|
137
|
-
end
|
138
|
-
Schema.create_table_sql(@table_name, @columns, @indexes)
|
139
|
-
end
|
140
|
-
|
141
|
-
def drop_sql
|
142
|
-
Schema.drop_table_sql(@table_name)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
attr_reader :instructions
|
147
|
-
|
148
|
-
def initialize(&block)
|
149
|
-
@instructions = []
|
150
|
-
instance_eval(&block) if block
|
151
|
-
end
|
152
|
-
|
153
|
-
def auto_primary_key(name, type = nil, opts = nil)
|
154
|
-
@auto_primary_key = {
|
155
|
-
:name => name,
|
156
|
-
:type => type || :serial,
|
157
|
-
:primary_key => true
|
158
|
-
}.merge(opts || {})
|
159
|
-
end
|
160
|
-
|
161
|
-
def create_table(table_name, &block)
|
162
|
-
@instructions << Generator.new(table_name, @auto_primary_key, &block)
|
163
|
-
end
|
164
|
-
|
165
|
-
def create(db)
|
166
|
-
@instructions.each do |s|
|
167
|
-
db.execute(s.create_sql)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
def drop(db)
|
172
|
-
@instructions.reverse_each do |s|
|
173
|
-
db.execute(s.drop_sql) if db.table_exists?(s.table_name)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def recreate(db)
|
178
|
-
drop(db)
|
179
|
-
create(db)
|
180
|
-
end
|
2
|
+
module Schema
|
181
3
|
end
|
182
4
|
end
|
183
5
|
|
6
|
+
require File.join(File.dirname(__FILE__), 'schema/schema_sql')
|
7
|
+
require File.join(File.dirname(__FILE__), 'schema/schema_generator')
|
8
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Schema
|
3
|
+
class Generator
|
4
|
+
def initialize(db, table_name, &block)
|
5
|
+
@db = db
|
6
|
+
@table_name = table_name
|
7
|
+
@columns = []
|
8
|
+
@indexes = []
|
9
|
+
instance_eval(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(type, name = nil, opts = nil)
|
13
|
+
return super unless name
|
14
|
+
column(name, type, opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
def primary_key(name, type = nil, opts = nil)
|
18
|
+
@primary_key = @db.serial_primary_key_options.merge({
|
19
|
+
:name => name
|
20
|
+
})
|
21
|
+
@primary_key.merge!({:type => type}) if type
|
22
|
+
@primary_key.merge!(opts) if opts
|
23
|
+
@primary_key
|
24
|
+
end
|
25
|
+
|
26
|
+
def column(name, type, opts = nil)
|
27
|
+
@columns << {:name => name, :type => type}.merge(opts || {})
|
28
|
+
end
|
29
|
+
|
30
|
+
def foreign_key(name, opts)
|
31
|
+
@columns << {:name => name, :type => :integer}.merge(opts || {})
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_column?(name)
|
35
|
+
@columns.each {|c| return true if c[:name] == name}
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
def index(columns, opts = nil)
|
40
|
+
columns = [columns] unless columns.is_a?(Array)
|
41
|
+
@indexes << {:columns => columns}.merge(opts || {})
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_info
|
45
|
+
if @primary_key && !has_column?(@primary_key[:name])
|
46
|
+
@columns.unshift(@primary_key)
|
47
|
+
end
|
48
|
+
[@table_name, @columns, @indexes]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Schema
|
3
|
+
module SQL
|
4
|
+
RESTRICT = 'RESTRICT'.freeze
|
5
|
+
CASCADE = 'CASCADE'.freeze
|
6
|
+
NO_ACTION = 'NO ACTION'.freeze
|
7
|
+
SET_NULL = 'SET NULL'.freeze
|
8
|
+
SET_DEFAULT = 'SET DEFAULT'.freeze
|
9
|
+
|
10
|
+
def on_delete_clause(action)
|
11
|
+
case action
|
12
|
+
when :restrict: RESTRICT
|
13
|
+
when :cascade: CASCADE
|
14
|
+
when :set_null: SET_NULL
|
15
|
+
when :set_default: SET_DEFAULT
|
16
|
+
else NO_ACTION
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
AUTOINCREMENT = 'AUTOINCREMENT'.freeze
|
21
|
+
|
22
|
+
def auto_increment_sql
|
23
|
+
AUTOINCREMENT
|
24
|
+
end
|
25
|
+
|
26
|
+
COMMA_SEPARATOR = ', '.freeze
|
27
|
+
UNIQUE = ' UNIQUE'.freeze
|
28
|
+
NOT_NULL = ' NOT NULL'.freeze
|
29
|
+
PRIMARY_KEY = ' PRIMARY KEY'.freeze
|
30
|
+
|
31
|
+
TYPES = Hash.new {|h, k| k}
|
32
|
+
TYPES[:double] = 'double precision'
|
33
|
+
|
34
|
+
def column_definition_sql(column)
|
35
|
+
sql = "#{column[:name]} #{TYPES[column[:type]]}"
|
36
|
+
column[:size] ||= 255 if column[:type] == :varchar
|
37
|
+
elements = column[:size] || column[:elements]
|
38
|
+
sql << "(#{literal(elements)})" if elements
|
39
|
+
sql << UNIQUE if column[:unique]
|
40
|
+
sql << NOT_NULL if column[:null] == false
|
41
|
+
sql << " DEFAULT #{literal(column[:default])}" if column.include?(:default)
|
42
|
+
sql << PRIMARY_KEY if column[:primary_key]
|
43
|
+
sql << " REFERENCES #{column[:table]}" if column[:table]
|
44
|
+
sql << " ON DELETE #{on_delete_clause(column[:on_delete])}" if column[:on_delete]
|
45
|
+
sql << " #{auto_increment_sql}" if column[:auto_increment]
|
46
|
+
sql
|
47
|
+
end
|
48
|
+
|
49
|
+
def column_list_sql(columns)
|
50
|
+
columns.map {|c| column_definition_sql(c)}.join(COMMA_SEPARATOR)
|
51
|
+
end
|
52
|
+
|
53
|
+
UNDERSCORE = '_'.freeze
|
54
|
+
|
55
|
+
def default_index_name(table_name, columns)
|
56
|
+
"#{table_name}_#{columns.join(UNDERSCORE)}_index"
|
57
|
+
end
|
58
|
+
|
59
|
+
def index_definition_sql(table_name, index)
|
60
|
+
fields = index[:columns].join(COMMA_SEPARATOR)
|
61
|
+
index_name = index[:name] || default_index_name(table_name, index[:columns])
|
62
|
+
if index[:unique]
|
63
|
+
"CREATE UNIQUE INDEX #{index_name} ON #{table_name} (#{fields});"
|
64
|
+
else
|
65
|
+
"CREATE INDEX #{index_name} ON #{table_name} (#{fields});"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def index_list_sql(table_name, indexes)
|
70
|
+
indexes.map {|i| index_definition_sql(table_name, i)}.join
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_table_sql(name, columns, indexes = nil)
|
74
|
+
sql = "CREATE TABLE #{name} (#{column_list_sql(columns)});"
|
75
|
+
sql << index_list_sql(name, indexes) if indexes && !indexes.empty?
|
76
|
+
sql
|
77
|
+
end
|
78
|
+
|
79
|
+
def drop_table_sql(name)
|
80
|
+
"DROP TABLE #{name};"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
data/lib/sequel/sqlite.rb
CHANGED
@@ -9,6 +9,10 @@ module Sequel
|
|
9
9
|
class Database < Sequel::Database
|
10
10
|
set_adapter_scheme :sqlite
|
11
11
|
|
12
|
+
def serial_primary_key_options
|
13
|
+
{:primary_key => true, :type => :integer, :auto_increment => true}
|
14
|
+
end
|
15
|
+
|
12
16
|
def connect
|
13
17
|
if @opts[:database].empty?
|
14
18
|
@opts[:database] = ':memory:'
|
data/spec/database_spec.rb
CHANGED
@@ -182,6 +182,7 @@ end
|
|
182
182
|
class DummyDatabase < Sequel::Database
|
183
183
|
attr_reader :sql
|
184
184
|
def execute(sql); @sql = sql; end
|
185
|
+
def transaction; yield; end
|
185
186
|
|
186
187
|
def dataset
|
187
188
|
DummyDataset.new(self)
|
@@ -200,7 +201,7 @@ context "Database#create_table" do
|
|
200
201
|
index :name, :unique => true
|
201
202
|
end
|
202
203
|
@db.sql.should ==
|
203
|
-
'CREATE TABLE test (id integer NOT NULL PRIMARY KEY, name text);CREATE UNIQUE INDEX test_name_index ON test (name);'
|
204
|
+
'CREATE TABLE test (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, name text);CREATE UNIQUE INDEX test_name_index ON test (name);'
|
204
205
|
end
|
205
206
|
end
|
206
207
|
|
@@ -218,13 +219,13 @@ context "Database#drop_table" do
|
|
218
219
|
specify "should construct proper SQL" do
|
219
220
|
@db.drop_table :test
|
220
221
|
@db.sql.should ==
|
221
|
-
'DROP TABLE test
|
222
|
+
'DROP TABLE test;'
|
222
223
|
end
|
223
224
|
|
224
225
|
specify "should accept multiple table names" do
|
225
226
|
@db.drop_table :a, :bb, :ccc
|
226
227
|
@db.sql.should ==
|
227
|
-
'DROP TABLE a
|
228
|
+
'DROP TABLE a;DROP TABLE bb;DROP TABLE ccc;'
|
228
229
|
end
|
229
230
|
end
|
230
231
|
|
data/spec/dataset_spec.rb
CHANGED
@@ -1404,6 +1404,17 @@ context "A paginated dataset" do
|
|
1404
1404
|
@paginated.prev_page.should be_nil
|
1405
1405
|
@d.paginate(4, 50).prev_page.should == 3
|
1406
1406
|
end
|
1407
|
+
|
1408
|
+
specify "should return the page range" do
|
1409
|
+
@paginated.page_range.should == (1..8)
|
1410
|
+
@d.paginate(4, 50).page_range.should == (1..4)
|
1411
|
+
end
|
1412
|
+
|
1413
|
+
specify "should return the record range for the current page" do
|
1414
|
+
@paginated.current_page_record_range.should == (1..20)
|
1415
|
+
@d.paginate(4, 50).current_page_record_range.should == (151..153)
|
1416
|
+
@d.paginate(5, 50).current_page_record_range.should == nil
|
1417
|
+
end
|
1407
1418
|
end
|
1408
1419
|
|
1409
1420
|
context "Dataset#columns" do
|
data/spec/schema_spec.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../lib/sequel')
|
2
|
+
|
3
|
+
class SchemaDummyDatabase < Sequel::Database
|
4
|
+
attr_reader :sqls
|
5
|
+
|
6
|
+
def execute(sql)
|
7
|
+
@sqls ||= []
|
8
|
+
@sqls << sql
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "DB#create_table" do
|
13
|
+
setup do
|
14
|
+
@db = SchemaDummyDatabase.new
|
15
|
+
end
|
16
|
+
|
17
|
+
specify "should accept the table name" do
|
18
|
+
@db.create_table(:cats) {}
|
19
|
+
@db.sqls.should == ['CREATE TABLE cats ();']
|
20
|
+
end
|
21
|
+
|
22
|
+
specify "should accept multiple columns" do
|
23
|
+
@db.create_table(:cats) do
|
24
|
+
column :id, :integer
|
25
|
+
column :name, :text
|
26
|
+
end
|
27
|
+
@db.sqls.should == ['CREATE TABLE cats (id integer, name text);']
|
28
|
+
end
|
29
|
+
|
30
|
+
specify "should accept method calls as data types" do
|
31
|
+
@db.create_table(:cats) do
|
32
|
+
integer :id
|
33
|
+
text :name
|
34
|
+
end
|
35
|
+
@db.sqls.should == ['CREATE TABLE cats (id integer, name text);']
|
36
|
+
end
|
37
|
+
|
38
|
+
specify "should accept primary key definition" do
|
39
|
+
@db.create_table(:cats) do
|
40
|
+
primary_key :id
|
41
|
+
end
|
42
|
+
@db.sqls.should == ['CREATE TABLE cats (id integer PRIMARY KEY AUTOINCREMENT);']
|
43
|
+
@db.sqls.clear
|
44
|
+
@db.create_table(:cats) do
|
45
|
+
primary_key :id, :serial, :auto_increment => false
|
46
|
+
end
|
47
|
+
@db.sqls.should == ['CREATE TABLE cats (id serial PRIMARY KEY);']
|
48
|
+
end
|
49
|
+
|
50
|
+
specify "should accept and literalize default values" do
|
51
|
+
@db.create_table(:cats) do
|
52
|
+
integer :id, :default => 123
|
53
|
+
text :name, :default => "abc'def"
|
54
|
+
end
|
55
|
+
@db.sqls.should == ["CREATE TABLE cats (id integer DEFAULT 123, name text DEFAULT 'abc''def');"]
|
56
|
+
end
|
57
|
+
|
58
|
+
specify "should accept not null definition" do
|
59
|
+
@db.create_table(:cats) do
|
60
|
+
integer :id
|
61
|
+
text :name, :null => false
|
62
|
+
end
|
63
|
+
@db.sqls.should == ["CREATE TABLE cats (id integer, name text NOT NULL);"]
|
64
|
+
end
|
65
|
+
|
66
|
+
specify "should accept unique definition" do
|
67
|
+
@db.create_table(:cats) do
|
68
|
+
integer :id
|
69
|
+
text :name, :unique => true
|
70
|
+
end
|
71
|
+
@db.sqls.should == ["CREATE TABLE cats (id integer, name text UNIQUE);"]
|
72
|
+
end
|
73
|
+
|
74
|
+
specify "should accept [SET|ENUM](...) types" do
|
75
|
+
@db.create_table(:cats) do
|
76
|
+
set :color, :elements => ['black', 'tricolor', 'grey']
|
77
|
+
end
|
78
|
+
@db.sqls.should == ["CREATE TABLE cats (color set('black', 'tricolor', 'grey'));"]
|
79
|
+
end
|
80
|
+
|
81
|
+
specify "should accept varchar size" do
|
82
|
+
@db.create_table(:cats) do
|
83
|
+
varchar :name
|
84
|
+
end
|
85
|
+
@db.sqls.should == ["CREATE TABLE cats (name varchar(255));"]
|
86
|
+
@db.sqls.clear
|
87
|
+
@db.create_table(:cats) do
|
88
|
+
varchar :name, :size => 51
|
89
|
+
end
|
90
|
+
@db.sqls.should == ["CREATE TABLE cats (name varchar(51));"]
|
91
|
+
end
|
92
|
+
|
93
|
+
specify "should accept foreign keys" do
|
94
|
+
@db.create_table(:cats) do
|
95
|
+
foreign_key :project_id, :table => :projects
|
96
|
+
end
|
97
|
+
@db.sqls.should == ["CREATE TABLE cats (project_id integer REFERENCES projects);"]
|
98
|
+
end
|
99
|
+
|
100
|
+
specify "should accept foreign keys with ON DELETE clause" do
|
101
|
+
@db.create_table(:cats) do
|
102
|
+
foreign_key :project_id, :table => :projects, :on_delete => :restrict
|
103
|
+
end
|
104
|
+
@db.sqls.should == ["CREATE TABLE cats (project_id integer REFERENCES projects ON DELETE RESTRICT);"]
|
105
|
+
|
106
|
+
@db.sqls.clear
|
107
|
+
@db.create_table(:cats) do
|
108
|
+
foreign_key :project_id, :table => :projects, :on_delete => :cascade
|
109
|
+
end
|
110
|
+
@db.sqls.should == ["CREATE TABLE cats (project_id integer REFERENCES projects ON DELETE CASCADE);"]
|
111
|
+
|
112
|
+
@db.sqls.clear
|
113
|
+
@db.create_table(:cats) do
|
114
|
+
foreign_key :project_id, :table => :projects, :on_delete => :no_action
|
115
|
+
end
|
116
|
+
@db.sqls.should == ["CREATE TABLE cats (project_id integer REFERENCES projects ON DELETE NO ACTION);"]
|
117
|
+
@db.sqls.clear
|
118
|
+
|
119
|
+
@db.sqls.clear
|
120
|
+
@db.create_table(:cats) do
|
121
|
+
foreign_key :project_id, :table => :projects, :on_delete => :set_null
|
122
|
+
end
|
123
|
+
@db.sqls.should == ["CREATE TABLE cats (project_id integer REFERENCES projects ON DELETE SET NULL);"]
|
124
|
+
@db.sqls.clear
|
125
|
+
|
126
|
+
@db.sqls.clear
|
127
|
+
@db.create_table(:cats) do
|
128
|
+
foreign_key :project_id, :table => :projects, :on_delete => :set_default
|
129
|
+
end
|
130
|
+
@db.sqls.should == ["CREATE TABLE cats (project_id integer REFERENCES projects ON DELETE SET DEFAULT);"]
|
131
|
+
@db.sqls.clear
|
132
|
+
end
|
133
|
+
|
134
|
+
specify "should accept index definitions" do
|
135
|
+
@db.create_table(:cats) do
|
136
|
+
integer :id
|
137
|
+
index :id
|
138
|
+
end
|
139
|
+
@db.sqls.should == ["CREATE TABLE cats (id integer);CREATE INDEX cats_id_index ON cats (id);"]
|
140
|
+
end
|
141
|
+
|
142
|
+
specify "should accept multiple index definitions" do
|
143
|
+
@db.create_table(:cats) do
|
144
|
+
integer :id
|
145
|
+
index :id
|
146
|
+
index :name
|
147
|
+
end
|
148
|
+
@db.sqls.should == ["CREATE TABLE cats (id integer);CREATE INDEX cats_id_index ON cats (id);CREATE INDEX cats_name_index ON cats (name);"]
|
149
|
+
end
|
150
|
+
|
151
|
+
specify "should accept custom index names" do
|
152
|
+
@db.create_table(:cats) do
|
153
|
+
integer :id
|
154
|
+
index :id, :name => 'abc'
|
155
|
+
end
|
156
|
+
@db.sqls.should == ["CREATE TABLE cats (id integer);CREATE INDEX abc ON cats (id);"]
|
157
|
+
end
|
158
|
+
|
159
|
+
specify "should accept unique index definitions" do
|
160
|
+
@db.create_table(:cats) do
|
161
|
+
integer :id
|
162
|
+
index :id, :unique => true
|
163
|
+
end
|
164
|
+
@db.sqls.should == ["CREATE TABLE cats (id integer);CREATE UNIQUE INDEX cats_id_index ON cats (id);"]
|
165
|
+
end
|
166
|
+
|
167
|
+
specify "should accept compound index definitions" do
|
168
|
+
@db.create_table(:cats) do
|
169
|
+
integer :id
|
170
|
+
index [:id, :name], :unique => true
|
171
|
+
end
|
172
|
+
@db.sqls.should == ["CREATE TABLE cats (id integer);CREATE UNIQUE INDEX cats_id_name_index ON cats (id, name);"]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context "DB#drop_table" do
|
177
|
+
setup do
|
178
|
+
@db = SchemaDummyDatabase.new
|
179
|
+
end
|
180
|
+
|
181
|
+
specify "should generate a DROP TABLE statement" do
|
182
|
+
@db.drop_table :cats
|
183
|
+
@db.sqls.should == ['DROP TABLE cats;']
|
184
|
+
end
|
185
|
+
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: sequel
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.9.
|
7
|
-
date: 2007-08-
|
6
|
+
version: 0.1.9.6
|
7
|
+
date: 2007-08-13 00:00:00 +03:00
|
8
8
|
summary: Concise ORM for Ruby.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -41,6 +41,7 @@ files:
|
|
41
41
|
- spec/dataset_spec.rb
|
42
42
|
- spec/expressions_spec.rb
|
43
43
|
- spec/migration_spec.rb
|
44
|
+
- spec/schema_spec.rb
|
44
45
|
- spec/adapters/mysql_spec.rb
|
45
46
|
- spec/adapters/sqlite_spec.rb
|
46
47
|
- lib/sequel
|
@@ -59,10 +60,13 @@ files:
|
|
59
60
|
- lib/sequel/odbc.rb
|
60
61
|
- lib/sequel/postgres.rb
|
61
62
|
- lib/sequel/pretty_table.rb
|
63
|
+
- lib/sequel/schema
|
62
64
|
- lib/sequel/schema.rb
|
63
65
|
- lib/sequel/sqlite.rb
|
64
66
|
- lib/sequel/dataset/dataset_convenience.rb
|
65
67
|
- lib/sequel/dataset/dataset_sql.rb
|
68
|
+
- lib/sequel/schema/schema_generator.rb
|
69
|
+
- lib/sequel/schema/schema_sql.rb
|
66
70
|
- CHANGELOG
|
67
71
|
test_files: []
|
68
72
|
|