Class: Gjp::Project

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/gjp/project.rb

Overview

encapsulates a Gjp project directory

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Logging

#log

Constructor Details

#initialize(path) ⇒ Project

Returns a new instance of Project.



13
14
15
16
# File 'lib/gjp/project.rb', line 13

def initialize(path)
  @full_path = Gjp::Project.find_project_dir(File.expand_path(path))
  @git = Gjp::Git.new(@full_path)
end

Instance Attribute Details

#full_pathObject

Returns the value of attribute full_path.



10
11
12
# File 'lib/gjp/project.rb', line 10

def full_path
  @full_path
end

#gitObject

Returns the value of attribute git.



11
12
13
# File 'lib/gjp/project.rb', line 11

def git
  @git
end

Class Method Details

.find_project_dir(starting_dir) ⇒ Object

finds the project directory up in the tree, like git does



27
28
29
30
31
32
33
34
35
36
# File 'lib/gjp/project.rb', line 27

def self.find_project_dir(starting_dir)
  result = starting_dir
  while is_project(result) == false && result != "/"
    result = File.expand_path("..", result)
  end

  raise NoProjectDirectoryError.new(starting_dir) if result == "/"

  result
end

.init(dir) ⇒ Object

inits a new project directory structure



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/gjp/project.rb', line 62

def self.init(dir)
  Dir.chdir(dir) do
    Gjp::Git.new(".").init

    FileUtils.mkdir_p "src"
    FileUtils.mkdir_p "kit"

    # populate the project with templates and take a snapshot
    project = Project.new(".")

    template_manager = Gjp::TemplateManager.new
    template_manager.copy "output", "."
    template_manager.copy "kit", "."
    template_manager.copy "src", "."
    template_manager.copy "gitignore", ".gitignore"

    project.take_snapshot "Template files added", :init
  end
end

.is_project(dir) ⇒ Object

returns true if the specified directory is a valid gjp project



39
40
41
42
43
# File 'lib/gjp/project.rb', line 39

def self.is_project(dir)
  File.directory?(File.join(dir, "src")) &&
  File.directory?(File.join(dir, "kit")) &&
  File.directory?(File.join(dir, ".git"))
end

Instance Method Details

#dry_runObject

starts a dry running phase: files added to kit/ will be added to the kit package, src/ will be reset at the current state when finished



85
86
87
88
89
90
91
92
93
94
# File 'lib/gjp/project.rb', line 85

def dry_run
  if is_dry_running
    return false
  end

  current_directory = Pathname.new(Dir.pwd).relative_path_from Pathname.new(@full_path)

  take_snapshot("Dry-run started", :dry_run_started, current_directory)
  true
end

#finish(abort) ⇒ Object

ends a dry-run. if abort is true, reverts the whole directory if abort is false, reverts sources and updates output file lists



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/gjp/project.rb', line 104

def finish(abort)
  if is_dry_running
    if abort
      @git.revert_whole_directory(".", latest_tag(:dry_run_started))
      @git.delete_tag(latest_tag(:dry_run_started))
    else
      take_snapshot "Changes during dry-run", :dry_run_changed

      @git.revert_whole_directory("src", latest_tag(:dry_run_started))

      take_snapshot "Dry run finished", :dry_run_finished
    end
    return true
  end
  false
end

#from_directory(subdirectory = "") ⇒ Object

runs a block from the project directory or a subdirectory



180
181
182
183
184
# File 'lib/gjp/project.rb', line 180

def from_directory(subdirectory = "")
  Dir.chdir(File.join(@full_path, subdirectory)) do
    yield
  end
end

#get_package_name(dir) ⇒ Object

returns the package name corresponding to the specified dir, if any raises NoPackageDirectoryError if dir is not a (sub)directory of a package



47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/gjp/project.rb', line 47

def get_package_name(dir)
  dir_path = Pathname.new(File.expand_path(dir)).relative_path_from(Pathname.new(@full_path))
  components = dir_path.to_s.split(File::SEPARATOR)
  if components.count >= 2 &&
   components.first == "src" &&
   Dir.exist?(File.join(@full_path, components[0], components[1]))
    components[1]
 else
   raise NoPackageDirectoryError
 end
rescue ArgumentError, NoProjectDirectoryError
  raise NoPackageDirectoryError.new(dir)
end

#get_produced_files(package) ⇒ Object

returns a list of files produced during dry-runs in a certain package



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/gjp/project.rb', line 192

def get_produced_files(package)
  dry_run_count = latest_tag_count(:dry_run_changed)
  log.debug "Getting produced files from #{dry_run_count} dry runs"
  if dry_run_count >= 1
    package_dir = File.join("src", package)
    (1..dry_run_count).map do |i|
      @git.changed_files_between("dry_run_started_#{i}", "dry_run_changed_#{i}", package_dir)
    end
      .flatten
      .uniq
      .sort
      .map { |file| Pathname.new(file).relative_path_from(Pathname.new(package_dir)).to_s }
  else
    []
  end
end

#is_dry_runningObject

returns true iff we are currently dry-running



97
98
99
# File 'lib/gjp/project.rb', line 97

def is_dry_running
  latest_tag_count(:dry_run_started) > latest_tag_count(:dry_run_finished)
end

#latest_dry_run_directoryObject

returns the latest dry run start directory



187
188
189
# File 'lib/gjp/project.rb', line 187

def latest_dry_run_directory
  @git.get_message(latest_tag(:dry_run_started))
end

#latest_tag(prefix) ⇒ Object

returns the tag with maximum count for a given tag prefix



170
171
172
# File 'lib/gjp/project.rb', line 170

def latest_tag(prefix)
  "#{prefix}_#{latest_tag_count(prefix)}"
end

#latest_tag_count(prefix) ⇒ Object

returns the maximum tag count for a given tag prefix



175
176
177
# File 'lib/gjp/project.rb', line 175

def latest_tag_count(prefix)
  @git.get_tag_maximum_suffix(prefix)
end

#merge_new_content(new_content, path, snapshot_message, tag_prefix) ⇒ Object

replaces content in path with new_content, takes a snapshot using snapshot_message and tag_prefix and 3-way merges new and old content with a previous snapshotted file same path tag_prefix, if it exists. returns the number of conflicts



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/gjp/project.rb', line 138

def merge_new_content(new_content, path, snapshot_message, tag_prefix)
  from_directory do
    log.debug "merging new content to #{path} with prefix #{tag_prefix}"
    already_existing = File.exist? path
    previous_tag = latest_tag(tag_prefix)

    if already_existing
      log.debug "moving #{path} to #{path}.gjp_user_edited"
      File.rename path, "#{path}.gjp_user_edited"
    end

    File.open(path, "w") { |io| io.write(new_content) }
    log.debug "taking snapshot with new content: #{snapshot_message}"
    take_snapshot(snapshot_message, tag_prefix)

    if already_existing
      if previous_tag == ""
        previous_tag = latest_tag(tag_prefix)
        log.debug "there was no tag with prefix #{tag_prefix} before snapshot"
        log.debug "defaulting to #{previous_tag} after snapshot"
      end

      # 3-way merge
      conflict_count = @git.merge_with_tag("#{path}", "#{path}.gjp_user_edited", previous_tag)
      File.delete "#{path}.gjp_user_edited"
      return conflict_count
    end
    return 0
  end
end

#nameObject



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

def name
  File.basename(@full_path)
end

#purge_jarsObject

moves any .jar from src/ to kit/ and links it back



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/gjp/project.rb', line 210

def purge_jars
  from_directory do
    result = []
    Find.find("src") do |file|
      if file =~ /.jar$/ && !File.symlink?(file)
        new_location = File.join("kit", "jars", Pathname.new(file).split[1])
        FileUtils.mv(file, new_location)

        link_target = Pathname.new(new_location)
          .relative_path_from(Pathname.new(file).split.first)
          .to_s

        File.symlink(link_target, file)
        result << [file, new_location]
      end
    end

    result
  end
end

#take_snapshot(message, tag_prefix = nil, tag_message = nil) ⇒ Object

takes a revertable snapshot of this project



122
123
124
125
126
127
128
129
130
131
132
# File 'lib/gjp/project.rb', line 122

def take_snapshot(message, tag_prefix = nil, tag_message = nil)
  tag = (
    if tag_prefix
      "#{tag_prefix}_#{latest_tag_count(tag_prefix) + 1}"
    else
      nil
    end
  )

  @git.commit_whole_directory(message, tag, tag_message)
end

#versionObject



22
23
24
# File 'lib/gjp/project.rb', line 22

def version
  latest_tag_count(:dry_run_finished)
end