hallettj-cloudrcs 0.0.1
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/History.txt +4 -0
- data/License.txt +14 -0
- data/Manifest.txt +35 -0
- data/PostInstall.txt +2 -0
- data/README.txt +293 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +75 -0
- data/config/requirements.rb +15 -0
- data/lib/active_record/acts/list.rb +256 -0
- data/lib/acts_as_list.rb +2 -0
- data/lib/cloud_rcs.rb +7 -0
- data/lib/cloud_rcs/patch.rb +404 -0
- data/lib/cloud_rcs/patch_types/addfile.rb +74 -0
- data/lib/cloud_rcs/patch_types/binary.rb +232 -0
- data/lib/cloud_rcs/patch_types/hunk.rb +263 -0
- data/lib/cloud_rcs/patch_types/move.rb +89 -0
- data/lib/cloud_rcs/patch_types/rmfile.rb +63 -0
- data/lib/cloud_rcs/primitive_patch.rb +147 -0
- data/lib/cloudrcs.rb +12 -0
- data/lib/cloudrcs/version.rb +9 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +82 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/website.rake +17 -0
- data/test/test_cloudrcs.rb +11 -0
- data/test/test_helper.rb +2 -0
- data/website/index.html +141 -0
- data/website/index.txt +83 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +138 -0
- data/website/template.html.erb +48 -0
- metadata +115 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
module CloudRCS
|
2
|
+
|
3
|
+
# A primitive patch type that represents that a file has been moved
|
4
|
+
# or renamed.
|
5
|
+
class Move < PrimitivePatch
|
6
|
+
validates_presence_of :path, :original_path
|
7
|
+
|
8
|
+
def after_initialize
|
9
|
+
verify_path_prefix
|
10
|
+
verify_original_path_prefix
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"move #{self.class.escape_path(original_path)} #{self.class.escape_path(path)}"
|
15
|
+
end
|
16
|
+
|
17
|
+
# The inverse patch moves the file back to its original location.
|
18
|
+
def inverse
|
19
|
+
Move.new(:original_path => path, :path => original_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def commute(patch)
|
23
|
+
if patch.is_a? Move
|
24
|
+
if patch.original_path == self.new_path
|
25
|
+
raise CommuteException(true, "Conflict: cannot commute move patches that affect the same file.")
|
26
|
+
elsif patch.new_path == self.original_path
|
27
|
+
raise CommuteException(true, "Conflict: commuting these move patches would result in two files with the same name.")
|
28
|
+
|
29
|
+
elsif patch.new_path == self.new_path
|
30
|
+
raise CommuteException(true, "Conflict: cannot commute move patches that affect the same files.")
|
31
|
+
|
32
|
+
else
|
33
|
+
patch1 = Move.new(:path => patch.path, :original_path => patch.original_path)
|
34
|
+
patch2 = Move.new(:path => self.path, :original_path => self.original_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
elsif patch.is_a? Addfile and patch.path == self.original_path
|
38
|
+
raise CommuteException(true, "Conflict: move and addfile are order-dependent in this case.")
|
39
|
+
|
40
|
+
elsif patch.is_a? Rmfile and patch.path == self.new_path
|
41
|
+
raise CommuteException(true, "Conflict: move and rmfile are order-dependent in this case.")
|
42
|
+
|
43
|
+
# If the other patch is something like a Hunk or a Binary, and
|
44
|
+
# it operates on the file path that this patch moves a file to,
|
45
|
+
# then the commuted version of that patch should have a file
|
46
|
+
# path that matches the original_path of this patch.
|
47
|
+
elsif patch.path == self.new_path
|
48
|
+
patch1 = patch.clone
|
49
|
+
patch1.path = self.original_path
|
50
|
+
patch2 = self.clone
|
51
|
+
|
52
|
+
else
|
53
|
+
patch1 = patch.clone
|
54
|
+
patch2 = self.clone
|
55
|
+
end
|
56
|
+
|
57
|
+
return patch1, patch2
|
58
|
+
end
|
59
|
+
|
60
|
+
def apply_to(file)
|
61
|
+
if file.path == original_path
|
62
|
+
file.path = new_path
|
63
|
+
end
|
64
|
+
return file
|
65
|
+
end
|
66
|
+
|
67
|
+
class << self
|
68
|
+
|
69
|
+
def generate(orig_file, changed_file)
|
70
|
+
return if orig_file.nil? or changed_file.nil?
|
71
|
+
if orig_file.path != changed_file.path
|
72
|
+
return Move.new(:original_path => orig_file.path, :path => changed_file.path)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse(contents)
|
77
|
+
unless contents =~ /^move\s+(\S+)\s+(\S+)\s*$/
|
78
|
+
raise "Failed to parse move patch: \"#{contents}\""
|
79
|
+
end
|
80
|
+
Move.new(:original_path => unescape_path($1), :path => unescape_path($2))
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
PATCH_TYPES << Move
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module CloudRCS
|
2
|
+
|
3
|
+
# A primitive patch type that represents the deletion of a file.
|
4
|
+
class Rmfile < PrimitivePatch
|
5
|
+
validates_presence_of :path
|
6
|
+
|
7
|
+
def after_initialize
|
8
|
+
verify_path_prefix
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
"rmfile #{self.class.escape_path(path)}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def inverse
|
16
|
+
Addfile.new(:path => path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def commute(patch)
|
20
|
+
if patch.is_a? Rmfile and patch.path == self.path
|
21
|
+
raise CommuteException(true, "Conflict: cannot remove the same file twice.")
|
22
|
+
elsif patch.is_a? Addfile and patch.path == self.path
|
23
|
+
raise CommuteException(true, "Conflict: commuting rmfile and addfile yields two files with the same name.")
|
24
|
+
elsif patch.is_a? Move and patch.path == self.path
|
25
|
+
raise CommuteException(true, "Conflict: commuting rmfile and move yields two files with the same name.")
|
26
|
+
else
|
27
|
+
patch1 = patch.clone
|
28
|
+
patch2 = self.clone
|
29
|
+
end
|
30
|
+
return patch1, patch2
|
31
|
+
end
|
32
|
+
|
33
|
+
def apply_to(file)
|
34
|
+
return file unless file and file.path == path
|
35
|
+
return nil # Returning nil simulates deletion.
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
|
40
|
+
def priority
|
41
|
+
90
|
42
|
+
end
|
43
|
+
|
44
|
+
def generate(orig_file, changed_file)
|
45
|
+
if changed_file.nil? and not orig_file.nil?
|
46
|
+
return Rmfile.new(:path => orig_file.path)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse(contents)
|
51
|
+
unless contents =~ /^rmfile\s+(\S+)\s*$/
|
52
|
+
raise "Failed to parse rmfile patch: #{contents}"
|
53
|
+
end
|
54
|
+
Rmfile.new(:path => unescape_path($1))
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
PATCH_TYPES << Rmfile
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module CloudRCS
|
2
|
+
|
3
|
+
# PrimitivePatch acts as an intermediary between Patch and the
|
4
|
+
# primitive patch types. It allows primitive patches to inherit
|
5
|
+
# methods from Patch, and to use the same table. But it also allows
|
6
|
+
# for defining behavior that differs from Patch but that is
|
7
|
+
# automatically inherited by all primitive patch types.
|
8
|
+
|
9
|
+
class PrimitivePatch < ActiveRecord::Base
|
10
|
+
PATH_PREFIX = "./"
|
11
|
+
|
12
|
+
# Primitive patches belong to a named patch and a file. They also
|
13
|
+
# use the acts_as_list plugin to maintain a specific order within
|
14
|
+
# the named patch.
|
15
|
+
belongs_to :patch
|
16
|
+
# belongs_to :file, :polymorphic => true
|
17
|
+
|
18
|
+
# validates_presence_of :patch_id
|
19
|
+
# validates_presence_of :file_id
|
20
|
+
|
21
|
+
acts_as_list :column => :rank, :scope => :patch_id
|
22
|
+
|
23
|
+
def apply!
|
24
|
+
target_file = locate_file(original_path || path)
|
25
|
+
old_target = target_file
|
26
|
+
target_file = apply_to(target_file)
|
27
|
+
if target_file.nil?
|
28
|
+
old_target.destroy
|
29
|
+
else
|
30
|
+
target_file.save
|
31
|
+
end
|
32
|
+
# update_attribute(:file, target_file)
|
33
|
+
return target_file
|
34
|
+
end
|
35
|
+
|
36
|
+
def named_patch?; false; end
|
37
|
+
def primitive_patch?; true; end
|
38
|
+
|
39
|
+
# def locate_file(path)
|
40
|
+
# raise "You must override the locate_file method for PrimitivePatch."
|
41
|
+
# self.class.file_class.locate(path)
|
42
|
+
# end
|
43
|
+
|
44
|
+
def apply_to(file)
|
45
|
+
override "apply_to(file)"
|
46
|
+
end
|
47
|
+
|
48
|
+
def inverse
|
49
|
+
override "inverse"
|
50
|
+
end
|
51
|
+
|
52
|
+
def commute(patch)
|
53
|
+
override "commute(patch)"
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_s
|
57
|
+
override "to_s"
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_a
|
61
|
+
[self]
|
62
|
+
end
|
63
|
+
|
64
|
+
def new_path; path; end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
# Most primitive patches contain a file path. The darcs patch format
|
69
|
+
# may require a different path prefix than CloudFiles do; so this
|
70
|
+
# method makes the conversion if required.
|
71
|
+
def verify_path_prefix
|
72
|
+
unless path =~ /^#{PATH_PREFIX}/
|
73
|
+
path = PATH_PREFIX + path unless path.blank?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
def verify_original_path_prefix
|
77
|
+
unless original_path =~ /^#{PATH_PREFIX}/
|
78
|
+
original_path = PATH_PREFIX + original_path unless original_path.blank?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def override(method)
|
85
|
+
raise "Method '#{method}' should be overridden by each patch type."
|
86
|
+
end
|
87
|
+
|
88
|
+
class << self
|
89
|
+
|
90
|
+
# Patch type priority represents the relative likelihood that a
|
91
|
+
# patch type will cause conflicts if it is applied before other
|
92
|
+
# patch types. Patch types with a high priority value are likely
|
93
|
+
# to cause conflicts if they are applied early, and so should be
|
94
|
+
# deferred until after other patch types have done their thing.
|
95
|
+
#
|
96
|
+
# Default priority is 50.
|
97
|
+
def priority
|
98
|
+
return 50
|
99
|
+
end
|
100
|
+
|
101
|
+
def generate(orig_file, changed_file)
|
102
|
+
override "generate"
|
103
|
+
end
|
104
|
+
|
105
|
+
def parse(contents)
|
106
|
+
override "parse"
|
107
|
+
end
|
108
|
+
|
109
|
+
def merge(patch_a, patch_b)
|
110
|
+
patch_b_prime = commute(patch_a.inverse, patch_b).first
|
111
|
+
return patch_a, patch_b_prime
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns class of :file association. Won't work with polymorphism.
|
115
|
+
# def file_class
|
116
|
+
# reflect_on_association(:file).class_name.constantize
|
117
|
+
# end
|
118
|
+
|
119
|
+
# Replace special charecters in file paths with ASCII codes
|
120
|
+
# bounded by backslashes.
|
121
|
+
def escape_path(path)
|
122
|
+
# The backslash will be re-interpolated by the regular
|
123
|
+
# expression; so four backslashes in the string definition are
|
124
|
+
# necessary instead of two.
|
125
|
+
special_chars = ['\\\\',' ']
|
126
|
+
path.gsub(/#{special_chars.join('|')}/) { |match| "\\#{match[0]}\\" }
|
127
|
+
end
|
128
|
+
|
129
|
+
# Replace escaped characters with the original versions. Escaped
|
130
|
+
# characters are of the format, /\\(\d{2,3})\\/; where the digits
|
131
|
+
# enclosed in backslashes represent the ASCII code of the original
|
132
|
+
# character.
|
133
|
+
def unescape_path(path)
|
134
|
+
path.gsub(/\\(\d{2,3})\\/) { $1.to_i.chr }
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def override(method)
|
140
|
+
raise "Class method '#{method}' should be overridden by each patch type."
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
data/lib/cloudrcs.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
gem 'activerecord', '>= 2.0'
|
5
|
+
gem 'diff-lcs', '>= 1.1'
|
6
|
+
|
7
|
+
require 'activerecord'
|
8
|
+
require 'diff/lcs'
|
9
|
+
|
10
|
+
require 'acts_as_list'
|
11
|
+
require 'digest/sha1'
|
12
|
+
require 'cloud_rcs'
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/cloudrcs.rb'}"
|
9
|
+
puts "Loading cloudrcs gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/script/txt2html
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
GEM_NAME = 'cloudrcs' # what ppl will type to install your gem
|
4
|
+
RUBYFORGE_PROJECT = 'cloudrcs'
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
begin
|
8
|
+
require 'newgem'
|
9
|
+
require 'rubyforge'
|
10
|
+
rescue LoadError
|
11
|
+
puts "\n\nGenerating the website requires the newgem RubyGem"
|
12
|
+
puts "Install: gem install newgem\n\n"
|
13
|
+
exit(1)
|
14
|
+
end
|
15
|
+
require 'redcloth'
|
16
|
+
require 'syntax/convertors/html'
|
17
|
+
require 'erb'
|
18
|
+
require File.dirname(__FILE__) + "/../lib/#{GEM_NAME}/version.rb"
|
19
|
+
|
20
|
+
version = Cloudrcs::VERSION::STRING
|
21
|
+
download = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
22
|
+
|
23
|
+
def rubyforge_project_id
|
24
|
+
RubyForge.new.autoconfig["group_ids"][RUBYFORGE_PROJECT]
|
25
|
+
end
|
26
|
+
|
27
|
+
class Fixnum
|
28
|
+
def ordinal
|
29
|
+
# teens
|
30
|
+
return 'th' if (10..19).include?(self % 100)
|
31
|
+
# others
|
32
|
+
case self % 10
|
33
|
+
when 1: return 'st'
|
34
|
+
when 2: return 'nd'
|
35
|
+
when 3: return 'rd'
|
36
|
+
else return 'th'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Time
|
42
|
+
def pretty
|
43
|
+
return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def convert_syntax(syntax, source)
|
48
|
+
return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
|
49
|
+
end
|
50
|
+
|
51
|
+
if ARGV.length >= 1
|
52
|
+
src, template = ARGV
|
53
|
+
template ||= File.join(File.dirname(__FILE__), '/../website/template.html.erb')
|
54
|
+
else
|
55
|
+
puts("Usage: #{File.split($0).last} source.txt [template.html.erb] > output.html")
|
56
|
+
exit!
|
57
|
+
end
|
58
|
+
|
59
|
+
template = ERB.new(File.open(template).read)
|
60
|
+
|
61
|
+
title = nil
|
62
|
+
body = nil
|
63
|
+
File.open(src) do |fsrc|
|
64
|
+
title_text = fsrc.readline
|
65
|
+
body_text_template = fsrc.read
|
66
|
+
body_text = ERB.new(body_text_template).result(binding)
|
67
|
+
syntax_items = []
|
68
|
+
body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
|
69
|
+
ident = syntax_items.length
|
70
|
+
element, syntax, source = $1, $2, $3
|
71
|
+
syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
|
72
|
+
"syntax-temp-#{ident}"
|
73
|
+
}
|
74
|
+
title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
|
75
|
+
body = RedCloth.new(body_text).to_html
|
76
|
+
body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
|
77
|
+
end
|
78
|
+
stat = File.stat(src)
|
79
|
+
created = stat.ctime
|
80
|
+
modified = stat.mtime
|
81
|
+
|
82
|
+
$stdout << template.result(binding)
|