simple_migrator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2efb61e7bb9748832cfeee309365c8ee003ef756
4
+ data.tar.gz: f34e0c895d10ccebef93ed296755013edeab2b7a
5
+ SHA512:
6
+ metadata.gz: 12e88647bced81100e2cc47113228f38ee60330b7255d92b3e8628af3cf61242cde89fcf3c7d3d8be829e563241e8f89ffc94af52572f3c05235fc31ef0a924c
7
+ data.tar.gz: 7bac0c4b19cc76546549ab740844cccc6ac755fb7d8d79bb7fc74b3f4b8bda4ec15abcbef17660e0f605fb2d93476f31b4af1d97924bed61b379d9dd33d06a93
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ .idea
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in simple_migrator.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Jesper Josefsson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,62 @@
1
+ # SimpleMigrator
2
+
3
+ `SimpleMigrator` serves a simple purpose:
4
+
5
+ - Use the [Sequel gem](https://github.com/jeremyevans/sequel) to access database
6
+ - Keep track of database schema migrations
7
+ - Do so without having to put migration files in a specific folders
8
+ - Run migrations in order, sorted by migration name
9
+
10
+ It has some important caveats:
11
+
12
+ - The programmer is responsible for choosing a unique name for migrations
13
+ - There's no way of controlling order other than using migration names
14
+
15
+ `SimpleMigrator` contains a module `Migratable` which can be used to define migrations using a simple DSL.
16
+
17
+ ## Usage
18
+
19
+ ### Just migrate stuff
20
+
21
+ db = Sequel.sqlite
22
+ migrator = SimpleMigrator.migrator(db)
23
+ migrator.migrate("my_migration") do |connection|
24
+ # do stuff
25
+ end
26
+ # Your stuff has been done
27
+ migrator.migrate("my_migration") do
28
+ # This time the migrator won't execute the block
29
+ end
30
+
31
+ ### Using Migratable
32
+
33
+ class Foo
34
+ include SimpleMigrator::Migratable
35
+
36
+ migration "20150303-1" do |db|
37
+ create_my_table(db)
38
+ create_my_other_table(db)
39
+ end
40
+
41
+ migration "20150303-2" do |db|
42
+ add_column(db)
43
+ end
44
+
45
+ def create_my_table(db)
46
+ end
47
+
48
+ def create_my_other_table(db)
49
+ end
50
+
51
+ def add_column(db)
52
+ end
53
+ end
54
+
55
+ db = Sequel.sqlite
56
+
57
+ migrator = SimpleMigrator.migrator(db)
58
+
59
+ foo = Foo.new
60
+
61
+ foo.migrate(migrator)
62
+
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,14 @@
1
+ require "sequel"
2
+
3
+ require "simple_migrator/version"
4
+ require "simple_migrator/migration"
5
+ require "simple_migrator/migrator"
6
+ require "simple_migrator/migratable"
7
+
8
+ module SimpleMigrator
9
+ class << self
10
+ def migrator(*args, &block)
11
+ Migrator.new(*args, &block)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,45 @@
1
+ module SimpleMigrator
2
+ module Migratable
3
+ def migrate!(migrator = migrator)
4
+ self.class.migrations.each do |migration|
5
+ rebound_proc = rebind_proc(migration.proc)
6
+ name = prefixed_migration_name(migration.name)
7
+ migrator.migrate(name, &rebound_proc)
8
+ end
9
+ end
10
+
11
+ def prefixed_migration_name(name)
12
+ if migration_prefix
13
+ [migration_prefix, name].join("_")
14
+ else
15
+ name
16
+ end
17
+ end
18
+
19
+ def migration_prefix; end
20
+
21
+ def migrator
22
+ raise NoMigratorError, "`migrate!` called without a migrator.\nEither provide a migrator or define a `migrator` method"
23
+ end
24
+
25
+ def rebind_proc(proc)5
26
+ Proc.new do |db|
27
+ instance_exec(db, &proc)
28
+ end
29
+ end
30
+
31
+ def self.included(base)
32
+ base.extend(ClassMethods)
33
+ end
34
+
35
+ module ClassMethods
36
+ def migration(name, &block)
37
+ migrations.add(Migration.new(name, block))
38
+ end
39
+
40
+ def migrations
41
+ @migrations ||= SortedSet.new
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,14 @@
1
+ module SimpleMigrator
2
+ class Migration
3
+ attr_reader :proc, :name
4
+
5
+ def initialize(name, proc)
6
+ @name = name
7
+ @proc = proc
8
+ end
9
+
10
+ def <=>(other)
11
+ self.name <=> other.name
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,54 @@
1
+ module SimpleMigrator
2
+ class Migrator
3
+ def initialize(connection)
4
+ @connection = connection
5
+ ensure_migration_table!
6
+ end
7
+
8
+ def migrate(name, &block)
9
+ connection.transaction do
10
+ unless migrated?(name)
11
+ execute(block)
12
+ was_migrated(name)
13
+ end
14
+ end
15
+ end
16
+
17
+ def migrated? migration_name
18
+ migrations_table.where(migration_name: migration_name).any?
19
+ end
20
+
21
+ def migrations_table
22
+ connection[table_name]
23
+ end
24
+
25
+ def self.table_name
26
+ :schema_migrations
27
+ end
28
+
29
+ def table_name
30
+ self.class.table_name
31
+ end
32
+
33
+ private
34
+
35
+ def execute(migration)
36
+ migration.call(connection)
37
+ end
38
+
39
+ attr_reader :connection
40
+
41
+ def ensure_migration_table!
42
+ connection.create_table?(table_name) do
43
+ primary_key :id
44
+ String :migration_name, null: false
45
+ index [:migration_name], unique: true
46
+ DateTime :timestamp, null: false
47
+ end
48
+ end
49
+
50
+ def was_migrated(migration_name)
51
+ migrations_table.insert(timestamp: Time.now.utc, migration_name: migration_name)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,3 @@
1
+ module SimpleMigrator
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'simple_migrator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "simple_migrator"
8
+ spec.version = SimpleMigrator::VERSION
9
+ spec.authors = ["Jesper Josefsson"]
10
+ spec.email = ["jesper.josefsson@gmail.com"]
11
+ spec.summary = %q{SimpleMigrator provides basic programmatic migrations for Sequel}
12
+ spec.license = "MIT"
13
+
14
+ spec.files = `git ls-files -z`.split("\x0")
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_runtime_dependency "sequel"
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "sqlite3"
25
+ spec.add_development_dependency "simplecov"
26
+ end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ module SimpleMigrator
4
+ class FakeMigrator
5
+
6
+ attr_accessor :driver
7
+
8
+ def migrate(_, &proc)
9
+ proc.call(driver)
10
+ end
11
+
12
+ end
13
+
14
+ describe Migratable do
15
+
16
+ let(:klass) do
17
+ Class.new do
18
+ include Migratable
19
+
20
+ migration("20140923-1") do |db|
21
+ db.do_stuff(my_instance_method)
22
+ end
23
+
24
+ migration("20140923-2") do |db|
25
+ db.do_stuff(my_instance_other_method)
26
+ end
27
+
28
+ def my_instance_method
29
+ "foo"
30
+ end
31
+
32
+ def my_instance_other_method
33
+ "bar"
34
+ end
35
+
36
+ def migrator
37
+ @migrator ||= FakeMigrator.new
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "::migration" do
43
+ it "adds the migration to the class variable" do
44
+ klass.migration("apa") { "foo" }
45
+ expect(klass.migrations.map(&:name)).to include("apa")
46
+ end
47
+ end
48
+
49
+ describe "::migrations" do
50
+ it "returns a sorted collection" do
51
+ migration_names = klass.migrations.map(&:name)
52
+ expect(migration_names).to be_sorted
53
+ end
54
+ end
55
+
56
+ describe "#migrate!" do
57
+ it "calls the instance methods" do
58
+ instance = klass.new
59
+ driver = double
60
+ expect(driver).to receive(:do_stuff).twice
61
+ instance.migrator.driver = driver
62
+ expect(instance).to receive(:my_instance_method)
63
+ expect(instance).to receive(:my_instance_other_method)
64
+ instance.migrate!
65
+ end
66
+ end
67
+
68
+ describe "#prefixed_migration_name" do
69
+ let(:subject) { klass.new }
70
+ context "when there is a migration_prefixed defined" do
71
+ it "prefixes the input" do
72
+ def subject.migration_prefix
73
+ "foo"
74
+ end
75
+ expect(subject.prefixed_migration_name("bar")).to eq("foo_bar")
76
+ end
77
+ end
78
+
79
+ context "when there is no migration prefix" do
80
+ it "returns the input" do
81
+ def subject.migration_prefix
82
+ nil
83
+ end
84
+ expect(subject.prefixed_migration_name("bar")).to eq("bar")
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ module SimpleMigrator
4
+ describe Migrator do
5
+ let(:connection) { Sequel.sqlite }
6
+ let(:migrator) { Migrator.new(connection) }
7
+
8
+ describe "#initialize" do
9
+ context "when no prior migration table exists" do
10
+ before do
11
+ Migrator.new(connection)
12
+ end
13
+ it "creates one" do
14
+ expect(connection.tables).to include(Migrator.table_name)
15
+ end
16
+ end
17
+ end
18
+
19
+ describe "#migrated?" do
20
+ context "when a migration of that name has been migrated" do
21
+ it "returns true" do
22
+ migrator.migrate("foo", & -> (_db) {})
23
+ expect(migrator.migrated?("foo")).to be_truthy
24
+ end
25
+ end
26
+
27
+ context "when the migration has not been run" do
28
+ it "returns true" do
29
+ expect(migrator.migrated?("foo")).to be_falsey
30
+ end
31
+ end
32
+ end
33
+
34
+ describe "#migrate" do
35
+ context "when the migration has not been run previously" do
36
+ it "runs the migration" do
37
+ expect { |b| migrator.migrate("foo", &b) }.to yield_control
38
+ end
39
+
40
+ it "writes the migration name to the migrations table" do
41
+ migration = -> (_) {}
42
+ migrator.migrate("foo", &migration)
43
+ migrations = migrator.migrations_table.select_map(:migration_name)
44
+ expect(migrations).to include("foo")
45
+ end
46
+ end
47
+
48
+ context "when the migration has already run" do
49
+ it "does not re run it" do
50
+ migrator.migrate("foo", & ->(_) {})
51
+ expect { |b| migrator.migrate("foo", &b) }.to_not yield_control
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe SimpleMigrator do
4
+ describe "::migrator" do
5
+ let(:db) { Sequel.sqlite }
6
+ it "returns a migrator" do
7
+ expect(SimpleMigrator.migrator(db)).to be_a(SimpleMigrator::Migrator)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,105 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'simple_migrator'
5
+ require 'sqlite3'
6
+
7
+ RSpec::Matchers.define(:be_sorted) do
8
+ match do |actual|
9
+ actual == actual.sort
10
+ end
11
+ end
12
+
13
+
14
+
15
+ # This file was generated by the `rspec --init` command. Conventionally, all
16
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
17
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
18
+ # this file to always be loaded, without a need to explicitly require it in any
19
+ # files.
20
+ #
21
+ # Given that it is always loaded, you are encouraged to keep this file as
22
+ # light-weight as possible. Requiring heavyweight dependencies from this file
23
+ # will add to the boot time of your test suite on EVERY test run, even for an
24
+ # individual file that may not need all of that loaded. Instead, consider making
25
+ # a separate helper file that requires the additional dependencies and performs
26
+ # the additional setup, and require it from the spec files that actually need
27
+ # it.
28
+ #
29
+ # The `.rspec` file also contains a few flags that are not defaults but that
30
+ # users commonly want.
31
+ #
32
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
33
+ RSpec.configure do |config|
34
+ # rspec-expectations config goes here. You can use an alternate
35
+ # assertion/expectation library such as wrong or the stdlib/minitest
36
+ # assertions if you prefer.
37
+ config.expect_with :rspec do |expectations|
38
+ # This option will default to `true` in RSpec 4. It makes the `description`
39
+ # and `failure_message` of custom matchers include text for helper methods
40
+ # defined using `chain`, e.g.:
41
+ # be_bigger_than(2).and_smaller_than(4).description
42
+ # # => "be bigger than 2 and smaller than 4"
43
+ # ...rather than:
44
+ # # => "be bigger than 2"
45
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
46
+ end
47
+
48
+ # rspec-mocks config goes here. You can use an alternate test double
49
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
50
+ config.mock_with :rspec do |mocks|
51
+ # Prevents you from mocking or stubbing a method that does not exist on
52
+ # a real object. This is generally recommended, and will default to
53
+ # `true` in RSpec 4.
54
+ mocks.verify_partial_doubles = true
55
+ end
56
+
57
+ # The settings below are suggested to provide a good initial experience
58
+ # with RSpec, but feel free to customize to your heart's content.
59
+ =begin
60
+ # These two settings work together to allow you to limit a spec run
61
+ # to individual examples or groups you care about by tagging them with
62
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
63
+ # get run.
64
+ config.filter_run :focus
65
+ config.run_all_when_everything_filtered = true
66
+
67
+ # Limits the available syntax to the non-monkey patched syntax that is
68
+ # recommended. For more details, see:
69
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
70
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
71
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
72
+ config.disable_monkey_patching!
73
+
74
+ # This setting enables warnings. It's recommended, but in some cases may
75
+ # be too noisy due to issues in dependencies.
76
+ config.warnings = true
77
+
78
+ # Many RSpec users commonly either run the entire suite or an individual
79
+ # file, and it's useful to allow more verbose output when running an
80
+ # individual spec file.
81
+ if config.files_to_run.one?
82
+ # Use the documentation formatter for detailed output,
83
+ # unless a formatter has already been configured
84
+ # (e.g. via a command-line flag).
85
+ config.default_formatter = 'doc'
86
+ end
87
+
88
+ # Print the 10 slowest examples and example groups at the
89
+ # end of the spec run, to help surface which specs are running
90
+ # particularly slow.
91
+ config.profile_examples = 10
92
+
93
+ # Run specs in random order to surface order dependencies. If you find an
94
+ # order dependency and want to debug it, you can fix the order by providing
95
+ # the seed, which is printed after each run.
96
+ # --seed 1234
97
+ config.order = :random
98
+
99
+ # Seed global randomization in this process using the `--seed` CLI option.
100
+ # Setting this allows you to use `--seed` to deterministically reproduce
101
+ # test failures related to randomization by passing the same `--seed` value
102
+ # as the one that triggered the failure.
103
+ Kernel.srand config.seed
104
+ =end
105
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_migrator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jesper Josefsson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sequel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email:
99
+ - jesper.josefsson@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - lib/simple_migrator.rb
111
+ - lib/simple_migrator/migratable.rb
112
+ - lib/simple_migrator/migration.rb
113
+ - lib/simple_migrator/migrator.rb
114
+ - lib/simple_migrator/version.rb
115
+ - simple_migrator.gemspec
116
+ - spec/migratable_spec.rb
117
+ - spec/migrator_spec.rb
118
+ - spec/simple_migrator_spec.rb
119
+ - spec/spec_helper.rb
120
+ homepage:
121
+ licenses:
122
+ - MIT
123
+ metadata: {}
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ requirements: []
139
+ rubyforge_project:
140
+ rubygems_version: 2.2.2
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: SimpleMigrator provides basic programmatic migrations for Sequel
144
+ test_files:
145
+ - spec/migratable_spec.rb
146
+ - spec/migrator_spec.rb
147
+ - spec/simple_migrator_spec.rb
148
+ - spec/spec_helper.rb