jm81-svn-fixture 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.md +77 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/lib/svn-fixture.rb +71 -0
- data/lib/svn-fixture/directory.rb +99 -0
- data/lib/svn-fixture/file.rb +58 -0
- data/lib/svn-fixture/repository.rb +168 -0
- data/lib/svn-fixture/revision.rb +65 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/svn-fixture/config_spec.rb +29 -0
- data/spec/svn-fixture/directory_spec.rb +194 -0
- data/spec/svn-fixture/file_spec.rb +65 -0
- data/spec/svn-fixture/fixtures/hello_world.rb +43 -0
- data/spec/svn-fixture/integration_spec.rb +102 -0
- data/spec/svn-fixture/repository_spec.rb +350 -0
- data/spec/svn-fixture/revision_spec.rb +70 -0
- data/spec/svn-fixture_spec.rb +89 -0
- metadata +81 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jared Morgan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
svn-fixture
|
2
|
+
===========
|
3
|
+
|
4
|
+
svn-fixture simplifies creating (or updating) a Subversion repository. It is
|
5
|
+
designed to be used in tests that require a Subversion repo, but can also be
|
6
|
+
used to initialize a repository according to some template. svn-fixture depends
|
7
|
+
on the Subversion Ruby bindings (see below for Installation help).
|
8
|
+
|
9
|
+
##Usage
|
10
|
+
|
11
|
+
svn-fixture uses blocks to mimic the structure of the Repository, in the
|
12
|
+
hierarchy: Repository -> Revision -> Directory tree structure with
|
13
|
+
subdirectories and files. For example:
|
14
|
+
|
15
|
+
SvnFixture::repo('hello_world') do
|
16
|
+
revision(1, 'Create directories',
|
17
|
+
:author => 'jmorgan',
|
18
|
+
:date => Time.parse('2009-01-01 12:00:00Z')) do
|
19
|
+
dir 'app'
|
20
|
+
dir 'docs'
|
21
|
+
dir 'lib'
|
22
|
+
end
|
23
|
+
|
24
|
+
revision 2, 'Add a file' do
|
25
|
+
dir 'app' do
|
26
|
+
file 'hello.rb' do
|
27
|
+
prop 'is_ruby', 'Yes'
|
28
|
+
body 'puts "Hello World"'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
SvnFixture::repo('hello_world').commit
|
35
|
+
|
36
|
+
See spec/svn-fixture/fixtures/hello_world.rb and
|
37
|
+
spec/svn-fixture/integration_spec.rb for a more complete example.
|
38
|
+
|
39
|
+
Each Repository is given a name ('hello_world' in the example above), so it can
|
40
|
+
be reopened multiple times. Repository#revision defines a new Revision. It
|
41
|
+
requires a name--but only for informational purposes--and a log message. A
|
42
|
+
Revision also accepts an options Hash including optional :author and :date
|
43
|
+
revision properties.
|
44
|
+
|
45
|
+
Within a Revision is a directory tree, specifying only **changes** in that
|
46
|
+
Revision. See Directory and File classes for details on available methods.
|
47
|
+
|
48
|
+
To actually (optionally) create the repository and make the changes and commits
|
49
|
+
specified in the Revision blocks, call Repository#commit. See Repository class
|
50
|
+
for finer tuned control over the create/checkout/commit process.
|
51
|
+
|
52
|
+
##Installation
|
53
|
+
|
54
|
+
Install Subversion Swig bindings for Ruby. Some distros have a package for this.
|
55
|
+
In debian: sudo apt-get install libsvn-ruby . See
|
56
|
+
[https://bssvnbrowser.bountysource.com/docs/subversion_ruby_bindings](https://bssvnbrowser.bountysource.com/docs/subversion_ruby_bindings) or
|
57
|
+
[http://svn.collab.net/repos/svn/trunk/subversion/bindings/swig/INSTALL](http://svn.collab.net/repos/svn/trunk/subversion/bindings/swig/INSTALL)
|
58
|
+
for more information.
|
59
|
+
|
60
|
+
To install the gem:
|
61
|
+
|
62
|
+
gem sources -a http://gems.github.com
|
63
|
+
sudo gem install jm81-svn-fixture
|
64
|
+
|
65
|
+
To require:
|
66
|
+
|
67
|
+
gem 'jm81-svn-fixture'
|
68
|
+
require 'svn-fixture'
|
69
|
+
|
70
|
+
Note: This library could work using the svn command line client instead. I use
|
71
|
+
the bindings regularly, so using them makes sense for me. However, if you want
|
72
|
+
to be able to use svn-fixture without installing the bindings, please send an
|
73
|
+
email to jmorgan at morgancreative dot net, and I'll give it a shot.
|
74
|
+
|
75
|
+
##Copyright
|
76
|
+
|
77
|
+
Copyright (c) 2009 Jared Morgan. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "svn-fixture"
|
8
|
+
gem.summary = %Q{TODO}
|
9
|
+
gem.email = "jmorgan@morgancreative.net"
|
10
|
+
gem.homepage = "http://github.com/jm81/svn-fixture"
|
11
|
+
gem.authors = ["Jared Morgan"]
|
12
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
13
|
+
end
|
14
|
+
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'spec/rake/spectask'
|
20
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
21
|
+
spec.libs << 'lib' << 'spec'
|
22
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
23
|
+
end
|
24
|
+
|
25
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
26
|
+
spec.libs << 'lib' << 'spec'
|
27
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
28
|
+
spec.rcov = true
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
task :default => :spec
|
33
|
+
|
34
|
+
require 'rake/rdoctask'
|
35
|
+
Rake::RDocTask.new do |rdoc|
|
36
|
+
if File.exist?('VERSION.yml')
|
37
|
+
config = YAML.load(File.read('VERSION.yml'))
|
38
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
39
|
+
else
|
40
|
+
version = ""
|
41
|
+
end
|
42
|
+
|
43
|
+
rdoc.rdoc_dir = 'rdoc'
|
44
|
+
rdoc.title = "svn-fixture #{version}"
|
45
|
+
rdoc.rdoc_files.include('README*')
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
47
|
+
end
|
48
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/svn-fixture.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "tmpdir"
|
3
|
+
require "date"
|
4
|
+
require "time"
|
5
|
+
# Subversion Ruby bindings must be installed (see README)
|
6
|
+
require "svn/core"
|
7
|
+
require "svn/fs"
|
8
|
+
require "svn/repos"
|
9
|
+
|
10
|
+
module SvnFixture
|
11
|
+
CONFIG_DEFAULTS = {
|
12
|
+
:base_path => File.join(Dir.tmpdir, 'svn-fixture')
|
13
|
+
}
|
14
|
+
|
15
|
+
class << self
|
16
|
+
# SvnFixture::config method returns Hash that can be edited.
|
17
|
+
# The only current option is
|
18
|
+
# +:base_path+ : The path at which repositories are created. It default
|
19
|
+
# to the OS tmp directory, plus "svn-fixture". For example,
|
20
|
+
# "/tmp/svn-fixture". The repo name is then appended in
|
21
|
+
# +SvnFixture::Repository+.
|
22
|
+
def config
|
23
|
+
@config ||= CONFIG_DEFAULTS.dup
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return time string formatted as expected by ::Svn::Client::Context#propset
|
27
|
+
# (example 2009-06-28T12:00:00.000000Z). If +val+ does not respond to
|
28
|
+
# +strftime+, val will first be parsed via +Time.parse+.
|
29
|
+
def svn_time(val)
|
30
|
+
return nil if val.nil?
|
31
|
+
val = Time.parse(val) unless val.respond_to?(:strftime)
|
32
|
+
val.strftime("%Y-%m-%dT%H:%M:%S.000000Z")
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return a Date or Time formatted as expected by
|
36
|
+
# ::Svn::Client::Context#propset (see +svn_time+); leave other values alone.
|
37
|
+
def svn_prop(val)
|
38
|
+
val.respond_to?(:strftime) ? svn_time(val) : val
|
39
|
+
end
|
40
|
+
|
41
|
+
# .repo is just a shortcut to +SvnFixture::Repository.get+
|
42
|
+
def repo(*args, &block)
|
43
|
+
SvnFixture::Repository.get(*args, &block)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Setup and return a simple ::Svn::Client::Context. This is called by
|
47
|
+
# Repository#checkout, but can also be used in called Directory.new or
|
48
|
+
# File.new directly. See SvnFixture::File for examples.
|
49
|
+
def simple_context
|
50
|
+
ctx = ::Svn::Client::Context.new
|
51
|
+
|
52
|
+
# I don't understand the auth_baton and log_baton, so I set them here,
|
53
|
+
# then use revision properties.
|
54
|
+
ctx.add_username_prompt_provider(0) do |cred, realm, username, may_save|
|
55
|
+
cred.username = "ANON"
|
56
|
+
end
|
57
|
+
ctx.set_log_msg_func {|items| [true, ""]}
|
58
|
+
ctx
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if defined?(Merb::Plugins)
|
64
|
+
# Make config accessible through Merb's Merb::Plugins.config hash
|
65
|
+
Merb::Plugins.config[:svn_fixture] = SvnFixture.config
|
66
|
+
end
|
67
|
+
|
68
|
+
# Require classes
|
69
|
+
%w{ repository revision directory file }.each do |file|
|
70
|
+
require File.dirname(__FILE__) + '/svn-fixture/' + file
|
71
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module SvnFixture
|
2
|
+
# A Directory to be added to or edited within the Repository. Normally, this
|
3
|
+
# would br done through Directory#dir, in a block given to a Directory or
|
4
|
+
# Revision, for example:
|
5
|
+
#
|
6
|
+
# SvnFixture.repo('repo_name') do
|
7
|
+
# revision(1, 'msg') do
|
8
|
+
# dir('test-dir') do
|
9
|
+
# prop('name', 'value')
|
10
|
+
# file('file.txt')
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# In that case, Revision takes care of passing the +ctx+ argument.
|
16
|
+
#
|
17
|
+
# To call SvnFixture::Directory.new directly, you will need to set up a
|
18
|
+
# context (instance of Svn::Client::Context) and check out a working copy.
|
19
|
+
# +SvnFixture.simple_context+ is a quick method for settin up a Context.
|
20
|
+
#
|
21
|
+
# Assuming an existing checked out working copy:
|
22
|
+
#
|
23
|
+
# ctx = SvnFixture.simple_context
|
24
|
+
# d = SvnFixture::Directory.new(ctx, '/full/fs/path/to/dir')
|
25
|
+
# d.prop('propname', 'Value')
|
26
|
+
#
|
27
|
+
# Or, call #checkout on Context:
|
28
|
+
#
|
29
|
+
# ctx = SvnFixture.simple_context
|
30
|
+
# ctx.checkout('file:///repository/uri', '/fs/path/of/wc')
|
31
|
+
# d = SvnFixture::Directory.new(ctx, '/fs/path/of/wc/to/dir')
|
32
|
+
# d.prop('propname', 'Value')
|
33
|
+
class Directory
|
34
|
+
|
35
|
+
# +new+ is normally called through Directory#dir (a block to a Revision is
|
36
|
+
# applied to the root Directory).
|
37
|
+
#
|
38
|
+
# Arguments are:
|
39
|
+
# - +ctx+: An Svn::Client::Context, normally from Repository#ctx
|
40
|
+
# - +path+: The path (on the file system) of the Directory in the working
|
41
|
+
# copy.
|
42
|
+
def initialize(ctx, path)
|
43
|
+
@ctx = ctx
|
44
|
+
@path = path
|
45
|
+
@path += "/" unless path[-1] == 47
|
46
|
+
end
|
47
|
+
|
48
|
+
# Create or access a subdirectory. Takes the name of the subdirectory (not a
|
49
|
+
# full path) and an optional block with the subdirectory as self.
|
50
|
+
def dir(name, &block)
|
51
|
+
path = @path + name
|
52
|
+
unless ::File.directory?(path)
|
53
|
+
FileUtils.mkdir_p(path)
|
54
|
+
@ctx.add(path)
|
55
|
+
end
|
56
|
+
d = self.class.new(@ctx, path)
|
57
|
+
d.instance_eval(&block) if block_given?
|
58
|
+
d
|
59
|
+
end
|
60
|
+
|
61
|
+
# Create or access a subdirectory. Takes the name of the file (not a
|
62
|
+
# full path) and an optional block with the File as self.
|
63
|
+
def file(name, &block)
|
64
|
+
path = @path + name
|
65
|
+
unless ::File.file?(path)
|
66
|
+
FileUtils.touch(path)
|
67
|
+
@ctx.add(path)
|
68
|
+
end
|
69
|
+
f = File.new(@ctx, path)
|
70
|
+
f.instance_eval(&block) if block_given?
|
71
|
+
f
|
72
|
+
end
|
73
|
+
|
74
|
+
# Move a File or Directory. From should be an existing node. From and to can
|
75
|
+
# be any relative path below the directory.
|
76
|
+
def move(from, to)
|
77
|
+
@ctx.mv(@path + from, @path + to)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Copy a File or Directory. From should be an existing node. From and to can
|
81
|
+
# be any relative path below the directory.
|
82
|
+
def copy(from, to)
|
83
|
+
@ctx.cp(@path + from, @path + to)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Delete (and remove from Repository) a child node.
|
87
|
+
def delete(name)
|
88
|
+
@ctx.delete(@path + name)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Set a property for the Directory
|
92
|
+
# (see http://svnbook.red-bean.com/en/1.1/ch07s02.html):
|
93
|
+
# - +name+: The property name (must be "human-readable text")
|
94
|
+
# - +value+: The value of the property.
|
95
|
+
def prop(name, value)
|
96
|
+
@ctx.propset(name, SvnFixture.svn_prop(value), @path[0..-2])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module SvnFixture
|
2
|
+
# A File to be added to or edited within the Repository. Normally, this would
|
3
|
+
# done through Directory#file, in a block given to a Directory or
|
4
|
+
# Revision, for example:
|
5
|
+
#
|
6
|
+
# SvnFixture.repo('repo_name') do
|
7
|
+
# revision(1, 'msg') do
|
8
|
+
# file('file.txt') do
|
9
|
+
# prop('name', 'value')
|
10
|
+
# body('Some Text')
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# In that case, Revision takes care of passing the +ctx+ argument.
|
16
|
+
#
|
17
|
+
# To call SvnFixture::File.new directly, you will need to set up a context
|
18
|
+
# (instance of Svn::Client::Context) and check out a working copy.
|
19
|
+
# +SvnFixture.simple_context+ is a quick method for settin up a Context.
|
20
|
+
#
|
21
|
+
# Assuming an existing checked out working copy:
|
22
|
+
#
|
23
|
+
# ctx = SvnFixture.simple_context
|
24
|
+
# f = SvnFixture::File.new(ctx, '/full/fs/path/to/file.txt')
|
25
|
+
# f.prop('propname', 'Value')
|
26
|
+
#
|
27
|
+
# Or, call #checkout on Context:
|
28
|
+
#
|
29
|
+
# ctx = SvnFixture.simple_context
|
30
|
+
# ctx.checkout('file:///repository/uri', '/fs/path/of/wc')
|
31
|
+
# f = SvnFixture::File.new(ctx, '/full/fs/path/to/file.txt')
|
32
|
+
# f.prop('propname', 'Value')
|
33
|
+
class File
|
34
|
+
|
35
|
+
# +new+ is normally called through Directory#file (a block to a Revision is
|
36
|
+
# applied to the root Directory).
|
37
|
+
#
|
38
|
+
# Arguments are:
|
39
|
+
# - +ctx+: An Svn::Client::Context, normally from Repository#ctx
|
40
|
+
# - +path+: The path (on the file system) of the File in the working copy
|
41
|
+
def initialize(ctx, path)
|
42
|
+
@ctx, @path = ctx, path
|
43
|
+
end
|
44
|
+
|
45
|
+
# Set a property for the file
|
46
|
+
# (see http://svnbook.red-bean.com/en/1.1/ch07s02.html):
|
47
|
+
# - +name+: The property name (must be "human-readable text")
|
48
|
+
# - +value+: The value of the property.
|
49
|
+
def prop(name, value)
|
50
|
+
@ctx.propset(name, SvnFixture.svn_prop(value), @path)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Set the content of a file
|
54
|
+
def body(val)
|
55
|
+
::File.open(@path, 'w') { |f| f.write(val) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
module SvnFixture
|
2
|
+
# Repository sets up the repository and is reponsible for checkouts and
|
3
|
+
# the actual commit(s). No actual work is done until +commit+ is called.
|
4
|
+
class Repository
|
5
|
+
attr_reader :repos, :ctx, :wc_path, :revisions
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# Get an SvnFixture::Repository by name. If not found, it creates a new
|
9
|
+
# one. It accepts a block which is evaluated within the Repository
|
10
|
+
# instance. +get+ is useful for re-accessing a Repository after initially
|
11
|
+
# created. For example:
|
12
|
+
#
|
13
|
+
# SvnFixture::Repository.get('test') do
|
14
|
+
# revision(1, 'log msg') ...
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# SvnFixture::Repository.get('test') do
|
18
|
+
# revision(2, 'log msg') ...
|
19
|
+
# revision(3, 'log msg') ...
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# SvnFixture::Repository.get('test').commit
|
23
|
+
def get(name, repos_path = nil, wc_path = nil, &block)
|
24
|
+
if repositories[name]
|
25
|
+
repositories[name].instance_eval(&block) if block_given?
|
26
|
+
repositories[name]
|
27
|
+
else
|
28
|
+
Repository.new(name, repos_path, wc_path, &block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Hash of {name => Repository} of currently defined Repositories
|
33
|
+
def repositories
|
34
|
+
@repositories ||= {}
|
35
|
+
end
|
36
|
+
|
37
|
+
# Remove all Repositories from +.repositories+ and delete repos and
|
38
|
+
# working copy directories. Useful to call upon completion of tests.
|
39
|
+
def destroy_all
|
40
|
+
repositories.each {|name, repo| repo.destroy}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Arguments (last two are optional)
|
45
|
+
# - +name+: The name of the repository, used by Repository.get and used in
|
46
|
+
# +repos_path+ and +wc_path+ if not given.
|
47
|
+
# - +repos_path+: The path where the repository is stored (defaults to
|
48
|
+
# "#{config[:base_path]}/repo_#{name}"
|
49
|
+
# - +wc_path+: The path where the working copy is checked out (defaults to
|
50
|
+
# "#{config[:base_path]}/wc_#{name}"
|
51
|
+
# Note: the paths should be normal file system paths, not file:/// paths.
|
52
|
+
#
|
53
|
+
# +new+ also accepts a block which is evaluated within the Repository
|
54
|
+
# instance:
|
55
|
+
#
|
56
|
+
# SvnFixture::Repository.new('name') do
|
57
|
+
# revision(1, 'log msg') ...
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# Otherwise, you could, for example:
|
61
|
+
#
|
62
|
+
# r = SvnFixture::Repository.new('name')
|
63
|
+
# r.revision(1, 'log msg') do
|
64
|
+
# ...
|
65
|
+
# end
|
66
|
+
# r.commit
|
67
|
+
def initialize(name, repos_path = nil, wc_path = nil, &block)
|
68
|
+
@name = name
|
69
|
+
if self.class.repositories[name]
|
70
|
+
raise RuntimeError, "A Repository with this name (#{@name}) already exists."
|
71
|
+
end
|
72
|
+
|
73
|
+
@repos_path = repos_path || ::File.join(SvnFixture::config[:base_path], "repo_#{name}")
|
74
|
+
@wc_path = wc_path || ::File.join(SvnFixture::config[:base_path], "wc_#{name}")
|
75
|
+
check_paths_available
|
76
|
+
@revisions = []
|
77
|
+
@dirs_created = [] # Keep track of any directories created for use by #destroy
|
78
|
+
self.class.repositories[name] = self
|
79
|
+
self.instance_eval(&block) if block_given?
|
80
|
+
end
|
81
|
+
|
82
|
+
# Add a Revision to this Repository. +name+ and +msg+ are required.
|
83
|
+
# - +name+: A name (or number of Revision). This is used in informational
|
84
|
+
# messages only.
|
85
|
+
# - +msg+: Log message for the revision.
|
86
|
+
# - +options+: :author and :date Revision properties.
|
87
|
+
# - Accepts a block that is processed by Revision#commit within a Directory
|
88
|
+
# instance (the root directory at this revision). See +Directory+ for
|
89
|
+
# more information.
|
90
|
+
def revision(name, msg, options = {}, &block)
|
91
|
+
r = Revision.new(name, msg, options, &block)
|
92
|
+
@revisions << r
|
93
|
+
r
|
94
|
+
end
|
95
|
+
|
96
|
+
# Create the Subversion repository. This is called by #checkout unless
|
97
|
+
# something already exists at @repos_path. It can also be called directly.
|
98
|
+
# This allows the flexibility of doing some work between creating the
|
99
|
+
# Repository and running checkout or commit (although I've yet to think of
|
100
|
+
# what that work would be), or creating the repository some other way.
|
101
|
+
def create
|
102
|
+
FileUtils.mkdir_p(@repos_path)
|
103
|
+
@dirs_created << @repos_path
|
104
|
+
::Svn::Repos.create(@repos_path)
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
# Checkout a working copy, and setup context. This is call by #commit unless
|
109
|
+
# something already exists at @wc_path. It can also be called directly.
|
110
|
+
# This allows the flexibility of doing some work between checking out the
|
111
|
+
# Repository and commit, or checking out some other way. Also, calls #create
|
112
|
+
# if needed.
|
113
|
+
def checkout
|
114
|
+
create unless ::File.exist?(@repos_path)
|
115
|
+
@repos = ::Svn::Repos.open(@repos_path)
|
116
|
+
@repos_uri = "file://" + ::File.expand_path(@repos_path)
|
117
|
+
FileUtils.mkdir_p(@wc_path)
|
118
|
+
@dirs_created << @wc_path
|
119
|
+
@ctx = SvnFixture::simple_context
|
120
|
+
@ctx.checkout(@repos_uri, @wc_path)
|
121
|
+
self
|
122
|
+
end
|
123
|
+
|
124
|
+
# Commit actually commits the changes of the revisions. It optionally
|
125
|
+
# accepts Revisions or Revision names. If none are given, it commits all
|
126
|
+
# revisions. If any of the arguments are Revisions (not revision names),
|
127
|
+
# they do not need to be explicitly part of this Repository (that is, they
|
128
|
+
# do not need to have been created through self#revision)
|
129
|
+
#
|
130
|
+
# repos.commit # Commits all Revisions added through self#revision
|
131
|
+
# repos.commit(1,2,4) # Commits Revisions named 1, 2, and 4, added through self#revision
|
132
|
+
# repos.commit(rev1, rev3) # Assuming rev1 and rev3 are instances of
|
133
|
+
# # SvnFixture::Revision, commits them
|
134
|
+
# # whether or not they were added through self#revision
|
135
|
+
#
|
136
|
+
# A Revision can be added to the revisions Array directly:
|
137
|
+
#
|
138
|
+
# repos.revisions << Revision.new(1, 'msg')
|
139
|
+
def commit(*to_commit)
|
140
|
+
checkout unless ::File.exist?(@wc_path)
|
141
|
+
to_commit = @revisions if to_commit.empty?
|
142
|
+
to_commit = [to_commit] if (!to_commit.respond_to?(:each) || to_commit.kind_of?(String))
|
143
|
+
|
144
|
+
to_commit.each do | rev |
|
145
|
+
rev = @revisions.find{ |r| r.name == rev } unless rev.kind_of?(Revision)
|
146
|
+
rev.commit(self)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Remove Repository from +.repositories+ and delete repos and working copy
|
151
|
+
# directories.
|
152
|
+
def destroy
|
153
|
+
@dirs_created.each { |d| FileUtils.rm_rf(d) }
|
154
|
+
self.class.repositories.delete(@name)
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
# Check if either @repos_path or @wc_path exist. Called by #initialize.
|
160
|
+
def check_paths_available
|
161
|
+
if ::File.exist?(@repos_path)
|
162
|
+
raise RuntimeError, "repos_path already exists (#{@repos_path})"
|
163
|
+
elsif ::File.exist?(@wc_path)
|
164
|
+
raise RuntimeError, "wc_path already exists (#{@wc_path})"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|