sql_migrations 2.2.1 → 2.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d7eefab432a38d45308f4a3e59cc8516151a6ff7
4
- data.tar.gz: acb7edb97f69f2b6d7447306bb024cacb024765f
3
+ metadata.gz: 4cd2f64f3d6c5864d8a53ee00437de877d31c07d
4
+ data.tar.gz: 3975b9223a91de17766cb2deb74c733de7717422
5
5
  SHA512:
6
- metadata.gz: 0c83d6b1ee4783fd09d935be49c812323d331b0023a148a42eda184b9d85afc714aee7f047075c70a51c806e4c1af202df393addf96c7a5da7c4b269ab69b826
7
- data.tar.gz: c134ae9d84e65f66d8bbe07273a63531bb2c94c3a8bab4def1ca678325a4461e28a77e4d6d2df8afe24c0fd1f37c9ef9b2b2a4241a3c71f204afcd9c491e7841
6
+ metadata.gz: e24cc1c8994f34a3c6238a6a7b838a405e4f21c92964671c3b6554d774663df12a140125bc17214d4623aef2da55588c3b814ebe910208424dc11fd2b391701c
7
+ data.tar.gz: 840fa00ad8010e4c9911328a05d53287660f3f7266598b5e91278eb215c9fea651ec928da23fdb04728766edf24bf848be225e8abc8c3ad8b80bf5016f08422a
data/.rubocop.yml CHANGED
@@ -4,3 +4,5 @@ Metrics/MethodLength:
4
4
  Max: 18
5
5
  Style/SignalException:
6
6
  Enabled: false
7
+ Style/ModuleFunction:
8
+ Enabled: false
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Simple standalone migrations you can use with plain SQL.
7
7
 
8
- This gives you possibility to execute migrations, seed datebase (on production and in test environment with fixtures) in non-Ruby projects.
8
+ This gives you possibility to execute migrations, seed datebase (on production and in test environment) in non-Ruby projects.
9
9
  `sql-migrations` can work with multiple different databases, and support many db adapters.
10
10
 
11
11
  ## Why ?
@@ -105,14 +105,14 @@ For example, if you work on old Zend 1 project, and you want to take benefit fro
105
105
  If you have multi-statement migrations you should provide `separator` configuration variable in `options` block. `options` key is optional in YAML.
106
106
 
107
107
 
108
- 4. Migrations/seeds/fixtures can be executed using rake tasks. So you will need to create `Rakefile`:
108
+ 4. Migrations/seeds can be executed using rake tasks. So you will need to create `Rakefile`:
109
109
 
110
110
  ```ruby
111
111
  require 'bundler'
112
112
  Bundler.require
113
113
 
114
114
  SqlMigrations::Config.load!('db/config/databases.yml')
115
- SqlMigrations.load_tasks
115
+ SqlMigrations.load_tasks!
116
116
  ```
117
117
 
118
118
  You can also create Rake tasks by yourself:
@@ -147,22 +147,19 @@ For example, if you work on old Zend 1 project, and you want to take benefit fro
147
147
 
148
148
  ## Usage
149
149
 
150
- 1. Valid migration/seeds/fixture file names match against regexp `/(\d{8})_(\d{6})_(.*)?\.sql/`. So valid filenames would be:
150
+ 1. Valid migration/seeds file names match against regexp `/(\d{8})_(\d{6})_(.*)?\.sql/`. So valid filenames would be:
151
151
 
152
152
 
153
153
  20150303_180100_test_migration.sql
154
154
  20150303_180100_whatever_description_of_seed.sql
155
- 20150303_180100_fixture1.sql
156
155
 
157
156
  You can put plain SQL into that files.
158
157
 
159
- 2. It is possible to create migration files, seed files and fixtures inside following directory structure:
158
+ 2. It is possible to create migration files and seed files inside following directory structure:
160
159
 
161
160
  db/
162
161
  migrations/
163
162
  20150303_180100_test_migration.sql
164
- fixtures/
165
- 20150303_180100_fixture1.sql
166
163
  seeds/
167
164
  20150303_180100_whatever_description_of_seed.sql
168
165
 
@@ -172,14 +169,11 @@ For example, if you work on old Zend 1 project, and you want to take benefit fro
172
169
  migrations/
173
170
  default/
174
171
  second_db/
175
- fixtures/
176
- default/
177
- second_db/
178
172
  seeds/
179
173
  default/
180
174
  second_db/
181
175
 
182
- `default/` directory is optional, you can put migrations/seed data/fixtures for default database in base directories:
176
+ `default/` directory is optional, you can put migrations/seed data for default database in base directories:
183
177
 
184
178
  db/
185
179
  migrations/
@@ -197,8 +191,7 @@ For example, if you work on old Zend 1 project, and you want to take benefit fro
197
191
 
198
192
  rake sqlmigrations:db:migrate # Run migrations
199
193
  rake sqlmigrations:db:seed # Seed database
200
- rake sqlmigrations:db:test:seed # Seed test database with fixtures
201
- rake sqlmigrations:files:list # List found migration and seed files
194
+ rake sqlmigrations:db:scripts # List all scripts found
202
195
 
203
196
 
204
197
  5. Then, run tasks:
@@ -210,15 +203,15 @@ For example, if you work on old Zend 1 project, and you want to take benefit fro
210
203
  # this will seed database with initial data
211
204
  rake sqlmigrations:db:seed
212
205
 
213
- # this will list all migrations/seed files/fixtures that where found
214
- rake sqlmigration:files:list
206
+ # this will list all migrations/seed files that where found
207
+ rake sqlmigration:db:scripts
215
208
 
216
209
  6. Environment variables
217
210
 
218
211
  If you want to run migration on different database (for example test) specify ENV:
219
212
 
220
213
  ENV=test rake sqlmigrations:db:migrate
221
- ENV=test rake sqlmigrations:db:test:seed
214
+ ENV=test rake sqlmigrations:db:seed
222
215
 
223
216
  or in production:
224
217
 
data/Rakefile CHANGED
@@ -6,8 +6,8 @@ RSpec::Core::RakeTask.new('spec')
6
6
  RuboCop::RakeTask.new
7
7
 
8
8
  task :test do
9
- Rake::Task['spec'].invoke
10
9
  Rake::Task['rubocop'].invoke
10
+ Rake::Task['spec'].invoke
11
11
  end
12
12
 
13
13
  task default: :test
@@ -1,57 +1,41 @@
1
- require 'sequel'
2
- require 'yaml'
3
- require 'find'
4
- require 'benchmark'
5
- require 'time'
6
- require 'erb'
7
-
8
1
  require 'sql_migrations/version'
9
2
  require 'sql_migrations/database'
10
3
  require 'sql_migrations/config'
11
- require 'sql_migrations/script_file'
12
- require 'sql_migrations/sql_script'
4
+ require 'sql_migrations/file'
5
+ require 'sql_migrations/script'
13
6
  require 'sql_migrations/migration'
14
7
  require 'sql_migrations/seed'
15
- require 'sql_migrations/fixture'
16
8
 
17
9
  # SqlMigrations
18
10
  #
19
11
  module SqlMigrations
20
- class << self
21
- def load_tasks
22
- load 'sql_migrations/tasks/migrate.rake'
23
- load 'sql_migrations/tasks/seed.rake'
24
- load 'sql_migrations/tasks/seed_test.rake'
25
- load 'sql_migrations/tasks/list.rake'
26
- end
12
+ extend self
27
13
 
28
- def migrate
29
- databases(&:execute_migrations)
30
- end
14
+ def migrate
15
+ databases(&:migrate)
16
+ end
31
17
 
32
- def seed
33
- databases(&:seed_database)
34
- end
18
+ def seed
19
+ databases(&:seed)
20
+ end
35
21
 
36
- def seed_test
37
- databases(&:seed_with_fixtures)
22
+ def scripts
23
+ Config.databases.each do |name, _config|
24
+ Migration.find(name).each { |migration| puts migration }
25
+ Seed.find(name).each { |seed| puts seed }
38
26
  end
27
+ end
39
28
 
40
- def list_files
41
- Config.databases.each do |name, _config|
42
- Migration.find(name).each { |migration| puts migration }
43
- Seed.find(name).each { |seed| puts seed }
44
- Fixture.find(name).each { |fixture| puts fixture }
45
- end
29
+ def databases
30
+ Config.databases.each do |name, config|
31
+ db = Database.new(name, config)
32
+ yield db if block_given?
46
33
  end
34
+ end
47
35
 
48
- private
49
-
50
- def databases
51
- Config.databases.each do |name, config|
52
- db = Database.new(name, config)
53
- yield db
54
- end
55
- end
36
+ def load_tasks!
37
+ load 'sql_migrations/tasks/migrate.rake'
38
+ load 'sql_migrations/tasks/seed.rake'
39
+ load 'sql_migrations/tasks/scripts.rake'
56
40
  end
57
41
  end
@@ -1,54 +1,59 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+
1
4
  module SqlMigrations
2
- # Configuration class
5
+ # Configuration module
3
6
  #
4
- class Config
5
- class << self
6
- attr_reader :env
7
-
8
- def load!(config_file, env = nil)
9
- @env = (env || ENV['ENV'] || ENV['RAKE_ENV'] || :development).to_sym
10
- config = get_config_for_env_from_file(config_file)
11
- @databases = config[:databases]
12
- @options = config[:options]
13
- { databases: @databases, options: @options }
14
- end
7
+ module Config
8
+ extend self
9
+
10
+ def load!(config_file, env = nil)
11
+ @env = (env || ENV['ENV'] || ENV['RAKE_ENV'] || :development).to_sym
12
+ config = get_config_for_env_from_file(config_file)
13
+ @databases = config[:databases]
14
+ @options = config[:options]
15
+ { databases: @databases, options: @options }
16
+ end
15
17
 
16
- def databases
17
- get_config_required(:@databases)
18
- end
18
+ def env
19
+ @env
20
+ end
19
21
 
20
- def options
21
- get_config_optional(:@options)
22
- end
22
+ def databases
23
+ get_config_required(:@databases)
24
+ end
23
25
 
24
- private
26
+ def options
27
+ get_config_optional(:@options)
28
+ end
25
29
 
26
- def get_config_required(config_variable)
27
- config_value = instance_variable_get(config_variable)
28
- if config_value.nil? || config_value.empty?
29
- raise "No configuration for `#{config_variable.to_s[1..-1]}` !"
30
- end
31
- config_value
32
- end
30
+ private
33
31
 
34
- def get_config_optional(config_variable)
35
- config_value = instance_variable_get(config_variable)
36
- (config_value.nil? || config_value.empty?) ? {} : config_value
32
+ def get_config_required(config_variable)
33
+ config_value = instance_variable_get(config_variable)
34
+ if config_value.nil? || config_value.empty?
35
+ raise "No configuration for `#{config_variable.to_s[1..-1]}` !"
37
36
  end
37
+ config_value
38
+ end
38
39
 
39
- def get_config_for_env_from_file(file)
40
- yaml_hash = YAML.load(ERB.new(File.new(file).read).result)
41
- config = symbolize_keys(yaml_hash)[@env]
42
- raise LoadError, "No configuration for `#{@env}` environment found !" unless config
43
- config
44
- end
40
+ def get_config_optional(config_variable)
41
+ config_value = instance_variable_get(config_variable)
42
+ (config_value.nil? || config_value.empty?) ? {} : config_value
43
+ end
44
+
45
+ def get_config_for_env_from_file(file)
46
+ yaml_hash = YAML.load(ERB.new(::File.new(file).read).result)
47
+ config = symbolize_keys(yaml_hash)[@env]
48
+ raise LoadError, "No configuration for `#{@env}` environment found !" unless config
49
+ config
50
+ end
45
51
 
46
- def symbolize_keys(hash)
47
- hash.each_with_object({}) do |(key, value), new_hash|
48
- new_key = key.is_a?(String) ? key.to_sym : key
49
- new_value = value.is_a?(Hash) ? symbolize_keys(value) : value
50
- new_hash[new_key] = new_value
51
- end
52
+ def symbolize_keys(hash)
53
+ hash.each_with_object({}) do |(key, value), new_hash|
54
+ new_key = key.is_a?(String) ? key.to_sym : key
55
+ new_value = value.is_a?(Hash) ? symbolize_keys(value) : value
56
+ new_hash[new_key] = new_value
52
57
  end
53
58
  end
54
59
  end
@@ -1,8 +1,10 @@
1
+ require 'sequel'
2
+
1
3
  module SqlMigrations
2
4
  # Class that represents database gem will connect to
3
5
  #
4
6
  class Database
5
- SCHEMA_TABLE = :sqlmigrations_schema
7
+ HISTORY_TABLE = :sqlmigrations_schema
6
8
  attr_reader :name, :driver
7
9
 
8
10
  def initialize(name, options)
@@ -19,38 +21,28 @@ module SqlMigrations
19
21
  install_table
20
22
  end
21
23
 
22
- def execute_migrations
24
+ def migrate
23
25
  migrations = Migration.find(@name)
24
26
  if !migrations.empty?
25
27
  puts "[i] Executing migrations for `#{@name}` database"
26
28
  migrations.each { |migration| migration.execute(self) }
27
29
  else
28
- puts "[i] No new migrations for `#{@name}` database"
30
+ puts "[i] No migrations for `#{@name}` database"
29
31
  end
30
32
  end
31
33
 
32
- def seed_database
34
+ def seed
33
35
  seeds = Seed.find(@name)
34
36
  if !seeds.empty?
35
37
  puts "[i] Seeding `#{@name}` database"
36
38
  seeds.each { |seed| seed.execute(self) }
37
39
  else
38
- puts "[i] No new seeds for `#{@name}` database"
39
- end
40
- end
41
-
42
- def seed_with_fixtures
43
- fixtures = Fixture.find(@name)
44
- if !fixtures.empty?
45
- puts "[i] Seeding `#{@name}` database with fixtures"
46
- fixtures.each { |fixture| fixture.execute(self) }
47
- else
48
- puts "[i] No new fixturess for `#{@name}` database"
40
+ puts "[i] No seeds for `#{@name}` database"
49
41
  end
50
42
  end
51
43
 
52
- def schema_dataset
53
- @driver[SCHEMA_TABLE]
44
+ def history
45
+ @driver[HISTORY_TABLE]
54
46
  end
55
47
 
56
48
  private
@@ -66,17 +58,17 @@ module SqlMigrations
66
58
  end
67
59
 
68
60
  def install_table
69
- # Check if we have migrations_schema table present
70
- return if @driver.table_exists?(SCHEMA_TABLE)
71
- puts "[!] Installing `#{SCHEMA_TABLE}`"
72
- @driver.create_table(SCHEMA_TABLE) do
61
+ return if @driver.table_exists?(HISTORY_TABLE)
62
+
63
+ puts "[!] Installing `#{HISTORY_TABLE}` history table"
64
+ @driver.create_table(HISTORY_TABLE) do
73
65
  # rubocop:disable Style/SingleSpaceBeforeFirstArg
74
66
  primary_key :id
75
67
  Bignum :time
76
68
  DateTime :executed
77
69
  String :name
78
70
  String :type
79
- index [:time, :type]
71
+ index [:time, :type]
80
72
  # rubocop:enable Style/SingleSpaceBeforeFirstArg
81
73
  end
82
74
  end
@@ -0,0 +1,67 @@
1
+ module SqlMigrations
2
+ # Class that represents script file
3
+ #
4
+ class File
5
+ attr_reader :name, :time, :date, :datetime, :database, :type, :path
6
+
7
+ def initialize(path, database, type)
8
+ @path = path
9
+ @database = database
10
+ @type = type.to_s
11
+
12
+ @file, @base, @parent = elements(path)
13
+ @date, @time, @name = match(@file) if @file
14
+ @datetime = (@date.to_s + @time.to_s).to_i
15
+ end
16
+
17
+ def valid?
18
+ [@name, @time, @date, @database, directories?].all?
19
+ end
20
+
21
+ def content
22
+ ::File.read(@path)
23
+ end
24
+
25
+ def ==(other)
26
+ datetime == other.datetime
27
+ end
28
+
29
+ def to_s
30
+ @file.to_s
31
+ end
32
+
33
+ private
34
+
35
+ def elements(path)
36
+ _filename, _base_directory, _parent_directory =
37
+ path.split(::File::SEPARATOR).last(3).reverse
38
+ end
39
+
40
+ def match(filename)
41
+ _, date, time, name =
42
+ filename.match(/^(\d{8})_(\d{6})_(.*)?\.sql$/).to_a
43
+ [date, time, name]
44
+ end
45
+
46
+ def directories?
47
+ if @database == :default
48
+ file_in_type_base_directory? || file_in_database_directory?
49
+ else
50
+ file_in_database_directory?
51
+ end
52
+ end
53
+
54
+ def file_in_type_base_directory?
55
+ @base == "#{@type}s"
56
+ end
57
+
58
+ def file_in_type_parent_directory?
59
+ @parent == "#{@type}s"
60
+ end
61
+
62
+ def file_in_database_directory?
63
+ file_in_type_parent_directory? &&
64
+ (@base == @database.to_s)
65
+ end
66
+ end
67
+ end
@@ -1,13 +1,9 @@
1
1
  module SqlMigrations
2
2
  # Migration class
3
3
  #
4
- class Migration < SqlScript
4
+ class Migration < Script
5
5
  def self.find(database_name)
6
6
  super(database_name, :migration)
7
7
  end
8
-
9
- def to_s
10
- "Migration #{@name} for `#{@database_name}` database, datetime: #{@date + @time}"
11
- end
12
8
  end
13
9
  end
@@ -0,0 +1,93 @@
1
+ require 'sequel'
2
+ require 'find'
3
+ require 'benchmark'
4
+ require 'time'
5
+
6
+ module SqlMigrations
7
+ # SqlScript class
8
+ #
9
+ class Script
10
+ DELEGATED = [:name, :date, :time, :datetime, :type, :content, :path]
11
+ attr_reader(*DELEGATED)
12
+
13
+ def initialize(file)
14
+ @file = file
15
+
16
+ DELEGATED.each do |method|
17
+ instance_variable_set("@#{method}", file.send(method))
18
+ end
19
+ end
20
+
21
+ def execute(db)
22
+ @database = db
23
+ return false unless new?
24
+ driver = @database.driver
25
+ begin
26
+ driver.transaction do
27
+ @benchmark = Benchmark.measure do
28
+ statements.each { |query| driver.run(query) }
29
+ end
30
+ end
31
+ rescue
32
+ puts "[-] Error while executing #{@type} #{@name} !"
33
+ raise
34
+ else
35
+ true & on_success
36
+ end
37
+ end
38
+
39
+ def self.find(database, type)
40
+ files = []
41
+
42
+ Find.find(Dir.pwd) do |path|
43
+ file = File.new(path, database, type)
44
+
45
+ raise "Duplicate time for #{type}s: #{files.find { |f| f == file }}, #{file}" if
46
+ file.valid? && files.include?(file)
47
+
48
+ files << file if file.valid?
49
+ end
50
+
51
+ files.sort_by(&:datetime).map { |file| new(file) }
52
+ end
53
+
54
+ def statements
55
+ separator = Config.options[:separator]
56
+ if separator
57
+ statements = @content.split(separator)
58
+ statements.collect!(&:strip)
59
+ statements.reject(&:empty?)
60
+ else
61
+ [content]
62
+ end
63
+ end
64
+
65
+ def to_s
66
+ "#{@type.capitalize} `#{@path}` for `#{@file.database}` database"
67
+ end
68
+
69
+ private
70
+
71
+ def new?
72
+ history = @database.history
73
+ last = history.order(Sequel.asc(:time)).where(type: @type).last
74
+ is_new = history.where(time: @datetime, type: @type).count == 0
75
+
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 " #{type.capitalize} file: #{@date}_#{@time}_#{@name}.sql"
87
+ puts " Benchmark: #{@benchmark}"
88
+
89
+ @database.history.insert(time: @datetime, name: @name,
90
+ type: @type, executed: DateTime.now)
91
+ end
92
+ end
93
+ end
@@ -1,13 +1,9 @@
1
1
  module SqlMigrations
2
2
  # Seed class
3
3
  #
4
- class Seed < SqlScript
4
+ class Seed < Script
5
5
  def self.find(database_name)
6
6
  super(database_name, :seed)
7
7
  end
8
-
9
- def to_s
10
- "Seed data #{@name} for `#{@database_name}` database, datetime: #{@date + @time}"
11
- end
12
8
  end
13
9
  end
@@ -1,8 +1,8 @@
1
1
  namespace :sqlmigrations do
2
- namespace :files do
2
+ namespace :db do
3
3
  desc 'List found migration and seed files'
4
- task :list do
5
- SqlMigrations.list_files
4
+ task :scripts do
5
+ SqlMigrations.scripts
6
6
  end
7
7
  end
8
8
  end
@@ -1,5 +1,5 @@
1
1
  # SqlMigrations
2
2
  #
3
3
  module SqlMigrations
4
- VERSION = '2.2.1'
4
+ VERSION = '2.4.0'
5
5
  end
@@ -1,6 +1,6 @@
1
1
  describe 'no loadable configuration file' do
2
2
  it 'should raise error when config is not loaded' do
3
- expect { SqlMigrations.list_files }.to raise_error RuntimeError
3
+ expect { SqlMigrations.scripts }.to raise_error RuntimeError
4
4
  end
5
5
  end
6
6
 
@@ -1,4 +1,4 @@
1
- describe 'schema table in database' do
1
+ describe 'history table in database' do
2
2
  before do
3
3
  allow(SqlMigrations::Config).to receive(:databases) { { default: {} } }
4
4
  end
@@ -7,7 +7,7 @@ describe 'schema table in database' do
7
7
  expect do
8
8
  @database = SqlMigrations::Database.new(:default, adapter: :sqlite)
9
9
  end.to output("[+] Connected to `default` database using sqlite adapter\n" \
10
- "[!] Installing `sqlmigrations_schema`\n").to_stdout
10
+ "[!] Installing `sqlmigrations_schema` history table\n").to_stdout
11
11
  expect(@database.driver.table_exists?(:sqlmigrations_schema)).to be true
12
12
  end
13
13
 
@@ -19,7 +19,7 @@ describe 'schema table in database' do
19
19
  DateTime :executed
20
20
  String :name
21
21
  String :type
22
- index [:time, :type]
22
+ index [:time, :type]
23
23
  # rubocop:enable Style/SingleSpaceBeforeFirstArg
24
24
  end
25
25
  expect do
@@ -6,7 +6,7 @@ describe 'migrations valid order support engine' do
6
6
  f.puts 'CREATE TABLE test_table(col_int INTEGER, col_str STRING)'
7
7
  end
8
8
 
9
- @database = SqlMigrations::Database.new(:default, adapter: :sqlite)
9
+ @database = SqlMigrations::Database.new(:default, adapter: :sqlite)
10
10
  migration = SqlMigrations::Migration.find(:default).first
11
11
  migration.execute(@database)
12
12
 
@@ -4,8 +4,10 @@ describe 'migration' do
4
4
  File.open('/migrations/20150305_154010_test_migration.sql', 'w') do |f|
5
5
  f.puts 'CREATE TABLE test_table(col_int INTEGER, col_str STRING)'
6
6
  end
7
+
7
8
  allow(SqlMigrations::Config).to receive(:databases) { { default: { development: {} } } }
8
9
  @migration = SqlMigrations::Migration.find(:default).first
10
+ @database = SqlMigrations::Database.new(:default, adapter: :sqlite)
9
11
  end
10
12
 
11
13
  it 'should be found and initialized' do
@@ -24,11 +26,28 @@ describe 'migration' do
24
26
  expect(@migration.name).to eql('test_migration')
25
27
  end
26
28
 
27
- it 'should be properly executed' do
28
- database = SqlMigrations::Database.new(:default, adapter: :sqlite)
29
- database.execute_migrations
30
- expect(@sqlite_db.table_exists?(:test_table)).to be true
31
- expect(@sqlite_db[:test_table].columns).to include(:col_int)
32
- expect(@sqlite_db[:test_table].columns).to include(:col_str)
29
+ context 'executing migrations' do
30
+ subject { -> { @database.migrate } }
31
+
32
+ it 'should be properly executed' do
33
+ subject.call
34
+ expect(@sqlite_db.table_exists?(:test_table)).to be true
35
+ expect(@sqlite_db[:test_table].columns).to include(:col_int)
36
+ expect(@sqlite_db[:test_table].columns).to include(:col_str)
37
+ end
38
+
39
+ context 'invalid migration' do
40
+ before do
41
+ File.open('/migrations/20150825_184010_invalid_migration.sql', 'w') do |f|
42
+ f.puts 'CREATE this_is_error TABLE invalid'
43
+ end
44
+ end
45
+
46
+ it 'should print warning about invalid migration' do
47
+ expect { subject.call }
48
+ .to output(/Error while executing migration invalid_migration !/).to_stdout
49
+ .and raise_error(Sequel::DatabaseError)
50
+ end
51
+ end
33
52
  end
34
53
  end
@@ -0,0 +1,71 @@
1
+ # rubocop:disable Metrics/LineLength
2
+ describe 'sql scripts' do
3
+ before do
4
+ allow(SqlMigrations::Config).to receive(:databases) { { default: {}, test2_db: {} } }
5
+ end
6
+
7
+ context 'valid scripts set' do
8
+ before do
9
+ Dir.mkdir('/migrations')
10
+ Dir.mkdir('/seeds')
11
+
12
+ File.open('/migrations/20150305_154010_first_test_migration.sql', 'w') do |f|
13
+ f.puts 'CREATE TABLE first_test_table(col_int1 INTEGER, col_str1 STRING)'
14
+ end
15
+ File.open('/migrations/20150305_154011_second_test_migration.sql', 'w') do |f|
16
+ f.puts 'CREATE TABLE second_test_table(col_int2 INTEGER, col_str2 STRING)'
17
+ end
18
+ File.open('/seeds/20150305_154010_test_seed.sql', 'w') do |f|
19
+ f.puts 'INSERT INTO first_test_table(col_int1, col_str1) VALUES(123, "test_string1")'
20
+ f.puts 'INSERT INTO second_test_table(col_int2, col_str2) VALUES(456, "test_string2")'
21
+ end
22
+
23
+ Dir.mkdir('/migrations/test2_db')
24
+ File.open('/migrations/test2_db/20150511_144000_second_db_test_migration.sql', 'w') do |f|
25
+ f.puts 'CREATE TABLE second_db_test_table(col_int1 INTEGER, col_str1 STRING)'
26
+ end
27
+
28
+ Dir.mkdir('/migrations/default')
29
+ File.open('/migrations/default//20150511_144100_default_db_test2_migration.sql', 'w') do |f|
30
+ f.puts 'CREATE TABLE default_db_test2_table(col_int1 INTEGER, col_str1 STRING)'
31
+ end
32
+ end
33
+
34
+ it 'should be found' do
35
+ expect { SqlMigrations.scripts }.to \
36
+ output("Migration `/migrations/20150305_154010_first_test_migration.sql` for `default` database\n" \
37
+ "Migration `/migrations/20150305_154011_second_test_migration.sql` for `default` database\n" \
38
+ "Migration `/migrations/default/20150511_144100_default_db_test2_migration.sql` for `default` database\n" \
39
+ "Seed `/seeds/20150305_154010_test_seed.sql` for `default` database\n" \
40
+ "Migration `/migrations/test2_db/20150511_144000_second_db_test_migration.sql` for `test2_db` database\n"
41
+ ).to_stdout
42
+ end
43
+
44
+ it 'should raise an error if two files of same type with same timestamp are present' do
45
+ File.open('/migrations/default/20150511_144100_duplicated_timestamp_migration.sql', 'w') do |f|
46
+ f.puts 'CREATE TABLE duplicated_migration(col_int1 INTEGER)'
47
+ end
48
+
49
+ expect { SqlMigrations.scripts }.to raise_error(RuntimeError, /Duplicate time for migrations: .*/)
50
+ end
51
+ end
52
+
53
+ context 'invalid files' do
54
+ before do
55
+ Dir.mkdir('/migrations')
56
+ File.open('/migrations/20150511_144100_test_migration.sql', 'w') do |f|
57
+ f.puts 'CREATE TABLE test_migration(col_int1 INTEGER)'
58
+ end
59
+ File.open('/migrations/.20150511_144100_testp_migration.sql.swp', 'w') do |f|
60
+ f.puts 'CREATE TABLE testd_migration(col_int1 INTEGER)'
61
+ end
62
+ end
63
+
64
+ it 'should not find .*sw? files' do
65
+ expect { SqlMigrations.scripts }
66
+ .to output("Migration `/migrations/20150511_144100_test_migration.sql` for `default` database\n")
67
+ .to_stdout
68
+ end
69
+ end
70
+ end
71
+ # rubocop:enable Metrics/LineLength
@@ -10,8 +10,8 @@ describe 'seed' do
10
10
  end
11
11
  allow(SqlMigrations::Config).to receive(:databases) { { default: { development: {} } } }
12
12
  database = SqlMigrations::Database.new(:default, adapter: :sqlite)
13
- database.execute_migrations
14
- database.seed_database
13
+ database.migrate
14
+ database.seed
15
15
  end
16
16
 
17
17
  it 'should be properly execute' do
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,9 @@
1
- require 'bundler/setup'
2
- require 'memfs'
3
1
  require 'simplecov'
2
+ require 'memfs'
3
+ require 'pp' # `superclass mismatch for class File` workaround
4
4
 
5
5
  SimpleCov.start
6
- Bundler.require
6
+ require 'sql_migrations'
7
7
 
8
8
  RSpec.configure do |config|
9
9
  config.profile_examples = 2
@@ -16,11 +16,11 @@ RSpec.configure do |config|
16
16
  MemFs.activate!
17
17
  # Reset configuration for every test suite
18
18
  SqlMigrations::Config.instance_eval('@databases = nil; @options = nil')
19
- @stdout, $stdout = $stdout, StringIO.new # Catch STDOUT do variable
19
+ @stdout, $stdout = $stdout, StringIO.new # Catch STDOUT do variable
20
20
  end
21
21
 
22
22
  config.after do
23
- $stdout, @stdout = @stdout, nil # Reassign STDOUT
23
+ $stdout, @stdout = @stdout, nil # Reassign STDOUT
24
24
  MemFs.deactivate!
25
25
  end
26
26
  end
@@ -18,10 +18,10 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ['lib']
19
19
 
20
20
  # rubocop:disable Style/SingleSpaceBeforeFirstArg
21
- spec.add_development_dependency 'rspec', '~> 3.2.0'
21
+ spec.add_development_dependency 'rspec', '~> 3.3'
22
22
  spec.add_development_dependency 'sqlite3', '~> 1.3.10'
23
23
  spec.add_development_dependency 'memfs', '~> 0.4.3'
24
- spec.add_development_dependency 'rubocop', '~> 0.31.0'
24
+ spec.add_development_dependency 'rubocop', '~> 0.33'
25
25
  spec.add_development_dependency 'simplecov', '~> 0.10.0'
26
26
  spec.add_dependency 'bundler', '~> 1.7'
27
27
  spec.add_dependency 'rake', '~> 10.0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sql_migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Grzegorz Bizon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-19 00:00:00.000000000 Z
11
+ date: 2015-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 3.2.0
19
+ version: '3.3'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 3.2.0
26
+ version: '3.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: sqlite3
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.31.0
61
+ version: '0.33'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.31.0
68
+ version: '0.33'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: simplecov
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -140,24 +140,21 @@ files:
140
140
  - lib/sql_migrations.rb
141
141
  - lib/sql_migrations/config.rb
142
142
  - lib/sql_migrations/database.rb
143
- - lib/sql_migrations/fixture.rb
143
+ - lib/sql_migrations/file.rb
144
144
  - lib/sql_migrations/migration.rb
145
- - lib/sql_migrations/script_file.rb
145
+ - lib/sql_migrations/script.rb
146
146
  - lib/sql_migrations/seed.rb
147
- - lib/sql_migrations/sql_script.rb
148
- - lib/sql_migrations/tasks/list.rake
149
147
  - lib/sql_migrations/tasks/migrate.rake
148
+ - lib/sql_migrations/tasks/scripts.rake
150
149
  - lib/sql_migrations/tasks/seed.rake
151
- - lib/sql_migrations/tasks/seed_test.rake
152
150
  - lib/sql_migrations/version.rb
153
151
  - spec/features/config_spec.rb
154
- - spec/features/fixture_spec.rb
152
+ - spec/features/history_table_spec.rb
155
153
  - spec/features/migration_multistatement_spec.rb
156
154
  - spec/features/migration_order_spec.rb
157
155
  - spec/features/migration_spec.rb
158
- - spec/features/schema_table_spec.rb
156
+ - spec/features/scripts_spec.rb
159
157
  - spec/features/seed_spec.rb
160
- - spec/features/sql_scripts_spec.rb
161
158
  - spec/spec_helper.rb
162
159
  - sql_migrations.gemspec
163
160
  homepage: http://github.com/grzesiek/sql_migrations
@@ -186,12 +183,11 @@ specification_version: 4
186
183
  summary: Simple standalone migrations you can use with plain SQL
187
184
  test_files:
188
185
  - spec/features/config_spec.rb
189
- - spec/features/fixture_spec.rb
186
+ - spec/features/history_table_spec.rb
190
187
  - spec/features/migration_multistatement_spec.rb
191
188
  - spec/features/migration_order_spec.rb
192
189
  - spec/features/migration_spec.rb
193
- - spec/features/schema_table_spec.rb
190
+ - spec/features/scripts_spec.rb
194
191
  - spec/features/seed_spec.rb
195
- - spec/features/sql_scripts_spec.rb
196
192
  - spec/spec_helper.rb
197
193
  has_rdoc:
@@ -1,13 +0,0 @@
1
- module SqlMigrations
2
- # Fixture class
3
- #
4
- class Fixture < SqlScript
5
- def self.find(database_name)
6
- super(database_name, :fixture)
7
- end
8
-
9
- def to_s
10
- "Fixture #{@name} for `#{@database_name}` database, datetime: #{@date + @time}"
11
- end
12
- end
13
- end
@@ -1,59 +0,0 @@
1
- module SqlMigrations
2
- # Class representing script file
3
- #
4
- class ScriptFile
5
- attr_reader :name, :time, :date, :database_name, :path
6
-
7
- def initialize(path, database_name, type)
8
- @filename,
9
- @base_directory,
10
- @parent_directory = path_elements(path)
11
- @path = path
12
- @type = type.to_s
13
- @database_name = database_name
14
- @date, @time, @name = match_regexp(@filename) if @filename
15
- end
16
-
17
- def content
18
- File.read(@path)
19
- end
20
-
21
- def valid?
22
- (@name && @time && @date && @database_name && matches_directories?) ? true : false
23
- end
24
-
25
- private
26
-
27
- def path_elements(path)
28
- _filename, _base_directory, _parent_directory =
29
- path.split(File::SEPARATOR).last(3).reverse
30
- end
31
-
32
- def match_regexp(filename)
33
- _, date, time, name =
34
- filename.match(/(\d{8})_(\d{6})_(.*)?\.sql/).to_a
35
- [date, time, name]
36
- end
37
-
38
- def matches_directories?
39
- if @database_name == :default
40
- file_in_type_base_directory? || file_in_database_directory?
41
- else
42
- file_in_database_directory?
43
- end
44
- end
45
-
46
- def file_in_type_base_directory?
47
- @base_directory == "#{@type}s"
48
- end
49
-
50
- def file_in_type_parent_directory?
51
- @parent_directory == "#{@type}s"
52
- end
53
-
54
- def file_in_database_directory?
55
- file_in_type_parent_directory? &&
56
- (@base_directory == @database_name.to_s)
57
- end
58
- end
59
- end
@@ -1,70 +0,0 @@
1
- module SqlMigrations
2
- # SqlScript class
3
- #
4
- class SqlScript < ScriptFile
5
- def initialize(path, database_name, type)
6
- super
7
- @datetime = (@date.to_s + @time.to_s).to_i
8
- end
9
-
10
- def execute(db)
11
- @database = db
12
- return false unless new?
13
- driver = @database.driver
14
- begin
15
- driver.transaction do
16
- @benchmark = Benchmark.measure do
17
- statements.each { |query| driver.run(query) }
18
- end
19
- end
20
- rescue
21
- puts "[-] Error while executing #{@type} #{@name} !"
22
- raise
23
- else
24
- true & on_success
25
- end
26
- end
27
-
28
- def statements
29
- separator = Config.options[:separator]
30
- if separator
31
- statements = content.split(separator)
32
- statements.collect!(&:strip)
33
- statements.reject(&:empty?)
34
- else
35
- [content]
36
- end
37
- end
38
-
39
- def self.find(database_name, type)
40
- scripts = []
41
- Find.find(Dir.pwd) do |path|
42
- candidate = new(path, database_name, type)
43
- scripts << candidate if candidate.valid?
44
- end
45
- scripts.sort_by { |file| (file.date + file.time).to_i }
46
- end
47
-
48
- private
49
-
50
- def new?
51
- schema = @database.schema_dataset
52
- last = schema.order(Sequel.asc(:time)).where(type: @type).last
53
- is_new = schema.where(time: @datetime, type: @type).count == 0
54
- if is_new && !last.nil?
55
- if last[:time] > @datetime
56
- raise "#{@type.capitalize} #{@name} has time BEFORE last one recorded !"
57
- end
58
- end
59
- is_new
60
- end
61
-
62
- def on_success
63
- puts "[+] Successfully executed #{@type}, name: #{@name}"
64
- puts " #{@type.capitalize} file: #{@date}_#{@time}_#{@name}.sql"
65
- puts " Benchmark: #{@benchmark}"
66
- schema = @database.schema_dataset
67
- schema.insert(time: @datetime, name: @name, type: @type, executed: DateTime.now)
68
- end
69
- end
70
- end
@@ -1,10 +0,0 @@
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.seed_test
7
- end
8
- end
9
- end
10
- end
@@ -1,22 +0,0 @@
1
- describe 'fixture' do
2
- before do
3
- Dir.mkdir('/migrations')
4
- File.open('/migrations/20150305_154010_test_migration.sql', 'w') do |f|
5
- f.puts 'CREATE TABLE test_table_fixture(col_int INTEGER, col_str STRING)'
6
- end
7
- Dir.mkdir('/fixtures')
8
- File.open('/fixtures/20150305_154012_test_fixture.sql', 'w') do |f|
9
- f.puts 'INSERT INTO test_table_fixture VALUES(12, "test_string_fixture")'
10
- end
11
- allow(SqlMigrations::Config).to receive(:databases) { { default: { development: {} } } }
12
- database = SqlMigrations::Database.new(:default, adapter: :sqlite)
13
- database.execute_migrations
14
- database.seed_with_fixtures
15
- end
16
-
17
- it 'should be properly executed' do
18
- expect(@sqlite_db.table_exists?(:test_table_fixture)).to be true
19
- expect(@sqlite_db[:test_table_fixture].first)
20
- .to eq(col_int: 12, col_str: 'test_string_fixture')
21
- end
22
- end
@@ -1,47 +0,0 @@
1
- # rubocop:disable Metrics/LineLength
2
- describe 'sql scripts' do
3
- before do
4
- Dir.mkdir('/migrations')
5
- Dir.mkdir('/fixtures')
6
- Dir.mkdir('/seeds')
7
-
8
- File.open('/migrations/20150305_154010_first_test_migration.sql', 'w') do |f|
9
- f.puts 'CREATE TABLE first_test_table(col_int1 INTEGER, col_str1 STRING)'
10
- end
11
- File.open('/migrations/20150305_154011_second_test_migration.sql', 'w') do |f|
12
- f.puts 'CREATE TABLE second_test_table(col_int2 INTEGER, col_str2 STRING)'
13
- end
14
- File.open('/seeds/20150305_154010_test_seed.sql', 'w') do |f|
15
- f.puts 'INSERT INTO first_test_table(col_int1, col_str1) VALUES(123, "test_string1")'
16
- f.puts 'INSERT INTO second_test_table(col_int2, col_str2) VALUES(456, "test_string2")'
17
- end
18
- File.open('/fixtures/20150518_154012_test_fixture.sql', 'w') do |f|
19
- f.puts 'INSERT INTO first_test2_table(col_int1, col_str1) VALUES(2123, "2test_string1")'
20
- f.puts 'INSERT INTO second_test2_table(col_int2, col_str2) VALUES(2456, "2test_string2")'
21
- end
22
-
23
- Dir.mkdir('/migrations/test2_db')
24
- File.open('/migrations/test2_db/20150511_144000_second_db_test_migration.sql', 'w') do |f|
25
- f.puts 'CREATE TABLE second_db_test_table(col_int1 INTEGER, col_str1 STRING)'
26
- end
27
-
28
- Dir.mkdir('/migrations/default')
29
- File.open('/migrations/default//20150511_144100_default_db_test2_migration.sql', 'w') do |f|
30
- f.puts 'CREATE TABLE default_db_test2_table(col_int1 INTEGER, col_str1 STRING)'
31
- end
32
-
33
- allow(SqlMigrations::Config).to receive(:databases) { { default: {}, test2_db: {} } }
34
- end
35
-
36
- it 'should be found' do
37
- expect { SqlMigrations.list_files }.to \
38
- output("Migration first_test_migration for `default` database, datetime: 20150305154010\n" \
39
- "Migration second_test_migration for `default` database, datetime: 20150305154011\n" \
40
- "Migration default_db_test2_migration for `default` database, datetime: 20150511144100\n" \
41
- "Seed data test_seed for `default` database, datetime: 20150305154010\n" \
42
- "Fixture test_fixture for `default` database, datetime: 20150518154012\n" \
43
- "Migration second_db_test_migration for `test2_db` database, datetime: 20150511144000\n"
44
- ).to_stdout
45
- end
46
- end
47
- # rubocop:enable Metrics/LineLength