polytexnic 0.9.9 → 0.9.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 821fd49a7e64e757649554fe43230066a4adabde
4
- data.tar.gz: 691430267b60c4c687bb84f7ba6fb70e1a0ea9c6
3
+ metadata.gz: 7d1357e1fdbafae6d29c54a966649109ceaef92f
4
+ data.tar.gz: a15026b6f6b37bc2c37fe9d9fa81571b62e3bf59
5
5
  SHA512:
6
- metadata.gz: d44043bfb3c04a58e30e1a85e5cf1c0a0046f92018c27e22310b22e145c2c1bceb23077d2cedabcdc7ec8db0802d9d7bbdcbae43a946edc7a2854cf33bda791e
7
- data.tar.gz: d07ba71163b7acc22d019d4d7b798838512a092db4791e2d56a4a867ad07bb220b79e6454e20646f9e5c19b130bdcde3161d256ce1ab35e6ebbbc4f307334bb6
6
+ metadata.gz: a38508985733c163261cd035a86d60c793b22c4027d737dee582431859163765ace742a67649599aba8225739f672f3018104e7c06f0225d899aa2e592cc95f3
7
+ data.tar.gz: da175c2ed00d3b95b8ff7c849123ec76dc6fb4980e3fb445a96fff73f93b75e93918ba461998605d121217a807bf8abad75fb0b7aef2220c0b91a324c2007413
@@ -0,0 +1,247 @@
1
+ module CodeInclusion
2
+ class CodeInclusionException < Exception; end;
3
+
4
+ class Code
5
+ CODE_REGEX = /^\s*%=\s+<<\s*\( # opening
6
+ \s*([^\s]+?) # path to file
7
+ (?:\[(.+?)\])? # optional section name
8
+ (?:,\s*tag:\s*([\w\.\/\-]+?))? # optional git tag
9
+ (?:,\s*lang:\s*(\w+))? # optional lang
10
+ (,\s*options:\s*.*)? # optional options
11
+ \s*\) # closing paren
12
+ /x
13
+
14
+ DEFAULT_LANGUAGE = 'text'
15
+
16
+ # Returns an instance of CodeInclusion::Code or nil
17
+ def self.for(line)
18
+ if (line =~ CODE_REGEX)
19
+ opts = {}
20
+ opts[:tag] = $3
21
+ opts[:custom_language] = $4
22
+ opts[:highlight] = $5
23
+ new($1, $2, opts)
24
+ end
25
+ end
26
+
27
+ attr_reader :filename, :sectionname, :opts
28
+
29
+ def initialize(filename, sectionname, opts)
30
+ @filename = filename
31
+ @sectionname = sectionname
32
+ @opts = opts
33
+ end
34
+
35
+ # Returns the formatted code or an error message
36
+ def to_s
37
+ return unless filename
38
+
39
+ result = []
40
+ result << "%= lang:#{language}#{opts[:highlight]}"
41
+ result << '\begin{code}'
42
+ result.concat(raw_code)
43
+ result << '\end{code}'
44
+
45
+ rescue CodeInclusionException => e
46
+ code_error(e.message)
47
+ end
48
+
49
+ def raw_code
50
+ reader.read
51
+ end
52
+
53
+ private
54
+
55
+ # Manufacture a reader of the appriopriate type
56
+ def reader
57
+ @reader ||=
58
+ if opts[:tag]
59
+ GitTaggedFileReader.new(filename,
60
+ sectionname,
61
+ opts.delete(:tag),
62
+ opts)
63
+ elsif sectionname
64
+ SectionReader.new(filename, sectionname)
65
+ else
66
+ FileReader.new(filename)
67
+ end
68
+ end
69
+
70
+ def language
71
+ extension_array = File.extname(filename).scan(/\.(.*)/).first
72
+ lang_from_extension = extension_array.nil? ? nil : extension_array[0]
73
+ (opts[:custom_language] || lang_from_extension || DEFAULT_LANGUAGE)
74
+ end
75
+
76
+ def code_error(details)
77
+ ["\\verb+ERROR: #{details}+"]
78
+ end
79
+ end
80
+
81
+
82
+ # GitTaggedFileReader retrieves code from a
83
+ # tagged file in your local git repository.
84
+ #
85
+ # Example: <<(lib/polytexnic/literal.rb, tag: v0.9.4)
86
+ class GitTaggedFileReader
87
+
88
+ def self.git
89
+ Git.new
90
+ end
91
+
92
+ attr_reader :filename, :sectionname, :tagname, :opts, :git
93
+
94
+ def initialize(filename, sectionname, tagname, opts={}, git=self.class.git)
95
+ @filename = filename
96
+ @sectionname = sectionname
97
+ @tagname = tagname
98
+ @opts = opts
99
+ @git = git
100
+ end
101
+
102
+ def read
103
+ ensure_tag_exists!
104
+
105
+ Dir.mktmpdir {|tmpdir|
106
+ checkout_file!(tmpdir)
107
+ read_file(tmpdir)
108
+ }
109
+ end
110
+
111
+ private
112
+
113
+ def ensure_tag_exists!
114
+ unless git.tag_exists?(tagname)
115
+ raise(CodeInclusionException, "Tag '#{tagname}' does not exist.")
116
+ end
117
+ end
118
+
119
+ def checkout_file!(tmpdir)
120
+ output = git.checkout(tmpdir, filename, tagname)
121
+ unless git.checkout_succeeded?
122
+ raise(CodeInclusionException, improve_error_message(output, tmpdir))
123
+ end
124
+ end
125
+
126
+ def read_file(tmpdir)
127
+ tmpfilename = File.join(tmpdir, filename)
128
+ CodeInclusion::Code.new(tmpfilename, sectionname, opts).raw_code
129
+ end
130
+
131
+ def improve_error_message(msg, tmpdir)
132
+ msg.gsub(/#{tmpdir}/, '').chomp(".\n") + " in tag #{tagname}."
133
+ end
134
+
135
+
136
+ class Git
137
+ def checkout(tmpdir, filename, tagname)
138
+ `git --work-tree=#{tmpdir} checkout #{tagname} #{filename} 2>&1`
139
+ end
140
+
141
+ def checkout_succeeded?
142
+ $? == 0
143
+ end
144
+
145
+ def tags
146
+ `git tag`
147
+ end
148
+
149
+ def tag_exists?(tagname)
150
+ tags.split("\n").include?(tagname)
151
+ end
152
+ end
153
+ end
154
+
155
+
156
+ # FileReader retrieves code from a file on disk.
157
+ #
158
+ # Example: <<(lib/polytexnic/literal.rb)
159
+ class FileReader
160
+ attr_reader :filename
161
+
162
+ def initialize(filename)
163
+ @filename = filename
164
+ end
165
+
166
+ def read
167
+ ensure_file_exists!
168
+ File.read(filename).split("\n")
169
+ end
170
+
171
+ def ensure_file_exists!
172
+ unless File.exist?(filename)
173
+ raise(CodeInclusionException, "File '#{filename}' does not exist")
174
+ end
175
+ end
176
+ end
177
+
178
+
179
+ # SectionReader retrieves code from a marked section in a file on disk.
180
+ #
181
+ # Example: <<(lib/polytexnic/literal.rb[my_section])
182
+ #
183
+ # Sections are delineated by '#// begin section_name' and '#// end',
184
+ # for example:
185
+ #
186
+ # #// begin my_section
187
+ # some code
188
+ # #// end
189
+ class SectionReader < FileReader
190
+ attr_reader :lines, :sectionname
191
+
192
+ def initialize(filename, sectionname)
193
+ super(filename)
194
+ @sectionname = sectionname
195
+ end
196
+
197
+ def read
198
+ @lines = super
199
+ ensure_section_exists!
200
+ lines.slice(index_of_first_line, length)
201
+ end
202
+
203
+ private
204
+ def exist?
205
+ !!index_of_section_begin
206
+ end
207
+
208
+ def index_of_section_begin
209
+ @section_begin_i ||=
210
+ lines.index {|line| clean(line) == section_begin_text }
211
+ end
212
+
213
+ def index_of_first_line
214
+ @first_line_i ||= index_of_section_begin + 1
215
+ end
216
+
217
+ def length
218
+ lines.slice(index_of_first_line, lines.size).index { |line|
219
+ clean(line) == (section_end_text)
220
+ }
221
+ end
222
+
223
+ def marker
224
+ '#//'
225
+ end
226
+
227
+ def section_begin_text
228
+ "#{marker} begin #{sectionname}"
229
+ end
230
+
231
+ def section_end_text
232
+ "#{marker} end"
233
+ end
234
+
235
+ def clean(str)
236
+ str.strip.squeeze(" ")
237
+ end
238
+
239
+ def ensure_section_exists!
240
+ unless exist?
241
+ section_err = "Could not find section header '#{section_begin_text}'"
242
+ file_err = " in file '#{filename}'"
243
+ raise(CodeInclusionException, section_err + file_err)
244
+ end
245
+ end
246
+ end
247
+ end
@@ -6,15 +6,6 @@ module Polytexnic
6
6
  # %= lang: <language>[, options: ...]
7
7
  LANG_REGEX = /^\s*%=\s+lang:\s*(\w+)(?:,\s*options:(.*))?/
8
8
 
9
- # Matches the line for code inclusion.
10
- # %= <</path/to/code.ext
11
- CODE_INCLUSION_REGEX = /^\s*%=\s+<<\s*\( # opening
12
- \s*([^\s]+?) # path to file
13
- (?:\[(.+?)\])? # optional section name
14
- (?:,\s*lang:\s*(\w+))? # optional lang
15
- (,\s*options:\s*.*)? # optional options
16
- \s*\) # closing paren
17
- /x
18
9
 
19
10
  # Makes the caches for literal environments.
20
11
  def cache_literal(polytex, format = :html)
@@ -74,7 +65,7 @@ module Polytexnic
74
65
  elsif line =~ /\s*\\end\{codelisting\}/ && !in_verbatim
75
66
  in_codelisting = false
76
67
  output << line
77
- elsif line =~ CODE_INCLUSION_REGEX && !in_verbatim
68
+ elsif (included_code = CodeInclusion::Code.for(line)) && !in_verbatim
78
69
  # Reduce to a previously solved problem.
79
70
  # We transform
80
71
  # %= <<(/path/to/file.rb)
@@ -84,10 +75,7 @@ module Polytexnic
84
75
  # <content of file or section.rb>
85
76
  # \end{code}
86
77
  # and then prepend the code to the current `lines` array.
87
- filename, sectionname, custom_language, highlight_options = $1, $2, $3, $4
88
- if filename
89
- lines.unshift(*include_code(filename, sectionname, custom_language, highlight_options))
90
- end
78
+ lines.unshift(*included_code.to_s)
91
79
  elsif line.begin_literal?
92
80
  in_verbatim = true
93
81
  literal_type = line.literal_type
@@ -160,34 +148,6 @@ module Polytexnic
160
148
  end
161
149
  end
162
150
 
163
- # Returns the marked up file or section to be included,
164
- # or an error message if file or section does not exist.
165
- def include_code(filename, sectionname, custom_language, highlight_options)
166
- reader = (sectionname ? IncludedSectionReader : IncludedFileReader).new
167
- lang = "#{code_language(filename, custom_language)}#{highlight_options}"
168
- code = ["%= lang:#{lang}"]
169
- code << '\begin{code}'
170
- code.concat(reader.read(filename, sectionname))
171
- code << '\end{code}'
172
-
173
- rescue FileNotFound => e
174
- code_error("File '#{e.message}' does not exist")
175
- rescue SectionNotFound => e
176
- msg = e.message
177
- err = "Could not find section header '#{msg}' in file '#{filename}'"
178
- code_error(err)
179
- end
180
-
181
- def code_error(details)
182
- "\\verb+ERROR: #{details}+"
183
- end
184
-
185
- def code_language(filename, custom_language)
186
- extension_array = File.extname(filename).scan(/\.(.*)/).first
187
- lang_from_extension = extension_array.nil? ? nil : extension_array[0]
188
- language = custom_language || lang_from_extension || 'text'
189
- end
190
-
191
151
  # Returns a permanent salt for the syntax highlighting cache.
192
152
  def code_salt
193
153
  'fbbc13ed4a51e27608037365e1d27a5f992b6339'
@@ -288,61 +248,9 @@ module Polytexnic
288
248
  literal_type
289
249
  end
290
250
  end
291
-
292
-
293
- class FileNotFound < Exception; end;
294
- class IncludedFileReader
295
- def read(filename, _)
296
- raise(FileNotFound, filename) unless File.exist?(filename)
297
- File.read(filename).split("\n")
298
- end
299
- end
300
-
301
- class SectionNotFound < Exception; end;
302
- class IncludedSectionReader < IncludedFileReader
303
- attr_reader :lines, :sectionname
304
-
305
- def read(filename, sectionname)
306
- @lines = super
307
- @sectionname = sectionname
308
-
309
- raise(SectionNotFound, section_begin_text) unless exist?
310
- lines.slice(index_of_first_line, length)
311
- end
312
-
313
- private
314
- def exist?
315
- !!index_of_section_begin
316
- end
317
-
318
- def index_of_section_begin
319
- @section_begin_i ||= lines.index(section_begin_text)
320
- end
321
-
322
- def index_of_first_line
323
- @first_line_i ||= index_of_section_begin + 1
324
- end
325
-
326
- def length
327
- lines.slice(index_of_first_line, lines.size).index(section_end_text)
328
- end
329
-
330
- def marker
331
- '#//'
332
- end
333
-
334
- def section_begin_text
335
- "#{marker} begin #{sectionname}"
336
- end
337
-
338
- def section_end_text
339
- "#{marker} end"
340
- end
341
- end
342
251
  end
343
252
  end
344
253
 
345
-
346
254
  class String
347
255
  include Polytexnic::Literal
348
256
 
@@ -391,3 +299,4 @@ class String
391
299
  end.join('|')
392
300
  end
393
301
  end
302
+
@@ -27,6 +27,7 @@ module Polytexnic
27
27
  headings(doc)
28
28
  sout(doc)
29
29
  kode(doc)
30
+ coloredtext(doc)
30
31
  filepath(doc)
31
32
  backslash_break(doc)
32
33
  spaces(doc)
@@ -601,6 +602,14 @@ module Polytexnic
601
602
  end
602
603
  end
603
604
 
605
+ def coloredtext(doc)
606
+ doc.xpath('//coloredtext').each do |node|
607
+ node.name = 'span'
608
+ node['style'] = "color: #{node['color']}"
609
+ clean_node node, 'color'
610
+ end
611
+ end
612
+
604
613
  # Converts filesystem path (\filepath) to the proper tag.
605
614
  def filepath(doc)
606
615
  doc.xpath('//filepath').each do |node|
@@ -1,5 +1,6 @@
1
1
  # encoding=utf-8
2
2
  require 'polytexnic/literal'
3
+ require 'polytexnic/code_inclusion'
3
4
  require 'polytexnic/preprocessors/html'
4
5
  require 'polytexnic/preprocessors/latex'
5
6
  require 'polytexnic/preprocessors/polytex'
@@ -112,6 +112,7 @@ module Polytexnic
112
112
  \newcommand{\codecaption}[1]{\xmlelt{heading}{#1}}
113
113
  \newcommand{\sout}[1]{\xmlelt{sout}{#1}}
114
114
  \newcommand{\kode}[1]{\xmlelt{kode}{#1}}
115
+ \newcommand{\coloredtext}[2]{\xmlelt{coloredtext}{\AddAttToCurrent{color}{#1}#2}}
115
116
  \newcommand{\filepath}[1]{\xmlelt{filepath}{#1}}
116
117
  \newcommand{\image}[1]{\xmlelt{image}{#1}}
117
118
  \newcommand{\imagebox}[1]{\xmlelt{imagebox}{#1}}
@@ -1,3 +1,3 @@
1
1
  module Polytexnic
2
- VERSION = "0.9.9"
2
+ VERSION = "0.9.10"
3
3
  end
@@ -238,6 +238,140 @@ describe Polytexnic::Pipeline do
238
238
  end
239
239
  it { should include "ERROR: File 'foobar.rb' does not exist" }
240
240
  end
241
+
242
+
243
+
244
+ context "from a git tag" do
245
+ shared_examples "an inclusion" do
246
+ it "resembles the given output" do
247
+ allow(CodeInclusion::GitTaggedFileReader).to receive(:git).
248
+ and_return(FakeGit.new)
249
+ expect(processed_text).to resemble(output)
250
+ end
251
+ end
252
+
253
+ context "the tag and file exist" do
254
+ before(:all) do
255
+ class FakeGit < CodeInclusion::GitTaggedFileReader::Git
256
+ # Write fake data to location on disk where git _would_ have placed the file
257
+ def checkout(tmpdir, filename, tagname)
258
+ f = File.join(tmpdir, filename)
259
+ File.open(f, 'w') { |file| file.write("Fake data") }
260
+ end
261
+ def tag_exists?(tagname)
262
+ true
263
+ end
264
+ def checkout_succeeded?
265
+ true
266
+ end
267
+ end
268
+ end
269
+
270
+ context "with tag only" do
271
+ let(:polytex) do <<-'EOS'
272
+ %= <<(tagged_file.rb, tag: fake_tag.1.0)
273
+ EOS
274
+ end
275
+ let(:output) do <<-'EOS'
276
+ <div class="code">
277
+ <div class="highlight">
278
+ <pre>
279
+ <span class="no">Fake</span> <span class="n">data</span>
280
+ </pre>
281
+ </div>
282
+ EOS
283
+ end
284
+ it_behaves_like "an inclusion"
285
+ end
286
+
287
+ context "with other params" do
288
+ let(:output) do <<-'EOS'
289
+ <div class="code">
290
+ <div class="highlight">
291
+ <pre>Fake data</pre>
292
+ </div>
293
+ </div>
294
+ EOS
295
+ end
296
+
297
+ context "with tag and lang" do
298
+ let(:polytex) do <<-'EOS'
299
+ %= <<(tagged_file.rb, tag: slashes/and-dashes-are/ok/too, lang: tex)
300
+ EOS
301
+ end
302
+ it_behaves_like "an inclusion"
303
+ end
304
+
305
+ context "with tag, lang and options" do
306
+ let(:polytex) do <<-'EOS'
307
+ %= <<(tagged_file.rb, tag: v0.9.4, lang: tex, options: "hl_lines": [5])
308
+ EOS
309
+ end
310
+ it_behaves_like "an inclusion"
311
+ end
312
+ end
313
+ end
314
+
315
+ context "the tag does not exist" do
316
+ before(:all) do
317
+ class FakeGit < CodeInclusion::GitTaggedFileReader::Git
318
+ def checkout(tmpdir, filename, tagname)
319
+ end
320
+ def tag_exists?(tagname)
321
+ false
322
+ end
323
+ def checkout_succeeded?
324
+ false
325
+ end
326
+ end
327
+ end
328
+
329
+ let(:polytex) do <<-'EOS'
330
+ %= <<(tagged_file.rb, tag: non_existent_tag)
331
+ EOS
332
+ end
333
+ let(:output) do <<-'EOS'
334
+ <p>
335
+ <span class="inline_verbatim">
336
+ ERROR: Tag 'non_existent_tag' does not exist.
337
+ </span>
338
+ </p>
339
+ EOS
340
+ end
341
+ it_behaves_like "an inclusion"
342
+ end
343
+
344
+ context "the file does not exist" do
345
+ before(:all) do
346
+ class FakeGit < CodeInclusion::GitTaggedFileReader::Git
347
+ def checkout(tmpdir, filename, tagname)
348
+ "pathspec #{tmpdir}/non_existent_file did not match any file(s) known to git.\n"
349
+ end
350
+ def tag_exists?(tagname)
351
+ true
352
+ end
353
+ def checkout_succeeded?
354
+ false
355
+ end
356
+ end
357
+ end
358
+
359
+ let(:polytex) do <<-'EOS'
360
+ %= <<(non_existent_file, tag: v0.9.4)
361
+ EOS
362
+ end
363
+ let(:output) do <<-'EOS'
364
+ <p>
365
+ <span class="inline_verbatim">
366
+ ERROR: pathspec /non_existent_file did not match any file(s) known to git in tag v0.9.4.
367
+ </span>
368
+ </p>
369
+ EOS
370
+ end
371
+ it_behaves_like "an inclusion"
372
+ end
373
+
374
+ end
241
375
  end
242
376
  end
243
377
 
@@ -254,4 +388,4 @@ end
254
388
  #// begin section_z
255
389
  "This is section_z; it's used by a test."
256
390
  "Section Z is your friend."
257
- #// end
391
+ #// end
@@ -46,5 +46,10 @@ describe 'Polytexnic::Pipeline#to_html' do
46
46
  let(:polytex) { '\kode{function\_name}' }
47
47
  it { should resemble '<code>function_name</code>' }
48
48
  end
49
+
50
+ describe "color command" do
51
+ let(:polytex) { '\coloredtext{red}{text}' }
52
+ it { should resemble '<span style="color: red">text</span>' }
53
+ end
49
54
  end
50
55
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polytexnic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.9
4
+ version: 0.9.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Hartl
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-05-07 00:00:00.000000000 Z
12
+ date: 2014-06-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -212,6 +212,7 @@ files:
212
212
  - README.md
213
213
  - Rakefile
214
214
  - lib/polytexnic.rb
215
+ - lib/polytexnic/code_inclusion.rb
215
216
  - lib/polytexnic/literal.rb
216
217
  - lib/polytexnic/postprocessor.rb
217
218
  - lib/polytexnic/postprocessors/html.rb