nestedtext 0.1.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 +7 -0
- data/.editorconfig +24 -0
- data/.gitignore +19 -0
- data/.rubocop.yml +143 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +12 -0
- data/CONTRIBUTING.md +4 -0
- data/Gemfile +31 -0
- data/LICENSE.txt +21 -0
- data/OSSMETADATA +1 -0
- data/README.md +113 -0
- data/SECURITY.md +12 -0
- data/lib/nestedtext/constants.rb +5 -0
- data/lib/nestedtext/core_ext.rb +18 -0
- data/lib/nestedtext/decode.rb +33 -0
- data/lib/nestedtext/dumper.rb +177 -0
- data/lib/nestedtext/encode.rb +35 -0
- data/lib/nestedtext/encode_helpers.rb +24 -0
- data/lib/nestedtext/errors.rb +262 -0
- data/lib/nestedtext/helpers.rb +7 -0
- data/lib/nestedtext/parser.rb +287 -0
- data/lib/nestedtext/scanners.rb +161 -0
- data/lib/nestedtext/version.rb +5 -0
- data/lib/nestedtext.rb +8 -0
- data/nestedtext.gemspec +27 -0
- metadata +77 -0
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
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 [](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
|
+
[](https://badge.fury.io/rb/nestedtext-ruby)
|
3
|
+
[](https://rubygems.org/gems/nestedtext-ruby)
|
4
|
+
[](https://nestedtext.org/en/v3.2/)
|
5
|
+
[](https://github.com/erikw/nestedtext-ruby/actions/workflows/ci.yml)
|
6
|
+
[](https://github.com/KenKundert/nestedtext_tests/tree/585e95a73d94ac1f48e71a154e2db0ab67cf30fa)
|
7
|
+
[](https://github.com/erikw/nestedtext-ruby/actions/workflows/codeql-analysis.yml)
|
8
|
+
[](https://codeclimate.com/github/erikw/nestedtext-ruby/maintainability)
|
9
|
+
[](https://codeclimate.com/github/erikw/nestedtext-ruby/test_coverage)
|
10
|
+
[](#)
|
11
|
+
[](LICENSE.txt)
|
12
|
+
[](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,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
|