sequel-inline_schema 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +1 -0
- data.tar.gz.sig +3 -0
- data/.document +5 -0
- data/.editorconfig +14 -0
- data/.rdoc_options +16 -0
- data/.simplecov +9 -0
- data/ChangeLog +48 -0
- data/History.md +4 -0
- data/LICENSE.txt +26 -0
- data/Manifest.txt +16 -0
- data/README.md +85 -0
- data/Rakefile +99 -0
- data/lib/sequel/inline_schema.rb +16 -0
- data/lib/sequel/plugins/inline_migrations.rb +475 -0
- data/lib/sequel/plugins/inline_schema.rb +281 -0
- data/spec/sequel/plugins/inline_migrations_spec.rb +283 -0
- data/spec/sequel/plugins/inline_schema_spec.rb +401 -0
- data/spec/spec_helper.rb +24 -0
- metadata +217 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6037f75d98e75ad634497e1000fdf8cac67cd388a366dc01d73e7a4f00897f15
|
4
|
+
data.tar.gz: 8c6d0fe5f0146d296e85a3e074cf28394e803104c93253e1f200c04274da3ba5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d5bf204ce162b8dae4e8780202f5031c2c7a48bf2f3f3eaa18552e2a346c9096a6c046a2e6c5232eb3f3f175314e332fa95d51c11a29711f848135d7f7701dd9
|
7
|
+
data.tar.gz: 2eed48961ec313876ecc72aa512d53f7cdca5ab0792fe93309c01d359d8dbe72489f02ce91e5250769580b1f00a8cae25f449219b77a5b8d8c207332605fc551
|
checksums.yaml.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
����&����sȻc�9�&�gDX��n��%���i��,�,����.|���>E�Cs��*���&36h'���J����S5��|4j
|
data.tar.gz.sig
ADDED
@@ -0,0 +1,3 @@
|
|
1
|
+
���Q.���e��Đ�xzEm��E�aQ��M'�5"�-`�����;w���C��?�[b��R�Z���m��v��`�2 �2�.�I��4�ң�~h�B:��;EL�C�=�P�:j�������T��j}�ɵuz�{�`p�dO��?�����Y�X�&!YQ
|
2
|
+
����v�a��Lk��y:\D�e�'� O���لR
|
3
|
+
�n99*�ԃ�ĽU�k�q�m��]5���W�u���ZzT.�g�S�t��J=���Y��?��a�{��Н(���O�M6�-{�;).�=�"�'ϳ�/c+�L����T�z�Ȋ�٘���U?��K�� ݶ����q+�K7�VX,7C���?HM��a`K&Wv�D�
|
data/.document
ADDED
data/.editorconfig
ADDED
data/.rdoc_options
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
--- !ruby/object:RDoc::Options
|
2
|
+
encoding: UTF-8
|
3
|
+
static_path: []
|
4
|
+
rdoc_include:
|
5
|
+
- .
|
6
|
+
charset: UTF-8
|
7
|
+
exclude:
|
8
|
+
hyperlink_all: false
|
9
|
+
line_numbers: false
|
10
|
+
main_page: README.md
|
11
|
+
markup: markdown
|
12
|
+
show_hash: false
|
13
|
+
tab_width: 8
|
14
|
+
title: sequel-plugins-inline_schema Documentation
|
15
|
+
visibility: :protected
|
16
|
+
webcvs:
|
data/.simplecov
ADDED
data/ChangeLog
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
2018-07-10 Michael Granger <ged@FaerieMUD.org>
|
2
|
+
|
3
|
+
* .hgtags:
|
4
|
+
Added tag v0.0.1 for changeset 5f6554f4bd40
|
5
|
+
[8fe6f86ec7c7] [tip]
|
6
|
+
|
7
|
+
* .hgsigs:
|
8
|
+
Added signature for changeset f94a7b19d4de
|
9
|
+
[5f6554f4bd40] [v0.0.1]
|
10
|
+
|
11
|
+
* .hgignore, History.md, README.md, Rakefile, sequel-
|
12
|
+
inline_schema.gemspec:
|
13
|
+
Prep for first release.
|
14
|
+
[f94a7b19d4de]
|
15
|
+
|
16
|
+
2017-09-28 Michael Granger <ged@FaerieMUD.org>
|
17
|
+
|
18
|
+
* lib/sequel/plugins/inline_migrations.rb,
|
19
|
+
lib/sequel/plugins/inline_schema.rb:
|
20
|
+
Fix documentation formatting, add some more details about migrations
|
21
|
+
[f7de01d27012] [github/master]
|
22
|
+
|
23
|
+
2017-09-27 Michael Granger <ged@FaerieMUD.org>
|
24
|
+
|
25
|
+
* README.md:
|
26
|
+
Fix README links.
|
27
|
+
[81085384e8eb]
|
28
|
+
|
29
|
+
* lib/sequel/plugins/inline_schema.rb,
|
30
|
+
spec/sequel/plugins/inline_schema_spec.rb:
|
31
|
+
More documentation updates, add drop_table hooks.
|
32
|
+
[dc2231fc945a]
|
33
|
+
|
34
|
+
* .hgignore, README.md, lib/sequel/plugins/inline_migrations.rb,
|
35
|
+
lib/sequel/plugins/inline_schema.rb:
|
36
|
+
Documentation fixes/additions.
|
37
|
+
[4783fffe2ab9]
|
38
|
+
|
39
|
+
* .document, .editorconfig, .gems, .hgignore, .pryrc, .rdoc_options,
|
40
|
+
.ruby-gemset, .ruby-version, .simplecov, Gemfile, History.md,
|
41
|
+
LICENSE.txt, Manifest.txt, README.md, Rakefile, certs/ged.pem,
|
42
|
+
lib/sequel/inline_schema.rb,
|
43
|
+
lib/sequel/plugins/inline_migrations.rb,
|
44
|
+
lib/sequel/plugins/inline_schema.rb, sequel-inline_schema.gemspec,
|
45
|
+
spec/sequel/plugins/inline_migrations_spec.rb,
|
46
|
+
spec/sequel/plugins/inline_schema_spec.rb, spec/spec_helper.rb:
|
47
|
+
Initial implementation
|
48
|
+
[fe2f291518ce]
|
data/History.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
This plugin uses code from the Sequel project under the MIT License:
|
2
|
+
|
3
|
+
Copyright (c) 2007-2008 Sharon Rosner
|
4
|
+
Copyright (c) 2008-2017 Jeremy Evans
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to
|
8
|
+
deal in the Software without restriction, including without limitation the
|
9
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
10
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
20
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
21
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
The rest is licensed under the same terms, but:
|
24
|
+
|
25
|
+
Copyright (c) 2017, Michael Granger
|
26
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
.document
|
2
|
+
.editorconfig
|
3
|
+
.rdoc_options
|
4
|
+
.simplecov
|
5
|
+
ChangeLog
|
6
|
+
History.md
|
7
|
+
LICENSE.txt
|
8
|
+
Manifest.txt
|
9
|
+
README.md
|
10
|
+
Rakefile
|
11
|
+
lib/sequel/inline_schema.rb
|
12
|
+
lib/sequel/plugins/inline_migrations.rb
|
13
|
+
lib/sequel/plugins/inline_schema.rb
|
14
|
+
spec/sequel/plugins/inline_migrations_spec.rb
|
15
|
+
spec/sequel/plugins/inline_schema_spec.rb
|
16
|
+
spec/spec_helper.rb
|
data/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# sequel-inline_schema
|
2
|
+
|
3
|
+
home
|
4
|
+
: http://bitbucket.org/ged/sequel-inlineschema
|
5
|
+
|
6
|
+
github
|
7
|
+
: https://github.com/ged/sequel-inlineschema
|
8
|
+
|
9
|
+
docs
|
10
|
+
: http://deveiate.org/code/sequel-inline_schema
|
11
|
+
|
12
|
+
|
13
|
+
## Description
|
14
|
+
|
15
|
+
This is a set of plugins for Sequel for declaring a model's table schema and
|
16
|
+
any migrations in the class itself (similar to the legacy `schema` plugin).
|
17
|
+
|
18
|
+
It has only really been tested with PostgreSQL, but patches that make it more generic are welcomed.
|
19
|
+
|
20
|
+
The two plugins are:
|
21
|
+
|
22
|
+
* Sequel::Plugins::InlineSchema
|
23
|
+
* Sequel::Plugins::InlineMigrations
|
24
|
+
|
25
|
+
Examples and usage documentation are included there.
|
26
|
+
|
27
|
+
|
28
|
+
## Prerequisites
|
29
|
+
|
30
|
+
* Ruby >= 2.4
|
31
|
+
* Sequel >= 5.0
|
32
|
+
|
33
|
+
|
34
|
+
## Installation
|
35
|
+
|
36
|
+
$ gem install sequel-inline_schema
|
37
|
+
|
38
|
+
|
39
|
+
## Contributing
|
40
|
+
|
41
|
+
You can check out the current development source with Mercurial via its
|
42
|
+
[project page][bitbucket]. Or if you prefer Git, via [its Github
|
43
|
+
mirror][github].
|
44
|
+
|
45
|
+
After checking out the source, run:
|
46
|
+
|
47
|
+
$ rake newb
|
48
|
+
|
49
|
+
This task will install any missing dependencies, run the tests/specs,
|
50
|
+
and generate the API documentation.
|
51
|
+
|
52
|
+
|
53
|
+
## License
|
54
|
+
|
55
|
+
This plugin uses code from the Sequel project under the MIT License:
|
56
|
+
|
57
|
+
Copyright (c) 2007-2008 Sharon Rosner
|
58
|
+
Copyright (c) 2008-2017 Jeremy Evans
|
59
|
+
|
60
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
61
|
+
of this software and associated documentation files (the "Software"), to
|
62
|
+
deal in the Software without restriction, including without limitation the
|
63
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
64
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
65
|
+
furnished to do so, subject to the following conditions:
|
66
|
+
|
67
|
+
The above copyright notice and this permission notice shall be included in
|
68
|
+
all copies or substantial portions of the Software.
|
69
|
+
|
70
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
71
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
72
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
73
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
74
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
75
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
76
|
+
|
77
|
+
The rest is licensed under the same terms, but:
|
78
|
+
|
79
|
+
Copyright (c) 2017-2018, Michael Granger
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
[bitbucket]: http://bitbucket.org/ged/sequel-inlineschema
|
84
|
+
[github]: https://github.com/ged/sequel-inlineschema
|
85
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'hoe'
|
5
|
+
rescue LoadError
|
6
|
+
abort "This Rakefile requires hoe (gem install hoe)"
|
7
|
+
end
|
8
|
+
|
9
|
+
GEMSPEC = 'sequel-inline_schema.gemspec'
|
10
|
+
|
11
|
+
|
12
|
+
Hoe.plugin :mercurial
|
13
|
+
Hoe.plugin :signing
|
14
|
+
Hoe.plugin :deveiate
|
15
|
+
|
16
|
+
Hoe.plugins.delete :rubyforge
|
17
|
+
|
18
|
+
hoespec = Hoe.spec 'sequel-inline_schema' do |spec|
|
19
|
+
|
20
|
+
spec.readme_file = 'README.md'
|
21
|
+
spec.history_file = 'History.md'
|
22
|
+
spec.urls = {
|
23
|
+
home: 'http://bitbucket.org/ged/sequel-inline_schema',
|
24
|
+
code: 'http://bitbucket.org/ged/sequel-inline_schema',
|
25
|
+
docs: 'http://deveiate.org/code/sequel-inline_schema',
|
26
|
+
github: 'http://github.com/ged/sequel-inline_schema',
|
27
|
+
}
|
28
|
+
|
29
|
+
spec.extra_rdoc_files = FileList[ '*.rdoc', '*.md' ]
|
30
|
+
spec.license 'BSD-3-Clause'
|
31
|
+
|
32
|
+
spec.developer 'Michael Granger', 'ged@FaerieMUD.org'
|
33
|
+
|
34
|
+
spec.dependency 'sequel', '~> 5.0'
|
35
|
+
|
36
|
+
spec.dependency 'hoe-deveiate', '~> 0.9', :developer
|
37
|
+
spec.dependency 'simplecov', '~> 0.13', :developer
|
38
|
+
spec.dependency 'rdoc-generator-fivefish', '~> 0.3', :developer
|
39
|
+
|
40
|
+
spec.require_ruby_version( '>=2.4.0' )
|
41
|
+
spec.hg_sign_tags = true if spec.respond_to?( :hg_sign_tags= )
|
42
|
+
spec.check_history_on_release = true if spec.respond_to?( :check_history_on_release= )
|
43
|
+
|
44
|
+
spec.rdoc_locations << "deveiate:/usr/local/www/public/code/#{remote_rdoc_dir}"
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
ENV['VERSION'] ||= hoespec.spec.version.to_s
|
49
|
+
|
50
|
+
# Run the tests before checking in
|
51
|
+
task 'hg:precheckin' => [ :check_history, :check_manifest, :gemspec, :spec ]
|
52
|
+
|
53
|
+
task :test => :spec
|
54
|
+
|
55
|
+
# Rebuild the ChangeLog immediately before release
|
56
|
+
task :prerelease => 'ChangeLog'
|
57
|
+
CLOBBER.include( 'ChangeLog' )
|
58
|
+
|
59
|
+
desc "Build a coverage report"
|
60
|
+
task :coverage do
|
61
|
+
ENV["COVERAGE"] = 'yes'
|
62
|
+
Rake::Task[:spec].invoke
|
63
|
+
end
|
64
|
+
CLOBBER.include( 'coverage' )
|
65
|
+
|
66
|
+
|
67
|
+
# Use the fivefish formatter for docs generated from development checkout
|
68
|
+
if File.directory?( '.hg' )
|
69
|
+
require 'rdoc/task'
|
70
|
+
|
71
|
+
Rake::Task[ 'docs' ].clear
|
72
|
+
RDoc::Task.new( 'docs' ) do |rdoc|
|
73
|
+
|
74
|
+
rdoc.markup = 'markdown'
|
75
|
+
rdoc.main = "README.md"
|
76
|
+
rdoc.rdoc_files.include( "*.md", "ChangeLog", "lib/**/*.rb" )
|
77
|
+
|
78
|
+
rdoc.generator = :fivefish
|
79
|
+
rdoc.title = 'sequel-inline_schema'
|
80
|
+
rdoc.rdoc_dir = 'doc'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
task :gemspec => GEMSPEC
|
85
|
+
file GEMSPEC => [ __FILE__, 'Manifest.txt' ]
|
86
|
+
task GEMSPEC do |task|
|
87
|
+
spec = $hoespec.spec
|
88
|
+
spec.files.delete( '.gemtest' )
|
89
|
+
spec.signing_key = nil
|
90
|
+
spec.cert_chain = ['certs/ged.pem']
|
91
|
+
spec.version = "#{spec.version.bump}.0.pre#{Time.now.strftime("%Y%m%d%H%M%S")}"
|
92
|
+
File.open( task.name, 'w' ) do |fh|
|
93
|
+
fh.write( spec.to_ruby )
|
94
|
+
end
|
95
|
+
end
|
96
|
+
CLOBBER.include( GEMSPEC.to_s )
|
97
|
+
|
98
|
+
task :default => :gemspec
|
99
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'sequel'
|
5
|
+
require 'sequel/plugins/inline_schema'
|
6
|
+
|
7
|
+
module Sequel::InlineSchema
|
8
|
+
|
9
|
+
# Package version
|
10
|
+
VERSION = '0.0.1'
|
11
|
+
|
12
|
+
# Version control revision
|
13
|
+
REVISION = %q$Revision: fe2f291518ce $
|
14
|
+
|
15
|
+
end # Sequel::InlineSchema
|
16
|
+
|
@@ -0,0 +1,475 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'sequel'
|
4
|
+
require 'sequel/model'
|
5
|
+
|
6
|
+
Sequel.extension( :migration )
|
7
|
+
|
8
|
+
|
9
|
+
# A plugin for Sequel::Model that allows migrations for the model to be defined
|
10
|
+
# directly in the class declaration. It uses the `inline_schema` plugin
|
11
|
+
# internally, and will add it for you if necessary.
|
12
|
+
#
|
13
|
+
# ## Example
|
14
|
+
#
|
15
|
+
# Define a base (abstract) model class:
|
16
|
+
#
|
17
|
+
# ```
|
18
|
+
# # lib/acme/model.rb
|
19
|
+
# module Acme
|
20
|
+
# Model = Class.new( Sequel::Model )
|
21
|
+
# Model.def_Model( Acme )
|
22
|
+
#
|
23
|
+
# class Model
|
24
|
+
# plugin :inline_schema
|
25
|
+
# plugin :inline_migrations
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
# ```
|
29
|
+
#
|
30
|
+
# Defining a model class with two migrations:
|
31
|
+
#
|
32
|
+
# ```
|
33
|
+
# # lib/acme/vendor.rb
|
34
|
+
# require 'acme/model'
|
35
|
+
#
|
36
|
+
# class Acme::Vendor < Acme::Model( :vendor )
|
37
|
+
#
|
38
|
+
# # The schema should always be kept up-to-date. I.e., it should be
|
39
|
+
# # modified along with each migration to reflect the state of the table
|
40
|
+
# # after the migration is applied.
|
41
|
+
# set_schema do
|
42
|
+
# primary_key :id
|
43
|
+
# String :name
|
44
|
+
# String :contact
|
45
|
+
# timestamp :created_at, :null => false
|
46
|
+
# timestamp :updated_at
|
47
|
+
#
|
48
|
+
# add_index :name
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# # Similar to Sequel's TimeStampMigrator, inline migrations have a symbolic
|
52
|
+
# # name, which is how they're tracked in the migrations table, and how
|
53
|
+
# # they're ordered when they're applied. The second argument is a human-readable
|
54
|
+
# # description that can be used for automated change control descriptions or
|
55
|
+
# # other tooling.
|
56
|
+
# migration( '20110228_1115_add_timestamps', "Add timestamp fields" ) do
|
57
|
+
# change do
|
58
|
+
# alter_table do
|
59
|
+
# add_column :created_at, :timestamp, :null => false
|
60
|
+
# add_column :updated_at, :timestamp
|
61
|
+
# end
|
62
|
+
# update( :created_at => :now[] )
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# migration( '20110303_1751_index_name', "Add an index to the name field" ) do
|
67
|
+
# change do
|
68
|
+
# alter_table do
|
69
|
+
# add_index :name
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# end
|
75
|
+
# ```
|
76
|
+
#
|
77
|
+
# Apply pending migrations.
|
78
|
+
#
|
79
|
+
# ```
|
80
|
+
# # bin/migrate
|
81
|
+
#
|
82
|
+
# require 'acme/model'
|
83
|
+
# require 'acme/vendor'
|
84
|
+
# # ...
|
85
|
+
#
|
86
|
+
# puts "Creating new tables, applying any pending migrations..."
|
87
|
+
# Acme::Model.migrate
|
88
|
+
# ```
|
89
|
+
#
|
90
|
+
# ## Notable Model Methods
|
91
|
+
#
|
92
|
+
# See Sequel::Plugins::InlineSchema::ClassMethods for documentation for the methods the
|
93
|
+
# plugin adds to your model class/es.
|
94
|
+
#
|
95
|
+
# * `migration` -- define a migration
|
96
|
+
# * `migrate` -- create any missing tables for the receiving model and any subclasses,
|
97
|
+
# then run any unapplied migrations.
|
98
|
+
#
|
99
|
+
# Inline migrations also have model hook methods:
|
100
|
+
#
|
101
|
+
# * `before_migration`
|
102
|
+
# * `after_migration`
|
103
|
+
#
|
104
|
+
# There's also a method that will return a configured Sequel::Plugins::InlineMigrations::Migrator
|
105
|
+
# in case you want to inspect what will happen when you call #migrate:
|
106
|
+
#
|
107
|
+
# * `migrator`
|
108
|
+
#
|
109
|
+
module Sequel::Plugins::InlineMigrations
|
110
|
+
|
111
|
+
### Sequel plugin API -- Called the first time the plugin is loaded for
|
112
|
+
### this model (unless it was already loaded by an ancestor class),
|
113
|
+
### before including/extending any modules, with the arguments and block
|
114
|
+
### provided to the call to Model.plugin.
|
115
|
+
def self::apply( model, *args ) # :nodoc:
|
116
|
+
@plugins ||= []
|
117
|
+
model.plugin( :subclasses ) # track subclasses
|
118
|
+
model.plugin( :inline_schema )
|
119
|
+
model.instance_variable_set( :@migrations, {} )
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
### A mixin that gets applied to inline migrations to add introspection attributes
|
124
|
+
### and accessors.
|
125
|
+
module MigrationIntrospection
|
126
|
+
|
127
|
+
### Extension callback -- adds 'name', 'model_class', and 'description' instance
|
128
|
+
### variables.
|
129
|
+
def self::extend_object( obj )
|
130
|
+
super
|
131
|
+
obj.instance_variable_set( :@description, nil )
|
132
|
+
obj.instance_variable_set( :@model_class, nil )
|
133
|
+
obj.instance_variable_set( :@name, nil )
|
134
|
+
end
|
135
|
+
|
136
|
+
attr_accessor :name, :model_class, :description
|
137
|
+
|
138
|
+
end # module MigrationIntrospection
|
139
|
+
|
140
|
+
|
141
|
+
# Methods to extend Model classes with.
|
142
|
+
#
|
143
|
+
# :markup: RDoc
|
144
|
+
module ClassMethods
|
145
|
+
|
146
|
+
# A Regexp for matching valid migration names
|
147
|
+
MIGRATION_NAME_PATTERN = /\A\d{8}_\d{4}_\w+\z/
|
148
|
+
|
149
|
+
|
150
|
+
# The Hash of Sequel::SimpleMigration objects for this model, keyed by name
|
151
|
+
attr_reader :migrations
|
152
|
+
|
153
|
+
|
154
|
+
### Add a migration with the specified +name+ and optional +description+. See the
|
155
|
+
### docs for Sequel::Migration for usage, and Sequel::MigrationDSL for the allowed
|
156
|
+
### syntax in the +block+. The name of the migration should be in the form:
|
157
|
+
### <year><month><day>_<hour><minute>_<underbarred_desc>
|
158
|
+
def migration( name, description=nil, &block )
|
159
|
+
raise ScriptError, "invalid migration name %p" % [ name ] unless
|
160
|
+
MIGRATION_NAME_PATTERN.match( name )
|
161
|
+
|
162
|
+
@migrations ||= {}
|
163
|
+
migration_obj = Sequel::MigrationDSL.create( &block )
|
164
|
+
migration_obj.extend( MigrationIntrospection )
|
165
|
+
migration_obj.name = name
|
166
|
+
migration_obj.model_class = self
|
167
|
+
migration_obj.description = description
|
168
|
+
|
169
|
+
@migrations[ name ] = migration_obj
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
### Table-migration hook; called once before missing tables are installed and pending
|
174
|
+
### migrations are run.
|
175
|
+
def before_migration
|
176
|
+
return true
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
### Table-migration hook; called once after missing tables are installed and
|
181
|
+
### pending migrations are run.
|
182
|
+
def after_migration
|
183
|
+
return true
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
### After table creation hook to register any existing migrations as being
|
188
|
+
### already applied, as the schema declared by set_schema should be the *latest*
|
189
|
+
### schema, and already incorporate the changes described by the migrations.
|
190
|
+
def after_create_table
|
191
|
+
super
|
192
|
+
self.register_existing_migrations
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
### Register any migrations on the receiver as having already been run (as when creating
|
197
|
+
### the table initially).
|
198
|
+
def register_existing_migrations
|
199
|
+
# Register existing migrations as already being applied
|
200
|
+
if self.migrations && !self.migrations.empty?
|
201
|
+
migrator = self.migrator
|
202
|
+
|
203
|
+
self.migrations.each_value do |migration|
|
204
|
+
migration_data = {
|
205
|
+
name: migration.name,
|
206
|
+
model_class: migration.model_class.name
|
207
|
+
}
|
208
|
+
next unless migrator.dataset.filter( migration_data ).empty?
|
209
|
+
self.db.log_info " fast-forwarding migration #{migration.name}..."
|
210
|
+
migrator.dataset.insert( migration_data )
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
### Create any new tables and run any pending migrations. If the optional +target+ is
|
216
|
+
### supplied, the migrations up to (and including) that one will be applied. If
|
217
|
+
### it has already been applied, any from the currently-applied one to it
|
218
|
+
### (inclusive) will be reversed. A target of +nil+ is equivalent to the last one
|
219
|
+
def migrate( target=nil )
|
220
|
+
migrator = self.migrator( target )
|
221
|
+
classes_to_install = self.uninstalled_tables
|
222
|
+
self.db.log_info "Classes with tables that need to be installed: %p" % [ classes_to_install ]
|
223
|
+
|
224
|
+
self.db.transaction do
|
225
|
+
self.before_migration
|
226
|
+
self.db.log_info "Creating tables that don't yet exist..."
|
227
|
+
classes_to_install.each( &:create_table )
|
228
|
+
|
229
|
+
self.db.log_info "Running any pending migrations..."
|
230
|
+
migrator.run
|
231
|
+
self.after_migration
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
### Return a configured inline migrator set with the given +target+ migration.
|
237
|
+
def migrator( target=nil )
|
238
|
+
self.db.log_info "Creating the migrator..."
|
239
|
+
Sequel::Plugins::InlineMigrations::Migrator.new( self, nil, target: target )
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
end # module ClassMethods
|
244
|
+
|
245
|
+
|
246
|
+
# Subclass of Sequel::Migrator that provides the logic for extracting and running
|
247
|
+
# migrations from the model classes themselves.
|
248
|
+
class Migrator < Sequel::Migrator
|
249
|
+
|
250
|
+
# Default options for .run and #initialize.
|
251
|
+
DEFAULT_OPTS = {
|
252
|
+
:table => :schema_migrations,
|
253
|
+
:column => :name,
|
254
|
+
}
|
255
|
+
|
256
|
+
|
257
|
+
### Migrates the supplied +db+ (a Sequel::Database) using the migrations declared in the
|
258
|
+
### given +baseclass+. The +baseclass+ is the class to gather migrations from; it and all
|
259
|
+
### of its concrete descendents will be considered.
|
260
|
+
###
|
261
|
+
### The +options+ this method understands:
|
262
|
+
###
|
263
|
+
### column
|
264
|
+
### : The column in the table that stores the migration version. Defaults to
|
265
|
+
### `:version`.
|
266
|
+
###
|
267
|
+
### current
|
268
|
+
### : The current version of the database. If not given, it is retrieved from the
|
269
|
+
### database using the `:table` and `:column` options.
|
270
|
+
###
|
271
|
+
### table
|
272
|
+
### : The name of the migrations table. Defaults to `:schema_migrations`.
|
273
|
+
###
|
274
|
+
### target
|
275
|
+
### : The target version to migrate to. If not given, migrates to the
|
276
|
+
### maximum version.
|
277
|
+
###
|
278
|
+
### Examples
|
279
|
+
###
|
280
|
+
### ```
|
281
|
+
### # Assuming Acme::Model is a Sequel::Model subclass, and Acme::Vendor is a subclass
|
282
|
+
### # of that...
|
283
|
+
### Sequel::InlineMigrations::Migrator.run( Acme::Model )
|
284
|
+
### Sequel::InlineMigrations::Migrator.run( Acme::Model, :target => 15, :current => 10 )
|
285
|
+
### Sequel::InlineMigrations::Migrator.run( Acme::Vendor, :column => :app2_version)
|
286
|
+
### Sequel::InlineMigrations::Migrator.run( Acme::Vendor, :column => :app2_version,
|
287
|
+
### :table => :schema_info2 )
|
288
|
+
### ```
|
289
|
+
def self::run( baseclass, db=nil, opts={} )
|
290
|
+
if db.is_a?( Hash )
|
291
|
+
opts = db
|
292
|
+
db = nil
|
293
|
+
end
|
294
|
+
|
295
|
+
new( baseclass, db, opts ).run
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
### Create a new Migrator that will organize migrations defined for
|
300
|
+
### +baseclass+ or any of its subclasses for the specified +db+.
|
301
|
+
### See Sequel::Plugins::InlineMigrations::Migrator.run for argument details.
|
302
|
+
def initialize( baseclass, db=nil, opts={} )
|
303
|
+
if db.is_a?( Hash )
|
304
|
+
opts = db
|
305
|
+
db = nil
|
306
|
+
end
|
307
|
+
|
308
|
+
db ||= baseclass.db
|
309
|
+
|
310
|
+
opts = DEFAULT_OPTS.merge( opts )
|
311
|
+
schema, table = db.send( :schema_and_table, opts[:table] )
|
312
|
+
|
313
|
+
@db = db
|
314
|
+
@baseclass = baseclass
|
315
|
+
@table = opts[ :table ]
|
316
|
+
@column = opts[ :column ]
|
317
|
+
@target = opts[ :target ]
|
318
|
+
@dataset = make_schema_dataset( @db, @table, @column )
|
319
|
+
end
|
320
|
+
|
321
|
+
|
322
|
+
######
|
323
|
+
public
|
324
|
+
######
|
325
|
+
|
326
|
+
# The database to which the migrator will apply its migrations; a Sequel::Database.
|
327
|
+
attr_reader :db
|
328
|
+
|
329
|
+
# The Class at the top of the hierarchy from which migrations will be fetched
|
330
|
+
attr_reader :baseclass
|
331
|
+
|
332
|
+
# The name of the migration table as a Sequel::SQL::QualifiedIdentifier.
|
333
|
+
attr_reader :table
|
334
|
+
|
335
|
+
# The name of the column which will contain the names of applied migrations as a Symbol.
|
336
|
+
attr_reader :column
|
337
|
+
|
338
|
+
# The migration table dataset (a Sequel::Dataset).
|
339
|
+
attr_reader :dataset
|
340
|
+
|
341
|
+
# The name of the target migration to play up or down to as a String.
|
342
|
+
attr_reader :target
|
343
|
+
|
344
|
+
|
345
|
+
### Apply all migrations to the database
|
346
|
+
def run
|
347
|
+
applied, pending = self.get_partitioned_migrations
|
348
|
+
|
349
|
+
# If no target was specified, and there are no pending
|
350
|
+
# migrations, return early.
|
351
|
+
return if pending.empty? && self.target.nil?
|
352
|
+
|
353
|
+
# If no target was specified, the last one is the target
|
354
|
+
target = self.target || pending.last.name
|
355
|
+
migrations = nil
|
356
|
+
direction = nil
|
357
|
+
|
358
|
+
if target == '0'
|
359
|
+
direction = :down
|
360
|
+
migrations = applied.reverse
|
361
|
+
|
362
|
+
elsif tgtidx = pending.find_index {|m| m.name == target }
|
363
|
+
migrations = pending[ 0..tgtidx ]
|
364
|
+
direction = :up
|
365
|
+
|
366
|
+
elsif tgtidx = applied.find_index {|m| m.name == target }
|
367
|
+
migrations = applied[ tgtidx..-1 ].reverse
|
368
|
+
direction = :down
|
369
|
+
|
370
|
+
else
|
371
|
+
raise Sequel::Error, "couldn't find migration %p"
|
372
|
+
end
|
373
|
+
|
374
|
+
# Run the selected migrations
|
375
|
+
self.db.log_info "Migrating %d steps %s..." % [ migrations.length, direction ]
|
376
|
+
migrations.each do |migration|
|
377
|
+
start = Time.now
|
378
|
+
self.db.log_info "Begin: %s, direction: %s" %
|
379
|
+
[ migration.description, direction ]
|
380
|
+
|
381
|
+
self.db.transaction do
|
382
|
+
migration.apply( self.db, direction )
|
383
|
+
|
384
|
+
mclass = migration.model_class.name
|
385
|
+
if direction == :up
|
386
|
+
self.dataset.insert( self.column => migration.name, :model_class => mclass )
|
387
|
+
else
|
388
|
+
self.dataset.filter( self.column => migration.name, :model_class => mclass ).delete
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
self.db.log_info " finished: %s, direction: %s (%0.6fs)" %
|
393
|
+
[ migration.description, direction, Time.now - start ]
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
|
398
|
+
### Fetch an Array of all model classes which are descended from the migrating subclass,
|
399
|
+
### inclusive.
|
400
|
+
def all_migrating_model_classes
|
401
|
+
return [ self.baseclass ] + self.baseclass.descendents
|
402
|
+
end
|
403
|
+
|
404
|
+
|
405
|
+
### Returns any migration objects found in the migrating subclass or any of its
|
406
|
+
### descendents as an Array of Sequel::SimpleMigration objects, sorted by the migration
|
407
|
+
### name and the name of its migrating class.
|
408
|
+
def all_migrations
|
409
|
+
migrations = self.all_migrating_model_classes.
|
410
|
+
collect( &:migrations ).
|
411
|
+
compact.
|
412
|
+
inject {|all, hash| all.merge(hash) }
|
413
|
+
|
414
|
+
return migrations.values.sort_by {|m| [m.name, m.model_class.name] }
|
415
|
+
end
|
416
|
+
|
417
|
+
|
418
|
+
### Returns two Arrays of migrations, the first one containing those which have already
|
419
|
+
### been applied, and the second containing migrations which are pending. Migrations that
|
420
|
+
### have been marked as applied but are (no longer) defined by a model class will be
|
421
|
+
### ignored.
|
422
|
+
def get_partitioned_migrations
|
423
|
+
|
424
|
+
# Get the list of applied migrations for the subclass and its descendents.
|
425
|
+
migrating_class_names = self.all_migrating_model_classes.map( &:name ).compact
|
426
|
+
applied_map = self.dataset.
|
427
|
+
filter( :model_class => migrating_class_names ).
|
428
|
+
select_hash( column, :model_class )
|
429
|
+
|
430
|
+
# Split up the migrations by whether or not it exists in the map of applied migrations.
|
431
|
+
# Each one is removed from the map, so it can be checked for consistency
|
432
|
+
part_migrations = self.all_migrations.partition do |migration|
|
433
|
+
applied_map.delete( migration.name )
|
434
|
+
end
|
435
|
+
|
436
|
+
# If there are any "applied" migrations left, it's likely been deleted since it was
|
437
|
+
# applied, so just ignore it.
|
438
|
+
unless applied_map.empty?
|
439
|
+
applied_map.each do |migration, classname|
|
440
|
+
db.log_info "No %s migration defined in %s; ignoring it." %
|
441
|
+
[ migration, classname ]
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
return part_migrations
|
446
|
+
end
|
447
|
+
|
448
|
+
|
449
|
+
#######
|
450
|
+
private
|
451
|
+
#######
|
452
|
+
|
453
|
+
### Returns the dataset for the schema_migrations table. If no such table
|
454
|
+
### exists, it is automatically created.
|
455
|
+
def make_schema_dataset( db, table, column )
|
456
|
+
ds = db.from( table )
|
457
|
+
db.log_info "Schema dataset is: %p" % [ ds ]
|
458
|
+
|
459
|
+
if !db.table_exists?( table ) || ds.columns.empty?
|
460
|
+
db.log_info "No migrations table: Installing one."
|
461
|
+
db.create_table( table ) do
|
462
|
+
String column, :primary_key => true
|
463
|
+
String :model_class, :null => false
|
464
|
+
end
|
465
|
+
elsif !ds.columns.include?( column )
|
466
|
+
raise Sequel::Error, "Migrator table %p does not contain column %p (%p)" %
|
467
|
+
[ table, column, ds.columns ]
|
468
|
+
end
|
469
|
+
|
470
|
+
return ds
|
471
|
+
end
|
472
|
+
|
473
|
+
end # class Migrator
|
474
|
+
|
475
|
+
end # Sequel::Plugins::InlineMigrations
|