prick 0.4.0 → 0.5.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 +95 -33
- data/lib/ext/fileutils.rb +7 -0
- data/lib/prick.rb +5 -3
- 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 -18
- 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 +199 -0
- data/lib/prick/project.rb +277 -0
- data/lib/prick/rdbms.rb +2 -1
- data/lib/prick/schema.rb +5 -10
- data/lib/prick/state.rb +129 -74
- data/lib/prick/version.rb +41 -28
- 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 +22 -20
- 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/project.rb
CHANGED
@@ -2,6 +2,283 @@ require "prick/state.rb"
|
|
2
2
|
|
3
3
|
require "tmpdir"
|
4
4
|
|
5
|
+
module Prick
|
6
|
+
class Project
|
7
|
+
attr_reader :name
|
8
|
+
attr_reader :user # Database user
|
9
|
+
attr_reader :head # Current branch/tag
|
10
|
+
attr_reader :schema
|
11
|
+
|
12
|
+
# Return the versioned database or the project database if `version` is nil
|
13
|
+
def database(version = nil)
|
14
|
+
version ? Database.new("#{name}-#{version}", user) : @database
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :cache
|
18
|
+
|
19
|
+
def initialize(name, user, head)
|
20
|
+
@name = name
|
21
|
+
@user = user || ENV['USER']
|
22
|
+
@head = head
|
23
|
+
@schema = Schema.new
|
24
|
+
@database = Database.new(name, user)
|
25
|
+
@cache = Cache.new
|
26
|
+
end
|
27
|
+
|
28
|
+
forward_methods :version, :base_version, :@head
|
29
|
+
forward_methods :clean?, :@head
|
30
|
+
forward_methods :tag?, :release_tag?, :migration_tag?, :@head
|
31
|
+
forward_methods :branch?, :release_branch?, :prerelease_branch?, :feature_branch?, :migration_branch?, :@head
|
32
|
+
|
33
|
+
def self.exist?(directory)
|
34
|
+
File.directory?(directory) && Dir.chdir(directory) { ProjectState.new.exist? }
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.create(name, user, directory)
|
38
|
+
user ||= ENV['USER']
|
39
|
+
|
40
|
+
FileUtils.mkdir(directory) if directory != "."
|
41
|
+
|
42
|
+
Dir.chdir(directory) {
|
43
|
+
# Initialize git instance
|
44
|
+
Git.init
|
45
|
+
|
46
|
+
# Create prick version file
|
47
|
+
PrickVersion.new.write(VERSION)
|
48
|
+
|
49
|
+
# Create project state file
|
50
|
+
ProjectState.new(name: name, user: user).write
|
51
|
+
|
52
|
+
# Directories
|
53
|
+
FileUtils.mkdir_p(DIRS)
|
54
|
+
DIRS.each { |dir| FileUtils.touch("#{dir}/.keep") }
|
55
|
+
|
56
|
+
# Copy default gitignore and schema files
|
57
|
+
Share.cp("gitignore", ".gitignore")
|
58
|
+
Share.cp("schema/schema", ".")
|
59
|
+
|
60
|
+
# Add .prick-migration file
|
61
|
+
MigrationState.new(version: Version.zero).create
|
62
|
+
|
63
|
+
# Add everything so far
|
64
|
+
Git.add(".")
|
65
|
+
Git.commit("Initial import")
|
66
|
+
|
67
|
+
# Rename branch "master"/"main" to "0.0.0_initial"
|
68
|
+
from_branch = Git.branch?("master") ? "master" : "main"
|
69
|
+
Git.rename_branch(from_branch, "0.0.0_initial")
|
70
|
+
|
71
|
+
# Create schema
|
72
|
+
schema = Schema.new
|
73
|
+
schema.version = Version.zero
|
74
|
+
Git.add schema.version_file
|
75
|
+
|
76
|
+
# Create release
|
77
|
+
Git.commit "Created release v0.0.0"
|
78
|
+
Git.create_tag(Version.zero)
|
79
|
+
Git.checkout_tag(Version.zero)
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
# Initialize from disk
|
84
|
+
#
|
85
|
+
# TODO Handle migrations
|
86
|
+
def self.load
|
87
|
+
if Git.detached?
|
88
|
+
name = "v#{Git.current_tag}"
|
89
|
+
else
|
90
|
+
name = Git.current_branch
|
91
|
+
end
|
92
|
+
begin
|
93
|
+
branch = Head.load(name)
|
94
|
+
rescue Version::FormatError
|
95
|
+
raise Fail, "Illegal branch name: #{name}"
|
96
|
+
end
|
97
|
+
state = ProjectState.new.read
|
98
|
+
Project.new(state.name, state.user, branch)
|
99
|
+
end
|
100
|
+
|
101
|
+
# def checkout(branch_or_tag) end
|
102
|
+
|
103
|
+
def build(database = self.database, version: nil)
|
104
|
+
database.clean
|
105
|
+
if version
|
106
|
+
FileUtils.mkdir_p(TMP_DIR)
|
107
|
+
Dir.mktmpdir("clone-", TMP_DIR) { |dir|
|
108
|
+
Command.command "git clone . #{dir}"
|
109
|
+
Dir.chdir(dir) {
|
110
|
+
Git.checkout_tag(version)
|
111
|
+
project = Project.load
|
112
|
+
project.head.build(database)
|
113
|
+
}
|
114
|
+
}
|
115
|
+
else
|
116
|
+
head.build(database)
|
117
|
+
end
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
# def make(database = self.database, version: nil)
|
122
|
+
# database.clean
|
123
|
+
# if cache.exist?(version)
|
124
|
+
# load(database, version: version)
|
125
|
+
# else
|
126
|
+
# build(database, version: version)
|
127
|
+
# cache.save(database, version) if version
|
128
|
+
# end
|
129
|
+
# self
|
130
|
+
# end
|
131
|
+
|
132
|
+
def load(database = self.database, version: nil, file: nil)
|
133
|
+
version.nil? ^ file.nil? or raise Internal, "Need exactly one of :file and :version to be defined"
|
134
|
+
database.clean
|
135
|
+
if version
|
136
|
+
cache.exist?(version) or raise Internal, "Can't find cache file for database #{database}"
|
137
|
+
cache.load(database, version)
|
138
|
+
else
|
139
|
+
database.load(file)
|
140
|
+
end
|
141
|
+
self
|
142
|
+
end
|
143
|
+
|
144
|
+
def save(database = nil, file: nil)
|
145
|
+
!database.nil? || file or raise Internal, "Need a database when saving to file"
|
146
|
+
database ||= self.database
|
147
|
+
if file
|
148
|
+
database.save(file)
|
149
|
+
else
|
150
|
+
cache.save(database)
|
151
|
+
end
|
152
|
+
self
|
153
|
+
end
|
154
|
+
|
155
|
+
# Create a schema diff between two database versions. to_version defaults to the
|
156
|
+
# current version. Returns the Diff object
|
157
|
+
def diff(from_version, to_version)
|
158
|
+
begin
|
159
|
+
from_db = Database.new("#{name}-base", user)
|
160
|
+
to_db = Database.new("#{name}-next", user)
|
161
|
+
from_version ||= version
|
162
|
+
build(from_db, version: from_version)
|
163
|
+
build(to_db, version: to_version)
|
164
|
+
Diff.new(from_db, to_db)
|
165
|
+
ensure
|
166
|
+
from_db&.drop
|
167
|
+
to_db&.drop
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def prepare_release(fork = nil)
|
172
|
+
check_clean(:release_tag)
|
173
|
+
@head = ReleaseBranch.new(fork, version).create
|
174
|
+
submit "Prepared new release based on v#{version}", true
|
175
|
+
self
|
176
|
+
end
|
177
|
+
|
178
|
+
def prepare_diff(from_version = version)
|
179
|
+
begin
|
180
|
+
from_name = "#{name}-base"
|
181
|
+
from_db = Database.new(from_name, user)
|
182
|
+
build(from_db, version: from_version)
|
183
|
+
|
184
|
+
to_name = "#{name}-next"
|
185
|
+
to_db = Database.new(to_name, user)
|
186
|
+
build(to_db)
|
187
|
+
|
188
|
+
if prerelease_branch?
|
189
|
+
head.migrate_features(from_db)
|
190
|
+
end
|
191
|
+
|
192
|
+
diff = Diff.new(from_db, to_db)
|
193
|
+
for path, lines, tables in [
|
194
|
+
[BEFORE_TABLES_DIFF_PATH, diff.before_table_changes, false],
|
195
|
+
[TABLES_DIFF_PATH, diff.table_changes, true],
|
196
|
+
[AFTER_TABLES_DIFF_PATH, diff.after_table_changes, false]]
|
197
|
+
if lines.empty?
|
198
|
+
if File.exist?(path)
|
199
|
+
if tables
|
200
|
+
Share.cp(File.join("diff", File.basename(path)), path)
|
201
|
+
Git.add(path)
|
202
|
+
else
|
203
|
+
Git.rm(path)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
else
|
207
|
+
Share.cp(File.join("diff", File.basename(path)), path)
|
208
|
+
File.open(path, "a") { |f| f.puts lines }
|
209
|
+
Git.add(path) if !tables
|
210
|
+
end
|
211
|
+
end
|
212
|
+
self
|
213
|
+
ensure
|
214
|
+
from_db&.drop
|
215
|
+
to_db&.drop
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def create_release(new_version)
|
220
|
+
check_clean(:release_branch)
|
221
|
+
head = ReleaseTag.new(new_version, version)
|
222
|
+
check_migration(head.migration)
|
223
|
+
(@head = head).create
|
224
|
+
self
|
225
|
+
end
|
226
|
+
|
227
|
+
def generate_schema
|
228
|
+
build = SchemaBuilder.new(database, SCHEMA_DIR).build(execute: false)
|
229
|
+
puts build.lines
|
230
|
+
end
|
231
|
+
|
232
|
+
def generate_migration
|
233
|
+
build = MigrationBuilder.new(database, MIGRATION_DIR).build(execute: false)
|
234
|
+
puts build.lines
|
235
|
+
end
|
236
|
+
|
237
|
+
private
|
238
|
+
def check(kind) self.send(:"#{kind}?") end
|
239
|
+
def check_clean(kind = nil)
|
240
|
+
clean? or raise Internal, "Dirty repository"
|
241
|
+
kind.nil? || check(kind) or raise Internal, "Not on a #{kind} tag/branh"
|
242
|
+
end
|
243
|
+
|
244
|
+
def check_branch() branch? or raise Internal, "Not on a branch" end
|
245
|
+
def check_tag() tag? or raise Internal, "Not on a tag" end
|
246
|
+
|
247
|
+
# FIXME: Use Cache::file
|
248
|
+
def cache_file(version) File.join(CACHE_DIR, "#{database(version)}.sql.gz") end
|
249
|
+
|
250
|
+
def submit(msg, commit = true)
|
251
|
+
Git.commit msg if commit
|
252
|
+
@message = msg
|
253
|
+
end
|
254
|
+
|
255
|
+
def clean(database) # FIXME: Use Database#clean
|
256
|
+
if database.exist?
|
257
|
+
database.recreate if database.loaded?
|
258
|
+
else
|
259
|
+
database.create
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def check_migration(migration)
|
264
|
+
begin
|
265
|
+
from_version = migration.base_version
|
266
|
+
from_db = Database.new("#{name}-base", user)
|
267
|
+
to_db = Database.new("#{name}-next", user)
|
268
|
+
build(from_db, version: from_version)
|
269
|
+
build(to_db)
|
270
|
+
migration.migrate(from_db)
|
271
|
+
Diff.new(from_db, to_db).same? or raise Error, "Schema/migration mismatch"
|
272
|
+
ensure
|
273
|
+
from_db&.drop
|
274
|
+
to_db&.drop
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
__END__
|
281
|
+
|
5
282
|
module Prick
|
6
283
|
class Project
|
7
284
|
# Name of project. Persisted in the project state file
|
data/lib/prick/rdbms.rb
CHANGED
@@ -9,7 +9,8 @@ module Prick
|
|
9
9
|
|
10
10
|
### EXECUTE SQL
|
11
11
|
|
12
|
-
# Execute the SQL statement and return stdout as an array of tuples
|
12
|
+
# Execute the SQL statement and return stdout as an array of tuples. FIXME:
|
13
|
+
# SQL can't contain '"'
|
13
14
|
def self.exec_sql(db, sql, user: ENV['USER'])
|
14
15
|
stdout = Command.command %(
|
15
16
|
{
|
data/lib/prick/schema.rb
CHANGED
@@ -9,22 +9,17 @@ module Prick
|
|
9
9
|
|
10
10
|
# Note this models the SCHEMAS_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
|
-
|