build-text 1.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8e56e9d24d3f0ec039b6380bb7e921f9ef7713c5
4
+ data.tar.gz: fd4360cd52c71b0474f94fb4e4c6d1d6517a637c
5
+ SHA512:
6
+ metadata.gz: 887249e37b4e6df5368acbd13c3733997e9095ae3ddb6f8fa58b8604881c2229bce72cae0cd2740f7dcc03d8ef70da42a805fd7f9b3bc6a04687a4c9098f1e59
7
+ data.tar.gz: ee42f4d82827b140c98e4a47c2efbd123881c195b0bbb0d1fa84536daa95c681bcb8016c6a9babf0db066397d949971e70fed170439f6fb3d1c6ca34ccce9df0
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --format documentation
2
+ --backtrace
3
+ --warnings
4
+ --require spec_helper
@@ -0,0 +1,19 @@
1
+ language: ruby
2
+ sudo: false
3
+ dist: trusty
4
+ cache: bundler
5
+ rvm:
6
+ - 2.1
7
+ - 2.2
8
+ - 2.3
9
+ - 2.4
10
+ - ruby-head
11
+ - jruby-head
12
+ matrix:
13
+ allow_failures:
14
+ - rvm: "ruby-head"
15
+ - rvm: "jruby-head"
16
+ addons:
17
+ apt:
18
+ packages:
19
+ - graphviz
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'guard'
7
+ gem 'guard-rspec'
8
+ end
9
+
10
+ group :test do
11
+ gem 'simplecov'
12
+ gem 'coveralls', require: false
13
+ end
@@ -0,0 +1,9 @@
1
+
2
+ directories %w(lib spec)
3
+ clearing :on
4
+
5
+ guard :rspec, cmd: "bundle exec rspec" do
6
+ watch(%r{^spec/.+_spec\.rb$})
7
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
8
+ watch("spec/spec_helper.rb") { "spec" }
9
+ end
@@ -0,0 +1,121 @@
1
+ # Build::Text
2
+
3
+ `Build::Text` provides substitutions and merging for textual files, typically code.
4
+
5
+ [![Build Status](https://secure.travis-ci.org/ioquatix/build-text.svg)](http://travis-ci.org/ioquatix/build-text)
6
+ [![Code Climate](https://codeclimate.com/github/ioquatix/build-text.svg)](https://codeclimate.com/github/ioquatix/build-text)
7
+ [![Coverage Status](https://coveralls.io/repos/ioquatix/build-text/badge.svg)](https://coveralls.io/r/ioquatix/build-text)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'build-text'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install build-text
22
+
23
+ ## Usage
24
+
25
+ ### Substitutions
26
+
27
+ Provides a very simple template language for doing text substitutions:
28
+
29
+ ```ruby
30
+ require 'build/text'
31
+
32
+ substitutions = Build::Text::Substitutions.new
33
+ substitutions['ANIMAL'] = 'cat'
34
+ substitutions['SOUND'] = 'meow'
35
+
36
+ puts substitutions.apply("The $ANIMAL says $SOUND.")
37
+ # Prints "The cat says meow."
38
+ ```
39
+
40
+ It can also do indented expressions.
41
+
42
+ ```ruby
43
+ require 'build/text'
44
+
45
+ substitutions = Build::Text::Substitutions.new
46
+ substitutions['MODULE'] = ["module Meow\n", "end\n"]
47
+
48
+ puts substitutions.apply("<MODULE>\ndef function\nend\n</MODULE>")
49
+ # Prints:
50
+ # module Meow
51
+ # def function
52
+ # end
53
+ # end
54
+ ```
55
+
56
+ ### Merging
57
+
58
+ A best effort merging strategy to combine two files. It uses LCS to match up sections, and then appends segments together.
59
+
60
+ Given the following input file:
61
+
62
+ ```c++
63
+ class Bob {
64
+ int alice();
65
+ }
66
+ ```
67
+
68
+ and the following template:
69
+
70
+ ```c++
71
+ class Bob {
72
+ int bob();
73
+ }
74
+ ```
75
+
76
+ it can be merged
77
+
78
+ ```ruby
79
+ merged = Build::Text::Merge.combine(input.lines, template.lines)
80
+ ```
81
+
82
+ and the merged result will be:
83
+
84
+ ```c++
85
+ class Bob {
86
+ int alice();
87
+ int bob();
88
+ }
89
+ ```
90
+
91
+ ## Contributing
92
+
93
+ 1. Fork it
94
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
95
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
96
+ 4. Push to the branch (`git push origin my-new-feature`)
97
+ 5. Create new Pull Request
98
+
99
+ ## License
100
+
101
+ Released under the MIT license.
102
+
103
+ Copyright, 2017, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
104
+
105
+ Permission is hereby granted, free of charge, to any person obtaining a copy
106
+ of this software and associated documentation files (the "Software"), to deal
107
+ in the Software without restriction, including without limitation the rights
108
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
109
+ copies of the Software, and to permit persons to whom the Software is
110
+ furnished to do so, subject to the following conditions:
111
+
112
+ The above copyright notice and this permission notice shall be included in
113
+ all copies or substantial portions of the Software.
114
+
115
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
116
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
117
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
118
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
119
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
120
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
121
+ THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |task|
5
+ task.rspec_opts = ["--require", "simplecov"] if ENV['COVERAGE']
6
+ end
7
+
8
+ task :default => :spec
@@ -0,0 +1,21 @@
1
+ # coding: utf-8
2
+ require_relative 'lib/build/text/version'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "build-text"
6
+ spec.version = Build::Text::VERSION
7
+ spec.authors = ["Samuel Williams"]
8
+ spec.email = ["samuel.williams@oriontransfer.co.nz"]
9
+ spec.summary = %q{Text substitutions and merging.}
10
+ spec.homepage = ""
11
+ spec.license = "MIT"
12
+
13
+ spec.files = `git ls-files -z`.split("\x0")
14
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
+ spec.require_paths = ["lib"]
17
+
18
+ spec.add_development_dependency "bundler", "~> 1.3"
19
+ spec.add_development_dependency "rspec", "~> 3.6"
20
+ spec.add_development_dependency "rake"
21
+ end
@@ -0,0 +1,24 @@
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_relative 'text/version'
22
+
23
+ require_relative 'text/merge'
24
+ require_relative 'text/substitutions'
@@ -0,0 +1,67 @@
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
+ module Build
22
+ module Text
23
+ class Indentation
24
+ TAB = "\t".freeze
25
+
26
+ def initialize(prefix, level, indent)
27
+ @prefix = prefix
28
+ @level = level
29
+ @indent = indent
30
+ end
31
+
32
+ def freeze
33
+ indentation
34
+
35
+ @prefix.freeze
36
+ @level.freeze
37
+ @indent.freeze
38
+
39
+ super
40
+ end
41
+
42
+ def indentation
43
+ @indentation ||= @prefix + (@indent * @level)
44
+ end
45
+
46
+ def + other
47
+ indentation + other
48
+ end
49
+
50
+ def << text
51
+ text.gsub(/^/){|m| m + indentation}
52
+ end
53
+
54
+ def by(depth)
55
+ Indentation.new(@prefix, @level + depth, @indent)
56
+ end
57
+
58
+ def with_prefix(prefix)
59
+ Indentation.new(prefix, @level, @indent)
60
+ end
61
+
62
+ def self.none
63
+ self.new('', 0, TAB)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,144 @@
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 Build
23
+ module Text
24
+ module Merge
25
+ Difference = Struct.new(:type, :value)
26
+
27
+ def self.combine(old_text, new_text)
28
+ lcs = lcs(old_text, new_text)
29
+ changes = []
30
+
31
+ n = 0; o = 0; l = 0
32
+ while o < old_text.size and n < new_text.size and l < lcs.size
33
+ if !similar(old_text[o], lcs[l])
34
+ changes << Difference.new(:old, old_text[o])
35
+ o+=1
36
+ elsif !similar(new_text[n], lcs[l])
37
+ changes << Difference.new(:new, new_text[n])
38
+ n+=1
39
+ else
40
+ changes << Difference.new(:both, lcs[l])
41
+ o+=1; n+=1; l+=1
42
+ end
43
+ end
44
+
45
+ changes.map do |change|
46
+ change.value
47
+ end
48
+ end
49
+
50
+ # This code is based directly on the Text gem implementation
51
+ # Returns a value representing the "cost" of transforming str1 into str2
52
+ def self.levenshtein_distance(s, t)
53
+ n = s.length
54
+ m = t.length
55
+
56
+ return m if n == 0
57
+ return n if m == 0
58
+
59
+ d = (0..m).to_a
60
+ x = nil
61
+
62
+ n.times do |i|
63
+ e = i+1
64
+
65
+ m.times do |j|
66
+ cost = (s[i] == t[j]) ? 0 : 1
67
+ x = [
68
+ d[j+1] + 1, # insertion
69
+ e + 1, # deletion
70
+ d[j] + cost # substitution
71
+ ].min
72
+ d[j] = e
73
+ e = x
74
+ end
75
+
76
+ d[m] = x
77
+ end
78
+
79
+ return x
80
+ end
81
+
82
+ # Calculate the similarity of two sequences, return true if they are with factor% similarity.
83
+ def self.similar(s, t, factor = 0.15)
84
+ return true if s == t
85
+
86
+ distance = levenshtein_distance(s, t)
87
+ average_length = (s.length + t.length) / 2.0
88
+
89
+ proximity = (distance.to_f / average_length)
90
+
91
+ return proximity <= factor
92
+ end
93
+
94
+ LCSNode = Struct.new(:value, :previous)
95
+
96
+ # Find the Longest Common Subsequence in the given sequences x, y.
97
+ def self.lcs(x, y)
98
+ # Create the lcs matrix:
99
+ m = Array.new(x.length + 1) do
100
+ Array.new(y.length + 1) do
101
+ LCSNode.new(nil, nil)
102
+ end
103
+ end
104
+
105
+ # LCS(i, 0) and LCS(0, j) are always 0:
106
+ for i in 0..x.length do m[i][0].value = 0 end
107
+ for j in 0..y.length do m[0][j].value = 0 end
108
+
109
+ # Main algorithm, solve row by row:
110
+ for i in 1..x.length do
111
+ for j in 1..y.length do
112
+ if similar(x[i-1], y[j-1])
113
+ # Value is based on maximizing the length of the matched strings:
114
+ m[i][j].value = m[i-1][j-1].value + (x[i-1].chomp.length + y[j-1].chomp.length) / 2.0
115
+ m[i][j].previous = [-1, -1]
116
+ else
117
+ if m[i-1][j].value >= m[i][j-1].value
118
+ m[i][j].value = m[i-1][j].value
119
+ m[i][j].previous = [-1, 0]
120
+ else
121
+ m[i][j].value = m[i][j-1].value
122
+ m[i][j].previous = [0, -1]
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ # Get the solution by following the path backwards from m[x.length][y.length]
129
+ lcs = []
130
+
131
+ i = x.length; j = y.length
132
+ until m[i][j].previous == nil do
133
+ if m[i][j].previous == [-1, -1]
134
+ lcs << x[i-1]
135
+ end
136
+
137
+ i, j = i + m[i][j].previous[0], j + m[i][j].previous[1]
138
+ end
139
+
140
+ return lcs.reverse!
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,200 @@
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 'indentation'
22
+
23
+ module Build
24
+ module Text
25
+ class Substitutions
26
+ def initialize(ordered = [])
27
+ @ordered = ordered
28
+ end
29
+
30
+ def freeze
31
+ @ordered.freeze
32
+
33
+ super
34
+ end
35
+
36
+ def []= keyword, value
37
+ if Array === value
38
+ open, close = *value.each_slice(value.length / 2)
39
+ @ordered << NestedSubstitution.new(keyword, open, close)
40
+ else
41
+ @ordered << SymbolicSubstitution.new('$' + keyword, value.to_s)
42
+ end
43
+ end
44
+
45
+ def << substitution
46
+ @ordered << substition
47
+ end
48
+
49
+ def + other
50
+ Substitutions.new(@ordered + other.ordered)
51
+ end
52
+
53
+ attr :ordered
54
+
55
+ def call(text)
56
+ apply(text)
57
+ end
58
+
59
+ def apply(text)
60
+ return text unless @ordered.count > 0
61
+
62
+ grouped = [[@ordered.first]]
63
+
64
+ @ordered.drop(1).each do |substitution|
65
+ if grouped.last[0].class == substitution.class
66
+ grouped.last << substitution
67
+ else
68
+ grouped << [substitution]
69
+ end
70
+ end
71
+
72
+ grouped.each do |group|
73
+ text = group.first.class.apply(text, group)
74
+ end
75
+
76
+ return text
77
+ end
78
+
79
+ class SymbolicSubstitution
80
+ def initialize(keyword, value)
81
+ @keyword = keyword
82
+ @value = value
83
+ end
84
+
85
+ attr :keyword
86
+ attr :value
87
+
88
+ def freeze
89
+ @keyword.freeze
90
+ @value.freeze
91
+
92
+ super
93
+ end
94
+
95
+ def apply(text)
96
+ text.gsub(@keyword, @value)
97
+ end
98
+
99
+ def self.apply(text, group)
100
+ substitutions = Hash[group.collect{|substitution| [substitution.keyword, substitution.value]}]
101
+
102
+ pattern = Regexp.new(substitutions.keys.map{|key| Regexp.escape(key)}.join('|'))
103
+
104
+ text.gsub(pattern) {|key| substitutions[key]}
105
+ end
106
+ end
107
+
108
+ class NestedSubstitution
109
+ def initialize(keyword, open, close, indent = "\t")
110
+ @keyword = keyword
111
+
112
+ @open = open
113
+ @close = close
114
+
115
+ @indent = indent
116
+ end
117
+
118
+ def freeze
119
+ @keyword.freeze
120
+ @open.freeze
121
+ @close.freeze
122
+ @indent.freeze
123
+
124
+ super
125
+ end
126
+
127
+ def line_pattern(prefix = '')
128
+ tag_pattern = Regexp.escape('<' + prefix + @keyword + '>')
129
+
130
+ # Line matching pattern:
131
+ Regexp.new('^(.*?)' + tag_pattern + '(.*)$', Regexp::MULTILINE | Regexp::EXTENDED)
132
+ end
133
+
134
+ def write_open(prefix, postfix, output, indentation)
135
+ depth = @open.size
136
+ indentation = indentation.with_prefix(prefix)
137
+
138
+ #output.write(prefix)
139
+ (0...depth).each do |i|
140
+ chunk = @open[i]
141
+ chunk.chomp! if i == depth-1
142
+
143
+ output.write(indentation.by(i) << chunk)
144
+ end
145
+ output.write(postfix)
146
+ end
147
+
148
+ def write_close(prefix, postfix, output, indentation)
149
+ depth = @close.size
150
+ indentation = indentation.with_prefix(prefix)
151
+
152
+ #output.write(prefix)
153
+ (0...depth).reverse_each do |i|
154
+ chunk = @close[-1 - i]
155
+ chunk.chomp! if i == 0
156
+
157
+ output.write(indentation.by(i) << chunk)
158
+ end
159
+ output.write(postfix)
160
+ end
161
+
162
+ def apply(text, level = 0)
163
+ open_pattern = line_pattern
164
+ close_pattern = line_pattern('/')
165
+
166
+ lines = text.each_line
167
+ output = StringIO.new
168
+
169
+ indent = lambda do |level, indentation|
170
+ while line = lines.next rescue nil
171
+ if line =~ open_pattern
172
+ write_open($1, $2, output, indentation)
173
+
174
+ indent[level + @open.count, indentation.by(@open.count)]
175
+
176
+ write_close($1, $2, output, indentation)
177
+ elsif line =~ close_pattern
178
+ break
179
+ else
180
+ output.write(indentation + line)
181
+ end
182
+ end
183
+ end
184
+
185
+ indent[0, Indentation.none]
186
+
187
+ return output.string
188
+ end
189
+
190
+ def self.apply(text, group)
191
+ group.each do |substitution|
192
+ text = substitution.apply(text)
193
+ end
194
+
195
+ return text
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,25 @@
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
+ module Build
22
+ module Text
23
+ VERSION = "1.0.0"
24
+ end
25
+ end
@@ -0,0 +1,46 @@
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
+ RSpec.describe Build::Text::Merge do
22
+ TARGET_FILE = <<-EOF
23
+ class Bob {
24
+ int alice();
25
+ }
26
+ EOF
27
+
28
+ TEMPLATE = <<-EOF
29
+ class Bob {
30
+ int bob();
31
+ }
32
+ EOF
33
+
34
+ MERGED = <<-EOF
35
+ class Bob {
36
+ int alice();
37
+ int bob();
38
+ }
39
+ EOF
40
+
41
+ it "can merge two files" do
42
+ merged = Build::Text::Merge.combine(TARGET_FILE.lines, TEMPLATE.lines)
43
+
44
+ expect(merged).to be == MERGED.lines
45
+ end
46
+ end
@@ -0,0 +1,61 @@
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
+ RSpec.describe Build::Text::Substitutions do
22
+ it "should substitute symbolic expressions" do
23
+ input = <<-EOF
24
+ $FOO $BAR
25
+ EOF
26
+
27
+ output = <<-EOF
28
+ baz bar
29
+ EOF
30
+
31
+ substitutions = Build::Text::Substitutions.new
32
+ substitutions['FOO'] = 'baz'
33
+ substitutions['BAR'] = 'bar'
34
+
35
+ expect(substitutions.apply(input)).to be == output
36
+ end
37
+
38
+ it "should indent nestest expressions" do
39
+ input = <<-EOF
40
+ <FOO>
41
+ $BAR
42
+ </FOO>
43
+ EOF
44
+
45
+ output = <<-EOF
46
+ enter
47
+ {
48
+ foo
49
+ Hello World
50
+ bar
51
+ }
52
+ exit
53
+ EOF
54
+
55
+ substitutions = Build::Text::Substitutions.new
56
+ substitutions['FOO'] = ["enter\n{\n", "foo\n", "bar\n", "}\nexit\n"]
57
+ substitutions['BAR'] = 'Hello World'
58
+
59
+ expect(substitutions.apply(input)).to be == output
60
+ end
61
+ end
@@ -0,0 +1,29 @@
1
+
2
+ if ENV['COVERAGE'] || ENV['TRAVIS']
3
+ begin
4
+ require 'simplecov'
5
+
6
+ SimpleCov.start do
7
+ add_filter "/spec/"
8
+ end
9
+
10
+ if ENV['TRAVIS']
11
+ require 'coveralls'
12
+ Coveralls.wear!
13
+ end
14
+ rescue LoadError
15
+ warn "Could not load simplecov: #{$!}"
16
+ end
17
+ end
18
+
19
+ require "bundler/setup"
20
+ require "build/text"
21
+
22
+ RSpec.configure do |config|
23
+ # Enable flags like --only-failures and --next-failure
24
+ config.example_status_persistence_file_path = ".rspec_status"
25
+
26
+ config.expect_with :rspec do |c|
27
+ c.syntax = :expect
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: build-text
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-07-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ - samuel.williams@oriontransfer.co.nz
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - Guardfile
67
+ - README.md
68
+ - Rakefile
69
+ - build-text.gemspec
70
+ - lib/build/text.rb
71
+ - lib/build/text/indentation.rb
72
+ - lib/build/text/merge.rb
73
+ - lib/build/text/substitutions.rb
74
+ - lib/build/text/version.rb
75
+ - spec/build/text/merge_spec.rb
76
+ - spec/build/text/substitutions_spec.rb
77
+ - spec/spec_helper.rb
78
+ homepage: ''
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.6.12
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Text substitutions and merging.
102
+ test_files:
103
+ - spec/build/text/merge_spec.rb
104
+ - spec/build/text/substitutions_spec.rb
105
+ - spec/spec_helper.rb