nora_mark 0.2beta6 → 0.2beta7

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