incodesert 0.1.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.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/bin/incodesert +131 -0
- data/lib/incodesert.rb +5 -0
- data/lib/incodesert/documents.rb +216 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5ad6be7bdf06c9cba44bfad3b572459fd801874a
|
4
|
+
data.tar.gz: dc69f1f2234bdfb51e85d8468701c2f8fc70f0dc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 687a44dcbfcca68c7a8749008fc64e749b25363f26e8d4fbd9c45b9e62c4b0e0438f636d74e1b2f146d371004629525d3bce626648dbece53be921d3cc9208bd
|
7
|
+
data.tar.gz: 11ca8ea225c4a98d84185373ffb70e1f80577694675c1695172f0a72d13341efc8f460462c41d4eb6baa295b765c5167cda95c58f7b5a71b00508c9555d900a7
|
data/.gemtest
ADDED
File without changes
|
data/bin/incodesert
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'incodesert'
|
5
|
+
|
6
|
+
def usage
|
7
|
+
puts <<EOF
|
8
|
+
incodesert [flags] <source-file> <dest-file> [extractions-file]
|
9
|
+
incodesert inserts the code snippets found in the source file delimited
|
10
|
+
by special comment blocks and replaces matching comment blocks in the
|
11
|
+
destination file. If the optional extractions-file is supplied, that
|
12
|
+
file will contain the snippets of code removed from the destination
|
13
|
+
file (useful for later reverting the dest file).
|
14
|
+
|
15
|
+
Flag:
|
16
|
+
--help
|
17
|
+
Displays this message
|
18
|
+
--verbose
|
19
|
+
Provides diagnostics on stdout
|
20
|
+
--no-warn
|
21
|
+
By default, the replaced blocks in the destination will contain a
|
22
|
+
warning comment that the code there was auto-inserted by incodesert.
|
23
|
+
This flag suppresses that warning comment (most likely you would use
|
24
|
+
this when reverting from an extractions file).
|
25
|
+
--no-bak
|
26
|
+
By default, a backup of the destination file is produced with a .bak
|
27
|
+
suffix, just in case something goes horribly wrong. This flag
|
28
|
+
cleans up that file upon completion.
|
29
|
+
--classname <name>
|
30
|
+
By default, any occurance of the token __CLASSNAME__ in the source
|
31
|
+
file is replaced with the camel cased version of the destination
|
32
|
+
filename (sans path and suffix). If you want to override this
|
33
|
+
behavior with an explicit classname, supply it here.
|
34
|
+
EOF
|
35
|
+
|
36
|
+
abort
|
37
|
+
end
|
38
|
+
|
39
|
+
# Monkeypatch String to do camel casing.
|
40
|
+
class String
|
41
|
+
def camel_case
|
42
|
+
return self if self !~ /_/ && self =~ /[A-Z]+.*/
|
43
|
+
split('_').map{|e| e.capitalize}.join
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
verbose = false
|
49
|
+
no_warn = false
|
50
|
+
no_bak = false
|
51
|
+
classname = nil
|
52
|
+
|
53
|
+
if ARGV.length < 1
|
54
|
+
usage
|
55
|
+
end
|
56
|
+
|
57
|
+
while ARGV[0] =~ /^--/
|
58
|
+
arg = ARGV.shift
|
59
|
+
if arg == "--help"
|
60
|
+
usage
|
61
|
+
elsif arg == "--verbose"
|
62
|
+
verbose = true
|
63
|
+
elsif arg == "--no-warn"
|
64
|
+
no_warn = true
|
65
|
+
elsif arg == "--no-bak"
|
66
|
+
no_bak = true
|
67
|
+
elsif arg == "--classname"
|
68
|
+
classname = ARGV.shift
|
69
|
+
else
|
70
|
+
warn "Unrecognized flag: #{arg}"
|
71
|
+
usage
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if ARGV.length < 2 and ARGV.length > 3
|
76
|
+
usage
|
77
|
+
end
|
78
|
+
|
79
|
+
source_name = ARGV.shift
|
80
|
+
dest_name = ARGV.shift
|
81
|
+
extractions_name = ARGV.shift if ARGV.any?
|
82
|
+
bak_name = dest_name + ".bak"
|
83
|
+
|
84
|
+
unless classname
|
85
|
+
# Deduce the classname represented by the dest file
|
86
|
+
# First strip off the path before the filename
|
87
|
+
classname = dest_name.sub(/\.\w+$/, "")
|
88
|
+
# Then strip off the suffix
|
89
|
+
classname = classname.sub(/^.*\//, "")
|
90
|
+
|
91
|
+
classname = classname.camel_case
|
92
|
+
end
|
93
|
+
|
94
|
+
source = File.open(source_name).read
|
95
|
+
# Protect against goofy carriage returns on non-unix systems
|
96
|
+
source.gsub!(/\r\n?/, "\n")
|
97
|
+
|
98
|
+
FileUtils.cp(dest_name, bak_name)
|
99
|
+
destination = File.open(bak_name).read
|
100
|
+
# Protect against goofy carriage returns on non-unix systems
|
101
|
+
destination.gsub!(/\r\n?/, "\n")
|
102
|
+
|
103
|
+
documents = Incodesert::Documents.new(source, destination)
|
104
|
+
documents.verbose = verbose
|
105
|
+
documents.no_warn = no_warn
|
106
|
+
documents.source_name = source_name
|
107
|
+
|
108
|
+
documents.replacements["CLASSNAME"] = classname
|
109
|
+
|
110
|
+
# Enough setup, let's light this candle!
|
111
|
+
documents.perform_insertions!
|
112
|
+
|
113
|
+
# Spew any warnings that came up (usually mismatched token names)
|
114
|
+
warn documents.warnings
|
115
|
+
|
116
|
+
File.open(dest_name, "w") do |file|
|
117
|
+
file.print documents.destination
|
118
|
+
end
|
119
|
+
|
120
|
+
if extractions_name
|
121
|
+
File.open(extractions_name, "w") do |file|
|
122
|
+
file.print documents.extractions
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
if no_bak
|
127
|
+
FileUtils.rm(bak_name)
|
128
|
+
end
|
129
|
+
|
130
|
+
# All done!
|
131
|
+
|
data/lib/incodesert.rb
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
module Incodesert
|
2
|
+
|
3
|
+
# Documents holds all three documents involved in an incodesert insertion.
|
4
|
+
# Those three documents are:
|
5
|
+
# * The source: A String holding the blocks to be inserted delimited by special
|
6
|
+
# comments.
|
7
|
+
# * The destination: A String to receive the insertions that contains insertion
|
8
|
+
# points delimited by the same comments as appear in the source.
|
9
|
+
# * The extractions: A String of all the blocks that were removed in the destination
|
10
|
+
# in order to be replaced by the source. This can be useful to revert the changes.
|
11
|
+
#
|
12
|
+
# Documents also performs the insertion upon calling +perform_insertions!+
|
13
|
+
#
|
14
|
+
# Author:: Kirk Bowers (mailto:kirkbowers@yahoo.com)
|
15
|
+
# Copyright:: Copyright (c) 2015 Kirk Bowers
|
16
|
+
# License:: MIT License
|
17
|
+
class Documents
|
18
|
+
|
19
|
+
attr_accessor :source
|
20
|
+
attr_accessor :destination
|
21
|
+
attr_reader :extractions
|
22
|
+
attr_reader :warnings
|
23
|
+
|
24
|
+
attr_accessor :replacements
|
25
|
+
|
26
|
+
attr_accessor :verbose
|
27
|
+
attr_accessor :no_warn
|
28
|
+
attr_accessor :source_name
|
29
|
+
|
30
|
+
def initialize(source = "", destination = "")
|
31
|
+
@source = source
|
32
|
+
@destination = destination
|
33
|
+
@extractions = []
|
34
|
+
@warnings = []
|
35
|
+
|
36
|
+
@replacements = {}
|
37
|
+
|
38
|
+
@verbose = false
|
39
|
+
@no_warn = false
|
40
|
+
@source_name = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# Shadow global warn function
|
44
|
+
def warn(message)
|
45
|
+
@warnings.push message
|
46
|
+
end
|
47
|
+
|
48
|
+
def perform_insertions!
|
49
|
+
process_source
|
50
|
+
|
51
|
+
process_destination
|
52
|
+
|
53
|
+
@destination = @destination.join("\n")
|
54
|
+
@extractions = @extractions.join("\n")
|
55
|
+
# Add an extra newline to extractions so it doesn't end without one if non-empty
|
56
|
+
@extractions += "\n" if @extractions != ""
|
57
|
+
|
58
|
+
@warnings = @warnings.join("\n")
|
59
|
+
@warnings += "\n" if @warnings != ""
|
60
|
+
end
|
61
|
+
|
62
|
+
#-----------------------------------------------------------
|
63
|
+
private
|
64
|
+
|
65
|
+
def process_source
|
66
|
+
@blocks = {}
|
67
|
+
@current_block_name = ""
|
68
|
+
|
69
|
+
# The "-1" is an unintuitive way of saying don't trim off trailing newlines.
|
70
|
+
# We want to preserve the source exactly.
|
71
|
+
lines = @source.split("\n", -1)
|
72
|
+
|
73
|
+
lines.each do |line|
|
74
|
+
# If we match either a C-style or script style comment, exactly 3 < (in) chars,
|
75
|
+
# one or more spaces, then anything as a token (even including spaces)
|
76
|
+
# that opens a source block
|
77
|
+
if line =~ /^(\s*(\/{2}|#))\s+<{3}\s+(.+)/
|
78
|
+
open_source_block(line, $1, $3)
|
79
|
+
# Similarly, match a comment, exactly 3 > (out) chars, and a token,
|
80
|
+
# close the block
|
81
|
+
elsif line =~ /^\s*(\/{2}|#)\s+>{3}\s+(.+)/
|
82
|
+
close_source_block(line, $2)
|
83
|
+
# If we are in a current block, remember this line
|
84
|
+
elsif @current_block_name != ""
|
85
|
+
@current_block.push(line)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def open_source_block(line, comment, name)
|
91
|
+
name = name.rstrip
|
92
|
+
puts "Source: open block: #{name}" if @verbose
|
93
|
+
@current_block_name = name
|
94
|
+
@current_block = [line]
|
95
|
+
unless @no_warn
|
96
|
+
@current_block.push("#{comment}")
|
97
|
+
@current_block.push("#{comment} WARNING!!! This code auto-inserted by incodesert")
|
98
|
+
@current_block.push("#{comment} Do not edit this block!")
|
99
|
+
if @source_name
|
100
|
+
@current_block.push("#{comment} If you need to make changes, edit the source: #{@source_name}")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def close_source_block(line, name)
|
106
|
+
name = name.rstrip
|
107
|
+
puts "Source: close block: #{name}" if @verbose
|
108
|
+
if name == @current_block_name
|
109
|
+
@current_block.push(line)
|
110
|
+
@blocks[name] = @current_block
|
111
|
+
else
|
112
|
+
warn "In source: open and close blocks do not match!!"
|
113
|
+
warn "Opened with #{@current_block_name}"
|
114
|
+
warn "Closed with #{name}"
|
115
|
+
end
|
116
|
+
|
117
|
+
# Either way, we've attempted to close a block, so clear the current block name
|
118
|
+
# to signify we are not currently in a block at all.
|
119
|
+
@current_block_name = ""
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
def process_destination
|
124
|
+
@current_block_name = ""
|
125
|
+
@extractions = []
|
126
|
+
|
127
|
+
lines = @destination.split("\n", -1)
|
128
|
+
|
129
|
+
# We're going to rebuild the destination from scratch now
|
130
|
+
@destination = []
|
131
|
+
|
132
|
+
lines.each do |line|
|
133
|
+
if line =~ /^\s*(\/{2}|#)\s+<{3}\s+(.+)/
|
134
|
+
open_destination_block(line, $2)
|
135
|
+
elsif line =~ /^\s*(\/{2}|#)\s+>{3}\s+(.+)/
|
136
|
+
close_destination_block(line, $2)
|
137
|
+
elsif @current_block_name != ""
|
138
|
+
@current_block.push(line)
|
139
|
+
else
|
140
|
+
@destination.push(line)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def open_destination_block(line, name)
|
146
|
+
name = name.rstrip
|
147
|
+
puts "Destination: open block: #{name}" if @verbose
|
148
|
+
@current_block_name = name
|
149
|
+
@current_block = [line]
|
150
|
+
end
|
151
|
+
|
152
|
+
def close_destination_block(in_line, name)
|
153
|
+
name = name.rstrip
|
154
|
+
puts "Destination: close block: #{name}" if @verbose
|
155
|
+
|
156
|
+
@current_block.push(in_line)
|
157
|
+
|
158
|
+
if name == @current_block_name
|
159
|
+
lines_to_insert = @blocks[name]
|
160
|
+
|
161
|
+
if lines_to_insert.nil?
|
162
|
+
# There was no such block in the source
|
163
|
+
# We need to leave this block as is in the destination
|
164
|
+
@destination.concat(@current_block)
|
165
|
+
else
|
166
|
+
puts "Destination: matched block from source, replacing" if @verbose
|
167
|
+
lines_to_insert.each do |line|
|
168
|
+
result = ""
|
169
|
+
# This nasty bit of logic lets us scan through each line to insert and
|
170
|
+
# see if have any __ delimited tokens to possibly replace.
|
171
|
+
# Some languages, like ruby and python, do have variables with names that
|
172
|
+
# match this pattern. Our tokens here are all caps, so there's very little
|
173
|
+
# chance of a clash. Any __ surrounded thing we see but don't recognize
|
174
|
+
# should be left as is. Otherwise we replace.
|
175
|
+
while line.length > 0
|
176
|
+
match = /(__(\w+)__)/.match(line)
|
177
|
+
if match
|
178
|
+
replacement = @replacements[match[2]]
|
179
|
+
if replacement
|
180
|
+
result += match.pre_match + replacement
|
181
|
+
else
|
182
|
+
# There's probably a cleaner way to do this, but it works.
|
183
|
+
result += match.pre_match + match[1]
|
184
|
+
end
|
185
|
+
# Advance the part of the line we're looking at
|
186
|
+
line = match.post_match
|
187
|
+
else
|
188
|
+
# No match, consume all that's left
|
189
|
+
result += line
|
190
|
+
line = ""
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
@destination.push(result)
|
195
|
+
end
|
196
|
+
|
197
|
+
# Preserve the replaced block
|
198
|
+
@extractions.concat(@current_block)
|
199
|
+
end
|
200
|
+
|
201
|
+
else
|
202
|
+
# Pass the current block through unmodified
|
203
|
+
@destination.concat(@current_block)
|
204
|
+
|
205
|
+
warn "In Destination: open and close blocks do not match!!"
|
206
|
+
warn "Opened with #{@current_block_name}"
|
207
|
+
warn "Closed with #{name}"
|
208
|
+
end
|
209
|
+
|
210
|
+
# Either way, we've attempted to close a block, so clear the current block name
|
211
|
+
# to signify we are not currently in a block at all.
|
212
|
+
@current_block_name = ""
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: incodesert
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kirk Bowers
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rdoc
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: hoe
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.13'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.13'
|
41
|
+
description: "code insert\n code in sert\n in code sert\n incodesert\n\n+incodesert+
|
42
|
+
(pronounced \"in code sert\", not \"inco desert\") \nis a utility for inserting
|
43
|
+
code snippets from one file into another.\nIt was originally intended to facilitate
|
44
|
+
autogenerating code and interlacing that\nautogened code with hand written code.
|
45
|
+
\ It is also useful for performing\nPre-rolled Blackbox Testing."
|
46
|
+
email:
|
47
|
+
- kirkbowers@yahoo.com
|
48
|
+
executables:
|
49
|
+
- incodesert
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- ".gemtest"
|
54
|
+
- bin/incodesert
|
55
|
+
- lib/incodesert.rb
|
56
|
+
- lib/incodesert/documents.rb
|
57
|
+
homepage: https://github.com/kirkbowers/incodesert
|
58
|
+
licenses:
|
59
|
+
- MIT
|
60
|
+
metadata: {}
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options:
|
63
|
+
- "--main"
|
64
|
+
- README.txt
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.4.5
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: code insert code in sert in code sert incodesert +incodesert+ (pronounced
|
83
|
+
"in code sert", not "inco desert") is a utility for inserting code snippets from
|
84
|
+
one file into another
|
85
|
+
test_files: []
|