prick 0.2.0 → 0.7.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 +6 -5
- data/Gemfile +4 -1
- data/TODO +10 -0
- data/doc/prick.txt +114 -0
- data/exe/prick +328 -402
- data/lib/ext/fileutils.rb +18 -0
- data/lib/ext/forward_method.rb +18 -0
- data/lib/ext/shortest_path.rb +44 -0
- data/lib/prick.rb +20 -10
- data/lib/prick/branch.rb +254 -0
- data/lib/prick/builder.rb +164 -0
- data/lib/prick/cache.rb +34 -0
- data/lib/prick/command.rb +19 -11
- data/lib/prick/constants.rb +122 -48
- data/lib/prick/database.rb +28 -20
- data/lib/prick/diff.rb +125 -0
- data/lib/prick/exceptions.rb +15 -3
- data/lib/prick/git.rb +77 -30
- data/lib/prick/head.rb +183 -0
- data/lib/prick/migration.rb +40 -200
- data/lib/prick/program.rb +493 -0
- data/lib/prick/project.rb +523 -351
- data/lib/prick/rdbms.rb +4 -13
- data/lib/prick/schema.rb +16 -90
- data/lib/prick/share.rb +64 -0
- data/lib/prick/state.rb +192 -0
- data/lib/prick/version.rb +62 -29
- data/libexec/strip-comments +33 -0
- data/make_releases +48 -345
- data/make_schema +10 -0
- data/prick.gemspec +14 -23
- 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/migration/features.yml +6 -0
- data/share/migration/migrate.sql +3 -0
- data/share/migration/migrate.yml +8 -0
- data/share/migration/tables.sql +3 -0
- data/share/schema/build.yml +14 -0
- data/share/schema/schema.sql +5 -0
- data/share/schema/schema/build.yml +3 -0
- data/share/schema/schema/prick/build.yml +14 -0
- data/share/schema/schema/prick/data.sql +7 -0
- data/share/schema/schema/prick/schema.sql +5 -0
- data/share/{schemas/prick/schema.sql → schema/schema/prick/tables.sql} +2 -5
- data/{file → share/schema/schema/public/.keep} +0 -0
- data/share/schema/schema/public/build.yml +14 -0
- data/share/schema/schema/public/schema.sql +3 -0
- data/test_assorted +192 -0
- data/test_feature +112 -0
- data/test_refactor +34 -0
- data/test_single_dev +83 -0
- metadata +43 -68
- data/lib/prick/build.rb +0 -376
- data/lib/prick/migra.rb +0 -22
- data/share/schemas/prick/data.sql +0 -8
data/lib/prick/rdbms.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
require 'prick/command.rb'
|
2
|
-
require 'prick/ensure.rb'
|
3
2
|
|
4
3
|
require 'csv'
|
5
4
|
|
6
5
|
module Prick
|
7
6
|
module Rdbms
|
8
|
-
|
7
|
+
# extend Ensure
|
9
8
|
|
10
9
|
### EXECUTE SQL
|
11
10
|
|
@@ -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
@@ -1,100 +1,26 @@
|
|
1
|
-
require "prick/
|
2
|
-
require "prick/exceptions.rb"
|
1
|
+
require "prick/state.rb"
|
3
2
|
|
4
3
|
module Prick
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
# Alternatively add some special tags:
|
12
|
-
#
|
13
|
-
# -- require prick (meaning include the prick schema)
|
14
|
-
# -- require public/user-schema (include this file)
|
15
|
-
#
|
16
|
-
class Schema
|
17
|
-
# Enclosing project
|
18
|
-
attr_reader :project
|
19
|
-
|
20
|
-
# Path to data file
|
21
|
-
def Schema.data_file() DATA_SQL_PATH end
|
22
|
-
def data_file() Schema.data_file end
|
23
|
-
|
24
|
-
# Version read from schemas/prick/data.sql
|
25
|
-
def version()
|
26
|
-
@version ||= begin
|
27
|
-
File.open(data_file, "r") { |file|
|
28
|
-
while !file.eof? && file.gets.chomp != COPY_STMT
|
29
|
-
;
|
30
|
-
end
|
31
|
-
!file.eof? or raise Fail, "No COPY statement in #{data_file}"
|
32
|
-
l = file.gets.chomp
|
33
|
-
a = l.split("\t")[1..].map { |val| val == '\N' ? nil : val }
|
34
|
-
a.size == FIELDS.size or raise Fail, "Illegal data format in #{data_file}"
|
35
|
-
custom, major, minor, patch, pre = a[0], *a[1..-2].map { |val| val && val.to_i }
|
36
|
-
v = Version.new("0.0.0", custom: (custom == '\N' ? nil : custom))
|
37
|
-
v.major = major
|
38
|
-
v.minor = minor
|
39
|
-
v.patch = patch
|
40
|
-
v.pre = (pre == "null" ? nil : pre)
|
41
|
-
v
|
42
|
-
}
|
43
|
-
end
|
44
|
-
end
|
4
|
+
PRICK_SCHEMA = "prick"
|
5
|
+
SCHEMA_NAMES = %w(schema roles types tables data constraints indexes views functions comments grants)
|
6
|
+
BUILD_BASE_NAME = "build"
|
7
|
+
BUILD_SQL_FILE = BUILD_BASE_NAME + ".sql"
|
8
|
+
BUILD_YML_FILE = BUILD_BASE_NAME + ".yml"
|
45
9
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
File.open(data_file, "w") { |f|
|
50
|
-
f.puts "--"
|
51
|
-
f.puts "-- This file is auto-generated by prick(1). Please don't touch"
|
52
|
-
f.puts COPY_STMT
|
53
|
-
f.print \
|
54
|
-
"1\t",
|
55
|
-
FIELDS[..-2].map { |f| @version.send(f.to_sym) || '\N' }.join("\t"),
|
56
|
-
"\t#{@version}\n"
|
57
|
-
f.puts "\\."
|
58
|
-
}
|
59
|
-
Git.add(data_file)
|
60
|
-
@version
|
61
|
-
end
|
62
|
-
|
63
|
-
def initialize(project)
|
64
|
-
@project = project
|
65
|
-
end
|
66
|
-
|
67
|
-
# Path to the schemas directory
|
68
|
-
def path() "#{project.path}/#{SCHEMA_DIR}" end
|
10
|
+
# Note this models the SCHEMA_DIR directory, not a database schema
|
11
|
+
class Schema
|
12
|
+
def yml_file() SchemaBuilder.yml_file(SCHEMA_DIR) end
|
69
13
|
|
70
|
-
def
|
71
|
-
|
72
|
-
end
|
14
|
+
def version() SchemaVersion.load end
|
15
|
+
def version=(version) SchemaVersion.new(version).write end
|
16
|
+
def version_file() SchemaVersion.new.path end
|
73
17
|
|
74
|
-
|
75
|
-
def build(database = project.database)
|
76
|
-
files = collect("schema.sql") + collect("data.sql")
|
77
|
-
Dir.chdir(path) {
|
78
|
-
files.each { |file| Rdbms.exec_file(database.name, file, user: project.user) }
|
79
|
-
}
|
80
|
-
end
|
18
|
+
def built?(database) database.exist? && database.version == version end
|
81
19
|
|
82
|
-
#
|
83
|
-
def
|
84
|
-
|
85
|
-
if File.exist?(filename)
|
86
|
-
[filename]
|
87
|
-
else
|
88
|
-
Dir["*/#{filename}"]
|
89
|
-
end
|
90
|
-
}
|
20
|
+
# `subject` can be a subpath of schema/ (ie. 'public/tables')
|
21
|
+
def build(database, subject = nil)
|
22
|
+
SchemaBuilder.new(database, SCHEMA_DIR).build(subject)
|
91
23
|
end
|
92
|
-
|
93
|
-
private
|
94
|
-
FIELDS = %w(custom major minor patch pre feature version)
|
95
|
-
COPY_STMT = "COPY prick.versions (id, #{FIELDS.join(', ')}) FROM stdin;"
|
96
|
-
|
97
|
-
DATA_SQL_PATH = "#{Prick::PRICK_DIR}/data.sql"
|
98
24
|
end
|
99
25
|
end
|
100
26
|
|
data/lib/prick/share.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
module Prick
|
3
|
+
class Share
|
4
|
+
# Procedural object for templating and copying share/ files
|
5
|
+
class Copier
|
6
|
+
attr_reader :clobber, :templates
|
7
|
+
|
8
|
+
def initialize(clobber, templates)
|
9
|
+
@clobber = clobber
|
10
|
+
@templates = templates
|
11
|
+
end
|
12
|
+
|
13
|
+
def cp(from, to)
|
14
|
+
if File.directory?(from)
|
15
|
+
cp_dir(from, to)
|
16
|
+
elsif File.file?(from)
|
17
|
+
cp_file(from, to)
|
18
|
+
else
|
19
|
+
raise Fail, "Can't copy #{from}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def cp_file(from, to)
|
24
|
+
if clobber || !File.exist?(to)
|
25
|
+
if templates.empty?
|
26
|
+
FileUtils.copy_file(from, to)
|
27
|
+
else
|
28
|
+
File.open(to, "w") { |f|
|
29
|
+
File.readlines(from).each { |l|
|
30
|
+
templates.each { |key, value| l.gsub!(/\[<#{key}>\]/, value) }
|
31
|
+
f.puts l
|
32
|
+
}
|
33
|
+
}
|
34
|
+
end
|
35
|
+
[to]
|
36
|
+
else
|
37
|
+
[]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def cp_dir(from, to)
|
42
|
+
FileUtils.mkdir_p(to)
|
43
|
+
Dir.children(from).map { |name|
|
44
|
+
cp(File.join(from, name), File.join(to, name))
|
45
|
+
}.flatten
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.cp(pattern, dest, clobber: true, templates: {})
|
50
|
+
copier = Copier.new(clobber, templates)
|
51
|
+
matches = Dir.glob(File.join(SHARE_PATH, pattern))
|
52
|
+
if File.directory?(dest)
|
53
|
+
matches.map { |from| copier.cp(from, File.join(dest, File.basename(from))) }.flatten
|
54
|
+
elsif matches.size == 1
|
55
|
+
copier.cp(matches.first, dest)
|
56
|
+
elsif matches.size == 0
|
57
|
+
[]
|
58
|
+
else
|
59
|
+
raise Internal, "Destination is not a directory: #{destdir}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
data/lib/prick/state.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Prick
|
5
|
+
# General interface for prick state files. Comments are automatically removed
|
6
|
+
class PrickFile
|
7
|
+
attr_reader :path
|
8
|
+
def initialize(path) @path = path end
|
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
|
15
|
+
|
16
|
+
protected
|
17
|
+
# Read file from disk or from the given branch or tag
|
18
|
+
def general_read(method, branch: nil, tag: nil)
|
19
|
+
!(branch && tag) or raise Internal, "Not both of `branch` and `tag` can be defined"
|
20
|
+
branch || tag ? Git.send(method, path, branch: branch, tag: tag) : File.open(path, "r").send(method)
|
21
|
+
end
|
22
|
+
|
23
|
+
def do_read(**opts) general_read(:read, **opts) end
|
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
|
106
|
+
end
|
107
|
+
|
108
|
+
# General interface for prick state files in YAML format
|
109
|
+
class State < PrickFile
|
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
|
113
|
+
def initialize(path, **fields)
|
114
|
+
super(path)
|
115
|
+
@fields = fields
|
116
|
+
@fields.each_key { |k| self.class.attr_accessor k }
|
117
|
+
@loaded = false
|
118
|
+
end
|
119
|
+
|
120
|
+
def create() write end
|
121
|
+
|
122
|
+
def set(**fields)
|
123
|
+
for field, value in fields
|
124
|
+
self.send(:"#{field}=", value)
|
125
|
+
end
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
def read(branch: nil, tag: nil)
|
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)
|
136
|
+
end
|
137
|
+
@loaded = true
|
138
|
+
self
|
139
|
+
end
|
140
|
+
|
141
|
+
def write(**fields)
|
142
|
+
set(**fields)
|
143
|
+
hash = @fields.map { |field, klass|
|
144
|
+
value = self.send(field)
|
145
|
+
value = value.to_s if klass == Version && !value.nil?
|
146
|
+
[field.to_s, value]
|
147
|
+
}.to_h
|
148
|
+
IO.write(@path, YAML.dump(hash))
|
149
|
+
raise if @version.is_a?(Array)
|
150
|
+
self
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class MigrationState < State
|
155
|
+
def initialize(version: nil, base_version: nil)
|
156
|
+
super(PRICK_MIGRATION_PATH, version: Version, base_version: Version)
|
157
|
+
set(version: version, base_version: base_version)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
class HeadState < State
|
162
|
+
def initialize(name: nil)
|
163
|
+
super(PRICK_HEAD_PATH, name: String)
|
164
|
+
set(name: name)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class FeatureState < State
|
169
|
+
def initialize(feature: nil)
|
170
|
+
super(PRICK_FEATURE_PATH, feature: String)
|
171
|
+
set(feature: feature)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
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
|
+
#
|
182
|
+
|
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)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
data/lib/prick/version.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
|
2
|
-
#
|
2
|
+
# "require 'semantic'" is moved to lib/prick.rb to avoid having Gem depend on it
|
3
3
|
# require 'semantic' # https://github.com/jlindsey/semantic
|
4
4
|
|
5
5
|
# Required by gem
|
6
6
|
module Prick
|
7
|
-
VERSION = "0.
|
7
|
+
VERSION = "0.7.0"
|
8
8
|
end
|
9
9
|
|
10
10
|
# Project related code starts here
|
11
11
|
module Prick
|
12
12
|
class Version
|
13
|
+
class FormatError < RuntimeError; end
|
14
|
+
|
13
15
|
include Comparable
|
14
16
|
|
15
17
|
PRE_LABEL = "pre"
|
@@ -18,11 +20,13 @@ module Prick
|
|
18
20
|
def self.zero() Version.new("0.0.0") end
|
19
21
|
def zero?() self == Version.zero end
|
20
22
|
|
21
|
-
# Return true if `string` is a version
|
22
|
-
|
23
|
-
|
23
|
+
# Return true if `string` is a version
|
24
|
+
def self.version?(string)
|
25
|
+
string.is_a?(String) or raise Internal, "String expected"
|
26
|
+
!(string =~ VERSION_RE).nil?
|
27
|
+
end
|
24
28
|
|
25
|
-
attr_accessor :
|
29
|
+
attr_accessor :fork
|
26
30
|
attr_accessor :semver
|
27
31
|
attr_accessor :feature
|
28
32
|
|
@@ -35,24 +39,27 @@ module Prick
|
|
35
39
|
def patch() @semver.patch end
|
36
40
|
def patch=(patch) @semver.patch = patch end
|
37
41
|
|
38
|
-
# Return true if this is a
|
39
|
-
def
|
42
|
+
# Return true if this is a fork release
|
43
|
+
def fork?() !@fork.nil? end
|
40
44
|
|
41
45
|
# Return true if this is a feature release
|
42
46
|
def feature?() !@feature.nil? end
|
43
47
|
|
44
|
-
# Return true if this is a release branch
|
45
|
-
def release?() !feature? end
|
48
|
+
# Return true if this is a release branch (and not a prerelease)
|
49
|
+
def release?() !feature? && !pre? end
|
46
50
|
|
47
51
|
# Return true if this is a pre-release
|
48
52
|
def pre?() !@semver.pre.nil? end
|
53
|
+
def prerelease?() pre? end
|
49
54
|
|
50
55
|
# The releases is stored as a String (eg. 'pre.1') in the semantic version
|
51
56
|
# but #pre returns only the Integer number
|
52
57
|
def pre() @semver.pre =~ PRE_RE ? $1.to_i : nil end
|
58
|
+
def prerelease() pre end
|
53
59
|
|
54
60
|
# #pre= expects an integer or nil argument
|
55
61
|
def pre=(pre) @semver.pre = (pre ? "#{PRE_LABEL}.#{pre}" : nil) end
|
62
|
+
def prerelease=(pre) self.pre = pre end
|
56
63
|
|
57
64
|
def dup() Version.new(self) end
|
58
65
|
def clone() Version.new(self) end
|
@@ -60,37 +67,52 @@ module Prick
|
|
60
67
|
def eql?(other) self == other end
|
61
68
|
def hash() @semver.hash end
|
62
69
|
|
63
|
-
def initialize(version,
|
70
|
+
def initialize(version, fork: nil, feature: nil)
|
64
71
|
case version
|
65
72
|
when String
|
66
|
-
version =~ VERSION_RE
|
67
|
-
|
68
|
-
@
|
69
|
-
@
|
70
|
-
@feature = feature || $3
|
73
|
+
version =~ VERSION_RE or raise Version::FormatError, "Expected a version, got #{version.inspect}"
|
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
|
80
|
-
raise "Expected a String, Version, or Semantic::Version, got #{version.class}"
|
86
|
+
raise Internal, "Expected a String, Version, or Semantic::Version, got #{version.class}"
|
81
87
|
end
|
82
88
|
end
|
83
89
|
|
90
|
+
# Try converting the string `version` into a Version object. Return nil if unsuccessful
|
91
|
+
def self.try(version)
|
92
|
+
version.is_a?(Version) ? version : (version?(version) ? new(version) : nil)
|
93
|
+
end
|
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
|
84
103
|
# `part` can be one of :major, :minor, :patch, or :pre. If pre is undefined, it
|
85
104
|
# is set to `pre_initial_value`
|
86
105
|
def increment(part, pre_initial_value = 1)
|
106
|
+
self.dup.increment!(part, pre_initial_value)
|
107
|
+
end
|
108
|
+
|
109
|
+
def increment!(part, pre_initial_value = 1)
|
87
110
|
if part == :pre
|
88
|
-
|
89
|
-
v.pre = (v.pre ? v.pre+1 : pre_initial_value)
|
90
|
-
v
|
111
|
+
self.pre = (self.pre ? self.pre + 1 : pre_initial_value)
|
91
112
|
else
|
92
|
-
|
113
|
+
@semver = semver.increment!(part)
|
93
114
|
end
|
115
|
+
self
|
94
116
|
end
|
95
117
|
|
96
118
|
def truncate(part)
|
@@ -109,8 +131,18 @@ module Prick
|
|
109
131
|
end
|
110
132
|
end
|
111
133
|
|
134
|
+
# def path
|
135
|
+
# parts = [FEATURE_DIR, truncate(:pre), feature].compact
|
136
|
+
# File.join(*parts)
|
137
|
+
# end
|
138
|
+
|
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
|
143
|
+
|
112
144
|
def <=>(other)
|
113
|
-
r = (
|
145
|
+
r = (fork || "") <=> (other.fork || "")
|
114
146
|
return r if r != 0
|
115
147
|
r = semver <=> other.semver
|
116
148
|
return r if r != 0
|
@@ -118,16 +150,17 @@ module Prick
|
|
118
150
|
return r
|
119
151
|
end
|
120
152
|
|
121
|
-
# Render as
|
122
|
-
def to_s
|
123
|
-
(
|
153
|
+
# Render as branch
|
154
|
+
def to_s(tag: false)
|
155
|
+
(fork ? "#{fork}-" : "") + (tag ? "v" : "") + semver.to_s + (feature ? "_#{feature}" : "")
|
124
156
|
end
|
125
157
|
|
158
|
+
# Render as a tag
|
159
|
+
def to_tag() to_s(tag: true) end
|
160
|
+
|
126
161
|
# Render as string
|
127
162
|
def inspect() to_s end
|
128
163
|
end
|
129
|
-
|
130
|
-
# ZERO = Version.new("0.0.0")
|
131
164
|
end
|
132
165
|
|
133
166
|
|