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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -5
  3. data/Gemfile +4 -1
  4. data/TODO +10 -0
  5. data/doc/prick.txt +114 -0
  6. data/exe/prick +328 -402
  7. data/lib/ext/fileutils.rb +18 -0
  8. data/lib/ext/forward_method.rb +18 -0
  9. data/lib/ext/shortest_path.rb +44 -0
  10. data/lib/prick.rb +20 -10
  11. data/lib/prick/branch.rb +254 -0
  12. data/lib/prick/builder.rb +164 -0
  13. data/lib/prick/cache.rb +34 -0
  14. data/lib/prick/command.rb +19 -11
  15. data/lib/prick/constants.rb +122 -48
  16. data/lib/prick/database.rb +28 -20
  17. data/lib/prick/diff.rb +125 -0
  18. data/lib/prick/exceptions.rb +15 -3
  19. data/lib/prick/git.rb +77 -30
  20. data/lib/prick/head.rb +183 -0
  21. data/lib/prick/migration.rb +40 -200
  22. data/lib/prick/program.rb +493 -0
  23. data/lib/prick/project.rb +523 -351
  24. data/lib/prick/rdbms.rb +4 -13
  25. data/lib/prick/schema.rb +16 -90
  26. data/lib/prick/share.rb +64 -0
  27. data/lib/prick/state.rb +192 -0
  28. data/lib/prick/version.rb +62 -29
  29. data/libexec/strip-comments +33 -0
  30. data/make_releases +48 -345
  31. data/make_schema +10 -0
  32. data/prick.gemspec +14 -23
  33. data/share/diff/diff.after-tables.sql +4 -0
  34. data/share/diff/diff.before-tables.sql +4 -0
  35. data/share/diff/diff.tables.sql +8 -0
  36. data/share/migration/diff.tables.sql +8 -0
  37. data/share/migration/features.yml +6 -0
  38. data/share/migration/migrate.sql +3 -0
  39. data/share/migration/migrate.yml +8 -0
  40. data/share/migration/tables.sql +3 -0
  41. data/share/schema/build.yml +14 -0
  42. data/share/schema/schema.sql +5 -0
  43. data/share/schema/schema/build.yml +3 -0
  44. data/share/schema/schema/prick/build.yml +14 -0
  45. data/share/schema/schema/prick/data.sql +7 -0
  46. data/share/schema/schema/prick/schema.sql +5 -0
  47. data/share/{schemas/prick/schema.sql → schema/schema/prick/tables.sql} +2 -5
  48. data/{file → share/schema/schema/public/.keep} +0 -0
  49. data/share/schema/schema/public/build.yml +14 -0
  50. data/share/schema/schema/public/schema.sql +3 -0
  51. data/test_assorted +192 -0
  52. data/test_feature +112 -0
  53. data/test_refactor +34 -0
  54. data/test_single_dev +83 -0
  55. metadata +43 -68
  56. data/lib/prick/build.rb +0 -376
  57. data/lib/prick/migra.rb +0 -22
  58. data/share/schemas/prick/data.sql +0 -8
@@ -2,12 +2,24 @@
2
2
  module Prick
3
3
  class Error < RuntimeError; end
4
4
  class Fail < Error; end
5
+
5
6
  class NotYet < NotImplementedError
6
- def initialize() super("Program error - Not yet implemented") end
7
+ def initialize() super("Internal error: Not yet implemented") end
8
+ end
9
+
10
+ class NotThis < ScriptError
11
+ def initialize() super("Internal error: Abstract method called") end
7
12
  end
8
- class AbstractMethod < ScriptError
9
- def initialize() super("Program error - Abstract method called") end
13
+
14
+ class Abstract < ScriptError
15
+ def initialize() super("Internal error: Abstract method called") end
10
16
  end
17
+
18
+ AbstractMethod = Abstract
19
+
11
20
  class Internal < ScriptError; end
21
+ class Oops < Internal
22
+ def initialize() super("Oops, this shouldn't happen") end
23
+ end
12
24
  end
13
25
 
data/lib/prick/git.rb CHANGED
@@ -9,34 +9,41 @@ module Prick
9
9
  Command.command "git init"
10
10
  end
11
11
 
12
- # Returns true if the repository has no modified files. Requires the
13
- # repository to have at least one commit. Creating the repository using
14
- # Project::initialize_directory guarantees that
12
+ # Returns true if the repository has no modified files or unresolved
13
+ # conflicts. Requires the repository to have at least one commit. Creating
14
+ # the repository using Project::initialize_directory guarantees that
15
15
  def self.clean?()
16
16
  Command.command("git status").find { |l|
17
- l =~ /Changes to be committed:/ || l =~ /^\s*modified:/
17
+ (l =~ /^Changes to be committed:/) ||
18
+ (l =~ /^Unmerged paths:/) ||
19
+ (l =~ /^Changes not staged for commit:/)
18
20
  }.nil?
19
21
  end
20
22
 
21
23
  # Returns true if the repository is on a detached branch
22
24
  def self.detached?
23
- line = File.readlines(".git/HEAD").first.chomp
24
- line =~ /^ref:/ ? false : true
25
+ Command.command? "git symbolic-ref -q HEAD", expect: 1
25
26
  end
26
27
 
27
28
  # The current tag. This is only defined if the repository is in "detached
28
29
  # head" mode
29
- def self.current_tag() raise "Not yet implemented" end
30
-
30
+ def self.current_tag()
31
+ self.detached? ? Command.command("git describe --tags").first.sub(/^v/, "") : nil
32
+ end
33
+
31
34
  # Return true if `version` has an associated tag
32
35
  def self.tag?(version)
33
- !list_tags.grep(version).empty?
36
+ !list_tags.grep(version.to_s).empty?
34
37
  end
35
38
 
36
- # List tag versions
37
39
  # Create version tag
38
- def self.create_tag(version)
39
- Command.command "git tag -a 'v#{version}' -m 'Release #{version}'"
40
+ def self.create_tag(version, message: "Release #{version}", commit_id: nil)
41
+ Command.command "git tag -a 'v#{version}' -m '#{message}' #{commit_id}"
42
+ end
43
+
44
+ # Create a cancel-version tag
45
+ def self.cancel_tag(version)
46
+ create_tag("#{version}_cancelled", message: "Cancel #{version}", commit_id: tag_id(version))
40
47
  end
41
48
 
42
49
  def self.delete_tag(version, remote: false)
@@ -44,18 +51,30 @@ module Prick
44
51
  Command.command("git push --delete origin 'v#{version}'", fail: false) if remote
45
52
  end
46
53
 
54
+ def self.tag_id(version)
55
+ Command.command("git rev-parse 'v#{version}^{}'").first
56
+ end
57
+
47
58
  # Checkout a version tag as a detached head
48
59
  def self.checkout_tag(version)
49
60
  Command.command "git checkout 'v#{version}'"
50
61
  end
51
62
 
52
- def self.list_tags
53
- Command.command("git tag").map { |tag| tag.sub(/^v(#{VERSION_SUB_RE})/, '\1') }
63
+ def self.list_tags(include_cancelled: false)
64
+ tags = Command.command("git tag")
65
+ if !include_cancelled
66
+ cancelled = tags.select { |tag| tag =~ /_cancelled$/ }
67
+ for cancel_tag in cancelled
68
+ tags.delete(cancel_tag)
69
+ tags.delete(cancel_tag.sub(/_cancelled$/, ""))
70
+ end
71
+ end
72
+ tags.map { |tag| tag = tag[1..-1] }
54
73
  end
55
74
 
56
- # Name of the current branch
75
+ # Name of the current branch. This is nil if on a tag ("detached HEAD")
57
76
  def self.current_branch()
58
- Command.command("git rev-parse --abbrev-ref HEAD").first
77
+ self.detached? ? nil : Command.command("git rev-parse --abbrev-ref HEAD").first
59
78
  end
60
79
 
61
80
  # Check if branch exist
@@ -69,6 +88,11 @@ module Prick
69
88
  Command.command "git branch #{name}"
70
89
  end
71
90
 
91
+ # Rename a branch
92
+ def self.rename_branch(from, to)
93
+ Command.command "git branch -m #{from} #{to}"
94
+ end
95
+
72
96
  # Destroy branch
73
97
  def self.delete_branch(name)
74
98
  Command.command "git branch -D #{name}", fail: false
@@ -94,28 +118,46 @@ module Prick
94
118
 
95
119
  Command.command "git merge --no-commit #{name}", fail: false
96
120
 
97
- # Reinstate included files
98
- files.each { |path, content|
121
+ # Restore excluded files
122
+ files.each { |path, content|
99
123
  File.open(path, "w") { |file| file.puts(content) }
100
124
  # Resolve git unmerged status
101
125
  Git.add(path)
102
126
  }
127
+
128
+ # TODO Detect outstanding merges
129
+ end
130
+
131
+ def self.merge_tag(name, exclude_files: [], fail: false)
132
+ merge_branch(name, exclude_files: exclude_files, fail: fail)
103
133
  end
104
134
 
105
- # List branches
106
- def self.list_branches
107
- Command.command "git branch --format='%(refname:short)'"
135
+ # List branches. Detached head "branches" are ignored unless :detached_head is true
136
+ def self.list_branches(detached_head: false)
137
+ if detached_head
138
+ Command.command "git branch --format='%(refname:short)'"
139
+ else
140
+ Command.command "git for-each-ref --format='%(refname:short)' refs/heads/*"
141
+ end
108
142
  end
109
143
 
110
144
  # Add a file to the index of the current branch
111
145
  def self.add(*files)
112
- Array(files).each { |file|
146
+ Array(files).flatten.each { |file|
113
147
  Dir.chdir(File.dirname(file)) {
114
148
  Command.command "git add '#{File.basename(file)}'"
115
149
  }
116
150
  }
117
151
  end
118
152
 
153
+ def self.changed?(file)
154
+ Command.command("git status --porcelain").any? { |l| l =~ /^.M #{file}$/ }
155
+ end
156
+
157
+ def self.added?(file)
158
+ Command.command("git status --porcelain").any? { |l| l =~ /^A. #{file}$/ }
159
+ end
160
+
119
161
  # Return content of file in the given tag or branch. Defaults to HEAD
120
162
  def self.readlines(file, tag: nil, branch: nil)
121
163
  !(tag && branch) or raise Internal, "Can't use both tag: and branch: options"
@@ -127,7 +169,7 @@ module Prick
127
169
  end.map { |l| "#{l}\n" }
128
170
  end
129
171
 
130
- # Return content of file
172
+ # Return content of file as a String
131
173
  def self.read(file, tag: nil, branch: nil)
132
174
  !(tag && branch) or raise Internal, "Can't use both tag: and branch: options"
133
175
  if tag
@@ -135,18 +177,23 @@ module Prick
135
177
  else
136
178
  branch ||= "HEAD"
137
179
  Command.command "git show #{branch}:#{file}"
138
- end.join("\n")
180
+ end.join("\n") + "\n"
139
181
  end
140
182
 
141
- def self.rm(file)
142
- Dir.chdir(File.dirname(file)) {
143
- Command.command "git rm -f '#{File.basename(file)}'", fail: false
183
+ def self.rm(*files)
184
+ Array(files).flatten.each { |file|
185
+ Dir.chdir(File.dirname(file)) {
186
+ Command.command "git rm -f '#{File.basename(file)}'", fail: false
187
+ }
144
188
  }
145
189
  end
146
190
 
147
- def self.rm_rf(file)
148
- Dir.chdir(File.dirname(file)) {
149
- Command.command "git rm -rf '#{File.basename(file)}'", fail: false
191
+ def self.rm_rf(*files)
192
+ Array(files).flatten.each { |file|
193
+ Dir.chdir(File.dirname(file)) {
194
+ next if file == ".keep"
195
+ Command.command "git rm -rf '#{File.basename(file)}'", fail: false
196
+ }
150
197
  }
151
198
  end
152
199
 
data/lib/prick/head.rb ADDED
@@ -0,0 +1,183 @@
1
+
2
+ module Prick
3
+ class Head
4
+ def project_name() Prick.project.name end
5
+
6
+ # Usually equal to #version.to_s
7
+ attr_reader :name
8
+
9
+ # Version. This should be equal to schema.version
10
+ attr_reader :version
11
+
12
+ def base_version() @migration.base_version end
13
+
14
+ attr_reader :schema
15
+
16
+ attr_reader :migration
17
+
18
+ def clean?() Git::clean? end
19
+
20
+ def tag?() false end
21
+ def release_tag?() false end
22
+ def migration_tag?() false end
23
+
24
+ def branch?() false end
25
+ def release_branch?() false end
26
+ def prerelease_branch?() false end
27
+ def feature_branch?() false end
28
+ def migration_branch?() false end
29
+
30
+ def initialize(name, version, migration)
31
+ @name = name
32
+ @version = version
33
+ @schema = Schema.new
34
+ @migration = migration
35
+ end
36
+
37
+ # TODO: Handle migrations
38
+ def self.load(name)
39
+ version, tag = Version.parse(name)
40
+ if tag
41
+ ReleaseTag.load
42
+ else
43
+ if version.release?
44
+ ReleaseBranch.load
45
+ elsif version.prerelease?
46
+ PrereleaseBranch.load(version)
47
+ elsif version.feature?
48
+ FeatureBranch.load(version)
49
+ else
50
+ raise NotHere
51
+ end
52
+ end
53
+ end
54
+
55
+ def create()
56
+ raise NotThis
57
+ end
58
+
59
+ def build(database)
60
+ schema.build(database)
61
+ end
62
+
63
+ def self.database_name(version)
64
+ version.truncate(:pre).to_s
65
+ end
66
+ end
67
+
68
+ class Tag < Head
69
+ def tag?() true end
70
+
71
+ def initialize(version, base_version, migration = nil)
72
+ migration ||= Migration.new(version, base_version)
73
+ super(version.to_s, version, migration)
74
+ end
75
+
76
+ def self.load
77
+ state = Migration.load
78
+ self.new(state.version, state.base_version)
79
+ end
80
+
81
+ def create
82
+ initial_branch_name = "#{version}_initial"
83
+ clean? or raise Internal, "Repository is not clean"
84
+ !Git.detached? or raise Internal, "Not on a branch"
85
+ Git.create_branch(initial_branch_name)
86
+ Git.checkout_branch(initial_branch_name)
87
+ migration.update(version)
88
+ schema.version = version
89
+ Git.commit "Created release v#{version}"
90
+ Git.create_tag(version)
91
+ Git.checkout_tag(version)
92
+ self
93
+ end
94
+ end
95
+
96
+ class ReleaseTag < Tag
97
+ def release_tag?() true end
98
+ end
99
+
100
+ # TODO
101
+ class PrereleaseTag < Tag
102
+ end
103
+
104
+ class MigrationTag < Tag
105
+ def migration_tag?() true end
106
+ end
107
+
108
+ class Branch < Head
109
+ def branch?() true end
110
+
111
+ def initialize(name, version, migration)
112
+ super(name, version, migration)
113
+ end
114
+
115
+ def create()
116
+ Git.create_branch(version)
117
+ Git.checkout_branch(version)
118
+ migration.create
119
+ self
120
+ end
121
+ end
122
+
123
+ class ReleaseBranch < Branch
124
+ def release_branch?() true end
125
+
126
+ def initialize(fork = nil, base_version)
127
+ if fork
128
+ version = Version.new(base_version, fork: fork)
129
+ else
130
+ version = base_version
131
+ end
132
+ super(version.to_s, version, Migration.new(nil, base_version))
133
+ end
134
+
135
+ def self.load
136
+ state = MigrationState.load
137
+ ReleaseBranch.new(nil, state.base_version)
138
+ end
139
+ end
140
+
141
+ class PrereleaseBranch < Branch
142
+ def prerelease_branch?() true end
143
+
144
+ def initialize(version, base_version)
145
+ target_version = version.truncate(:pre)
146
+ super(version.to_s, target_version, Migration.new(target_version, base_version))
147
+ end
148
+
149
+ def self.load
150
+ state = MigrationState.load
151
+ PrereleaseBranch.new(state.version, state.base_version)
152
+ end
153
+ end
154
+
155
+ class FeatureBranch < Branch
156
+ def feature_branch?() true end
157
+
158
+ def initialize(feature_name, base_version)
159
+ name = Version.new(base_version, feature: feature_name).to_s
160
+ super(name, base_version, FeatureMigration.new(feature_name, base_version))
161
+ end
162
+
163
+ def self.load
164
+ state = MigrationState.load
165
+ FeatureBranch.new(state.version.feature)
166
+ end
167
+ end
168
+
169
+ # TODO: Versioned migrations (or maybe not)
170
+ class MigrationBranch < Branch
171
+ def migrationn_branch?() true end
172
+
173
+ def initialize(version, base_version)
174
+ if (version.fork || "") == (base_version.fork || "")
175
+ name = version.to_s + "-" + base_version.semver.to_s
176
+ else
177
+ name = version.to_s + "-" + base_version.to_s
178
+ end
179
+ super(name, version, Migration.new(version, base_version))
180
+ end
181
+ end
182
+ end
183
+
@@ -1,230 +1,70 @@
1
1
 
2
- require 'yaml'
3
-
4
2
  module Prick
5
3
  class Migration
6
- KEEP_FILE = ".keep"
7
-
8
- attr_reader :path
9
-
10
- def files() raise AbstractMethod end
11
- def release_files() files end
12
-
13
- # Return versions of the features in this migration
14
- def feature_versions() raise AbstractMethod end
15
-
16
- def initialize(path, template_dir)
17
- @path = path
18
- @template_dir = template_dir
19
- end
20
-
21
- def exist?() File.exist?(keep_file) end
22
- def create() FileUtils.touch_p(keep_file); Git.add(keep_file) end
23
- def destroy() Git.rm_rf(path) end
24
-
25
- def present?() exist? && files.all? { |f| File.exist?(f) } end
26
- def prepare() files.each { |f| FileUtils.cp(template_file(f), path) }; Git.add(path) end
27
-
28
- def include_feature(feature) raise AbstractMethod end
29
-
30
- def migrate() raise AbstractMethod end
31
-
32
- def to_s() path end
33
- def <=>(other) path <=> other.path end
34
-
35
- def self.path(version)
36
- if version.feature?
37
- File.join(FEATURE_DIR, version.truncate(:feature).to_s, version.feature)
38
- else
39
- File.join(FEATURE_DIR, version)
40
- end
41
- end
42
-
43
- def self.version(path)
44
- Version.new(path.delete_prefix("#{FEATURE_DIR}/").sub("/", "_"))
45
- end
46
-
47
- def self.feature?(path)
48
- Version.new(path.delete_prefix("#{FEATURE_DIR}/").sub("/", "_")).feature?
49
- end
50
-
51
- def self.files(path) raise AbstractMethod end
52
- def self.release_files(path) files(path) end
53
-
54
- def dump(&block)
55
- # puts self.class
56
- # indent {
57
- # puts "path : #{path}"
58
- # puts "files: #{files.inspect}"
59
- # puts "release_files: #{release_files.inspect}"
60
- # puts "feature_versions: #{feature_versions.map(&:to_s).inspect}"
61
- # puts "keep_file: #{keep_file}"
62
- # yield if block_given?
63
- # }
64
- end
65
-
66
- protected
67
- def template_file(path) File.join(SHARE_PATH, @template_dir, File.basename(path)) end
68
- def keep_file() File.join(path, KEEP_FILE) end
69
- end
70
-
71
- class ReleaseMigration < Migration
72
- FEATURES_TMPL_DIR = "features"
73
- FILES = [
74
- FEATURES_SQL = "features.sql",
75
- FEATURES_YML = "features.yml",
76
- MIGRATIONS_SQL = "migrations.sql",
77
- DIFF_SQL = "diff.sql"
78
- ]
79
-
80
- attr_reader :features_yml
81
- attr_reader :features_sql
82
- attr_reader :migrations_sql
83
- attr_reader :diff_sql
84
-
85
- def files() [features_yml, features_sql, migrations_sql, diff_sql] end
4
+ attr_reader :version
5
+ attr_reader :base_version
86
6
 
87
- def initialize(path)
88
- super(path, FEATURES_TMPL_DIR)
89
- @features_yml = File.join(path, FEATURES_YML)
90
- @features_sql = File.join(path, FEATURES_SQL)
91
- @migrations_sql = File.join(path, MIGRATIONS_SQL)
92
- @diff_sql = File.join(path, DIFF_SQL)
7
+ def initialize(version, base_version)
8
+ @version = version
9
+ @base_version = base_version
93
10
  end
94
11
 
95
- def feature_versions() read_features_yml.map { |path| Migration.version(path) } end
96
- def feature_paths() read_features_yml end
97
-
98
- # `feature` is a Feature object
99
- def include_feature(migration, append: true)
100
- migration.is_a?(FeatureMigration) or raise "Expected FeatureMigration object, got #{migration.class}"
101
- !feature_paths.include?(migration.path) or raise Error, "Feature #{migration.version} is already included"
102
- exclude_files = [Schema.data_file] +
103
- migration.release_files +
104
- migration.feature_paths.map { |path| FeatureMigration.release_files(path) }.flatten
105
-
106
- Git.merge_branch(migration.version, exclude_files: exclude_files)
107
-
108
- feature_paths = YAML.load(Git.read(migration.features_yml, branch: migration.version))
109
- append_features_yml(feature_paths, append: append)
110
-
111
- feature_paths.each { |feature_path|
112
- if path != File.dirname(feature_path)
113
- FileUtils.ln_s(File.join("../..", feature_path), path)
114
- Git.add(File.join(path, File.basename(feature_path)))
115
- end
116
- }
12
+ def self.load
13
+ state = MigrationState.load
14
+ Migration.new(state.version, state.base_version)
117
15
  end
118
16
 
119
- def migrate(database_name)
120
- puts "ReleaseMigration#migrate"
121
- if File.exist?(migrations_sql)
122
- Dir.chdir(path) {
123
- puts " cd #{path}"
124
- puts " psql -d #{database_name} < #{MIGRATIONS_SQL}"
125
- Command.command "psql -d #{database_name} < #{MIGRATIONS_SQL}"
126
- }
127
- end
17
+ # Remove content of the migration/ directory
18
+ def self.clear
19
+ FileUtils.empty!(MIGRATION_DIR)
128
20
  end
129
21
 
22
+ def exist?() MigrationState.exist? end
130
23
 
131
- def self.files(path)
132
- FILES.map { |file| File.join(path, file) }
24
+ def create()
25
+ files = Share.cp "migration/*", MIGRATION_DIR
26
+ state = MigrationState.new.write(version: version, base_version: base_version)
27
+ Git.add files, state.path
28
+ self
133
29
  end
134
30
 
135
- def read_features_yml(branch = nil) YAML.load(File.read(features_yml)) || [] end
136
-
137
- def append_features_yml(paths, append: true)
138
- write_features_yml(read_features_yml.insert(append ? -1 : 0, *paths))
31
+ def update(version)
32
+ state = MigrationState.new.read
33
+ @version = state.version = version
34
+ state.write
35
+ Git.add state.path
36
+ self
139
37
  end
140
38
 
141
- def write_features_yml(paths)
142
- FileUtils.cp(template_file(features_yml), features_yml)
143
- File.open(features_yml, "a") { |f| f.write(paths.to_yaml) }
144
- FileUtils.cp(template_file(features_sql), features_sql)
145
- File.open(features_sql, "a") { |f|
146
- paths.map { |path|
147
- f.puts "\\cd #{File.basename(path)}"
148
- f.puts "\\i migrate.sql" }
149
- f.puts "\\cd .."
150
- f.puts
151
- }
152
- Git.add(features_yml)
153
- Git.add(features_sql)
39
+ def migrate(database)
40
+ base_version or raise Internal, "Can't migrate from nil to #{version}"
41
+ version or raise Internal, "Can't migrate from #{base_version} to nil"
42
+ MigrationBuilder.new(database, MIGRATION_DIR).build
154
43
  end
155
44
  end
156
45
 
157
46
  class FeatureMigration < Migration
158
- FEATURE_TMPL_DIR = "features/feature"
159
- FILES = [
160
- MIGRATE_SQL = "migrate.sql",
161
- DIFF_SQL = "diff.sql"
162
- ]
47
+ attr_reader :feature
163
48
 
164
- attr_reader :name
165
- attr_reader :version
166
- attr_reader :release_migration # The enclosing release migration
167
-
168
- attr_reader :migrate_sql
169
- attr_reader :diff_sql
170
-
171
- def features_yml() release_migration.features_yml end
172
- def features_sql() release_migration.features_sql end
173
- def migrations_sql() release_migration.migrations_sql end
174
- def release_diff_sql() release_migration.diff_sql end
175
-
176
- def files() [migrate_sql, diff_sql] end
177
- def release_files() release_migration.files end
178
-
179
- def feature_versions() release_migration.feature_versions end
180
- def feature_paths() release_migration.feature_paths end
181
-
182
- def initialize(path, name: File.basename(path))
183
- Migration.feature?(path) or raise "Expected a feature path, got #{path}"
184
- super(path, FEATURE_TMPL_DIR)
185
- @name = name
186
- @version = Migration.version(path)
187
- @release_migration = ReleaseMigration.new(File.dirname(path))
188
- @migrate_sql = File.join(path, MIGRATE_SQL)
189
- @diff_sql = File.join(path, DIFF_SQL)
49
+ def initialize(feature, base_version)
50
+ super(base_version, base_version)
51
+ @feature = feature
190
52
  end
191
53
 
192
- def exist?() release_migration.exist? && super end
193
- def create()
194
- release_migration.exist? || release_migration.create
195
- super
54
+ def self.load
55
+ migration_state = MigrationState.load
56
+ feature_state = FeatureState.load
57
+ FeatureMigration.new(feature_state.feature, migration_state.base_version)
196
58
  end
197
59
 
198
- def present?() release_migration.present? && super end
199
- def prepare()
200
- release_migration.present? || release_migration.prepare
60
+ def create()
201
61
  super
202
- release_migration.append_features_yml([path])
203
- end
204
-
205
- def include_feature(migration)
206
- release_migration.include_feature(migration, append: false)
207
- end
208
-
209
- def self.files(path)
210
- release_files + FILES.map { |file| File.join(path, file) }
211
- end
212
-
213
- def self.release_files(path)
214
- ReleaseMigration.files(path)
215
- end
216
-
217
- def dump
218
- super {
219
- puts "name: #{name}"
220
- puts "release_migration: #{path}"
221
- }
62
+ files = Share.cp "migration", File.join(MIGRATION_DIR, feature)
63
+ state = FeatureState.write(feature: feature)
64
+ Git.add files, state.path
65
+ self
222
66
  end
223
67
  end
224
68
  end
225
69
 
226
70
 
227
-
228
-
229
-
230
-