rubycut-babushka 0.10.6

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.
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