sqrbl 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +24 -1
- data/README.txt +14 -6
- data/TODO.txt +6 -5
- data/lib/sqrbl.rb +13 -13
- data/lib/sqrbl/{base_migration_writer.rb → base_conversion_writer.rb} +11 -11
- data/lib/sqrbl/{migration.rb → conversion.rb} +11 -11
- data/lib/sqrbl/group.rb +6 -6
- data/lib/sqrbl/{individual_migration_writer.rb → individual_conversion_writer.rb} +5 -5
- data/lib/sqrbl/mixins/method_missing_delegation.rb +4 -4
- data/lib/sqrbl/step.rb +7 -0
- data/lib/sqrbl/{unified_migration_writer.rb → unified_conversion_writer.rb} +3 -3
- data/spec/functional/base_conversion_writer_spec.rb +12 -0
- data/spec/functional/{individual_migration_writer_spec.rb → individual_conversion_writer_spec.rb} +11 -11
- data/spec/functional/{unified_migration_writer_spec.rb → unified_conversion_writer_spec.rb} +15 -15
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/{migration_spec.rb → conversion_spec.rb} +19 -19
- data/spec/unit/group_spec.rb +13 -13
- data/spec/unit/sqrbl_spec.rb +14 -14
- data/spec/unit/step_spec.rb +12 -0
- metadata +12 -12
- data/spec/functional/base_migration_writer_spec.rb +0 -12
data/History.txt
CHANGED
@@ -1,6 +1,29 @@
|
|
1
|
+
== 0.2.0 / 2009-09-03
|
2
|
+
|
3
|
+
* API change: switch to using the word "conversion" instead of
|
4
|
+
"migration" throughout the library. While this was mostly a
|
5
|
+
find-and-replace job, I'm bumping the version number to indicate the
|
6
|
+
API breakage.
|
7
|
+
|
8
|
+
I'd found that using the word "migration" got people thinking in terms
|
9
|
+
of ActiveRecord, which isn't quite the intended purpose of this
|
10
|
+
library. SQrbL was written to manage the process of doing one-off
|
11
|
+
database conversions in a repeatable way.
|
12
|
+
|
13
|
+
(At raSANTIAGO, I helped a few organizations switch from other
|
14
|
+
programs to CiviCRM, and I wrote SQrbL to help me keep track of all
|
15
|
+
the various steps involved in importing their legacy
|
16
|
+
constituent-management data to CiviCRM.)
|
17
|
+
|
18
|
+
== 0.1.3 / 2009-08-07
|
19
|
+
|
20
|
+
* Added Step#helpers method for code cosmetics.
|
21
|
+
|
1
22
|
== 0.1.2 / 2009-08-06
|
2
23
|
|
3
|
-
* No changes -- just want to make sure the latest release is actually
|
24
|
+
* No changes -- just want to make sure the latest release is actually
|
25
|
+
usable after flailing around trying to use the 'bones' and 'rubyforge'
|
26
|
+
gems.
|
4
27
|
|
5
28
|
== 0.1.0 / 2009-08-06
|
6
29
|
|
data/README.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
== SQrbL: Making database
|
1
|
+
== SQrbL: Making database conversions suck less since July 2009!
|
2
2
|
|
3
3
|
Copyright (c) 2009 raSANTIAGO + Associates LLC (http://www.rasantiago.com)
|
4
4
|
|
@@ -10,13 +10,13 @@ SQrbL's creator and primary author is Sam Livingston-Gray.
|
|
10
10
|
|
11
11
|
== DESCRIPTION:
|
12
12
|
|
13
|
-
SQrbL was created to help manage an extremely specific problem: managing SQL-based database
|
13
|
+
SQrbL was created to help manage an extremely specific problem: managing SQL-based database conversions.
|
14
14
|
|
15
15
|
In essence, SQrbL is a tool for managing multiple SQL queries using Ruby. SQrbL borrows some terminology and ideas from ActiveRecord's schema migrations, but where ActiveRecord manages changes to your database schema over time, SQrbL was written to manage the process of transforming your data from one schema to another. (Of course, you could use SQrbL for the former case as well -- just use it to write DDL queries -- but ActiveRecord has better tools for figuring out which migrations have already been applied.)
|
16
16
|
|
17
17
|
===How It Works:
|
18
18
|
|
19
|
-
You describe the steps in your
|
19
|
+
You describe the steps in your conversion in a SQrbL file. Each step can produce as much or as little SQL as you like. Each step has an "up" and a "down" part -- so you can do and undo each step as many times as you need to, until you get it just right. When you run your SQrbL file, it creates a tree of *.sql files containing the output from your conversion steps, one file per step. It also creates the combined files "all_up.sql" and "all_down.sql"; these contain all of your steps combined into one giant file so that, when Cutover Day arrives, you can copy/paste the whole thing into your SQL client and run it all at once.
|
20
20
|
|
21
21
|
===Why It Exists:
|
22
22
|
|
@@ -26,13 +26,21 @@ SQL is a fantastic DSL for describing queries. It's not bad at doing transforma
|
|
26
26
|
|
27
27
|
SQL.rb seemed a bit too pretentious, so I went with SQrbL -- as in, "You got Ruby in my SQL." I pronounce it "squirble" (rhymes with "squirrel"). The proper capitalization can be annoying to type, so I've aliased the SQrbL class as Sqrbl.
|
28
28
|
|
29
|
+
Note: While not quite a literal pronunciation, "Screwball" is an accepted alternate. Mostly 'cause it's funny.
|
30
|
+
|
29
31
|
== SYNOPSIS:
|
30
32
|
|
31
33
|
<i>(Note that, in the following code sample, I'm using the convention that do/end blocks are used primarily for their side effects, and curly-brace blocks are used primarily for their return value.)</i>
|
32
34
|
|
33
|
-
Sqrbl.
|
35
|
+
Sqrbl.conversion "Convert from old widgets to new widgets" do
|
34
36
|
set_output_directory '/path/to/generated/sql'
|
35
37
|
|
38
|
+
helpers do
|
39
|
+
def widget_import_note
|
40
|
+
'"Imported from old_widgets"'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
36
44
|
group "Widgets" do
|
37
45
|
step "Create widgets" do
|
38
46
|
up do
|
@@ -42,7 +50,7 @@ SQL.rb seemed a bit too pretentious, so I went with SQrbL -- as in, "You got Rub
|
|
42
50
|
insert_into("new_widgets", {
|
43
51
|
:name => 'widget_name',
|
44
52
|
:part_num => 'CONCAT("X_", part_number)',
|
45
|
-
:note =>
|
53
|
+
:note => widget_import_note,
|
46
54
|
})
|
47
55
|
}
|
48
56
|
FROM old_widgets
|
@@ -59,7 +67,7 @@ SQL.rb seemed a bit too pretentious, so I went with SQrbL -- as in, "You got Rub
|
|
59
67
|
end
|
60
68
|
end
|
61
69
|
|
62
|
-
The above code sample describes a
|
70
|
+
The above code sample describes a conversion with one step: moving the data in an `old_widgets` table to a `new_widgets` table. When run, this will generate a set of *.sql files in the directory /path/to/generated/sql.
|
63
71
|
|
64
72
|
== REQUIREMENTS:
|
65
73
|
|
data/TODO.txt
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
+
- Change verbiage: "conversion" instead of "migration".
|
2
|
+
|
3
|
+
- Writers should create some sort of output to let you know that something happened.
|
4
|
+
|
1
5
|
- bin/sqrbl should create an empty SQrbL file with the following simple template:
|
2
6
|
require 'rubygems'
|
3
7
|
require 'sqrbl'
|
4
|
-
Sqrbl.
|
8
|
+
Sqrbl.conversion do
|
5
9
|
# (copy the rest from README.txt)
|
6
10
|
end
|
7
11
|
|
8
|
-
-
|
9
|
-
|
10
|
-
- Define #helper(&block) as a simple wrapper to instance_eval on the same object. (Purpose: allow you to define helpers in a block that can be code-folded.)
|
11
|
-
|
12
|
+
- Add some string-munging for pluralize(n, thing, things), and possibly move #unix_name into the string-munging module.
|
data/lib/sqrbl.rb
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
# For more information, please see README.txt and LICENSE.txt in the project's root directory.
|
3
3
|
#
|
4
4
|
# Most of this was autogenerated by the 'bones' gem.
|
5
|
-
# The primary domain-specific method of interest here is Sqrbl.
|
5
|
+
# The primary domain-specific method of interest here is Sqrbl.conversion.
|
6
6
|
module Sqrbl
|
7
7
|
|
8
8
|
# :stopdoc:
|
9
|
-
VERSION = '0.
|
9
|
+
VERSION = '0.2.0'
|
10
10
|
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
11
11
|
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
12
12
|
# :startdoc:
|
@@ -46,22 +46,22 @@ module Sqrbl
|
|
46
46
|
Dir.glob(search_me).sort.each {|rb| require rb}
|
47
47
|
end
|
48
48
|
|
49
|
-
# Convenience method: builds and writes a
|
50
|
-
def self.
|
51
|
-
returning(
|
52
|
-
|
49
|
+
# Convenience method: builds and writes a conversion in one go.
|
50
|
+
def self.conversion(&block)
|
51
|
+
returning(build_conversion(&block)) do |cvn|
|
52
|
+
write_conversion!(cvn)
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
# Convenience method: Calls Sqrbl::
|
57
|
-
def self.
|
58
|
-
Sqrbl::
|
56
|
+
# Convenience method: Calls Sqrbl::Conversion.build and passes it the given block.
|
57
|
+
def self.build_conversion(&block)
|
58
|
+
Sqrbl::Conversion.build(&block)
|
59
59
|
end
|
60
60
|
|
61
|
-
# Convenience method: Write a
|
62
|
-
def self.
|
63
|
-
|
64
|
-
writer_class.
|
61
|
+
# Convenience method: Write a Conversion using all subclasses of BaseConversionWriter.
|
62
|
+
def self.write_conversion!(conversion)
|
63
|
+
BaseConversionWriter.subclasses.each do |writer_class|
|
64
|
+
writer_class.write_conversion!(conversion)
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
|
3
3
|
module Sqrbl
|
4
|
-
# Base class for other
|
5
|
-
class
|
6
|
-
attr_accessor :
|
4
|
+
# Base class for other conversion writers.
|
5
|
+
class BaseConversionWriter
|
6
|
+
attr_accessor :conversion, :output_directory
|
7
7
|
|
8
8
|
class << self
|
9
9
|
# List all classes that inherit from this one
|
@@ -16,23 +16,23 @@ module Sqrbl
|
|
16
16
|
end
|
17
17
|
|
18
18
|
# Convenience method: create a new instance and invoke <tt>write!</tt> on it.
|
19
|
-
def
|
20
|
-
new(
|
19
|
+
def write_conversion!(conversion)
|
20
|
+
new(conversion).write!
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def initialize(
|
25
|
-
@
|
24
|
+
def initialize(conversion) #:nodoc:
|
25
|
+
@conversion = conversion
|
26
26
|
set_default_output_dir!
|
27
27
|
end
|
28
28
|
|
29
29
|
protected
|
30
30
|
# Set the output_directory attribute based on the output_directory or creating_file
|
31
|
-
# attributes of the given
|
31
|
+
# attributes of the given conversion, in that order
|
32
32
|
def set_default_output_dir!
|
33
|
-
raise "No
|
34
|
-
self.output_directory ||=
|
35
|
-
self.output_directory ||=
|
33
|
+
raise "No conversion given!" unless conversion
|
34
|
+
self.output_directory ||= conversion.output_directory
|
35
|
+
self.output_directory ||= conversion.creating_file && File.join(File.expand_path(File.dirname(conversion.creating_file)), 'sql')
|
36
36
|
raise "Unable to determine output directory!" unless self.output_directory
|
37
37
|
end
|
38
38
|
|
@@ -4,27 +4,27 @@
|
|
4
4
|
|
5
5
|
module Sqrbl
|
6
6
|
|
7
|
-
# The
|
7
|
+
# The Conversion class doesn't do much on its own; it's basically just a
|
8
8
|
# container for a list of Group objects, which are created using #group.
|
9
9
|
#
|
10
10
|
# Note also that because Group includes the MethodMissingDelegation mixin,
|
11
|
-
# instance methods defined in the block given to
|
11
|
+
# instance methods defined in the block given to Conversion.build will become
|
12
12
|
# available to all groups (and their related objects) that belong to a
|
13
|
-
#
|
14
|
-
class
|
13
|
+
# Conversion instance. For more information, see MethodMissingDelegation.
|
14
|
+
class Conversion
|
15
15
|
attr_reader :groups, :creating_file, :output_directory
|
16
16
|
|
17
17
|
include HasTodos
|
18
18
|
|
19
|
-
# Creates a new
|
19
|
+
# Creates a new Conversion object and evaluates the block in its binding.
|
20
20
|
# As a result, any methods called within the block will affect the state
|
21
21
|
# of that object (and that object only).
|
22
22
|
#
|
23
|
-
# <i>(Eventually, this will also pass the
|
23
|
+
# <i>(Eventually, this will also pass the conversion to a helper object that
|
24
24
|
# will create the tree of output *.sql files.)</i>
|
25
25
|
def self.build(&block)
|
26
|
-
returning(self.new) do |
|
27
|
-
|
26
|
+
returning(self.new) do |conversion|
|
27
|
+
conversion.instance_eval(&block)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -44,17 +44,17 @@ module Sqrbl
|
|
44
44
|
groups << Group.new(self, name, &block)
|
45
45
|
end
|
46
46
|
|
47
|
-
# Convenience method: iterate all StepPair objects in the
|
47
|
+
# Convenience method: iterate all StepPair objects in the conversion
|
48
48
|
def step_pairs
|
49
49
|
groups.map(&:steps).flatten
|
50
50
|
end
|
51
51
|
|
52
|
-
# Convenience method: iterate all 'up' steps in the
|
52
|
+
# Convenience method: iterate all 'up' steps in the conversion
|
53
53
|
def up_steps
|
54
54
|
step_pairs.map(&:up_step)
|
55
55
|
end
|
56
56
|
|
57
|
-
# Convenience method: iterate all 'down' steps in the
|
57
|
+
# Convenience method: iterate all 'down' steps in the conversion
|
58
58
|
def down_steps
|
59
59
|
step_pairs.map(&:down_step)
|
60
60
|
end
|
data/lib/sqrbl/group.rb
CHANGED
@@ -3,21 +3,21 @@
|
|
3
3
|
|
4
4
|
|
5
5
|
module Sqrbl
|
6
|
-
# Like the
|
6
|
+
# Like the Conversion class, Group doesn't do much on its own.
|
7
7
|
# It's basically a container for a list of StepPair objects, which are created using #step.
|
8
8
|
#
|
9
|
-
# Group delegates +method_missing+ calls to its +
|
9
|
+
# Group delegates +method_missing+ calls to its +conversion+ object.
|
10
10
|
# For more information, see MethodMissingDelegation.
|
11
11
|
class Group
|
12
|
-
attr_reader :
|
12
|
+
attr_reader :conversion, :description, :block, :steps
|
13
13
|
|
14
14
|
include Sqrbl::ExpectsBlockWithNew
|
15
15
|
include Sqrbl::MethodMissingDelegation
|
16
|
-
delegate_method_missing_to :
|
16
|
+
delegate_method_missing_to :conversion
|
17
17
|
include HasTodos
|
18
18
|
|
19
|
-
def initialize(
|
20
|
-
@
|
19
|
+
def initialize(conversion, description, options = {}, &block)
|
20
|
+
@conversion = conversion
|
21
21
|
@description = description
|
22
22
|
@block = lambda(&block)
|
23
23
|
@steps = []
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Sqrbl
|
2
|
-
# Writes one [numbered] file per
|
3
|
-
#
|
4
|
-
class
|
2
|
+
# Writes one [numbered] file per conversion step, in two main directories: sql/up/ and sql/down/.
|
3
|
+
# Conversion steps are contained in a [numbered] subfolder that corresponds to the group.
|
4
|
+
class IndividualConversionWriter < BaseConversionWriter
|
5
5
|
# Prepare the up and down directories and populate them with individual files.
|
6
6
|
#
|
7
7
|
# WARNING: <b>RECURSIVELY DELETES ALL FILES</b> in output_directory/up and output_directory/down unless passed :safe_mode => true in the options hash!
|
@@ -22,7 +22,7 @@ module Sqrbl
|
|
22
22
|
|
23
23
|
# Write out the contents of the individual files, each in its group's subfolder
|
24
24
|
def write_individual_files!
|
25
|
-
|
25
|
+
conversion.groups.each_with_index do |group, idx|
|
26
26
|
group_dir = "%0#{group_num_width}d_%s" % [idx + 1, group.unix_name]
|
27
27
|
up_subdir = File.join(output_directory, 'up', group_dir)
|
28
28
|
down_subdir = File.join(output_directory, 'down', group_dir)
|
@@ -38,7 +38,7 @@ module Sqrbl
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def group_num_width # :nodoc:
|
41
|
-
|
41
|
+
conversion.groups.length.to_s.length
|
42
42
|
end
|
43
43
|
|
44
44
|
def step_num_width(step) # :nodoc:
|
@@ -7,7 +7,7 @@ module Sqrbl
|
|
7
7
|
# references to one another. The graph will have the following general shape
|
8
8
|
# (where --> indicates a relationship that is generally one-to-many):
|
9
9
|
#
|
10
|
-
#
|
10
|
+
# Conversion --> Group --> StepPair --> Step
|
11
11
|
#
|
12
12
|
# Also, because these various objects are created with blocks that are
|
13
13
|
# instance_eval'd, those blocks can define helper methods that will be
|
@@ -16,14 +16,14 @@ module Sqrbl
|
|
16
16
|
# Taken together, these two features (back-references and instance_eval)
|
17
17
|
# let us provide a useful facility for defining helper methods inside a
|
18
18
|
# block, and accessing them using scoping rules that work in an intuitive
|
19
|
-
# manner. For example, if a
|
19
|
+
# manner. For example, if a Conversion defines a method #foo, which is
|
20
20
|
# later called from inside a Step's block, the Step can catch that call
|
21
21
|
# using method_missing and delegate it to its StepPair, which in turn
|
22
|
-
# will delegate to its Group, which in turn will delegate to its
|
22
|
+
# will delegate to its Group, which in turn will delegate to its Conversion.
|
23
23
|
#
|
24
24
|
# Confused yet? Here's a slightly modified version of the example in README.txt:
|
25
25
|
#
|
26
|
-
# Sqrbl.
|
26
|
+
# Sqrbl.conversion "Convert from old widgets to new widgets" do
|
27
27
|
# def new_widget_insert()
|
28
28
|
# insert_into("new_widgets", {
|
29
29
|
# :name => 'widget_name',
|
data/lib/sqrbl/step.rb
CHANGED
@@ -54,6 +54,13 @@ EOF
|
|
54
54
|
eval_block_on_initialize(options)
|
55
55
|
end
|
56
56
|
|
57
|
+
# This method is purely cosmetic. If you want to define a number
|
58
|
+
# of helpers and use your editor's code-folding features to
|
59
|
+
# collapse them all together, define them in a +helper+ block.
|
60
|
+
def helpers(&block)
|
61
|
+
yield
|
62
|
+
end
|
63
|
+
|
57
64
|
# Writes a commented-out Todo item to +output+.
|
58
65
|
#
|
59
66
|
# Also, creates a Todo object that may be used to create console
|
@@ -2,7 +2,7 @@ module Sqrbl
|
|
2
2
|
# Writes two files: output_directory/all_up.sql and output_directory/all_down.sql.
|
3
3
|
# * output_directory/all_up.sql contains all 'up' steps, in creation order.
|
4
4
|
# * output_directory/all_down.sql contains all 'down' steps, in reverse creation order.
|
5
|
-
class
|
5
|
+
class UnifiedConversionWriter < BaseConversionWriter
|
6
6
|
# Create all_up.sql and all_down.sql in output_directory.
|
7
7
|
def write!
|
8
8
|
ensure_dir_exists(output_directory)
|
@@ -12,12 +12,12 @@ module Sqrbl
|
|
12
12
|
|
13
13
|
# Output from all 'up' steps, in creation order
|
14
14
|
def all_up_steps_output
|
15
|
-
|
15
|
+
conversion.up_steps.map(&:output).join
|
16
16
|
end
|
17
17
|
|
18
18
|
# Output from all 'down' steps, in reverse creation order
|
19
19
|
def all_down_steps_output
|
20
|
-
|
20
|
+
conversion.down_steps.reverse.map(&:output).join
|
21
21
|
end
|
22
22
|
|
23
23
|
protected
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
2
|
+
|
3
|
+
include Sqrbl
|
4
|
+
|
5
|
+
describe BaseConversionWriter do
|
6
|
+
it "should know what its subclasses are" do
|
7
|
+
BaseConversionWriter.subclasses.should == [
|
8
|
+
IndividualConversionWriter,
|
9
|
+
UnifiedConversionWriter,
|
10
|
+
]
|
11
|
+
end
|
12
|
+
end
|
data/spec/functional/{individual_migration_writer_spec.rb → individual_conversion_writer_spec.rb}
RENAMED
@@ -2,9 +2,9 @@ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
|
2
2
|
|
3
3
|
include Sqrbl
|
4
4
|
|
5
|
-
describe
|
5
|
+
describe IndividualConversionWriter do
|
6
6
|
before(:each) do
|
7
|
-
@
|
7
|
+
@cvn = Sqrbl.build_conversion do
|
8
8
|
group 'Group one' do
|
9
9
|
step 'Step one' do
|
10
10
|
up { write 'Step one up' }
|
@@ -14,7 +14,7 @@ describe IndividualMigrationWriter do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
@base_dir = File.expand_path(File.join(File.dirname(__FILE__), 'sql'))
|
17
|
-
@writer =
|
17
|
+
@writer = IndividualConversionWriter.new(@cvn)
|
18
18
|
end
|
19
19
|
|
20
20
|
it "should have a output_directory attribute" do
|
@@ -36,9 +36,9 @@ describe IndividualMigrationWriter do
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
describe "for a
|
39
|
+
describe "for a conversion with two groups and three steps" do
|
40
40
|
before(:each) do
|
41
|
-
@
|
41
|
+
@cvn = Sqrbl.build_conversion do
|
42
42
|
group 'Group one' do
|
43
43
|
step 'Step one' do
|
44
44
|
up { write 'Step one up' }
|
@@ -58,11 +58,11 @@ describe IndividualMigrationWriter do
|
|
58
58
|
end
|
59
59
|
|
60
60
|
@base_dir = File.expand_path(File.join(File.dirname(__FILE__), 'sql'))
|
61
|
-
@writer =
|
61
|
+
@writer = IndividualConversionWriter.new(@cvn)
|
62
62
|
end
|
63
63
|
|
64
64
|
it "should create a separate subdirectory in [output_directory]/up for each group" do
|
65
|
-
@
|
65
|
+
@cvn.groups.each_with_index do |group, idx|
|
66
66
|
group_dir = "#{idx + 1}_#{group.unix_name}"
|
67
67
|
up_subdir = File.join(@writer.output_directory, 'up', group_dir)
|
68
68
|
down_subdir = File.join(@writer.output_directory, 'down', group_dir)
|
@@ -76,7 +76,7 @@ describe IndividualMigrationWriter do
|
|
76
76
|
end
|
77
77
|
|
78
78
|
it "should create a separate file for each step, in the subdirectory for that step's group" do
|
79
|
-
@
|
79
|
+
@cvn.groups.each_with_index do |group, idx|
|
80
80
|
group_dir = "#{idx + 1}_#{group.unix_name}"
|
81
81
|
up_subdir = File.join(@writer.output_directory, 'up', group_dir)
|
82
82
|
down_subdir = File.join(@writer.output_directory, 'down', group_dir)
|
@@ -93,9 +93,9 @@ describe IndividualMigrationWriter do
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
describe "for a
|
96
|
+
describe "for a conversion with 10 groups" do
|
97
97
|
before(:each) do
|
98
|
-
@
|
98
|
+
@cvn = Sqrbl.build_conversion do
|
99
99
|
group 'Group one' do
|
100
100
|
step 'Step one' do
|
101
101
|
up { write 'Step one up' }
|
@@ -158,7 +158,7 @@ describe IndividualMigrationWriter do
|
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
161
|
-
@writer =
|
161
|
+
@writer = IndividualConversionWriter.new(@cvn)
|
162
162
|
end
|
163
163
|
|
164
164
|
it "should left-fill the group numbers with zeros" do
|
@@ -2,26 +2,26 @@ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
|
2
2
|
|
3
3
|
include Sqrbl
|
4
4
|
|
5
|
-
describe
|
6
|
-
describe "for a
|
7
|
-
it "should set #output_directory from the
|
8
|
-
|
9
|
-
writer =
|
10
|
-
writer.output_directory.should ==
|
5
|
+
describe UnifiedConversionWriter do
|
6
|
+
describe "for a conversion with #output_directory set" do
|
7
|
+
it "should set #output_directory from the conversion's #output_directory" do
|
8
|
+
cvn = mock('Conversion', :output_directory => '/path/to/sql', :creating_file => nil)
|
9
|
+
writer = UnifiedConversionWriter.new(cvn)
|
10
|
+
writer.output_directory.should == cvn.output_directory
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
describe "for a
|
15
|
-
it "should set #output_directory using the dirname of the
|
16
|
-
|
17
|
-
writer =
|
14
|
+
describe "for a conversion with output_directory blank" do
|
15
|
+
it "should set #output_directory using the dirname of the conversion's #creating_file, plus /sql" do
|
16
|
+
cvn = mock('Conversion', :output_directory => nil, :creating_file => '/path/to/some/sqrbl_file.rb')
|
17
|
+
writer = UnifiedConversionWriter.new(cvn)
|
18
18
|
writer.output_directory.should == '/path/to/some/sql'
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
describe "for a
|
22
|
+
describe "for a conversion with two groups and three steps" do
|
23
23
|
before(:each) do
|
24
|
-
@
|
24
|
+
@cvn = Sqrbl.build_conversion do
|
25
25
|
group 'Group one' do
|
26
26
|
step 'Step one' do
|
27
27
|
up { write 'Step one up' }
|
@@ -41,7 +41,7 @@ describe UnifiedMigrationWriter do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
@base_dir = File.expand_path(File.join(File.dirname(__FILE__), 'sql'))
|
44
|
-
@writer =
|
44
|
+
@writer = UnifiedConversionWriter.new(@cvn)
|
45
45
|
end
|
46
46
|
|
47
47
|
it "should not create the target directory if it already exists" do
|
@@ -65,7 +65,7 @@ describe UnifiedMigrationWriter do
|
|
65
65
|
end
|
66
66
|
|
67
67
|
it "should combine the contents of all up steps in order" do
|
68
|
-
@writer.all_up_steps_output.should == @
|
68
|
+
@writer.all_up_steps_output.should == @cvn.up_steps.map(&:output).join
|
69
69
|
end
|
70
70
|
|
71
71
|
it "should write the contents of all up steps to the all_up.sql file" do
|
@@ -83,7 +83,7 @@ describe UnifiedMigrationWriter do
|
|
83
83
|
end
|
84
84
|
|
85
85
|
it "should combine the contents of all down steps in REVERSE order" do
|
86
|
-
@writer.all_down_steps_output.should == @
|
86
|
+
@writer.all_down_steps_output.should == @cvn.down_steps.reverse.map(&:output).join
|
87
87
|
end
|
88
88
|
|
89
89
|
it "should write the contents of all down steps to the all_down.sql file" do
|
data/spec/spec_helper.rb
CHANGED
@@ -19,7 +19,7 @@ Spec::Runner.configure do |config|
|
|
19
19
|
end
|
20
20
|
|
21
21
|
|
22
|
-
# Stub out the various things that the
|
22
|
+
# Stub out the various things that the conversion writers do.
|
23
23
|
# Call this after defining the mocks you care about (because mocks created first take precedence).
|
24
24
|
# Then, calling #write! shouldn't actually touch the filesystem.
|
25
25
|
def stub_out_file_creation
|
@@ -2,35 +2,35 @@ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
|
2
2
|
|
3
3
|
include Sqrbl
|
4
4
|
|
5
|
-
describe
|
6
|
-
describe "A new
|
5
|
+
describe Conversion do
|
6
|
+
describe "A new conversion" do
|
7
7
|
it "should know the file that created it" do
|
8
|
-
|
9
|
-
|
8
|
+
cvn = Conversion.new
|
9
|
+
cvn.creating_file.should == File.expand_path(__FILE__)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
describe "A
|
13
|
+
describe "A conversion built with Conversion.build" do
|
14
14
|
it "should still know the file that created it" do
|
15
|
-
|
16
|
-
|
15
|
+
cvn = Conversion.build {}
|
16
|
+
cvn.creating_file.should == File.expand_path(__FILE__)
|
17
17
|
end
|
18
18
|
|
19
19
|
describe "when the block calls #group once" do
|
20
20
|
it "should have one group with the proper description" do
|
21
|
-
|
21
|
+
cvn = Conversion.build do
|
22
22
|
group "first group" do
|
23
23
|
'hello world'
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
27
|
-
|
26
|
+
cvn.groups.all? { |group| group.should be_kind_of(Group) }
|
27
|
+
cvn.groups.map(&:description).should == ['first group']
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
31
|
describe "when the block calls #group twice" do
|
32
32
|
it "should have two group with the proper descriptions" do
|
33
|
-
|
33
|
+
cvn = Conversion.build do
|
34
34
|
group "first group" do
|
35
35
|
'hello world'
|
36
36
|
end
|
@@ -38,31 +38,31 @@ describe Migration do
|
|
38
38
|
'goodbye world'
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
42
|
-
|
41
|
+
cvn.groups.all? { |group| group.should be_kind_of(Group) }
|
42
|
+
cvn.groups.map(&:description).should == ['first group', 'second group']
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
it "should pass itself as a dependency to any group it builds" do
|
47
|
-
|
47
|
+
cvn = Conversion.build do
|
48
48
|
group("first group" ) { '' }
|
49
49
|
group("second group" ) { '' }
|
50
50
|
group("group the third") { '' }
|
51
51
|
end
|
52
52
|
|
53
|
-
|
53
|
+
cvn.groups.all? { |group| group.conversion.should == cvn }
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
57
|
describe "when #todo is called" do
|
58
58
|
it "should create a Todo object that saves the message" do
|
59
|
-
|
60
|
-
|
59
|
+
cvn = Conversion.build { todo 'foo' }
|
60
|
+
cvn.todos.map(&:message).should == ['foo']
|
61
61
|
end
|
62
62
|
|
63
63
|
it "should create a Todo object that saves the location of the #todo call" do
|
64
|
-
|
65
|
-
|
64
|
+
cvn = Conversion.build { todo 'foo' }; line_num = __LINE__
|
65
|
+
cvn.todos.first.location.should == [__FILE__, line_num].join(':')
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
data/spec/unit/group_spec.rb
CHANGED
@@ -4,26 +4,26 @@ include Sqrbl
|
|
4
4
|
|
5
5
|
describe Group do
|
6
6
|
before(:each) do
|
7
|
-
@
|
7
|
+
@cvn = mock('Conversion')
|
8
8
|
end
|
9
9
|
|
10
10
|
it "should evaluate the block in its own context" do
|
11
|
-
group = Group.new(@
|
11
|
+
group = Group.new(@cvn, "foo", :skip_block_evaluation => true) do
|
12
12
|
hello_world()
|
13
13
|
end
|
14
14
|
group.should_receive(:hello_world)
|
15
15
|
group.send(:evaluate_block!)
|
16
16
|
end
|
17
17
|
|
18
|
-
it "should delegate method_missing calls back to the
|
19
|
-
@
|
20
|
-
group = Group.new(@
|
18
|
+
it "should delegate method_missing calls back to the conversion object" do
|
19
|
+
@cvn.should_receive(:goodbye_world)
|
20
|
+
group = Group.new(@cvn, "foo") do
|
21
21
|
goodbye_world()
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
it "methods defined in the group block should become available as methods on the Group instance" do
|
26
|
-
group = Group.new(@
|
26
|
+
group = Group.new(@cvn, "foo") do
|
27
27
|
def hello_world
|
28
28
|
"Hello, world!"
|
29
29
|
end
|
@@ -34,12 +34,12 @@ describe Group do
|
|
34
34
|
|
35
35
|
describe "when #todo is called" do
|
36
36
|
it "should create a Todo object that saves the message" do
|
37
|
-
group = Group.new(@
|
37
|
+
group = Group.new(@cvn, 'foo') { todo 'bar' }
|
38
38
|
group.todos.map(&:message).should == ['bar']
|
39
39
|
end
|
40
40
|
|
41
41
|
it "should create a Todo object that saves the location of the #todo call" do
|
42
|
-
group = Group.new(@
|
42
|
+
group = Group.new(@cvn, 'foo') { todo 'bar' }; line_num = __LINE__
|
43
43
|
group.todos.first.location.should == [__FILE__, line_num].join(':')
|
44
44
|
end
|
45
45
|
end
|
@@ -47,7 +47,7 @@ describe Group do
|
|
47
47
|
describe "when #step is called" do
|
48
48
|
it "should create a StepPair object and pass it the group, the description, and the block arg" do
|
49
49
|
test_self = self
|
50
|
-
group = Group.new(@
|
50
|
+
group = Group.new(@cvn, "foo") do
|
51
51
|
StepPair.should_receive(:new).with(self, 'do something').and_yield # Look weird? See ./README.txt
|
52
52
|
step('do something') {}
|
53
53
|
end
|
@@ -56,18 +56,18 @@ describe Group do
|
|
56
56
|
|
57
57
|
describe :valid? do
|
58
58
|
it "should return false if steps is empty" do
|
59
|
-
group = Group.new(@
|
59
|
+
group = Group.new(@cvn, "foo") {}
|
60
60
|
group.should_not be_valid
|
61
61
|
end
|
62
62
|
|
63
63
|
it "should return false if steps is not empty, but any step is invalid" do
|
64
|
-
group = Group.new(@
|
64
|
+
group = Group.new(@cvn, "foo") { step("foo") {} }
|
65
65
|
group.steps.map(&:valid?).should include(false)
|
66
66
|
group.should_not be_valid
|
67
67
|
end
|
68
68
|
|
69
69
|
it "should return true if steps is not empty, and all steps are themselves valid" do
|
70
|
-
group = Group.new(@
|
70
|
+
group = Group.new(@cvn, "foo") do
|
71
71
|
step("foo") do
|
72
72
|
up {}
|
73
73
|
down {}
|
@@ -78,7 +78,7 @@ describe Group do
|
|
78
78
|
end
|
79
79
|
|
80
80
|
it "should have a unix_name property that consists of the regular name with characters sanitized" do
|
81
|
-
group = Group.new(@
|
81
|
+
group = Group.new(@cvn, "This be a test, matey! ARRR!!") {}
|
82
82
|
group.unix_name.should == 'this_be_a_test_matey_arrr'
|
83
83
|
end
|
84
84
|
|
data/spec/unit/sqrbl_spec.rb
CHANGED
@@ -3,25 +3,25 @@ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
|
3
3
|
include Sqrbl
|
4
4
|
|
5
5
|
describe Sqrbl do
|
6
|
-
it ".
|
7
|
-
|
8
|
-
|
6
|
+
it ".build_conversion should return an instance of Sqrbl::Conversion" do
|
7
|
+
conversion = Sqrbl.build_conversion { 'hello, world!' }
|
8
|
+
conversion.should be_kind_of(Sqrbl::Conversion)
|
9
9
|
end
|
10
10
|
|
11
|
-
it ".
|
12
|
-
|
13
|
-
|
14
|
-
Sqrbl.should_receive(:
|
15
|
-
Sqrbl.
|
11
|
+
it ".conversion should take the return value from Sqrbl::Conversion.build and pass it to write_conversion" do
|
12
|
+
cvn = mock('Conversion')
|
13
|
+
Conversion.should_receive(:build).and_yield.and_return(cvn)
|
14
|
+
Sqrbl.should_receive(:write_conversion!).with(cvn)
|
15
|
+
Sqrbl.conversion { 'Hello, world!' }
|
16
16
|
end
|
17
17
|
|
18
|
-
describe :
|
19
|
-
it "should take a
|
20
|
-
|
21
|
-
|
22
|
-
writer_class.should_receive(:
|
18
|
+
describe :write_conversion! do
|
19
|
+
it "should take a conversion object and pass it off to any and all writers that exist" do
|
20
|
+
cvn = mock('Conversion')
|
21
|
+
BaseConversionWriter.subclasses.each do |writer_class|
|
22
|
+
writer_class.should_receive(:write_conversion!).with(cvn)
|
23
23
|
end
|
24
|
-
Sqrbl.
|
24
|
+
Sqrbl.write_conversion!(cvn)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
data/spec/unit/step_spec.rb
CHANGED
@@ -32,6 +32,18 @@ describe Step do
|
|
32
32
|
step.hello_world.should == "Hello, world!"
|
33
33
|
end
|
34
34
|
|
35
|
+
it "methods defined in a #helpers block should become available as methods on the Step instance" do
|
36
|
+
step = Step.new(@pair) do
|
37
|
+
helpers do
|
38
|
+
def hello_world
|
39
|
+
"Hello, world!"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
step.should respond_to(:hello_world)
|
44
|
+
step.hello_world.should == "Hello, world!"
|
45
|
+
end
|
46
|
+
|
35
47
|
describe "when #todo is called" do
|
36
48
|
it "should create a Todo object that saves the message" do
|
37
49
|
step = Step.new(@pair) { todo 'foo' }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sqrbl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Livingston-Gray
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-09-04 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -23,7 +23,7 @@ dependencies:
|
|
23
23
|
version: 2.5.1
|
24
24
|
version:
|
25
25
|
description: |-
|
26
|
-
SQrbL was created to help manage an extremely specific problem: managing SQL-based database
|
26
|
+
SQrbL was created to help manage an extremely specific problem: managing SQL-based database conversions.
|
27
27
|
|
28
28
|
In essence, SQrbL is a tool for managing multiple SQL queries using Ruby. SQrbL borrows some terminology and ideas from ActiveRecord's schema migrations, but where ActiveRecord manages changes to your database schema over time, SQrbL was written to manage the process of transforming your data from one schema to another. (Of course, you could use SQrbL for the former case as well -- just use it to write DDL queries -- but ActiveRecord has better tools for figuring out which migrations have already been applied.)
|
29
29
|
email: geeksam@gmail.com
|
@@ -48,24 +48,24 @@ files:
|
|
48
48
|
- lib/core_exts/object.rb
|
49
49
|
- lib/core_exts/symbol.rb
|
50
50
|
- lib/sqrbl.rb
|
51
|
-
- lib/sqrbl/
|
51
|
+
- lib/sqrbl/base_conversion_writer.rb
|
52
52
|
- lib/sqrbl/call_stack.rb
|
53
|
+
- lib/sqrbl/conversion.rb
|
53
54
|
- lib/sqrbl/group.rb
|
54
|
-
- lib/sqrbl/
|
55
|
-
- lib/sqrbl/migration.rb
|
55
|
+
- lib/sqrbl/individual_conversion_writer.rb
|
56
56
|
- lib/sqrbl/mixins/expects_block_with_new.rb
|
57
57
|
- lib/sqrbl/mixins/has_todos.rb
|
58
58
|
- lib/sqrbl/mixins/method_missing_delegation.rb
|
59
59
|
- lib/sqrbl/step.rb
|
60
60
|
- lib/sqrbl/step_pair.rb
|
61
|
-
- lib/sqrbl/
|
61
|
+
- lib/sqrbl/unified_conversion_writer.rb
|
62
62
|
- spec/README.txt
|
63
|
-
- spec/functional/
|
64
|
-
- spec/functional/
|
65
|
-
- spec/functional/
|
63
|
+
- spec/functional/base_conversion_writer_spec.rb
|
64
|
+
- spec/functional/individual_conversion_writer_spec.rb
|
65
|
+
- spec/functional/unified_conversion_writer_spec.rb
|
66
66
|
- spec/spec_helper.rb
|
67
|
+
- spec/unit/conversion_spec.rb
|
67
68
|
- spec/unit/group_spec.rb
|
68
|
-
- spec/unit/migration_spec.rb
|
69
69
|
- spec/unit/sqrbl_spec.rb
|
70
70
|
- spec/unit/step_pair_spec.rb
|
71
71
|
- spec/unit/step_spec.rb
|
@@ -110,6 +110,6 @@ rubyforge_project: sqrbl
|
|
110
110
|
rubygems_version: 1.3.4
|
111
111
|
signing_key:
|
112
112
|
specification_version: 3
|
113
|
-
summary: "SQrbL was created to help manage an extremely specific problem: managing SQL-based database
|
113
|
+
summary: "SQrbL was created to help manage an extremely specific problem: managing SQL-based database conversions"
|
114
114
|
test_files: []
|
115
115
|
|
@@ -1,12 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
2
|
-
|
3
|
-
include Sqrbl
|
4
|
-
|
5
|
-
describe BaseMigrationWriter do
|
6
|
-
it "should know what its subclasses are" do
|
7
|
-
BaseMigrationWriter.subclasses.should == [
|
8
|
-
IndividualMigrationWriter,
|
9
|
-
UnifiedMigrationWriter,
|
10
|
-
]
|
11
|
-
end
|
12
|
-
end
|