jm81-dm-svn 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/.gitignore +22 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/lib/dm-svn/config.rb +38 -0
- data/lib/dm-svn/model.rb +12 -0
- data/lib/dm-svn/svn/categorized.rb +113 -0
- data/lib/dm-svn/svn/changeset.rb +119 -0
- data/lib/dm-svn/svn/node.rb +128 -0
- data/lib/dm-svn/svn/sync.rb +85 -0
- data/lib/dm-svn/svn.rb +144 -0
- data/lib/dm-svn.rb +12 -0
- data/spec/dm-svn/config_spec.rb +51 -0
- data/spec/dm-svn/database.yml +16 -0
- data/spec/dm-svn/fixtures/articles_comments.rb +95 -0
- data/spec/dm-svn/fixtures/fiction_site.rb +84 -0
- data/spec/dm-svn/fixtures/news_site.rb +55 -0
- data/spec/dm-svn/model_spec.rb +5 -0
- data/spec/dm-svn/spec_helper.rb +53 -0
- data/spec/dm-svn/svn/categorized_spec.rb +137 -0
- data/spec/dm-svn/svn/changeset_spec.rb +41 -0
- data/spec/dm-svn/svn/node_spec.rb +124 -0
- data/spec/dm-svn/svn/sync_spec.rb +110 -0
- data/spec/dm-svn/svn_spec.rb +212 -0
- data/spec/spec.opts +0 -0
- data/spec/spec_helper.rb +26 -0
- metadata +127 -0
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
.DS_Store
|
2
|
+
log/*
|
3
|
+
tmp/*
|
4
|
+
TAGS
|
5
|
+
*~
|
6
|
+
.#*
|
7
|
+
schema/schema.rb
|
8
|
+
schema/*_structure.sql
|
9
|
+
schema/*.sqlite3
|
10
|
+
schema/*.sqlite
|
11
|
+
schema/*.db
|
12
|
+
*.sqlite
|
13
|
+
*.sqlite3
|
14
|
+
*.db
|
15
|
+
src/*
|
16
|
+
.hgignore
|
17
|
+
.hg/*
|
18
|
+
.svn/*
|
19
|
+
.project
|
20
|
+
.loadpath
|
21
|
+
lib/wistle/tmp/*
|
22
|
+
config/recaptcha.yml
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "dm-svn"
|
8
|
+
gem.summary = %Q{Sync content from a Subversion repository to a DataMapper model}
|
9
|
+
gem.email = "jmorgan@morgancreative.net"
|
10
|
+
gem.homepage = "http://github.com/jm81/dm-svn"
|
11
|
+
gem.authors = ["Jared Morgan"]
|
12
|
+
gem.add_dependency('dm-core')
|
13
|
+
gem.add_dependency('dm-aggregates')
|
14
|
+
gem.add_dependency('dm-validations')
|
15
|
+
gem.add_dependency('jm81-svn-fixture', '>= 0.1.1')
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
end
|
18
|
+
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'spec/rake/spectask'
|
24
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
25
|
+
spec.libs << 'lib' << 'spec'
|
26
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
27
|
+
end
|
28
|
+
|
29
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
30
|
+
spec.libs << 'lib' << 'spec'
|
31
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
32
|
+
spec.rcov = true
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
task :default => :spec
|
37
|
+
|
38
|
+
require 'rake/rdoctask'
|
39
|
+
Rake::RDocTask.new do |rdoc|
|
40
|
+
if File.exist?('VERSION.yml')
|
41
|
+
config = YAML.load(File.read('VERSION.yml'))
|
42
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
43
|
+
else
|
44
|
+
version = ""
|
45
|
+
end
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = "dm-svn #{version}"
|
49
|
+
rdoc.rdoc_files.include('README*')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
end
|
52
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module DmSvn
|
2
|
+
class Config
|
3
|
+
OPTS = [:uri, :username, :password,
|
4
|
+
:body_property, :property_prefix, :extension]
|
5
|
+
|
6
|
+
attr_accessor *OPTS
|
7
|
+
attr_accessor :path_from_root # Used by Sync.
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
# Set defaults
|
11
|
+
@body_property = 'body'
|
12
|
+
@property_prefix = 'ws:'
|
13
|
+
@extension = 'txt'
|
14
|
+
|
15
|
+
# Try to set variables from database.yml.
|
16
|
+
# The location of database.yml should, I suppose, be configurable.
|
17
|
+
# Oh, well.
|
18
|
+
if Object.const_defined?("RAILS_ROOT")
|
19
|
+
f = "#{RAILS_ROOT}/config/database.yml"
|
20
|
+
env = Kernel.const_defined?("RAILS_ENV") ? RAILS_ENV : "development"
|
21
|
+
elsif Object.const_defined?("Merb")
|
22
|
+
f = "#{Merb.root}/config/database.yml"
|
23
|
+
env = Merb.env.to_sym || :development
|
24
|
+
end
|
25
|
+
|
26
|
+
if f
|
27
|
+
config = YAML.load(IO.read(f))[env]
|
28
|
+
OPTS.each do |field|
|
29
|
+
config_field = config["svn_#{field}"] || config["svn_#{field}".to_sym]
|
30
|
+
if config_field
|
31
|
+
instance_variable_set("@#{field}", config_field)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/dm-svn/model.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
module DmSvn
|
2
|
+
module Svn
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
# Override belongs_to to add a :dm-svn option if :dm-svn => true, include
|
7
|
+
# Categorized and set up @svn_category and @svn_category_model instance
|
8
|
+
# methods.
|
9
|
+
def belongs_to(what, options = {})
|
10
|
+
svn = options.delete(:svn)
|
11
|
+
if svn
|
12
|
+
@svn_category = what
|
13
|
+
@svn_category_model = options[:class_name] || what.to_s.camel_case
|
14
|
+
include(DmSvn::Svn::Categorized)
|
15
|
+
end
|
16
|
+
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
# Method name for accessing the parent instance.
|
21
|
+
def svn_category
|
22
|
+
@svn_category
|
23
|
+
end
|
24
|
+
|
25
|
+
# Name of the parent model class (as a String)
|
26
|
+
def svn_category_model
|
27
|
+
@svn_category_model
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
# This module is including when belongs_to is called with :dm-svn => true.
|
33
|
+
# It overrides #path and #path= to take into account categories (folders in
|
34
|
+
# the Subversion repository). It also overrides .get to accept get_parent
|
35
|
+
# argument.
|
36
|
+
module Categorized
|
37
|
+
class << self
|
38
|
+
def included(klass)
|
39
|
+
klass.extend(ClassMethods)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# The path from the svn root for the model. Includes any folders.
|
44
|
+
def path
|
45
|
+
cat = self.send(self.class.svn_category)
|
46
|
+
|
47
|
+
if cat && !cat.path.blank?
|
48
|
+
return cat.path + "/" + @svn_name
|
49
|
+
end
|
50
|
+
|
51
|
+
return @svn_name
|
52
|
+
end
|
53
|
+
|
54
|
+
# Set the path. This is responsible for moving the record to a different
|
55
|
+
# parent, etc.
|
56
|
+
def path=(value)
|
57
|
+
value = value[1..-1] while value[0..0] == "/"
|
58
|
+
ary = value.split("/")
|
59
|
+
immediate = ary.pop
|
60
|
+
parent = ary.join("/")
|
61
|
+
|
62
|
+
if parent.blank?
|
63
|
+
self.send("#{self.class.svn_category}=", nil)
|
64
|
+
else
|
65
|
+
category_model = Object.const_get(self.class.svn_category_model)
|
66
|
+
category = category_model.get_or_create(parent)
|
67
|
+
self.send("#{self.class.svn_category}=", category)
|
68
|
+
end
|
69
|
+
|
70
|
+
attribute_set(:svn_name, immediate)
|
71
|
+
end
|
72
|
+
|
73
|
+
module ClassMethods
|
74
|
+
|
75
|
+
# Get by path, which gets parent (possibly recursively) first.
|
76
|
+
def get_by_path(value)
|
77
|
+
value = value[1..-1] while value[0..0] == "/"
|
78
|
+
ary = value.split("/")
|
79
|
+
immediate = ary.pop
|
80
|
+
parent = ary.join("/")
|
81
|
+
|
82
|
+
if parent.blank?
|
83
|
+
first(:svn_name => immediate, "#{self.svn_category}_id".to_sym => nil)
|
84
|
+
else
|
85
|
+
category_model = Object.const_get(self.svn_category_model)
|
86
|
+
category = category_model.get_by_path(parent)
|
87
|
+
return nil if category.nil?
|
88
|
+
first(:svn_name => immediate, "#{self.svn_category}_id".to_sym => category.id)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Add get_parent argument. If true and a path is not found for the
|
93
|
+
# model, try to find path in parent model (if any).
|
94
|
+
def get(path_or_id, get_parent = false)
|
95
|
+
if path_or_id.is_a?(String)
|
96
|
+
i = get_by_path(path_or_id)
|
97
|
+
if i || !get_parent
|
98
|
+
return i
|
99
|
+
else # if get_parent
|
100
|
+
category_model = Object.const_get(@svn_category_model)
|
101
|
+
return nil if category_model == self.class
|
102
|
+
category_model.get_by_path(path_or_id)
|
103
|
+
end
|
104
|
+
else
|
105
|
+
super(path_or_id)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module DmSvn
|
2
|
+
module Svn
|
3
|
+
|
4
|
+
# Store information about a particular changeset, and runs the actual
|
5
|
+
# updates for that changeset. Aside from handling the
|
6
|
+
# list of changes, it can be passed to methods that need the revision,
|
7
|
+
# date and author.
|
8
|
+
class Changeset
|
9
|
+
include Comparable
|
10
|
+
|
11
|
+
attr_reader :changes, :revision, :date, :author, :repos, :config
|
12
|
+
|
13
|
+
def initialize(changes, revision, author, date, sync)
|
14
|
+
@changes, @revision, @author, @date = changes, revision, author, date
|
15
|
+
@model, @config, @repos, @sync = sync.model, sync.config, sync.repos, sync
|
16
|
+
end
|
17
|
+
|
18
|
+
# Changesets are sorted by revision number, ascending.
|
19
|
+
def <=>(other)
|
20
|
+
self.revision <=> other.revision
|
21
|
+
end
|
22
|
+
|
23
|
+
# Process this changeset.
|
24
|
+
# This doesn't account for possible move/replace conflicts (A node is moved,
|
25
|
+
# then the old node is replaced by a new one). I assume those are rare
|
26
|
+
# enough that I won't code around them, for now.
|
27
|
+
def process
|
28
|
+
return if changes.nil?
|
29
|
+
|
30
|
+
modified, deleted, copied = [], [], []
|
31
|
+
|
32
|
+
changes.each_pair do |path, change|
|
33
|
+
next if short_path(path).blank? || !path_in_root?(path)
|
34
|
+
|
35
|
+
case change.action
|
36
|
+
when "M", "A", "R" # Modified, Added or Replaced
|
37
|
+
modified << path
|
38
|
+
when "D"
|
39
|
+
deleted << path
|
40
|
+
end
|
41
|
+
copied << [path, change.copyfrom_path] if change.copyfrom_path
|
42
|
+
end
|
43
|
+
|
44
|
+
# Perform moves
|
45
|
+
copied.each do |copy|
|
46
|
+
del = deleted.find { |d| d == copy[1] }
|
47
|
+
if del
|
48
|
+
# Change the path. No need to perform other updates, as this is an
|
49
|
+
# "A" or "R" and thus is in the +modified+ Array.
|
50
|
+
record = get(del)
|
51
|
+
record.move_to(short_path(copy[0])) if record
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Perform deletes
|
56
|
+
deleted.each do |path|
|
57
|
+
record = get(path)
|
58
|
+
record.destroy if record # May have been moved or refer to a directory
|
59
|
+
end
|
60
|
+
|
61
|
+
# Perform modifies and adds
|
62
|
+
modified.each do |path|
|
63
|
+
node = Node.new(self, path)
|
64
|
+
|
65
|
+
if @config.extension &&
|
66
|
+
node.file? &&
|
67
|
+
path !~ /\.#{@config.extension}\Z/
|
68
|
+
if path =~ /\.yml\Z/ && @model.svn_category_model
|
69
|
+
# Update parent directory, if applicable
|
70
|
+
path = path.split("/")[0..-2].join("/")
|
71
|
+
node = Node.new(self, path)
|
72
|
+
else
|
73
|
+
next
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
record = get(path) || new_record(node)
|
78
|
+
|
79
|
+
# update record
|
80
|
+
record.update_from_svn(node)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get the relative path from config.uri
|
85
|
+
def short_path(path)
|
86
|
+
path = fs_path(path)
|
87
|
+
path = path.sub(/\.#{@config.extension}\Z/, '') if @config.extension
|
88
|
+
path
|
89
|
+
end
|
90
|
+
|
91
|
+
# Path used to communicate with svn repos.
|
92
|
+
def fs_path(path)
|
93
|
+
path = path[@config.path_from_root.length..-1].to_s
|
94
|
+
path = path[1..-1] if path[0] == ?/
|
95
|
+
path
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def path_in_root?(path)
|
101
|
+
return true if @config.path_from_root.blank?
|
102
|
+
path[0..@config.path_from_root.length - 1].to_s == @config.path_from_root
|
103
|
+
end
|
104
|
+
|
105
|
+
# Get an object of the @model, by path.
|
106
|
+
def get(path)
|
107
|
+
@model.get(short_path(path), true)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Create a new object of the @model
|
111
|
+
def new_record(node)
|
112
|
+
node.file? ?
|
113
|
+
@model.new :
|
114
|
+
Object.const_get(@model.svn_category_model).new
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module DmSvn
|
2
|
+
module Svn
|
3
|
+
|
4
|
+
# A Node (file or directory) at a path and revision [Changeset].
|
5
|
+
# In particular, this class exists to determine the type (file or directory)
|
6
|
+
# of the node and retrieve the body of a node (for files),
|
7
|
+
# and other properties, either stored in yaml or a Subversion properties
|
8
|
+
class Node
|
9
|
+
|
10
|
+
attr_reader :path
|
11
|
+
|
12
|
+
def initialize(changeset, path)
|
13
|
+
@changeset, @path = changeset, path
|
14
|
+
end
|
15
|
+
|
16
|
+
# Shortened path (from Changeset#short_path)
|
17
|
+
def short_path
|
18
|
+
@changeset.short_path(@path)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Shortened path (from Changeset#fs_path)
|
22
|
+
def fs_path
|
23
|
+
@changeset.fs_path(@path)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Body of the node (nil for a directory)
|
27
|
+
def body
|
28
|
+
return nil unless file?
|
29
|
+
has_yaml_props? ?
|
30
|
+
yaml_split[1] :
|
31
|
+
data[0]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Properties of the node. The properties are accessed from three sources,
|
35
|
+
# listed by order of precedence:
|
36
|
+
# 1. YAML properties, either in meta.yml (directories), or at the
|
37
|
+
# beginning of a file, between "---" and "..." lines.
|
38
|
+
# 2. Properties stored in subversion's property mechanism.
|
39
|
+
# 3. svn_updated_[rev|at|by] as determined by revision properties.
|
40
|
+
def properties
|
41
|
+
rev_properties.merge(svn_properties).merge(yaml_properties)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Is the Node a file?
|
45
|
+
def file?
|
46
|
+
repos.stat(fs_path, revision).file?
|
47
|
+
end
|
48
|
+
|
49
|
+
# Is the Node a directory?
|
50
|
+
def directory?
|
51
|
+
repos.stat(fs_path, revision).directory?
|
52
|
+
end
|
53
|
+
|
54
|
+
# Methods derived from Changeset instance variables.
|
55
|
+
%w{revision author repos date config}.each do |f|
|
56
|
+
define_method(f) do
|
57
|
+
@changeset.__send__(f)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Get data as array of body, properties
|
64
|
+
def data
|
65
|
+
@data ||= file? ?
|
66
|
+
repos.file(fs_path, revision) :
|
67
|
+
repos.dir(fs_path, revision)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Properties based on the revision information: svn_updated_[rev|at|by]
|
71
|
+
# as a hash
|
72
|
+
def rev_properties
|
73
|
+
{
|
74
|
+
'svn_updated_at' => @changeset.date,
|
75
|
+
'svn_updated_by' => @changeset.author,
|
76
|
+
'svn_updated_rev' => @changeset.revision
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
# Get properties stored as subversion properties, that begin with the
|
81
|
+
# Config#property_prefix.
|
82
|
+
def svn_properties
|
83
|
+
props = {}
|
84
|
+
data[1].each do |name, val|
|
85
|
+
if name =~ /\A#{config.property_prefix}(.*)/
|
86
|
+
props[$1] = val
|
87
|
+
end
|
88
|
+
end
|
89
|
+
props
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get YAML properties. For a directory, these are stored in meta.yml,
|
93
|
+
# for a file, they are stored at the beginning of the file: the first line
|
94
|
+
# will be "---" and the YAML will end before a line containing "..."
|
95
|
+
def yaml_properties
|
96
|
+
if directory?
|
97
|
+
fs_yaml_path = fs_path.blank? ? 'meta.yml' : File.join(fs_path, 'meta.yml')
|
98
|
+
yaml_path = File.join(@path, 'meta.yml')
|
99
|
+
repos.stat(fs_yaml_path, revision) ?
|
100
|
+
YAML.load(self.class.new(@changeset, yaml_path).body) :
|
101
|
+
{}
|
102
|
+
else
|
103
|
+
has_yaml_props? ?
|
104
|
+
YAML.load(yaml_split[0]) :
|
105
|
+
{}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Determine if file has yaml properties, by checking if the file starts
|
110
|
+
# with three leading dashes
|
111
|
+
def has_yaml_props?
|
112
|
+
file? && data[0][0..2] == "---"
|
113
|
+
end
|
114
|
+
|
115
|
+
# Split a file between properties and body at a line with three dots.
|
116
|
+
# Left trim the body and there may have been blank lines added for
|
117
|
+
# clarity.
|
118
|
+
def yaml_split
|
119
|
+
data[0].gsub!("\r", "")
|
120
|
+
ary = data[0].split("\n...\n")
|
121
|
+
ary[1] = ary[1].lstrip
|
122
|
+
ary
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module DmSvn
|
2
|
+
module Svn
|
3
|
+
class Sync
|
4
|
+
|
5
|
+
attr_reader :model, :config, :repos
|
6
|
+
|
7
|
+
def initialize(model_row)
|
8
|
+
@model_row = model_row
|
9
|
+
@model = Object.const_get(@model_row.name)
|
10
|
+
@config = @model_row.config
|
11
|
+
end
|
12
|
+
|
13
|
+
# Get changesets
|
14
|
+
def changesets
|
15
|
+
sets = []
|
16
|
+
|
17
|
+
@repos.log('', @model_row.revision, @repos.latest_revnum, 0, true, false
|
18
|
+
) do |changes, rev, author, date, msg|
|
19
|
+
sets << Changeset.new(changes, rev, author, date, self)
|
20
|
+
end
|
21
|
+
|
22
|
+
sets.sort
|
23
|
+
end
|
24
|
+
|
25
|
+
# There is the possibility for uneccessary updates, as a database row may be
|
26
|
+
# modified several times (if modified in multiple revisions) in a single
|
27
|
+
# call. This is inefficient, but--for now--not enough to justify more
|
28
|
+
# complex code.
|
29
|
+
def run
|
30
|
+
connect(@config.uri)
|
31
|
+
return false if @repos.latest_revnum <= @model_row.revision
|
32
|
+
|
33
|
+
changesets.each do |c| # Sorted by revision, ascending
|
34
|
+
c.process
|
35
|
+
# Update model_row.revision
|
36
|
+
row_update = @model_row.class.get(@model_row.id)
|
37
|
+
row_update.update_attributes(:revision => c.revision)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Ensure that @model_row.revision is now set to the latest (even if there
|
41
|
+
# weren't applicable changes in the latest revision).
|
42
|
+
row_update = @model_row.class.get(@model_row.id)
|
43
|
+
row_update.update_attributes(:revision => @repos.latest_revnum)
|
44
|
+
return true
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def connect(uri)
|
50
|
+
@ctx = context(uri)
|
51
|
+
|
52
|
+
# This will raise some error if connection fails for whatever reason.
|
53
|
+
# I don't currently see a reason to handle connection errors here, as I
|
54
|
+
# assume the best handling would be to raise another error.
|
55
|
+
@repos = ::Svn::Ra::Session.open(uri, {}, callbacks)
|
56
|
+
@config.path_from_root = @config.uri[(@repos.repos_root.length)..-1]
|
57
|
+
return true
|
58
|
+
end
|
59
|
+
|
60
|
+
def context(uri)
|
61
|
+
# Client::Context, which paticularly holds an auth_baton.
|
62
|
+
ctx = ::Svn::Client::Context.new
|
63
|
+
if @config.username && @config.password
|
64
|
+
# TODO: What if another provider type is needed? Is this plausible?
|
65
|
+
ctx.add_simple_prompt_provider(0) do |cred, realm, username, may_save|
|
66
|
+
cred.username = @config.username
|
67
|
+
cred.password = @config.password
|
68
|
+
end
|
69
|
+
elsif URI.parse(uri).scheme == "file"
|
70
|
+
ctx.add_username_prompt_provider(0) do |cred, realm, username, may_save|
|
71
|
+
cred.username = @config.username || "ANON"
|
72
|
+
end
|
73
|
+
else
|
74
|
+
ctx.auth_baton = ::Svn::Core::AuthBaton.new()
|
75
|
+
end
|
76
|
+
ctx
|
77
|
+
end
|
78
|
+
|
79
|
+
# callbacks for Svn::Ra::Session.open. This includes the client +context+.
|
80
|
+
def callbacks
|
81
|
+
::Svn::Ra::Callbacks.new(@ctx.auth_baton)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|