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