teapot 1.3.1 → 2.0.0.pre.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.travis.yml +6 -0
- data/Gemfile +1 -1
- data/bin/teapot +3 -3
- data/lib/teapot/command/build.rb +123 -0
- data/lib/teapot/{metadata.rb → command/clean.rb} +19 -20
- data/lib/teapot/command/create.rb +112 -0
- data/lib/teapot/command/fetch.rb +183 -0
- data/lib/teapot/command/list.rb +97 -0
- data/lib/teapot/command/status.rb +74 -0
- data/lib/teapot/command/visualize.rb +70 -0
- data/lib/teapot/command.rb +52 -132
- data/lib/teapot/context.rb +27 -11
- data/lib/teapot/loader.rb +6 -22
- data/lib/teapot/target.rb +2 -2
- data/lib/teapot/version.rb +1 -1
- data/spec/teapot/command_spec.rb +20 -12
- data/spec/teapot/context_spec.rb +13 -16
- data/teapot.gemspec +6 -4
- metadata +35 -45
- data/PLANNING.md +0 -20
- data/lib/teapot/controller/build.rb +0 -107
- data/lib/teapot/controller/clean.rb +0 -35
- data/lib/teapot/controller/create.rb +0 -73
- data/lib/teapot/controller/fetch.rb +0 -173
- data/lib/teapot/controller/generate.rb +0 -45
- data/lib/teapot/controller/list.rb +0 -82
- data/lib/teapot/controller/visualize.rb +0 -50
- data/lib/teapot/controller.rb +0 -81
- data/lib/teapot/dependency.rb +0 -25
- data/lib/teapot/generator.rb +0 -138
- data/lib/teapot/merge.rb +0 -142
- data/lib/teapot/repository.rb +0 -135
- data/lib/teapot/substitutions.rb +0 -258
- data/spec/teapot/generator_spec/teapot.rb +0 -54
- data/spec/teapot/generator_spec/template/$NAME.txt +0 -1
- data/spec/teapot/generator_spec.rb +0 -46
- data/spec/teapot/merge_spec.rb +0 -50
- data/spec/teapot/metadata_spec.rb +0 -31
- data/spec/teapot/substitutions_spec.rb +0 -65
- data/spec/teapot/wait_spec/teapot.rb +0 -41
- data/spec/teapot/wait_spec.rb +0 -53
@@ -1,82 +0,0 @@
|
|
1
|
-
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
20
|
-
|
21
|
-
require_relative '../controller'
|
22
|
-
|
23
|
-
module Teapot
|
24
|
-
class Controller
|
25
|
-
def list(only = nil)
|
26
|
-
# Should this somehow consider context.root_package?
|
27
|
-
context.configuration.packages.each do |package|
|
28
|
-
# The root package is the local package for this context:
|
29
|
-
next unless only == nil or only.include?(package.name)
|
30
|
-
|
31
|
-
log "Package #{package.name} (from #{package.path}):".bright
|
32
|
-
|
33
|
-
begin
|
34
|
-
definitions = context.load(package)
|
35
|
-
|
36
|
-
definitions.each do |definition|
|
37
|
-
log "\t#{definition}"
|
38
|
-
|
39
|
-
definition.description.each_line do |line|
|
40
|
-
log "\t\t#{line.chomp}".color(:cyan)
|
41
|
-
end if definition.description
|
42
|
-
|
43
|
-
case definition
|
44
|
-
when Project
|
45
|
-
log "\t\t- Summary: #{definition.summary}" if definition.summary
|
46
|
-
log "\t\t- License: #{definition.license}" if definition.license
|
47
|
-
log "\t\t- Website: #{definition.website}" if definition.website
|
48
|
-
log "\t\t- Version: #{definition.version}" if definition.version
|
49
|
-
|
50
|
-
definition.authors.each do |author|
|
51
|
-
contact_text = [author.email, author.website].compact.collect{|text|" <#{text}>"}.join
|
52
|
-
log "\t\t- Author: #{author.name}" + contact_text
|
53
|
-
end
|
54
|
-
when Target
|
55
|
-
definition.dependencies.each do |dependency|
|
56
|
-
log "\t\t- #{dependency}".color(:red)
|
57
|
-
end
|
58
|
-
|
59
|
-
definition.provisions.each do |name, provision|
|
60
|
-
log "\t\t- #{provision}".color(:green)
|
61
|
-
end
|
62
|
-
when Configuration
|
63
|
-
definition.materialize
|
64
|
-
|
65
|
-
definition.packages.each do |package|
|
66
|
-
log "\t\t- #{package}".color(:green)
|
67
|
-
end
|
68
|
-
|
69
|
-
definition.imports.select(&:explicit).each do |import|
|
70
|
-
log "\t\t- unmaterialised import #{import.name}".color(:red)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
rescue NonexistantTeapotError => error
|
75
|
-
log "\t#{error.message}".color(:red)
|
76
|
-
rescue IncompatibleTeapotError => error
|
77
|
-
log "\t#{error.message}".color(:red)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
20
|
-
|
21
|
-
require_relative '../controller'
|
22
|
-
|
23
|
-
require 'graphviz'
|
24
|
-
|
25
|
-
module Teapot
|
26
|
-
class Controller
|
27
|
-
def visualize(dependency_names = [], output_path: nil, dependency_name: nil)
|
28
|
-
configuration = context.configuration
|
29
|
-
|
30
|
-
chain = context.dependency_chain(dependency_names, context.configuration)
|
31
|
-
|
32
|
-
if dependency_name
|
33
|
-
provider = context.dependencies[dependency_name]
|
34
|
-
|
35
|
-
# TODO The visualisation generated isn't quite right. It's introspecting too much from the packages and not reflecting #ordered and #provisions.
|
36
|
-
chain = chain.partial(provider)
|
37
|
-
end
|
38
|
-
|
39
|
-
visualization = Build::Dependency::Visualization.new
|
40
|
-
|
41
|
-
graph = visualization.generate(chain)
|
42
|
-
|
43
|
-
if output
|
44
|
-
Graphviz::output(graph, :path => output)
|
45
|
-
end
|
46
|
-
|
47
|
-
return graph
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
data/lib/teapot/controller.rb
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
20
|
-
|
21
|
-
require_relative 'context'
|
22
|
-
require_relative 'configuration'
|
23
|
-
require_relative 'version'
|
24
|
-
|
25
|
-
require 'uri'
|
26
|
-
require 'rainbow'
|
27
|
-
require 'rainbow/ext/string'
|
28
|
-
require 'fileutils'
|
29
|
-
|
30
|
-
require 'build/logger'
|
31
|
-
|
32
|
-
module Teapot
|
33
|
-
class Controller
|
34
|
-
def initialize(root, options)
|
35
|
-
@root = Pathname(root)
|
36
|
-
@options = options
|
37
|
-
|
38
|
-
@log_output = @options.fetch(:log, $stderr)
|
39
|
-
@logging = @options[:logging]
|
40
|
-
end
|
41
|
-
|
42
|
-
def verbose?
|
43
|
-
@logging == :verbose
|
44
|
-
end
|
45
|
-
|
46
|
-
def quiet?
|
47
|
-
@logging == :quiet
|
48
|
-
end
|
49
|
-
|
50
|
-
def logger
|
51
|
-
@logger ||= Logger.new(@log_output).tap do |logger|
|
52
|
-
logger.formatter = Build::CompactFormatter.new(verbose: verbose?)
|
53
|
-
|
54
|
-
if verbose?
|
55
|
-
logger.level = Logger::DEBUG
|
56
|
-
elsif quiet?
|
57
|
-
logger.level = Logger::WARN
|
58
|
-
else
|
59
|
-
logger.level = Logger::INFO
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def log(*args)
|
65
|
-
logger.info(*args)
|
66
|
-
end
|
67
|
-
|
68
|
-
def configuration
|
69
|
-
@options[:configuration]
|
70
|
-
end
|
71
|
-
|
72
|
-
def context
|
73
|
-
@context ||= Context.new(@root, configuration: configuration)
|
74
|
-
end
|
75
|
-
|
76
|
-
# Reload the current context, e.g. if it's been modified by a generator.
|
77
|
-
def reload!
|
78
|
-
@context = nil
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
data/lib/teapot/dependency.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
20
|
-
|
21
|
-
require 'build/dependency'
|
22
|
-
|
23
|
-
module Teapot
|
24
|
-
Dependency = ::Build::Dependency
|
25
|
-
end
|
data/lib/teapot/generator.rb
DELETED
@@ -1,138 +0,0 @@
|
|
1
|
-
# Copyright, 2013, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
20
|
-
|
21
|
-
require_relative 'definition'
|
22
|
-
require_relative 'substitutions'
|
23
|
-
require_relative 'merge'
|
24
|
-
|
25
|
-
require 'tempfile'
|
26
|
-
|
27
|
-
module Teapot
|
28
|
-
class GeneratorError < StandardError
|
29
|
-
def initialize(message, generator = nil)
|
30
|
-
super(message)
|
31
|
-
|
32
|
-
@generator = generator
|
33
|
-
end
|
34
|
-
|
35
|
-
attr :generator
|
36
|
-
end
|
37
|
-
|
38
|
-
class Generator < Definition
|
39
|
-
def initialize(context, package, name)
|
40
|
-
super context, package, name
|
41
|
-
|
42
|
-
@generate = nil
|
43
|
-
end
|
44
|
-
|
45
|
-
def generate(&block)
|
46
|
-
@generate = Proc.new(&block)
|
47
|
-
end
|
48
|
-
|
49
|
-
def generate!(*args)
|
50
|
-
@generate[*args]
|
51
|
-
end
|
52
|
-
|
53
|
-
def substitute(text, substitutions)
|
54
|
-
return text unless substitutions
|
55
|
-
|
56
|
-
if Hash === substitutions
|
57
|
-
pattern = Regexp.new(substitutions.keys.map{|x| Regexp.escape(x)}.join('|'))
|
58
|
-
|
59
|
-
text.gsub(pattern) {|key| substitutions[key]}
|
60
|
-
else
|
61
|
-
substitutions.call(text)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def write(source, destination, substitutions = nil, mode = "a")
|
66
|
-
source_path = Pathname(path) + source
|
67
|
-
destination_path = Pathname(context.root) + destination
|
68
|
-
|
69
|
-
destination_path.dirname.mkpath
|
70
|
-
|
71
|
-
File.open(destination_path, mode) do |file|
|
72
|
-
text = File.read(source_path)
|
73
|
-
|
74
|
-
file.write substitute(text, substitutions)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def append(source, destination, substitutions = nil)
|
79
|
-
write(source, destination, substitutions, "a")
|
80
|
-
end
|
81
|
-
|
82
|
-
def merge(source, destination, substitutions = nil)
|
83
|
-
source_path = Pathname(path) + source
|
84
|
-
destination_path = Pathname(context.root) + destination
|
85
|
-
|
86
|
-
if destination_path.exist?
|
87
|
-
temporary_file = Tempfile.new(destination_path.basename.to_s)
|
88
|
-
|
89
|
-
# This functionality works, but needs improvements.
|
90
|
-
begin
|
91
|
-
# Need to ask user what to do?
|
92
|
-
write(source_path, temporary_file.path, substitutions, "w")
|
93
|
-
|
94
|
-
result = Merge::combine(destination.readlines, temporary_file.readlines)
|
95
|
-
|
96
|
-
destination.open("w") do |file|
|
97
|
-
file.write result.join
|
98
|
-
end
|
99
|
-
ensure
|
100
|
-
temporary_file.unlink
|
101
|
-
end
|
102
|
-
else
|
103
|
-
write(source_path, destination_path, substitutions, "w")
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def is_binary(path)
|
108
|
-
if path.exist?
|
109
|
-
return path.read(1024).bytes.find{|byte| byte >= 0 and byte <= 6}
|
110
|
-
else
|
111
|
-
return false
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def copy_binary(source_path, destination_path)
|
116
|
-
destination_path.dirname.create
|
117
|
-
FileUtils.cp source_path, destination_path
|
118
|
-
end
|
119
|
-
|
120
|
-
def copy(source, destination, substitutions = nil)
|
121
|
-
source_path = Pathname(path) + source
|
122
|
-
destination_path = Pathname(context.root) + destination
|
123
|
-
|
124
|
-
if source_path.directory?
|
125
|
-
source_path.children(false).each do |child_path|
|
126
|
-
copy(source_path + child_path, destination_path + substitute(child_path.to_s, substitutions), substitutions)
|
127
|
-
end
|
128
|
-
else
|
129
|
-
if is_binary(source_path) or is_binary(destination)
|
130
|
-
destination_path = Pathname(context.root) + destination
|
131
|
-
copy_binary(source_path, destination_path)
|
132
|
-
else
|
133
|
-
merge(source_path, destination, substitutions)
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
data/lib/teapot/merge.rb
DELETED
@@ -1,142 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# Copyright, 2013, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
3
|
-
#
|
4
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
-
# of this software and associated documentation files (the "Software"), to deal
|
6
|
-
# in the Software without restriction, including without limitation the rights
|
7
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
-
# copies of the Software, and to permit persons to whom the Software is
|
9
|
-
# furnished to do so, subject to the following conditions:
|
10
|
-
#
|
11
|
-
# The above copyright notice and this permission notice shall be included in
|
12
|
-
# all copies or substantial portions of the Software.
|
13
|
-
#
|
14
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
-
# THE SOFTWARE.
|
21
|
-
|
22
|
-
module Teapot
|
23
|
-
module Merge
|
24
|
-
Difference = Struct.new(:type, :value)
|
25
|
-
|
26
|
-
def self.combine(old_text, new_text)
|
27
|
-
lcs = lcs(old_text, new_text)
|
28
|
-
changes = []
|
29
|
-
|
30
|
-
n = 0; o = 0; l = 0
|
31
|
-
while o < old_text.size and n < new_text.size and l < lcs.size
|
32
|
-
if !similar(old_text[o], lcs[l])
|
33
|
-
changes << Difference.new(:old, old_text[o])
|
34
|
-
o+=1
|
35
|
-
elsif !similar(new_text[n], lcs[l])
|
36
|
-
changes << Difference.new(:new, new_text[n])
|
37
|
-
n+=1
|
38
|
-
else
|
39
|
-
changes << Difference.new(:both, lcs[l])
|
40
|
-
o+=1; n+=1; l+=1
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
changes.map do |change|
|
45
|
-
change.value
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# This code is based directly on the Text gem implementation
|
50
|
-
# Returns a value representing the "cost" of transforming str1 into str2
|
51
|
-
def self.levenshtein_distance(s, t)
|
52
|
-
n = s.length
|
53
|
-
m = t.length
|
54
|
-
|
55
|
-
return m if n == 0
|
56
|
-
return n if m == 0
|
57
|
-
|
58
|
-
d = (0..m).to_a
|
59
|
-
x = nil
|
60
|
-
|
61
|
-
n.times do |i|
|
62
|
-
e = i+1
|
63
|
-
|
64
|
-
m.times do |j|
|
65
|
-
cost = (s[i] == t[j]) ? 0 : 1
|
66
|
-
x = [
|
67
|
-
d[j+1] + 1, # insertion
|
68
|
-
e + 1, # deletion
|
69
|
-
d[j] + cost # substitution
|
70
|
-
].min
|
71
|
-
d[j] = e
|
72
|
-
e = x
|
73
|
-
end
|
74
|
-
|
75
|
-
d[m] = x
|
76
|
-
end
|
77
|
-
|
78
|
-
return x
|
79
|
-
end
|
80
|
-
|
81
|
-
# Calculate the similarity of two sequences, return true if they are with factor% similarity.
|
82
|
-
def self.similar(s, t, factor = 0.15)
|
83
|
-
return true if s == t
|
84
|
-
|
85
|
-
distance = levenshtein_distance(s, t)
|
86
|
-
average_length = (s.length + t.length) / 2.0
|
87
|
-
|
88
|
-
proximity = (distance.to_f / average_length)
|
89
|
-
|
90
|
-
return proximity <= factor
|
91
|
-
end
|
92
|
-
|
93
|
-
LCSNode = Struct.new(:value, :previous)
|
94
|
-
|
95
|
-
# Find the Longest Common Subsequence in the given sequences x, y.
|
96
|
-
def self.lcs(x, y)
|
97
|
-
# Create the lcs matrix:
|
98
|
-
m = Array.new(x.length + 1) do
|
99
|
-
Array.new(y.length + 1) do
|
100
|
-
LCSNode.new(nil, nil)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
# LCS(i, 0) and LCS(0, j) are always 0:
|
105
|
-
for i in 0..x.length do m[i][0].value = 0 end
|
106
|
-
for j in 0..y.length do m[0][j].value = 0 end
|
107
|
-
|
108
|
-
# Main algorithm, solve row by row:
|
109
|
-
for i in 1..x.length do
|
110
|
-
for j in 1..y.length do
|
111
|
-
if similar(x[i-1], y[j-1])
|
112
|
-
# Value is based on maximizing the length of the matched strings:
|
113
|
-
m[i][j].value = m[i-1][j-1].value + (x[i-1].chomp.length + y[j-1].chomp.length) / 2.0
|
114
|
-
m[i][j].previous = [-1, -1]
|
115
|
-
else
|
116
|
-
if m[i-1][j].value >= m[i][j-1].value
|
117
|
-
m[i][j].value = m[i-1][j].value
|
118
|
-
m[i][j].previous = [-1, 0]
|
119
|
-
else
|
120
|
-
m[i][j].value = m[i][j-1].value
|
121
|
-
m[i][j].previous = [0, -1]
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
# Get the solution by following the path backwards from m[x.length][y.length]
|
128
|
-
lcs = []
|
129
|
-
|
130
|
-
i = x.length; j = y.length
|
131
|
-
until m[i][j].previous == nil do
|
132
|
-
if m[i][j].previous == [-1, -1]
|
133
|
-
lcs << x[i-1]
|
134
|
-
end
|
135
|
-
|
136
|
-
i, j = i + m[i][j].previous[0], j + m[i][j].previous[1]
|
137
|
-
end
|
138
|
-
|
139
|
-
return lcs.reverse!
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
data/lib/teapot/repository.rb
DELETED
@@ -1,135 +0,0 @@
|
|
1
|
-
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
20
|
-
|
21
|
-
module Teapot
|
22
|
-
module Git
|
23
|
-
# This module needs to be refactored. Perhaps use Process::Group.
|
24
|
-
module Commands
|
25
|
-
class CommandError < StandardError
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.run(*args, &block)
|
29
|
-
options = Hash === args.last ? args.pop : {}
|
30
|
-
|
31
|
-
args = args.flatten.collect(&:to_s)
|
32
|
-
|
33
|
-
puts args.join(' ').color(:blue) + " in #{options[:chdir] || Dir.getwd}"
|
34
|
-
|
35
|
-
pid = Process.spawn(*args, options, &block)
|
36
|
-
_, result = Process.wait2(pid)
|
37
|
-
|
38
|
-
if result.exitstatus == 0
|
39
|
-
true
|
40
|
-
else
|
41
|
-
raise CommandError.new("Non-zero exit status: #{result} while running #{args.join(' ')}!")
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.run!(*args, &block)
|
46
|
-
run(*args, &block)
|
47
|
-
rescue CommandError
|
48
|
-
false
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
class Repository
|
53
|
-
def initialize(root, options = {})
|
54
|
-
@root = root
|
55
|
-
@options = options
|
56
|
-
end
|
57
|
-
|
58
|
-
def init!
|
59
|
-
run!("init", @root)
|
60
|
-
end
|
61
|
-
|
62
|
-
def clone!(remote_url, branch = nil, commit = nil)
|
63
|
-
branch_args = branch ? ["--branch", branch] : []
|
64
|
-
|
65
|
-
@root.create
|
66
|
-
|
67
|
-
run!("clone", remote_url, @root, *branch_args)
|
68
|
-
|
69
|
-
if commit
|
70
|
-
run("reset", "--hard", commit)
|
71
|
-
end
|
72
|
-
|
73
|
-
run("submodule", "update", "--init", "--recursive")
|
74
|
-
rescue
|
75
|
-
#@root.rmtree
|
76
|
-
|
77
|
-
raise
|
78
|
-
end
|
79
|
-
|
80
|
-
def update(branch, commit = nil)
|
81
|
-
run("fetch", "origin")
|
82
|
-
run("checkout", branch)
|
83
|
-
|
84
|
-
# Pull any changes, if you might get the error from above:
|
85
|
-
# Your branch is behind 'origin/0.1' by 1 commit, and can be fast-forwarded.
|
86
|
-
run("pull")
|
87
|
-
|
88
|
-
# Checkout the specific version if it was given:
|
89
|
-
if commit
|
90
|
-
run("reset", "--hard", commit)
|
91
|
-
end
|
92
|
-
|
93
|
-
run("submodule", "update", "--init", "--recursive")
|
94
|
-
end
|
95
|
-
|
96
|
-
def add(files)
|
97
|
-
if files == :all
|
98
|
-
run("add", "--all")
|
99
|
-
else
|
100
|
-
run("add", *files)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def commit(message)
|
105
|
-
run("commit", "-m", message)
|
106
|
-
end
|
107
|
-
|
108
|
-
def status
|
109
|
-
input, output = IO.pipe
|
110
|
-
|
111
|
-
Commands.run("git", "status", "--porcelain", :out => output)
|
112
|
-
|
113
|
-
output.close
|
114
|
-
|
115
|
-
return input.readlines.collect{|line| line.chomp.split(/\s+/, 2)}
|
116
|
-
end
|
117
|
-
|
118
|
-
private
|
119
|
-
|
120
|
-
def run(*args)
|
121
|
-
Commands.run("git", *args, :chdir => @root)
|
122
|
-
end
|
123
|
-
|
124
|
-
def run!(*args)
|
125
|
-
Commands.run("git", *args)
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
module Repository
|
131
|
-
def self.new(*args)
|
132
|
-
Git::Repository.new(*args)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|