qipowl 0.9.3 → 0.9.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +157 -76
- data/bin/bowler +4 -2
- data/config/bowlers/html.yaml +24 -9
- data/extras/drafts/parsing.md +53 -42
- data/features/bowler.feature +14 -0
- data/features/html.feature +76 -27
- data/features/step_definitions/bowler_steps.rb +20 -0
- data/lib/qipowl.rb +29 -19
- data/lib/qipowl/bowlers/html.rb +148 -96
- data/lib/qipowl/bowlers/i_sp_ru.rb +9 -9
- data/lib/qipowl/constants.rb +2 -1
- data/lib/qipowl/core/bowler.rb +31 -22
- data/lib/qipowl/core/mapper.rb +20 -22
- data/lib/qipowl/core/monkeypatches.rb +18 -25
- data/lib/qipowl/core/ruler.rb +48 -43
- data/lib/qipowl/version.rb +1 -1
- data/qipowl.gemspec +0 -2
- data/spec/html_additional_test.yaml +3 -0
- metadata +2 -35
data/features/bowler.feature
CHANGED
@@ -6,3 +6,17 @@ Feature: Mapper-Ruler-Bowler abstraction produces valid bowlers
|
|
6
6
|
Given we use "html" bowler
|
7
7
|
When the input string is ""
|
8
8
|
Then bowler has all the method aliases
|
9
|
+
|
10
|
+
Scenario: Config params set properly
|
11
|
+
Given nothing
|
12
|
+
When we request param "bowlers"
|
13
|
+
Then param value ends with "/qipowl/config/bowlers"
|
14
|
+
|
15
|
+
Scenario: Additional map mergeg properly
|
16
|
+
Given we add additional bowler directory "spec"
|
17
|
+
And we use "html" bowler with additional map "html_additional_test"
|
18
|
+
When the input string is "This is ⌚striked⌚ out."
|
19
|
+
And the execute method is called on bowler
|
20
|
+
Then the result should equal to "<p>This is <del>striked</del> out.</p>"
|
21
|
+
|
22
|
+
|
data/features/html.feature
CHANGED
@@ -46,11 +46,16 @@ Feature: All the possibilities of HTML parser
|
|
46
46
|
|
47
47
|
Scenario: Block tag Λ
|
48
48
|
Given we use "html" bowler
|
49
|
-
When the input string is
|
49
|
+
When the input string is
|
50
|
+
"""
|
51
|
+
Λ ruby
|
52
|
+
@mapping[:block] = ≡bold≡
|
53
|
+
Λ
|
54
|
+
"""
|
50
55
|
And the execute method is called on bowler
|
51
56
|
Then the result should equal to
|
52
57
|
"""
|
53
|
-
|
58
|
+
|
54
59
|
<pre class='ruby'>@mapping[:block] = ≡bold≡</pre>
|
55
60
|
"""
|
56
61
|
|
@@ -106,7 +111,7 @@ Feature: All the possibilities of HTML parser
|
|
106
111
|
| "Wikipedia¹http://wikipedia.org" | "<p><a href='http://wikipedia.org'>Wikipedia</a></p>" |
|
107
112
|
| "Wikipedia†Best knowledge base†" | "<p><abbr title='Best knowledge base'>Wikipedia</abbr></p>" |
|
108
113
|
| "Inplace picture¹http://mudasobwa.ru/images/am.jpg goes here." | "<p><img class='inplace' alt='Inplace picture' src='http://mudasobwa.ru/images/am.jpg'/> goes here.</p>" |
|
109
|
-
| "http://mudasobwa.ru/images/am.jpg Standalone picture" | " ␍ <figure> ␍
|
114
|
+
| "http://mudasobwa.ru/images/am.jpg Standalone picture" | " ␍ <figure> ␍ <img src='http://mudasobwa.ru/images/am.jpg'/> ␍ <figcaption> ␍ <p>Standalone picture</p> ␍ </figcaption> ␍ </figure> ␍ " |
|
110
115
|
|
111
116
|
Scenario Outline: Markdown atavisms ⇒ links
|
112
117
|
Given we use "html" bowler
|
@@ -117,7 +122,7 @@ Feature: All the possibilities of HTML parser
|
|
117
122
|
Examples:
|
118
123
|
| input | output |
|
119
124
|
| "Here ![Image](http://mudasobwa.ru/images/am.jpg) goes" | "<p>Here <img class='inplace' alt='Image' src='http://mudasobwa.ru/images/am.jpg'/> goes</p>" |
|
120
|
-
| "![Figure](http://mudasobwa.ru/images/am.jpg)" | " ␍ <figure> ␍
|
125
|
+
| "![Figure](http://mudasobwa.ru/images/am.jpg)" | " ␍ <figure> ␍ <img src='http://mudasobwa.ru/images/am.jpg'/> ␍ <figcaption> ␍ <p>Figure</p> ␍ </figcaption> ␍ </figure> ␍ " |
|
121
126
|
| "Here [Link](http://wikipedia.org/) goes" | "<p>Here <a href='http://wikipedia.org/'>Link</a> goes</p>" |
|
122
127
|
| "Here *italic* goes" | "<p>Here <em>italic</em> goes</p>" |
|
123
128
|
| "Here inplace*it*alic goes" | "<p>Here inplace<em>it</em>alic goes</p>" |
|
@@ -135,12 +140,12 @@ Feature: All the possibilities of HTML parser
|
|
135
140
|
|
136
141
|
Examples:
|
137
142
|
| input | output |
|
138
|
-
| "List: • li1 • li2" | "<p>List:</p> <ul class='fancy'><li>li1
|
139
|
-
| "List: • li1 • nested 1 • nested 2 • li2" | "<p>List:</p> <ul class='fancy'><li>li1
|
140
|
-
| "Data: ▶ dt — dd ▶ dt — dd" | "<p>Data:</p> <dl> ␍ <dt>dt</dt> ␍ <dd>dd
|
143
|
+
| "List: • li1 • li2" | "<p>List:</p> <ul class='fancy'><li>li1</li> <li>li2</li></ul>" |
|
144
|
+
| "List: • li1 • nested 1 • nested 2 • li2" | "<p>List:</p> <ul class='fancy'><li>li1</li> <ul class='fancy'><li>nested 1</li> <li>nested 2</li></ul> <li>li2</li></ul>" |
|
145
|
+
| "Data: ▶ dt — dd ▶ dt — dd" | "<p>Data:</p> <dl> ␍ <dt>dt</dt> ␍ <dd>dd</dd> ␍ ␍ <dt>dt</dt> ␍ <dd>dd</dd> ␍ </dl>" |
|
141
146
|
| "§1 Header" | "<h1>Header</h1>" |
|
142
147
|
| "〉 Blockquote" | "<blockquote><p class='blockquote'>Blockquote</p></blockquote>" |
|
143
|
-
|
148
|
+
|
144
149
|
Scenario Outline: Self tags
|
145
150
|
Given we use "html" bowler
|
146
151
|
When the input string is <input>
|
@@ -148,8 +153,12 @@ Feature: All the possibilities of HTML parser
|
|
148
153
|
Then the result should equal to <output>
|
149
154
|
|
150
155
|
Examples:
|
151
|
-
| input
|
152
|
-
| "here qipowl goes"
|
156
|
+
| input | output |
|
157
|
+
| "here qipowl goes" | "<p>here <strong><a href='http://qipowl.github.com'>qipowl</a></strong> goes</p>" |
|
158
|
+
| "here Qipowl goes" | "<p>here <strong><a href='http://Qipowl.github.com'>Qipowl</a></strong> goes</p>" |
|
159
|
+
| "here tag goes" | "<p>here <a href='/tags/tag'>tag</a> goes</p>" |
|
160
|
+
| "here tag2 goes" | "<p>here <b><a href='/tags/tag2'>tag2</a></b> goes</p>" |
|
161
|
+
| "here tag3 goes" | "<p>here <b><a href='/tags/tag3'>tag3</a></b> goes</p>" |
|
153
162
|
|
154
163
|
Scenario: Address tag
|
155
164
|
Given we use "html" bowler
|
@@ -162,7 +171,7 @@ Feature: All the possibilities of HTML parser
|
|
162
171
|
When the input string is "List: × li1 line item × li2"
|
163
172
|
And rule "×" is added to mapping as "li" in "regular" section with "ol" enclosure
|
164
173
|
And the execute method is called on bowler
|
165
|
-
Then the result should equal to "<p>List:</p> <ol><li>li1 line item
|
174
|
+
Then the result should equal to "<p>List:</p> <ol><li>li1 line item</li> <li>li2</li></ol>"
|
166
175
|
|
167
176
|
Scenario: Entity removal
|
168
177
|
Given we use "html" bowler
|
@@ -178,19 +187,19 @@ Feature: All the possibilities of HTML parser
|
|
178
187
|
✍
|
179
188
|
Preamble: given
|
180
189
|
✍
|
181
|
-
|
190
|
+
|
182
191
|
〉 http://mudasobwa.ru/i/self.jpg With caption
|
183
192
|
‒ Wiki, http://wikipedia.ru
|
184
|
-
|
193
|
+
|
185
194
|
Nice?
|
186
195
|
"""
|
187
196
|
And the execute method is called on bowler
|
188
197
|
Then the result should equal to
|
189
198
|
"""
|
190
|
-
|
191
|
-
|
199
|
+
|
200
|
+
|
192
201
|
<blockquote><p class='blockquote'>http://mudasobwa.ru/i/self.jpg With caption
|
193
|
-
<br/> <small><a href='http://wikipedia.ru'>Wiki</a></small
|
202
|
+
<br/> <small><a href='http://wikipedia.ru'>Wiki</a></small></p></blockquote>
|
194
203
|
<p>Nice?</p>
|
195
204
|
"""
|
196
205
|
|
@@ -201,13 +210,13 @@ Feature: All the possibilities of HTML parser
|
|
201
210
|
---
|
202
211
|
This is a comment text
|
203
212
|
---
|
204
|
-
|
213
|
+
|
205
214
|
Nice?
|
206
215
|
"""
|
207
216
|
And the execute method is called on bowler
|
208
217
|
Then the result should equal to
|
209
218
|
"""
|
210
|
-
|
219
|
+
|
211
220
|
<p>Nice?</p>
|
212
221
|
"""
|
213
222
|
|
@@ -216,28 +225,28 @@ Feature: All the possibilities of HTML parser
|
|
216
225
|
When the input string is
|
217
226
|
"""
|
218
227
|
§2 Blockquotes
|
219
|
-
|
228
|
+
|
220
229
|
〉 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere
|
221
230
|
erat a ante.
|
222
231
|
〉 Nested blockquote first line
|
223
232
|
〉 Nested blockquote second line
|
224
233
|
〉 Lorem ipsum para text 2.
|
225
|
-
|
234
|
+
|
226
235
|
Some para text.
|
227
|
-
|
236
|
+
|
228
237
|
〉 Intro 1.
|
229
238
|
• list item ≡with bold≡ 1
|
230
|
-
• list item ≈emphasized≈
|
239
|
+
• list item ≈emphasized≈
|
231
240
|
• nested list item 1
|
232
|
-
• nested list item 2
|
233
|
-
|
241
|
+
• nested list item 2
|
242
|
+
|
234
243
|
〉 Blockquote standalone.
|
235
|
-
|
244
|
+
|
236
245
|
〉 Intro 2.
|
237
|
-
• list item 2.1
|
246
|
+
• list item 2.1
|
238
247
|
• list item 2.2
|
239
248
|
〉 Continuing intro 2.
|
240
|
-
|
249
|
+
|
241
250
|
Blockquote standalone.
|
242
251
|
"""
|
243
252
|
And the execute method is called on bowler
|
@@ -245,3 +254,43 @@ Feature: All the possibilities of HTML parser
|
|
245
254
|
And the result should match "</li> <ul class='fancy'><li>nested list item 1"
|
246
255
|
And the result should match "<p>Blockquote standalone.</p>"
|
247
256
|
|
257
|
+
Scenario Outline: Postprocessing: spaces
|
258
|
+
Given we use "html" bowler
|
259
|
+
When the input string is <input>
|
260
|
+
And the execute method is called on bowler
|
261
|
+
Then the result should equal to <output>
|
262
|
+
|
263
|
+
Examples:
|
264
|
+
| input | output |
|
265
|
+
| "here «quote» goes" | "<p>here «quote» goes</p>" |
|
266
|
+
| "here « quote¹http://wikipedia.org » goes" | "<p>here «<a href='http://wikipedia.org'>quote</a>» goes</p>" |
|
267
|
+
| "This is λsystem.dllλ file." | "<p>This is <code>system.dll</code> file.</p>" |
|
268
|
+
| "This is ✓8:35✓ time." | "<p>This is <span class='notypo'>8:35</span> time.</p>" |
|
269
|
+
|
270
|
+
Scenario: Video embedded
|
271
|
+
Given we use "html" bowler
|
272
|
+
When the input string is
|
273
|
+
"""
|
274
|
+
http://qipowl.herokuapp.com/images/owl.png
|
275
|
+
|
276
|
+
☞ Video embedded:
|
277
|
+
|
278
|
+
http://www.youtube.com/watch?v=KFKxlYNfT_o
|
279
|
+
"""
|
280
|
+
And the execute method is called on bowler
|
281
|
+
Then the result should equal to
|
282
|
+
"""
|
283
|
+
|
284
|
+
<figure>
|
285
|
+
<img src='http://qipowl.herokuapp.com/images/owl.png'/>
|
286
|
+
<figcaption>
|
287
|
+
<p></p>
|
288
|
+
</figcaption>
|
289
|
+
</figure>
|
290
|
+
|
291
|
+
<p class='text-info'>Video embedded:</p>
|
292
|
+
|
293
|
+
<iframe class='youtube' width='560' height='315' src='http://www.youtube.com/embed/KFKxlYNfT_o'
|
294
|
+
frameborder='0' allowfullscreen></iframe>
|
295
|
+
|
296
|
+
"""
|
@@ -1,10 +1,17 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
Given(/^nothing$/) do
|
4
|
+
end
|
5
|
+
|
3
6
|
Given(/^we use "(.*?)" bowler$/) do |f|
|
4
7
|
# @f = "config/bowlers/#{f}.yaml"
|
5
8
|
@bowler = Qipowl::Ruler.new_bowler f
|
6
9
|
end
|
7
10
|
|
11
|
+
Given(/^we use "(.*?)" bowler with additional map "(.*?)"$/) do |f, map|
|
12
|
+
@bowler = Qipowl::Ruler.new_bowler(f, additional_maps: map)
|
13
|
+
end
|
14
|
+
|
8
15
|
When(/^the input string is "(.*?)"$/) do |input|
|
9
16
|
@input = input
|
10
17
|
end
|
@@ -25,6 +32,16 @@ When(/^rule "(.*?)" is removed from mapping$/) do |key|
|
|
25
32
|
@bowler.remove_entity key
|
26
33
|
end
|
27
34
|
|
35
|
+
When(/^we request param "(.*?)"$/) do |param|
|
36
|
+
@param_value = Qipowl["#{param}".to_sym]
|
37
|
+
end
|
38
|
+
|
39
|
+
When(/^we add additional bowler directory "(.*?)"$/) do |dir|
|
40
|
+
Qipowl::configure do
|
41
|
+
add :bowlers, dir
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
28
45
|
Then(/^bowler has all the method aliases$/) do
|
29
46
|
puts @bowler.class.instance_methods(false)
|
30
47
|
puts '='*60
|
@@ -39,3 +56,6 @@ Then(/^the output is "(.*?)"$/) do |result|
|
|
39
56
|
expect(@result).to eq(result)
|
40
57
|
end
|
41
58
|
|
59
|
+
Then(/^param value ends with "(.*?)"$/) do |s|
|
60
|
+
expect("#{@param_value}").to match(/#{s}$/)
|
61
|
+
end
|
data/lib/qipowl.rb
CHANGED
@@ -18,30 +18,40 @@ Encoding.default_internal = Encoding::UTF_8
|
|
18
18
|
module Qipowl
|
19
19
|
extend self
|
20
20
|
|
21
|
-
# Basic single-method DSL with .parameter method
|
22
|
-
# being used to define a set of available settings.
|
23
|
-
# This method takes one or more symbols, with each one being
|
24
|
-
# a name of the configuration option.
|
25
|
-
def params *names
|
26
|
-
names.each do |name|
|
27
|
-
attr_accessor name
|
28
|
-
define_method name do |*values|
|
29
|
-
value = values.first
|
30
|
-
value ? self.send("#{name}=", value) : instance_variable_get("@#{name}")
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
21
|
# A wrapper for the configuration block
|
36
|
-
def
|
22
|
+
def configure &block
|
37
23
|
instance_eval(&block)
|
38
24
|
end
|
39
25
|
|
26
|
+
def [](key)
|
27
|
+
config[key.to_sym]
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def set(key, value)
|
33
|
+
config[key.to_sym] = value
|
34
|
+
end
|
35
|
+
|
36
|
+
def add(key, value)
|
37
|
+
config[key.to_sym] = [*config[key.to_sym]] << value
|
38
|
+
end
|
39
|
+
|
40
|
+
def config
|
41
|
+
@config ||= Hash.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def method_missing(sym, *args)
|
45
|
+
if sym.to_s =~ /(.+)=$/
|
46
|
+
config[$1.to_sym] = args.first
|
47
|
+
else
|
48
|
+
config[sym.to_sym]
|
49
|
+
end
|
50
|
+
end
|
40
51
|
end
|
41
52
|
|
42
|
-
Qipowl::
|
43
|
-
|
44
|
-
bowlers_dir File.expand_path(File.join(__dir__, '..', 'config', 'bowlers'))
|
53
|
+
Qipowl::configure do
|
54
|
+
set :bowlers, File.expand_path(File.join(__dir__, '..', 'config', 'bowlers'))
|
45
55
|
end
|
46
56
|
|
47
57
|
class Qipowl::Html
|
@@ -49,4 +59,4 @@ class Qipowl::Html
|
|
49
59
|
def self.parse s
|
50
60
|
(@bowler ||= Qipowl::Ruler.new_bowler "html").execute s
|
51
61
|
end
|
52
|
-
end
|
62
|
+
end
|
data/lib/qipowl/bowlers/html.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'net/http'
|
4
|
+
require 'base64'
|
4
5
|
require 'typogrowth'
|
5
6
|
|
6
7
|
require_relative '../core/bowler'
|
@@ -8,7 +9,7 @@ require_relative '../core/bowler'
|
|
8
9
|
module Qipowl
|
9
10
|
module Mappers
|
10
11
|
class HtmlBowlerMapper < BowlerMapper
|
11
|
-
|
12
|
+
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
@@ -23,59 +24,80 @@ module Qipowl
|
|
23
24
|
# @param [Array] args the words, gained since last call to {#harvest}
|
24
25
|
# @return [Array] the array of words with trimmed `grip` tag
|
25
26
|
def ∀_grip *args
|
27
|
+
data = ∃_grip(__callee__)
|
26
28
|
mine, rest = [*args].join(SEPARATOR).split("#{__callee__}∎", 2)
|
27
|
-
[tagify(
|
29
|
+
[tagify(data[:tag], {:class => data[:class]}, mine), rest]
|
28
30
|
end
|
29
31
|
|
30
32
|
# `:alone` default handler
|
31
33
|
# @param [Array] args the words, gained since last call to {#harvest}
|
32
34
|
# @return [Array] the array of words with prepended `alone` tag
|
33
35
|
def ∀_alone *args
|
34
|
-
|
36
|
+
data = ∃_alone(__callee__)
|
37
|
+
[standalone(data[:tag], {:class => data[:class]}), args]
|
35
38
|
end
|
36
39
|
|
37
40
|
# `:block` default handler
|
41
|
+
# FIXME TODO make an ability to supply class
|
38
42
|
# @param [Array] args the words, gained since last call to {#harvest}
|
39
43
|
# @param [String] param the text to be places on the same string as
|
40
44
|
# opening tag
|
41
45
|
# @return [Nil] nil
|
42
|
-
def ∀_block
|
43
|
-
|
44
|
-
param =
|
45
|
-
|
46
|
+
def ∀_block arg
|
47
|
+
data = ∃_block(__callee__)
|
48
|
+
param, body = Base64.decode64(
|
49
|
+
"#{arg.to_s.unbowl.uncarriage(false)}"
|
50
|
+
).force_encoding('UTF-8').split(/\n/, 2)
|
51
|
+
param, body = '', param if body.nil?
|
52
|
+
harvest __callee__,
|
46
53
|
tagify(
|
47
|
-
|
48
|
-
{:class =>
|
49
|
-
|
54
|
+
data[:tag],
|
55
|
+
{:class => param.strip.empty? ? data[:class] : param.strip },
|
56
|
+
body.hsub(String::HTML_ENTITIES)
|
50
57
|
)
|
51
58
|
end
|
52
|
-
|
59
|
+
|
53
60
|
# `:magnet` default handler
|
54
61
|
# @param [Array] args the words, gained since last call to {#harvest}
|
55
62
|
# @return [Array] the array of words with trimmed `magnet` tag
|
56
63
|
def ∀_magnet *args
|
64
|
+
data = ∃_magnet(__callee__)
|
57
65
|
param, *rest = args.flatten
|
58
66
|
param = param.unbowl.to_s.prepend("#{__callee__}#{String::NBSP}")
|
59
|
-
[tagify(
|
67
|
+
[tagify(data[:tag], {:class => data[:class]}, param), rest]
|
60
68
|
end
|
61
|
-
|
69
|
+
|
62
70
|
# `:regular` default handler
|
63
71
|
# @param [Array] args the words, gained since last call to {#harvest}
|
64
72
|
def ∀_regular *args
|
65
|
-
|
66
|
-
|
67
|
-
∃_regular_tag(canonize(__callee__)),
|
68
|
-
{:class => ∃_regular(canonize(__callee__))[:class]},
|
69
|
-
args
|
70
|
-
)
|
73
|
+
data = ∃_regular(canonize __callee__)
|
74
|
+
harvest __callee__, tagify(data[:tag], {:class => data[:class]}, args)
|
71
75
|
end
|
72
|
-
|
76
|
+
|
73
77
|
# `:self` default handler
|
74
78
|
# @param [Array] args the words, gained since last call to {#harvest}
|
75
|
-
def ∀_self *args
|
76
|
-
|
79
|
+
def ∀_self *args, &cb
|
80
|
+
data = ∃_self(__callee__)
|
81
|
+
cally = block_given? ? yield : __callee__
|
82
|
+
text = case data[:format]
|
83
|
+
when NilClass then cally
|
84
|
+
when String then cally.to_s.gsub(/(#{cally})/, data[:format])
|
85
|
+
# when Regexp then cally.to_s.gsub(cally.to_s, data[:format])
|
86
|
+
when Symbol then send(data[:format], cally) rescue cally # FIXME Do I need rescue here?!
|
87
|
+
else raise "Bad format specified for #{data[:tag]}"
|
88
|
+
end
|
89
|
+
[data[:tag] ? tagify(data[:tag], {:class => data[:class]}, text) : text, args]
|
90
|
+
end
|
91
|
+
|
92
|
+
##############################################################################
|
93
|
+
### Self :: Specific handlers ###
|
94
|
+
##############################################################################
|
95
|
+
# Handler for tags.
|
96
|
+
# @param [String] arg the word to be processed
|
97
|
+
# @return [Array] the array of words with procesed tag
|
98
|
+
def tagger_format tag
|
99
|
+
"<a href='/tags/#{tag}'>#{tag}</a>"
|
77
100
|
end
|
78
|
-
|
79
101
|
##############################################################################
|
80
102
|
### Grip :: Specific handlers ###
|
81
103
|
##############################################################################
|
@@ -83,28 +105,30 @@ module Qipowl
|
|
83
105
|
# @param [Array] args the words, gained since last call to {#harvest}
|
84
106
|
# @return [Array] the array of words with trimmed `abbr` tag
|
85
107
|
def † *args
|
108
|
+
data = ∃_grip(__callee__)
|
86
109
|
term, *title = args.flatten
|
87
110
|
mine, rest = [*title].join(SEPARATOR).split("#{__callee__}∎", 2)
|
88
|
-
[tagify(
|
111
|
+
[tagify(data[:tag], {:title => mine, :class => data[:class]}, term), rest]
|
89
112
|
end
|
90
113
|
|
91
114
|
# Handler for anchors.
|
92
115
|
# @param [Array] args the words, gained since last call to {#harvest}
|
93
116
|
# @return [Array] the array of words with trimmed `a` tag
|
94
117
|
def ⚓ *args
|
118
|
+
data = ∃_grip(__callee__)
|
95
119
|
href, *title = args.flatten
|
96
120
|
mine, rest = [*title].join(SEPARATOR).split("#{__callee__}∎", 2)
|
97
121
|
href = href.unbowl
|
98
122
|
[
|
99
123
|
case get_href_content(href)
|
100
|
-
when :img
|
124
|
+
when :img
|
101
125
|
standalone :img, { :src => href, :alt => [*mine].join(SEPARATOR), :class => 'inplace' }
|
102
|
-
else
|
103
|
-
tagify
|
126
|
+
else
|
127
|
+
tagify data[:tag], {:href => href}, mine
|
104
128
|
end, rest
|
105
129
|
]
|
106
130
|
end
|
107
|
-
|
131
|
+
|
108
132
|
##############################################################################
|
109
133
|
### Alone :: Specific handlers ###
|
110
134
|
##############################################################################
|
@@ -114,7 +138,7 @@ module Qipowl
|
|
114
138
|
# @return [Nil] nil
|
115
139
|
def —— *args
|
116
140
|
harvest nil, orphan(args.join(SEPARATOR)) unless args.vacant?
|
117
|
-
harvest __callee__, standalone(∃
|
141
|
+
harvest __callee__, standalone(∃_alone(__callee__)[:tag])
|
118
142
|
end
|
119
143
|
|
120
144
|
##############################################################################
|
@@ -142,12 +166,12 @@ module Qipowl
|
|
142
166
|
ljref = "<span style='white-space: nowrap;'><a href='http://#{param}.livejournal.com/profile?mode=full'><img src='http://l-stat.livejournal.com/img/userinfo.gif' alt='[info]' style='border: 0pt none ; vertical-align: bottom; padding-right: 1px;' height='17' width='17'></a><a href='http://#{param}.livejournal.com/?style=mine'><b>#{param}</b></a></span>"
|
143
167
|
[ljref, rest]
|
144
168
|
end
|
145
|
-
|
169
|
+
|
146
170
|
def ☇ *args
|
147
171
|
param, *rest = args.flatten
|
148
|
-
[tagify(∃
|
172
|
+
[tagify(∃_magnet(__callee__)[:tag], {:name => param.unbowl}, String::ZERO_WIDTH_SPACE), rest]
|
149
173
|
end
|
150
|
-
|
174
|
+
|
151
175
|
##############################################################################
|
152
176
|
### Regular :: Specific handlers ###
|
153
177
|
##############################################################################
|
@@ -158,25 +182,24 @@ module Qipowl
|
|
158
182
|
id, *rest = args.flatten
|
159
183
|
harvest nil, orphan(rest.join(SEPARATOR)) unless rest.vacant?
|
160
184
|
harvest __callee__, %Q(
|
161
|
-
<iframe class='youtube' width='560' height='315' src='http://www.youtube.com/embed/#{id.unbowl}'
|
185
|
+
<iframe class='youtube' width='560' height='315' src='http://www.youtube.com/embed/#{id.unbowl}'
|
162
186
|
frameborder='0' allowfullscreen></iframe>
|
163
187
|
)
|
164
188
|
end
|
165
|
-
|
189
|
+
|
166
190
|
# Handler for standalone pictures and
|
167
191
|
# @todo Make it to understand quotes when there is a plain HTML on the other side
|
168
|
-
#
|
169
|
-
# @param
|
192
|
+
#
|
193
|
+
# @param
|
170
194
|
# @return [Nil] nil
|
171
195
|
def ⚘ *args
|
172
196
|
href, *title = args.flatten
|
197
|
+
title = title.join(SEPARATOR).strip
|
173
198
|
harvest __callee__, %Q(
|
174
199
|
<figure>
|
175
200
|
<img src='#{href.unbowl}'/>
|
176
201
|
<figcaption>
|
177
|
-
<p>
|
178
|
-
#{title.join(SEPARATOR)}
|
179
|
-
</p>
|
202
|
+
<p>#{title}</p>
|
180
203
|
</figcaption>
|
181
204
|
</figure>
|
182
205
|
)
|
@@ -193,35 +216,61 @@ module Qipowl
|
|
193
216
|
#{tagify :dd, {}, dd}
|
194
217
|
)
|
195
218
|
end
|
196
|
-
# Alias for {#▶}, according to YAML rules specifies additional
|
219
|
+
# Alias for {#▶}, according to YAML rules specifies additional
|
197
220
|
# class for the data list `<dl>` tag behind (`dl-horizontal`.)
|
198
221
|
alias_method :▷, :▶
|
199
222
|
|
200
223
|
protected
|
224
|
+
def shadows
|
225
|
+
@shadows ||= ([] \
|
226
|
+
<< self.class::BLOCK_TAGS.keys.map {|b| /#{b}.*?#{b}/m } \
|
227
|
+
<< self.class::CUSTOM_TAGS.keys.map {|re| /#{re}/m} \
|
228
|
+
<< self.class::MAGNET_TAGS.keys.map {|m| /#{m}\s+\S+/} \
|
229
|
+
).flatten
|
230
|
+
end
|
231
|
+
|
201
232
|
# @see {Qipowl::Bowlers::Bowler#defreeze}
|
202
|
-
#
|
233
|
+
#
|
203
234
|
# @param [String] str to be defreezed
|
204
235
|
def defreeze str
|
205
|
-
|
236
|
+
s = shadows
|
237
|
+
str.typo(sections: [:sequence, :quotes], shadows: s).defuse(shadows: s)
|
206
238
|
end
|
207
239
|
|
240
|
+
def serveup str
|
241
|
+
s = [
|
242
|
+
/<pre.*?>.*?<\/pre>/m,
|
243
|
+
/<code.*?>.*?<\/code>/m
|
244
|
+
]
|
245
|
+
|
246
|
+
str.gsub(/⌦./, '').gsub(/.⌫/, '').typo(
|
247
|
+
sections: [:punctuation, :orphans], shadows: s
|
248
|
+
)
|
249
|
+
end
|
250
|
+
|
251
|
+
|
208
252
|
# Computes the level of the `:linewide` element by counting
|
209
253
|
# preceeding non-breakable spaces. For instance, nested lists
|
210
254
|
# are produced by appending `"\u{00A0}"` to the line item
|
211
|
-
# DSL tag:
|
212
|
-
#
|
255
|
+
# DSL tag:
|
256
|
+
#
|
213
257
|
# li = "• li1 \u{00A0}• nested 1 \u{00A0}• nested 2 • li2"
|
214
|
-
#
|
215
|
-
# @param [Symbol|String] callee the DSL symbol to get the level information for.
|
216
|
-
# @return [Integer] the level requested.
|
217
258
|
#
|
218
|
-
|
219
|
-
|
220
|
-
|
259
|
+
# @param [Symbol|String] cally the DSL symbol to get the level information for.
|
260
|
+
# @return [Integer] the level requested.
|
261
|
+
#
|
262
|
+
def level cally
|
263
|
+
(cally = cally.to_s).gsub(/#{String::NBSP}/, '').empty? ?
|
264
|
+
-1 : (0..cally.length-1).each { |i| break i if cally[i] != String::NBSP }
|
221
265
|
end
|
222
|
-
|
223
|
-
def canonize
|
224
|
-
|
266
|
+
|
267
|
+
def canonize cally
|
268
|
+
cally.to_s.gsub(/^#{String::NBSP}*/, '').to_sym if cally
|
269
|
+
end
|
270
|
+
|
271
|
+
def normalize cally
|
272
|
+
scally = cally.to_s.unbowl
|
273
|
+
scally.gsub(/\p{L}/, '').empty? ? scally.downcase.bowl.to_sym : cally
|
225
274
|
end
|
226
275
|
|
227
276
|
# Produces html paragraph tag (`<p>`) with no class.
|
@@ -233,70 +282,73 @@ module Qipowl
|
|
233
282
|
end
|
234
283
|
|
235
284
|
# @see Qipowl::Bowler#harvest
|
236
|
-
#
|
285
|
+
#
|
237
286
|
# Additionally it checks if there was a `:linewide` item, requiring
|
238
287
|
# surrounding html element (like `<ul>` aroung several `<li>`s.)
|
239
|
-
#
|
240
|
-
# @param [Symbol]
|
288
|
+
#
|
289
|
+
# @param [Symbol] cally of method
|
241
290
|
# @param [String] str to be harvested
|
242
|
-
def harvest
|
243
|
-
if
|
244
|
-
level(
|
291
|
+
def harvest cally, str
|
292
|
+
if cally.nil? || cally != @cally
|
293
|
+
level(cally).downto(level(@cally) + 1) { |i|
|
245
294
|
str += i.␚ify
|
246
|
-
} unless ∃_enclosures(canonize
|
247
|
-
|
248
|
-
if prev = ∃_enclosures(canonize(@
|
249
|
-
level(@
|
295
|
+
} unless ∃_enclosures(canonize cally).nil?
|
296
|
+
|
297
|
+
if prev = ∃_enclosures(canonize(@cally))
|
298
|
+
level(@cally).downto(level(cally) + 1) { |i|
|
250
299
|
@yielded.last.sub!(/\A/, opening(prev[:tag], {:class => prev[:class]}))
|
251
300
|
@yielded.each { |s| s.gsub!(/#{i.␚ify}/) { closing(prev[:tag]) } }
|
252
301
|
}
|
253
302
|
end
|
254
|
-
|
255
|
-
@
|
303
|
+
|
304
|
+
@cally = cally
|
256
305
|
end
|
257
|
-
super
|
306
|
+
super cally, str
|
258
307
|
end
|
259
308
|
|
260
309
|
private
|
261
|
-
# Hence we cannot simply declare the DSL for it, we need to handle
|
310
|
+
# Hence we cannot simply declare the DSL for it, we need to handle
|
262
311
|
# calls to all the _methods_, starting with those symbols.
|
263
|
-
#
|
312
|
+
#
|
264
313
|
# @param [Symbol] method as specified by caller (`method_missing`.)
|
265
314
|
# @param [Array] args as specified by caller (`method_missing`.)
|
266
315
|
# @param [Proc] block as specified by caller (`method_missing`.)
|
267
|
-
#
|
316
|
+
#
|
268
317
|
# @return [Array] the array of words
|
269
318
|
def special_handler method, *args, &block
|
270
319
|
# Sublevel markers, e.g. “ •” is level 2 line-item
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
self.class.
|
275
|
-
|
320
|
+
if level(method) > 0 && self.class::REGULAR_TAGS.keys.include?(m = (canonize method))
|
321
|
+
self.class.class_eval "alias_method :#{method}, :#{m}"
|
322
|
+
send method, args, block
|
323
|
+
elsif self.class.instance_methods.include?(m = (normalize method))
|
324
|
+
send(m, args) { yield if block_given? ; method.to_s }
|
325
|
+
else
|
326
|
+
[method, args].flatten
|
327
|
+
end
|
276
328
|
end
|
277
329
|
|
278
330
|
# Constructs opening html tag for the input given.
|
279
|
-
#
|
331
|
+
#
|
280
332
|
# To construct `abbr` tag with `title` _Title_ and class _default_:
|
281
|
-
#
|
333
|
+
#
|
282
334
|
# opening :abbr, { :title=>'Title', :class=>'default' }
|
283
|
-
#
|
335
|
+
#
|
284
336
|
# @param [String] tag to produce opening tag string from.
|
285
|
-
# @param [Hash] params to be put into opening tag as attributes.
|
337
|
+
# @param [Hash] params to be put into opening tag as attributes.
|
286
338
|
# @return [String] opening tag for the input given.
|
287
339
|
def opening tag, params={}
|
288
340
|
attrs = params.inject("") { |m, el| m.prepend " #{el.first}='#{el.last}'" unless el.last.nil? ; m }
|
289
341
|
"<#{tag}#{attrs}>"
|
290
342
|
end
|
291
|
-
|
343
|
+
|
292
344
|
# Constructs closing html tag for the input given.
|
293
|
-
#
|
345
|
+
#
|
294
346
|
# @param [String] tag to produce closing tag string from.
|
295
347
|
# @return [String] opening tag for the input given.
|
296
348
|
def closing tag
|
297
349
|
"</#{tag}>"
|
298
350
|
end
|
299
|
-
|
351
|
+
|
300
352
|
# (see opening)
|
301
353
|
# Acts most like an {#opening} method, but closes an element inplace
|
302
354
|
# (used for `hr`, `br`, `img`).
|
@@ -305,14 +357,14 @@ module Qipowl
|
|
305
357
|
end
|
306
358
|
# Constructs valid tag for the input given, concatenating
|
307
359
|
# opening and closing tags around the text passed in `args`.
|
308
|
-
#
|
360
|
+
#
|
309
361
|
# @param [String] tag to produce html tag string from.
|
310
|
-
# @param [Hash] params to be put into opening tag as attributes.
|
311
|
-
# @param [Array] args the words, to be tagged around.
|
362
|
+
# @param [Hash] params to be put into opening tag as attributes.
|
363
|
+
# @param [Array] args the words, to be tagged around.
|
312
364
|
# @return [String] opening tag for the input given.
|
313
365
|
def tagify tag, params, *args
|
314
366
|
text = [*args].join(SEPARATOR)
|
315
|
-
text.vacant? ? '' : "#{opening tag, params}#{text}#{closing tag}"
|
367
|
+
text.vacant? ? '' : "#{opening tag, params}#{text.strip}#{closing tag}"
|
316
368
|
end
|
317
369
|
|
318
370
|
|
@@ -329,7 +381,7 @@ module Qipowl
|
|
329
381
|
else
|
330
382
|
:text
|
331
383
|
end
|
332
|
-
|
384
|
+
|
333
385
|
# uri = URI(href.to_s.unbowl)
|
334
386
|
# Net::HTTP.start(uri.host, uri.port) do |http|
|
335
387
|
# http.open_timeout = 1
|
@@ -353,17 +405,17 @@ module Qipowl
|
|
353
405
|
end
|
354
406
|
=begin
|
355
407
|
# Markup processor for Html output.
|
356
|
-
#
|
408
|
+
#
|
357
409
|
# This class produces HTML from markup as Markdown does.
|
358
|
-
|
410
|
+
|
359
411
|
# Amount of unnamed instances of the class (needed for new class name generation)
|
360
412
|
@@inst_count = 0
|
361
|
-
|
362
|
-
|
413
|
+
|
414
|
+
|
363
415
|
# `:handshake` default handler
|
364
416
|
# @param [String] from packed as string operand “before”
|
365
417
|
# @param [String] from packed as string operand “after”
|
366
|
-
# @return
|
418
|
+
# @return
|
367
419
|
def ∈ *args
|
368
420
|
from, till, *rest = args.flatten
|
369
421
|
tag = @mapping.handshake(__callee__)
|
@@ -371,13 +423,13 @@ end
|
|
371
423
|
[tagify(tag, {}, "#{from.unbowl}#{__callee__}#{till.unbowl}".gsub(String::SYMBOL_FOR_SPACE, ' ')), rest]
|
372
424
|
end
|
373
425
|
alias_method :⊂, :∈
|
374
|
-
|
375
426
|
|
376
|
-
|
427
|
+
|
428
|
+
|
377
429
|
# @see {Qipowl::Bowler#serveup}
|
378
430
|
#
|
379
431
|
# Additionally it beatifies the output HTML
|
380
|
-
#
|
432
|
+
#
|
381
433
|
# @param [String] str to be roasted
|
382
434
|
def serveup str
|
383
435
|
result = ''
|
@@ -402,11 +454,11 @@ end
|
|
402
454
|
end
|
403
455
|
|
404
456
|
if __FILE__ == $0
|
405
|
-
|
457
|
+
|
406
458
|
i = 0
|
407
459
|
Dir.glob("#{File.dirname(__FILE__)}/../../../data/octopress-site/source/_posts/**/*.owl").each {|f|
|
408
460
|
puts "Processing ##{i += 1}: #{f}"
|
409
461
|
Qipowl::Html.parse File.read(f)
|
410
462
|
}
|
411
463
|
end
|
412
|
-
=end
|
464
|
+
=end
|