asciidoctor 1.5.6.2 → 1.5.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of asciidoctor might be problematic. Click here for more details.

Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +330 -143
  3. data/README-fr.adoc +441 -0
  4. data/README-jp.adoc +418 -0
  5. data/README-zh_CN.adoc +430 -0
  6. data/README.adoc +454 -0
  7. data/Rakefile +57 -0
  8. data/asciidoctor.gemspec +7 -1
  9. data/data/locale/attributes-ar.adoc +22 -0
  10. data/data/locale/attributes-bg.adoc +22 -0
  11. data/data/locale/attributes-ca.adoc +22 -0
  12. data/data/locale/attributes-cs.adoc +22 -0
  13. data/data/locale/attributes-da.adoc +22 -0
  14. data/data/locale/attributes-de.adoc +22 -0
  15. data/data/locale/attributes-en.adoc +23 -0
  16. data/data/locale/attributes-es.adoc +22 -0
  17. data/data/locale/attributes-fa.adoc +22 -0
  18. data/data/locale/attributes-fi.adoc +22 -0
  19. data/data/locale/attributes-fr.adoc +22 -0
  20. data/data/locale/attributes-hu.adoc +22 -0
  21. data/data/locale/attributes-id.adoc +22 -0
  22. data/data/locale/attributes-it.adoc +22 -0
  23. data/data/locale/attributes-ja.adoc +22 -0
  24. data/data/locale/attributes-kr.adoc +22 -0
  25. data/data/locale/attributes-nb.adoc +22 -0
  26. data/data/locale/attributes-nl.adoc +22 -0
  27. data/data/locale/attributes-nn.adoc +22 -0
  28. data/data/locale/attributes-pl.adoc +22 -0
  29. data/data/locale/attributes-pt.adoc +22 -0
  30. data/data/locale/attributes-pt_BR.adoc +22 -0
  31. data/data/locale/attributes-ro.adoc +22 -0
  32. data/data/locale/attributes-ru.adoc +22 -0
  33. data/data/locale/attributes-sr.adoc +22 -0
  34. data/data/locale/attributes-sr_Latn.adoc +22 -0
  35. data/data/locale/attributes-tr.adoc +22 -0
  36. data/data/locale/attributes-uk.adoc +22 -0
  37. data/data/locale/attributes-zh_CN.adoc +22 -0
  38. data/data/locale/attributes-zh_TW.adoc +22 -0
  39. data/data/locale/attributes.adoc +8 -649
  40. data/data/stylesheets/asciidoctor-default.css +77 -72
  41. data/features/xref.feature +366 -7
  42. data/lib/asciidoctor.rb +107 -93
  43. data/lib/asciidoctor/abstract_block.rb +247 -239
  44. data/lib/asciidoctor/abstract_node.rb +56 -58
  45. data/lib/asciidoctor/block.rb +3 -3
  46. data/lib/asciidoctor/callouts.rb +1 -1
  47. data/lib/asciidoctor/cli/invoker.rb +36 -9
  48. data/lib/asciidoctor/cli/options.rb +63 -25
  49. data/lib/asciidoctor/converter.rb +23 -13
  50. data/lib/asciidoctor/converter/base.rb +4 -0
  51. data/lib/asciidoctor/converter/docbook45.rb +16 -9
  52. data/lib/asciidoctor/converter/docbook5.rb +115 -97
  53. data/lib/asciidoctor/converter/factory.rb +29 -31
  54. data/lib/asciidoctor/converter/html5.rb +229 -192
  55. data/lib/asciidoctor/converter/manpage.rb +72 -50
  56. data/lib/asciidoctor/converter/template.rb +12 -12
  57. data/lib/asciidoctor/core_ext.rb +5 -1
  58. data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +6 -0
  59. data/lib/asciidoctor/document.rb +168 -77
  60. data/lib/asciidoctor/extensions.rb +79 -47
  61. data/lib/asciidoctor/helpers.rb +33 -11
  62. data/lib/asciidoctor/inline.rb +3 -2
  63. data/lib/asciidoctor/list.rb +2 -1
  64. data/lib/asciidoctor/logging.rb +122 -0
  65. data/lib/asciidoctor/parser.rb +406 -382
  66. data/lib/asciidoctor/path_resolver.rb +169 -162
  67. data/lib/asciidoctor/reader.rb +166 -121
  68. data/lib/asciidoctor/section.rb +45 -28
  69. data/lib/asciidoctor/stylesheets.rb +13 -5
  70. data/lib/asciidoctor/substitutors.rb +328 -254
  71. data/lib/asciidoctor/table.rb +105 -48
  72. data/lib/asciidoctor/timings.rb +34 -6
  73. data/lib/asciidoctor/version.rb +1 -1
  74. data/man/asciidoctor.1 +41 -23
  75. data/man/asciidoctor.adoc +14 -8
  76. data/test/api_test.rb +1004 -0
  77. data/test/attributes_test.rb +241 -50
  78. data/test/blocks_test.rb +549 -124
  79. data/test/converter_test.rb +170 -78
  80. data/test/document_test.rb +208 -767
  81. data/test/extensions_test.rb +188 -53
  82. data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +1 -1
  83. data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +1 -1
  84. data/test/fixtures/file-with-missing-include.adoc +1 -0
  85. data/test/fixtures/include-file.jsx +8 -0
  86. data/test/fixtures/lists.adoc +96 -0
  87. data/test/fixtures/other-chapters.adoc +11 -0
  88. data/test/fixtures/outer-include.adoc +5 -0
  89. data/test/fixtures/sample.asciidoc +5 -1
  90. data/test/fixtures/subdir/index.adoc +3 -0
  91. data/test/fixtures/subdir/inner-include.adoc +3 -0
  92. data/test/fixtures/subdir/middle-include.adoc +5 -0
  93. data/test/fixtures/tagged-class-enclosed.rb +0 -1
  94. data/test/fixtures/unclosed-tag.adoc +3 -0
  95. data/test/fixtures/unexpected-end-tag.adoc +4 -0
  96. data/test/invoker_test.rb +101 -40
  97. data/test/links_test.rb +266 -72
  98. data/test/lists_test.rb +243 -45
  99. data/test/logger_test.rb +211 -0
  100. data/test/manpage_test.rb +124 -6
  101. data/test/options_test.rb +46 -1
  102. data/test/paragraphs_test.rb +23 -10
  103. data/test/parser_test.rb +30 -1
  104. data/test/paths_test.rb +115 -33
  105. data/test/preamble_test.rb +1 -1
  106. data/test/reader_test.rb +337 -81
  107. data/test/sections_test.rb +656 -72
  108. data/test/substitutions_test.rb +182 -57
  109. data/test/tables_test.rb +324 -57
  110. data/test/test_helper.rb +77 -32
  111. data/test/text_test.rb +7 -7
  112. metadata +67 -3
@@ -1,3 +1,6 @@
1
+ # NOTE .to_s hides require from Opal
2
+ require 'asciidoctor'.to_s unless defined? Asciidoctor
3
+
1
4
  # encoding: UTF-8
2
5
  module Asciidoctor
3
6
  # Extensions provide a way to participate in the parsing and converting
@@ -107,37 +110,46 @@ module Extensions
107
110
  # opts - An optional Hash of options (default: {}):
108
111
  # :level - [Integer] The level to assign to this section; defaults to
109
112
  # one greater than the parent level (optional).
110
- # :numbered - [Boolean] A flag to force numbering, which falls back to the
113
+ # :numbered - [Boolean] A flag to force numbering, which falls back to the
111
114
  # state of the sectnums document attribute (optional).
112
115
  #
113
116
  # Returns a [Section] node with all properties properly initialized.
114
117
  def create_section parent, title, attrs, opts = {}
115
118
  doc = parent.document
116
- doctype, level = doc.doctype, (opts[:level] || parent.level + 1)
119
+ book = (doctype = doc.doctype) == 'book'
120
+ level = opts[:level] || parent.level + 1
117
121
  if (style = attrs.delete 'style')
118
- if style == 'abstract' && doctype == 'book'
122
+ if book && style == 'abstract'
119
123
  sectname, level = 'chapter', 1
120
124
  else
121
125
  sectname, special = style, true
122
126
  level = 1 if level == 0
123
127
  end
124
- elsif doctype == 'book'
125
- sectname = level == 0 ? 'part' : (level == 1 ? 'chapter' : 'section')
128
+ elsif book
129
+ sectname = level == 0 ? 'part' : (level > 1 ? 'section' : 'chapter')
126
130
  elsif doctype == 'manpage' && (title.casecmp 'synopsis') == 0
127
131
  sectname, special = 'synopsis', true
128
132
  else
129
133
  sectname = 'section'
130
134
  end
131
- sect = Section.new parent, level, false
135
+ sect = Section.new parent, level
132
136
  sect.title, sect.sectname = title, sectname
133
137
  if special
134
138
  sect.special = true
135
- sect.numbered = true if opts.fetch :numbered, (style == 'appendix')
136
- elsif opts.fetch :numbered, (level > 0 && (doc.attributes.key? 'sectnums'))
137
- sect.numbered = sect.special ? (parent.context == :section && parent.numbered) : true
139
+ if opts.fetch :numbered, (style == 'appendix')
140
+ sect.numbered = true
141
+ elsif !(opts.key? :numbered) && (doc.attr? 'sectnums', 'all')
142
+ sect.numbered = book && level == 1 ? :chapter : true
143
+ end
144
+ elsif level > 0
145
+ if opts.fetch :numbered, (doc.attr? 'sectnums')
146
+ sect.numbered = sect.special ? parent.numbered && true : true
147
+ end
148
+ else
149
+ sect.numbered = true if opts.fetch :numbered, (book && (doc.attr? 'partnums'))
138
150
  end
139
151
  unless (id = attrs.delete 'id') == false
140
- sect.id = attrs['id'] = id || ((doc.attributes.key? 'sectids') ? (Section.generate_id sect.title, doc) : nil)
152
+ sect.id = attrs['id'] = id || ((doc.attr? 'sectids') ? (Section.generate_id sect.title, doc) : nil)
141
153
  end
142
154
  sect.update_attributes attrs
143
155
  sect
@@ -152,7 +164,7 @@ module Extensions
152
164
  # parent - The parent Block (Block, Section, or Document) of this new image block.
153
165
  # attrs - A Hash of attributes to control how the image block is built.
154
166
  # Use the target attribute to set the source of the image.
155
- # Use the alt attribute to specify an alternate text for the image.
167
+ # Use the alt attribute to specify an alternative text for the image.
156
168
  # opts - An optional Hash of options (default: {})
157
169
  #
158
170
  # Returns a [Block] node with all properties properly initialized.
@@ -643,13 +655,12 @@ module Extensions
643
655
  # Public: Returns the {Asciidoctor::Document} on which the extensions in this registry are being used.
644
656
  attr_reader :document
645
657
 
646
- # Public: Returns the Array of {Group} classes, instances and/or Procs that have been registered.
658
+ # Public: Returns the Hash of {Group} classes, instances, and/or Procs that have been registered with this registry.
647
659
  attr_reader :groups
648
660
 
649
661
  def initialize groups = {}
650
662
  @groups = groups
651
- @preprocessor_extensions = @tree_processor_extensions = @postprocessor_extensions = @include_processor_extensions = @docinfo_processor_extensions = nil
652
- @block_extensions = @block_macro_extensions = @inline_macro_extensions = nil
663
+ @preprocessor_extensions = @tree_processor_extensions = @postprocessor_extensions = @include_processor_extensions = @docinfo_processor_extensions = @block_extensions = @block_macro_extensions = @inline_macro_extensions = nil
653
664
  @document = nil
654
665
  end
655
666
 
@@ -661,19 +672,21 @@ module Extensions
661
672
  # Returns the instance of this [Registry].
662
673
  def activate document
663
674
  @document = document
664
- (Extensions.groups.values + @groups.values).each do |group|
665
- case group
666
- when ::Proc
667
- case group.arity
668
- when 0, -1
669
- instance_exec(&group)
670
- when 1
671
- group.call self
675
+ unless (ext_groups = Extensions.groups.values + @groups.values).empty?
676
+ ext_groups.each do |group|
677
+ case group
678
+ when ::Proc
679
+ case group.arity
680
+ when 0, -1
681
+ instance_exec(&group)
682
+ when 1
683
+ group.call self
684
+ end
685
+ when ::Class
686
+ group.new.activate self
687
+ else
688
+ group.activate self
672
689
  end
673
- when ::Class
674
- group.new.activate self
675
- else
676
- group.activate self
677
690
  end
678
691
  end
679
692
  self
@@ -705,7 +718,7 @@ module Extensions
705
718
  #
706
719
  # # as a method block
707
720
  # preprocessor do
708
- # process |doc, reader|
721
+ # process do |doc, reader|
709
722
  # ...
710
723
  # end
711
724
  # end
@@ -757,7 +770,7 @@ module Extensions
757
770
  #
758
771
  # # as a method block
759
772
  # tree_processor do
760
- # process |document|
773
+ # process do |document|
761
774
  # ...
762
775
  # end
763
776
  # end
@@ -814,7 +827,7 @@ module Extensions
814
827
  #
815
828
  # # as a method block
816
829
  # postprocessor do
817
- # process |document, output|
830
+ # process do |document, output|
818
831
  # ...
819
832
  # end
820
833
  # end
@@ -866,7 +879,7 @@ module Extensions
866
879
  #
867
880
  # # as a method block
868
881
  # include_processor do
869
- # process |document, output|
882
+ # process do |document, output|
870
883
  # ...
871
884
  # end
872
885
  # end
@@ -918,7 +931,7 @@ module Extensions
918
931
  #
919
932
  # # as a method block
920
933
  # docinfo_processor do
921
- # process |doc|
934
+ # process do |doc|
922
935
  # at_location :footer
923
936
  # 'footer content'
924
937
  # end
@@ -1007,14 +1020,14 @@ module Extensions
1007
1020
  # # as a method block
1008
1021
  # block do
1009
1022
  # named :shout
1010
- # process |parent, reader, attrs|
1023
+ # process do |parent, reader, attrs|
1011
1024
  # ...
1012
1025
  # end
1013
1026
  # end
1014
1027
  #
1015
1028
  # # as a method block with an explicit block name
1016
1029
  # block :shout do
1017
- # process |parent, reader, attrs|
1030
+ # process do |parent, reader, attrs|
1018
1031
  # ...
1019
1032
  # end
1020
1033
  # end
@@ -1096,14 +1109,14 @@ module Extensions
1096
1109
  # # as a method block
1097
1110
  # block_macro do
1098
1111
  # named :gist
1099
- # process |parent, target, attrs|
1112
+ # process do |parent, target, attrs|
1100
1113
  # ...
1101
1114
  # end
1102
1115
  # end
1103
1116
  #
1104
1117
  # # as a method block with an explicit macro name
1105
1118
  # block_macro :gist do
1106
- # process |parent, target, attrs|
1119
+ # process do |parent, target, attrs|
1107
1120
  # ...
1108
1121
  # end
1109
1122
  # end
@@ -1168,7 +1181,7 @@ module Extensions
1168
1181
  # inline_macro ChromeInlineMacro
1169
1182
  #
1170
1183
  # # as an InlineMacroProcessor subclass with an explicit macro name
1171
- # inline_macro ChromeInineMacro, :chrome
1184
+ # inline_macro ChromeInlineMacro, :chrome
1172
1185
  #
1173
1186
  # # as an instance of an InlineMacroProcessor subclass
1174
1187
  # inline_macro ChromeInlineMacro.new
@@ -1180,19 +1193,19 @@ module Extensions
1180
1193
  # inline_macro 'ChromeInlineMacro'
1181
1194
  #
1182
1195
  # # as a name of an InlineMacroProcessor subclass with an explicit macro name
1183
- # inline_macro 'ChromeInineMacro', :chrome
1196
+ # inline_macro 'ChromeInlineMacro', :chrome
1184
1197
  #
1185
1198
  # # as a method block
1186
1199
  # inline_macro do
1187
1200
  # named :chrome
1188
- # process |parent, target, attrs|
1201
+ # process do |parent, target, attrs|
1189
1202
  # ...
1190
1203
  # end
1191
1204
  # end
1192
1205
  #
1193
1206
  # # as a method block with an explicit macro name
1194
1207
  # inline_macro :chrome do
1195
- # process |parent, target, attrs|
1208
+ # process do |parent, target, attrs|
1196
1209
  # ...
1197
1210
  # end
1198
1211
  # end
@@ -1477,17 +1490,36 @@ module Extensions
1477
1490
  # Public: Resolves the Class object for the qualified name.
1478
1491
  #
1479
1492
  # Returns Class
1480
- def class_for_name qualified_name
1481
- resolved = ::Object
1482
- (qualified_name.split '::').each do |name|
1483
- unless name.empty? || ((resolved.const_defined? name) && ::Module === (resolved = resolved.const_get name))
1484
- raise ::NameError, %(Could not resolve class for name: #{qualified_name})
1493
+ if RUBY_MIN_VERSION_2
1494
+ def class_for_name qualified_name
1495
+ resolved = ::Object.const_get qualified_name, false
1496
+ raise unless ::Class === resolved
1497
+ resolved
1498
+ rescue
1499
+ raise ::NameError, %(Could not resolve class for name: #{qualified_name})
1500
+ end
1501
+ elsif RUBY_MIN_VERSION_1_9
1502
+ def class_for_name qualified_name
1503
+ resolved = (qualified_name.split '::').reduce ::Object do |current, name|
1504
+ name.empty? ? current : (current.const_get name, false)
1485
1505
  end
1506
+ raise unless ::Class === resolved
1507
+ resolved
1508
+ rescue
1509
+ raise ::NameError, %(Could not resolve class for name: #{qualified_name})
1510
+ end
1511
+ else
1512
+ def class_for_name qualified_name
1513
+ resolved = (qualified_name.split '::').reduce ::Object do |current, name|
1514
+ # NOTE on Ruby 1.8, const_defined? only checks for constant in current scope
1515
+ name.empty? ? current : ((current.const_defined? name) ? (current.const_get name) : raise)
1516
+ end
1517
+ raise unless ::Class === resolved
1518
+ resolved
1519
+ rescue
1520
+ raise ::NameError, %(Could not resolve class for name: #{qualified_name})
1486
1521
  end
1487
- raise ::NameError, %(Could not resolve class for name: #{qualified_name}) unless ::Class === resolved
1488
- resolved
1489
1522
  end
1490
1523
  end
1491
-
1492
1524
  end
1493
1525
  end
@@ -16,29 +16,31 @@ module Helpers
16
16
  # (default: true)
17
17
  # on_failure - a Symbol that indicates how to handle a load failure (:abort, :warn, :ignore) (default: :abort)
18
18
  #
19
- # returns The return value of Kernel#require if the library is available and can be, or was previously, loaded.
20
- # Otherwise, Kernel#raise is called with an appropriate message if on_failure is :abort.
21
- # Otherwise, Kernel#warn is called with an appropriate message and nil returned if on_failure is :warn.
19
+ # Returns The [Boolean] return value of Kernel#require if the library can be loaded.
20
+ # Otherwise, if on_failure is :abort, Kernel#raise is called with an appropriate message.
21
+ # Otherwise, if on_failure is :warn, Kernel#warn is called with an appropriate message and nil returned.
22
22
  # Otherwise, nil is returned.
23
23
  def self.require_library name, gem_name = true, on_failure = :abort
24
24
  require name
25
25
  rescue ::LoadError => e
26
+ include Logging unless include? Logging
26
27
  if gem_name
27
28
  gem_name = name if gem_name == true
28
29
  case on_failure
29
30
  when :abort
30
31
  raise ::LoadError, %(asciidoctor: FAILED: required gem '#{gem_name}' is not installed. Processing aborted.)
31
32
  when :warn
32
- warn %(asciidoctor: WARNING: optional gem '#{gem_name}' is not installed. Functionality disabled.)
33
+ logger.warn %(optional gem '#{gem_name}' is not installed. Functionality disabled.)
33
34
  end
34
35
  else
35
36
  case on_failure
36
37
  when :abort
37
38
  raise ::LoadError, %(asciidoctor: FAILED: #{e.message.chomp '.'}. Processing aborted.)
38
39
  when :warn
39
- warn %(asciidoctor: WARNING: #{e.message.chomp '.'}. Functionality disabled.)
40
+ logger.warn %(#{e.message.chomp '.'}. Functionality disabled.)
40
41
  end
41
42
  end
43
+ nil
42
44
  end
43
45
 
44
46
  # Public: Normalize the data to prepare for parsing
@@ -48,7 +50,7 @@ module Helpers
48
50
  #
49
51
  # returns a String Array of normalized lines
50
52
  def self.normalize_lines data
51
- data.class == ::String ? (normalize_lines_from_string data) : (normalize_lines_array data)
53
+ ::String === data ? (normalize_lines_from_string data) : (normalize_lines_array data)
52
54
  end
53
55
 
54
56
  # Public: Normalize the array of lines to prepare them for parsing
@@ -195,14 +197,34 @@ module Helpers
195
197
  end
196
198
  end
197
199
 
198
- def self.mkdir_p(dir)
200
+ def self.mkdir_p dir
199
201
  unless ::File.directory? dir
200
- parent_dir = ::File.dirname(dir)
201
- if !::File.directory?(parent_dir = ::File.dirname(dir)) && parent_dir != '.'
202
- mkdir_p(parent_dir)
202
+ unless (parent_dir = ::File.dirname dir) == '.'
203
+ mkdir_p parent_dir
204
+ end
205
+ begin
206
+ ::Dir.mkdir dir
207
+ rescue ::SystemCallError
208
+ raise unless ::File.directory? dir
203
209
  end
204
- ::Dir.mkdir(dir)
205
210
  end
206
211
  end
212
+
213
+ ROMAN_NUMERALS = {
214
+ 'M' => 1000, 'CM' => 900, 'D' => 500, 'CD' => 400, 'C' => 100, 'XC' => 90,
215
+ 'L' => 50, 'XL' => 40, 'X' => 10, 'IX' => 9, 'V' => 5, 'IV' => 4, 'I' => 1
216
+ }
217
+
218
+ # Converts an integer to a Roman numeral.
219
+ #
220
+ # val - the [Integer] value to convert
221
+ #
222
+ # Returns the [String] roman numeral for this integer
223
+ def self.int_to_roman val
224
+ ROMAN_NUMERALS.map {|l, i|
225
+ repeat, val = val.divmod i
226
+ l * repeat
227
+ }.join
228
+ end
207
229
  end
208
230
  end
@@ -21,8 +21,9 @@ class Inline < AbstractNode
21
21
  @type = opts[:type]
22
22
  @target = opts[:target]
23
23
 
24
- unless (more_attributes = opts[:attributes]).nil_or_empty?
25
- update_attributes more_attributes
24
+ # value of attributes option for inline nodes may be nil
25
+ if (attrs = opts[:attributes])
26
+ @attributes = attrs.dup
26
27
  end
27
28
  end
28
29
 
@@ -73,7 +73,8 @@ class ListItem < AbstractBlock
73
73
  #
74
74
  # Returns the converted String text for this ListItem
75
75
  def text
76
- apply_subs @text, @subs
76
+ # NOTE @text can be nil if dd node only has block content
77
+ @text && (apply_subs @text, @subs)
77
78
  end
78
79
 
79
80
  # Public: Set the String text.
@@ -0,0 +1,122 @@
1
+ require 'logger'
2
+
3
+ module Asciidoctor
4
+ class Logger < ::Logger
5
+ attr_reader :max_severity
6
+
7
+ def initialize *args
8
+ super
9
+ self.progname = 'asciidoctor'
10
+ self.formatter = BasicFormatter.new
11
+ self.level = WARN
12
+ end
13
+
14
+ def add severity, message = nil, progname = nil
15
+ if (severity ||= UNKNOWN) > (@max_severity ||= severity)
16
+ @max_severity = severity
17
+ end
18
+ super
19
+ end
20
+
21
+ class BasicFormatter < Formatter
22
+ SEVERITY_LABELS = { 'WARN' => 'WARNING', 'FATAL' => 'FAILED' }
23
+
24
+ def call severity, _, progname, msg
25
+ %(#{progname}: #{SEVERITY_LABELS[severity] || severity}: #{::String === msg ? msg : msg.inspect}\n)
26
+ end
27
+ end
28
+
29
+ module AutoFormattingMessage
30
+ def inspect
31
+ (sloc = self[:source_location]) ? %(#{sloc}: #{self[:text]}) : self[:text]
32
+ end
33
+ end
34
+ end
35
+
36
+ class MemoryLogger < ::Logger
37
+ # NOTE Ruby 1.8.7 returns constants as strings instead of symbols
38
+ SEVERITY_LABELS = ::Hash[Severity.constants.map {|c| [(Severity.const_get c), c.to_sym] }]
39
+
40
+ attr_reader :messages
41
+
42
+ def initialize
43
+ self.level = WARN
44
+ @messages = []
45
+ end
46
+
47
+ def add severity, message = nil, progname = nil
48
+ message = block_given? ? yield : progname unless message
49
+ @messages << { :severity => SEVERITY_LABELS[severity || UNKNOWN], :message => message }
50
+ true
51
+ end
52
+
53
+ def clear
54
+ @messages.clear
55
+ end
56
+
57
+ def empty?
58
+ @messages.empty?
59
+ end
60
+
61
+ def max_severity
62
+ empty? ? nil : @messages.map {|m| Severity.const_get m[:severity] }.max
63
+ end
64
+ end
65
+
66
+ class NullLogger < ::Logger
67
+ attr_reader :max_severity
68
+
69
+ def initialize; end
70
+
71
+ def add severity, message = nil, progname = nil
72
+ if (severity ||= UNKNOWN) > (@max_severity ||= severity)
73
+ @max_severity = severity
74
+ end
75
+ true
76
+ end
77
+ end
78
+
79
+ module LoggerManager
80
+ @logger_class = Logger
81
+ class << self
82
+ attr_accessor :logger_class
83
+
84
+ # NOTE subsequent calls to logger access the logger via the logger property directly
85
+ def logger pipe = $stderr
86
+ memoize_logger
87
+ @logger ||= (@logger_class.new pipe)
88
+ end
89
+
90
+ def logger= logger
91
+ @logger = logger || (@logger_class.new $stderr)
92
+ end
93
+
94
+ private
95
+ def memoize_logger
96
+ class << self
97
+ alias_method :logger, :logger
98
+ if RUBY_ENGINE == 'opal'
99
+ define_method :logger do @logger end
100
+ else
101
+ attr_reader :logger
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ module Logging
109
+ def self.included into
110
+ into.extend Logging
111
+ end
112
+
113
+ private
114
+ def logger
115
+ LoggerManager.logger
116
+ end
117
+
118
+ def message_with_context text, context = {}
119
+ ({ :text => text }.merge context).extend Logger::AutoFormattingMessage
120
+ end
121
+ end
122
+ end