sql_migrations 2.2.1 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
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