teepee 0.6.0 → 0.7.0

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: 39c6bf274f4c82db707b2ba44ffe90057f87e0b3
4
- data.tar.gz: 4a57736a99aee7dd8e119defe2d288bcaa53c18a
3
+ metadata.gz: 7d3f7f012149fdff0a39dfec0d8a8dd301eec2e0
4
+ data.tar.gz: 75cc86a2801db966891c6be12570b496c21c5f15
5
5
  SHA512:
6
- metadata.gz: 6a2155902fecf31514399bcc9bf3c8a44c85f8ed1d139d09e0ef301b4e20a0a575800adb95d9b479a14644610ec20e7f0a881f570c0737b49f2d564f27acd271
7
- data.tar.gz: d81d6dfd76f3aef611b214997401705e3def22204c6c064e92244251d8249433325c44bc9ca6ed1be6b63a50cce703c24fbf90e42b8adea7d906d32bd184431d
6
+ metadata.gz: c30fedd28550b28526f5f0b259c44939fe04b0727f8ce65c403430d98788bd17a345c4c37458db4a2779581cadc7c49b8fd008bf57a0b080a4c1031b8da82de0
7
+ data.tar.gz: 5634379fef912b567b5e55bbedd3a6d623fe49d4d11968bf85522f7bfb34dcff8b91a59896f1e9912d71cd668baeb0f3ec5f8553dced5e71da420fb1901923b4
data/lib/teepee.rb CHANGED
@@ -35,536 +35,24 @@
35
35
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36
36
  # POSSIBILITY OF SUCH DAMAGE.
37
37
 
38
- require 'active_support/all'
39
- require 'monkey-patch'
40
-
41
- include ERB::Util
42
-
43
38
  module Teepee
44
- TB_COM = "http://thinkingbicycle.com"
45
-
46
- module MathFunctions
47
- class << self
48
- def degrees2radians degrees
49
- degrees * Math::PI / 180.0
50
- end
51
-
52
- def lgamma n
53
- Math::lgamma(n).first
54
- end
55
-
56
- def radians2degrees radians
57
- radians * 180.0 / Math::PI
58
- end
59
- end
60
- end
61
-
62
- class ParseError < RuntimeError
63
- end
64
-
65
- class ParserNode
66
- end
67
-
68
- class Token < ParserNode
69
- class << self
70
- # The child classes should implement this method. If there is an
71
- # immediate match, they should return a newly-created instance of
72
- # themselves and the rest of the input as a string. If there is no match,
73
- # they should return nil.
74
- def matches? text
75
- raise NotImplementedError,
76
- "Child class #{self.class} should implement this."
77
- end
78
- end
79
- end
80
-
81
- class SingleCharacterToken < Token
82
- def text
83
- self.class.character_matched
84
- end
85
-
86
- class << self
87
- def character_matched
88
- self::CHARACTER_MATCHED
89
- end
90
-
91
- def matches? text
92
- if text.first == character_matched
93
- return [self.new, text.rest]
94
- else
95
- return nil
96
- end
97
- end
98
- end
99
- end
100
-
101
- class StringToken < Token
102
- attr_reader :text
103
-
104
- def initialize(text)
105
- raise ArgumentError if not text.is_a? String
106
- raise ArgumentError if not text =~ self.class.full_match_regex
107
- @text = text
108
- end
109
-
110
- def to_s
111
- @text
112
- end
113
-
114
- def to_html
115
- @text
116
- end
117
-
118
- class << self
119
- def full_match_regex
120
- self::FULL_MATCH_REGEX # Define this in a child class.
121
- end
122
-
123
- def front_match_regex
124
- self::FRONT_MATCH_REGEX # Define this in a child class.
125
- end
126
-
127
- def count_regex
128
- self::COUNT_REGEX # Define this in a child class.
129
- end
130
-
131
- def matches? text
132
- if text =~ front_match_regex
133
- count = text.index count_regex
134
- if count.nil?
135
- return [self.new(text), ""]
136
- else
137
- return [self.new(text[0 ... count]), text[count .. -1]]
138
- end
139
- else
140
- return nil
141
- end
142
- end
143
- end
144
- end
145
-
146
- class BackslashToken < SingleCharacterToken
147
- CHARACTER_MATCHED = "\\"
148
- end
149
-
150
- class LeftBraceToken < SingleCharacterToken
151
- CHARACTER_MATCHED = "{"
152
- end
153
-
154
- class RightBraceToken < SingleCharacterToken
155
- CHARACTER_MATCHED = "}"
156
- end
157
-
158
-
159
- class EmptyNewlinesToken < StringToken
160
- FULL_MATCH_REGEX = /\A\n\n+\z/
161
- FRONT_MATCH_REGEX = /\A\n\n+/
162
- COUNT_REGEX = /[^\n]/
163
-
164
- def newlines
165
- text
166
- end
167
- end
168
-
169
- class WhitespaceToken < StringToken
170
- FULL_MATCH_REGEX = /\A\s+\z/
171
- FRONT_MATCH_REGEX = /\A\s+/
172
- COUNT_REGEX = /\S/
173
-
174
- def whitespace
175
- text
176
- end
177
-
178
- def to_html
179
- " " # Replace all whitespace tokens with a single space.
180
- end
181
- end
182
-
183
- class WordToken < StringToken
184
- FULL_MATCH_REGEX = /\A[^\s{}\\]+\z/
185
- FRONT_MATCH_REGEX = /[^\s{}\\]+/
186
- COUNT_REGEX = /[\s{}\\]/
187
-
188
- def to_html
189
- html_escape text
190
- end
191
-
192
- def word
193
- text
194
- end
195
- end
196
-
197
- class NumberToken < Token
198
- attr_reader :number, :text
199
-
200
- def initialize(text)
201
- raise ArgumentError if not text.is_a? String
202
- @text = text
203
- end
204
-
205
- def parse
206
- end
207
-
208
- def to_s
209
- number.to_s
210
- end
211
-
212
- def to_html
213
- to_s
214
- end
215
-
216
- class << self
217
- def matches? text
218
- end
219
- end
220
- end
221
-
222
- class Tokenizer
223
- attr_reader :text, :tokens
224
- def initialize(text)
225
- @text = text
226
- tokenize
227
- end
228
-
229
- def tokenize
230
- @tokens = []
231
- rest = text.gsub("\r", "")
232
- while rest.length > 0
233
- if result = BackslashToken.matches?(rest) or # Single Character Tokens
234
- result = LeftBraceToken.matches?(rest) or
235
- result = RightBraceToken.matches?(rest) or
236
- result = EmptyNewlinesToken.matches?(rest) or # String Tokens
237
- result = WhitespaceToken.matches?(rest) or
238
- result = NumberToken.matches?(rest) or
239
- result = WordToken.matches?(rest)
240
- then
241
- @tokens << result[0]
242
- rest = result[1]
243
- else
244
- raise RuntimeError, "Couldn't tokenize the remaining text."
245
- end
246
- end
247
- return @tokens
248
- end
249
- end
250
-
251
- class CommandParser < ParserNode
252
- attr_reader :command, :expressions
253
-
254
- def initialize(command, expressions)
255
- raise ArgumentError if not command.is_a? WordToken
256
- @command = command
257
- raise ArgumentError if not expressions.is_a? Array
258
- expressions.each {|expression| raise ArgumentError if not expression.kind_of? ParserNode}
259
- @expressions = expressions
260
- end
261
-
262
- def command_error(message)
263
- %{<span style="color: red">[#{message}]</span>}
264
- end
265
-
266
- def to_html
267
- case command.word
268
- when "backslash", "bslash"
269
- "\\"
270
- when "left-brace", "left_brace", "leftbrace", "lbrace", "opening-brace", "opening_brace",
271
- "openingbrace", "obrace"
272
- "{"
273
- when "right-brace", "right_brace", "rightbrace", "rbrace", "closing-brace", "closing_brace",
274
- "closingbrace", "cbrace"
275
- "}"
276
- when "br", "newline"
277
- "\n</br>\n"
278
- when "bold", "b", "textbf"
279
- html_tag :b
280
- when "del", "s", "strike", "strikethrough", "strikeout"
281
- html_tag :del
282
- when "i"
283
- command_error "Complex numbers are not yet supported."
284
- when "italic", "textit", "it"
285
- html_tag :i
286
- when "underline", "u"
287
- html_tag :u
288
- when "tt", "texttt", "teletype", "typewriter"
289
- html_tag :tt
290
- when "small"
291
- html_tag :small
292
- when "big"
293
- html_tag :big
294
- when "subscript", "sub"
295
- html_tag :sub
296
- when "superscript", "sup"
297
- html_tag :sup
298
- when "user", "user-id", "user_id"
299
- user_command_handler
300
- when "link-id", "link_id"
301
- link_id_command_handler
302
- when "keyword-id", "keyword_id"
303
- keyword_id_command_handler
304
- when "tag-id", "tag_id"
305
- tag_id_command_handler
306
- when "forum-id", "forum_id"
307
- forum_id_command_handler
308
- when "folder-id", "folder_id"
309
- folder_id_command_handler
310
- when "bookmarks-folder-id", "bookmarks_folder_id", "bookmarks_folder-id", "bookmarks-folder_id",
311
- "bookmark-folder-id", "bookmark_folder_id", "bookmark_folder-id", "bookmark-folder_id"
312
- bookmarks_folder_id_command_handler
313
- when "pi"
314
- "#{Math::PI}"
315
- when "e"
316
- "#{Math::E}"
317
- when "+"
318
- injectable_math_function_handler 0, :+
319
- when "-"
320
- if (numbers = numbers_from_expressions).length == 1
321
- injectable_math_function_handler 0, :-
322
- else
323
- reducable_math_function_handler :-
324
- end
325
- when "*"
326
- injectable_math_function_handler 1, :*
327
- when "/"
328
- if (numbers = numbers_from_expressions).length == 1
329
- 1 / numbers.first
330
- else
331
- reducable_math_function_handler :/
332
- end
333
- when "%"
334
- injectable_math_function_handler numbers_from_expressions.first, :%
335
- when "^", "**"
336
- number, exponent = numbers_from_expressions
337
- number.send :**, exponent
338
- when "sin", "cos", "tan",
339
- "asin", "acos", "atan",
340
- "sinh", "cosh", "tanh",
341
- "asinh", "acosh", "atanh",
342
- "erf", "erfc",
343
- "gamma", "log10", "sqrt"
344
- math_function_handler command.word.to_sym
345
- when "d2r", "deg->rad", "degrees->radians"
346
- MathFunctions::degrees2radians number_from_expression
347
- when "r2d", "rad->deg", "radians->degrees"
348
- MathFunctions::radians2degrees number_from_expression
349
- when "lgamma"
350
- MathFunctions::lgamma number_from_expression
351
- when "ld", "log2"
352
- Math.log2 number_from_expression
353
- when "ln"
354
- Math.log number_from_expression
355
- when "log"
356
- base, number = numbers_from_expressions
357
- if number.nil?
358
- Math.log base
359
- else
360
- Math.log number, base
361
- end
362
- when "ldexp"
363
- fraction, exponent = numbers_from_expressions
364
- Math.ldexp fraction, exponent
365
- when "hypot"
366
- Math.sqrt numbers_from_expressions.map {|n| n**2}
367
- else
368
- command_error "unknown command #{command.to_html}"
369
- end
370
- end
371
-
372
- def html_tag(tag)
373
- "<#{tag}>" + expressions.map(&:to_html).join + "</#{tag}>"
374
- end
375
-
376
- def tb_href(target, string)
377
- %{<a href="#{TB_COM}/#{target}">#{string}</a>}
378
- end
379
-
380
- def numbers_from_expressions
381
- expressions
382
- .map do |number|
383
- begin
384
- Float(number.to_html)
385
- rescue ArgumentError
386
- nil
387
- end
388
- end.reject &:nil?
389
- end
390
-
391
- def number_from_expression
392
- numbers_from_expressions.first
393
- end
394
-
395
- def injectable_math_function_handler(initial, function)
396
- numbers_from_expressions.inject initial, function
397
- end
398
-
399
- def reducable_math_function_handler(function)
400
- numbers_from_expressions.reduce function
401
- end
402
-
403
- def math_function_handler(function)
404
- Math.send function, numbers_from_expressions.first
405
- end
406
-
407
- def user_command_handler
408
- user = expressions.select {|expr| expr.is_a? WordToken}.first
409
- if not user
410
- command_error "user: error: no user specified"
411
- else
412
- if @@action_view.kind_of? ActionView::Base
413
- the_user = User.smart_find user.to_s
414
- if the_user
415
- @@action_view.render partial: 'users/name_link',
416
- locals: {the_user: the_user}
417
- else
418
- command_error "unknown user #{user.to_s}"
419
- end
420
- else
421
- tb_href "users/#{user}", user.to_s
422
- end
423
- end
424
- end
425
-
426
- def id_command_handler(klass,
427
- singular = klass.to_s.camelcase_to_snakecase,
428
- plural = singular.pluralize,
429
- partial = "#{plural}/inline",
430
- view="")
431
- id = expressions.select {|expr| expr.is_a? WordToken}.first
432
- if not id
433
- command_error "#{singular}_id: error: no #{singular} ID specified"
434
- elsif not id.to_s =~ /\A[0-9]+\z/
435
- command_error "#{singular}_id: error: invalid #{singular} ID specified"
436
- else
437
- if @@action_view.kind_of? ActionView::Base
438
- thing = klass.find Integer(id.to_s)
439
- if thing
440
- @@action_view.render partial: partial,
441
- locals: {singular.to_sym => thing}
442
- else
443
- command_error "unknown #{singular} ID #{id.to_s}"
444
- end
445
- else
446
- tb_href "/#{plural}/#{id.to_s}/#{view}", "#{klass} ##{id.to_s}"
447
- end
448
- end
449
- end
450
-
451
- def link_id_command_handler
452
- id_command_handler Link
453
- end
454
-
455
- def tag_id_command_handler
456
- id_command_handler Tag
457
- end
458
-
459
- def folder_id_command_handler
460
- id_command_handler Folder
461
- end
462
-
463
- def forum_id_command_handler
464
- id_command_handler Forum
465
- end
466
-
467
- def bookmarks_folder_id_command_handler
468
- id_command_handler Folder, "folder", "folders", "folders/bookmarks_inline", "bookmarks"
469
- end
470
-
471
- class << self
472
- @@action_view = nil
473
- @@controller = nil
474
-
475
- def parse(tokens)
476
- expressions = []
477
- rest = tokens
478
- backslash, command, left_brace = rest.shift(3)
479
- right_brace = nil
480
- raise ParseError if not backslash.is_a? BackslashToken
481
- raise ParseError if not command.is_a? WordToken
482
- if not left_brace.is_a? LeftBraceToken # A command with no interior.
483
- rest.unshift left_brace if not left_brace.is_a? WhitespaceToken
484
- return [CommandParser.new(command, []), rest]
485
- end
486
- while rest.length > 0
487
- if rest.first.is_a? WordToken
488
- expressions << rest.shift
489
- elsif rest.first.is_a? WhitespaceToken
490
- expressions << rest.shift
491
- elsif rest.first.is_a? BackslashToken
492
- result, rest = CommandParser.parse(rest)
493
- expressions << result
494
- elsif rest.first.is_a? RightBraceToken
495
- right_brace = rest.shift
496
- return [CommandParser.new(command, expressions), rest]
497
- else
498
- raise ParseError
499
- end
500
- end
501
- if right_brace.nil? # Allow a forgotten final right brace.
502
- return [CommandParser.new(command, expressions), rest]
503
- end
504
- end
505
-
506
- def action_view=(new)
507
- @@action_view = new
508
- end
509
-
510
- def controller=(new)
511
- @@controller = new
512
- end
513
- end
514
- end
515
-
516
- class ParagraphParser < ParserNode
517
- attr_reader :expressions, :tokens
518
-
519
- def initialize(tokens)
520
- raise ArgumentError if not tokens.is_a? Array
521
- tokens.each {|token| raise ArgumentError if not token.kind_of? ParserNode}
522
- @tokens = tokens
523
- parse
524
- end
525
-
526
- def parse
527
- @expressions = []
528
- rest = tokens
529
- while rest.length > 0
530
- if rest.first.is_a? WordToken
531
- @expressions << rest.shift
532
- elsif rest.first.is_a? WhitespaceToken
533
- @expressions << rest.shift
534
- elsif rest.first.is_a? BackslashToken
535
- command, rest = CommandParser.parse(rest)
536
- @expressions << command
537
- else
538
- return self
539
- end
540
- end
541
- end
542
-
543
- def to_html
544
- "<p>\n" + expressions.map(&:to_html).join + "\n</p>\n"
545
- end
546
- end
547
-
548
- class Parser < ParserNode
549
- attr_reader :paragraphs, :split_tokens, :text, :tokenizer
550
-
551
- def tokens
552
- tokenizer.tokens
553
- end
39
+ end
554
40
 
555
- def initialize(text)
556
- @text = text
557
- @tokenizer = Tokenizer.new text
558
- parse
559
- end
41
+ include ERB::Util
560
42
 
561
- def parse
562
- @split_tokens = tokens.split {|token| token.class == EmptyNewlinesToken}
563
- @paragraphs = @split_tokens.map {|split_tokens| ParagraphParser.new split_tokens}
564
- end
43
+ require 'active_support/all'
44
+ require 'monkey-patch'
565
45
 
566
- def to_html
567
- paragraphs.map(&:to_html).join "\n"
568
- end
569
- end
570
- end
46
+ require 'teepee/constants'
47
+ require 'teepee/errors'
48
+ require 'teepee/parser-node'
49
+ require 'teepee/token'
50
+ require 'teepee/commander'
51
+ require 'teepee/actionable-commander'
52
+ require 'teepee/single-character-token'
53
+ require 'teepee/string-token'
54
+ require 'teepee/number-token'
55
+ require 'teepee/tokenizer'
56
+ require 'teepee/command-parser'
57
+ require 'teepee/paragraph-parser'
58
+ require 'teepee/parser'