turntables 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +3 -0
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/.travis.yml +6 -0
- data/.yardopts +1 -0
- data/ChangeLog.rdoc +10 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +141 -0
- data/Rakefile +33 -0
- data/bin/turntables +12 -0
- data/lib/turntables/constants/repository_constants.rb +6 -0
- data/lib/turntables/db_registry.rb +83 -0
- data/lib/turntables/repository.rb +148 -0
- data/lib/turntables/sql_modules/db_registry_sql.rb +7 -0
- data/lib/turntables/sql_modules/version_history_sql.rb +30 -0
- data/lib/turntables/transaction.rb +39 -0
- data/lib/turntables/turntable.rb +40 -0
- data/lib/turntables/turntable_exception.rb +7 -0
- data/lib/turntables/version.rb +4 -0
- data/lib/turntables/version_history.rb +90 -0
- data/lib/turntables.rb +2 -0
- data/spec/data/locations/.gitkeep +0 -0
- data/spec/data/locations/herp.db +0 -0
- data/spec/data/locations/loc1/.gitkeep +0 -0
- data/spec/data/malformed-dir/malformed.txt +1 -0
- data/spec/data/sql-just-monolithic/mono/1.sql +6 -0
- data/spec/data/sql-just-monolithic/seq/.gitkeep +0 -0
- data/spec/data/sql-just-sequential/mono/.gitkeep +0 -0
- data/spec/data/sql-just-sequential/seq/1.sql +6 -0
- data/spec/data/sql-just-sequential/seq/2.sql +7 -0
- data/spec/data/sql-just-sequential/seq/3.sql +9 -0
- data/spec/data/sql-seq-and-mono/mono/3.sql +24 -0
- data/spec/data/sql-seq-and-mono/seq/1.sql +6 -0
- data/spec/data/sql-seq-and-mono/seq/2.sql +7 -0
- data/spec/data/sql-seq-and-mono/seq/3.sql +9 -0
- data/spec/db_registry_sql_spec.rb +9 -0
- data/spec/repository_constants_spec.rb +13 -0
- data/spec/repository_spec.rb +39 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/turntable_exception_spec.rb +16 -0
- data/spec/turntable_spec.rb +71 -0
- data/spec/turntables_spec.rb +8 -0
- data/spec/version_history_spec.rb +55 -0
- data/spec/version_history_sql_spec.rb +31 -0
- data/turntables.gemspec +26 -0
- metadata +184 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f3b7cb827f0bb39e3cc31e2ead6a8d271e3068bf
|
4
|
+
data.tar.gz: bd4970ab7927b33a741e17b331056d03b81e388d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 09104fc43b8e05ca2a45798b0d3742c3f70189e95cfcbfa4779e2dd8ed42b18b49b4a2e9ffe03055133523b53dff4b9d81f258048ad15eadd8986753fd72725e
|
7
|
+
data.tar.gz: 32404ef39e95bb8dd4a61e1f1faa5add76b315b9a9607c146615e29b78a9fb8adb525f795e3145d793dd9916ff6fc7d030236e39afbcffb3d4f2d3af0aa619ad
|
data/.document
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour --format documentation
|
data/.travis.yml
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup rdoc --title "turntables Documentation" --protected
|
data/ChangeLog.rdoc
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
=== 1.0.0 / 2013-07-03
|
2
|
+
|
3
|
+
* Initial release:
|
4
|
+
* Can create db versioning with sequential transactions
|
5
|
+
* Can create db versioning with monolithic transactions
|
6
|
+
* Can detect malformed dirs / repos of said sql
|
7
|
+
|
8
|
+
* TODO
|
9
|
+
* Want to have a way to set the database name
|
10
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 psyomn
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
= turntables {<img src="https://codeclimate.com/github/psyomn/turntables.png" />}[https://codeclimate.com/github/psyomn/turntables] {<img src="https://travis-ci.org/psyomn/turntables.png?branch=master" alt="Build Status" />}[https://travis-ci.org/psyomn/turntables]
|
2
|
+
|
3
|
+
* {Source}[http://github.com/psyomn/turntables]
|
4
|
+
* {Homepage}[https://rubygems.org/gems/turntables]
|
5
|
+
* {Documentation}[http://rubydoc.info/gems/turntables/frames]
|
6
|
+
|
7
|
+
== Description
|
8
|
+
|
9
|
+
This is not really ready, but putting it out there because I actually need it
|
10
|
+
for something else I'm working on. If you can give a hand it will be very
|
11
|
+
appreciated (contact me).
|
12
|
+
|
13
|
+
== Features
|
14
|
+
|
15
|
+
== Examples
|
16
|
+
This is a simple tutorial on how to invoke the database manager.
|
17
|
+
|
18
|
+
You need a directory structure like this:
|
19
|
+
|
20
|
+
.
|
21
|
+
|-- main.rb
|
22
|
+
`-- sql
|
23
|
+
|-- mono
|
24
|
+
`-- seq
|
25
|
+
|-- 1.sql
|
26
|
+
|-- 2.sql
|
27
|
+
|-- 3.sql
|
28
|
+
`-- 4.sql
|
29
|
+
|
30
|
+
The sql files can be like this for example. The comments that are prepended
|
31
|
+
with '--$' are inserted in the version history table within the table manager
|
32
|
+
so that if someone needs to diagnose issues on the long run, there are the
|
33
|
+
comments there at least (along with a version, and date field).
|
34
|
+
|
35
|
+
1.sql:
|
36
|
+
|
37
|
+
--$ First version will be created with this. The comments to be added in the
|
38
|
+
--$ versions history table can be prepended with the '$' sign in order for you
|
39
|
+
--$ specify comments (if needed). It's here if you want it.
|
40
|
+
-- This is an example file to show how the turntables gem could work maybe
|
41
|
+
-- with people. and stuff.
|
42
|
+
-- This SQL file adds the person, and work_descriptions entities to the
|
43
|
+
-- database.
|
44
|
+
CREATE TABLE persons (
|
45
|
+
id integer primary key autoincrement,
|
46
|
+
name varchar(50),
|
47
|
+
surname varchar(50),
|
48
|
+
age integer
|
49
|
+
);
|
50
|
+
|
51
|
+
CREATE TABLE work_descriptions (
|
52
|
+
id integer primary key autoincrement,
|
53
|
+
description text
|
54
|
+
);
|
55
|
+
|
56
|
+
2.sql:
|
57
|
+
|
58
|
+
--$ Bump to version 2. This is starting to look good!
|
59
|
+
-- And this is for the second revision of the database.
|
60
|
+
|
61
|
+
CREATE TABLE inventory (
|
62
|
+
id integer primary key autoincrement,
|
63
|
+
description text
|
64
|
+
);
|
65
|
+
|
66
|
+
3.sql:
|
67
|
+
|
68
|
+
--$ We did a mistake. We forgot to add a field to the persons table. The extra
|
69
|
+
--$ field to be added is the DOB.
|
70
|
+
ALTER TABLE persons ADD COLUMN dob BIGINT;
|
71
|
+
|
72
|
+
4.sql:
|
73
|
+
|
74
|
+
--$ This table is added because now the system requires accounts as well.
|
75
|
+
--$ Sat Jul 13 21:45:40 EDT 2013
|
76
|
+
-- Table to create accounts for people.
|
77
|
+
CREATE TABLE accounts (
|
78
|
+
balance float,
|
79
|
+
owner_id integer,
|
80
|
+
FOREIGN KEY(owner_id) references persons(id)
|
81
|
+
);
|
82
|
+
|
83
|
+
main.rb should be like this:
|
84
|
+
|
85
|
+
require 'turntables'
|
86
|
+
|
87
|
+
include Turntables
|
88
|
+
|
89
|
+
puts "This is to test the library and proof of concept of turntables"
|
90
|
+
|
91
|
+
# Ideal way of calling this library
|
92
|
+
turntable = Turntable.new
|
93
|
+
|
94
|
+
# sql is a directory containing two subdirs: seq, mono
|
95
|
+
# seq contains sequential transactions
|
96
|
+
# mono contains monolithic transactions
|
97
|
+
turntable.register('sql')
|
98
|
+
turntable.make!
|
99
|
+
|
100
|
+
# Note: There should not be any other calls to the library. We give it the
|
101
|
+
# sql repository location, and then it's the responsibility of Turntables to
|
102
|
+
# decide whether to take action or not.
|
103
|
+
|
104
|
+
If you require that the database be made at a specified location, you should use
|
105
|
+
the method `make_at!(location)`. To do this, just do what the above code does
|
106
|
+
until `make!`. Then you need to call `make_at!` in the following way:
|
107
|
+
|
108
|
+
turntable.make_at!("path/to/database.db")
|
109
|
+
|
110
|
+
Right now versions should be marked as the filename. Try to have intergers as
|
111
|
+
versions, to avoid odd behaviour. So in this case versions {1,2,3} would
|
112
|
+
respectively be in file form {1.sql, 2.sql, 3.sql}.
|
113
|
+
|
114
|
+
The monolithic transactions should be a combinations of all the versions, up to
|
115
|
+
the one it denotes. In other words, version 4 would be the combination of all
|
116
|
+
previous ones including itself {1.sql, 2.sql, 3.sql, 4.sql}.
|
117
|
+
|
118
|
+
== Requirements
|
119
|
+
|
120
|
+
* I've tested this with Ruby Versions = {2.0, 1.9.3}, though you should not
|
121
|
+
really have any issues in theory.
|
122
|
+
|
123
|
+
* SQLite3 gem
|
124
|
+
|
125
|
+
== Install
|
126
|
+
|
127
|
+
$ gem install turntables
|
128
|
+
|
129
|
+
== Synopsis
|
130
|
+
|
131
|
+
$ turntables
|
132
|
+
|
133
|
+
== Copyright
|
134
|
+
|
135
|
+
Copyright (c) 2013 psyomn
|
136
|
+
|
137
|
+
== License
|
138
|
+
|
139
|
+
License is MIT regardless what you may see or find elsewhere; I might have not
|
140
|
+
changed it due to lack of time.
|
141
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bundler'
|
7
|
+
rescue LoadError => e
|
8
|
+
warn e.message
|
9
|
+
warn "Run `gem install bundler` to install Bundler."
|
10
|
+
exit -1
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
Bundler.setup(:development)
|
15
|
+
rescue Bundler::BundlerError => e
|
16
|
+
warn e.message
|
17
|
+
warn "Run `bundle install` to install missing gems."
|
18
|
+
exit e.status_code
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake'
|
22
|
+
|
23
|
+
require 'rspec/core/rake_task'
|
24
|
+
RSpec::Core::RakeTask.new
|
25
|
+
|
26
|
+
task :test => :spec
|
27
|
+
task :default => :spec
|
28
|
+
|
29
|
+
require "bundler/gem_tasks"
|
30
|
+
|
31
|
+
require 'yard'
|
32
|
+
YARD::Rake::YardocTask.new
|
33
|
+
task :doc => :yard
|
data/bin/turntables
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'sqlite3'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
require 'turntables/sql_modules/db_registry_sql'
|
5
|
+
|
6
|
+
module Turntables
|
7
|
+
# Database Registry pattern that connects to an sqlite 3 database.
|
8
|
+
# @author Simon Symeonidis
|
9
|
+
class DbRegistry
|
10
|
+
include Singleton
|
11
|
+
include DbRegistrySql
|
12
|
+
|
13
|
+
# Init with default db name
|
14
|
+
# @param dbname is the name of the database is if it not specified
|
15
|
+
# TODO we need to be able to set this somehow differently - applications
|
16
|
+
# might require to name their database with their own specific name.
|
17
|
+
def initialize(dbname="default.db")
|
18
|
+
@handle = SQLite3::Database.new(dbname)
|
19
|
+
@name = dbname
|
20
|
+
end
|
21
|
+
|
22
|
+
# Execute (any sort) of sql
|
23
|
+
# @param sql is the multiple arguments of the function to execute. Usually
|
24
|
+
# you should give it first the sql you want that is to be prepared. Then
|
25
|
+
# you specify the variables to be set in the query, in the right order.
|
26
|
+
# @example Simple usage
|
27
|
+
# sql = "INSERT INTO person (name, surname) values (?,?)"
|
28
|
+
# DbRegistry.instance.execute(sql,"jon","doe")
|
29
|
+
# @return sql data
|
30
|
+
def execute(*sql)
|
31
|
+
@handle.execute(*sql)
|
32
|
+
rescue => ex
|
33
|
+
puts ex.message
|
34
|
+
puts ex.backtrace
|
35
|
+
puts "Offending sql: "
|
36
|
+
puts sql
|
37
|
+
end
|
38
|
+
|
39
|
+
# For special queries that may contain multiple statements. For example a
|
40
|
+
# query that contains first a 'create table' query, and then some inserts to
|
41
|
+
# poppulate that table. Ideally this should be used in order to create the
|
42
|
+
# tables in sequence.
|
43
|
+
# @param sql is the sql that contains multiple statements
|
44
|
+
def execute_batch(sql)
|
45
|
+
@handle.execute_batch(sql)
|
46
|
+
rescue => ex
|
47
|
+
puts ex.message
|
48
|
+
puts ex.backtrace
|
49
|
+
puts "Offending sql: "
|
50
|
+
puts sql
|
51
|
+
end
|
52
|
+
|
53
|
+
# Check if a table exists in the database
|
54
|
+
# @param name is the name of the table to check if exists
|
55
|
+
# @return true if table exists, false if not
|
56
|
+
def table_exists?(name)
|
57
|
+
val = @handle.execute(ExistsSql, "table", name)
|
58
|
+
1 == val.flatten[0]
|
59
|
+
end
|
60
|
+
|
61
|
+
# Close the current database.
|
62
|
+
# @warn This is mainly here for the rspec testing, and should not be used
|
63
|
+
# unless you really know what you're doing.
|
64
|
+
def close!
|
65
|
+
@handle.close unless @handle.closed?
|
66
|
+
end
|
67
|
+
|
68
|
+
# Open the database, with the name given previously
|
69
|
+
# @warn This is mainly here for the rspec testing, and should not be used
|
70
|
+
# unless you really know what you're doing.
|
71
|
+
def open!
|
72
|
+
@handle = SQLite3::Database.new(@name) if @handle.closed?
|
73
|
+
end
|
74
|
+
|
75
|
+
# The database name
|
76
|
+
attr_accessor :name
|
77
|
+
|
78
|
+
private
|
79
|
+
# Other classes should not use the database handle directly
|
80
|
+
attr :handle
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
# lib
|
4
|
+
require 'turntables/transaction'
|
5
|
+
require 'turntables/version_history'
|
6
|
+
require 'turntables/db_registry'
|
7
|
+
require 'turntables/sql_modules/version_history_sql'
|
8
|
+
require 'turntables/constants/repository_constants'
|
9
|
+
|
10
|
+
module Turntables
|
11
|
+
# @author Simon Symeonidis
|
12
|
+
# A turntables repository. This class is responsible for handling the
|
13
|
+
# versioning. This includes tasks such as checking database availability,
|
14
|
+
# and pulling up the version history table and modifying where needed (for
|
15
|
+
# example when completing a transaction, recording the date, the new version,
|
16
|
+
# and comments about said transaction).
|
17
|
+
class Repository
|
18
|
+
include RepositoryConstants
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@transactions = Array.new
|
22
|
+
@monolithics = Array.new
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param location is the location of the sql repository for now (a directory
|
26
|
+
# for now).
|
27
|
+
def register(location)
|
28
|
+
@relative_dir = location
|
29
|
+
@sequential_dir = "#{@relative_dir}/#{SeqDir}/"
|
30
|
+
@monolithic_dir = "#{@relative_dir}/#{MonoDir}/"
|
31
|
+
|
32
|
+
# Initialize the transactions
|
33
|
+
init_sequential_transactions!
|
34
|
+
init_monolithic_transactions!
|
35
|
+
end
|
36
|
+
|
37
|
+
# Function to call in order to make the database.
|
38
|
+
#
|
39
|
+
# TODO: Here, it should detect in what state the current database is in, and
|
40
|
+
# go from there. In other words, whether it can skip sequential database
|
41
|
+
# transactions by loading a monolithic one to exclude previous transactions.
|
42
|
+
def make!
|
43
|
+
select_transactions!
|
44
|
+
@transactions.each do |transaction|
|
45
|
+
vh = VersionHistory.new(transaction.version, transaction.comment)
|
46
|
+
VersionHistory.insert(vh)
|
47
|
+
DbRegistry.instance.execute_batch(transaction.data)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Check to see if the directory structure is malformed.
|
52
|
+
# @return true if the directory structure is malformed
|
53
|
+
def malformed?
|
54
|
+
abs_seq = File.expand_path(@sequential_dir)
|
55
|
+
abs_mon = File.expand_path(@monolithic_dir)
|
56
|
+
!(File.exists?(abs_seq) && File.exists?(abs_mon))
|
57
|
+
end
|
58
|
+
|
59
|
+
attr_accessor :sequential_dir
|
60
|
+
attr_accessor :monolithic_dir
|
61
|
+
attr_accessor :relative_dir
|
62
|
+
# Array<Turntables::Transaction>
|
63
|
+
attr_accessor :transactions
|
64
|
+
# Array<Turntables::Transaction>
|
65
|
+
attr_accessor :monolithics
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Depending on what has been done before, we need to choose the proper
|
70
|
+
# transactions.
|
71
|
+
# TODO: This probably can be done cleaner
|
72
|
+
def select_transactions!
|
73
|
+
check = VersionHistory.check
|
74
|
+
if check == :fresh
|
75
|
+
# Fresh db means, we create the version history table
|
76
|
+
prepend_monolithic_transactions!
|
77
|
+
VersionHistory.pull_up!
|
78
|
+
else
|
79
|
+
last_version = VersionHistory.find_last.version
|
80
|
+
@transactions.select!{|tr| tr.version > last_version}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# This checks to see if any monolithic transactions exist, which can
|
85
|
+
# eliminate previous sequential transactions.
|
86
|
+
def prepend_monolithic_transactions!
|
87
|
+
max = @monolithics.max_by &:version
|
88
|
+
unless max.nil?
|
89
|
+
@transactions.select!{|tr| tr.version > max.version}
|
90
|
+
@transactions = @transactions.unshift(max)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Find all the transactions that are to be processed sequentially
|
95
|
+
# @return nil
|
96
|
+
def init_sequential_transactions!
|
97
|
+
init_generic_transactions(sequential_files, @transactions)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Find all the transactions that are to be processed only once
|
101
|
+
# @return nil
|
102
|
+
def init_monolithic_transactions!
|
103
|
+
init_generic_transactions(monolithic_files, @monolithics)
|
104
|
+
end
|
105
|
+
|
106
|
+
# this is a generic transaction reader
|
107
|
+
# return nil
|
108
|
+
def init_generic_transactions(file_list, transaction_holder)
|
109
|
+
file_list.each do |path|
|
110
|
+
data = File.open(path).read
|
111
|
+
filename = path.split(/\//).last
|
112
|
+
transaction_holder.push Transaction.new(data,filename)
|
113
|
+
end
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
|
117
|
+
# Get the sequential transactional files
|
118
|
+
def sequential_files
|
119
|
+
get_files_in_dir(@sequential_dir)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Get the monolithic transactional files
|
123
|
+
def monolithic_files
|
124
|
+
get_files_in_dir(@monolithic_dir)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Return the files that are in the given directory
|
128
|
+
# @param path is the path to look for files in the directory
|
129
|
+
# @return Array<String> of files sorted by stringnum_comparison predicate
|
130
|
+
def get_files_in_dir(path)
|
131
|
+
Dir["#{path}*"].sort!{|e1,e2| stringnum_comparison(e1,e2)}
|
132
|
+
end
|
133
|
+
|
134
|
+
# Compare two strings with each other by extracting the digits
|
135
|
+
def stringnum_comparison(a,b)
|
136
|
+
extract_digits(a) <=> extract_digits(b)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Extract the numbers from the string, and convert to Fixnum
|
140
|
+
# @param string is the string to extract the numbers from
|
141
|
+
# @return the number that was found in the string
|
142
|
+
def extract_digits(string)
|
143
|
+
string.gsub(/\D/,'').to_i
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Turntables
|
2
|
+
# @author Simon Symeonidis
|
3
|
+
# Some static information factored out to avoid class variables, and to
|
4
|
+
# separate concerns.
|
5
|
+
module VersionHistorySql
|
6
|
+
# Table name of this guy
|
7
|
+
TableName = "version_histories"
|
8
|
+
|
9
|
+
# Table schema for the version history table.
|
10
|
+
Create = "CREATE TABLE #{TableName} ("\
|
11
|
+
"id INTEGER PRIMARY KEY AUTOINCREMENT, "\
|
12
|
+
"version BIGINT, "\
|
13
|
+
"date BIGINT, "\
|
14
|
+
"comment TEXT)"
|
15
|
+
|
16
|
+
# Select last inserted transaction
|
17
|
+
SelectLast = "SELECT * FROM #{TableName} "\
|
18
|
+
" WHERE id=(SELECT MAX(id) FROM #{TableName});"
|
19
|
+
|
20
|
+
# Select a record by id
|
21
|
+
SelectById = "SELECT * FROM #{TableName} WHERE id=?"
|
22
|
+
|
23
|
+
# Select all the records
|
24
|
+
SelectAll = "SELECT * FROM #{TableName}"
|
25
|
+
|
26
|
+
# Sql to insert a version history into the table
|
27
|
+
Insert = "INSERT INTO #{TableName} (version,date,comment)"\
|
28
|
+
" values (?,?,?)"
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Turntables
|
2
|
+
# @author Simon Symeonidis
|
3
|
+
# @date Wed Jul 10 19:51:42 EDT 2013
|
4
|
+
# This class takes care of a single transaction
|
5
|
+
class Transaction
|
6
|
+
|
7
|
+
# Initialize this object with the contents of the sql file
|
8
|
+
# @param sql_file_contents are the contents of the given sql file
|
9
|
+
# @param filename is the filename of the given sql file. We use the filenames
|
10
|
+
# for the version that it is supposed to upgrade to.
|
11
|
+
def initialize(sql_file_contents,filename)
|
12
|
+
# Select only the lines that begin with '--$'
|
13
|
+
@comment = sql_file_contents.lines.select{|el| el.match(/--\$/)}.join
|
14
|
+
@comment.gsub!(/--\$/, '')
|
15
|
+
@version = filename.to_i
|
16
|
+
@data = sql_file_contents
|
17
|
+
end
|
18
|
+
|
19
|
+
# The version should be obtained from the file name of the respective sql
|
20
|
+
# file.
|
21
|
+
attr_accessor :version
|
22
|
+
|
23
|
+
# The comment should be parsed out from the respective sql file. We want to
|
24
|
+
# keep the comments in the sql file to keep things organized. We do not want
|
25
|
+
# to alter the sql language to fit our needs. Therefore we just require the
|
26
|
+
# user to comment lines as '--$ my comment here' in order to parse them out
|
27
|
+
# @example How to write comments that are to be parsed
|
28
|
+
# --$ author jon doe
|
29
|
+
# --$ This sql file will update the schema to version 1.2
|
30
|
+
# --$ You should note this and that
|
31
|
+
# -- This commen line would be ignored
|
32
|
+
#
|
33
|
+
# CREATE TABLE accounts ( ... )
|
34
|
+
attr_accessor :comment
|
35
|
+
|
36
|
+
# The sql information to be passed on to the db registry
|
37
|
+
attr_accessor :data
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'turntables/db_registry'
|
2
|
+
require 'turntables/repository'
|
3
|
+
require 'turntables/turntable_exception'
|
4
|
+
|
5
|
+
module Turntables
|
6
|
+
# @author Simon Symeonidis
|
7
|
+
# The facade controller to the rest of this library.
|
8
|
+
class Turntable
|
9
|
+
# Default constructor, that initializes some standard parameters
|
10
|
+
def initialize
|
11
|
+
@revisions = Array.new
|
12
|
+
@repository = Repository.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# Register a revision that needs to be processed later
|
16
|
+
# @param repository_root_path is the root path to all the sql.
|
17
|
+
def register(repository_root_path)
|
18
|
+
@repository.register(repository_root_path)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Make the repository at a specific location instead of default.
|
22
|
+
def make_at!(location)
|
23
|
+
DbRegistry.instance.close!
|
24
|
+
DbRegistry.instance.name = location
|
25
|
+
DbRegistry.instance.open!
|
26
|
+
make!
|
27
|
+
end
|
28
|
+
|
29
|
+
# Create the tables by going through each revision
|
30
|
+
def make!
|
31
|
+
if @repository.malformed?
|
32
|
+
raise TurntableException, "The directory structure is malformed."
|
33
|
+
else
|
34
|
+
@repository.make!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_accessor :repository
|
39
|
+
end
|
40
|
+
end
|