sql_migrations 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7a7af01aa26b454dce549effa9f5f7bbdefeaa41
4
+ data.tar.gz: f0e181d2405b79d2422d71642f5f3ddeed771af9
5
+ SHA512:
6
+ metadata.gz: 22a9502d97115601a346f6880f797cd2a673a9f34e1fe6087fca4b6b641629d0387e3d6e2ef54d31ba8463f31dab150cf6b42442c3d592043fef0c14679b39a8
7
+ data.tar.gz: 033d9346b11d1c0b7479d29a18aec46b2cbc454fa0a9e247d00d1edf47c5f31549e89d8708ede401f4cdc8475dbd628a99fb3bf3fc6e44102bafb54493cb2f52
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ .*.sw?
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sql_migrations.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Grzegorz Bizon <grzegorz.bizon@ntsn.pl>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,182 @@
1
+ # sql-migrations
2
+
3
+ Simple standalone migrations gem you can use with plain SQL.
4
+
5
+ You can execute migrations, seed datebase on production and on test environment with fixtures in non-Ruby pojects.
6
+ `sql-migrations` can work with multiple different databases.
7
+
8
+ ## Why ?
9
+
10
+ This is particularly useful in old projects that doesn't have migrations support, and you really want to use Continues Delivery strategy.
11
+ Without migrations you wouldn't be able to setup your test environment for automated testing (functional tests, unit tests, integration tests).
12
+
13
+ For example, if you work in old Zend1 project, and you want to take benefit from using Continues Deployment/Continues Integration mechanisms - you may find this project useful.
14
+
15
+ ## Install
16
+
17
+ `sql-migrations` are created using Ruby.
18
+
19
+ 1. First - install Ruby environment, with `rbenv` or `rvm`.
20
+ 2. If your project is not created using Ruby, create your Gemfile:
21
+
22
+ ```ruby
23
+ source 'https://rubygems.org'
24
+ gem 'mysql2'
25
+ gem 'sql_migrations'
26
+ ```
27
+
28
+ You can use all database adapters, that are supported by `Sequel`.
29
+ Adapters supported by `Sequel`, by now, are:
30
+
31
+ ADO, Amalgalite, CUBRID, DataObjects, DB2, DBI, Firebird,
32
+ FoundationDB SQL Layer, IBM_DB, Informix, JDBC, MySQL, Mysql2,
33
+ ODBC, OpenBase, Oracle, PostgreSQL, SQLAnywhere, SQLite3,
34
+ Swift, and TinyTDS
35
+
36
+ If you are using PostgreSQL use
37
+
38
+ ```ruby
39
+ gem 'pg'
40
+ ```
41
+
42
+ 3. Run `bundle install`
43
+
44
+ 4. Create database config file, for example in `config/databases.yml`
45
+
46
+ ```yaml
47
+ default:
48
+ development:
49
+ adapter: mysql2
50
+ encoding: utf8
51
+ database: test_db_dev
52
+ username: test_user
53
+ password: test_pass
54
+ host: 192.168.1.1
55
+ test:
56
+ adapter: mysql2
57
+ encoding: utf8
58
+ database: test_db_test
59
+ username: test_user
60
+ password: test_pass
61
+ host: 192.168.1.1
62
+
63
+ production:
64
+ adapter: mysql2
65
+ encoding: utf8
66
+ database: test_db_prod
67
+ username: test_user
68
+ password: test_pass
69
+ host: 192.168.1.100
70
+ second_db:
71
+ development:
72
+ adapter: mysql2
73
+ encoding: utf8
74
+ database: second_db_dev
75
+ username: test_user
76
+ password: test_pass
77
+ host: 127.0.0.1
78
+ test:
79
+ adapter: mysql2
80
+ encoding: utf8
81
+ database: second_db_test
82
+ username: test_user
83
+ password: test_pass
84
+ host: 127.0.0.1
85
+ ```
86
+
87
+ Note that you need to define `default` database set.
88
+
89
+ 4. Migrations/seed/fixtures are executed using rake tasks. So you will need to create `Rakefile`. Example `Rakefile`:
90
+
91
+ ```ruby
92
+ require 'bundler'
93
+ Bundler.require
94
+
95
+ SqlMigrations.load!('db/config/databases.yml')
96
+ SqlMigrations.load_tasks
97
+ ```
98
+
99
+ 5. It's ready !
100
+
101
+
102
+ ## Usage
103
+
104
+ 1. Valid migration/seed/fixture file names match agains regexp `/(\d{8})_(\d{6})_(.*)?\.sql/`. So valid filenames would be:
105
+
106
+
107
+ ```
108
+ 20150303_180100_test_migration.sql
109
+ 20150303_180100_whatever_description_of_seed.sql
110
+ 20150303_180100_fixture1.sql
111
+ ```
112
+
113
+ You can put plain SQL into that files.
114
+
115
+ 2. You can create migrations files, seed files and fixtures in directories like this:
116
+
117
+ ```
118
+ db/
119
+ migrations/
120
+ fixtures/
121
+ seed/
122
+ ```
123
+
124
+ If you want to use multiple databases, create database directories:
125
+
126
+ db/
127
+ migrations/
128
+ default/
129
+ second_db/
130
+ fixtures/
131
+ default/
132
+ second_db/
133
+ seed/
134
+ default/
135
+ second_db/
136
+
137
+ `default/` directory is mandatory, you can put migrations/seed data/fixtures for default database in base directories:
138
+
139
+ db/
140
+ migrations/
141
+ 20150303_180100_test_migration.sql
142
+ second_db/
143
+ 20150303_180101_test_migration_for_second_db.sql
144
+
145
+ 3. In every database that is specified in YAML config, `sql-migrations` will create table `sqlmigrations_schema`
146
+ 4. If everything is set up properly, you should see `sqlmigrations` tasks after typing
147
+
148
+
149
+ rake -T
150
+
151
+ 5. Run tasks:
152
+
153
+
154
+ rake sqlmigrations:db:migrate # this will execute migrations
155
+ rake sqlmigrations:db:seed # this will seed database with initial data
156
+ rake sqlmigration:files:list # this will list all migrations/seed files/fixtures that where found
157
+
158
+ 6. Enviroment variables
159
+
160
+ If you want to run migration on different database (for example test) specify ENV:
161
+
162
+ ```bash
163
+ ENV=test rake sqlmigrations:db:migrate
164
+ ENV=test rake sqlmigrations:db:test:seed
165
+ ```
166
+
167
+ or in production:
168
+
169
+ ```bash
170
+ ENV=production rake sqlmigrations:db:migrate
171
+ ENV=production rake sqlmigrations:db:seed
172
+ ```
173
+
174
+ ## TODO
175
+
176
+ 1. Tests
177
+ 2. Generator for `databases.yml`
178
+ 3. Generator for migrations
179
+
180
+ ## License
181
+
182
+ This is free sofware licensed under MIT license, see LICENSE file
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,62 @@
1
+ module SqlMigrations
2
+ class Database
3
+
4
+ SCHEMA_TABLE = :sqlmigrations_schema
5
+ attr_reader :db
6
+
7
+ def initialize(options)
8
+ @name = options[:name] || :default
9
+ begin
10
+ @db = Sequel.connect(adapter: options['adapter'],
11
+ host: options['host'],
12
+ database: options['database'],
13
+ user: options['username'],
14
+ password: options['password'],
15
+ test: true)
16
+ rescue
17
+ puts "[-] Could not connect to database using #{options['adapter']} adapter"
18
+ raise
19
+ else
20
+ puts "[+] Connected to database using #{options['adapter']} adapter"
21
+ end
22
+ install_table
23
+ end
24
+
25
+ def execute_migrations
26
+ puts "[i] Executing migrations"
27
+ Migration.find(@name).each { |migration| migration.execute(self) }
28
+ end
29
+
30
+ def seed_database
31
+ puts "[i] Seeding database"
32
+ Seed.find(@name).each { |seed| seed.execute(self) }
33
+ end
34
+
35
+ def seed_with_fixtures
36
+ puts "[i] Seeding test database with fixtures"
37
+ Fixture.find(@name).each { |fixture| fixture.execute(self) }
38
+ end
39
+
40
+ def schema_dataset
41
+ @db[SCHEMA_TABLE]
42
+ end
43
+
44
+ private
45
+ def install_table
46
+ # Check if we have migrations_schema table present
47
+ unless @db.table_exists?(SCHEMA_TABLE)
48
+ puts "[!] Installing `#{SCHEMA_TABLE}`"
49
+ @db.create_table(SCHEMA_TABLE) do
50
+ primary_key :id
51
+ Bignum :time
52
+ DateTime :executed
53
+ String :name
54
+ String :type
55
+ index [ :time, :type ]
56
+ end
57
+ end
58
+ end
59
+
60
+
61
+ end
62
+ end
@@ -0,0 +1,13 @@
1
+ module SqlMigrations
2
+ class Fixture < SqlScript
3
+
4
+ def self.find(db_name)
5
+ super(db_name, :fixture)
6
+ end
7
+
8
+ def to_s
9
+ "Fixture #{@name}, datetime: #{@date + @time}"
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module SqlMigrations
2
+ class Migration < SqlScript
3
+
4
+ def self.find(db_name)
5
+ super(db_name, :migrations)
6
+ end
7
+
8
+ def to_s
9
+ "Migration #{@name} for db: #{@db_name}, datetime: #{@date + @time}"
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module SqlMigrations
2
+ class Seed < SqlScript
3
+
4
+ def self.find(db_name)
5
+ super(db_name, :seed)
6
+ end
7
+
8
+ def to_s
9
+ "Seed data #{@name}, datetime: #{@date + @time}"
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,92 @@
1
+ module SqlMigrations
2
+ class SqlScript
3
+
4
+ attr_reader :date, :time, :name
5
+
6
+ def initialize(path, opts)
7
+ @date = opts[:date]
8
+ @time = opts[:time]
9
+ @name = opts[:name]
10
+ @path = opts[:path]
11
+ @db_name = opts[:db_name]
12
+ @content = IO.read(path)
13
+ @type = self.class.name.downcase.split('::').last
14
+ @datetime = (@date + @time).to_i
15
+ end
16
+
17
+ def execute(db)
18
+ @database = db
19
+ return unless is_new?
20
+ begin
21
+ @database.db.transaction do
22
+ @benchmark = Benchmark.measure do
23
+ @database.db.run @content
24
+ end
25
+ end
26
+ rescue
27
+ puts "[-] Error while executing #{@type} #{@name} !"
28
+ raise
29
+ else
30
+ on_success
31
+ end
32
+ end
33
+
34
+ def self.find(db_name, type)
35
+ scripts = []
36
+ Find.find(Dir.pwd) do |path|
37
+ if !db_name.is_a? Array then db_name = [ db_name ] end
38
+ file_date, file_time, file_name, file_db = self.file_options(db_name, type, path)
39
+ next unless file_name
40
+ scripts << self.new(path, date: file_date, time: file_time, name: file_name, db_name: file_db)
41
+ end
42
+ scripts.sort_by { |file| (file.date + file.time).to_i }
43
+ end
44
+
45
+ private
46
+ def self.file_options(db_names, type, path)
47
+
48
+ up_dir, dir, filename = path.split(File::SEPARATOR)[-3, 3]
49
+
50
+ # Only files that match agains this regexp
51
+ file_opts = File.basename(path).match(/(\d{8})_(\d{6})_(.*)?\.sql/)
52
+ return nil unless file_opts
53
+
54
+ file_in_type_dir = (dir == type.to_s)
55
+ file_in_type_updir = (up_dir == type.to_s)
56
+ file_in_db_dir = db_names.include?(dir.to_sym)
57
+
58
+ # There is exception when we are looking for files for more than one database
59
+ # or we are checking default database only
60
+ if db_names == [ :default ] || db_names.count > 1
61
+ return nil unless (file_in_type_dir || (file_in_db_dir && file_in_type_updir))
62
+ else
63
+ # Only files for specific type (migration/fixture/seed)
64
+ # Only files for specific database
65
+ return nil unless (file_in_db_dir && file_in_type_updir)
66
+ end
67
+
68
+ file_database = db_names.include?(dir.to_sym) ? dir : :default
69
+ return file_opts[1], file_opts[2], file_opts[3], file_database
70
+ end
71
+
72
+ def is_new?
73
+ schema = @database.schema_dataset
74
+ last = schema.order(Sequel.asc(:time)).where(type: @type).last
75
+ is_new = schema.where(time: @datetime, type: @type).count == 0
76
+ if is_new && !last.nil?
77
+ if last[:time] > @datetime
78
+ raise "#{@type.capitalize} #{@name} has time BEFORE last one recorded !"
79
+ end
80
+ end
81
+ is_new
82
+ end
83
+
84
+ def on_success
85
+ puts "[+] Successfully executed #{@type}, name: #{@name}"
86
+ puts " Benchmark: #{@benchmark}"
87
+ schema = @database.schema_dataset
88
+ schema.insert(time: @datetime, name: @name, type: @type, executed: DateTime.now)
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,50 @@
1
+ module SqlMigrations
2
+ class Supervisor
3
+
4
+ def initialize
5
+ @env = (ENV['ENV'] ||= "development").to_sym
6
+ @options = SqlMigrations.options
7
+ @databases = get_databases_from_config
8
+ end
9
+
10
+ def migrate
11
+ databases_run { |db| db.execute_migrations }
12
+ end
13
+
14
+ def seed
15
+ databases_run { |db| db.seed_database }
16
+ end
17
+
18
+ def seed_test
19
+ databases_run { |db| db.seed_with_fixtures }
20
+ end
21
+
22
+ def list_files
23
+ Migration.find(@databases).each { |migration| puts migration }
24
+ Seed.find(@databases).each { |seed| puts seed }
25
+ Fixture.find(@databases).each { |fixture| puts fixture }
26
+ end
27
+
28
+ private
29
+ def get_databases_from_config
30
+ databases = @options.map { |k, v| k.to_sym }
31
+ unless databases.include?(:default)
32
+ raise "Default database configuration not found !"
33
+ end
34
+ databases
35
+ end
36
+
37
+ def databases_run
38
+ @databases.each do |db|
39
+ db_options = @options[db.to_s][@env.to_s]
40
+ db_options.merge!(name: db)
41
+ if db_options
42
+ yield Database.new(db_options)
43
+ else
44
+ raise "Configuration for #{db} in environment #{@env} not found !"
45
+ end
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,8 @@
1
+ namespace :sqlmigrations do
2
+ namespace :files do
3
+ desc "List found migration and seed files"
4
+ task :list do
5
+ SqlMigrations::Supervisor.new.list_files
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ namespace :sqlmigrations do
2
+ namespace :db do
3
+ desc "Run migrations"
4
+ task :migrate do
5
+ SqlMigrations::Supervisor.new.migrate
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ namespace :sqlmigrations do
2
+ namespace :db do
3
+ desc "Seed database"
4
+ task :seed do
5
+ SqlMigrations::Supervisor.new.seed
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ namespace :sqlmigrations do
2
+ namespace :db do
3
+ namespace :test do
4
+ desc "Seed test database with fixtures"
5
+ task :seed do
6
+ SqlMigrations::Supervisor.new.seed_test
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module SqlMigrations
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,30 @@
1
+ require 'sequel'
2
+ require 'yaml'
3
+ require 'find'
4
+ require 'benchmark'
5
+ require 'time'
6
+
7
+ require 'sql_migrations/version'
8
+ require 'sql_migrations/database'
9
+ require 'sql_migrations/supervisor'
10
+ require 'sql_migrations/sql_script'
11
+ require 'sql_migrations/migration'
12
+ require 'sql_migrations/seed'
13
+ require 'sql_migrations/fixture'
14
+
15
+ module SqlMigrations
16
+ class << self
17
+ attr_reader :options
18
+
19
+ def load_tasks
20
+ load "sql_migrations/tasks/migrate.rake"
21
+ load "sql_migrations/tasks/seed.rake"
22
+ load "sql_migrations/tasks/seed_test.rake"
23
+ load "sql_migrations/tasks/list.rake"
24
+ end
25
+
26
+ def load!(config_file)
27
+ @options = YAML::load_file(config_file)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sql_migrations/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sql_migrations"
8
+ spec.version = SqlMigrations::VERSION
9
+ spec.authors = ["Grzegorz Bizon"]
10
+ spec.email = ["grzegorz.bizon@ntsn.pl"]
11
+ spec.summary = %q{Simple standalone migrations you can use with plain SQL}
12
+ spec.homepage = "http://github.com/grzesiek/sql-migrations"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency 'bundler', '~> 1.7'
21
+ spec.add_dependency 'rake', '~> 10.0'
22
+ spec.add_dependency 'sequel', '~> 4.19.0'
23
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sql_migrations
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Grzegorz Bizon
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sequel
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 4.19.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 4.19.0
55
+ description:
56
+ email:
57
+ - grzegorz.bizon@ntsn.pl
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE
65
+ - README.md
66
+ - Rakefile
67
+ - lib/sql_migrations.rb
68
+ - lib/sql_migrations/database.rb
69
+ - lib/sql_migrations/fixture.rb
70
+ - lib/sql_migrations/migration.rb
71
+ - lib/sql_migrations/seed.rb
72
+ - lib/sql_migrations/sql_script.rb
73
+ - lib/sql_migrations/supervisor.rb
74
+ - lib/sql_migrations/tasks/list.rake
75
+ - lib/sql_migrations/tasks/migrate.rake
76
+ - lib/sql_migrations/tasks/seed.rake
77
+ - lib/sql_migrations/tasks/seed_test.rake
78
+ - lib/sql_migrations/version.rb
79
+ - sql_migrations.gemspec
80
+ homepage: http://github.com/grzesiek/sql-migrations
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.2.2
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Simple standalone migrations you can use with plain SQL
104
+ test_files: []
105
+ has_rdoc: