incodesert 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,5 @@
1
+ module Incodesert
2
+ VERSION = "0.1.0"
3
+ end
4
+
5
+ require 'incodesert/documents'
@@ -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: []