dachsfisch 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: 4bddbcd258c840e0622575978329494dc4a597b6903a24fa178d927697f64a6f
4
+ data.tar.gz: 42d8a736a7c7fe52749ab10a2d11341635d4709d1f6e6fd96f39b67d01eab649
5
+ SHA512:
6
+ metadata.gz: a1a06034a4aba42b83dc18d43e964421c971e0b881b326075fb5760499fb97cc81c73f534154a008d2e2eee47c71badacff20f455c3980212fea58e1e6fb8c6f
7
+ data.tar.gz: '09f10e321376aa8480b97d3763443a3e10ba1e8024256e546aaa194f178bb4ac5acabc418e46da7f2af382b40a977af61e9ed50256f78f0fe4ed66122063ec1c'
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format progress
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,39 @@
1
+ # layout cop settings
2
+
3
+ Layout/ArgumentAlignment:
4
+ EnforcedStyle: with_fixed_indentation
5
+
6
+ Layout/CaseIndentation:
7
+ EnforcedStyle: end
8
+ SupportedStyles:
9
+ - case
10
+ - end
11
+ IndentOneStep: true
12
+
13
+ Layout/FirstArrayElementIndentation:
14
+ EnforcedStyle: consistent
15
+
16
+ Layout/FirstHashElementIndentation:
17
+ EnforcedStyle: consistent
18
+
19
+ #
20
+ # There are good reasons for key as well as table style.
21
+ #
22
+ Layout/HashAlignment:
23
+ Enabled: false
24
+
25
+ Layout/LineLength:
26
+ Exclude:
27
+ - "spec/**/*"
28
+ Max: 140
29
+
30
+ Layout/MultilineMethodCallIndentation:
31
+ EnforcedStyle: indented
32
+
33
+ Layout/SpaceInsideBlockBraces:
34
+ EnforcedStyle: space
35
+ EnforcedStyleForEmptyBraces: no_space
36
+ SpaceBeforeBlockParameters: false
37
+
38
+ Layout/SpaceInsideHashLiteralBraces:
39
+ EnforcedStyle: no_space
data/.rubocop/lint.yml ADDED
@@ -0,0 +1,13 @@
1
+ # lint cop settings
2
+
3
+ #
4
+ # False positives:
5
+ # * expect { something }.to change { something } often triggers this
6
+ #
7
+ Lint/AmbiguousBlockAssociation:
8
+ Exclude:
9
+ - "spec/**/*_spec.rb"
10
+
11
+ Lint/EmptyBlock:
12
+ Exclude:
13
+ - 'spec/**/*'
@@ -0,0 +1,31 @@
1
+ # metric cop settings
2
+
3
+ Metrics/BlockLength:
4
+ Exclude:
5
+ # Common files with e.g. block based DSLs
6
+ - "spec/**/*"
7
+ - "**/*.rake"
8
+ - "Rakefile"
9
+ - "Guardfile"
10
+ - "**/*/Rakefile"
11
+ - 'dachsfisch.gemspec'
12
+ Max: 50
13
+
14
+ Metrics/ClassLength:
15
+ Max: 150
16
+
17
+ #
18
+ # Often used as a proxy for complexity in a method, but causes many false
19
+ # positives, e.g. when generating large, but simple, hashes.
20
+ # We want to rely on CyclomaticComplexity instead.
21
+ #
22
+ Metrics/MethodLength:
23
+ Enabled: true
24
+ Max: 20
25
+
26
+ #
27
+ # This seems to be the cop that is closest to what we're interested in, which
28
+ # is the kind of complexity that usually surfaces in deep nesting.
29
+ #
30
+ Metrics/CyclomaticComplexity:
31
+ Enabled: true
@@ -0,0 +1,4 @@
1
+ # rails cop settings
2
+
3
+ Rails:
4
+ Enabled: true
@@ -0,0 +1,24 @@
1
+ # rspec cop settings
2
+
3
+ RSpec:
4
+ Include:
5
+ - "spec/**/*_spec.rb"
6
+ - "spec/spec_helper.rb"
7
+ - "spec/dachsfisch_spec.rb"
8
+
9
+ #
10
+ # Too stupid. There are also views, templates, request specs etc.
11
+ #
12
+ RSpec/DescribeClass:
13
+ Exclude:
14
+ - "spec/custom_matchers/*"
15
+
16
+ RSpec/MultipleExpectations:
17
+ Exclude:
18
+ - "spec/custom_matchers/*"
19
+
20
+ RSpec/MultipleMemoizedHelpers:
21
+ Enabled: false
22
+
23
+ RSpec/NestedGroups:
24
+ Max: 5
@@ -0,0 +1,74 @@
1
+ # style cop settings
2
+
3
+ #
4
+ # Nein. Period. Try to keep it English, but there *will* references using
5
+ # unicode characters.
6
+ #
7
+ Style/AsciiComments:
8
+ Enabled: false
9
+
10
+ #
11
+ # Both styles or mixtures are reasonable
12
+ #
13
+ Style/ClassAndModuleChildren:
14
+ EnforcedStyle: compact
15
+ Enabled: false
16
+
17
+ #
18
+ # Maybe a bit uncommon for new devs and often results in heavily indented code
19
+ # blocks.
20
+ #
21
+ Style/ConditionalAssignment:
22
+ Enabled: false
23
+
24
+ #
25
+ # Would be better but unlikely...
26
+ #
27
+ Style/Documentation:
28
+ Enabled: false
29
+
30
+ #
31
+ # Okay for conditions, but false positive in return statements (e.g. APIs)
32
+ #
33
+ Style/DoubleNegation:
34
+ Enabled: false
35
+
36
+ #
37
+ # Our default string token has the '%{value}' format
38
+ #
39
+ Style/FormatStringToken:
40
+ EnforcedStyle: template
41
+
42
+ #
43
+ # Far to often easy to read without.
44
+ #
45
+ Style/GuardClause:
46
+ Enabled: false
47
+
48
+ #
49
+ # IfUnlessModifier has no own line length but we do not want it to force 120
50
+ # chars long modifiers just because we allow a few long lines.
51
+ #
52
+ Style/IfUnlessModifier:
53
+ Enabled: false
54
+
55
+ #
56
+ # Well, we do this. To often to disable them. Studid.
57
+ #
58
+ Style/MultilineBlockChain:
59
+ Enabled: false
60
+
61
+ #Style/NumericPredicate:
62
+ # Enabled: false
63
+
64
+ Style/RaiseArgs:
65
+ EnforcedStyle: compact
66
+
67
+ Style/SignalException:
68
+ EnforcedStyle: only_raise
69
+
70
+ Style/TrailingCommaInArrayLiteral:
71
+ EnforcedStyleForMultiline: comma
72
+
73
+ Style/TrailingCommaInHashLiteral:
74
+ EnforcedStyleForMultiline: comma
data/.rubocop.yml ADDED
@@ -0,0 +1,22 @@
1
+ require:
2
+ - rubocop-performance
3
+ - rubocop-rspec
4
+
5
+ inherit_from:
6
+ - .rubocop/layout.yml
7
+ - .rubocop/lint.yml
8
+ - .rubocop/metrics.yml
9
+ - .rubocop/rspec.yml
10
+ - .rubocop/style.yml
11
+
12
+ AllCops:
13
+ TargetRubyVersion: 3.2
14
+ UseCache: True
15
+ NewCops: enable
16
+ Exclude:
17
+ - 'bin/*'
18
+ - 'vendor/**/*'
19
+ # Ignore local files for faster processing
20
+ - 'tmp/**/*'
21
+ - 'out/**/*'
22
+ - 'coverage/**/*'
@@ -0,0 +1,128 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, religion, or sexual identity
10
+ and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ * Demonstrating empathy and kindness toward other people
21
+ * Being respectful of differing opinions, viewpoints, and experiences
22
+ * Giving and gracefully accepting constructive feedback
23
+ * Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ * Focusing on what is best not just for us as individuals, but for the
26
+ overall community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ * The use of sexualized language or imagery, and sexual attention or
31
+ advances of any kind
32
+ * Trolling, insulting or derogatory comments, and personal or political attacks
33
+ * Public or private harassment
34
+ * Publishing others' private information, such as a physical or email
35
+ address, without their explicit permission
36
+ * Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ## Scope
52
+
53
+ This Code of Conduct applies within all community spaces, and also applies when
54
+ an individual is officially representing the community in public spaces.
55
+ Examples of representing our community include using an official e-mail address,
56
+ posting via an official social media account, or acting as an appointed
57
+ representative at an online or offline event.
58
+
59
+ ## Enforcement
60
+
61
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+ reported to the community leaders responsible for enforcement at
63
+ sebastian.serth@hpi.de.
64
+ All complaints will be reviewed and investigated promptly and fairly.
65
+
66
+ All community leaders are obligated to respect the privacy and security of the
67
+ reporter of any incident.
68
+
69
+ ## Enforcement Guidelines
70
+
71
+ Community leaders will follow these Community Impact Guidelines in determining
72
+ the consequences for any action they deem in violation of this Code of Conduct:
73
+
74
+ ### 1. Correction
75
+
76
+ **Community Impact**: Use of inappropriate language or other behavior deemed
77
+ unprofessional or unwelcome in the community.
78
+
79
+ **Consequence**: A private, written warning from community leaders, providing
80
+ clarity around the nature of the violation and an explanation of why the
81
+ behavior was inappropriate. A public apology may be requested.
82
+
83
+ ### 2. Warning
84
+
85
+ **Community Impact**: A violation through a single incident or series
86
+ of actions.
87
+
88
+ **Consequence**: A warning with consequences for continued behavior. No
89
+ interaction with the people involved, including unsolicited interaction with
90
+ those enforcing the Code of Conduct, for a specified period of time. This
91
+ includes avoiding interactions in community spaces as well as external channels
92
+ like social media. Violating these terms may lead to a temporary or
93
+ permanent ban.
94
+
95
+ ### 3. Temporary Ban
96
+
97
+ **Community Impact**: A serious violation of community standards, including
98
+ sustained inappropriate behavior.
99
+
100
+ **Consequence**: A temporary ban from any sort of interaction or public
101
+ communication with the community for a specified period of time. No public or
102
+ private interaction with the people involved, including unsolicited interaction
103
+ with those enforcing the Code of Conduct, is allowed during this period.
104
+ Violating these terms may lead to a permanent ban.
105
+
106
+ ### 4. Permanent Ban
107
+
108
+ **Community Impact**: Demonstrating a pattern of violation of community
109
+ standards, including sustained inappropriate behavior, harassment of an
110
+ individual, or aggression toward or disparagement of classes of individuals.
111
+
112
+ **Consequence**: A permanent ban from any sort of public interaction within
113
+ the community.
114
+
115
+ ## Attribution
116
+
117
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
+ version 2.0, available at
119
+ https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120
+
121
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct
122
+ enforcement ladder](https://github.com/mozilla/diversity).
123
+
124
+ [homepage]: https://www.contributor-covenant.org
125
+
126
+ For answers to common questions about this code of conduct, see the FAQ at
127
+ https://www.contributor-covenant.org/faq. Translations are available at
128
+ https://www.contributor-covenant.org/translations.
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in dachsfisch.gemspec
6
+ gemspec
7
+
8
+ gem 'equivalent-xml'
9
+ gem 'pry'
10
+ gem 'rake'
11
+ gem 'rspec'
12
+ gem 'rspec-github'
13
+ gem 'rubocop'
14
+ gem 'rubocop-performance'
15
+ gem 'rubocop-rspec'
16
+ gem 'simplecov'
data/Gemfile.lock ADDED
@@ -0,0 +1,101 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ dachsfisch (0.1.0)
5
+ nokogiri (>= 1.14.1, < 2.0.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ base64 (0.1.1)
12
+ coderay (1.1.3)
13
+ diff-lcs (1.5.0)
14
+ docile (1.4.0)
15
+ equivalent-xml (0.6.0)
16
+ nokogiri (>= 1.4.3)
17
+ json (2.6.3)
18
+ language_server-protocol (3.17.0.3)
19
+ method_source (1.0.0)
20
+ mini_portile2 (2.8.4)
21
+ nokogiri (1.15.4)
22
+ mini_portile2 (~> 2.8.2)
23
+ racc (~> 1.4)
24
+ parallel (1.23.0)
25
+ parser (3.2.2.3)
26
+ ast (~> 2.4.1)
27
+ racc
28
+ pry (0.14.2)
29
+ coderay (~> 1.1)
30
+ method_source (~> 1.0)
31
+ racc (1.7.1)
32
+ rainbow (3.1.1)
33
+ rake (13.0.6)
34
+ regexp_parser (2.8.1)
35
+ rexml (3.2.6)
36
+ rspec (3.12.0)
37
+ rspec-core (~> 3.12.0)
38
+ rspec-expectations (~> 3.12.0)
39
+ rspec-mocks (~> 3.12.0)
40
+ rspec-core (3.12.2)
41
+ rspec-support (~> 3.12.0)
42
+ rspec-expectations (3.12.3)
43
+ diff-lcs (>= 1.2.0, < 2.0)
44
+ rspec-support (~> 3.12.0)
45
+ rspec-github (2.4.0)
46
+ rspec-core (~> 3.0)
47
+ rspec-mocks (3.12.6)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.12.0)
50
+ rspec-support (3.12.1)
51
+ rubocop (1.56.0)
52
+ base64 (~> 0.1.1)
53
+ json (~> 2.3)
54
+ language_server-protocol (>= 3.17.0)
55
+ parallel (~> 1.10)
56
+ parser (>= 3.2.2.3)
57
+ rainbow (>= 2.2.2, < 4.0)
58
+ regexp_parser (>= 1.8, < 3.0)
59
+ rexml (>= 3.2.5, < 4.0)
60
+ rubocop-ast (>= 1.28.1, < 2.0)
61
+ ruby-progressbar (~> 1.7)
62
+ unicode-display_width (>= 2.4.0, < 3.0)
63
+ rubocop-ast (1.29.0)
64
+ parser (>= 3.2.1.0)
65
+ rubocop-capybara (2.18.0)
66
+ rubocop (~> 1.41)
67
+ rubocop-factory_bot (2.23.1)
68
+ rubocop (~> 1.33)
69
+ rubocop-performance (1.19.0)
70
+ rubocop (>= 1.7.0, < 2.0)
71
+ rubocop-ast (>= 0.4.0)
72
+ rubocop-rspec (2.23.2)
73
+ rubocop (~> 1.33)
74
+ rubocop-capybara (~> 2.17)
75
+ rubocop-factory_bot (~> 2.22)
76
+ ruby-progressbar (1.13.0)
77
+ simplecov (0.22.0)
78
+ docile (~> 1.1)
79
+ simplecov-html (~> 0.11)
80
+ simplecov_json_formatter (~> 0.1)
81
+ simplecov-html (0.12.3)
82
+ simplecov_json_formatter (0.1.4)
83
+ unicode-display_width (2.4.2)
84
+
85
+ PLATFORMS
86
+ ruby
87
+
88
+ DEPENDENCIES
89
+ dachsfisch!
90
+ equivalent-xml
91
+ pry
92
+ rake
93
+ rspec
94
+ rspec-github
95
+ rubocop
96
+ rubocop-performance
97
+ rubocop-rspec
98
+ simplecov
99
+
100
+ BUNDLED WITH
101
+ 2.4.19
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Karol
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/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # Dachsfisch
2
+
3
+ [![Build Status](https://github.com/openHPI/dachsfisch/workflows/CI/badge.svg)](https://github.com/openHPI/dachsfisch/actions?query=workflow%3ACI)
4
+ [![codecov](https://codecov.io/gh/openHPI/dachsfisch/branch/main/graph/badge.svg?token=K267SFJO7S)](https://codecov.io/gh/openHPI/dachsfisch)
5
+
6
+ This gem offers a Ruby implementation of the [BadgerFish standard](http://www.sklar.com/badgerfish/), a set of rules for converting documents between the XML and JSON format. This gem supports conversion in both directions, specifically XML-to-JSON and JSON-to-XML.
7
+
8
+ The rules for converting XML documents to JSON using Badgerfish are:
9
+
10
+ - Element names become object properties.
11
+ - Text content of elements goes in the `$` property of an object.
12
+ - Nested elements become nested properties.
13
+ - Multiple elements at the same level become array elements.
14
+ - Attributes go in properties whose names begin with `@`.
15
+ - Active namespaces for an element go in the element's `@xmlns` property.
16
+ - The default namespace URI goes in `@xmlns.$`.
17
+ - Other namespaces go in other properties of `@xmlns`.
18
+ - Elements with namespace prefixes become object properties, too.
19
+ - The `@xmlns` property goes only in objects relative to the tag where namespace was declared.
20
+ - Text fragments in mixed contents (tags and text) become properties named `$1`, `$2`, etc.
21
+ - Comment tags, similar to text fragments, become properties named `!1`, `!2`, etc.
22
+ - CDATA sections become properties named `#1`, `#2`, etc.
23
+
24
+ Please see our [examples](spec/support/examples.rb) for more details. Those rules are derived from [this list](http://dropbox.ashlock.us/open311/json-xml/).
25
+
26
+ ## Installation
27
+
28
+ Add this line to your application's Gemfile:
29
+
30
+ ```ruby
31
+ gem 'dachsfisch', github: 'openHPI/dachsfisch'
32
+ ```
33
+
34
+ And then execute:
35
+
36
+ ```
37
+ $ bundle
38
+ ```
39
+
40
+ Note: Removing support for ancient Ruby or Rails versions will not result in a new major. Please be extra careful when using ancient Ruby or Rails versions and updating gems.
41
+
42
+ ## Usage
43
+
44
+ Based on the desired conversion, use one of the following classes. In both cases, the input is expected to be a valid string.
45
+
46
+ ### XML-to-JSON
47
+
48
+ ```ruby
49
+ xml = '<alice>bob</alice>'
50
+ json = Dachsfisch::XML2JSONConverter.perform(xml: xml)
51
+ ```
52
+
53
+ ### JSON-to-XML
54
+
55
+ ```ruby
56
+ json = '{ "alice": { "$" : "bob" } }'
57
+ xml = Dachsfisch::JSON2XMLConverter.perform(json: json)
58
+ ```
59
+
60
+ ## Development
61
+
62
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
63
+
64
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
65
+
66
+ ## Contributing
67
+
68
+ Bug reports and pull requests are welcome on GitHub at https://github.com/openHPI/dachsfisch. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/openHPI/dachsfisch/blob/main/CODE_OF_CONDUCT.md).
69
+
70
+ ## License
71
+
72
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
73
+
74
+ ## Code of Conduct
75
+
76
+ Everyone interacting in this project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/openHPI/dachsfisch/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ RuboCop::RakeTask.new
10
+
11
+ task default: %i[spec rubocop]
data/SECURITY.md ADDED
@@ -0,0 +1,14 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ To receive fixes for security vulnerabilities it is required to always upgrade to the latest version of dachsfisch.
6
+ See https://github.com/openHPI/dachsfisch/releases for the latest version.
7
+
8
+ ## Reporting a Vulnerability
9
+
10
+ If you have found a vulnerability or you are uncertain whether what you have discovered is a vulnerability,
11
+ please send an email to sebastian.serth@hpi.de ([GPG Key](https://github.com/mrserth.gpg)).
12
+
13
+ If you have a patch for the issue please use `git format-patch` and attach it to the email. Please do not open an issue or
14
+ pull request on GitHub as that may disclose sensitive details around the vulnerability.
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/dachsfisch/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'dachsfisch'
7
+ spec.version = Dachsfisch::VERSION
8
+ spec.authors = ['Karol']
9
+ spec.email = ['git@koehn.pro']
10
+
11
+ spec.summary = 'Badgerfish implementation'
12
+ spec.description = 'Implements a bidirectional converter for XML and JSON based on the badgerfish-specification'
13
+ spec.homepage = 'https://github.com/openHPI/dachsfisch'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.2'
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(__dir__) do
20
+ `git ls-files -z`.split("\x0").reject do |f|
21
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)})
22
+ end
23
+ end
24
+
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{\Aexe/}) {|f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_dependency 'nokogiri', '>= 1.14.1', '< 2.0.0'
30
+
31
+ spec.metadata['rubygems_mfa_required'] = 'true'
32
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dachsfisch
4
+ class ConverterBase
5
+ def self.perform(**)
6
+ new(**).execute
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dachsfisch
4
+ class DachsfischError < StandardError; end
5
+ class InvalidJSONInputError < DachsfischError; end
6
+ class InvalidXMLInputError < DachsfischError; end
7
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dachsfisch
4
+ class JSON2XMLConverter < ConverterBase
5
+ def initialize(json:)
6
+ super()
7
+ @json_hash = JSON.parse json
8
+ rescue TypeError, JSON::ParserError => e
9
+ raise InvalidJSONInputError.new(e.message)
10
+ end
11
+
12
+ def execute
13
+ fragment = Nokogiri::XML::DocumentFragment.new(Nokogiri::XML::Document.new)
14
+
15
+ Nokogiri::XML::Builder.with fragment do |xml|
16
+ add_element xml, @json_hash
17
+ end
18
+ fragment.elements.deconstruct.map(&:to_xml).join("\n")
19
+ end
20
+
21
+ private
22
+
23
+ def add_element(xml, element)
24
+ return unless element.is_a? Hash
25
+
26
+ element.each do |key, value|
27
+ add_node(xml, key, value) unless key.start_with?('@')
28
+ end
29
+ end
30
+
31
+ def add_node(xml, key, element)
32
+ case element
33
+ when Hash
34
+ node = xml.send(key) { add_element(xml, element) }
35
+ handle_attribute_and_namespaces(node, element)
36
+ when Array
37
+ element.each do |sub_element|
38
+ add_node xml, key, sub_element
39
+ end
40
+ else
41
+ add_text_node(xml, element, key[0])
42
+ end
43
+ end
44
+
45
+ def handle_attribute_and_namespaces(node, element)
46
+ element.keys.filter {|element_key| element_key.start_with?('@') }.each do |attribute_key|
47
+ if attribute_key.start_with? '@xmlns'
48
+ element[attribute_key].each do |namespace_key, namespace|
49
+ # add namespace of current scope to node. The root-ns($) gets 'xmlns' as key, named namespaces 'xmlns:name' respectively.
50
+ node["xmlns#{namespace_key == '$' ? '' : ":#{namespace_key}"}"] = namespace
51
+ end
52
+ else
53
+ node[attribute_key.delete_prefix('@')] = element[attribute_key]
54
+ end
55
+ end
56
+ end
57
+
58
+ def add_text_node(xml, element, type)
59
+ case type
60
+ when '!'
61
+ xml.comment element
62
+ when '#'
63
+ xml.cdata element
64
+ when '$'
65
+ xml.text element
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dachsfisch
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dachsfisch
4
+ class XML2JSONConverter < ConverterBase
5
+ def initialize(xml:)
6
+ super()
7
+ @fragment = Nokogiri::XML::DocumentFragment.parse(xml)
8
+ raise InvalidXMLInputError.new('input empty') if xml.nil? || xml.empty?
9
+ raise InvalidXMLInputError.new(@fragment.errors) if @fragment.errors.length.positive?
10
+ end
11
+
12
+ def execute
13
+ result = {}
14
+ @fragment.elements.deconstruct.each do |root|
15
+ result[node_name(root)] = extract_node(root)
16
+ end
17
+ result.to_json
18
+ end
19
+
20
+ private
21
+
22
+ def extract_node(node)
23
+ hash = {}
24
+ active_namespaces = add_namespaces_to_active_namespaces(node)
25
+ hash['@xmlns'] = active_namespaces unless active_namespaces.empty?
26
+
27
+ handle_attributes(hash, node)
28
+ node.children.each do |child|
29
+ handle_content(hash, child)
30
+ end
31
+ hash
32
+ end
33
+
34
+ def handle_content(hash, child)
35
+ case child.class.to_s # use string representation because CData inherits Text
36
+ when 'Nokogiri::XML::Text'
37
+ add_text_with_custom_key(hash, child, '$')
38
+ when 'Nokogiri::XML::Comment'
39
+ add_text_with_custom_key(hash, child, '!', strip_text: false)
40
+ when 'Nokogiri::XML::CDATA'
41
+ add_text_with_custom_key(hash, child, '#', strip_text: false)
42
+ else
43
+ add_value_to_hash(hash, child)
44
+ end
45
+ end
46
+
47
+ def add_text_with_custom_key(hash, child, base_key, strip_text: true)
48
+ value = strip_text ? child.text.strip : child.text
49
+ return if value.empty? && strip_text
50
+
51
+ child_key = next_key_index hash, base_key
52
+ hash[child_key] = value
53
+ end
54
+
55
+ def add_value_to_hash(hash, child)
56
+ child_key = node_name(child)
57
+ existing_value = hash[child_key]
58
+ new_value = extract_node(child)
59
+
60
+ if existing_value.nil?
61
+ hash[child_key] = new_value
62
+ elsif existing_value.is_a? Array
63
+ existing_value << new_value
64
+ else
65
+ hash[child_key] = [existing_value, new_value]
66
+ end
67
+ end
68
+
69
+ def node_name(node)
70
+ node.namespace&.prefix.nil? ? node.name : "#{node.namespace.prefix}:#{node.name}"
71
+ end
72
+
73
+ def handle_attributes(hash, node)
74
+ node.attribute_nodes.each do |attribute|
75
+ hash["@#{attribute.name}"] = attribute.value
76
+ end
77
+ end
78
+
79
+ def add_namespaces_to_active_namespaces(node)
80
+ node.namespaces.transform_keys {|k| convert_namespace_key(k) }
81
+ end
82
+
83
+ def convert_namespace_key(key)
84
+ return '$' if key == 'xmlns'
85
+
86
+ key.delete_prefix 'xmlns:'
87
+ end
88
+
89
+ def next_key_index(hash, base_key)
90
+ key_count = hash.keys.count {|k| k.start_with? base_key }
91
+
92
+ "#{base_key}#{key_count + 1}"
93
+ end
94
+ end
95
+ end
data/lib/dachsfisch.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+ require 'json'
5
+
6
+ require 'dachsfisch/version'
7
+ require 'dachsfisch/converter_base'
8
+ require 'dachsfisch/xml2_json_converter'
9
+ require 'dachsfisch/json2_xml_converter'
10
+ require 'dachsfisch/errors'
11
+
12
+ module Dachsfisch
13
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dachsfisch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Karol
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-08-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.14.1
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 2.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 1.14.1
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 2.0.0
33
+ description: Implements a bidirectional converter for XML and JSON based on the badgerfish-specification
34
+ email:
35
+ - git@koehn.pro
36
+ executables: []
37
+ extensions: []
38
+ extra_rdoc_files: []
39
+ files:
40
+ - ".rspec"
41
+ - ".rubocop.yml"
42
+ - ".rubocop/layout.yml"
43
+ - ".rubocop/lint.yml"
44
+ - ".rubocop/metrics.yml"
45
+ - ".rubocop/rails.yml"
46
+ - ".rubocop/rspec.yml"
47
+ - ".rubocop/style.yml"
48
+ - CODE_OF_CONDUCT.md
49
+ - Gemfile
50
+ - Gemfile.lock
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - SECURITY.md
55
+ - dachsfisch.gemspec
56
+ - lib/dachsfisch.rb
57
+ - lib/dachsfisch/converter_base.rb
58
+ - lib/dachsfisch/errors.rb
59
+ - lib/dachsfisch/json2_xml_converter.rb
60
+ - lib/dachsfisch/version.rb
61
+ - lib/dachsfisch/xml2_json_converter.rb
62
+ homepage: https://github.com/openHPI/dachsfisch
63
+ licenses:
64
+ - MIT
65
+ metadata:
66
+ rubygems_mfa_required: 'true'
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '3.2'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubygems_version: 3.4.15
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Badgerfish implementation
86
+ test_files: []