sequel 0.1.9.2 → 0.1.9.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ === SVN
2
+
3
+ * Added Sequel.dbi convenience method for using DBI connection strings to open DBI databases.
4
+
5
+ === 0.1.9.3 (2007-08-10)
6
+
7
+ * Added support for specifying field size in schema definitions (thanks Florian Aßmann.)
8
+
9
+ * Added migration code based on work by Florian Aßmann.
10
+
11
+ * Reintroduced metaid dependency. No need to keep a local copy of it.
12
+
1
13
  === 0.1.9.2 (2007-07-24)
2
14
 
3
15
  * Removed metaid dependency. Refactored requires in lib/sequel.rb.
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
6
6
  include FileUtils
7
7
 
8
8
  NAME = "sequel"
9
- VERS = "0.1.9.2"
9
+ VERS = "0.1.9.3"
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",
@@ -43,7 +43,9 @@ spec = Gem::Specification.new do |s|
43
43
  s.homepage = 'http://sequel.rubyforge.org'
44
44
  s.executables = ['sequel']
45
45
 
46
- s.required_ruby_version = '>= 1.8.2'
46
+ s.add_dependency('metaid')
47
+
48
+ s.required_ruby_version = '>= 1.8.4'
47
49
 
48
50
  s.files = %w(COPYING README Rakefile) + Dir.glob("{bin,doc,spec,lib}/**/*")
49
51
 
data/lib/sequel.rb CHANGED
@@ -1,6 +1,8 @@
1
+ require 'metaid'
2
+
1
3
  files = %w[
2
- metaid core_ext error database connection_pool
3
- schema pretty_table expressions dataset model
4
+ core_ext error database connection_pool
5
+ schema pretty_table expressions dataset migration model
4
6
  ]
5
7
  dir = File.join(File.dirname(__FILE__), 'sequel')
6
8
  files.each {|f| require(File.join(dir, f))}
data/lib/sequel/dbi.rb CHANGED
@@ -5,14 +5,19 @@ end
5
5
  require 'dbi'
6
6
 
7
7
  module Sequel
8
+ def self.dbi(conn_string, opts = nil)
9
+ opts ||= {}
10
+ opts.merge!(:database => conn_string)
11
+ Sequel::DBI::Database.new(opts)
12
+ end
13
+
8
14
  module DBI
9
-
10
15
  class Database < Sequel::Database
11
16
  set_adapter_scheme :dbi
12
17
 
13
18
  def connect
14
- dbname = @opts[:database] =~ /^DBI:/ ? \
15
- @opts[:database] : @opts[:database] = 'DBI:' + @opts[:database]
19
+ dbname = @opts[:database]
20
+ dbname = 'DBI:' + dbname unless dbname =~ /^DBI:/
16
21
  ::DBI.connect(dbname, @opts[:user], @opts[:password])
17
22
  end
18
23
 
@@ -0,0 +1,180 @@
1
+ # The migration code is based on work by Florian Aßmann:
2
+ # http://code.google.com/p/ruby-sequel/issues/detail?id=23
3
+
4
+ module Sequel
5
+ # The Migration class describes a database migration that can be reversed.
6
+ # The migration looks very similar to ActiveRecord (Rails) migrations, e.g.:
7
+ #
8
+ # class CreateSessions < Sequel::Migration
9
+ # def up
10
+ # create_table :sessions do
11
+ # primary_key :id
12
+ # varchar :session_id, :length => 32, :unique => true
13
+ # timestamp :created_at
14
+ # text :data
15
+ # end
16
+ # end
17
+ #
18
+ # def down
19
+ # execute 'DROP TABLE sessions'
20
+ # end
21
+ # end
22
+ #
23
+ # To apply a migration to a database, you can invoke the #apply with
24
+ # the target database instance and the direction :up or :down, e.g.:
25
+ #
26
+ # DB = Sequel.open ('sqlite:///mydb')
27
+ # CreateSessions.apply(DB, :up)
28
+ #
29
+ class Migration
30
+ # Creates a new instance of the migration and sets the @db attribute.
31
+ def initialize(db)
32
+ @db = db
33
+ end
34
+
35
+ # Adds the new migration class to the list of Migration descendants.
36
+ def self.inherited(base)
37
+ descendants << base
38
+ end
39
+
40
+ # Returns the list of Migration descendants.
41
+ def self.descendants
42
+ @descendants ||= []
43
+ end
44
+
45
+ def up; end #:nodoc:
46
+ def down; end #:nodoc:
47
+
48
+ # Applies the migration to the supplied database in the specified
49
+ # direction.
50
+ def self.apply(db, direction)
51
+ obj = new(db)
52
+ case direction
53
+ when :up: obj.up
54
+ when :down: obj.down
55
+ else
56
+ raise SequelError, "Invalid migration direction (#{direction})"
57
+ end
58
+ end
59
+
60
+ # Intercepts method calls intended for the database and sends them along.
61
+ def method_missing(method_sym, *args, &block)
62
+ @db.send method_sym, *args, &block
63
+ end
64
+ end
65
+
66
+ # The Migrator module performs migrations based on migration files in a
67
+ # specified directory. The migration files should be named using the
68
+ # following pattern (in similar fashion to ActiveRecord migrations):
69
+ #
70
+ # <version>_<title>.rb
71
+ #
72
+ # For example, the following files are considered migration files:
73
+ #
74
+ # 001_create_sessions.rb
75
+ # 002_add_data_column.rb
76
+ # ...
77
+ #
78
+ # The migration files should contain one or more migration classes based
79
+ # on Sequel::Migration.
80
+ #
81
+ # To apply a migration, the #apply method must be invoked with the database
82
+ # instance, the directory of migration files and the target version. If
83
+ # no current version is supplied, it is read from the database. The migrator
84
+ # automatically creates a schema_info table in the database to keep track
85
+ # of the current migration version. If no migration version is stored in the
86
+ # database, the version is considered to be 0. If no target version is
87
+ # specified, the database is migrated to the latest version available in the
88
+ # migration directory.
89
+ #
90
+ # For example, to migrate the database to the latest version:
91
+ #
92
+ # Sequel::Migrator.apply(DB, '.')
93
+ #
94
+ # To migrate the database from version 1 to version 5:
95
+ #
96
+ # Sequel::Migrator.apply(DB, '.', 5, 1)
97
+ #
98
+ module Migrator
99
+ # Migrates the supplied database in the specified directory from the
100
+ # current version to the target version. If no current version is
101
+ # supplied, it is extracted from a schema_info table. The schema_info
102
+ # table is automatically created and maintained by the apply function.
103
+ def self.apply(db, directory, target = nil, current = nil)
104
+ # determine current and target version and direction
105
+ current ||= get_current_migration_version(db)
106
+ target ||= latest_migration_version(directory)
107
+ raise SequelError, "No current version available" if current.nil?
108
+ raise SequelError, "No target version available" if target.nil?
109
+ direction = current < target ? :up : :down
110
+
111
+ classes = migration_classes(directory, target, current, direction)
112
+
113
+ db.transaction do
114
+ classes.each {|c| c.apply(db, direction)}
115
+ set_current_migration_version(db, target)
116
+ end
117
+ end
118
+
119
+ # Returns a list of migration classes filter for the migration range and
120
+ # ordered according to the migration direction.
121
+ def self.migration_classes(directory, target, current, direction)
122
+ range = direction == :up ?
123
+ (current + 1)..target : (target + 1)..current
124
+
125
+ # load migration files
126
+ Migration.descendants.clear # remove any defined migration classes
127
+ migration_files(directory, range).each {|fn| load(fn)}
128
+
129
+ # get migration classes
130
+ classes = Migration.descendants
131
+ classes.reverse! if direction == :down
132
+ classes
133
+ end
134
+
135
+ MIGRATION_FILE_PATTERN = '[0-9][0-9][0-9]_*.rb'.freeze
136
+
137
+ # Returns any found migration files in the supplied directory.
138
+ def self.migration_files(directory, range = nil)
139
+ pattern = File.join(directory, MIGRATION_FILE_PATTERN)
140
+ files = Dir[pattern].inject([]) do |m, path|
141
+ m[File.basename(path).to_i] = path
142
+ m
143
+ end
144
+ filtered = range ? files[range] : files
145
+ filtered ? filtered.compact : []
146
+ end
147
+
148
+ def self.latest_migration_version(directory)
149
+ l = migration_files(directory).last
150
+ l ? File.basename(l).to_i : nil
151
+ end
152
+
153
+ # Gets the current migration version stored in the database. If no version
154
+ # number is stored, 0 is returned.
155
+ def self.get_current_migration_version(db)
156
+ r = schema_info_dataset(db).first
157
+ r ? r[:version] : 0
158
+ end
159
+
160
+ # Sets the current migration version stored in the database.
161
+ def self.set_current_migration_version(db, version)
162
+ dataset = schema_info_dataset(db)
163
+ if dataset.first
164
+ dataset.update(:version => version)
165
+ else
166
+ dataset << {:version => version}
167
+ end
168
+ end
169
+
170
+ # Returns the dataset for the schema_info table. If no such table
171
+ # exists, it is automatically created.
172
+ def self.schema_info_dataset(db)
173
+ unless db.table_exists?(:schema_info)
174
+ db.create_table(:schema_info) {integer :version}
175
+ end
176
+
177
+ db[:schema_info]
178
+ end
179
+ end
180
+ end
data/lib/sequel/schema.rb CHANGED
@@ -2,6 +2,7 @@ module Sequel
2
2
  class Schema
3
3
  COMMA_SEPARATOR = ', '.freeze
4
4
  COLUMN_DEF = '%s %s'.freeze
5
+ SIZE_DEF = '(%d)'.freeze
5
6
  UNIQUE = ' UNIQUE'.freeze
6
7
  NOT_NULL = ' NOT NULL'.freeze
7
8
  DEFAULT = ' DEFAULT %s'.freeze
@@ -31,6 +32,8 @@ module Sequel
31
32
 
32
33
  def self.column_definition(column)
33
34
  c = COLUMN_DEF % [column[:name], TYPES[column[:type]]]
35
+ column[:size] ||= 255 if column[:type] == :varchar
36
+ c << SIZE_DEF % column[:size] if column[:size]
34
37
  c << UNIQUE if column[:unique]
35
38
  c << NOT_NULL if column[:null] == false
36
39
  c << DEFAULT % PGconn.quote(column[:default]) if column.include?(:default)
@@ -0,0 +1,241 @@
1
+ require File.join(File.dirname(__FILE__), '../lib/sequel')
2
+
3
+ context "Migration classes" do
4
+ setup do
5
+ Sequel::Migration.descendants.clear
6
+ end
7
+
8
+ specify "should be registred in Migration.descendants" do
9
+ @class = Class.new(Sequel::Migration)
10
+
11
+ Sequel::Migration.descendants.should == [@class]
12
+ end
13
+
14
+ specify "should be registered in the right order" do
15
+ @c1 = Class.new(Sequel::Migration)
16
+ @c2 = Class.new(Sequel::Migration)
17
+ @c3 = Class.new(Sequel::Migration)
18
+
19
+ Sequel::Migration.descendants.should == [@c1, @c2, @c3]
20
+ end
21
+ end
22
+
23
+ context "Migration#apply" do
24
+ setup do
25
+ @c = Class.new do
26
+ define_method(:one) {|x| [1111, x]}
27
+ define_method(:two) {|x| [2222, x]}
28
+ end
29
+ @db = @c.new
30
+
31
+ @migration = Class.new(Sequel::Migration) do
32
+ define_method(:up) {one(3333)}
33
+ define_method(:down) {two(4444)}
34
+ end
35
+ end
36
+
37
+ specify "should raise for an invalid direction" do
38
+ proc {@migration.apply(@db, :hahaha)}.should raise_error(SequelError)
39
+ end
40
+
41
+ specify "should apply the up direction correctly" do
42
+ @migration.apply(@db, :up).should == [1111, 3333]
43
+ end
44
+
45
+ specify "should apply the down direction correctly" do
46
+ @migration.apply(@db, :down).should == [2222, 4444]
47
+ end
48
+ end
49
+
50
+ class DummyMigrationDataset
51
+ attr_reader :from
52
+
53
+ def initialize(x); @from = x; end
54
+
55
+ @@version = nil
56
+
57
+ def version; @@version; end
58
+ def version=(x); @@version = x; end
59
+ def first; @@version ? {:version => @@version} : nil; end
60
+ def update(h); @@version = h[:version]; end
61
+ def <<(h); @@version = h[:version]; end
62
+ end
63
+
64
+ class DummyMigrationDB
65
+ attr_reader :creates, :drops, :table_created
66
+
67
+ def initialize
68
+ @creates = []
69
+ @drops = []
70
+ end
71
+
72
+ def create(x); @creates << x; end
73
+ def drop(x); @drops << x; end
74
+
75
+ def [](x); DummyMigrationDataset.new(x); end
76
+
77
+ def create_table(x); raise if @table_created == x; @table_created = x; end
78
+ def table_exists?(x); @table_created == x; end
79
+
80
+ def transaction; yield; end
81
+ end
82
+
83
+ MIGRATION_001 = %[
84
+ class CreateSessions < Sequel::Migration
85
+ def up
86
+ create(1111)
87
+ end
88
+
89
+ def drop
90
+ drop(1111)
91
+ end
92
+ end
93
+ ]
94
+
95
+ MIGRATION_002 = %[
96
+ class CreateNodes < Sequel::Migration
97
+ def up
98
+ create(2222)
99
+ end
100
+
101
+ def down
102
+ drop(2222)
103
+ end
104
+ end
105
+ ]
106
+
107
+ MIGRATION_003 = %[
108
+ class CreateUsers < Sequel::Migration
109
+ def up
110
+ create(3333)
111
+ end
112
+
113
+ def down
114
+ drop(3333)
115
+ end
116
+ end
117
+ ]
118
+
119
+ MIGRATION_005 = %[
120
+ class CreateAttributes < Sequel::Migration
121
+ def up
122
+ create(5555)
123
+ end
124
+
125
+ def down
126
+ drop(5555)
127
+ end
128
+ end
129
+ ]
130
+
131
+ context "Sequel::Migrator" do
132
+ setup do
133
+ @db = DummyMigrationDB.new
134
+
135
+ File.open('001_create_sessions.rb', 'w') {|f| f << MIGRATION_001}
136
+ File.open('002_create_nodes.rb', 'w') {|f| f << MIGRATION_002}
137
+ File.open('003_create_users.rb', 'w') {|f| f << MIGRATION_003}
138
+ File.open('005_create_attributes.rb', 'w') {|f| f << MIGRATION_005}
139
+
140
+ @db[:schema_info].version = nil
141
+ end
142
+
143
+ teardown do
144
+ Object.send(:remove_const, "CreateSessions") if Object.const_defined?("CreateSessions")
145
+ Object.send(:remove_const, "CreateNodes") if Object.const_defined?("CreateNodes")
146
+ Object.send(:remove_const, "CreateUsers") if Object.const_defined?("CreateUsers")
147
+ Object.send(:remove_const, "CreateAttributes") if Object.const_defined?("CreateAttributes")
148
+
149
+ FileUtils.rm('001_create_sessions.rb')
150
+ FileUtils.rm('002_create_nodes.rb')
151
+ FileUtils.rm('003_create_users.rb')
152
+ FileUtils.rm('005_create_attributes.rb')
153
+ end
154
+
155
+ specify "should return the list of files for a specified version range" do
156
+ Sequel::Migrator.migration_files('.', 1..1).should == \
157
+ ['./001_create_sessions.rb']
158
+
159
+ Sequel::Migrator.migration_files('.', 1..3).should == \
160
+ ['./001_create_sessions.rb', './002_create_nodes.rb', './003_create_users.rb']
161
+
162
+ Sequel::Migrator.migration_files('.', 3..5).should == \
163
+ ['./003_create_users.rb', './005_create_attributes.rb']
164
+
165
+ Sequel::Migrator.migration_files('.', 7..8).should == []
166
+ end
167
+
168
+ specify "should return the latest version available" do
169
+ Sequel::Migrator.latest_migration_version('.').should == 5
170
+ end
171
+
172
+ specify "should load the migration classes for the specified range" do
173
+ Sequel::Migrator.migration_classes('.', 3, 0, :up).should == \
174
+ [CreateSessions, CreateNodes, CreateUsers]
175
+ end
176
+
177
+ specify "should start from current + 1 for the up direction" do
178
+ Sequel::Migrator.migration_classes('.', 3, 1, :up).should == \
179
+ [CreateNodes, CreateUsers]
180
+ end
181
+
182
+ specify "should end on current + 1 for the down direction" do
183
+ Sequel::Migrator.migration_classes('.', 2, 5, :down).should == \
184
+ [CreateAttributes, CreateUsers]
185
+ end
186
+
187
+ specify "should automatically create the schema_info table" do
188
+ @db.table_exists?(:schema_info).should be_false
189
+ Sequel::Migrator.schema_info_dataset(@db)
190
+ @db.table_exists?(:schema_info).should be_true
191
+
192
+ # should not raise if table already exists
193
+ proc {Sequel::Migrator.schema_info_dataset(@db)}.should_not raise_error
194
+ end
195
+
196
+ specify "should return a dataset for the schema_info table" do
197
+ d = Sequel::Migrator.schema_info_dataset(@db)
198
+ d.from.should == :schema_info
199
+ end
200
+
201
+ specify "should get the migration version stored in the database" do
202
+ # default is 0
203
+ Sequel::Migrator.get_current_migration_version(@db).should == 0
204
+
205
+ Sequel::Migrator.schema_info_dataset(@db) << {:version => 4321}
206
+
207
+ Sequel::Migrator.get_current_migration_version(@db).should == 4321
208
+ end
209
+
210
+ specify "should set the migration versison stored in the datbase" do
211
+ Sequel::Migrator.get_current_migration_version(@db).should == 0
212
+ Sequel::Migrator.set_current_migration_version(@db, 6666)
213
+ Sequel::Migrator.get_current_migration_version(@db).should == 6666
214
+ end
215
+
216
+ specify "should apply migrations correctly in the up direction" do
217
+ Sequel::Migrator.apply(@db, '.', 3, 2)
218
+ @db.creates.should == [3333]
219
+
220
+ Sequel::Migrator.get_current_migration_version(@db).should == 3
221
+
222
+ Sequel::Migrator.apply(@db, '.', 5)
223
+ @db.creates.should == [3333, 5555]
224
+
225
+ Sequel::Migrator.get_current_migration_version(@db).should == 5
226
+ end
227
+
228
+ specify "should apply migrations correctly in the down direction" do
229
+ Sequel::Migrator.apply(@db, '.', 1, 5)
230
+ @db.drops.should == [5555, 3333, 2222]
231
+
232
+ Sequel::Migrator.get_current_migration_version(@db).should == 1
233
+ end
234
+
235
+ specify "should apply migrations up to the latest version if no target is given" do
236
+ Sequel::Migrator.apply(@db, '.')
237
+ @db.creates.should == [1111, 2222, 3333, 5555]
238
+
239
+ Sequel::Migrator.get_current_migration_version(@db).should == 5
240
+ end
241
+ 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.2
7
- date: 2007-07-24 00:00:00 +03:00
6
+ version: 0.1.9.3
7
+ date: 2007-08-10 00:00:00 +03:00
8
8
  summary: Concise ORM for Ruby.
9
9
  require_paths:
10
10
  - lib
@@ -20,7 +20,7 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 1.8.2
23
+ version: 1.8.4
24
24
  version:
25
25
  platform: ruby
26
26
  signing_key:
@@ -40,6 +40,7 @@ files:
40
40
  - spec/database_spec.rb
41
41
  - spec/dataset_spec.rb
42
42
  - spec/expressions_spec.rb
43
+ - spec/migration_spec.rb
43
44
  - spec/adapters/mysql_spec.rb
44
45
  - spec/adapters/sqlite_spec.rb
45
46
  - lib/sequel
@@ -52,7 +53,7 @@ files:
52
53
  - lib/sequel/dbi.rb
53
54
  - lib/sequel/error.rb
54
55
  - lib/sequel/expressions.rb
55
- - lib/sequel/metaid.rb
56
+ - lib/sequel/migration.rb
56
57
  - lib/sequel/model.rb
57
58
  - lib/sequel/mysql.rb
58
59
  - lib/sequel/odbc.rb
@@ -89,5 +90,13 @@ extensions: []
89
90
 
90
91
  requirements: []
91
92
 
92
- dependencies: []
93
-
93
+ dependencies:
94
+ - !ruby/object:Gem::Dependency
95
+ name: metaid
96
+ version_requirement:
97
+ version_requirements: !ruby/object:Gem::Version::Requirement
98
+ requirements:
99
+ - - ">"
100
+ - !ruby/object:Gem::Version
101
+ version: 0.0.0
102
+ version:
data/lib/sequel/metaid.rb DELETED
@@ -1,24 +0,0 @@
1
- # Metaid == a few simple metaclass helper
2
- # (See http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html.)
3
- class Object
4
- # The hidden singleton lurks behind everyone
5
- def metaclass; class << self; self; end; end
6
- def meta_eval &blk; metaclass.instance_eval &blk; end
7
-
8
- # Adds methods to a metaclass
9
- def meta_def name, &blk
10
- meta_eval { define_method name, &blk }
11
- end
12
- end
13
-
14
- class Module
15
- # Defines an instance method within a module
16
- def module_def name, &blk
17
- module_eval { define_method name, &blk }
18
- end
19
- end
20
-
21
- class Class
22
- alias class_def module_def
23
- end
24
-