Class: VCLog::Repo

Inherits:
Object
  • Object
show all
Defined in:
lib/vclog/repo.rb

Overview

Encapsulate representaiton of the repository.

Constant Summary collapse

ROOT_GLOB =

File glob used to find project root directory.

'{.ruby,.map/,.git/,.hg/,_darcs/,.svn/}'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root, options = {}) ⇒ Repo

Setup new Repo instance.

Raises:

  • (ArgumentError)


35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/vclog/repo.rb', line 35

def initialize(root, options={})
  options[:root] = root if root

  @config  = Config.new(options)
  @options = options

  vcs_type = @config.vcs_type

  raise ArgumentError, "Not a recognized version control system." unless vcs_type

  @adapter = Adapters.const_get(vcs_type.capitalize).new(self)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(s, *a, &b) ⇒ Object

Delegate missing methods to SCM adapter.



267
268
269
270
271
272
273
# File 'lib/vclog/repo.rb', line 267

def method_missing(s, *a, &b)
  if adapter.respond_to?(s)
    adapter.send(s, *a, &b)
  else
    super(s,*a,&b)
  end
end

Instance Attribute Details

#optionsObject (readonly)

Options hash.



30
31
32
# File 'lib/vclog/repo.rb', line 30

def options
  @options
end

#rootObject (readonly)

Project root directory.



25
26
27
# File 'lib/vclog/repo.rb', line 25

def root
  @root
end

Instance Method Details

#adapterObject

Returns instance of an Adapter subclass.



51
52
53
# File 'lib/vclog/repo.rb', line 51

def adapter
  @adapter
end

#apply_heuristics(changes) ⇒ Object

Apply heuristics to changes.



128
129
130
131
132
# File 'lib/vclog/repo.rb', line 128

def apply_heuristics(changes)
  changes.each do |change|
    change.apply_heuristics(heuristics)
  end
end

#ask_yn(message) ⇒ Object (private)

Ask yes/no question.



280
281
282
283
284
285
286
287
# File 'lib/vclog/repo.rb', line 280

def ask_yn(message)
  case ask(message)
  when 'y', 'Y', 'yes'
    true
  else
    false
  end
end

#autotag(prefix = nil) ⇒ Object

Read history file and make a commit tag for any release not already tagged. Unless the force option is set the user will be prompted for each new tag.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/vclog/repo.rb', line 95

def autotag(prefix=nil)
  history_file.tags.each do |tag|
    label = "#{prefix}#{tag.name}"
    if not adapter.tag?(label)
      chg = adapter.change_by_date(tag.date)
      if chg
        if force? or ask_yn(new_tag_message(label, tag) + "\nCreate tag? [yN] ")
          adapter.tag(chg.rev, label, tag.date, tag.message)
        end
      else
        puts "No commit found for #{label} #{tag.date.strftime('%Y-%m-%d')}."
      end
    end
  end
end

#bumpString

TODO:

Allow configuration of version bump thresholds

Make an educated guess as to the next version number based on changes made since previous release.

Returns:

  • (String)

    version number



214
215
216
217
218
219
220
221
222
223
224
# File 'lib/vclog/repo.rb', line 214

def bump
  last_release = releases(changes).first
  max = last_release.changes.map{ |c| c.level }.max
  if max > 1
    bump_part('major')
  elsif max >= 0
    bump_part('minor')
  else
    bump_part('patch')
  end
end

#bump_part(part = nil) ⇒ Object

Provides a bumped version number.



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/vclog/repo.rb', line 229

def bump_part(part=nil)
  raise "bad version part - #{part}" unless ['major', 'minor', 'patch', 'build', ''].include?(part.to_s)

  if tags.last
    v = tags[-1].name # TODO: ensure the latest version
    v = tags[-2].name if v == 'HEAD'
  else
    v = '0.0.0'
  end

  v = v.split(/\W/)    # TODO: preserve split chars

  case part.to_s
  when 'major'
    v[0] = v[0].succ
    (1..(v.size-1)).each{ |i| v[i] = '0' }
    v.join('.')
  when 'minor'
    v[1] = '0' unless v[1]
    v[1] = v[1].succ
    (2..(v.size-1)).each{ |i| v[i] = '0' }
    v.join('.')
  when 'patch'
    v[1] = '0' unless v[1]
    v[2] = '0' unless v[2]
    v[2] = v[2].succ
    (3..(v.size-1)).each{ |i| v[i] = '0' }
    v.join('.')
  else
    v[-1] = '0' unless v[-1]
    v[-1] = v[-1].succ
    v.join('.')
  end
end

#change_pointsObject

List of all change points.



121
122
123
# File 'lib/vclog/repo.rb', line 121

def change_points
  @change_points ||= apply_heuristics(adapter.change_points)
end

#changesObject

List of all changes.



114
115
116
# File 'lib/vclog/repo.rb', line 114

def changes
  @changes ||= apply_heuristics(adapter.changes)
end

#configObject

Configuration.



58
59
60
# File 'lib/vclog/repo.rb', line 58

def config
  @config
end

#force?Boolean

Check force option.

Returns:

  • (Boolean)


79
80
81
# File 'lib/vclog/repo.rb', line 79

def force?
  config.force?
end

#heuristicsObject

Load heuristics script.



72
73
74
# File 'lib/vclog/repo.rb', line 72

def heuristics
  config.heuristics
end

#history_fileObject

Access to Repo’s HISTORY file.



86
87
88
# File 'lib/vclog/repo.rb', line 86

def history_file
  @history_file ||= HistoryFile.new(options[:history_file] || root)
end

#new_tag_message(label, tag) ⇒ Object (private)

Returns a String.



292
293
294
# File 'lib/vclog/repo.rb', line 292

def new_tag_message(label, tag)
  "#{label} / #{tag.date.strftime('%Y-%m-%d')}\n#{tag.message}"
end

#releases(changes) ⇒ Array<Release>

Collect releases for the given set of changes.

Releases are groups of changes segregated by tags. The release version, release date and release note are defined by hard tag commits.

Parameters:

  • changes (Array<Change>)

    List of Change objects.

Returns:

  • (Array<Release>)

    List of Release objects.



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/vclog/repo.rb', line 146

def releases(changes)
  rel  = []
  tags = self.tags.dup

  #ver  = repo.bump(version)

  name = config.version || 'HEAD'
  user = adapter.user
  date = ::Time.now + (3600 * 24) # one day ahead

  change = Change.new(:id=>'HEAD', :date=>date, :who=>user)

  tags << Tag.new(:name=>name, :date=>date, :who=>user, :msg=>"Current Development", :commit=>change)

  # TODO: Do we need to add a Time.now tag?
  # add current verion to release list (if given)
  #previous_version = tags[0].name
  #if current_version < previous_version  # TODO: need to use natural comparision
  #  raise ArgumentError, "Release version is less than previous version (#{previous_version})."
  #end

  # sort by release date
  tags = tags.sort{ |a,b| a.date <=> b.date }

  # organize into deltas
  delta = []
  last  = nil
  tags.each do |tag|
    delta << [tag, [last, tag.commit.date]]
    last = tag.commit.date
  end

  # gather changes for each delta
  delta.each do |tag, (started, ended)|
    if started
      set = changes.select{ |c| c.date >= started && c.date < ended  }
      #gt_vers, gt_date = gt.name, gt.date
      #lt_vers, lt_date = lt.name, lt.date
      #gt_date = Time.parse(gt_date) unless Time===gt_date
      #lt_date = Time.parse(lt_date) unless Time===lt_date
      #log = changelog.after(gt).before(lt)
    else
      #lt_vers, lt_date = lt.name, lt.date
      #lt_date = Time.parse(lt_date) unless Time===lt_date
      #log = changelog.before(lt_date)
      set = changes.select{ |c| c.date < ended }
    end
    rel << Release.new(tag, set)
  end
  rel.sort
end

#report(options) ⇒ Object

Print a report with given options.



201
202
203
204
# File 'lib/vclog/repo.rb', line 201

def report(options)
  report = Report.new(self, options)
  report.print
end