nestedtext 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![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,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
|