nora_mark 0.2beta6 → 0.2beta7

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,43 @@
1
+ module NoraMark
2
+ class Transformer
3
+ def initialize(rules, options)
4
+ @rules = rules
5
+ @options = options
6
+ end
7
+ def transform(node)
8
+ node.all_nodes.each do
9
+ |node|
10
+ if match_rule = @rules.find { |rule| node.match?(rule[0]) }
11
+ selector, action, p = match_rule
12
+ NodeBuilder.new(node, @options).send(action, &p)
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ class TransformerFactory
19
+ attr_accessor :rules, :options
20
+
21
+ def self.create(text: nil, &block)
22
+ instance = new
23
+ instance.instance_eval do
24
+ @rules = []
25
+ @options = {}
26
+ if text
27
+ instance_eval text
28
+ else
29
+ instance_eval &block
30
+ end
31
+ Transformer.new(@rules, @options)
32
+ end
33
+ end
34
+
35
+ def transform_options options
36
+ (@options ||= {}).merge options
37
+ end
38
+
39
+ def for_node(selector, action = :replace, &block)
40
+ @rules << [ selector, action, block ]
41
+ end
42
+ end
43
+ end
@@ -1,3 +1,3 @@
1
1
  module NoraMark
2
- VERSION = "0.2beta6"
2
+ VERSION = "0.2beta7"
3
3
  end
data/lib/nora_mark.rb CHANGED
@@ -1,12 +1,16 @@
1
1
  require "nora_mark/version"
2
2
  require 'nora_mark/html/generator'
3
- require 'nora_mark/node'
4
3
  require 'nora_mark/parser'
4
+ require 'nora_mark/node_util'
5
+ require 'nora_mark/node'
6
+ require 'nora_mark/node_set'
7
+ require 'nora_mark/transformer'
8
+ require 'nora_mark/node_builder'
5
9
  require 'securerandom'
6
10
 
7
11
  module NoraMark
8
- class Document < Node
9
- attr_accessor :document_name
12
+ class Document
13
+ attr_accessor :document_name, :root
10
14
  private_class_method :new
11
15
 
12
16
  def self.parse(string_or_io, param = {})
@@ -18,11 +22,20 @@ module NoraMark
18
22
  |pr|
19
23
  src = pr.call(src)
20
24
  end
21
- @parser = Parser.new(src)
22
- if (!@parser.parse)
23
- raise @parser.raise_error
25
+ parser = Parser.new(src)
26
+ if (!parser.parse)
27
+ raise parser.raise_error
28
+ end
29
+ @root = parser.result
30
+ @root.document_name ||= @document_name
31
+ @root.reparent
32
+ @root.first_child.inject(1) do |page_no, node|
33
+ if node.kind_of? Page
34
+ node.page_no = page_no
35
+ page_no = page_no + 1
36
+ end
37
+ page_no
24
38
  end
25
- @content = @parser.result
26
39
  end
27
40
  instance
28
41
  end
@@ -33,7 +46,8 @@ module NoraMark
33
46
 
34
47
  def html
35
48
  if @html.nil?
36
- @html = @html_generator.convert(self, @render_parameter)
49
+ @transformers[:html].each { |t| t.transform @root }
50
+ @html = Html::Generator.new(@param).convert(@root.clone, @render_parameter)
37
51
  end
38
52
  @html
39
53
  end
@@ -42,16 +56,19 @@ module NoraMark
42
56
  @render_parameter.merge! param
43
57
  self
44
58
  end
59
+
60
+ def add_transformer(generator: :html, text: nil, &block)
61
+ (@transformers[generator] ||= []) << TransformerFactory.create(text: text, &block)
62
+ end
45
63
 
46
64
  def initialize(param = {})
65
+ @param = param
47
66
  @preprocessors = [
48
67
  Proc.new { |text| text.gsub(/\r?\n(\r?\n)+/, "\n\n") },
49
68
  ]
50
- @html_generator = Html::Generator.new(param)
51
69
  @document_name = param[:document_name] || "noramark_#{SecureRandom.uuid}"
52
70
  @render_parameter = {}
71
+ @transformers = { html: []}
53
72
  end
54
-
55
-
56
73
  end
57
74
  end
@@ -1,9 +1,9 @@
1
1
  module Nokogiri
2
2
  module XML
3
3
  class Element
4
- def selector
4
+ def selector remove_id: true
5
5
  sel = name
6
- if !self['id'].nil?
6
+ if !remove_id && !self['id'].nil?
7
7
  sel = sel + '#' + self['id'].split(' ').join('#')
8
8
  end
9
9
  if !self['class'].nil?
@@ -15,8 +15,8 @@ module Nokogiri
15
15
  }
16
16
  sel
17
17
  end
18
- def selector_and_text
19
- [selector, text]
18
+ def selector_and_text remove_id: true
19
+ [selector(remove_id: remove_id), text]
20
20
  end
21
21
  alias a selector_and_text
22
22
  def child_loop
@@ -25,17 +25,17 @@ module Nokogiri
25
25
  def child_a(index)
26
26
  element_children[index].selector_and_text
27
27
  end
28
- def selector_and_children
29
- [selector] + children.select{|c| c.elem? || c.text.strip.size > 0}.map{|c|
28
+ def selector_and_children remove_id: true
29
+ [selector(remove_id: remove_id)] + children.select{|c| c.elem? || c.text.strip.size > 0}.map{|c|
30
30
  if !c.elem?
31
31
  c.text
32
32
  elsif c.element_children.size == 0
33
- c.selector_and_text
33
+ c.selector_and_text remove_id: remove_id
34
34
  else
35
- c.selector_and_children
35
+ c.selector_and_children remove_id: remove_id
36
36
  end
37
37
  }
38
38
  end
39
39
  end
40
40
  end
41
- end
41
+ end
@@ -77,7 +77,7 @@ describe NoraMark do
77
77
 
78
78
 
79
79
  it 'should convert simple paragraph in english mode specified in frontmatter' do
80
- text = "---\nlang: en\ntitle: the title\n---\nparagraph begins.\n2nd line.\n 3rd line.\n\n\n next paragraph."
80
+ text = "---\nlang: en\ntitle: the title\n---\n\n\n\nparagraph begins.\n2nd line.\n 3rd line.\n\n\n next paragraph."
81
81
  noramark = NoraMark::Document.parse(text)
82
82
  converted = noramark.html
83
83
  body = Nokogiri::XML::Document.parse(converted[0]).root.at_xpath('xmlns:body')
@@ -195,8 +195,26 @@ describe NoraMark do
195
195
  )
196
196
  end
197
197
 
198
+ it 'should handle divs with empty lines' do
199
+ text = "\n\n\nd('wo-pgroup') {\n\n\n1st line. \n\n\n}\n\n\nd {\n 2nd div.\n}"
200
+ noramark = NoraMark::Document.parse(text, lang: 'ja', title: 'the title')
201
+ converted = noramark.html
202
+ body = Nokogiri::XML::Document.parse(converted[0]).root.at_xpath('xmlns:body')
203
+ expect(body.element_children[0].selector_and_children).to eq(
204
+ ['div',
205
+ ['p', '1st line.']
206
+ ])
207
+ expect(body.element_children[1].selector_and_children).to eq(
208
+ ['div',
209
+ ['div.pgroup',
210
+ ['p', '2nd div.']]
211
+ ]
212
+
213
+ )
214
+ end
215
+
198
216
  it 'should nest div without pgroup and with pgroup' do
199
- text = "d(wo-pgroup) {\nd {\nnested.\n} \n}\nd {\nin pgroup\n}"
217
+ text = "d(wo-pgroup) {\nd {\nnested.\n} \n}\n\nd {\nin pgroup\n}"
200
218
  noramark = NoraMark::Document.parse(text, lang: 'ja', title: 'the title')
201
219
  converted = noramark.html
202
220
  body = Nokogiri::XML::Document.parse(converted[0]).root.at_xpath('xmlns:body')
@@ -232,9 +250,9 @@ describe NoraMark do
232
250
  noramark = NoraMark::Document.parse(text, lang: 'ja', title: 'the title')
233
251
  converted = noramark.html
234
252
  body = Nokogiri::XML::Document.parse(converted[0]).root.at_xpath('xmlns:body')
235
- expect(body.element_children[0].selector_and_children).to eq(
253
+ expect(body.element_children[0].selector_and_children(remove_id: false)).to eq(
236
254
  ['div#thecontents.preface-one',
237
- ['h1', 'title.']
255
+ ['h1#heading_index_1', 'title.']
238
256
  ]
239
257
  )
240
258
  end
@@ -370,7 +388,7 @@ describe NoraMark do
370
388
  end
371
389
 
372
390
  it 'should handle block image with before caption' do
373
- text = "this is normal line.\nimage(./image1.jpg, alt text, caption_before: true): caption text"
391
+ text = "this is normal line.\nimage(./image1.jpg, alt text)[caption_before: true]: caption text"
374
392
  noramark = NoraMark::Document.parse(text, lang: 'ja', title: 'the title')
375
393
  converted = noramark.html
376
394
  body = Nokogiri::XML::Document.parse(converted[0]).root.at_xpath('xmlns:body')
@@ -891,16 +909,18 @@ EOF
891
909
  )
892
910
  end
893
911
  it 'should convert markdown style heading with empty body' do
894
- text = "=: タイトルです。\n==: 次のタイトルです。これから書きます。"
912
+ text = "=: タイトルです。\n*:中身です。\n\n==: 次のタイトルです。これから書きます。\n\n==:ここもこれから。"
895
913
  noramark = NoraMark::Document.parse(text, lang: 'ja', title: 'the title')
896
914
  converted = noramark.html
897
915
  body = Nokogiri::XML::Document.parse(converted[0]).root.at_xpath('xmlns:body')
898
- expect(body.element_children.size).to eq 1
899
916
  expect(body.element_children[0].selector_and_children).to eq(
900
917
  ['section',
901
918
  ['h1', 'タイトルです。'],
919
+ ['ul', ['li', '中身です。']],
902
920
  ['section',
903
- ['h2', '次のタイトルです。これから書きます。']]])
921
+ ['h2', '次のタイトルです。これから書きます。']],
922
+ ['section',
923
+ ['h2', 'ここもこれから。']]])
904
924
  end
905
925
  it 'should markdown style heading interrupted by other headed section' do
906
926
  text = "=: タイトルです。\r\nこれは、セクションの中です。\n =: また次のセクションです。\n次のセクションの中です。"
@@ -1025,13 +1045,220 @@ EOF
1025
1045
  to_validate << nokogiri_doc.to_s
1026
1046
  end
1027
1047
  end
1028
-
1029
1048
  @stdout = capture(:stdout) do
1030
1049
  puts %x(java -jar #{jar} -c #{schema} #{file_to_validate})
1031
1050
  end
1032
1051
  expect(@stdout.strip).to eq ""
1033
1052
  end
1034
1053
  end
1054
+ describe 'node manipulation' do
1055
+ before do
1056
+ @text = <<EOF
1057
+ 1st line.
1058
+ d.the_class {
1059
+ 3rd line.
1060
+ }
1061
+ 5th line.
1062
+ EOF
1063
+ end
1064
+ it 'should access line number' do
1065
+ noramark = NoraMark::Document.parse(@text)
1066
+ page = noramark.root.children[0]
1067
+ expect(page.children.size).to eq 3
1068
+ expect(page.line_no).to eq 1
1069
+ expect(page.children[0].line_no).to eq 1
1070
+ expect(page.children[1].line_no).to eq 2
1071
+ expect(page.children[2].children[0].line_no).to eq 5
1072
+ end
1073
+
1074
+ it 'replace existing node' do
1075
+ noramark = NoraMark::Document.parse(@text)
1076
+ page = noramark.root.children[0]
1077
+ first_pgroup = page.children[0]
1078
+ line_no = first_pgroup.line_no
1079
+ new_node = NoraMark::ParagraphGroup.new(['the_id'], ['the_class'], [], {}, [ NoraMark::Text.new("replaced.", line_no)], line_no)
1080
+ first_pgroup.replace(new_node)
1081
+ body = Nokogiri::XML::Document.parse(noramark.html[0]).root.at_xpath('xmlns:body')
1082
+ expect(body.element_children[0].selector_and_children(remove_id: false)).to eq(
1083
+ ['p#the_id.the_class', 'replaced.'])
1084
+ end
1085
+
1086
+ it 'modify existing node by DSL' do
1087
+ text = "1st line.\nfoobar(title)[level: 3] {\n in the section.\n}\n=: section 2."
1088
+ noramark = NoraMark::Document.parse(text, lang: 'ja')
1089
+
1090
+ noramark.add_transformer(generator: :html) do
1091
+ for_node 'foobar', :modify do
1092
+ @node.name = 'section'
1093
+ @node.prepend_child block("h#{@node.named_parameters[:level]}", @node.parameters[0])
1094
+ end
1095
+ end
1096
+ body = Nokogiri::XML::Document.parse(noramark.html[0]).root.at_xpath('xmlns:body')
1097
+ expect(body.element_children[0].selector_and_children()).to eq(
1098
+ ['div.pgroup', [ 'p', '1st line.' ]])
1099
+ expect(body.element_children[1].selector_and_children()).to eq(
1100
+ ['section', [ 'h3', 'title' ], ['div.pgroup', ['p', 'in the section.']]])
1101
+ expect(body.element_children[2].selector_and_children()).to eq(
1102
+ ['section', [ 'h1','section 2.' ]])
1103
+ end
1104
+ it 'replace existing node by DSL' do
1105
+ text = "1st line.\nfoobar(title)[level: 3] {\n in the section.\n}\n=: section 2."
1106
+ noramark = NoraMark::Document.parse(text, lang: 'ja')
1107
+
1108
+ noramark.add_transformer(generator: :html) do
1109
+ for_node 'foobar', :replace do
1110
+ block('section',
1111
+ [
1112
+ block( "h#{@node.named_parameters[:level]}", @node.parameters[0]),
1113
+ ] + @node.children)
1114
+ end
1115
+ end
1116
+ body = Nokogiri::XML::Document.parse(noramark.html[0]).root.at_xpath('xmlns:body')
1117
+ expect(body.element_children[0].selector_and_children()).to eq(
1118
+ ['div.pgroup', [ 'p', '1st line.' ]])
1119
+ expect(body.element_children[1].selector_and_children()).to eq(
1120
+ ['section', [ 'h3', 'title' ], ['div.pgroup', ['p', 'in the section.']]])
1121
+ expect(body.element_children[2].selector_and_children()).to eq(
1122
+ ['section', [ 'h1','section 2.' ]])
1123
+ end
1124
+ it 'generate complex-style headed section' do
1125
+ text = <<EOF
1126
+ ---
1127
+ lang: ja
1128
+ ---
1129
+
1130
+ =: 見出し
1131
+ sub: 副見出し
1132
+
1133
+ パラグラフ。
1134
+ パラグラフ。
1135
+
1136
+ EOF
1137
+ noramark = NoraMark::Document.parse(text)
1138
+ noramark.add_transformer(generator: :html) do
1139
+ for_node({:type => :HeadedSection}, :replace) do
1140
+ header = block('header',
1141
+ block('div',
1142
+ block("h#{@node.level}", @node.heading),
1143
+ classes: ['hgroup']))
1144
+ if (fc = @node.first_child).name == 'sub'
1145
+ fc.name = 'p'
1146
+ fc.classes = ['subh']
1147
+ header.first_child.append_child fc
1148
+ end
1149
+ body = block('div', @node.children, classes:['section-body'])
1150
+ block('section', [ header, body ], template: @node)
1151
+ end
1152
+ end
1153
+ body = Nokogiri::XML::Document.parse(noramark.html[0]).root.at_xpath('xmlns:body')
1154
+ expect(body.element_children[0].selector_and_children()).to eq(
1155
+ ['section', [ 'header', ['div.hgroup', ['h1', '見出し'], ['p.subh', '副見出し']]],
1156
+ ['div.section-body',
1157
+ ['div.pgroup', ['p', 'パラグラフ。'], ['p', 'パラグラフ。']]]])
1158
+ end
1159
+ it 'converts my markup' do
1160
+ text = "speak(Alice): Alice is speaking.\nspeak(Bob): and this is Bob."
1161
+ noramark = NoraMark::Document.parse(text)
1162
+ noramark.add_transformer(generator: :html) do
1163
+ for_node("speak", :modify) do
1164
+ @node.name = 'p'
1165
+ @node.prepend_child inline('span', @node.parameters[0], classes: ['speaker'])
1166
+ end
1167
+ end
1168
+ body = Nokogiri::XML::Document.parse(noramark.html[0]).root.at_xpath('xmlns:body')
1169
+ expect(body.element_children[0].selector_and_children()).to eq(
1170
+ ['p', ['span.speaker', 'Alice'], 'Alice is speaking.'])
1171
+ expect(body.element_children[1].selector_and_children()).to eq(
1172
+ ['p', ['span.speaker', 'Bob'], 'and this is Bob.'])
1173
+
1174
+ end
1175
+
1176
+
1177
+ it 'should reparent tree' do
1178
+ text = <<EOF
1179
+ 1st line.
1180
+ d {
1181
+ in the div
1182
+ }
1183
+ *: ul item
1184
+ text [s{with inline}] within
1185
+ EOF
1186
+ root = NoraMark::Document.parse(text).root
1187
+
1188
+ expect(root.parent).to be nil
1189
+ expect(root.prev).to be nil
1190
+ expect(root.next).to be nil
1191
+ expect(root.content).to be nil
1192
+
1193
+ expect(root.children.size).to eq 1
1194
+ page = root.first_child
1195
+ expect(root.children[0]).to eq page
1196
+ expect(root.last_child).to eq page
1197
+ expect(page.parent).to eq root
1198
+ expect(page.prev).to be nil
1199
+ expect(page.next).to be nil
1200
+
1201
+ first_pgroup = page.first_child
1202
+ expect(first_pgroup.class).to be NoraMark::ParagraphGroup
1203
+ expect(first_pgroup.parent).to eq page
1204
+ expect(first_pgroup.children.size).to eq 1
1205
+ expect(first_pgroup.prev).to be nil
1206
+
1207
+ paragraph = first_pgroup.first_child
1208
+ expect(paragraph.class).to be NoraMark::Paragraph
1209
+ expect(paragraph.parent).to be first_pgroup
1210
+ expect(paragraph.prev).to be nil
1211
+ expect(paragraph.next).to be nil
1212
+ expect(paragraph.children.size).to eq 1
1213
+ text = paragraph.first_child
1214
+ expect(text.class).to be NoraMark::Text
1215
+ expect(text.content).to eq '1st line.'
1216
+ expect(text.children.size).to eq 0
1217
+ expect(text.prev).to be nil
1218
+ expect(text.next).to be nil
1219
+ expect(text.parent).to eq paragraph
1220
+
1221
+ second_div = first_pgroup.next
1222
+ expect(second_div.class).to be NoraMark::Block
1223
+ end
1224
+ end
1225
+ describe 'table of contents' do
1226
+ before do
1227
+ @text = <<EOF
1228
+ ---
1229
+ lang: ja
1230
+ title: title
1231
+ ---
1232
+ =: [strong{chapter 1}]
1233
+ text
1234
+ ==: section 1-1
1235
+ text
1236
+ newpage:
1237
+ section {
1238
+ h6: [strong{some column}]
1239
+ text
1240
+ }
1241
+ EOF
1242
+ @noramark = NoraMark::Document.parse(@text, document_name: 'nora')
1243
+ end
1244
+ it 'should assign ids to headers' do
1245
+ body = Nokogiri::XML::Document.parse(@noramark.html[0]).root.at_xpath('xmlns:body')
1246
+ h1 = body.at_xpath('//xmlns:h1')
1247
+ expect(h1.selector remove_id: false).to eq "h1#heading_index_1"
1248
+ h2 = body.at_xpath('//xmlns:h2')
1249
+ expect(h2.selector remove_id: false).to eq "h2#heading_index_2"
1250
+ body = Nokogiri::XML::Document.parse(@noramark.html[1]).root.at_xpath('xmlns:body')
1251
+ h6 = body.at_xpath('//xmlns:h6')
1252
+ expect(h6.selector remove_id: false).to eq "h6#heading_index_3"
1253
+ end
1254
+ it 'should generate tocs' do
1255
+ toc = @noramark.html.toc
1256
+ expect(toc.size).to eq 3
1257
+ expect(toc[0]).to eq({link: "nora_00001.xhtml#heading_index_1", level: 1, text: "chapter 1"})
1258
+ expect(toc[1]).to eq({link: "nora_00001.xhtml#heading_index_2", level: 2, text: "section 1-1"})
1259
+ expect(toc[2]).to eq({link: "nora_00002.xhtml#heading_index_3", level: 6, text: "some column"})
1260
+ end
1261
+ end
1035
1262
  end
1036
1263
  end
1037
1264
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nora_mark
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2beta6
4
+ version: 0.2beta7
5
5
  platform: ruby
6
6
  authors:
7
7
  - KOJIMA Satoshi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-06 00:00:00.000000000 Z
11
+ date: 2014-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kpeg
@@ -82,6 +82,7 @@ files:
82
82
  - README.md
83
83
  - Rakefile
84
84
  - bin/nora2html
85
+ - example/nora-simple-ja.css
85
86
  - example/nora-simple.css
86
87
  - example/noramark-reference-ja.nora
87
88
  - example/noramark-reference-ja_00001.xhtml
@@ -96,9 +97,13 @@ files:
96
97
  - lib/nora_mark/html/util.rb
97
98
  - lib/nora_mark/html/writer_selector.rb
98
99
  - lib/nora_mark/node.rb
100
+ - lib/nora_mark/node_builder.rb
101
+ - lib/nora_mark/node_set.rb
102
+ - lib/nora_mark/node_util.rb
99
103
  - lib/nora_mark/parser.kpeg
100
104
  - lib/nora_mark/parser.kpeg.rb
101
105
  - lib/nora_mark/parser.rb
106
+ - lib/nora_mark/transformer.rb
102
107
  - lib/nora_mark/version.rb
103
108
  - nora_mark.gemspec
104
109
  - spec/created_files/.gitignore