monark 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Changelog.md +5 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +9 -0
- data/README.md +87 -0
- data/Rakefile +15 -0
- data/lib/generators/monark_generator.rb +9 -0
- data/lib/generators/templates/Migrations.rb +42 -0
- data/lib/monark/context.rb +61 -0
- data/lib/monark/dsl.rb +35 -0
- data/lib/monark/migration.rb +46 -0
- data/lib/monark/railtie.rb +7 -0
- data/lib/monark/schema.rb +48 -0
- data/lib/monark/tasks.rake +35 -0
- data/lib/monark/version.rb +3 -0
- data/lib/monark.rb +33 -0
- data/monark.gemspec +25 -0
- data/test/fixtures/Migrations.rb +12 -0
- data/test/fixtures/bar.sql.erb +1 -0
- data/test/fixtures/baz.rb +1 -0
- data/test/fixtures/foo.sql +1 -0
- data/test/helper.rb +43 -0
- data/test/monark/context_test.rb +127 -0
- data/test/monark/dsl_test.rb +62 -0
- data/test/monark/migration_test.rb +34 -0
- data/test/monark/schema_test.rb +26 -0
- data/test/monark_test.rb +49 -0
- metadata +164 -0
data/.gitignore
ADDED
data/Changelog.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Copyright (c) 2013 Norman Clarke and Flexminder Inc
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# Monark
|
2
|
+
|
3
|
+
Monark is a database migrator and seeder, intended to supplement Active
|
4
|
+
Record's migrations and replace Rails's seed tasks.
|
5
|
+
|
6
|
+
It keeps track of which SQL or Ruby code has been run on your database so that
|
7
|
+
you can change make incremental changes to your production database without
|
8
|
+
having to run ad-hoc deployment scripts.
|
9
|
+
|
10
|
+
Why "Monark?" I wanted to call it "Monarch" but the name was taken and I
|
11
|
+
couldn't think of anything better.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
gem 'monark'
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install monark
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Read [this file](https://github.com/flexminder/monark/blob/master/lib/generators/templates/Migrations.rb) for information on how to set up Monark.
|
30
|
+
|
31
|
+
### Setup
|
32
|
+
|
33
|
+
rails generate monark
|
34
|
+
edit db/monark/Migrations.rb
|
35
|
+
add migrations to db/monark
|
36
|
+
|
37
|
+
### Tasks
|
38
|
+
|
39
|
+
# rake monark:assume_done # Assume all migrations have been performed
|
40
|
+
# rake monark:migrate # Perform pending migrations
|
41
|
+
# rake monark:reload # Perform reloadable and pending migrations
|
42
|
+
# rake monark:reset # Perform all migrations again
|
43
|
+
|
44
|
+
|
45
|
+
If you're porting your seeds or other existing database management code to
|
46
|
+
Monark, you'll probably want add your existing seeds, deploy and peform `rake monark:assume_done` to make sure you don't overwrite anything in production.
|
47
|
+
|
48
|
+
Then you can safely add more migrations and deploy them with
|
49
|
+
`rake monark:migrate`.
|
50
|
+
|
51
|
+
## Deficiencies
|
52
|
+
|
53
|
+
It only works with Active Record for now, since this is what we currently need
|
54
|
+
at FlexMinder. Support for other database libraries would be nice to have but
|
55
|
+
we probably won't add that until we need it ourselves.
|
56
|
+
|
57
|
+
## Contributing
|
58
|
+
|
59
|
+
1. Fork it
|
60
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
61
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
62
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
63
|
+
5. Create new Pull Request
|
64
|
+
|
65
|
+
## License
|
66
|
+
|
67
|
+
Copyright (c) 2013 Norman Clarke and Flexminder Inc.
|
68
|
+
|
69
|
+
MIT License
|
70
|
+
|
71
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
72
|
+
this software and associated documentation files (the "Software"), to deal in
|
73
|
+
the Software without restriction, including without limitation the rights to
|
74
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
75
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
76
|
+
so, subject to the following conditions:
|
77
|
+
|
78
|
+
The above copyright notice and this permission notice shall be included in all
|
79
|
+
copies or substantial portions of the Software.
|
80
|
+
|
81
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
82
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
83
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
84
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
85
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
86
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
87
|
+
SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "rake/testtask"
|
2
|
+
require "rake/clean"
|
3
|
+
|
4
|
+
CLEAN.replace Rake::FileList['**/*.gem', 'doc', 'coverage']
|
5
|
+
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.test_files = FileList['test/**/*_test.rb']
|
9
|
+
end
|
10
|
+
|
11
|
+
task :default => :test
|
12
|
+
|
13
|
+
task :gem do
|
14
|
+
sh 'gem build monark.gemspec'
|
15
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Migrations.rb
|
2
|
+
#
|
3
|
+
# This file was generated by Monark and is used to manage seeds and database
|
4
|
+
# changes that Active Record Migrations don't handle very well.
|
5
|
+
#
|
6
|
+
# You can manage Monark with the following rake tasks:
|
7
|
+
#
|
8
|
+
# rake monark:assume_done # Assume all migrations have been performed
|
9
|
+
# rake monark:migrate # Perform pending migrations
|
10
|
+
# rake monark:reload # Perform reloadable and pending migrations
|
11
|
+
# rake monark:reset # Perform all migrations again
|
12
|
+
#
|
13
|
+
# In your Migrations.rb file itself, you can break your modifications up into
|
14
|
+
# groups used in different environments. This lets you load specific development
|
15
|
+
# or test fixtures that share some data with production (i.e., a list of
|
16
|
+
# countries for a form select), but also have some fake data you want to keep
|
17
|
+
# out of production (i.e., fake application users).
|
18
|
+
groups :development, :production do
|
19
|
+
|
20
|
+
# The `add` method applies code that is only loaded once, ever, unless you're
|
21
|
+
# resetting your database. This is good for seed data or for altering your
|
22
|
+
# schema.
|
23
|
+
#
|
24
|
+
# Here, `foo` should be a file in db/monark. Various file types are supported,
|
25
|
+
# plain .sql files, .sql.erb files which are passed through Erb before
|
26
|
+
# loading, and plain .rb files.
|
27
|
+
add 'foo'
|
28
|
+
|
29
|
+
# The `load` method applies code that can be reloaded. This is good for stored
|
30
|
+
# procedures, which are application code that may be changed. You need to take
|
31
|
+
# care that any file you pass as an argument here is safe to reload; for
|
32
|
+
# example, any CREATE FUNCTION should probably have a corresponding DROP
|
33
|
+
# FUNCTION.
|
34
|
+
load 'bar'
|
35
|
+
end
|
36
|
+
|
37
|
+
# You can invoke a block of Ruby code with `add` or `load`. You can pass a
|
38
|
+
# group or groups as a hash option, or organize them in blocks like in the
|
39
|
+
# example above.
|
40
|
+
add 'say hello', :group => :test do
|
41
|
+
puts 'hello world!'
|
42
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Monark
|
2
|
+
|
3
|
+
# Monark runtime. Encapsulates state and manages migrations.
|
4
|
+
class Context
|
5
|
+
attr_accessor :root, :env, :migrations_file_name, :connection
|
6
|
+
attr_reader :migrations, :version
|
7
|
+
attr_writer :manifest
|
8
|
+
|
9
|
+
def initialize(root, migrations_file_name = Monark::DEFAULT_MIGRATIONS_FILE_NAME)
|
10
|
+
self.root = Pathname.new(root)
|
11
|
+
self.migrations_file_name = migrations_file_name
|
12
|
+
@migrations = []
|
13
|
+
@env = Monark.env
|
14
|
+
end
|
15
|
+
|
16
|
+
def manifest
|
17
|
+
@manifest ||= root.join(migrations_file_name).read
|
18
|
+
end
|
19
|
+
|
20
|
+
def version=(number)
|
21
|
+
@version = Integer(number)
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_manifest
|
25
|
+
@loaded_manifest ||= begin
|
26
|
+
DSL.new(self).instance_eval(manifest, root.join(migrations_file_name).to_s, 0)
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def push(*args)
|
32
|
+
migrations.push(*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def done
|
36
|
+
load_manifest
|
37
|
+
count = migrations.count
|
38
|
+
schema = Schema.new
|
39
|
+
schema.next_migration = count if count > schema.next_migration
|
40
|
+
end
|
41
|
+
|
42
|
+
def assume_done
|
43
|
+
load_manifest
|
44
|
+
schema = Schema.new
|
45
|
+
schema.next_migration = migrations.count
|
46
|
+
end
|
47
|
+
|
48
|
+
def migrate(options = {})
|
49
|
+
load_manifest
|
50
|
+
schema = Schema.new
|
51
|
+
ActiveRecord::Base.transaction do
|
52
|
+
migrations.each_with_index do |migration, index|
|
53
|
+
if (index >= schema.next_migration) || (migration.reloadable? && options[:reload])
|
54
|
+
migration.perform
|
55
|
+
end
|
56
|
+
end
|
57
|
+
done
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/monark/dsl.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Monark
|
2
|
+
|
3
|
+
# The Monark DSL.
|
4
|
+
class DSL
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :_context
|
8
|
+
|
9
|
+
def initialize(context = nil)
|
10
|
+
@_context = context
|
11
|
+
end
|
12
|
+
|
13
|
+
def groups(*args, &block)
|
14
|
+
groups = args.flatten.compact
|
15
|
+
yield if groups.empty? || groups.include?(_context.env.to_sym)
|
16
|
+
end
|
17
|
+
alias group groups
|
18
|
+
|
19
|
+
def set_version(arg)
|
20
|
+
_context.version = arg
|
21
|
+
end
|
22
|
+
|
23
|
+
def add(name, options = {}, &block)
|
24
|
+
groups(options.fetch(:group, options[:groups])) do
|
25
|
+
_context.push Migration.new(_context.root, name, block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def load(name, options = {}, &block)
|
30
|
+
add(name, options, &block)
|
31
|
+
_context.migrations.last.reloadable = true
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Monark
|
2
|
+
|
3
|
+
# A Monark migration. Acceptable formats are `.sql`, `.sql.erb` and `.rb`.
|
4
|
+
class Migration
|
5
|
+
|
6
|
+
attr_accessor :root, :path, :kind, :reloadable
|
7
|
+
alias reloadable? reloadable
|
8
|
+
|
9
|
+
def initialize(root, path, proc = nil)
|
10
|
+
self.root = Pathname(root.to_s)
|
11
|
+
self.path = path
|
12
|
+
@code = proc
|
13
|
+
end
|
14
|
+
|
15
|
+
def code
|
16
|
+
@code ||= load_file("sql", "sql.erb", "rb")
|
17
|
+
end
|
18
|
+
|
19
|
+
def perform
|
20
|
+
code
|
21
|
+
if !kind
|
22
|
+
puts "-> calling '#{@path}'"
|
23
|
+
@code.call
|
24
|
+
elsif @kind == "rb"
|
25
|
+
puts "-> evaluating '#{@path}'"
|
26
|
+
binding.eval(@code, path, 0)
|
27
|
+
else
|
28
|
+
puts "-> executing '#{@path}'"
|
29
|
+
ActiveRecord::Base.connection.execute @code
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def load_file(*extensions)
|
36
|
+
extensions.each do |ext|
|
37
|
+
file = root.join("#{path}.#{ext}")
|
38
|
+
if file.readable?
|
39
|
+
data = file.read
|
40
|
+
@kind = ext.gsub(/\.erb\z/, '')
|
41
|
+
return ext =~ /\.erb\z/ ? ERB.new(data).result(binding) : data
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Monark
|
2
|
+
|
3
|
+
# The Monark schema. Creates and updates the schema in an SQL database.
|
4
|
+
class Schema
|
5
|
+
|
6
|
+
attr_accessor :context
|
7
|
+
attr_reader :version, :next_migration
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
load_info
|
11
|
+
end
|
12
|
+
|
13
|
+
def next_migration=(value)
|
14
|
+
value = Integer(value)
|
15
|
+
conn.execute "UPDATE monark_info SET next_migration = #{value}"
|
16
|
+
@next_migration = value
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def create_table_if_not_exists
|
22
|
+
conn.execute 'CREATE TABLE IF NOT EXISTS monark_info(version integer NOT NULL, next_migration integer NOT NULL)'
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_first_row
|
26
|
+
result = conn.select_all 'SELECT version, next_migration FROM monark_info LIMIT 1'
|
27
|
+
if result.empty?
|
28
|
+
@version = 1
|
29
|
+
@next_migration = 0
|
30
|
+
conn.execute "INSERT INTO monark_info (version, next_migration) VALUES (#{@version}, #{@next_migration})"
|
31
|
+
else
|
32
|
+
@version = Integer(result[0]['version'])
|
33
|
+
@next_migration = Integer(result[0]['next_migration'])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_info
|
38
|
+
conn.transaction do
|
39
|
+
create_table_if_not_exists
|
40
|
+
get_first_row
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def conn
|
45
|
+
ActiveRecord::Base.connection
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
namespace :monark do
|
2
|
+
|
3
|
+
def monark_root
|
4
|
+
@monark_root ||= Rails.root.join("db/monark")
|
5
|
+
end
|
6
|
+
|
7
|
+
desc 'Perform pending migrations'
|
8
|
+
task :migrate => :environment do
|
9
|
+
context = Monark::Context.new monark_root
|
10
|
+
context.migrate
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'Perform reloadable and pending migrations'
|
14
|
+
task :reload => :environment do
|
15
|
+
context = Monark::Context.new monark_root
|
16
|
+
context.migrate(:reload => true)
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'Perform all migrations again'
|
20
|
+
task :reset => :environment do
|
21
|
+
ActiveRecord::Base.connection.transaction do
|
22
|
+
schema = Monark::Schema.new
|
23
|
+
schema.next_migration = 0
|
24
|
+
Rake::Task['monark:migrate'].invoke
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'Assume all migrations have been performed'
|
29
|
+
task :assume_done => :environment do
|
30
|
+
context = Monark::Context.new monark_root
|
31
|
+
context.assume_done
|
32
|
+
schema = Monark::Schema.new
|
33
|
+
puts "Next migration set to #{schema.next_migration}"
|
34
|
+
end
|
35
|
+
end
|
data/lib/monark.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
require 'active_record'
|
5
|
+
|
6
|
+
require 'monark/context'
|
7
|
+
require 'monark/schema'
|
8
|
+
require 'monark/dsl'
|
9
|
+
require 'monark/migration'
|
10
|
+
require 'monark/version'
|
11
|
+
|
12
|
+
if defined?(Rails)
|
13
|
+
require 'monark/railtie'
|
14
|
+
end
|
15
|
+
|
16
|
+
# Monark is a simple migrator.
|
17
|
+
module Monark
|
18
|
+
|
19
|
+
DEFAULT_MIGRATIONS_FILE_NAME = 'Migrations.rb'
|
20
|
+
DEFAULT_ENV = 'development'
|
21
|
+
|
22
|
+
def self.env
|
23
|
+
if ENV.key?('MONARK_ENV')
|
24
|
+
ENV['MONARK_ENV']
|
25
|
+
elsif defined?(::Rails)
|
26
|
+
::Rails.env.to_s
|
27
|
+
elsif ENV.key?('RACK_ENV')
|
28
|
+
ENV['RACK_ENV']
|
29
|
+
else
|
30
|
+
DEFAULT_ENV.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/monark.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'monark/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'monark'
|
8
|
+
spec.version = Monark::VERSION
|
9
|
+
spec.authors = ['Norman Clarke']
|
10
|
+
spec.email = ['norman@njclarke.com']
|
11
|
+
spec.description = 'A simple migrator/seeder'
|
12
|
+
spec.summary = 'A simple migrator/seeder'
|
13
|
+
spec.homepage = 'https://github.com/flexminder/monark'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.test_files = spec.files.grep(%r{^test/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency 'activerecord'
|
21
|
+
spec.add_development_dependency 'sqlite3'
|
22
|
+
spec.add_development_dependency 'bundler', "~> 1.3"
|
23
|
+
spec.add_development_dependency 'rake'
|
24
|
+
spec.add_development_dependency 'minitest'
|
25
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
INSERT INTO <%= Thread.current[:table] %> VALUES (1);
|
@@ -0,0 +1 @@
|
|
1
|
+
Thread.current[:hello] = 'world'
|
@@ -0,0 +1 @@
|
|
1
|
+
CREATE TABLE foo (a_col INTEGER);
|
data/test/helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'set'
|
5
|
+
require 'active_record'
|
6
|
+
|
7
|
+
$VERBOSE = true
|
8
|
+
|
9
|
+
require 'monark'
|
10
|
+
|
11
|
+
class Module
|
12
|
+
def test(name, &block)
|
13
|
+
define_method "test #{name}", block
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Monark::Migration
|
18
|
+
def puts(*args); end
|
19
|
+
end
|
20
|
+
|
21
|
+
connection_params = {
|
22
|
+
:adapter => 'sqlite3',
|
23
|
+
:database => ':memory:',
|
24
|
+
:encoding => 'utf8'
|
25
|
+
}
|
26
|
+
|
27
|
+
ActiveRecord::Base.establish_connection connection_params
|
28
|
+
|
29
|
+
module Monark
|
30
|
+
module TestHelp
|
31
|
+
def fixtures_path
|
32
|
+
Pathname(File.expand_path("../fixtures", __FILE__))
|
33
|
+
end
|
34
|
+
|
35
|
+
def context
|
36
|
+
@context ||= Context.new('/tmp')
|
37
|
+
end
|
38
|
+
|
39
|
+
def transaction
|
40
|
+
ActiveRecord::Base.transaction { yield ; raise ActiveRecord::Rollback }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Monark
|
4
|
+
class ContextTest < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
include TestHelp
|
7
|
+
|
8
|
+
def teardown
|
9
|
+
# TODO: Add as method to Schema class
|
10
|
+
ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS monark_info"
|
11
|
+
Thread.current[:counter] = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
test 'should perform migrations' do
|
15
|
+
Thread.current[:table] = 'foo'
|
16
|
+
transaction do
|
17
|
+
context = Context.new(fixtures_path)
|
18
|
+
context.migrate
|
19
|
+
schema = Schema.new
|
20
|
+
assert_equal 3, schema.next_migration
|
21
|
+
assert_equal 1, schema.version
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
test '#done should set the next migration past the current ones' do
|
26
|
+
transaction do
|
27
|
+
context = Context.new(fixtures_path)
|
28
|
+
context.done
|
29
|
+
assert_equal 3, Schema.new.next_migration
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
test '#migrate should skip completed migrations' do
|
34
|
+
transaction do
|
35
|
+
schema = Schema.new
|
36
|
+
schema.next_migration = 1
|
37
|
+
context = Context.new(fixtures_path)
|
38
|
+
Thread.current[:counter] = 0
|
39
|
+
context.manifest = <<-END
|
40
|
+
add 'foo' do
|
41
|
+
Thread.current[:counter] = Thread.current[:counter].next
|
42
|
+
end
|
43
|
+
add 'bar' do
|
44
|
+
Thread.current[:counter] = Thread.current[:counter].next
|
45
|
+
end
|
46
|
+
add 'baz' do
|
47
|
+
Thread.current[:counter] = Thread.current[:counter].next
|
48
|
+
end
|
49
|
+
END
|
50
|
+
context.migrate
|
51
|
+
assert_equal 2, Thread.current[:counter]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
test '#migrate should optionally reload migrations added with load' do
|
56
|
+
transaction do
|
57
|
+
schema = Schema.new
|
58
|
+
schema.next_migration = 100
|
59
|
+
context = Context.new(fixtures_path)
|
60
|
+
Thread.current[:counter] = 0
|
61
|
+
context.manifest = <<-END
|
62
|
+
load 'foo' do
|
63
|
+
Thread.current[:counter] = Thread.current[:counter].next
|
64
|
+
end
|
65
|
+
END
|
66
|
+
context.migrate
|
67
|
+
assert_equal 0, Thread.current[:counter]
|
68
|
+
assert_equal 100, schema.next_migration
|
69
|
+
context.migrate(:reload => true)
|
70
|
+
assert_equal 100, schema.next_migration
|
71
|
+
assert_equal 1, Thread.current[:counter]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
test 'should have a default migrations file name' do
|
76
|
+
assert_equal 'Migrations.rb', DEFAULT_MIGRATIONS_FILE_NAME
|
77
|
+
end
|
78
|
+
|
79
|
+
test 'constructor should set a root Pathname' do
|
80
|
+
assert_equal Pathname, context.root.class
|
81
|
+
assert_equal '/tmp', context.root.to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
test 'constructor should set a migrations file name' do
|
85
|
+
context = Context.new('/tmp', 'foo.rb')
|
86
|
+
assert_equal 'foo.rb', context.migrations_file_name
|
87
|
+
end
|
88
|
+
|
89
|
+
test 'constructor should set default migrations file name' do
|
90
|
+
assert_equal DEFAULT_MIGRATIONS_FILE_NAME, context.migrations_file_name
|
91
|
+
end
|
92
|
+
|
93
|
+
test 'should load the migrations file as the manifest' do
|
94
|
+
context = Context.new(fixtures_path)
|
95
|
+
data = File.read(fixtures_path.join('Migrations.rb'))
|
96
|
+
assert_equal data, context.manifest
|
97
|
+
end
|
98
|
+
|
99
|
+
test 'should push to the migrations array' do
|
100
|
+
context.push :foo
|
101
|
+
assert_equal :foo, context.migrations.first
|
102
|
+
end
|
103
|
+
|
104
|
+
test 'should set version from number or numeric string' do
|
105
|
+
context.version = 1
|
106
|
+
assert_equal 1, context.version
|
107
|
+
context.version = '2'
|
108
|
+
assert_equal 2, context.version
|
109
|
+
end
|
110
|
+
|
111
|
+
test 'should raise error if the version can not be cast to a number' do
|
112
|
+
begin
|
113
|
+
context.version = 'foo'
|
114
|
+
flunk 'should have raised an error'
|
115
|
+
rescue ArgumentError
|
116
|
+
assert true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
test 'should load manifest' do
|
121
|
+
context = Context.new(fixtures_path)
|
122
|
+
context.load_manifest
|
123
|
+
assert_equal 42, context.version
|
124
|
+
assert_equal 3, context.migrations.count
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Monark
|
4
|
+
class DSLTest < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
test 'set_version should set the version on the context' do
|
7
|
+
dsl.set_version 42
|
8
|
+
assert_equal 42, context.version
|
9
|
+
end
|
10
|
+
|
11
|
+
test 'add should instantiate a migration and add it to the context' do
|
12
|
+
dsl.add "foo"
|
13
|
+
assert_equal Migration, context.migrations.first.class
|
14
|
+
end
|
15
|
+
|
16
|
+
test 'add should not add a migration if the group differs from the env' do
|
17
|
+
dsl.add "foo", :group => :production
|
18
|
+
assert_equal [], context.migrations
|
19
|
+
end
|
20
|
+
|
21
|
+
test 'add should add a migration if the groups is the env' do
|
22
|
+
dsl.add "foo", :group => :development
|
23
|
+
assert_equal Migration, context.migrations.first.class
|
24
|
+
end
|
25
|
+
|
26
|
+
test 'add should not add a migration if the groups do not include the env' do
|
27
|
+
dsl.add "foo", :groups => [:production, :whatever]
|
28
|
+
assert_equal [], context.migrations
|
29
|
+
end
|
30
|
+
|
31
|
+
test 'add should add a migration if the groups include the env' do
|
32
|
+
dsl.add "foo", :groups => [:production, :development]
|
33
|
+
assert_equal Migration, context.migrations.first.class
|
34
|
+
end
|
35
|
+
|
36
|
+
test 'group should yield if the group is the env' do
|
37
|
+
dsl.group(:development) {assert true}
|
38
|
+
end
|
39
|
+
|
40
|
+
test 'group should yield if the group differs from the env' do
|
41
|
+
dsl.group(:production) {flunk}
|
42
|
+
end
|
43
|
+
|
44
|
+
test 'groups should not yield the groups do not include the env' do
|
45
|
+
dsl.groups(:production, :test) {flunk}
|
46
|
+
end
|
47
|
+
|
48
|
+
test 'groups should yield the groups include the env' do
|
49
|
+
dsl.groups(:production, :development) {assert true}
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def dsl
|
55
|
+
@dsl ||= DSL.new(context)
|
56
|
+
end
|
57
|
+
|
58
|
+
def context
|
59
|
+
@context ||= Context.new(File.expand_path('../fixtures', __FILE__))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Monark
|
4
|
+
class MigrationTest < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
include TestHelp
|
7
|
+
|
8
|
+
test 'should load SQL' do
|
9
|
+
migration = Migration.new(fixtures_path, "foo")
|
10
|
+
assert_equal "CREATE TABLE foo (a_col INTEGER);", migration.code
|
11
|
+
end
|
12
|
+
|
13
|
+
test 'should load SQL + Erb' do
|
14
|
+
begin
|
15
|
+
Thread.current[:table] = "foo"
|
16
|
+
migration = Migration.new(fixtures_path, "bar")
|
17
|
+
assert_equal "INSERT INTO foo VALUES (1);", migration.code
|
18
|
+
ensure
|
19
|
+
Thread.current[:table] = nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
test 'should load Ruby' do
|
24
|
+
migration = Migration.new(fixtures_path, "baz")
|
25
|
+
assert_equal %q{Thread.current[:hello] = 'world'}, migration.code
|
26
|
+
end
|
27
|
+
|
28
|
+
test 'should load a proc' do
|
29
|
+
migration = Migration.new(fixtures_path, "say hello", -> {puts 'hello world'})
|
30
|
+
assert_kind_of Proc, migration.code
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Monark
|
4
|
+
class SchemaTest < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
include TestHelp
|
7
|
+
|
8
|
+
test 'should create table and record' do
|
9
|
+
transaction do
|
10
|
+
schema = Schema.new
|
11
|
+
assert_equal 1, schema.version
|
12
|
+
assert_equal 0, schema.next_migration
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
test 'should load schema info from existing records' do
|
17
|
+
transaction do
|
18
|
+
schema = Schema.new
|
19
|
+
schema.version
|
20
|
+
schema = Schema.new
|
21
|
+
assert_equal 1, schema.version
|
22
|
+
assert_equal 0, schema.next_migration
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/test/monark_test.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Monark
|
4
|
+
|
5
|
+
def teardown
|
6
|
+
Object.send(:remove_const, :Rails) rescue NameError
|
7
|
+
ENV.delete 'RACK_ENV'
|
8
|
+
ENV.delete 'MONARK_ENV'
|
9
|
+
end
|
10
|
+
|
11
|
+
test 'should have a version' do
|
12
|
+
assert_not_nil Monark::VERSION
|
13
|
+
end
|
14
|
+
|
15
|
+
test "should load env from ENV['MONARK_ENV'] if present" do
|
16
|
+
ENV['MONARK_ENV'] = 'production'
|
17
|
+
assert_equal 'production', Monark.env
|
18
|
+
end
|
19
|
+
|
20
|
+
test 'should load env from Rails.env if present' do
|
21
|
+
::Rails = OpenStruct.new(:env => 'production')
|
22
|
+
assert_equal 'production', Monark.env
|
23
|
+
end
|
24
|
+
|
25
|
+
test "should load env from ENV['RACK_ENV'] if present" do
|
26
|
+
ENV['RACK_ENV'] = 'production'
|
27
|
+
assert_equal 'production', Monark.env
|
28
|
+
end
|
29
|
+
|
30
|
+
test 'env should default to development' do
|
31
|
+
assert_equal 'development', Monark.env
|
32
|
+
end
|
33
|
+
|
34
|
+
test 'should favor MONARK_ENV over Rails env' do
|
35
|
+
::Rails = OpenStruct.new(:env => "test")
|
36
|
+
ENV['MONARK_ENV'] = 'production'
|
37
|
+
assert_equal 'production', Monark.env
|
38
|
+
ENV.delete 'MONARK_ENV'
|
39
|
+
assert_equal 'test', Monark.env
|
40
|
+
end
|
41
|
+
|
42
|
+
test 'should favor Rails env over RACK_ENV' do
|
43
|
+
::Rails = OpenStruct.new(:env => "test")
|
44
|
+
ENV['RACK_ENV'] = 'production'
|
45
|
+
assert_equal 'test', Monark.env
|
46
|
+
Object.send :remove_const, :Rails
|
47
|
+
assert_equal 'production', Monark.env
|
48
|
+
end
|
49
|
+
end
|
metadata
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: monark
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Norman Clarke
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
none: false
|
21
|
+
name: activerecord
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
requirement: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ! '>='
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '0'
|
29
|
+
none: false
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
version_requirements: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ! '>='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
none: false
|
37
|
+
name: sqlite3
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
requirement: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
none: false
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
version_requirements: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ~>
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '1.3'
|
52
|
+
none: false
|
53
|
+
name: bundler
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.3'
|
61
|
+
none: false
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
none: false
|
69
|
+
name: rake
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
none: false
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
none: false
|
85
|
+
name: minitest
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
requirement: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ! '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
none: false
|
94
|
+
description: A simple migrator/seeder
|
95
|
+
email:
|
96
|
+
- norman@njclarke.com
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- .gitignore
|
102
|
+
- Changelog.md
|
103
|
+
- Gemfile
|
104
|
+
- LICENSE.txt
|
105
|
+
- README.md
|
106
|
+
- Rakefile
|
107
|
+
- lib/generators/monark_generator.rb
|
108
|
+
- lib/generators/templates/Migrations.rb
|
109
|
+
- lib/monark.rb
|
110
|
+
- lib/monark/context.rb
|
111
|
+
- lib/monark/dsl.rb
|
112
|
+
- lib/monark/migration.rb
|
113
|
+
- lib/monark/railtie.rb
|
114
|
+
- lib/monark/schema.rb
|
115
|
+
- lib/monark/tasks.rake
|
116
|
+
- lib/monark/version.rb
|
117
|
+
- monark.gemspec
|
118
|
+
- test/fixtures/Migrations.rb
|
119
|
+
- test/fixtures/bar.sql.erb
|
120
|
+
- test/fixtures/baz.rb
|
121
|
+
- test/fixtures/foo.sql
|
122
|
+
- test/helper.rb
|
123
|
+
- test/monark/context_test.rb
|
124
|
+
- test/monark/dsl_test.rb
|
125
|
+
- test/monark/migration_test.rb
|
126
|
+
- test/monark/schema_test.rb
|
127
|
+
- test/monark_test.rb
|
128
|
+
homepage: https://github.com/flexminder/monark
|
129
|
+
licenses:
|
130
|
+
- MIT
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ! '>='
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
none: false
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ! '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
none: false
|
147
|
+
requirements: []
|
148
|
+
rubyforge_project:
|
149
|
+
rubygems_version: 1.8.23
|
150
|
+
signing_key:
|
151
|
+
specification_version: 3
|
152
|
+
summary: A simple migrator/seeder
|
153
|
+
test_files:
|
154
|
+
- test/fixtures/Migrations.rb
|
155
|
+
- test/fixtures/bar.sql.erb
|
156
|
+
- test/fixtures/baz.rb
|
157
|
+
- test/fixtures/foo.sql
|
158
|
+
- test/helper.rb
|
159
|
+
- test/monark/context_test.rb
|
160
|
+
- test/monark/dsl_test.rb
|
161
|
+
- test/monark/migration_test.rb
|
162
|
+
- test/monark/schema_test.rb
|
163
|
+
- test/monark_test.rb
|
164
|
+
has_rdoc:
|