Class: Prick::Project

Inherits:
Object
  • Object
show all
Defined in:
lib/prick/project.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name = nil, user = nil) ⇒ Project

‘name` is the project name and `user` is the name of the Postgresql user. `name` defaults to the name of the current directory and `user` defaults to `name`



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/prick/project.rb', line 77

def initialize(name = nil, user = nil)
  @name = name || File.basename(Dir.getwd)
  @name =~ PROJECT_NAME_RE or raise Error, "Illegal project name: #@name"
  @user = user || @name
  @user =~ USER_NAME_RE or raise Error, "Illegal postgres user name: #@user"
#     Rdbms.ensure_state(:user_exist, @user)
  Rdbms.create_user(@user) if !Rdbms.exist_user?(@user) # FIXME
  @schema = Schema.new(self)
  @database = Database.new(@name, @user)

  @builds_by_name = {}
  @builds_by_version = {}

  @releases = []
  @prereleases = []
  @features = {}

  @ignored_feature_nodes = []
  @ignored_release_nodes = []

  @orphan_feature_nodes = []
  @orphan_release_nodes = [] # Includes prereleases

  @orphan_git_branches = []

  load_project
end

Instance Attribute Details

#databaseObject (readonly)

The project database. The project database has the same name as the project and doesn’t include a version. It does not have to exist



31
32
33
# File 'lib/prick/project.rb', line 31

def database
  @database
end

#featuresObject (readonly)

Hash from base-release name to feature. Note that this doesn’t follow release history



44
45
46
# File 'lib/prick/project.rb', line 44

def features
  @features
end

#ignored_feature_nodesObject (readonly)

Ignored and orphaned objects



47
48
49
# File 'lib/prick/project.rb', line 47

def ignored_feature_nodes
  @ignored_feature_nodes
end

#ignored_release_nodesObject (readonly)

Returns the value of attribute ignored_release_nodes.



48
49
50
# File 'lib/prick/project.rb', line 48

def ignored_release_nodes
  @ignored_release_nodes
end

#nameObject (readonly)

Name of project. Used in database and release names. Defaults to the name of the current directory



18
19
20
# File 'lib/prick/project.rb', line 18

def name
  @name
end

#orphan_feature_nodesObject (readonly)

Returns the value of attribute orphan_feature_nodes.



49
50
51
# File 'lib/prick/project.rb', line 49

def orphan_feature_nodes
  @orphan_feature_nodes
end

#orphan_git_branchesObject (readonly)

Returns the value of attribute orphan_git_branches.



51
52
53
# File 'lib/prick/project.rb', line 51

def orphan_git_branches
  @orphan_git_branches
end

#orphan_release_nodesObject (readonly)

Returns the value of attribute orphan_release_nodes.



50
51
52
# File 'lib/prick/project.rb', line 50

def orphan_release_nodes
  @orphan_release_nodes
end

#prereleasesObject (readonly)

List of pre-releases



41
42
43
# File 'lib/prick/project.rb', line 41

def prereleases
  @prereleases
end

#releasesObject (readonly)

List of releases ordered from oldest to newest. Custom releases are sorted after regular releases and alphabetical between themselves



38
39
40
# File 'lib/prick/project.rb', line 38

def releases
  @releases
end

#schemaObject (readonly)

Schema. Represents the schema definition under the schemas/ directory



24
25
26
# File 'lib/prick/project.rb', line 24

def schema
  @schema
end

#userObject (readonly)

Name of Postgresql user that own the database(s). Defaults to #name



21
22
23
# File 'lib/prick/project.rb', line 21

def user
  @user
end

Class Method Details

.initialize_directory(directory) ⇒ Object

Initialize an on-disk prick instance



144
145
146
147
148
149
150
151
152
153
154
# File 'lib/prick/project.rb', line 144

def self.initialize_directory(directory)
  FileUtils.mkdir_p(directory)
  Dir.chdir(directory) {
    DIRS.each { |dir| 
      !File.exist?(dir) or raise Fail, "Already initialized: Directory '#{dir}' exists"
    }

    FileUtils.mkdir_p(DIRS)
    DIRS.each { |dir| FileUtils.touch("#{dir}/.keep") }
  }
end

.initialize_project(name = File.basename(Dir.getwd), user = ) ⇒ Object

Create an initial release 0.0.0. ::initialize_project expects to be executed in the project directory



158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/prick/project.rb', line 158

def self.initialize_project(name = File.basename(Dir.getwd), user = name || ENV['USER'])
  FileUtils.cp("#{SHARE_PATH}/gitignore", ".gitignore")
  FileUtils.cp("#{SHARE_PATH}/schemas/prick/schema.sql", "schemas/prick")
  FileUtils.cp("#{SHARE_PATH}/schemas/prick/data.sql", "schemas/prick")
  Git.init
  Git.add(".")
  Git.commit("Initial import")
  project = Project.new(name, user)
  release = Release.new(project, nil, Version.new("0.0.0"))
  release.create

  Git.delete_branch("master")
end

Instance Method Details

#[](name_or_version) ⇒ Object



105
106
107
108
109
110
111
112
113
114
# File 'lib/prick/project.rb', line 105

def [](name_or_version) 
  name = version = name_or_version
  case name_or_version
    when String; @builds_by_name[name]
    when Version; @builds_by_version[version]
    when NilClass; nil
  else
    raise Internal, "Expected String or Version index, got #{name_or_version.class}"
  end
end

#[]=(name_or_version, branch) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/prick/project.rb', line 116

def []=(name_or_version, branch)
  case name_or_version
    when String; 
      name = name_or_version
      version = Version.new(name)
    when Version
      version = name_or_version
      name = version.to_s
  else
    raise Internal, "Expected String or Version index, got #{name_or_version.class}"
  end
  @builds_by_name[name] = @builds_by_version[version] = branch
end

#build(version = nil) ⇒ Object

Build the current database from the content of schema



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/prick/project.rb', line 287

def build(version = nil)
  if version.nil?
    database.recreate
    schema.build 
  else
    release = self[version] or raise Error, "Can't find release #{version}"
    release.database.recreate
    FileUtils.mkdir_p(CACHE_DIR)
    if !File.directory?(File.join(CLONE_DIR, ".git"))
      FileUtils.rm_rf(CLONE_DIR)
      Command.command "git clone . #{CLONE_DIR}"
    end
    Dir.chdir(CLONE_DIR) {
      Git.checkout_tag(version.to_s)
      project = Project.new(name, user)
      release.database.recreate
      project.schema.build(release.database)
    }
    release.cache
  end
end

#build?(name_or_version) ⇒ Boolean

Returns:

  • (Boolean)


132
133
134
135
136
137
138
139
140
141
# File 'lib/prick/project.rb', line 132

def build?(name_or_version) 
  name = version = name_or_version
  case name_or_version
    when String; @builds_by_name.key?(name)
    when Version; @builds_by_version.key?(version)
    when NilClass; nil
  else
    raise Internal, "Expected String or Version index, got #{name_or_version.class}"
  end
end

#buildsObject

List of all builds



34
# File 'lib/prick/project.rb', line 34

def builds() @builds_by_name.values end

#checkout(name) ⇒ Object

COMMON METHODS



177
178
179
180
# File 'lib/prick/project.rb', line 177

def checkout(name)
  check_clean
  Git.checkout_branch(name)
end

#cleanup(version = nil, project: false) ⇒ Object

Remove temporary files and databases



348
349
350
351
352
353
354
355
356
357
358
# File 'lib/prick/project.rb', line 348

def cleanup(version = nil, project: false)
  if version
    release = self[version]
    release.archive.delete
    release.database.drop
  else
    FileUtils::rm_rf CLONE_DIR
    FileUtils::rm_f Dir.glob(File.join(CACHE_DIR, Prick.dump_glob(name)))
    databases(all: true, project: project).each(&:drop)
  end
end

#commit_featureObject



277
278
279
280
281
# File 'lib/prick/project.rb', line 277

def commit_feature
  msg = File.readlines(".git/MERGE_MSG").first.chomp
  Git.commit msg
#     Command.command "git commit -F .git/MERGE_MSG"
end

#create_feature(name) ⇒ Object



262
263
264
265
266
267
# File 'lib/prick/project.rb', line 262

def create_feature(name)
  check_clean
  release? or raise Error, "Need to be on a release branch to create a feature"
  feature = Feature.new(self, release, name)
  feature.create
end

#create_migrationObject



283
284
# File 'lib/prick/project.rb', line 283

def create_migration
end

#create_prerelease(target_version) ⇒ Object



248
249
250
251
252
253
# File 'lib/prick/project.rb', line 248

def create_prerelease(target_version)
  check_clean
  release? or raise Error, "Need to be on a release branch to create a pre-release"
  prerelease = Prerelease.new(self, release, target_version.increment(:pre, 0), target_version)
  prerelease.create
end

#create_release(version = nil) ⇒ Object



233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/prick/project.rb', line 233

def create_release(version = nil)
  check_clean
  if release?
    !version.nil? or raise Error, "Need version argument when on a release branch"
    new_release = Release.new(self, release, version)
  elsif prerelease?
    version.nil? or raise Error, "Can't use version argument when on a pre-release branch"
    new_release = release.target_release
  else
    raise Error, "Need to be on a release or pre-prelease branch to create a new release"
  end
  new_release.create
  new_release
end

#databases(all: false, project: true) ⇒ Object

Sorted list of databases associated with a release. Also include the project database if project. is true. If all: is true, also list databases that match the project but is without an associated release



65
66
67
68
69
70
71
72
# File 'lib/prick/project.rb', line 65

def databases(all: false, project: true)
  r = (project ? [database] : [])
  if all
    r + Rdbms.list_databases(Prick.database_re(name)).map { |name| Database.new(name, user) }
  else
    r + @releases.map(&:database)
  end
end

#dirty?Boolean

Returns:

  • (Boolean)


130
# File 'lib/prick/project.rb', line 130

def dirty?() !Git.clean? end

#feature(name) ⇒ Object

Create and switch to feature branch



195
196
197
198
199
200
# File 'lib/prick/project.rb', line 195

def feature(name)
  check_clean
  release? or raise Error, "Need to be on a release branch to create a feature"
  feature = Feature.new(self, release, name).ensure(:active)
  Git.commit "Created feature #{name}"
end

#feature?Boolean

True if we’re on a feature branch

Returns:

  • (Boolean)


60
# File 'lib/prick/project.rb', line 60

def feature?() release && !release.version.feature.nil? end

#include_feature(feature_version) ⇒ Object

Note: Does not commit



270
271
272
273
274
275
# File 'lib/prick/project.rb', line 270

def include_feature(feature_version)
  check_clean
  prerelease? || feature? or raise Error, "Need to be on a prerelease or feature branch to include features"
  Git.branch?(feature_version) or raise Error, "Can't find branch #{feature_version}"
  release.include_feature(self[feature_version])
end

#increment_prereleaseObject



255
256
257
258
259
260
# File 'lib/prick/project.rb', line 255

def increment_prerelease
  check_clean
  prerelease? or raise Error, "Need to be on a pre-release branch to make an incremental pre-release"
  prerelease = Prerelease.new(self, release.base_release, release.version.increment(:pre))
  prerelease.create
end

#load_database(version, file = nil) ⇒ Object

Load file into the given database version. If version is nil the current database is used. If file is nil, the database associated with the given version is used. Not both version and file can be nil



320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/prick/project.rb', line 320

def load_database(version, file = nil)
  check_clean
  !version.nil? || !file.nil? or raise Fail, "Not both version and file can be nil"
  if version
    release = self[version]
    file ||= release.archive.path
    File.file?(file) or raise Error, "Can't find #{file}"
    release.database.recreate
    release.database.load(file)
  else
    database.recreate
    database.load(file)
  end
end

#migrateObject

Migrate the database from its current version to the current release on disk



183
184
185
186
187
188
189
190
# File 'lib/prick/project.rb', line 183

def migrate
  check_clean
  database.exist? or raise "Project database not found"
  history = release.history.keys.reverse.drop(1)
  history.select! { |release| release.version >= database.version }
  history.each { |release| release.migration.migrate(database.name) }
  database.version = release.version
end

#pathObject

Returns the full path to the project directory (TODO: Test)



173
# File 'lib/prick/project.rb', line 173

def path() File.expand_path(".") end

#prepare_migrationObject



221
222
223
224
225
226
227
228
229
230
231
# File 'lib/prick/project.rb', line 221

def prepare_migration
  check_clean
  prerelease? || feature? or raise Error, "Need to be on a prerelease or feature branch to include features"

  base_database = release.base_release.database
  build(base_release.version) if !base_database.loaded?
  build # if !database.loaded?

  diff_sql = release.migration.diff_sql
  Migra.migrate(base_database.name, database.name, diff_sql)
end

#prepare_releaseObject

Create the first prerelease

TODO Make it just copy files and commit them. Supports a single-user workflow. Requires a :prepared state



215
216
217
218
219
# File 'lib/prick/project.rb', line 215

def prepare_release
  check_clean
  release? or raise Error, "Need to be on a release branch to prepare a new release"
  release.prepare
end

#prerelease?Boolean

True if we’re on a pre-release branch

Returns:

  • (Boolean)


57
# File 'lib/prick/project.rb', line 57

def prerelease?() release && !release.version.pre.nil? && !feature? end

#rebase(version) ⇒ Object



202
203
204
205
206
207
# File 'lib/prick/project.rb', line 202

def rebase(version)
  check_clean
  feature? or raise Error, "Need to be on a feature branch to rebase"
  release.rebase(version)
  Git.commit "Rebased to #{version}"
end

#releaseObject

The current release, prerelease or feature



27
# File 'lib/prick/project.rb', line 27

def release() @builds_by_name[Git.current_branch] end

#release?Boolean

True if we’re on a release branch

Returns:

  • (Boolean)


54
# File 'lib/prick/project.rb', line 54

def release?() release && release.version.pre.nil? && !feature? end

#use_database(file = nil) ⇒ Object

Load file into current database



310
311
312
313
314
315
# File 'lib/prick/project.rb', line 310

def use_database(file = nil)
  check_clean
  File.file?(file) or raise Error, "Can't find #{file}"
  database.recreate
  database.load(file)
end