sql_migrations 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: