rubycut-babushka 0.10.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (171) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +31 -0
  3. data/README.markdown +246 -0
  4. data/Rakefile +26 -0
  5. data/bin/babushka +11 -0
  6. data/deps/babushka.rb +101 -0
  7. data/deps/dev.rb +12 -0
  8. data/deps/fhs.rb +31 -0
  9. data/deps/git.rb +29 -0
  10. data/deps/homebrew.rb +30 -0
  11. data/deps/os_x.rb +33 -0
  12. data/deps/packages.rb +22 -0
  13. data/deps/pkg_managers.rb +110 -0
  14. data/deps/ruby.rb +23 -0
  15. data/deps/rubygems.rb +24 -0
  16. data/deps/system.rb +10 -0
  17. data/deps/templates/app.rb +68 -0
  18. data/deps/templates/external.rb +12 -0
  19. data/deps/templates/installer.rb +31 -0
  20. data/deps/templates/managed.rb +105 -0
  21. data/deps/templates/ppa.rb +24 -0
  22. data/deps/templates/src.rb +42 -0
  23. data/deps/templates/tmbundle.rb +15 -0
  24. data/lib/babushka.rb +28 -0
  25. data/lib/babushka/accepts_block_for.rb +72 -0
  26. data/lib/babushka/accepts_list_for.rb +49 -0
  27. data/lib/babushka/accepts_value_for.rb +24 -0
  28. data/lib/babushka/base.rb +78 -0
  29. data/lib/babushka/bug_reporter.rb +55 -0
  30. data/lib/babushka/cmdline.rb +133 -0
  31. data/lib/babushka/cmdline/handler.rb +41 -0
  32. data/lib/babushka/cmdline/helpers.rb +127 -0
  33. data/lib/babushka/cmdline/parser.rb +69 -0
  34. data/lib/babushka/colorizer.rb +59 -0
  35. data/lib/babushka/core_patches/array.rb +171 -0
  36. data/lib/babushka/core_patches/blank.rb +22 -0
  37. data/lib/babushka/core_patches/bytes.rb +52 -0
  38. data/lib/babushka/core_patches/hash.rb +107 -0
  39. data/lib/babushka/core_patches/hashish.rb +14 -0
  40. data/lib/babushka/core_patches/integer.rb +25 -0
  41. data/lib/babushka/core_patches/io.rb +8 -0
  42. data/lib/babushka/core_patches/numeric.rb +16 -0
  43. data/lib/babushka/core_patches/object.rb +27 -0
  44. data/lib/babushka/core_patches/string.rb +116 -0
  45. data/lib/babushka/core_patches/symbol.rb +12 -0
  46. data/lib/babushka/core_patches/try.rb +15 -0
  47. data/lib/babushka/core_patches/uri.rb +24 -0
  48. data/lib/babushka/dep.rb +470 -0
  49. data/lib/babushka/dep_context.rb +18 -0
  50. data/lib/babushka/dep_definer.rb +115 -0
  51. data/lib/babushka/dep_pool.rb +49 -0
  52. data/lib/babushka/dep_runner.rb +85 -0
  53. data/lib/babushka/dsl.rb +26 -0
  54. data/lib/babushka/git_repo.rb +185 -0
  55. data/lib/babushka/helpers/git_helpers.rb +32 -0
  56. data/lib/babushka/helpers/log_helpers.rb +176 -0
  57. data/lib/babushka/helpers/path_helpers.rb +34 -0
  58. data/lib/babushka/helpers/run_helpers.rb +145 -0
  59. data/lib/babushka/helpers/shell_helpers.rb +229 -0
  60. data/lib/babushka/helpers/suggest_helpers.rb +16 -0
  61. data/lib/babushka/helpers/uri_helpers.rb +36 -0
  62. data/lib/babushka/ip.rb +160 -0
  63. data/lib/babushka/lambda_chooser.rb +40 -0
  64. data/lib/babushka/levenshtein.rb +125 -0
  65. data/lib/babushka/meta_dep.rb +65 -0
  66. data/lib/babushka/meta_dep_context.rb +15 -0
  67. data/lib/babushka/parameter.rb +143 -0
  68. data/lib/babushka/pkg_helper.rb +81 -0
  69. data/lib/babushka/pkg_helpers/apt_helper.rb +61 -0
  70. data/lib/babushka/pkg_helpers/base_helper.rb +19 -0
  71. data/lib/babushka/pkg_helpers/binpkgsrc_helper.rb +48 -0
  72. data/lib/babushka/pkg_helpers/binports_helper.rb +34 -0
  73. data/lib/babushka/pkg_helpers/brew_helper.rb +110 -0
  74. data/lib/babushka/pkg_helpers/gem_helper.rb +120 -0
  75. data/lib/babushka/pkg_helpers/macports_helper.rb +22 -0
  76. data/lib/babushka/pkg_helpers/npm_helper.rb +45 -0
  77. data/lib/babushka/pkg_helpers/pacman_helper.rb +27 -0
  78. data/lib/babushka/pkg_helpers/pip_helper.rb +45 -0
  79. data/lib/babushka/pkg_helpers/src_helper.rb +16 -0
  80. data/lib/babushka/pkg_helpers/yum_helper.rb +25 -0
  81. data/lib/babushka/popen.rb +40 -0
  82. data/lib/babushka/prompt.rb +176 -0
  83. data/lib/babushka/renderable.rb +67 -0
  84. data/lib/babushka/resource.rb +215 -0
  85. data/lib/babushka/run_reporter.rb +60 -0
  86. data/lib/babushka/shell.rb +108 -0
  87. data/lib/babushka/source.rb +216 -0
  88. data/lib/babushka/source_pool.rb +146 -0
  89. data/lib/babushka/system_definitions.rb +97 -0
  90. data/lib/babushka/system_profile.rb +210 -0
  91. data/lib/babushka/task.rb +142 -0
  92. data/lib/babushka/vars.rb +108 -0
  93. data/lib/babushka/version_of.rb +65 -0
  94. data/lib/babushka/version_str.rb +57 -0
  95. data/lib/babushka/xml_string.rb +28 -0
  96. data/lib/components.rb +82 -0
  97. data/lib/fancypath/fancypath.rb +200 -0
  98. data/lib/inkan/inkan.rb +76 -0
  99. data/spec/acceptance/acceptance.rb +43 -0
  100. data/spec/acceptance_helper.rb +113 -0
  101. data/spec/archives/Blah.app.zip +0 -0
  102. data/spec/archives/archive.tar +0 -0
  103. data/spec/archives/archive.tar.bz2 +0 -0
  104. data/spec/archives/archive.tar.gz +0 -0
  105. data/spec/archives/archive.tbz2 +0 -0
  106. data/spec/archives/archive.tgz +0 -0
  107. data/spec/archives/archive.zip +0 -0
  108. data/spec/archives/content.txt +5 -0
  109. data/spec/archives/invalid_archive +5 -0
  110. data/spec/archives/nested archive/content.txt +5 -0
  111. data/spec/archives/nested_archive.tar +0 -0
  112. data/spec/archives/really_a_gzip.zip +0 -0
  113. data/spec/archives/test-0.3.1.tgz +0 -0
  114. data/spec/archives/tgz_archive +0 -0
  115. data/spec/archives/zip_without_extension +0 -0
  116. data/spec/babushka/accepts_for_spec.rb +174 -0
  117. data/spec/babushka/accepts_for_support.rb +72 -0
  118. data/spec/babushka/cmdline/console_spec.rb +11 -0
  119. data/spec/babushka/cmdline/help_spec.rb +61 -0
  120. data/spec/babushka/cmdline/version_spec.rb +10 -0
  121. data/spec/babushka/core_patches_spec.rb +171 -0
  122. data/spec/babushka/dep_context_spec.rb +58 -0
  123. data/spec/babushka/dep_definer_spec.rb +152 -0
  124. data/spec/babushka/dep_definer_support.rb +36 -0
  125. data/spec/babushka/dep_spec.rb +567 -0
  126. data/spec/babushka/dep_support.rb +29 -0
  127. data/spec/babushka/deps_spec.rb +113 -0
  128. data/spec/babushka/gem_helper_spec.rb +90 -0
  129. data/spec/babushka/git_repo_spec.rb +396 -0
  130. data/spec/babushka/ip_spec.rb +131 -0
  131. data/spec/babushka/lambda_chooser_spec.rb +115 -0
  132. data/spec/babushka/meta_dep_definer_spec.rb +127 -0
  133. data/spec/babushka/meta_dep_wrapper_spec.rb +32 -0
  134. data/spec/babushka/parameter_spec.rb +135 -0
  135. data/spec/babushka/path_helpers_spec.rb +102 -0
  136. data/spec/babushka/prompt_spec.rb +188 -0
  137. data/spec/babushka/renderable_spec.rb +100 -0
  138. data/spec/babushka/resource_spec.rb +141 -0
  139. data/spec/babushka/run_helpers_spec.rb +26 -0
  140. data/spec/babushka/shell_helpers_spec.rb +244 -0
  141. data/spec/babushka/shell_spec.rb +19 -0
  142. data/spec/babushka/source_pool_spec.rb +320 -0
  143. data/spec/babushka/source_pool_support.rb +31 -0
  144. data/spec/babushka/source_spec.rb +382 -0
  145. data/spec/babushka/source_support.rb +17 -0
  146. data/spec/babushka/system_profile_spec.rb +61 -0
  147. data/spec/babushka/task_spec.rb +141 -0
  148. data/spec/babushka/uri_spec.rb +13 -0
  149. data/spec/babushka/vars_spec.rb +59 -0
  150. data/spec/babushka/version_of_spec.rb +110 -0
  151. data/spec/babushka/version_str_spec.rb +130 -0
  152. data/spec/babushka/version_str_support.rb +37 -0
  153. data/spec/babushka/xml_string_spec.rb +98 -0
  154. data/spec/deps/bad/broken.rb +7 -0
  155. data/spec/deps/bad/working.rb +3 -0
  156. data/spec/deps/good/meta.rb +14 -0
  157. data/spec/deps/good/test.rb +11 -0
  158. data/spec/deps/outer/deps.rb +19 -0
  159. data/spec/deps/outer/more deps.rb +11 -0
  160. data/spec/deps/params/params.rb +10 -0
  161. data/spec/fancypath/fancypath_spec.rb +272 -0
  162. data/spec/fancypath_support.rb +10 -0
  163. data/spec/inkan/inkan_spec.rb +217 -0
  164. data/spec/renderable/different_example.conf.erb +4 -0
  165. data/spec/renderable/example.conf.erb +3 -0
  166. data/spec/renderable/example.sh +6 -0
  167. data/spec/renderable/with_binding.conf.erb +4 -0
  168. data/spec/renderable/xml_example.conf.erb +8 -0
  169. data/spec/repos/remote.git.tgz +0 -0
  170. data/spec/spec_helper.rb +87 -0
  171. metadata +238 -0
@@ -0,0 +1,16 @@
1
+ module Babushka
2
+ module SuggestHelpers
3
+ def suggest_value_for typo, choices
4
+ if (possible_matches = choices.similar_to typo.to_s).empty?
5
+ nil # nothing to suggest
6
+ elsif possible_matches.length == 1
7
+ Prompt.confirm "#{"Did you mean".colorize('grey')} '#{possible_matches.first}'#{"?".colorize('grey')}" do
8
+ possible_matches.first
9
+ end or false
10
+ else
11
+ log "Similar: #{possible_matches.map {|d| "'#{d}'" }.join(', ')}"
12
+ Prompt.get_value("Did you mean any of those".colorize('grey'), :default => possible_matches.first)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ module Babushka
2
+ module UriHelpers
3
+
4
+ def setup_source_uris
5
+ parse_uris
6
+ requires_when_unmet(@uris.map(&:scheme).uniq & %w[ git ])
7
+ end
8
+
9
+ def parse_uris
10
+ @uris = source.map(&uri_processor(:escape)).map(&uri_processor(:parse))
11
+ @extra_uris = extra_source.map(&uri_processor(:escape)).map(&uri_processor(:parse)) if respond_to?(:extra_source)
12
+ end
13
+
14
+ def uri_processor(method_name)
15
+ L{|uri| URI.send(method_name, uri.respond_to?(:call) ? uri.call : uri.to_s) }
16
+ end
17
+
18
+ def process_sources &block
19
+ @extra_uris.each {|uri| handle_source uri } unless @extra_uris.nil?
20
+ @uris.all? {|uri| handle_source uri, &block } unless @uris.nil?
21
+ end
22
+
23
+ def handle_source uri, &block
24
+ uri = uri_processor(:parse).call(uri) unless uri.is_a?(URI)
25
+ case uri.scheme
26
+ when 'git'
27
+ git uri, &block
28
+ when 'http', 'https', 'ftp', nil # We let `curl` work out the protocol if it's nil.
29
+ Resource.extract uri, &block
30
+ else
31
+ log_error "Babushka can't handle #{uri.scheme}:// URLs yet. But it can if you write a patch! :)"
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,160 @@
1
+ module Babushka
2
+ class IP
3
+ attr_reader :bytes
4
+
5
+ def initialize input
6
+ @bytes = sanitize input
7
+ end
8
+
9
+ def to_s
10
+ bytes.join '.'
11
+ end
12
+
13
+ def == other
14
+ bytes == other.bytes
15
+ end
16
+
17
+ # Returns whether this IP should be considered a valid one for a client to be using.
18
+ def valid?
19
+ [:public, :private, :loopback].include? describe
20
+ end
21
+
22
+ def next
23
+ offset_by(1)
24
+ end
25
+
26
+ def prev
27
+ offset_by(-1)
28
+ end
29
+
30
+ private
31
+
32
+ def sanitize input
33
+ if input.is_a? IP
34
+ input.bytes.dup
35
+ elsif input.is_a? Array
36
+ input.select {|i| (0..255).include? i }
37
+ else
38
+ parse_and_sanitize input do |str,val|
39
+ val if ((1..255) === val) || (val == 0 && str == '0')
40
+ end
41
+ end
42
+ end
43
+
44
+ def parse_and_sanitize input, &block
45
+ parts = input.strip.split('.')
46
+ parts.zip(
47
+ parts.map(&:to_i)
48
+ ).map {|(str,val)|
49
+ yield str, val
50
+ }.compact
51
+ end
52
+
53
+ def offset_by offset
54
+ IP.new bytes.reverse.inject([offset]) {|acc,byte|
55
+ carry, next_byte = (byte + acc.pop).divmod(256)
56
+ acc.push next_byte
57
+ acc.push carry
58
+ }[0...4].reverse.join('.')
59
+ end
60
+
61
+ # Returns a symbol describing the class of IP address +self+ represents, if any.
62
+ #
63
+ # Examples:
64
+ #
65
+ # "Hello world!".valid_ip? #=> false
66
+ # "192.168.".valid_ip? #=> false
67
+ # "127.0.0.1".valid_ip? #=> :loopback
68
+ # "172.24.137.6".valid_ip? #=> :private
69
+ # "169.254.1.142".valid_ip? #=> :self_assigned
70
+ # "72.9.108.122".valid_ip? #=> :public
71
+ def describe
72
+ if bytes.length != 4
73
+ false
74
+ elsif bytes.starts_with? 0 # Source hosts on "this" network
75
+ :reserved
76
+ elsif bytes.starts_with? 127 # Loopback network; RFC1700
77
+ :loopback
78
+ elsif bytes.starts_with? 10 # Class-A private; RFC1918
79
+ :private
80
+ elsif bytes.starts_with?(172) && ((16..31) === bytes[1]) # Class-B private; RFC1918
81
+ :private
82
+ elsif bytes.starts_with? 169, 254 # Link-local range; RFC3330/3927
83
+ [0, 255].include?(bytes[2]) ? :reserved : :self_assigned
84
+ elsif bytes.starts_with? 192, 0, 2 # TEST-NET - used as example.com IP
85
+ :reserved
86
+ elsif bytes.starts_with? 192, 88, 99 # 6-to-4 relay anycast; RFC3068
87
+ :reserved
88
+ elsif bytes.starts_with? 192, 168 # Class-C private; RFC1918
89
+ :private
90
+ elsif bytes.starts_with? 198, 18 # Benchmarking; RFC2544
91
+ :reserved
92
+ else
93
+ :public
94
+ end
95
+ end
96
+ end
97
+
98
+ class IPRange < IP
99
+ def valid?
100
+ !bytes.empty?
101
+ end
102
+
103
+ def padded_bytes
104
+ bytes.concat(['x'] * (4 - bytes.length))
105
+ end
106
+
107
+ def ip_for address_part
108
+ IP.new padded_bytes.zip(
109
+ IPTail.new(address_part).padded_bytes
110
+ ).map {|(network, address)|
111
+ [network, address, 0].detect {|i| i != 'x' }
112
+ }
113
+ end
114
+
115
+ def first
116
+ ip_for 'x.0.0.1'
117
+ end
118
+
119
+ def last
120
+ ip_for 'x.255.255.255'
121
+ end
122
+
123
+ def subnet
124
+ padded_bytes.map {|byte|
125
+ byte == 'x' ? '0' : '255'
126
+ }.join('.')
127
+ end
128
+
129
+ def broadcast
130
+ padded_bytes.map {|byte|
131
+ byte == 'x' ? '255' : byte
132
+ }.join('.')
133
+ end
134
+
135
+ private
136
+ def sanitize input
137
+ if /^\d+(\.\d+)*(\.x)+$/ !~ input
138
+ []
139
+ else
140
+ parse_and_sanitize input.gsub(/x(\.x)*$/, 'x') do |str,val|
141
+ if ((1..255) === val) || (val == 0 && str == '0')
142
+ val
143
+ elsif str == 'x'
144
+ str
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ class IPTail < IPRange
152
+ def padded_bytes
153
+ (['x'] * (4 - bytes.length)).concat bytes
154
+ end
155
+ private
156
+ def sanitize input
157
+ super(input.split('.').reverse.join('.')).reverse
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,40 @@
1
+ module Babushka
2
+ class LambdaChooser
3
+
4
+ attr_reader :owner
5
+
6
+ def var(name, opts = {}) owner.var(name, opts) end
7
+
8
+ def initialize owner, *possible_choices, &block
9
+ raise ArgumentError, "You can't use :otherwise as a choice name, because it's reserved." if possible_choices.include?(:otherwise)
10
+ @owner = owner
11
+ @possible_choices = possible_choices.push(:otherwise)
12
+ @block = block
13
+ @results = {}
14
+ end
15
+
16
+ def choose choices, method_name = nil
17
+ self.class.send :alias_method, (method_name || :on), :process_choice
18
+ block_result = instance_eval(&@block)
19
+ @results.empty? ? block_result : [*choices].push(:otherwise).pick {|c| @results[c] }
20
+ end
21
+
22
+ def otherwise first = nil, *rest, &block
23
+ process_choice :otherwise, first, *rest, &block
24
+ end
25
+
26
+ def process_choice choice, first = nil, *rest, &block
27
+ raise "You can supply values or a block, but not both." if first && block
28
+ raise "The choice '#{choice}' isn't valid." unless @possible_choices.include?(choice)
29
+
30
+ @results[choice] = if block
31
+ block
32
+ elsif first.is_a? Hash
33
+ first
34
+ else
35
+ [*first].concat(rest)
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,125 @@
1
+ # This code is licensed under the MIT license with permission
2
+ # from the author. The license follows.
3
+ #
4
+ # Copyright (c) 2008-2009 Paul Battley (pbattley@gmail.com)
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ module Babushka
26
+ # The Levenshtein distance is a metric for measuring the amount
27
+ # of difference between two sequences (i.e., the so called edit
28
+ # distance). The Levenshtein distance between two sequences is
29
+ # given by the minimum number of operations needed to transform
30
+ # one sequence into the other, where an operation is an
31
+ # insertion, deletion, or substitution of a single element.
32
+ #
33
+ # More information about the Levenshtein distance algorithm:
34
+ # http://en.wikipedia.org/wiki/Levenshtein_distance .
35
+
36
+ module Levenshtein
37
+ VERSION = "0.2.0"
38
+
39
+ # Returns the Levenshtein distance as a number between 0.0 and
40
+ # 1.0. It's basically the Levenshtein distance divided by the
41
+ # length of the longest sequence.
42
+
43
+ def self.normalized_distance(s1, s2, threshold=nil)
44
+ s1, s2 = s2, s1 if s1.length > s2.length # s1 is the short one; s2 is the long one.
45
+
46
+ if s2.length == 0
47
+ 0.0 # Since s1.length < s2.length, s1 must be empty as well.
48
+ else
49
+ if threshold
50
+ if d = self.distance(s1, s2, (threshold*s2.length+1).to_i)
51
+ d.to_f/s2.length
52
+ else
53
+ nil
54
+ end
55
+ else
56
+ self.distance(s1, s2).to_f/s2.length
57
+ end
58
+ end
59
+ end
60
+
61
+ # Returns the Levenshtein distance between two sequences.
62
+ #
63
+ # The two sequences can be two strings, two arrays, or two other
64
+ # objects. Strings, arrays and arrays of strings are handled with
65
+ # optimized (very fast) C code. All other sequences are handled
66
+ # with generic (fast) C code.
67
+ #
68
+ # The sequences should respond to :length and :[] and all objects
69
+ # in the sequences (as returned by []) should response to :==.
70
+
71
+ def self.distance(s1, s2, threshold=nil)
72
+ s1, s2 = s2, s1 if s1.length > s2.length # s1 is the short one; s2 is the long one.
73
+
74
+ # Handle some basic circumstances.
75
+
76
+ return 0 if s1 == s2
77
+ return s2.length if s1.length == 0
78
+
79
+ if threshold
80
+ return nil if (s2.length-s1.length) >= threshold
81
+
82
+ a1, a2 = nil, nil
83
+ a1, a2 = s1, s2 if s1.respond_to?(:-) and s2.respond_to?(:-)
84
+ a1, a2 = s1.scan(/./), s2.scan(/./) if s1.respond_to?(:scan) and s2.respond_to?(:scan)
85
+
86
+ if a1 and a2
87
+ return nil if (a1-a2).length >= threshold
88
+ return nil if (a2-a1).length >= threshold
89
+ end
90
+ end
91
+
92
+ distance_fast_or_slow(s1, s2, threshold)
93
+ end
94
+
95
+ def self.distance_fast_or_slow(s1, s2, threshold) # :nodoc:
96
+ if respond_to?(:levenshtein_distance_fast)
97
+ levenshtein_distance_fast(s1, s2, threshold) # Implemented in C.
98
+ else
99
+ levenshtein_distance_slow(s1, s2, threshold) # Implemented in Ruby.
100
+ end
101
+ end
102
+
103
+ def self.levenshtein_distance_slow(s1, s2, threshold) # :nodoc:
104
+ row = (0..s1.length).to_a
105
+
106
+ 1.upto(s2.length) do |y|
107
+ prow = row
108
+ row = [y]
109
+
110
+ 1.upto(s1.length) do |x|
111
+ row[x] = [prow[x]+1, row[x-1]+1, prow[x-1]+(s1[x-1]==s2[y-1] ? 0 : 1)].min
112
+ end
113
+
114
+ # Stop analysing this sequence as soon as the best possible
115
+ # result for this sequence is bigger than the best result so far.
116
+ # (The minimum value in the next row will be equal to or greater
117
+ # than the minimum value in this row.)
118
+
119
+ return nil if threshold and row.min >= threshold
120
+ end
121
+
122
+ row[-1]
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,65 @@
1
+ module Babushka
2
+ class MetaDep
3
+ include LogHelpers
4
+
5
+ INVALID_NAMES = %w[base]
6
+
7
+ VALID_NAME_START_CHARS = /[a-z]/
8
+ VALID_NAME_CHARS = /#{VALID_NAME_START_CHARS}[a-z0-9_]*/
9
+ VALID_NAME_START = /^#{VALID_NAME_START_CHARS}/
10
+ VALID_NAME = /\A#{VALID_NAME_CHARS}\z/m
11
+
12
+ TEMPLATE_NAME_MATCH = /\A.+\.(#{VALID_NAME_CHARS})\z/m
13
+
14
+ def self.for supplied_name, source, opts, &block
15
+ name = supplied_name.to_s.downcase
16
+
17
+ if name.empty?
18
+ raise ArgumentError, "You can't define a template with a blank name."
19
+ elsif INVALID_NAMES.include? name
20
+ raise ArgumentError, "You can't use '#{name}' for a template name, because it's reserved."
21
+ elsif name[VALID_NAME_START].nil?
22
+ raise ArgumentError, "You can't use '#{name}' for a template name - it must start with a letter."
23
+ elsif name[VALID_NAME].nil?
24
+ raise ArgumentError, "You can't use '#{name}' for a template name - it can only contain [a-z0-9_]."
25
+ elsif Base.sources.current_load_source.templates.for(name)
26
+ raise ArgumentError, "A template called '#{name}' has already been defined."
27
+ else
28
+ new name, source, opts, &block
29
+ end
30
+ end
31
+
32
+ attr_reader :name, :source, :opts, :context_class
33
+
34
+ def desc; context_class.desc end
35
+
36
+ def initialize name, source, opts, &block
37
+ @name, @source, @opts, @block = name, source, opts, block
38
+ debug "Defining #{source.name}:#{name} template"
39
+ @context_class = build_context block
40
+ source.templates.register self
41
+ end
42
+
43
+ # Returns this template's name, including the source name as a prefix if
44
+ # this template is in a cloneable source.
45
+ #
46
+ # A cloneable source is one that babushka knows how to automatically
47
+ # update; i.e. a source that babushka could have installed itself.
48
+ #
49
+ # In effect, a cloneable source is one whose deps you prefix when you run
50
+ # them, so this method returns the template's name in the same form as you
51
+ # would refer to it when using it from another source.
52
+ def contextual_name
53
+ source.cloneable? ? "#{source.name}:#{name}" : name
54
+ end
55
+
56
+ def build_context block
57
+ Class.new(MetaDepContext, &block).tap {|context|
58
+ shadow = self
59
+ context.metaclass.send :define_method, :source_template do
60
+ shadow
61
+ end
62
+ }
63
+ end
64
+ end
65
+ end