bridgetown-webfinger 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3c1086d7e337e4a0654b54bac8d38521cbe18c7502cfd816d8c6f164a40553b4
4
+ data.tar.gz: b014d6ffbb34b2b839af88730993d376d2dd1a4ff0ad43e51110eb397e03c6e2
5
+ SHA512:
6
+ metadata.gz: 1dd3dbc3a4097dbeb8283cf3c92bc1a8d9eb9d88e3555a1f3a61a65a13f2a048f25e09d69c4e1c25def7d68748ac9c1f32a494a3ff314e812c463c27e9b3311a
7
+ data.tar.gz: e3452f05f6cedd25bf31e99752aedec2d30b4e747fe7f9e094995ad2909b7a89dccf38203b70b47288a1c07f802f69654b8f722ee95c7dd8f889283e1b6acc58
data/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0](https://github.com/michaelherold/bridgetown-webfinger/tree/v0.1.0) - 2023-02-25
9
+
10
+ ### Added
11
+
12
+ - Static file support for rendering a `.well-known/webfinger` file that always responds with the same information.
13
+ - Dynamic support via a Roda plugin for handling multiple authors and proper filtering for `rel` filters. This includes the ability to set `Access-Control-Allow-Origin` headers via the `allowed_hosts` option.
14
+ - Validation of all Webfinger properties and printed warnings for invalid values to help ensure you write interoperable Webfinger properties.
15
+ - An automation to quickly bootstrap your site's Webfinger capabilities.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,55 @@
1
+ # Contributing
2
+
3
+ In the spirit of [free software](http://www.fsf.org/licensing/essays/free-sw.html), we encourage **everyone** to help improve this project. Here are some ways _you_ can contribute:
4
+
5
+ * Use alpha, beta, and pre-release versions.
6
+ * Report bugs.
7
+ * Suggest new features.
8
+ * Write or edit documentation.
9
+ * Write specifications.
10
+ * Write code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace).
11
+ * Refactor code.
12
+ * Fix [issues].
13
+ * Review patches.
14
+
15
+ [issues]: https://github.com/michaelherold/bridgetown-webfinger/issues
16
+
17
+ ## Submitting an Issue
18
+
19
+ We use the [GitHub issue tracker][issues] to track bugs and features. Before submitting a bug report or feature request, check to make sure no one has already submitted it.
20
+
21
+ When submitting a bug report, please include a `<details>` block that includes a stack trace and any details that may be necessary to reproduce the bug, including your gem version, Ruby version, and operating system. This looks like the following:
22
+
23
+ ```markdown
24
+ <details>
25
+ <summary>A description of the details block</summary>
26
+
27
+ All of the content that you want in here, perhaps with code fences. Note that
28
+ if you only have a code fence in here, you _must_ separate it from the <summary>
29
+ tag and the closing </details> or it won't render correctly.
30
+
31
+ Notice the empty line here ↓
32
+
33
+ </details>
34
+ ```
35
+
36
+ Ideally, a bug report should include a pull request with failing specs.
37
+
38
+ ## Submitting a Pull Request
39
+
40
+ 1. Fork the repository.
41
+ 2. Create a topic branch.
42
+ 3. Add specs for your unimplemented feature or bug fix.
43
+ 4. Run `bundle exec rake test`. If your specs pass, return to step 3.
44
+ 5. Implement your feature or bug fix.
45
+ 6. Run `bundle exec rake`. If your specs or any of the linters fail, return to step 5.
46
+ 7. Open `coverage/index.html`. If your changes are not fully covered by your tests, return to step 3.
47
+ 8. Add documentation for your feature or bug fix.
48
+ 9. Commit and push your changes.
49
+ 10. Submit a pull request.
50
+
51
+ ## Tools to Help You Succeed
52
+
53
+ After checking out the repository, run `bin/setup` to install dependencies. Then, run `bundle exec rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
54
+
55
+ Before committing code, run `bundle exec rake` to check that the code conforms to the style guidelines of the project, that all of the tests are green (if you're writing a feature; if you're only submitting a failing test, then it does not have to pass!), and that you sufficiently documented the changes.
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ # The MIT License (MIT)
2
+
3
+ Copyright © 2023-present
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # Bridgetown Webfinger plugin
2
+
3
+ A [Bridgetown][1] plugin for handling [Webfinger][2] requests for the Fediverse and [IndieWeb][3].
4
+
5
+ Whether you run a single-author, statically rendered site or a multi-author site with a dynamic backend, this is your one-stop shop for Webfinger support.
6
+
7
+ This plugin allows for hosting Webfinger lookups on your website for [`acct:` URIs][4]. This is the first step to giving your site the ability to post to Fediverse apps like [Mastodon][5], [Pleroma][6], or [PeerTube][7]. While not all Fediverse apps use Webfinger, enough do that adding Webfinger support will be necessary to have your site participate in the Fediverse.
8
+
9
+ [1]: https://www.bridgetownrb.com
10
+ [2]: https://webfinger.net/
11
+ [3]: https://indieweb.org/
12
+ [4]: https://datatracker.ietf.org/doc/html/rfc7565
13
+ [5]: https://joinmastodon.org/
14
+ [6]: https://pleroma.social/
15
+ [7]: https://joinpeertube.org/
16
+
17
+ ## Installation
18
+
19
+ Run this command to add this plugin to your site's Gemfile:
20
+
21
+ bundle add bridgetown-webfinger
22
+
23
+ Or you can use [an automation script][8] instead for guided setup:
24
+
25
+ bin/bt apply https://github.com/michaelherold/bridgetown-webfinger
26
+
27
+ [8]: https://www.bridgetownrb.com/docs/automations
28
+
29
+ ## Usage
30
+
31
+ This plugin runs in one of two modes: static mode or dynamic mode.
32
+
33
+ Static mode is for when you run a single-author, fully static website without using any of Bridgetown's Server-Side Rendering (SSR) capabilities. It is mildly non-compliant with the Webfinger specification, but is an accepted practice for static sites.
34
+
35
+ Dynamic mode uses a Roda plugin to serve author data for all accounts defined in your `src/_data/authors.yml` file.
36
+
37
+ ### Authors data file
38
+
39
+ Within your authors data file, each author requires a `webfinger` associative array with data about the account. For example, for the account `bilbo`, who has a Twitter account at `https://twitter.com/bilbobaggins` and hosts an avatar at `https://bagend.com/bilbo.png`, the authors data file entry might look like:
40
+
41
+ ```yaml
42
+ # src/_data/authors.yml
43
+ ---
44
+ bilbo:
45
+ webfinger:
46
+ aliases:
47
+ - https://twitter.com/bilbobaggins
48
+ links:
49
+ - href: https://bagend.com/bilbo.png
50
+ rel: http://webfinger.net/rel/avatar
51
+ type: image/png
52
+ ```
53
+
54
+ You may configure any number of authors in this way, however static mode works with only a single author.
55
+
56
+ ### Static mode
57
+
58
+ To use static mode, configure your [authors data file](#authors-data-file) and add the following to your `config/initializers.rb` file:
59
+
60
+ ```ruby
61
+ # config/initializers.rb
62
+ Bridgetown.configure do
63
+ init "bridgetown-webfinger", static: true
64
+ end
65
+ ```
66
+
67
+ Bridgetown will now generate a `.well-known/webfinger` file using your first author's information when building the site.
68
+
69
+ #### Deploying to Netlify
70
+
71
+ To have the correct headers for your static file, add the following to your `netlify.toml`.
72
+
73
+ ```toml
74
+ [[headers]]
75
+ for = "/.well-known/webfinger"
76
+
77
+ [headers.values]
78
+ Access-Control-Allow-Origin = "*"
79
+ Content-Type = "application/jrd+json"
80
+ ```
81
+
82
+ See [the allowed hosts section below](#allowed-hosts) for more information about the access control header.
83
+
84
+ ### Dynamic mode
85
+
86
+ To use dynamic mode, configure your [authors data file](#authors-data-file) and add the following to your `config/initializers.rb` file:
87
+
88
+ ```ruby
89
+ # config/initializers.rb
90
+ Bridgetown.configure do
91
+ init "bridgetown-webfinger", static: false, allowed_hosts: "*"
92
+ end
93
+ ```
94
+
95
+ Then, add the `bridgetown_webfinger` plugin to your `RodaApp` and call the `bridgetown_webfinger` request method:
96
+
97
+ ```ruby
98
+ # server/roda_app.rb
99
+ class RodaApp < Bridgetown::Rack::Roda
100
+ plugin :bridgetown_ssr # required
101
+ plugin :bridgetown_webfinger
102
+
103
+ route do |r|
104
+ r.bridgetown_webfinger
105
+ end
106
+ end
107
+ ```
108
+
109
+ This generates a `.well-known/webfinger` route that serves `acct:` URI requests for authors on your site.
110
+
111
+ #### Allowed hosts
112
+
113
+ The [Webfinger specification][9] states that [servers must include the `Access-Control-Allow-Origin`][10] header to enable Cross-Origin Resource Sharing (CORS) requests and that they should use the least restrictive setting.
114
+
115
+ Unless you are hosting private Webfinger information — e.g. within a corporate network, to only authenticated followers, or for any other reason — you should set this to `"*"` using the configuration above.
116
+
117
+ If this is private information that you wish to restrict [set the header value appropriately][11].
118
+
119
+ [9]: https://datatracker.ietf.org/doc/html/rfc7033
120
+ [10]: https://datatracker.ietf.org/doc/html/rfc7033#section-5
121
+ [11]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
122
+
123
+ ## Contributing
124
+
125
+ So you're interested in contributing to Bridgetown Webfinger? Check out our [contributing guidelines](CONTRIBUTING.md) for more information on how to do that.
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/bridgetown/webfinger/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "bridgetown-webfinger"
7
+ spec.version = Bridgetown::Webfinger::VERSION
8
+ spec.author = "Michael Herold"
9
+ spec.email = "opensource@michaeljherold.com"
10
+ spec.summary = "Adds structured support for Webfinger to Bridgetown sites"
11
+ spec.homepage = "https://github.com/michaelherold/bridgetown-webfinger"
12
+ spec.license = "MIT"
13
+
14
+ spec.files = %w[CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md]
15
+ spec.files += %w[bridgetown-webfinger.gemspec]
16
+ spec.files += Dir["lib/**/*.rb"]
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.required_ruby_version = ">= 3.0.0"
20
+
21
+ spec.add_dependency "bridgetown", ">= 1.2", "< 2.0"
22
+ spec.add_dependency "uri", ">= 0.12.0"
23
+ spec.add_dependency "zeitwerk"
24
+
25
+ spec.add_development_dependency "bundler", ">= 2"
26
+
27
+ spec.metadata = {
28
+ "bug_tracker_uri" => "https://github.com/michaelherold/bridgetown-webfinger/issues",
29
+ "changelog_uri" => "https://github.com/michaelherold/bridgetown-webfinger/blob/main/CHANGELOG.md",
30
+ "documentation_uri" => "https://rubydoc.info/gems/bridgetown-webfinger/#{Bridgetown::Webfinger::VERSION}",
31
+ "homepage_uri" => "https://github.com/michaelherold/bridgetown-webfinger",
32
+ "rubygems_mfa_required" => "true",
33
+ "source_code_uri" => "https://github.com/michaelherold/bridgetown-webfinger"
34
+ }
35
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Webfinger
5
+ # Wraps an `alias` member within a {JRD}, which is a URI identifying the same subject
6
+ class Alias < Model
7
+ # Parses and maybe-returns an {Alias} when the value is one
8
+ #
9
+ # Aliases [must be URIs][1] so when the value is not a proper URI, it is
10
+ # ignored.
11
+ #
12
+ # [1]: https://datatracker.ietf.org/doc/html/rfc7033#section-4.4.2
13
+ #
14
+ # @since 0.1.0
15
+ # @api private
16
+ #
17
+ # @return [Alias, nil] an {Alias} when the value is a URI, nil otherwise
18
+ def self.parse(moniker)
19
+ if Webfinger.uri?(moniker)
20
+ new(moniker)
21
+ else
22
+ warn(
23
+ "Webfinger alias is malformed: #{moniker.inspect}, ignoring"
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Webfinger
5
+ # The [builder class][1] for working with Webfinger in Bridgetown
6
+ #
7
+ # [1]: https://www.bridgetownrb.com/docs/plugins#introduction-to-the-builder-api
8
+ class Builder < Bridgetown::Builder
9
+ include Logging
10
+
11
+ # The hook method for enabling the builder's behavior
12
+ #
13
+ # @since 0.1.0
14
+ # @api private
15
+ #
16
+ # @return [void]
17
+ def build
18
+ generator :static_file
19
+ end
20
+
21
+ private
22
+
23
+ # Builds a static Webfinger file when correctly configured
24
+ #
25
+ # @since 0.1.0
26
+ # @api private
27
+ #
28
+ # @return [void]
29
+ def static_file
30
+ return unless config.webfinger.static
31
+ return unless (host = extract_site_host)
32
+ return unless (account, data = extract_authors_from_site_data)
33
+
34
+ site.add_generated_page StaticFile.new(site, account: account, data: data, host: host)
35
+ end
36
+
37
+ # Extracts the site's host from its configuration
38
+ #
39
+ # @since 0.1.0
40
+ # @api private
41
+ #
42
+ # @return [String, nil] String when configured correctly, nil otherwise
43
+ def extract_site_host
44
+ host = site.config.url
45
+
46
+ if !host || host.empty?
47
+ warn(
48
+ "bridgetown-webfinger did not find your site's host configured at " \
49
+ "`site.config.url`; this is unsupported by static files so will be ignored"
50
+ )
51
+ return
52
+ end
53
+
54
+ uri = URI(host)
55
+
56
+ if uri.is_a?(URI::HTTP)
57
+ uri.host
58
+ elsif uri.instance_of?(URI::Generic)
59
+ uri.to_s
60
+ else
61
+ warn(
62
+ "bridgetown-webfinger detected a malformed host in your site configuration " \
63
+ "(#{uri} from `site.config.url`); this is unsupported and will be ignored"
64
+ )
65
+ end
66
+ end
67
+
68
+ # Extracts the author from the site's data
69
+ #
70
+ # @since 0.1.0
71
+ # @api private
72
+ #
73
+ # @return [HashWithDotAccess::Hash, nil] Hash when configured correctly, nil otherwise
74
+ def extract_authors_from_site_data
75
+ unless (authors = site.data.authors) && !authors.empty?
76
+ warn(
77
+ "bridgetown-webfinger did not detect any authors at `site.data.authors`; " \
78
+ "this is unsupported by static files so will be ignored"
79
+ )
80
+ return
81
+ end
82
+
83
+ unless authors.size == 1
84
+ warn(
85
+ "bridgetown-webfinger discovered multiple authors; this is unsupported " \
86
+ "by static files so will be ignored"
87
+ )
88
+ return
89
+ end
90
+
91
+ authors.first
92
+ end
93
+
94
+ # The static Webfinger file to serve
95
+ class StaticFile < Bridgetown::GeneratedPage
96
+ # Initializes a new static Webfinger file
97
+ #
98
+ # @since 0.1.0
99
+ # @api private
100
+ #
101
+ # @param site [Bridgetown::Site] the site object
102
+ # @param account [String] the account for the {URI::Acct}
103
+ # @param data [HashWithDotAccess::Hash] the data for the Webfinger JRD
104
+ # @param host [String] the host for the {URI::Acct}
105
+ # @return [void]
106
+ def initialize(site, account:, data:, host:)
107
+ super(site, __dir__, "/.well-known", "webfinger", from_plugin: true)
108
+
109
+ @subject = URI::Acct.build({account: account, host: host}).to_s
110
+ @jrd = JRD.parse(@subject, data.webfinger)
111
+
112
+ self.content = JSON.pretty_generate(@jrd.to_h)
113
+ end
114
+
115
+ # The {JRD} forming the data model for the static Webfinger page
116
+ #
117
+ # @since 0.1.0
118
+ # @api private
119
+ #
120
+ # @return [JRD] the JSON Resource Descriptor to serve for Webfinger
121
+ attr_reader :jrd
122
+
123
+ # The subject of the {JRD}
124
+ #
125
+ # @since 0.1.0
126
+ # @api private
127
+ #
128
+ # @return [String] the subject for the JRD to serve
129
+ attr_reader :subject
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Webfinger
5
+ # Wraps an `href` member within a {Link}, which is the target URI
6
+ class Href < Model
7
+ # Parses and maybe-returns an {Href} when the value is one
8
+ #
9
+ # Hrefs [must be URIs][1] so when the value is not a proper URI, it is
10
+ # ignored.
11
+ #
12
+ # [1]: https://datatracker.ietf.org/doc/html/rfc7033#section-4.4.4.3
13
+ #
14
+ # @since 0.1.0
15
+ # @api private
16
+ #
17
+ # @return [Href, nil] an {Href} when the value is a URI, nil otherwise
18
+ def self.parse(href)
19
+ if Webfinger.uri?(href)
20
+ new(href)
21
+ else
22
+ warn(
23
+ "Webfinger link href is malformed: #{href.inspect}, ignoring"
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @param config [Bridgetown::Configuration::ConfigurationDSL]
4
+ # @param allowed_origins [String] the value to use for the
5
+ # `Access-Control-Allow-Origin` header in the Roda plugin. Only override this
6
+ # if you're hosting private Webfinger data due to the [spec][1] saying, "servers
7
+ # SHOULD support the least restrictive setting"
8
+ # @param static [Boolean] whether to use static file rendering or not
9
+ #
10
+ # [1]: https://datatracker.ietf.org/doc/html/rfc7033#section-5
11
+ Bridgetown.initializer "bridgetown-webfinger" do |config, allowed_origins: "*", static: true|
12
+ options = {allowed_origins: allowed_origins, static: static}
13
+
14
+ # :nocov: Because it's not possible to show coverage for both branches
15
+ if config.webfinger
16
+ config.webfinger Bridgetown::Utils.deep_merge_hashes(options, config.webfinger)
17
+ else
18
+ config.webfinger(options)
19
+ end
20
+ # :nocov:
21
+
22
+ config.builder Bridgetown::Webfinger::Builder
23
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Webfinger
5
+ # A model of a [JSON Resource Descriptor][1]
6
+ #
7
+ # [1]: https://datatracker.ietf.org/doc/html/rfc7033#section-4.4
8
+ class JRD
9
+ include Logging
10
+
11
+ # Parses and maybe-returns a {JRD} when the subject and data form one
12
+ #
13
+ # {JRD}s can describe any resource identifiable via a URI, whether you want
14
+ # to give public directory information about people via `acct:` URIs,
15
+ # copyright information about an article, or perhaps license information
16
+ # about a library.
17
+ #
18
+ # @since 0.1.0
19
+ # @api public
20
+ #
21
+ # @example Convert a Hash into a sanitized {JRD}
22
+ #
23
+ # Bridgetown::Webfinger::JRD.parse(
24
+ # "acct:bilbo@bagend.com",
25
+ # {
26
+ # aliases: ["https://bilbobaggins.com/"],
27
+ # links: [
28
+ # {
29
+ # href: "https://bagend.com/bilbo",
30
+ # rel: "http://webfinger.net/rel/profile-page",
31
+ # type: "text/html",
32
+ # properties: {
33
+ # "http://packetizer.com/ns/name" => "Bilbo @ Bag End"
34
+ # },
35
+ # titles: {
36
+ # "en-us" => "Bilbo Baggins's blog"
37
+ # }
38
+ # }
39
+ # ],
40
+ # properties: {
41
+ # "http://packetizer.com/ns/name" => "Bilbo Baggins"
42
+ # }
43
+ # }
44
+ # )
45
+ #
46
+ # @param subject [String] the (nominally) optional subject identified by
47
+ # the JRD; _should_ be present but is not _required_
48
+ # @param data [Hash] the data hash containing information to add to the
49
+ # JRD
50
+ # @return [JRD, nil] a {JRD} when the information is valid, nil otherwise
51
+ def self.parse(subject, data)
52
+ aliases = Array(data[:aliases]).filter_map { |moniker| Alias.parse(moniker) }
53
+ links = Array(data[:links]).filter_map { |link| Link.parse(link) }
54
+ properties = Properties.parse(data[:properties])
55
+ subject = Webfinger.uri?(subject) ? subject : nil
56
+
57
+ new(
58
+ aliases: (!aliases.empty?) ? aliases : nil,
59
+ links: (!links.empty?) ? links : nil,
60
+ properties: properties,
61
+ subject: subject
62
+ )
63
+ end
64
+
65
+ # Creates a new {JRD}
66
+ #
67
+ # @since 0.1.0
68
+ # @api public
69
+ #
70
+ # @example Creating a new {JRD} without parsing a Hash
71
+ #
72
+ # Bridgetown::Webfinger::JRD.new(
73
+ # subject: "acct:bilbo@bagend.com",
74
+ # aliases: [Bridgetown::Webfinger::Alias.new("https://bilbobaggins.com/")]
75
+ # )
76
+ #
77
+ # @param aliases [Array<Alias>, nil] the optional {Alias}es for the resource
78
+ # @param links [Array<Link>, nil] the optional {Link}s related to the resource
79
+ # @param properties [Properties] the optional {Properties} describing the resource
80
+ # @param subject [String, nil] the (nominally) optional subject URI for the resource
81
+ def initialize(aliases: nil, links: nil, properties: nil, subject: nil)
82
+ @aliases = aliases
83
+ @links = links
84
+ @properties = properties
85
+ @subject = subject
86
+ end
87
+
88
+ # The list of {Alias}es for the resource
89
+ #
90
+ # @since 0.1.0
91
+ # @api public
92
+ #
93
+ # @example Read the {Alias}es for a {JRD}
94
+ #
95
+ # jrd = Bridgetown::Webfinger::JRD.parse(
96
+ # "acct:bilbo@bagend.com",
97
+ # {aliases: ["https://bilbobaggins.com"]}
98
+ # )
99
+ # jrd.aliases
100
+ #
101
+ # @return [Array<Alias>, nil] the optional {Alias}es for the resource
102
+ attr_reader :aliases
103
+
104
+ # The list of {Link}s for the resource
105
+ #
106
+ # @since 0.1.0
107
+ # @api public
108
+ #
109
+ # @example Read the {Link}s for a {JRD}
110
+ #
111
+ # jrd = Bridgetown::Webfinger::JRD.parse(
112
+ # "acct:bilbo@bagend.com",
113
+ # {links: [{href: "https://bilbobaggins.com", rel: "https://webfinger.net/rel/avatar"}]}
114
+ # )
115
+ # jrd.links
116
+ #
117
+ # @return [Array<Link>, nil] the optional {Link}s related to the resource
118
+ attr_reader :links
119
+
120
+ # The list of {Properties} for the resource
121
+ #
122
+ # @since 0.1.0
123
+ # @api public
124
+ #
125
+ # @example Read the {Properties} for a {JRD}
126
+ #
127
+ # jrd = Bridgetown::Webfinger::JRD.parse(
128
+ # "acct:bilbo@bagend.com",
129
+ # {properties: {"http://packetizer.com/ns/name" => "Bilbo Baggins"}}
130
+ # )
131
+ # jrd.properties
132
+ #
133
+ # @return [Properties, nil] the optional {Properties} describing the resource
134
+ attr_reader :properties
135
+
136
+ # The subject of the {JRD}
137
+ #
138
+ # @since 0.1.0
139
+ # @api public
140
+ #
141
+ # @example Read the subject for a {JRD}
142
+ #
143
+ # jrd = Bridgetown::Webfinger::JRD.parse("acct:bilbo@bagend.com")
144
+ # jrd.subject #=> "acct:bilbo@bagend.com"
145
+ #
146
+ # @return [String, nil] the (nominally) optional subject URI for the resource
147
+ attr_reader :subject
148
+
149
+ # Converts the {JRD} to a JSON-serializable Hash
150
+ #
151
+ # @since 0.1.0
152
+ # @api private
153
+ #
154
+ # @return [Hash] the JRD as a JSON-compatible Hash
155
+ def to_h
156
+ result = {subject: subject}
157
+ result[:aliases] = aliases if aliases
158
+ result[:links] = links.map(&:to_h) if links
159
+ result[:properties] = properties.to_h if properties
160
+ result
161
+ end
162
+ end
163
+ end
164
+ end