nestedtext 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9ab788532b6de8dfb6b589b789afe4d04a6bc7ef9403aaf69f02306680ff4955
4
+ data.tar.gz: 994fe9aae478fb40d191ea96452a0c247b3385dd4e8fe28a323d271feb3e0f11
5
+ SHA512:
6
+ metadata.gz: f6ce566b64bd8874c8b3fae58ab772177249103bf65176cc2ee02e8c74993fa47bd1ed269a9b69c0e6f7a8fecc667fee0e76ec509ef1f1e5608ce8b4d97859ae
7
+ data.tar.gz: 22dab61d1a2cad3eafa3f097c03bbf802747bbe363269161292a663d06d80e61152025de4d6facafba31006f07fb1a5d847775ca483b809b418536d60ce14ecc
data/.editorconfig ADDED
@@ -0,0 +1,24 @@
1
+ # Modified version of https://github.com/ruby/ruby/blob/master/.editorconfig
2
+
3
+ root = true
4
+
5
+ [*]
6
+ end_of_line = lf
7
+ indent_size = 4
8
+ indent_style = space
9
+ insert_final_newline = true
10
+ tab_width = 4
11
+ trim_trailing_whitespace = true
12
+
13
+ [*.rb]
14
+ indent_size = 2
15
+
16
+ [*.gemspec]
17
+ indent_size = 2
18
+
19
+ [*.yml]
20
+ indent_size = 2
21
+
22
+ [test/nestedtext/encode_test.rb]
23
+ # So we can test trailing whitespace strings.
24
+ trim_trailing_whitespace = unset
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ # Contrary to ruby apps, ruby gems should not check in Gemfile.lock.
2
+ # Reference: https://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
3
+ Gemfile.lock
4
+
5
+ # Bundle local config
6
+ .bundle/
7
+
8
+ # Package gem from $(rake install)
9
+ pkg/
10
+
11
+ # minitest
12
+ /test/html_reports/
13
+ /test/reports/
14
+
15
+ # simplecov
16
+ /coverage/
17
+
18
+ # byebug
19
+ /.byebug_history
data/.rubocop.yml ADDED
@@ -0,0 +1,143 @@
1
+ # See pages relaed at https://docs.rubocop.org/rubocop/1.12/cops_layout.html
2
+
3
+
4
+ # TODO clean this upand remove Excludes not needed.
5
+
6
+ # Rubocop extensions
7
+ require:
8
+ - rubocop-rake
9
+
10
+ inherit_mode:
11
+ merge:
12
+ - Exclude # Merge my AllCops.Excllude with default exludes from https://github.com/rubocop/rubocop/blob/master/config/default.yml
13
+
14
+ AllCops:
15
+ TargetRubyVersion: 3.0
16
+ Include:
17
+ - lib/**/*.rb
18
+ - spec/**/*.rb
19
+ Exclude:
20
+ # Travis: during build there will be a lot of rubocop config files in this pat which will cause build failure as the refer to gems which are not installed by this project.
21
+ # See https://github.com/rubocop/rubocop/issues/9832
22
+ - gemfiles/vendor/bundle/**/*
23
+
24
+ Gemspec/DateAssignment:
25
+ Enabled: true
26
+
27
+ Layout/LineEndStringConcatenationIndentation:
28
+ Enabled: true
29
+
30
+ Lint/AmbiguousAssignment:
31
+ Enabled: true
32
+ Lint/DeprecatedConstants:
33
+ Enabled: true
34
+ Lint/DuplicateBranch:
35
+ Enabled: true
36
+ Lint/DuplicateRegexpCharacterClassElement:
37
+ Enabled: true
38
+ Lint/EmptyBlock:
39
+ Enabled: true
40
+ Lint/EmptyClass:
41
+ Enabled: true
42
+ Layout/EmptyLineBetweenDefs:
43
+ Enabled: true
44
+ Exclude:
45
+ - lib/nestedtext-ruby/errors.rb
46
+ Lint/EmptyInPattern:
47
+ Enabled: true
48
+ Lint/LambdaWithoutLiteralBlock:
49
+ Enabled: true
50
+ Layout/LineLength:
51
+ Max: 120
52
+ Lint/NoReturnInBeginEndBlocks:
53
+ Enabled: true
54
+ Lint/NumberedParameterAssignment:
55
+ Enabled: true
56
+ Lint/OrAssignmentToConstant:
57
+ Enabled: true
58
+ Lint/RedundantDirGlobSort:
59
+ Enabled: true
60
+ Layout/SpaceBeforeBrackets:
61
+ Enabled: true
62
+ Lint/SymbolConversion:
63
+ Enabled: true
64
+ Lint/ToEnumArguments:
65
+ Enabled: true
66
+ Lint/TripleQuotes:
67
+ Enabled: true
68
+ Lint/UnexpectedBlockArity:
69
+ Enabled: true
70
+ Lint/UnmodifiedReduceAccumulator:
71
+ Enabled: true
72
+ Lint/UnreachableCode:
73
+ Severity: error
74
+ Lint/UselessAccessModifier:
75
+ Enabled: false
76
+
77
+ Metrics/AbcSize:
78
+ Enabled: true
79
+ Metrics/BlockLength:
80
+ Enabled: true
81
+ Max: 100
82
+ Metrics/MethodLength:
83
+ Enabled: true
84
+ Max: 25
85
+
86
+ Naming/FileName:
87
+ Enabled: false
88
+ Exclude:
89
+ - lib/nestedtext-ruby.rb
90
+ Naming/InclusiveLanguage:
91
+ Enabled: true
92
+
93
+ Style/ArgumentsForwarding:
94
+ Enabled: true
95
+ Style/CollectionCompact:
96
+ Enabled: true
97
+ Style/Documentation:
98
+ Enabled: true
99
+ Exclude:
100
+ - lib/nestedtext-ruby/errors.rb
101
+ Style/DocumentDynamicEvalDefinition:
102
+ Enabled: true
103
+ Style/EndlessMethod:
104
+ Enabled: true
105
+ Style/HashConversion:
106
+ Enabled: true
107
+ Style/HashExcept:
108
+ Enabled: true
109
+ Style/IfWithBooleanLiteralBranches:
110
+ Enabled: true
111
+ Style/InPatternThen:
112
+ Enabled: true
113
+ Style/MultilineInPatternThen:
114
+ Enabled: true
115
+ Style/NegatedIfElseCondition:
116
+ Enabled: true
117
+ Style/NilLambda:
118
+ Enabled: true
119
+ Style/QuotedSymbols:
120
+ Enabled: true
121
+ Style/RedundantArgument:
122
+ Enabled: true
123
+ Style/RegexpLiteral:
124
+ Enabled: false
125
+ Style/SingleLineMethods:
126
+ Enabled: true
127
+ Exclude:
128
+ - lib/nestedtext-ruby/errors.rb
129
+ Style/StringChars:
130
+ Enabled: true
131
+ Style/StringLiterals:
132
+ Enabled: true
133
+ EnforcedStyle: double_quotes
134
+ Style/StringLiteralsInInterpolation:
135
+ Enabled: true
136
+ EnforcedStyle: double_quotes
137
+ Style/SwapValues:
138
+ Enabled: true
139
+
140
+
141
+ # Reference: https://github.com/rubocop/rubocop-rake/blob/master/config/default.yml
142
+ Rake:
143
+ Enabled: true
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.1.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+
10
+ ## [0.1.0] - 2022-01-24
11
+ ### Added
12
+ - Initial release. If this release works, an 1.0.0 will soon follow.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,4 @@
1
+ # How to contribute
2
+ Please use GitHub tooling (issues, PRs) to disucssion and code contributions!
3
+
4
+ When you open an PR, Travis will build your code, run tests, liters and so on.
data/Gemfile ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Put require=false on gem's that we don't need to import in code (cli exec only)
4
+
5
+ source "https://rubygems.org"
6
+
7
+ # Include dependencies from the .gemspec
8
+ gemspec
9
+
10
+ # Development dependencies
11
+ # Should rather be here than in the .gemspec
12
+ # Reference: https://github.com/rubygems/bundler/pull/7222
13
+ # However there's an argument for using gemspec too: https://bundler.io/guides/creating_gem.html#testing-our-gem
14
+ group :development, :test do
15
+ gem "appraisal", "~> 2.4", require: false
16
+ gem "gem-release", "~> 2.0", require: false
17
+ gem "pry-byebug", "~> 3.9"
18
+ gem "rake", "~> 13.0", require: false
19
+ gem "solargraph", require: false
20
+ end
21
+
22
+ group :test do
23
+ gem "minitest-byebug", "~> 0.0.3"
24
+ gem "minitest-reporters", "~> 1.4", require: false
25
+ gem "rubocop", "~> 1.18", require: false
26
+ gem "rubocop-rake", "~> 0.6", require: false
27
+ gem "simplecov", "~> 0.21", require: false
28
+ end
29
+
30
+ gem "warning", "~> 1.2"
31
+ gem "word_wrap", "~> 1.0"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Erik Westrup
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/OSSMETADATA ADDED
@@ -0,0 +1 @@
1
+ osslifecycle=active
data/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # NestedText Ruby Library [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Get%20a%20nifty%20tooltip%20for%20term%20definitions%20in%20your%20Jekyll%20blog%20with%20this%20plugin&url=https://github.com/erikw/nestedtext-ruby&via=erik_westrup&hashtags=jekyll,plugin)
2
+ [![Gem Version](https://badge.fury.io/rb/nestedtext-ruby.svg)](https://badge.fury.io/rb/nestedtext-ruby)
3
+ [![Gem Downloads](https://ruby-gem-downloads-badge.herokuapp.com/nestedtext-ruby?color=brightgreen&type=total&label=gem%20downloads)](https://rubygems.org/gems/nestedtext-ruby)
4
+ [![Data Format Version Supported](https://img.shields.io/badge/%F0%9F%84%BD%F0%9F%85%83%20Version%20Supported-3.2.1-blueviolet)](https://nestedtext.org/en/v3.2/)
5
+ [![Tests](https://github.com/erikw/nestedtext-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/erikw/nestedtext-ruby/actions/workflows/ci.yml)
6
+ [![Official Tests](https://img.shields.io/badge/%F0%9F%8F%81%20Official%20Tests-Passing-success)](https://github.com/KenKundert/nestedtext_tests/tree/585e95a73d94ac1f48e71a154e2db0ab67cf30fa)
7
+ [![CodeQL](https://github.com/erikw/nestedtext-ruby/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/erikw/nestedtext-ruby/actions/workflows/codeql-analysis.yml)
8
+ [![Code Climate Maintainability](https://api.codeclimate.com/v1/badges/7ffb648ec4b77f3f9eb8/maintainability)](https://codeclimate.com/github/erikw/nestedtext-ruby/maintainability)
9
+ [![Code Climate Test Coverage](https://api.codeclimate.com/v1/badges/7ffb648ec4b77f3f9eb8/test_coverage)](https://codeclimate.com/github/erikw/nestedtext-ruby/test_coverage)
10
+ [![SLOC](https://img.shields.io/tokei/lines/github/erikw/nestedtext-ruby)](#)
11
+ [![License](https://img.shields.io/github/license/erikw/nestedtext-ruby)](LICENSE.txt)
12
+ [![OSS Lifecycle](https://img.shields.io/osslifecycle/erikw/nestedtext-ruby)](https://github.com/Netflix/osstracker)
13
+
14
+
15
+ Inspired by the `JSON` and `YAML` modules.
16
+
17
+ This project will soon be released! :tada:
18
+
19
+ On-going development is at branch [**dev**](https://github.com/erikw/nestedtext-ruby/tree/dev).
20
+
21
+ # What is NestedText?
22
+ TODO
23
+
24
+ ## Examples
25
+ TODO NT examples
26
+
27
+ # Usage
28
+ TODO Link to lib docs
29
+ TODO link to my test repo showin live usage.
30
+
31
+
32
+ # Usage
33
+ ## Decoding (reading NT)
34
+
35
+ ## Encoding (writing NT)
36
+
37
+ ## Custom Classes Serialization
38
+ This library has support for serialization/deserialization of custom classes as well.
39
+ `strict: false` flag needed
40
+ See [encode_custom_classes_test.rb](test/nestedtext/encode_custom_classes_test.rb) for more real working examples.
41
+
42
+
43
+ # Installation
44
+ 1. Add this gem to your ruby project's Gemfile
45
+ - Simply with `$ bundle add nestedtext` when standing in the project root
46
+ - Or manually by adding to `Gemfile`
47
+ ```ruby
48
+ gem 'nestedtext'
49
+ ```
50
+ and then running `$ bundle install`.
51
+ ```
52
+ 1. Require the library and start using it!
53
+ ```ruby
54
+ require 'nestedtext'
55
+
56
+ NestedText::load(...)
57
+ NestedText::dump(...)
58
+ obj.to_nt
59
+ ```
60
+
61
+
62
+
63
+ # Development
64
+
65
+ 1. Clone the repo
66
+ ```console
67
+ $ git clone https://github.com/erikw/nestedtext-ruby.git && cd $(basename "$_" .git)
68
+ ```
69
+ 1. Install a supported ruby version (see .gemspec) with a ruby version manager e.g. [rbenv](https://github.com/rbenv/rbenv), [asdf](http://asdf-vm.com/) or [RVM](https://rvm.io/rvm/install)
70
+ 1. run `$ script/setup` to install dependencies
71
+ 1. run `$ script/test` to run the tests
72
+ 1. You can also run `$ script/console` for an interactive prompt that will allow you to experiment.
73
+
74
+ To install this gem onto your local machine, run `$ bundle exec rake install`.
75
+
76
+ ## Releasing
77
+ Instructions for releasing on rubygems.org below. Optionally make a GitHub [release](https://github.com/erikw/nestedtext-ruby/releases) after this for the pushed git tag.
78
+
79
+ ## (manually) Using bundler/gem_tasks rake tasks
80
+ Following instructions from [bundler.io](https://bundler.io/guides/creating_gem.html#releasing-the-gem):
81
+ ```console
82
+ $ vi -p lib/nestedtext/version.rb CHANGELOG.md
83
+ $ bundle exec rake build
84
+ $ ver=$(ruby -r ./lib/nestedtext/version.rb -e 'puts NestedText::VERSION')
85
+ $ bundle exec rake release
86
+ ```
87
+
88
+ ## (semi-manually) Using gem-release gem extension
89
+ Using [gem-release](https://github.com/svenfuchs/gem-release):
90
+ ```console
91
+ $ vi CHANGELOG.md && git add CHANGELOG.md && git commit -m "Update CHANGELOG.md" && git push
92
+ $ gem bump --version minor --tag --sign --push --release
93
+ ```
94
+ For `--version`, use `major|minor|patch` as needed.
95
+
96
+ ## (semi-automatic) Using GitHub Actions CD
97
+ Just push a new semver tag and the workflow [cd.yml](.github/workflows/cd.yml) will publish a new release at rubygems.org.
98
+
99
+ ```console
100
+ $ vi -p lib/nestedtext/version.rb CHANGELOG.md
101
+ $ git commit -am "Prepare vX.Y.Z" && git push
102
+ $ git tag x.y.z && git push --tags
103
+ ```
104
+
105
+
106
+ # Contributing
107
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/erikw/nestedtext-ruby](https://github.com/erikw/nestedtext-ruby).
108
+
109
+ # License
110
+ The gem is available as open source with the [License](./LICENSE.txt).
111
+
112
+ # Acknowledgement & Thanks
113
+ Thanks to the data format authors making it easier making new implementations by providing an [official test suite](https://github.com/KenKundert/nestedtext_tests).
data/SECURITY.md ADDED
@@ -0,0 +1,12 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ | Version | Supported |
6
+ | ------- | ------------------ |
7
+ | 1.x.x | :white_check_mark: |
8
+ | < 1.0 | :x: |
9
+
10
+
11
+ ## Reporting a Vulnerability
12
+ Please open a GitHub Issue in this repository for any potential issues!
@@ -0,0 +1,5 @@
1
+ require "stringio"
2
+ module NestedText
3
+ TOP_LEVEL_TYPES = [Object, Hash, Array, String]
4
+ CUSTOM_CLASS_KEY = "__nestedtext_class__"
5
+ end
@@ -0,0 +1,18 @@
1
+ require "nestedtext/encode_helpers"
2
+
3
+ # TODO: add encoding of more Ruby native classes like Integer, Float etc plus commons like Set,....? Not covered in NestedText language.
4
+ # Or leave this to a schema validator 3rd party plugin maybe? And replace my custom class decoding (and also encoding?)?
5
+ # Or both: add encoding/decoding of more native classes, and allow decoding + applying a schema with 3rd party.
6
+ # Or encourage using Marshal from core?
7
+
8
+ class String include NestedText::NTEncodeStrictMixing end
9
+ class Array include NestedText::NTEncodeStrictMixing end
10
+ class Hash include NestedText::NTEncodeStrictMixing end
11
+
12
+ class NilClass
13
+ include NestedText::NTEncodeStrictMixing
14
+
15
+ def self.nt_create(_data) = nil
16
+
17
+ def encode_nt_with() = ""
18
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nestedtext/parser"
4
+ require "nestedtext/errors"
5
+ require "nestedtext/helpers"
6
+
7
+ require "logger"
8
+ require "stringio"
9
+
10
+ module NestedText
11
+ def self.load(ntstring, top_class: Object, strict: true)
12
+ # logger = Logger.new(STDOUT) # TODO: make this available to other classes in module. How avoid singleton?
13
+ # logger.info "input=#{raw_input_string}"
14
+ # logger.info "top=#{top}"
15
+
16
+ raise Errors::WrongInputTypeError.new([String], ntstring) unless ntstring.nil? || ntstring.is_a?(String)
17
+
18
+ assert_valid_top_level_type top_class
19
+
20
+ Parser.new(StringIO.new(ntstring), top_class, strict: strict).parse
21
+ end
22
+
23
+ def self.load_file(filename, top_class: Object, strict: true)
24
+ raise Errors::WrongInputTypeError.new([String], filename) unless !filename.nil? && filename.is_a?(String)
25
+
26
+ assert_valid_top_level_type top_class
27
+
28
+ # Open explicitly in text mode to detect \r as line ending.
29
+ File.open(filename, mode = "rt") do |file|
30
+ Parser.new(file, top_class, strict: strict).parse
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,177 @@
1
+ # TODO: is this exposed to client who import this file? If so, hide it!
2
+ class String
3
+ def normalize_line_endings
4
+ # windows/mac -> unix
5
+ gsub(/\r\n?/, "\n")
6
+ end
7
+ end
8
+
9
+ module NestedText
10
+ class Dumper
11
+ def initialize(opts = EncodeOptions.new)
12
+ @indentation = opts.indentation
13
+ @strict = opts.strict
14
+ @trace_cycles = nil
15
+ @trace_keys = nil
16
+ end
17
+
18
+ def dump(obj)
19
+ @trace_cycles = []
20
+ @trace_keys = []
21
+ dump_any obj
22
+ end
23
+
24
+ private
25
+
26
+ def self.add_prefix(prefix, target)
27
+ if target.empty? || target[0] == "\n"
28
+ target.prepend(prefix)
29
+ else
30
+ target.prepend(prefix, " ")
31
+ end
32
+ end
33
+
34
+ def self.multiline_key?(key)
35
+ syntax1 = "{[#"
36
+ syntax2 = ":->"
37
+
38
+ key.empty? ||
39
+ key != key.strip ||
40
+ key.include?("\n") ||
41
+ key.include?(": ") ||
42
+ syntax1.include?(key.lstrip[0]) ||
43
+ syntax2.include?(key.lstrip[0]) && key.lstrip[1] == " "
44
+ end
45
+
46
+ def convert_key(key)
47
+ if key.nil?
48
+ ""
49
+ elsif key.is_a? String
50
+ key.normalize_line_endings
51
+ elsif !@strict
52
+ key.to_s
53
+ else
54
+ raise Errors::DumpHashKeyStrictString, key
55
+ end
56
+ end
57
+
58
+ def indent(target)
59
+ indentstr = " " * @indentation
60
+ indented = "\n" + target.lines.map { |line| indentstr + line }.join
61
+ target.replace indented
62
+ end
63
+
64
+ # TODO: different name on method and instance var...
65
+ def trace_cycles(obj)
66
+ raise Errors::DumpCyclicReferencesDetected, traced_key if @trace_cycles.include?(obj)
67
+
68
+ @trace_cycles << obj
69
+ yield
70
+ ensure
71
+ @trace_cycles.pop
72
+ end
73
+
74
+ # TODO: different name on method and instance var...
75
+ def trace_keys(key)
76
+ @trace_keys << key
77
+ yield
78
+ ensure
79
+ @trace_keys.pop
80
+ end
81
+
82
+ def traced_key
83
+ @trace_keys.last
84
+ end
85
+
86
+ def dump_any(obj, depth: 0, **kwargs)
87
+ trace_cycles(obj) do
88
+ case obj
89
+ when Hash then dump_hash(obj, depth: depth, **kwargs)
90
+ when Array then dump_array(obj, depth: depth, **kwargs)
91
+ when String then dump_string(obj, depth: depth, **kwargs)
92
+ when nil
93
+ @strict ? "" : dump_custom_class(nil, depth: depth, **kwargs)
94
+ else
95
+ dump_custom_class(obj, depth: depth, **kwargs)
96
+ end
97
+ end
98
+ end
99
+
100
+ # TODO: document that @strict: false allows to_s on key object
101
+ def dump_hash(obj, depth: 0, **kwargs)
102
+ rep = if obj.empty?
103
+ "{}"
104
+ else
105
+ obj.map do |key, value|
106
+ trace_keys(key) do
107
+ key = convert_key(key)
108
+
109
+ if Dumper.multiline_key?(key)
110
+ key_lines = key.empty? ? [""] : key.lines
111
+ key_lines << "" if key_lines[-1][-1] =~ /\n|\r/
112
+ rep_key = key_lines.map { |line| Dumper.add_prefix(":", line) }.join
113
+ force_multiline = value.is_a? String
114
+ rep_value = dump_any(value, depth: depth + 1, force_multiline: force_multiline, **kwargs)
115
+ else
116
+ rep_key = "#{key}:"
117
+ rep_value = dump_any(value, depth: depth + 1, **kwargs)
118
+ rep_key += " " unless rep_value.empty? || rep_value.include?("\n")
119
+ end
120
+ "#{rep_key}#{rep_value}"
121
+ end
122
+ end.join("\n")
123
+ end
124
+ indent(rep) if depth > 0
125
+ rep
126
+ end
127
+
128
+ def dump_array(obj, depth: 0, **kwargs)
129
+ rep = if obj.empty?
130
+ # TODO: replace this special case with simply general inline rendering detection.
131
+ "[]"
132
+ else
133
+ obj.each_with_index.map do |e, i|
134
+ trace_keys(i) do
135
+ e_rep = dump_any(e, depth: depth + 1, **kwargs)
136
+ Dumper.add_prefix("-", e_rep)
137
+ end
138
+ end.join("\n")
139
+ end
140
+
141
+ indent(rep) if depth > 0
142
+ rep
143
+ end
144
+
145
+ def dump_string(obj, depth: 0, force_multiline: false)
146
+ obj = obj.normalize_line_endings
147
+ lines = obj.lines
148
+ lines << "\n" if !lines.empty? && lines[-1][-1] == "\n"
149
+ if lines.length > 1 || depth == 0 || force_multiline
150
+ lines.each do |line|
151
+ Dumper.add_prefix(">", line)
152
+ end
153
+ end
154
+
155
+ # Case of empty input string. No space after '>'
156
+ lines << ">" if lines.empty? && (depth == 0 || force_multiline)
157
+
158
+ rep = lines.join.chomp
159
+ indent(rep) if !rep.empty? && depth > 0 && (rep.include?("\n") || force_multiline)
160
+ rep
161
+ end
162
+
163
+ def dump_custom_class(obj, **kwargs)
164
+ raise Errors::DumpUnsupportedTypeError.new(obj, traced_key) if @strict
165
+
166
+ if obj.is_a? Symbol
167
+ dump_string(obj.id2name, **kwargs)
168
+ elsif obj.respond_to? :encode_nt_with
169
+ class_name = obj.nil? ? "nil" : obj.class.name
170
+ enc = { CUSTOM_CLASS_KEY => class_name, "data" => obj.encode_nt_with }
171
+ dump_any(enc, **kwargs)
172
+ else
173
+ dump_string(obj.to_s, **kwargs)
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nestedtext/errors"
4
+ require "nestedtext/encode_helpers"
5
+ require "nestedtext/dumper"
6
+
7
+ # Model after JSON
8
+ # NestedText.dump(obj, io=nil) => dumps to string, or to IO if given
9
+ # NestedText.dump_file(obj, filename)
10
+
11
+ module NestedText
12
+ # TODO: strict should maybe be false by default, as this is what ntpy does. If so, make the same for the load functions.
13
+ def self.dump(obj, io: nil, indentation: 4, strict: true)
14
+ # io - additionaly write the out result to IO and still return result.
15
+
16
+ raise Errors::DumpBadIO, io unless io.nil? || io.respond_to?(:write) && io.respond_to?(:fsync)
17
+
18
+ opts = EncodeOptions.new(indentation, strict)
19
+ dumper = Dumper.new(opts)
20
+ result = dumper.dump obj
21
+ unless io.nil?
22
+ io.write(result)
23
+ io.fsync
24
+ end
25
+ result
26
+ end
27
+
28
+ def self.dump_file(obj, filename, **kwargs)
29
+ raise Errors::DumpFileBadPath, filename unless filename.is_a? String
30
+
31
+ File.open(filename, mode = "wt") do |file|
32
+ dump(obj, io: file, **kwargs)
33
+ end
34
+ end
35
+ end