prick 0.3.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/TODO +7 -0
- data/exe/prick +235 -163
- data/lib/ext/fileutils.rb +7 -0
- data/lib/prick.rb +7 -5
- data/lib/prick/builder.rb +31 -8
- data/lib/prick/cache.rb +34 -0
- data/lib/prick/constants.rb +106 -54
- data/lib/prick/database.rb +26 -20
- data/lib/prick/diff.rb +103 -25
- data/lib/prick/git.rb +31 -9
- data/lib/prick/head.rb +183 -0
- data/lib/prick/migration.rb +41 -181
- data/lib/prick/program.rb +255 -0
- data/lib/prick/project.rb +264 -0
- data/lib/prick/rdbms.rb +3 -12
- data/lib/prick/schema.rb +6 -11
- data/lib/prick/state.rb +129 -74
- data/lib/prick/version.rb +41 -28
- data/prick.gemspec +3 -1
- data/share/diff/diff.after-tables.sql +4 -0
- data/share/diff/diff.before-tables.sql +4 -0
- data/share/diff/diff.tables.sql +8 -0
- data/share/migration/diff.tables.sql +8 -0
- data/share/{release_migration → migration}/features.yml +0 -0
- data/share/migration/migrate.sql +3 -0
- data/share/{release_migration → migration}/migrate.yml +3 -0
- data/share/migration/tables.sql +3 -0
- data/share/{schemas → schema/schema}/build.yml +0 -0
- data/share/{schemas → schema/schema}/prick/build.yml +0 -0
- data/share/schema/schema/prick/data.sql +7 -0
- data/share/{schemas → schema/schema}/prick/schema.sql +0 -0
- data/share/{schemas → schema/schema}/prick/tables.sql +2 -2
- data/share/{schemas → schema/schema}/public/.keep +0 -0
- data/share/{schemas → schema/schema}/public/build.yml +0 -0
- data/share/{schemas → schema/schema}/public/schema.sql +0 -0
- data/test_refactor +34 -0
- metadata +23 -21
- data/file +0 -0
- data/lib/prick/build.rb +0 -376
- data/lib/prick/migra.rb +0 -22
- data/share/feature_migration/diff.sql +0 -2
- data/share/feature_migration/migrate.sql +0 -2
- data/share/release_migration/diff.sql +0 -3
- data/share/release_migration/migrate.sql +0 -5
- data/share/schemas/prick/data.sql +0 -7
data/lib/prick/rdbms.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'prick/command.rb'
|
2
|
-
#require 'prick/ensure.rb'
|
3
2
|
|
4
3
|
require 'csv'
|
5
4
|
|
@@ -15,7 +14,9 @@ module Prick
|
|
15
14
|
{
|
16
15
|
echo "set role #{user};"
|
17
16
|
echo "set search_path to public;"
|
18
|
-
|
17
|
+
cat <<'EOF'
|
18
|
+
#{sql}
|
19
|
+
EOF
|
19
20
|
} | psql --csv --tuples-only --quiet -v ON_ERROR_STOP=1 -d #{db}
|
20
21
|
)
|
21
22
|
CSV.new(stdout.join("\n")).read
|
@@ -131,16 +132,6 @@ module Prick
|
|
131
132
|
data_opt = (data ? "" : "--schema-only")
|
132
133
|
Command.command "pg_dump --no-owner #{data_opt} #{db} | gzip --to-stdout >#{file}"
|
133
134
|
end
|
134
|
-
|
135
|
-
private
|
136
|
-
@ensure_states = {
|
137
|
-
exist_user: [:create_user, :drop_user],
|
138
|
-
exist_database: [
|
139
|
-
lambda { |this, db, user| this.ensure_state(:exist_user, user) },
|
140
|
-
lambda { |this, db, user| this.create_database(db, owner: user) },
|
141
|
-
:drop_database
|
142
|
-
]
|
143
|
-
}
|
144
135
|
end
|
145
136
|
end
|
146
137
|
|
data/lib/prick/schema.rb
CHANGED
@@ -7,24 +7,19 @@ module Prick
|
|
7
7
|
BUILD_SQL_FILE = BUILD_BASE_NAME + ".sql"
|
8
8
|
BUILD_YML_FILE = BUILD_BASE_NAME + ".yml"
|
9
9
|
|
10
|
-
# Note this models the
|
10
|
+
# Note this models the SCHEMA_DIR directory, not a database schema
|
11
11
|
class Schema
|
12
|
-
|
13
|
-
def yml_file() SchemaBuilder.yml_file(directory) end
|
12
|
+
def yml_file() SchemaBuilder.yml_file(SCHEMA_DIR) end
|
14
13
|
|
15
|
-
def version() SchemaVersion.
|
16
|
-
def version=(version) SchemaVersion.new(
|
17
|
-
def version_file() SchemaVersion.new
|
18
|
-
|
19
|
-
def initialize(directory = SCHEMAS_DIR)
|
20
|
-
@directory = directory
|
21
|
-
end
|
14
|
+
def version() SchemaVersion.load end
|
15
|
+
def version=(version) SchemaVersion.new(version).write end
|
16
|
+
def version_file() SchemaVersion.new.path end
|
22
17
|
|
23
18
|
def built?(database) database.exist? && database.version == version end
|
24
19
|
|
25
20
|
# `subject` can be a subpath of schema/ (ie. 'public/tables')
|
26
21
|
def build(database, subject = nil)
|
27
|
-
SchemaBuilder.new(database,
|
22
|
+
SchemaBuilder.new(database, SCHEMA_DIR).build(subject)
|
28
23
|
end
|
29
24
|
end
|
30
25
|
end
|
data/lib/prick/state.rb
CHANGED
@@ -2,25 +2,119 @@
|
|
2
2
|
require 'yaml'
|
3
3
|
|
4
4
|
module Prick
|
5
|
+
# General interface for prick state files. Comments are automatically removed
|
5
6
|
class PrickFile
|
6
7
|
attr_reader :path
|
7
8
|
def initialize(path) @path = path end
|
8
9
|
def exist?() File.exist?(path) end
|
10
|
+
def self.exist?() self.new.exist? end # Require an #initializer with no arguments
|
11
|
+
|
12
|
+
def self.load(*args) self.new(*args).read end
|
13
|
+
|
14
|
+
def self.read() raise NotThis end
|
9
15
|
|
10
16
|
protected
|
17
|
+
# Read file from disk or from the given branch or tag
|
11
18
|
def general_read(method, branch: nil, tag: nil)
|
19
|
+
!(branch && tag) or raise Internal, "Not both of `branch` and `tag` can be defined"
|
12
20
|
branch || tag ? Git.send(method, path, branch: branch, tag: tag) : File.open(path, "r").send(method)
|
13
21
|
end
|
22
|
+
|
14
23
|
def do_read(**opts) general_read(:read, **opts) end
|
15
|
-
def
|
24
|
+
def do_readline(**opts) do_readlines(**opts).first end
|
25
|
+
def do_readlines(**opts) general_read(:readlines, **opts).reject { |l| l =~ /^\s*#/ } end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Models the .prick-version file. It contains just one line with the version
|
29
|
+
# of prick itself
|
30
|
+
class PrickVersion < PrickFile
|
31
|
+
def initialize() super PRICK_VERSION_FILE end
|
32
|
+
|
33
|
+
# Return the version
|
34
|
+
def read(branch: nil, tag: nil)
|
35
|
+
Version.new(do_readline(branch: branch, tag: tag).chomp.sub(/^prick-/, ""))
|
36
|
+
end
|
37
|
+
|
38
|
+
# Write prick version
|
39
|
+
def write(version)
|
40
|
+
File.open(path, "w") { |file| file.puts "prick-#{version}" }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Models the schema version that is stored in the data.sql file in the prick
|
45
|
+
# schema directory. Note that SchemaVersion caches the version. Use #clear to
|
46
|
+
# reset the cache
|
47
|
+
class SchemaVersion < PrickFile
|
48
|
+
def initialize(version = nil)
|
49
|
+
@version = version
|
50
|
+
super SCHEMA_VERSION_PATH
|
51
|
+
end
|
52
|
+
|
53
|
+
def clear() @version = nil end
|
54
|
+
|
55
|
+
def create() raise Internal, "This should not happen" end
|
56
|
+
|
57
|
+
def read(**opts)
|
58
|
+
return @version if @version
|
59
|
+
lines = do_readlines
|
60
|
+
while !lines.empty?
|
61
|
+
line = lines.shift.chomp
|
62
|
+
next if line != COPY_STMT
|
63
|
+
data = lines.shift.chomp
|
64
|
+
a = data.split("\t").map { |val| parse_sql_literal(val) }
|
65
|
+
a.size == FIELDS.size + 1 or raise Fail, "Illegal data format in #{path}"
|
66
|
+
fork, major, minor, patch, pre, feature, version = *a[1..-1]
|
67
|
+
@version = Version.new("0.0.0", fork: fork, feature: feature)
|
68
|
+
@version.major = major
|
69
|
+
@version.minor = minor
|
70
|
+
@version.patch = patch
|
71
|
+
@version.pre = pre
|
72
|
+
return @version
|
73
|
+
end
|
74
|
+
raise Fail, "No COPY statement in #{path}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def write(version = @version)
|
78
|
+
# puts "Writing #{path}"
|
79
|
+
version_string = version.truncate(:pre).to_s
|
80
|
+
File.open(path, "w") { |f|
|
81
|
+
f.puts "--"
|
82
|
+
f.puts "-- This file is auto-generated by prick(1). Please don't touch"
|
83
|
+
f.puts COPY_STMT
|
84
|
+
f.print \
|
85
|
+
"1\t",
|
86
|
+
FIELDS[..-2].map { |f| version.send(f.to_sym) || '\N' }.join("\t"),
|
87
|
+
"\t#{version_string}\n"
|
88
|
+
f.puts "\\."
|
89
|
+
}
|
90
|
+
Git.add(path)
|
91
|
+
version
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
FIELDS = %w(fork major minor patch pre feature version)
|
96
|
+
COPY_STMT = "COPY prick.versions (id, #{FIELDS.join(', ')}) FROM stdin;"
|
97
|
+
|
98
|
+
def parse_sql_literal(s)
|
99
|
+
case s
|
100
|
+
when '\N', 'null'; nil
|
101
|
+
when/^\d+$/; s.to_i
|
102
|
+
else
|
103
|
+
s
|
104
|
+
end
|
105
|
+
end
|
16
106
|
end
|
17
107
|
|
108
|
+
# General interface for prick state files in YAML format
|
18
109
|
class State < PrickFile
|
19
|
-
# `fields` is a Hash from field name (Symbol) to field type (class)
|
110
|
+
# `fields` is a Hash from field name (Symbol) to field type (class) where a
|
111
|
+
# class can be a class known to YAML (Integer, String, etc.) or Version.
|
112
|
+
# The #initialize method generates an accessor methods for each field
|
20
113
|
def initialize(path, **fields)
|
21
114
|
super(path)
|
22
115
|
@fields = fields
|
23
116
|
@fields.each_key { |k| self.class.attr_accessor k }
|
117
|
+
@loaded = false
|
24
118
|
end
|
25
119
|
|
26
120
|
def create() write end
|
@@ -29,23 +123,23 @@ module Prick
|
|
29
123
|
for field, value in fields
|
30
124
|
self.send(:"#{field}=", value)
|
31
125
|
end
|
126
|
+
self
|
32
127
|
end
|
33
128
|
|
34
129
|
def read(branch: nil, tag: nil)
|
35
|
-
if
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
42
|
-
else
|
43
|
-
raise NotYet
|
130
|
+
return self if @loaded
|
131
|
+
hash = YAML.load(do_read(branch: branch, tag: tag))
|
132
|
+
for field, klass in @fields
|
133
|
+
value = hash[field.to_s]
|
134
|
+
value = Version.new(value) if klass == Version && !value.nil?
|
135
|
+
self.instance_variable_set("@#{field}", value)
|
44
136
|
end
|
137
|
+
@loaded = true
|
45
138
|
self
|
46
139
|
end
|
47
140
|
|
48
|
-
def write
|
141
|
+
def write(**fields)
|
142
|
+
set(**fields)
|
49
143
|
hash = @fields.map { |field, klass|
|
50
144
|
value = self.send(field)
|
51
145
|
value = value.to_s if klass == Version && !value.nil?
|
@@ -58,80 +152,41 @@ module Prick
|
|
58
152
|
end
|
59
153
|
|
60
154
|
class MigrationState < State
|
61
|
-
def initialize(
|
62
|
-
super(
|
155
|
+
def initialize(version: nil, base_version: nil)
|
156
|
+
super(PRICK_MIGRATION_PATH, version: Version, base_version: Version)
|
63
157
|
set(version: version, base_version: base_version)
|
64
158
|
end
|
65
159
|
end
|
66
160
|
|
67
|
-
class
|
68
|
-
def initialize(
|
69
|
-
super(
|
70
|
-
set(
|
161
|
+
class HeadState < State
|
162
|
+
def initialize(name: nil)
|
163
|
+
super(PRICK_HEAD_PATH, name: String)
|
164
|
+
set(name: name)
|
71
165
|
end
|
72
166
|
end
|
73
167
|
|
74
|
-
class
|
75
|
-
def initialize(
|
76
|
-
super(
|
77
|
-
set(
|
168
|
+
class FeatureState < State
|
169
|
+
def initialize(feature: nil)
|
170
|
+
super(PRICK_FEATURE_PATH, feature: String)
|
171
|
+
set(feature: feature)
|
78
172
|
end
|
79
173
|
end
|
80
174
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
175
|
+
# class FeaturesState < State
|
176
|
+
# def initialize(directory, features: [])
|
177
|
+
# super(File.join(directory, FEATURES_STATE_FILE), features: Array)
|
178
|
+
# set(features: features)
|
179
|
+
# end
|
180
|
+
# end
|
181
|
+
#
|
87
182
|
|
88
|
-
|
89
|
-
|
183
|
+
# Models the .prick-project file that contains the name of the project and
|
184
|
+
# the database user
|
185
|
+
class ProjectState < State
|
186
|
+
def initialize(name: nil, user: nil)
|
187
|
+
super(PROJECT_STATE_FILE, name: String, user: String)
|
188
|
+
set(name: name, user: user)
|
90
189
|
end
|
91
190
|
end
|
92
191
|
|
93
|
-
class SchemaVersion < PrickFile
|
94
|
-
def initialize(schema = SCHEMAS_DIR) super(File.join(schema, "prick", "data.sql")) end
|
95
|
-
|
96
|
-
def read(**opts)
|
97
|
-
lines = do_readlines
|
98
|
-
while !lines.empty?
|
99
|
-
line = lines.shift.chomp
|
100
|
-
next if line != COPY_STMT
|
101
|
-
l = lines.shift.chomp
|
102
|
-
a = l.split("\t")[1..].map { |val| val == '\N' ? nil : val }
|
103
|
-
a.size == FIELDS.size or raise Fail, "Illegal data format in #{path}"
|
104
|
-
custom, major, minor, patch, pre = a[0], *a[1..-2].map { |val| val && val.to_i }
|
105
|
-
v = Version.new("0.0.0", custom: (custom == '\N' ? nil : custom))
|
106
|
-
v.major = major
|
107
|
-
v.minor = minor
|
108
|
-
v.patch = patch
|
109
|
-
v.pre = (pre == "null" ? nil : pre)
|
110
|
-
return v
|
111
|
-
end
|
112
|
-
raise Fail, "No COPY statement in #{path}"
|
113
|
-
end
|
114
|
-
|
115
|
-
def write(version)
|
116
|
-
version_string = version.truncate(:pre).to_s
|
117
|
-
File.open(path, "w") { |f|
|
118
|
-
f.puts "--"
|
119
|
-
f.puts "-- This file is auto-generated by prick(1). Please don't touch"
|
120
|
-
f.puts COPY_STMT
|
121
|
-
f.print \
|
122
|
-
"1\t",
|
123
|
-
FIELDS[..-2].map { |f| version.send(f.to_sym) || '\N' }.join("\t"),
|
124
|
-
"\t#{version_string}\n"
|
125
|
-
f.puts "\\."
|
126
|
-
}
|
127
|
-
Git.add(path)
|
128
|
-
end
|
129
|
-
|
130
|
-
def create() raise Internal, "This should not happen" end
|
131
|
-
|
132
|
-
private
|
133
|
-
FIELDS = %w(custom major minor patch pre feature version)
|
134
|
-
COPY_STMT = "COPY prick.versions (id, #{FIELDS.join(', ')}) FROM stdin;"
|
135
|
-
end
|
136
192
|
end
|
137
|
-
|
data/lib/prick/version.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
# Required by gem
|
6
6
|
module Prick
|
7
|
-
VERSION = "0.
|
7
|
+
VERSION = "0.8.0"
|
8
8
|
end
|
9
9
|
|
10
10
|
# Project related code starts here
|
@@ -21,9 +21,12 @@ module Prick
|
|
21
21
|
def zero?() self == Version.zero end
|
22
22
|
|
23
23
|
# Return true if `string` is a version
|
24
|
-
def self.version?(string)
|
24
|
+
def self.version?(string)
|
25
|
+
string.is_a?(String) or raise Internal, "String expected"
|
26
|
+
!(string =~ VERSION_RE).nil?
|
27
|
+
end
|
25
28
|
|
26
|
-
attr_accessor :
|
29
|
+
attr_accessor :fork
|
27
30
|
attr_accessor :semver
|
28
31
|
attr_accessor :feature
|
29
32
|
|
@@ -36,8 +39,8 @@ module Prick
|
|
36
39
|
def patch() @semver.patch end
|
37
40
|
def patch=(patch) @semver.patch = patch end
|
38
41
|
|
39
|
-
# Return true if this is a
|
40
|
-
def
|
42
|
+
# Return true if this is a fork release
|
43
|
+
def fork?() !@fork.nil? end
|
41
44
|
|
42
45
|
# Return true if this is a feature release
|
43
46
|
def feature?() !@feature.nil? end
|
@@ -47,13 +50,16 @@ module Prick
|
|
47
50
|
|
48
51
|
# Return true if this is a pre-release
|
49
52
|
def pre?() !@semver.pre.nil? end
|
53
|
+
def prerelease?() pre? end
|
50
54
|
|
51
55
|
# The releases is stored as a String (eg. 'pre.1') in the semantic version
|
52
56
|
# but #pre returns only the Integer number
|
53
57
|
def pre() @semver.pre =~ PRE_RE ? $1.to_i : nil end
|
58
|
+
def prerelease() pre end
|
54
59
|
|
55
60
|
# #pre= expects an integer or nil argument
|
56
61
|
def pre=(pre) @semver.pre = (pre ? "#{PRE_LABEL}.#{pre}" : nil) end
|
62
|
+
def prerelease=(pre) self.pre = pre end
|
57
63
|
|
58
64
|
def dup() Version.new(self) end
|
59
65
|
def clone() Version.new(self) end
|
@@ -61,19 +67,19 @@ module Prick
|
|
61
67
|
def eql?(other) self == other end
|
62
68
|
def hash() @semver.hash end
|
63
69
|
|
64
|
-
def initialize(version,
|
70
|
+
def initialize(version, fork: nil, feature: nil)
|
65
71
|
case version
|
66
72
|
when String
|
67
73
|
version =~ VERSION_RE or raise Version::FormatError, "Expected a version, got #{version.inspect}"
|
68
|
-
@
|
69
|
-
@semver = Semantic::Version.new($
|
70
|
-
@feature = feature || $
|
74
|
+
@fork = fork || $1
|
75
|
+
@semver = Semantic::Version.new($3)
|
76
|
+
@feature = feature || $4
|
71
77
|
when Semantic::Version
|
72
|
-
@
|
78
|
+
@fork = fork
|
73
79
|
@semver = version.dup
|
74
80
|
@feature = feature
|
75
81
|
when Version
|
76
|
-
@
|
82
|
+
@fork = fork || version.fork
|
77
83
|
@semver = version.semver.dup
|
78
84
|
@feature = feature || version.feature
|
79
85
|
else
|
@@ -83,9 +89,17 @@ module Prick
|
|
83
89
|
|
84
90
|
# Try converting the string `version` into a Version object. Return nil if unsuccessful
|
85
91
|
def self.try(version)
|
86
|
-
version?(version) ? new(version) : nil
|
92
|
+
version.is_a?(Version) ? version : (version?(version) ? new(version) : nil)
|
87
93
|
end
|
88
94
|
|
95
|
+
# Parse a branch or tag name into a Version object. Return a [version, tag]
|
96
|
+
# tuple where tag is true if name was a tag
|
97
|
+
def self.parse(name)
|
98
|
+
name =~ VERSION_RE or raise Version::FormatError, "Expected a version, got #{version.inspect}"
|
99
|
+
fork, tag, semver, feature = $1, $2, $3, $4
|
100
|
+
version = Version.new(semver, fork: fork, feature: feature)
|
101
|
+
[version, tag]
|
102
|
+
end
|
89
103
|
# `part` can be one of :major, :minor, :patch, or :pre. If pre is undefined, it
|
90
104
|
# is set to `pre_initial_value`
|
91
105
|
def increment(part, pre_initial_value = 1)
|
@@ -117,20 +131,18 @@ module Prick
|
|
117
131
|
end
|
118
132
|
end
|
119
133
|
|
120
|
-
|
134
|
+
# def path
|
135
|
+
# parts = [FEATURE_DIR, truncate(:pre), feature].compact
|
136
|
+
# File.join(*parts)
|
137
|
+
# end
|
121
138
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
def link
|
128
|
-
!feature? or raise Internal, "Version #{to_s} is a feature, not a release"
|
129
|
-
File.join(RELEASE_DIR, to_s)
|
130
|
-
end
|
139
|
+
# def link
|
140
|
+
# !feature? or raise Internal, "Version #{to_s} is a feature, not a release"
|
141
|
+
# File.join(RELEASE_DIR, to_s)
|
142
|
+
# end
|
131
143
|
|
132
144
|
def <=>(other)
|
133
|
-
r = (
|
145
|
+
r = (fork || "") <=> (other.fork || "")
|
134
146
|
return r if r != 0
|
135
147
|
r = semver <=> other.semver
|
136
148
|
return r if r != 0
|
@@ -138,16 +150,17 @@ module Prick
|
|
138
150
|
return r
|
139
151
|
end
|
140
152
|
|
141
|
-
# Render as
|
142
|
-
def to_s
|
143
|
-
(
|
153
|
+
# Render as branch
|
154
|
+
def to_s(tag: false)
|
155
|
+
(fork ? "#{fork}-" : "") + (tag ? "v" : "") + semver.to_s + (feature ? "_#{feature}" : "")
|
144
156
|
end
|
145
157
|
|
158
|
+
# Render as a tag
|
159
|
+
def to_tag() to_s(tag: true) end
|
160
|
+
|
146
161
|
# Render as string
|
147
162
|
def inspect() to_s end
|
148
163
|
end
|
149
|
-
|
150
|
-
# ZERO = Version.new("0.0.0")
|
151
164
|
end
|
152
165
|
|
153
166
|
|