build-text 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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