diff --git a/lib/toon.rb b/lib/toon.rb index fc2969b..135a80e 100644 --- a/lib/toon.rb +++ b/lib/toon.rb @@ -17,17 +17,19 @@ module Toon # @param delimiter [String] Delimiter for array values and tabular rows (default: ',') # @param length_marker [String, false] Optional marker to prefix array lengths (default: false) # @return [String] TOON-formatted string - def encode(input, indent: 2, delimiter: DEFAULT_DELIMITER, length_marker: false) + # @return [nil] if writing to output + def encode(input, indent: 2, delimiter: DEFAULT_DELIMITER, length_marker: false, output: nil) normalized_value = Normalizer.normalize_value(input) - options = resolve_options(indent: indent, delimiter: delimiter, length_marker: length_marker) + options = resolve_options(indent: indent, delimiter: delimiter, length_marker: length_marker, output:) Encoders.encode_value(normalized_value, options) end - def resolve_options(indent:, delimiter:, length_marker:) + def resolve_options(indent:, delimiter:, length_marker:, output:) { indent: indent, delimiter: delimiter, - length_marker: length_marker + length_marker: length_marker, + output: output } end end diff --git a/lib/toon/encoders.rb b/lib/toon/encoders.rb index 672373f..71fdcda 100644 --- a/lib/toon/encoders.rb +++ b/lib/toon/encoders.rb @@ -16,7 +16,7 @@ def encode_value(value, options) return Primitives.encode_primitive(value, options[:delimiter]) end - writer = LineWriter.new(options[:indent]) + writer = LineWriter.new(options[:indent], options[:output]) if Normalizer.json_array?(value) encode_array(nil, value, writer, 0, options) @@ -24,6 +24,8 @@ def encode_value(value, options) encode_object(value, writer, 0, options) end + options[:output] and return + writer.to_s end diff --git a/lib/toon/writer.rb b/lib/toon/writer.rb index 26db038..83d7806 100644 --- a/lib/toon/writer.rb +++ b/lib/toon/writer.rb @@ -1,19 +1,45 @@ # frozen_string_literal: true module Toon + # LineWriter is a utility class for building formatted output lines with + # consistent indentation. + # + # It manages the construction of output strings by handling indentation and + # line termination. + # + # @api private class LineWriter - def initialize(indent_size) - @lines = [] + # Initializes a new LineWriter instance with the specified indentation size + # and optional output target. + # + # @param indent_size [ Integer ] the number of spaces to use for each + # indentation level @param output [ StringIO, nil ] the output target to + # write to, or nil to use a new StringIO instance + def initialize(indent_size, output = nil) + @output = output || StringIO.new @indentation_string = ' ' * indent_size + @started = false end + # Pushes a content line with specified indentation to the output. + # + # @param depth [ Integer ] the indentation depth level + # @param content [ String ] the content to push + # @return [ LineWriter ] returns self to allow method chaining def push(depth, content) indent = @indentation_string * depth - @lines << indent + content + @output << ?\n if @started + @output << indent + content + @started = true + self end + # Returns the string representation of the written content with trailing + # newlines removed. + # + # @return [ String ] the final output string with trailing newline stripped def to_s - @lines.join("\n") + @output.string end end private_constant :LineWriter diff --git a/spec/toon_spec.rb b/spec/toon_spec.rb index 4dfb1de..aefe16e 100644 --- a/spec/toon_spec.rb +++ b/spec/toon_spec.rb @@ -771,4 +771,20 @@ expect(Toon.encode(obj)).to eq('tags[3]: reading,gaming,coding') end end + + describe 'output option' do + let :obj do + { 'hello' => '世界' } + end + + it 'can encode lines to IO interface object' do + output = StringIO.new + expect(Toon.encode(obj, output:)).to be_nil + expect(output.string).to eq "hello: 世界" + end + + it 'can encode with return value' do + expect(Toon.encode(obj)).to eq 'hello: 世界' + end + end end