w3c_api 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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +33 -0
  3. data/README.adoc +1265 -0
  4. data/Rakefile +8 -0
  5. data/exe/w3c_api +6 -0
  6. data/lib/w3c_api/cli.rb +41 -0
  7. data/lib/w3c_api/client.rb +320 -0
  8. data/lib/w3c_api/commands/affiliation.rb +48 -0
  9. data/lib/w3c_api/commands/ecosystem.rb +57 -0
  10. data/lib/w3c_api/commands/group.rb +87 -0
  11. data/lib/w3c_api/commands/output_formatter.rb +18 -0
  12. data/lib/w3c_api/commands/participation.rb +34 -0
  13. data/lib/w3c_api/commands/series.rb +40 -0
  14. data/lib/w3c_api/commands/specification.rb +68 -0
  15. data/lib/w3c_api/commands/translation.rb +31 -0
  16. data/lib/w3c_api/commands/user.rb +77 -0
  17. data/lib/w3c_api/models/affiliation.rb +86 -0
  18. data/lib/w3c_api/models/affiliations.rb +33 -0
  19. data/lib/w3c_api/models/base.rb +39 -0
  20. data/lib/w3c_api/models/call_for_translation.rb +59 -0
  21. data/lib/w3c_api/models/call_for_translation_ref.rb +15 -0
  22. data/lib/w3c_api/models/charter.rb +110 -0
  23. data/lib/w3c_api/models/charters.rb +17 -0
  24. data/lib/w3c_api/models/collection_base.rb +79 -0
  25. data/lib/w3c_api/models/connected_account.rb +54 -0
  26. data/lib/w3c_api/models/delegate_enumerable.rb +54 -0
  27. data/lib/w3c_api/models/ecosystem.rb +72 -0
  28. data/lib/w3c_api/models/ecosystems.rb +33 -0
  29. data/lib/w3c_api/models/extension.rb +12 -0
  30. data/lib/w3c_api/models/group.rb +173 -0
  31. data/lib/w3c_api/models/groups.rb +38 -0
  32. data/lib/w3c_api/models/join_emails.rb +12 -0
  33. data/lib/w3c_api/models/link.rb +17 -0
  34. data/lib/w3c_api/models/participation.rb +109 -0
  35. data/lib/w3c_api/models/participations.rb +17 -0
  36. data/lib/w3c_api/models/serie.rb +88 -0
  37. data/lib/w3c_api/models/series.rb +41 -0
  38. data/lib/w3c_api/models/series_collection.rb +17 -0
  39. data/lib/w3c_api/models/spec_version.rb +96 -0
  40. data/lib/w3c_api/models/spec_version_ref.rb +18 -0
  41. data/lib/w3c_api/models/spec_versions.rb +17 -0
  42. data/lib/w3c_api/models/specification.rb +79 -0
  43. data/lib/w3c_api/models/specifications.rb +17 -0
  44. data/lib/w3c_api/models/translation.rb +162 -0
  45. data/lib/w3c_api/models/translations.rb +40 -0
  46. data/lib/w3c_api/models/user.rb +178 -0
  47. data/lib/w3c_api/models/users.rb +44 -0
  48. data/lib/w3c_api/models.rb +15 -0
  49. data/lib/w3c_api/version.rb +5 -0
  50. data/lib/w3c_api.rb +9 -0
  51. metadata +166 -0
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require_relative 'output_formatter'
5
+ require_relative '../client'
6
+
7
+ module W3cApi
8
+ module Commands
9
+ # Thor CLI command for specification operations
10
+ class Specification < Thor
11
+ include OutputFormatter
12
+
13
+ desc 'fetch [OPTIONS]', 'Fetch specifications'
14
+ option :shortname, type: :string, desc: 'Filter by shortname'
15
+ option :version, type: :string, desc: 'Specific version of the specification'
16
+ option :status, type: :string, desc: 'Filter by status'
17
+ option :format, type: :string, default: 'yaml', enum: %w[json yaml], desc: 'Output format'
18
+ def fetch
19
+ client = W3cApi::Client.new
20
+
21
+ specifications = if options[:shortname] && options[:version]
22
+ # Single specification version
23
+ client.specification_version(options[:shortname], options[:version])
24
+ elsif options[:shortname]
25
+ # Single specification
26
+ client.specification(options[:shortname])
27
+ elsif options[:status]
28
+ # Specifications by status
29
+ client.specifications_by_status(options[:status])
30
+ else
31
+ # All specifications
32
+ client.specifications
33
+ end
34
+
35
+ output_results(specifications, options[:format])
36
+ end
37
+
38
+ desc 'versions', 'Fetch versions of a specification'
39
+ option :shortname, type: :string, required: true, desc: 'Specification shortname'
40
+ option :format, type: :string, default: 'yaml', enum: %w[json yaml], desc: 'Output format'
41
+ def versions
42
+ client = W3cApi::Client.new
43
+ versions = client.specification_versions(options[:shortname])
44
+ output_results(versions, options[:format])
45
+ end
46
+
47
+ desc 'supersedes', 'Fetch specifications that this specification supersedes'
48
+ option :shortname, type: :string, required: true, desc: 'Specification shortname'
49
+ option :format, type: :string, default: 'yaml', enum: %w[json yaml], desc: 'Output format'
50
+ def supersedes
51
+ # We need to add client.specification_supersedes method in the client
52
+ client = W3cApi::Client.new
53
+ specifications = client.specification_supersedes(options[:shortname])
54
+ output_results(specifications, options[:format])
55
+ end
56
+
57
+ desc 'superseded-by', 'Fetch specifications that supersede this specification'
58
+ option :shortname, type: :string, required: true, desc: 'Specification shortname'
59
+ option :format, type: :string, default: 'yaml', enum: %w[json yaml], desc: 'Output format'
60
+ def superseded_by
61
+ # We need to add client.specification_superseded_by method in the client
62
+ client = W3cApi::Client.new
63
+ specifications = client.specification_superseded_by(options[:shortname])
64
+ output_results(specifications, options[:format])
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require_relative 'output_formatter'
5
+ require_relative '../client'
6
+
7
+ module W3cApi
8
+ module Commands
9
+ # Thor CLI command for translation operations
10
+ class Translation < Thor
11
+ include OutputFormatter
12
+
13
+ desc 'fetch [OPTIONS]', 'Fetch a translation by ID'
14
+ option :id, type: :string, desc: 'Translation ID'
15
+ option :format, type: :string, default: 'yaml', enum: %w[json yaml], desc: 'Output format'
16
+ def fetch
17
+ client = W3cApi::Client.new
18
+
19
+ translations = if options[:id]
20
+ # Single specification version
21
+ client.translation(options[:id])
22
+ else
23
+ # All translations
24
+ client.translations
25
+ end
26
+
27
+ output_results(translations, options[:format])
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require_relative 'output_formatter'
5
+ require_relative '../client'
6
+
7
+ module W3cApi
8
+ module Commands
9
+ # Thor CLI command for user operations
10
+ class User < Thor
11
+ include OutputFormatter
12
+
13
+ desc 'fetch [OPTIONS]', 'Fetch a user by ID'
14
+ option :id, type: :string, required: true, desc: 'User ID (required)'
15
+ option :format, type: :string, default: 'yaml', enum: %w[json yaml], desc: 'Output format'
16
+ def fetch
17
+ client = W3cApi::Client.new
18
+ user = client.user(options[:id])
19
+ output_results(user, options[:format])
20
+ end
21
+
22
+ desc 'groups', 'Fetch groups a user is a member of'
23
+ option :id, type: :string, required: true, desc: 'User ID'
24
+ option :format, type: :string, default: 'yaml', enum: %w[json yaml], desc: 'Output format'
25
+ def groups
26
+ client = W3cApi::Client.new
27
+ groups = client.user_groups(options[:id])
28
+ output_results(groups, options[:format])
29
+ end
30
+
31
+ desc 'specifications', 'Fetch specifications a user has contributed to'
32
+ option :id, type: :string, required: true, desc: 'User ID (string or numeric)'
33
+ option :format, type: :string, default: 'yaml', enum: %w[json yaml], desc: 'Output format'
34
+ def specifications
35
+ client = W3cApi::Client.new
36
+ specifications = client.user_specifications(options[:id])
37
+ output_results(specifications, options[:format])
38
+ end
39
+
40
+ desc 'affiliations', 'Fetch affiliations of a user'
41
+ option :id, type: :string, required: true, desc: 'User ID (string or numeric)'
42
+ option :format, type: :string, default: 'yaml', enum: %w[json yaml], desc: 'Output format'
43
+ def affiliations
44
+ client = W3cApi::Client.new
45
+ affiliations = client.user_affiliations(options[:id])
46
+ output_results(affiliations, options[:format])
47
+ end
48
+
49
+ desc 'participations', 'Fetch participations of a user'
50
+ option :id, type: :string, required: true, desc: 'User ID (string or numeric)'
51
+ option :format, type: :string, default: 'yaml', enum: %w[json yaml], desc: 'Output format'
52
+ def participations
53
+ client = W3cApi::Client.new
54
+ participations = client.user_participations(options[:id])
55
+ output_results(participations, options[:format])
56
+ end
57
+
58
+ desc 'chair-of-groups', 'Fetch groups a user chairs'
59
+ option :id, type: :string, required: true, desc: 'User ID (string or numeric)'
60
+ option :format, type: :string, default: 'yaml', enum: %w[json yaml], desc: 'Output format'
61
+ def chair_of_groups
62
+ client = W3cApi::Client.new
63
+ groups = client.user_chair_of_groups(options[:id])
64
+ output_results(groups, options[:format])
65
+ end
66
+
67
+ desc 'team-contact-of-groups', 'Fetch groups a user is a team contact of'
68
+ option :id, type: :string, required: true, desc: 'User ID (string or numeric)'
69
+ option :format, type: :string, default: 'yaml', enum: %w[json yaml], desc: 'Output format'
70
+ def team_contact_of_groups
71
+ client = W3cApi::Client.new
72
+ groups = client.user_team_contact_of_groups(options[:id])
73
+ output_results(groups, options[:format])
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'link'
5
+
6
+ # Example affiliation response:
7
+ # {
8
+ # "id"=>48830,
9
+ # "name"=>"Postmedia",
10
+ # "discr"=>"organization",
11
+ # "is-member"=>false,
12
+ # "is-member-association"=>false,
13
+ # "is-partner-member"=>false,
14
+ # "_links"=>{
15
+ # "self"=>{
16
+ # "href"=>"https://api.w3.org/affiliations/48830"
17
+ # },
18
+ # "participants"=>{
19
+ # "href"=>"https://api.w3.org/affiliations/48830/participants"
20
+ # },
21
+ # "participations"=>{
22
+ # "href"=>"https://api.w3.org/affiliations/48830/participations"
23
+ # }
24
+ # }
25
+ # }
26
+
27
+ # Fetch index response:
28
+ # {
29
+ # "href"=>"https://api.w3.org/affiliations/1001",
30
+ # "title"=>"Framkom (Forskningsaktiebolaget Medie-och Kommunikationsteknik)"
31
+ # },
32
+
33
+
34
+ module W3cApi
35
+ module Models
36
+ class AffiliationLinks < Lutaml::Model::Serializable
37
+ attribute :self, Link
38
+ attribute :participants, Link
39
+ attribute :participations, Link
40
+ end
41
+
42
+ class Affiliation < Base
43
+ attribute :id, :integer
44
+ attribute :name, :string
45
+ attribute :href, :string
46
+ attribute :title, :string
47
+ attribute :descr, :string
48
+ attribute :is_member, :boolean
49
+ attribute :is_member_association, :boolean
50
+ attribute :is_partner_member, :boolean
51
+ attribute :_links, AffiliationLinks
52
+
53
+ # Get participants of this affiliation
54
+ def participants(client = nil)
55
+ return nil unless client && _links&.participants
56
+
57
+ client.affiliation_participants(id)
58
+ end
59
+
60
+ # Get participations of this affiliation
61
+ def participations(client = nil)
62
+ return nil unless client && _links&.participations
63
+
64
+ client.affiliation_participations(id)
65
+ end
66
+
67
+ def self.from_response(response)
68
+ transformed_response = transform_keys(response)
69
+
70
+ affiliation = new
71
+ transformed_response.each do |key, value|
72
+ case key
73
+ when :_links
74
+ links = value.each_with_object({}) do |(link_name, link_data), acc|
75
+ acc[link_name] = Link.new(href: link_data[:href], title: link_data[:title])
76
+ end
77
+ affiliation._links = AffiliationLinks.new(links)
78
+ else
79
+ affiliation.send("#{key}=", value) if affiliation.respond_to?("#{key}=")
80
+ end
81
+ end
82
+ affiliation
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lutaml/model'
4
+ require_relative 'affiliation'
5
+ require_relative 'delegate_enumerable'
6
+ require_relative 'collection_base'
7
+
8
+ # {
9
+ # "page"=>1,
10
+ # "limit"=>1000,
11
+ # "pages"=>16,
12
+ # "total"=>15918,
13
+ # "_links"=>{
14
+ # "affiliations"=>[
15
+ # {
16
+ # "href"=>"https://api.w3.org/affiliations/1001",
17
+ # "title"=>"Framkom (Forskningsaktiebolaget Medie-och Kommunikationsteknik)"
18
+ # },
19
+ # {
20
+ # "href"=>"https://api.w3.org/affiliations/1003",
21
+ # "title"=>"BackWeb Technologies, Inc."
22
+ # },
23
+
24
+ module W3cApi
25
+ module Models
26
+ class Affiliations < CollectionBase
27
+ attribute :affiliations, Affiliation, collection: true
28
+
29
+ delegate_enumerable :affiliations
30
+ collection_instance_class Affiliation, :affiliations
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lutaml/model'
4
+ require 'json'
5
+ require 'yaml'
6
+
7
+ module W3cApi
8
+ module Models
9
+ class Base < Lutaml::Model::Serializable
10
+ # Common methods for all W3C API models
11
+
12
+ # Create a model instance from a JSON hash
13
+ def self.from_response(data)
14
+ # Convert keys with hyphens to snake_case for Ruby
15
+ transformed_data = transform_keys(data)
16
+
17
+ # Create model instance
18
+ new(transformed_data)
19
+ end
20
+
21
+ # Utility function to transform kebab-case to snake_case
22
+ def self.transform_keys(data)
23
+ case data
24
+ when Hash
25
+ result = {}
26
+ data.each do |key, value|
27
+ snake_key = key.to_s.tr('-', '_').to_sym
28
+ result[snake_key] = transform_keys(value)
29
+ end
30
+ result
31
+ when Array
32
+ data.map { |item| transform_keys(item) }
33
+ else
34
+ data
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'link'
5
+ require_relative 'spec_version_ref'
6
+
7
+ # {
8
+ # "uri": "https://www.w3.org/WAI/videos/standards-and-benefits/"
9
+ # "title": "Video Introduction to Web Accessibility and W3C Standards"
10
+ # "_links": {
11
+ # "self": {
12
+ # "href": "https://api.w3.org/callsfortranslation/5"
13
+ # }
14
+ # "translations": {
15
+ # "href": "https://api.w3.org/callsfortranslation/5/translations"
16
+ # }
17
+ # }
18
+ # }
19
+
20
+ module W3cApi
21
+ module Models
22
+ class CallForTranslationLinks < Lutaml::Model::Serializable
23
+ attribute :self, Link
24
+ attribute :translations, Link
25
+ end
26
+
27
+ class CallForTranslation < Base
28
+ attribute :uri, :string
29
+ attribute :title, :string
30
+ attribute :spec_version, SpecVersionRef
31
+ attribute :_links, CallForTranslationLinks
32
+
33
+ # Get translations for this call for translation
34
+ def translations(client = nil)
35
+ return nil unless client && _links&.translations
36
+
37
+ client.call_for_translation_translations(uri)
38
+ end
39
+
40
+ def self.from_response(response)
41
+ transformed_response = transform_keys(response)
42
+
43
+ cft = new
44
+ transformed_response.each do |key, value|
45
+ case key
46
+ when :_links
47
+ links = value.each_with_object({}) do |(link_name, link_data), acc|
48
+ acc[link_name] = Link.new(href: link_data[:href], title: link_data[:title])
49
+ end
50
+ cft._links = CallForTranslationLinks.new(links)
51
+ else
52
+ cft.send("#{key}=", value) if cft.respond_to?("#{key}=")
53
+ end
54
+ end
55
+ cft
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lutaml/model'
4
+ require_relative 'spec_version_ref'
5
+
6
+ module W3cApi
7
+ module Models
8
+ class CallForTranslationRef < Lutaml::Model::Serializable
9
+ attribute :uri, :string
10
+ attribute :title, :string
11
+ attribute :comments, :string
12
+ attribute :spec_version, SpecVersionRef
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'extension'
5
+ require_relative 'link'
6
+
7
+ # Example charter response format - specific fields may vary by group:
8
+ # {
9
+ # "end": "2025-10-31"
10
+ # "title": "Accessibility Guidelines Working Group Charter"
11
+ # "start": "1997-10-06"
12
+ # "initial_end": "1999-10-06"
13
+ # "uri": "https://www.w3.org/2022/05/accessibility-guidelines-wg-charter.html"
14
+ # "cfp_uri": "https://lists.w3.org/Archives/Member/w3c-ac-members/..."
15
+ # "extensions": [...]
16
+ # "required_new_commitments": false
17
+ # "patent_policy": "pre-2020"
18
+ # "_links": {
19
+ # "self": {
20
+ # "href": "https://api.w3.org/groups/wg/ag/charters/492"
21
+ # },
22
+ # "group": {
23
+ # "href": "https://api.w3.org/groups/wg/ag"
24
+ # }
25
+ # }
26
+ # }
27
+
28
+ module W3cApi
29
+ module Models
30
+ class CharterLinks < Lutaml::Model::Serializable
31
+ attribute :self, Link
32
+ attribute :group, Link
33
+ end
34
+
35
+ class Charter < Base
36
+ attribute :end, :string # Date-time format
37
+ attribute :href, :string
38
+ attribute :title, :string
39
+ attribute :start, :string # Date-time format
40
+ attribute :initial_end, :string # Date-time format
41
+ attribute :uri, :string, pattern: %r{https?://www\.w3\.org.*}
42
+ attribute :cfp_uri, :string, pattern: %r{https://lists\.w3\.org/Archives/Member/w3c-ac-members/.*}
43
+ attribute :extensions, Extension, collection: true
44
+ attribute :required_new_commitments, :boolean
45
+ attribute :patent_policy, :string
46
+ attribute :_links, CharterLinks
47
+
48
+ # Return the group this charter belongs to
49
+ def group(client = nil)
50
+ return nil unless client && _links&.group
51
+
52
+ group_href = _links.group.href
53
+ group_id = group_href.split('/').last
54
+
55
+ client.group(group_id)
56
+ end
57
+
58
+ # Parse date strings to Date objects
59
+ def end_date
60
+ Date.parse(self.end) if self.end
61
+ rescue Date::Error
62
+ nil
63
+ end
64
+
65
+ def start_date
66
+ Date.parse(start) if start
67
+ rescue Date::Error
68
+ nil
69
+ end
70
+
71
+ def initial_end_date
72
+ Date.parse(initial_end) if initial_end
73
+ rescue Date::Error
74
+ nil
75
+ end
76
+
77
+ # Check if this charter is active
78
+ def active?
79
+ start_date &&
80
+ (end_date.nil? || end_date >= Date.today) &&
81
+ start_date <= Date.today
82
+ rescue Date::Error
83
+ false
84
+ end
85
+
86
+ # Check if this charter has been extended
87
+ def extended?
88
+ !extensions.nil? && !extensions.empty?
89
+ end
90
+
91
+ def self.from_response(response)
92
+ transformed_response = transform_keys(response)
93
+
94
+ charter = new
95
+ transformed_response.each do |key, value|
96
+ case key
97
+ when :_links
98
+ links = value.each_with_object({}) do |(link_name, link_data), acc|
99
+ acc[link_name] = Link.new(href: link_data[:href], title: link_data[:title])
100
+ end
101
+ charter._links = CharterLinks.new(links)
102
+ else
103
+ charter.send("#{key}=", value) if charter.respond_to?("#{key}=")
104
+ end
105
+ end
106
+ charter
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lutaml/model'
4
+ require_relative 'charter'
5
+ require_relative 'delegate_enumerable'
6
+ require_relative 'collection_base'
7
+
8
+ module W3cApi
9
+ module Models
10
+ class Charters < CollectionBase
11
+ attribute :charters, Charter, collection: true
12
+
13
+ delegate_enumerable :charters
14
+ collection_instance_class Charter, :charters
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lutaml/model'
4
+ require 'json'
5
+ require 'yaml'
6
+
7
+ module W3cApi
8
+ module Models
9
+ class CollectionBase < Lutaml::Model::Serializable
10
+ # Common methods for all W3C API model collections
11
+ extend DelegateEnumerable
12
+
13
+ def self.collection_instance_class(klass, attribute)
14
+ @collection_instance_class ||= klass
15
+ @collection_attribute = attribute
16
+ end
17
+
18
+ # Create a model instance from a hash response
19
+ def self.from_response(data)
20
+ return new({ @collection_attribute => [] }) if data.nil?
21
+
22
+ # Handle the case where data is expected to have 'items' key
23
+ if data.is_a?(Hash)
24
+ return new({ @collection_attribute => [] }) unless data.key?(:items) || data.key?('items')
25
+
26
+ items_key = data.key?(:items) ? :items : 'items'
27
+ items = data[items_key] || []
28
+
29
+ # Set pagination metadata
30
+ result = new
31
+ data.each do |key, value|
32
+ next if key == items_key
33
+
34
+ result.send("#{key}=", value) if result.respond_to?("#{key}=")
35
+ end
36
+
37
+ # Process items array
38
+ transformed_items = items.map do |item|
39
+ @collection_instance_class.new(transform_keys(item))
40
+ end
41
+
42
+ result.send("#{@collection_attribute}=", transformed_items)
43
+ return result
44
+
45
+ # Handle case where response is a hash but doesn't contain 'items'
46
+
47
+ end
48
+
49
+ # Handle case where data is directly the items array
50
+ if data.is_a?(Array)
51
+ transformed_data = data.map do |item|
52
+ @collection_instance_class.new(transform_keys(item))
53
+ end
54
+ return new({ @collection_attribute => transformed_data })
55
+ end
56
+
57
+ # For backward compatibility, try to create a model instance anyway
58
+ new({ @collection_attribute => [] })
59
+ end
60
+
61
+ # Utility function to transform kebab-case to snake_case
62
+ def self.transform_keys(data)
63
+ case data
64
+ when Hash
65
+ result = {}
66
+ data.each do |key, value|
67
+ snake_key = key.to_s.tr('-', '_').to_sym
68
+ result[snake_key] = transform_keys(value)
69
+ end
70
+ result
71
+ when Array
72
+ data.map { |item| transform_keys(item) }
73
+ else
74
+ data
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lutaml/model'
4
+
5
+ # {
6
+ # "created"=>"2019-07-18T21:28:31+00:00",
7
+ # "updated"=>"2021-01-21T10:49:57+00:00",
8
+ # "service"=>"github",
9
+ # "identifier"=>"1801875",
10
+ # "nickname"=>"davidsgrogan",
11
+ # "profile-picture"=>"https://avatars.githubusercontent.com/u/1801875?v=4",
12
+ # "href"=>"https://github.com/davidsgrogan",
13
+ # "_links"=>{
14
+ # "user"=>{
15
+ # "href"=>"https://api.w3.org/users/c2yerd5euz48gcw08s44oww8g4oo8w8"
16
+ # }
17
+ # }
18
+ # }
19
+
20
+ module W3cApi
21
+ module Models
22
+ class ConnectedAccountLinks < Lutaml::Model::Serializable
23
+ attribute :user, Link
24
+ end
25
+
26
+ class ConnectedAccount < Base
27
+ attribute :created, :date_time
28
+ attribute :updated, :date_time
29
+ attribute :service, :string
30
+ attribute :identifier, :string
31
+ attribute :nickname, :string
32
+ attribute :profile_picture, :string
33
+ attribute :href, :string
34
+ attribute :_links, ConnectedAccountLinks
35
+
36
+ def self.from_response(response)
37
+ transformed_response = transform_keys(response)
38
+ account = new
39
+ transformed_response.each do |key, value|
40
+ case key
41
+ when :_links
42
+ links = value.each_with_object({}) do |(link_name, link_data), acc|
43
+ acc[link_name] = Link.new(href: link_data[:href], title: link_data[:title])
44
+ end
45
+ account._links = ConnectedAccountLinks.new(links)
46
+ else
47
+ account.send("#{key}=", value) if account.respond_to?("#{key}=")
48
+ end
49
+ end
50
+ account
51
+ end
52
+ end
53
+ end
54
+ end