jm81-svn-fixture 0.1.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.
- 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
|