thermal 0.1.1 → 0.2.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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -20
  3. data/README.md +200 -27
  4. data/data/db.yml +1987 -0
  5. data/data/original.yml +1635 -0
  6. data/lib/thermal/byte_buffer.rb +48 -0
  7. data/lib/thermal/db/charset.rb +36 -0
  8. data/lib/thermal/db/cjk_encoding.rb +46 -0
  9. data/lib/thermal/db/data.rb +77 -0
  10. data/lib/thermal/db/device.rb +79 -0
  11. data/lib/thermal/db/encoding.rb +35 -0
  12. data/lib/thermal/db/loader.rb +65 -0
  13. data/lib/thermal/db.rb +43 -0
  14. data/lib/thermal/dsl.rb +28 -0
  15. data/lib/thermal/escpos/buffer.rb +167 -0
  16. data/lib/thermal/escpos/cmd.rb +11 -0
  17. data/lib/thermal/escpos/writer.rb +93 -0
  18. data/lib/thermal/escpos_star/buffer.rb +38 -0
  19. data/lib/thermal/escpos_star/writer.rb +17 -0
  20. data/lib/thermal/printer.rb +56 -21
  21. data/lib/thermal/profile.rb +71 -0
  22. data/lib/thermal/stargraphic/capped_byte_buffer.rb +20 -0
  23. data/lib/thermal/stargraphic/chunked_byte_buffer.rb +62 -0
  24. data/lib/thermal/stargraphic/writer.rb +318 -0
  25. data/lib/thermal/starprnt/buffer.rb +46 -0
  26. data/lib/thermal/starprnt/writer.rb +81 -0
  27. data/lib/thermal/util.rb +74 -0
  28. data/lib/thermal/version.rb +5 -3
  29. data/lib/thermal/writer_base.rb +122 -0
  30. data/lib/thermal.rb +81 -8
  31. metadata +59 -57
  32. data/.gitignore +0 -3
  33. data/.rspec +0 -2
  34. data/.travis.yml +0 -6
  35. data/Gemfile +0 -3
  36. data/Rakefile +0 -1
  37. data/lib/devices/btpr880.rb +0 -33
  38. data/lib/devices/html.rb +0 -14
  39. data/lib/thermal/parser.rb +0 -30
  40. data/spec/btpr880_spec.rb +0 -36
  41. data/spec/fixtures/receipt.html +0 -6
  42. data/spec/printer_spec.rb +0 -29
  43. data/spec/spec_helper.rb +0 -3
  44. data/spec/thermal_spec.rb +0 -7
  45. data/tasks/console.rake +0 -9
  46. data/tasks/spec.rake +0 -3
  47. data/thermal.gemspec +0 -16
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thermal
4
+ module Starprnt
5
+ class Buffer
6
+ COMMANDS = %i[ printRawText
7
+ printRasterReceipt
8
+ printImage
9
+ printBase64Image
10
+ print ].freeze
11
+
12
+ # print is a special command that takes an an array of subcommands
13
+ def print(array)
14
+ commands.push(print: []) if last_command != :print
15
+ commands.last[:print] += Array.wrap(array)
16
+ end
17
+
18
+ # other commands take a single object
19
+ (COMMANDS - [:print]).each do |cmd|
20
+ define_method(::Thermal::Util.underscore(cmd)) do |object|
21
+ commands.push(cmd => object)
22
+ end
23
+ end
24
+
25
+ def flush
26
+ tmp = commands
27
+ @commands = []
28
+ tmp
29
+ end
30
+
31
+ def to_a
32
+ commands.deep_dup
33
+ end
34
+
35
+ protected
36
+
37
+ def commands
38
+ @commands ||= []
39
+ end
40
+
41
+ def last_command
42
+ commands.last&.keys&.first
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thermal
4
+ module Starprnt
5
+ class Writer < ::Thermal::WriterBase
6
+ DEFAULT_QR_CODE_CELL_SIZE = 4
7
+
8
+ def print(flush: true, output: nil)
9
+ set_utf8_encoding
10
+ super
11
+ feed(5)
12
+ cut
13
+ flush ? buffer.flush : buffer.to_a
14
+ end
15
+
16
+ def self.format
17
+ 'starprnt'
18
+ end
19
+
20
+ def text(str, feed: true, replace: nil, **_kwargs)
21
+ str = ::Thermal::Util.normalize_utf8(str, replace: replace)
22
+ str = "#{str}\n" if feed
23
+ write(str) if str
24
+ end
25
+
26
+ def hr(style = nil, width: col_width)
27
+ write("#{hr_char(style) * width}\n")
28
+ end
29
+
30
+ def write_bold!
31
+ buffer.print(enableEmphasis: !!@bold)
32
+ end
33
+
34
+ def write_underline!
35
+ buffer.print(enableUnderline: !!@underline)
36
+ end
37
+
38
+ def write_align!
39
+ cmd = case @align
40
+ when :right
41
+ 'Right'
42
+ when :center
43
+ 'Center'
44
+ else
45
+ 'Left'
46
+ end
47
+ buffer.print(appendAlignment: cmd)
48
+ end
49
+
50
+ def feed(lines = 1)
51
+ buffer.print(appendLineFeed: lines)
52
+ end
53
+
54
+ def image(path, opts = {})
55
+ buffer.print_base64_image(::Escpos::Image.new(path, opts).to_escpos)
56
+ end
57
+
58
+ def qr_code(url, cell_size = DEFAULT_QR_CODE_CELL_SIZE)
59
+ buffer.print(appendQrCode: url, alignment: 'Center', cell: cell_size)
60
+ end
61
+
62
+ def cut
63
+ buffer.print(appendCutPaper: 'PartialCut')
64
+ end
65
+
66
+ protected
67
+
68
+ def write(str)
69
+ buffer.print(append: str)
70
+ end
71
+
72
+ def set_utf8_encoding
73
+ buffer.print(appendEncoding: 'UTF-8')
74
+ end
75
+
76
+ def buffer
77
+ @buffer ||= ::Thermal::Starprnt::Buffer.new
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thermal
4
+ # Some methods in this class are lovingly borrowed from ActiveSupport
5
+ module Util
6
+ extend self
7
+
8
+ def normalize_utf8(text, replace: nil, unf: :nfc)
9
+ return if !text || text.empty?
10
+
11
+ replace ||= ::Thermal.replace_char
12
+ text = text.encode('UTF-8', invalid: :replace, undef: :replace, replace: replace)
13
+ text.delete!("\r") # not ever used
14
+ text = text.unicode_normalize(unf) if unf
15
+ text unless text.empty?
16
+ end
17
+
18
+ def underscore(word)
19
+ word = word.to_s
20
+ return word unless /[A-Z-]/.match?(word)
21
+
22
+ word = word.dup
23
+ word.gsub!(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { (Regexp.last_match(1) || Regexp.last_match(2)) << '_' }
24
+ word.tr!('-', '_')
25
+ word.downcase!
26
+ word
27
+ end
28
+
29
+ def index_by(enumerable)
30
+ if block_given?
31
+ result = {}
32
+ enumerable.each { |elem| result[yield(elem)] = elem }
33
+ result
34
+ else
35
+ enumerable.to_enum(:index_by) { size if respond_to?(:size) }
36
+ end
37
+ end
38
+
39
+ def index_with(enumerable, default = (no_default = true))
40
+ if block_given?
41
+ result = {}
42
+ enumerable.each { |elem| result[elem] = yield(elem) }
43
+ result
44
+ elsif no_default
45
+ enumerable.to_enum(:index_with) { size if respond_to?(:size) }
46
+ else
47
+ result = {}
48
+ enumerable.each { |elem| result[elem] = default }
49
+ result
50
+ end
51
+ end
52
+
53
+ def deep_freeze!(object)
54
+ case object
55
+ when Hash
56
+ object.each_value { |v| deep_freeze!(v) }
57
+ when Array
58
+ object.each { |j| deep_freeze!(j) }
59
+ end
60
+ object.freeze
61
+ end
62
+
63
+ def self.deep_dup(object)
64
+ case object
65
+ when Hash
66
+ object.transform_values { |v| deep_dup(v) }
67
+ when Array
68
+ object.map { |j| deep_dup(j) }
69
+ else
70
+ object.dup
71
+ end
72
+ end
73
+ end
74
+ end
@@ -1,3 +1,5 @@
1
- module Thermal
2
- VERSION = "0.1.1"
3
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Thermal
4
+ VERSION = '0.2.0'
5
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thermal
4
+ class WriterBase
5
+ extend Forwardable
6
+
7
+ ALIGNMENTS = %i[left center right].freeze
8
+
9
+ def initialize(profile)
10
+ @profile = profile
11
+ @bold = false
12
+ @underline = false
13
+ @align = :left
14
+ end
15
+
16
+ def format
17
+ self.class.format if self.class.respond_to?(:format)
18
+ end
19
+
20
+ def print(**_kwargs)
21
+ yield
22
+ end
23
+
24
+ def text(str, feed: true, replace: nil)
25
+ # do nothing
26
+ end
27
+
28
+ def hr
29
+ # do nothing
30
+ end
31
+
32
+ def align(alignment = nil, &)
33
+ alignment = alignment.downcase.to_sym if alignment.is_a?(String)
34
+ alignment ||= :left
35
+ raise "Invalid align #{alignment.inspect}" unless ALIGNMENTS.include?(alignment)
36
+
37
+ write_style(align: alignment, &)
38
+ end
39
+
40
+ def bold(enabled = true, &) # rubocop:disable Style/OptionalBooleanParameter
41
+ write_style(bold: !!enabled, &)
42
+ end
43
+
44
+ def underline(enabled = true, weight: nil, &block) # rubocop:disable Style/OptionalBooleanParameter
45
+ underline = weight || enabled
46
+ underline = false if !underline || underline == 0
47
+ write_style(underline: underline, &block)
48
+ end
49
+
50
+ def feed
51
+ # do nothing
52
+ end
53
+
54
+ def cut
55
+ # do nothing
56
+ end
57
+
58
+ def image
59
+ # do nothing
60
+ end
61
+
62
+ def qr_code
63
+ # do nothing
64
+ end
65
+
66
+ def_delegators :@profile,
67
+ :supports?,
68
+ :col_width
69
+
70
+ protected
71
+
72
+ def hr_char(style = nil)
73
+ # rubocop:disable Lint/DuplicateBranch
74
+ case style
75
+ when :bold then '▄'
76
+ when :double then '═'
77
+ when :underline then '_'
78
+ when :half_lower then '▄'
79
+ when :half_upper then '▀'
80
+ when :full then '█'
81
+ else '─'
82
+ end
83
+ # rubocop:enable Lint/DuplicateBranch
84
+ end
85
+
86
+ def write_style(align: nil, bold: nil, underline: nil)
87
+ prev_align = @align
88
+ prev_bold = @bold
89
+ prev_underline = @underline
90
+ @align = align unless align.nil?
91
+ @bold = bold unless bold.nil?
92
+ @underline = underline unless underline.nil?
93
+ write_align! unless @align == prev_align
94
+ write_bold! unless @bold == prev_bold
95
+ write_underline! unless @underline == prev_underline
96
+ return unless block_given?
97
+
98
+ yield
99
+ inner_align = @align
100
+ inner_bold = @bold
101
+ inner_underline = @underline
102
+ @align = prev_align
103
+ @bold = prev_bold
104
+ @underline = prev_underline
105
+ write_align! unless @align == inner_align
106
+ write_bold! unless @bold == inner_bold
107
+ write_underline! unless @underline == inner_underline
108
+ end
109
+
110
+ def write_align!
111
+ # do nothing
112
+ end
113
+
114
+ def write_bold!
115
+ # do nothing
116
+ end
117
+
118
+ def write_underline!
119
+ # do nothing
120
+ end
121
+ end
122
+ end
data/lib/thermal.rb CHANGED
@@ -1,8 +1,81 @@
1
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
2
-
3
- require 'thermal/version'
4
- require 'thermal/printer'
5
- require 'thermal/parser'
6
-
7
- require 'devices/html'
8
- require 'devices/btpr880'
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'yaml'
5
+ require 'fileutils'
6
+ require 'pathname'
7
+ require 'base64'
8
+
9
+ require 'escpos' # TODO: get rid of this
10
+
11
+ $LOAD_PATH.unshift File.dirname(__FILE__)
12
+ require 'thermal/version'
13
+ require 'thermal/util'
14
+ require 'thermal/writer_base'
15
+ require 'thermal/byte_buffer'
16
+ require 'thermal/db/data'
17
+ require 'thermal/db/loader'
18
+ require 'thermal/db/charset'
19
+ require 'thermal/db/cjk_encoding'
20
+ require 'thermal/db/device'
21
+ require 'thermal/db/encoding'
22
+ require 'thermal/db'
23
+ require 'thermal/escpos/cmd'
24
+ require 'thermal/escpos/buffer'
25
+ require 'thermal/escpos/writer'
26
+ require 'thermal/escpos_star/buffer'
27
+ require 'thermal/escpos_star/writer'
28
+ require 'thermal/stargraphic/capped_byte_buffer'
29
+ # require 'thermal/stargraphic/chunked_byte_buffer'
30
+ require 'thermal/stargraphic/writer'
31
+ require 'thermal/starprnt/buffer'
32
+ require 'thermal/starprnt/writer'
33
+ require 'thermal/profile'
34
+ require 'thermal/printer'
35
+ require 'thermal/dsl'
36
+
37
+ module Thermal
38
+ class << self
39
+ DEFAULT_REPLACE_CHAR = ' '
40
+ DEFAULT_TMP_DIR = 'tmp/thermal'
41
+
42
+ attr_reader :tmp_dir
43
+
44
+ def replace_char
45
+ @replace_char ||= DEFAULT_REPLACE_CHAR
46
+ end
47
+
48
+ def replace_char=(value)
49
+ @replace_char = value || ''
50
+ end
51
+
52
+ def tmp_path(filename)
53
+ raise 'Must set Thermal.tmp_dir' unless tmp_dir || app_root
54
+
55
+ path = tmp_dir ? Pathname.new(tmp_dir) : app_root.join(DEFAULT_TMP_DIR)
56
+ path = path.join(filename)
57
+ FileUtils.mkdir_p(File.dirname(path))
58
+ path
59
+ end
60
+
61
+ def tmp_dir=(value)
62
+ unless value
63
+ @tmp_dir = nil
64
+ return
65
+ end
66
+
67
+ value = Pathname.new(value)
68
+ raise ArgumentError.new('Thermal.tmp_dir must be an absolute path') unless value.absolute?
69
+
70
+ @tmp_dir = value
71
+ end
72
+
73
+ def app_root
74
+ Rails.root if defined?(::Rails)
75
+ end
76
+
77
+ def gem_root
78
+ Pathname.new(File.expand_path('..', __dir__))
79
+ end
80
+ end
81
+ end
metadata CHANGED
@@ -1,113 +1,115 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thermal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
5
- prerelease:
4
+ version: 0.2.0
6
5
  platform: ruby
7
6
  authors:
8
- - Tyler Kellen
9
- autorequire:
7
+ - Johnny Shields
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-12-27 00:00:00.000000000 Z
11
+ date: 2025-03-12 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: rspec
14
+ name: base64
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - ">="
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
- type: :development
20
+ type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
- name: pry
28
+ name: escpos
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - "<"
36
32
  - !ruby/object:Gem::Version
37
- version: '0'
38
- type: :development
33
+ version: 1.0.0
34
+ type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - "<"
44
39
  - !ruby/object:Gem::Version
45
- version: '0'
40
+ version: 1.0.0
46
41
  - !ruby/object:Gem::Dependency
47
- name: hirb
42
+ name: escpos-image
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - "<"
52
46
  - !ruby/object:Gem::Version
53
- version: '0'
54
- type: :development
47
+ version: 1.0.0
48
+ type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - "<"
60
53
  - !ruby/object:Gem::Version
61
- version: '0'
62
- description: ''
63
- email: tyler@sleekcode.net
54
+ version: 1.0.0
55
+ description: Thermal printer support for Ruby. Used to print receipts, chits, tickets,
56
+ labels, etc.
57
+ email:
58
+ - johnny.shields@gmail.com
64
59
  executables: []
65
60
  extensions: []
66
61
  extra_rdoc_files: []
67
62
  files:
68
- - .gitignore
69
- - .rspec
70
- - .travis.yml
71
- - Gemfile
72
63
  - LICENSE
73
64
  - README.md
74
- - Rakefile
75
- - lib/devices/btpr880.rb
76
- - lib/devices/html.rb
65
+ - data/db.yml
66
+ - data/original.yml
77
67
  - lib/thermal.rb
78
- - lib/thermal/parser.rb
68
+ - lib/thermal/byte_buffer.rb
69
+ - lib/thermal/db.rb
70
+ - lib/thermal/db/charset.rb
71
+ - lib/thermal/db/cjk_encoding.rb
72
+ - lib/thermal/db/data.rb
73
+ - lib/thermal/db/device.rb
74
+ - lib/thermal/db/encoding.rb
75
+ - lib/thermal/db/loader.rb
76
+ - lib/thermal/dsl.rb
77
+ - lib/thermal/escpos/buffer.rb
78
+ - lib/thermal/escpos/cmd.rb
79
+ - lib/thermal/escpos/writer.rb
80
+ - lib/thermal/escpos_star/buffer.rb
81
+ - lib/thermal/escpos_star/writer.rb
79
82
  - lib/thermal/printer.rb
83
+ - lib/thermal/profile.rb
84
+ - lib/thermal/stargraphic/capped_byte_buffer.rb
85
+ - lib/thermal/stargraphic/chunked_byte_buffer.rb
86
+ - lib/thermal/stargraphic/writer.rb
87
+ - lib/thermal/starprnt/buffer.rb
88
+ - lib/thermal/starprnt/writer.rb
89
+ - lib/thermal/util.rb
80
90
  - lib/thermal/version.rb
81
- - spec/btpr880_spec.rb
82
- - spec/fixtures/receipt.html
83
- - spec/printer_spec.rb
84
- - spec/spec_helper.rb
85
- - spec/thermal_spec.rb
86
- - tasks/console.rake
87
- - tasks/spec.rake
88
- - thermal.gemspec
89
- homepage: https://github.com/tkellen/ruby-thermal/
91
+ - lib/thermal/writer_base.rb
92
+ homepage: https://github.com/tablecheck/thermal
90
93
  licenses: []
91
- post_install_message:
94
+ metadata:
95
+ rubygems_mfa_required: 'true'
96
+ post_install_message:
92
97
  rdoc_options: []
93
98
  require_paths:
94
99
  - lib
95
100
  required_ruby_version: !ruby/object:Gem::Requirement
96
- none: false
97
101
  requirements:
98
- - - ! '>='
102
+ - - ">="
99
103
  - !ruby/object:Gem::Version
100
- version: '0'
104
+ version: '3.1'
101
105
  required_rubygems_version: !ruby/object:Gem::Requirement
102
- none: false
103
106
  requirements:
104
- - - ! '>='
107
+ - - ">="
105
108
  - !ruby/object:Gem::Version
106
109
  version: '0'
107
110
  requirements: []
108
- rubyforge_project:
109
- rubygems_version: 1.8.24
110
- signing_key:
111
- specification_version: 3
112
- summary: Convert basic HTML into thermal printer escape codes.
111
+ rubygems_version: 3.5.11
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Thermal printer support for Ruby
113
115
  test_files: []
data/.gitignore DELETED
@@ -1,3 +0,0 @@
1
- coverage
2
- *.gem
3
- Gemfile.lock
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --color
2
- --fail-fast
data/.travis.yml DELETED
@@ -1,6 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 1.8.7
4
- - 1.9.2
5
- - 1.9.3
6
- script: rspec
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
data/Rakefile DELETED
@@ -1 +0,0 @@
1
- Dir.glob('tasks/*.rake').each { |r| import r }
@@ -1,33 +0,0 @@
1
- require "socket"
2
-
3
- class BTPR880 < Thermal::Printer
4
-
5
- ESC = 27.chr
6
- GS = 29.chr
7
- CODES = {
8
- :bold => ESC+"E",
9
- :underline => ESC+"-",
10
- :small => ESC+"M",
11
- :invert => GS+"B",
12
- :linebreak => "\n"
13
- }
14
-
15
- TRANSLATE = {
16
- :strong => [CODES[:bold]+"1", CODES[:bold]+"0"],
17
- :u => [CODES[:underline]+"1", CODES[:underline]+"0"],
18
- :small => [CODES[:small]+"1", CODES[:small]+"0"],
19
- :mark => [CODES[:invert]+"1", CODES[:invert]+"0"],
20
- :br => [CODES[:linebreak], ""]
21
- }
22
-
23
- def self.translate
24
- TRANSLATE
25
- end
26
-
27
- def self.print(ip, port=9001, &block)
28
- sock = TCPSocket.new(ip, port)
29
- yield sock if block_given?
30
- sock.close
31
- end
32
-
33
- end
data/lib/devices/html.rb DELETED
@@ -1,14 +0,0 @@
1
- class HTML < Thermal::Printer
2
-
3
- TRANSLATE = {
4
- :strong => ["<strong>", "</strong>"],
5
- :underline => ["<u>", "</u>"],
6
- :mark => ["<mark>", "</mark>"],
7
- :br => ["<br/>"]
8
- }
9
-
10
- def self.translate
11
- TRANSLATE
12
- end
13
-
14
- end
@@ -1,30 +0,0 @@
1
- require 'nokogiri'
2
-
3
- module Thermal
4
- class Parser
5
- attr_accessor :device
6
-
7
- def initialize(device)
8
- @device = device
9
- end
10
-
11
- def process(str)
12
- fragment = Nokogiri::HTML.fragment(str).children
13
- traverse(fragment)
14
- end
15
-
16
- def traverse(fragment)
17
- fragment.map do |node|
18
- output = ""
19
- if node.class == Nokogiri::XML::Text
20
- output << node.content.gsub("\n","")
21
- elsif node.class == Nokogiri::XML::Element
22
- output << @device.startCode(node.name)
23
- output << traverse(node.children)
24
- output << @device.endCode(node.name)
25
- end
26
- output
27
- end.join('')
28
- end
29
- end
30
- end