weasyl-ruby 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/lib/weasyl.rb +39 -0
- data/lib/weasyl/api.rb +59 -0
- data/lib/weasyl/media.rb +157 -0
- data/lib/weasyl/messages.rb +97 -0
- data/lib/weasyl/submission.rb +87 -0
- metadata +62 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4a1d6133b4d7d2241406cf52b4a8bdf5661b6ec3
|
4
|
+
data.tar.gz: 2ac8ef501b4271827d0ae8f0aeeed3fe1d0c375f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7cd7647771291b304b3e1b40c748224e8f2651e06f9fa165a96d7a77a96a2f1b03edc7d20efe2eb9c71e3302483914933f3526a8755a6b6b71ca986bd21c887b
|
7
|
+
data.tar.gz: 598cb59ae9f73dd7f65431e5ff90d7381e57548bfda4060dfedf5836b6cae6f8346aaa62bd48dae21268a1820bb51ae6402302879d244d0a788a48c00605065f
|
data/lib/weasyl.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2018 Maxine Michalski <maxine@furfind.net>
|
4
|
+
#
|
5
|
+
# This file is part of Weasyl.
|
6
|
+
#
|
7
|
+
# Weasyl is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# Weasyl is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with Weasyl. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
require 'open-uri'
|
21
|
+
require 'uri'
|
22
|
+
require 'json'
|
23
|
+
|
24
|
+
require_relative 'weasyl/api'
|
25
|
+
require_relative 'weasyl/messages'
|
26
|
+
require_relative 'weasyl/submission'
|
27
|
+
|
28
|
+
# Namespace for all classes in this gem.
|
29
|
+
# @author Maxine Michalski
|
30
|
+
# @since 0.1.0
|
31
|
+
module Weasyl
|
32
|
+
MAJOR = 0
|
33
|
+
MINOR = 1
|
34
|
+
PATCH = 0
|
35
|
+
NAME = 'weasyl-ruby'
|
36
|
+
AUTHOR = '~maxinered'
|
37
|
+
VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}"
|
38
|
+
USER_AGENT = "#{Weasyl::NAME}/#{Weasyl::VERSION} by #{Weasyl::AUTHOR}"
|
39
|
+
end
|
data/lib/weasyl/api.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2018 Maxine Michalski <maxine@furfind.net>
|
4
|
+
#
|
5
|
+
# This file is part of Weasyl.
|
6
|
+
#
|
7
|
+
# Weasyl is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# Weasyl is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with Weasyl. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
require 'singleton'
|
21
|
+
module Weasyl
|
22
|
+
# @author Maxine Michalski
|
23
|
+
# Helper class to abstract actual API communication
|
24
|
+
# @since 0.1.0
|
25
|
+
class API
|
26
|
+
include Singleton
|
27
|
+
# Sets the API key. OAuth isn't supported yet.
|
28
|
+
# @notice This method is not supposed to be used directly!
|
29
|
+
# @return [String] API key for weasyl API
|
30
|
+
attr_accessor :key
|
31
|
+
def fetch(endpoint, params = {})
|
32
|
+
params = if params.empty?
|
33
|
+
''
|
34
|
+
else
|
35
|
+
"?#{params.to_a.map { |a| a.join('=') }.join('&')}"
|
36
|
+
end
|
37
|
+
raise ArgumentError, 'API key can\'t be empty' if @key.nil?
|
38
|
+
JSON.parse(URI.parse("https://weasyl.com/api/#{endpoint}#{params}")
|
39
|
+
.read('X-Weasyl-API-Key' => @key, 'User-Agent' => Weasyl::USER_AGENT),
|
40
|
+
symbolize_names: true)
|
41
|
+
end
|
42
|
+
|
43
|
+
# A test method to test API connection
|
44
|
+
# @author Maxine Michalski
|
45
|
+
# @return [Hash] A Hash object with user information
|
46
|
+
# @since 0.1.0
|
47
|
+
def whoami
|
48
|
+
fetch(:whoami)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Display version of weasyl API we connected to
|
52
|
+
# @author Maxine Michalski
|
53
|
+
# @since 0.1.0
|
54
|
+
# @return [String] String that shows current weasyl API version
|
55
|
+
def version
|
56
|
+
fetch(:version)[:short_sha]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/weasyl/media.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2018 Maxine Michalski <maxine@furfind.net>
|
4
|
+
#
|
5
|
+
# This file is part of Weasyl.
|
6
|
+
#
|
7
|
+
# Weasyl is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# Weasyl is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with Weasyl. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
module Weasyl
|
21
|
+
# Container for media information
|
22
|
+
# @author Maxine Michalski
|
23
|
+
# @since 0.1.0
|
24
|
+
class Media
|
25
|
+
# @return [Array<Weasyl::Thumbnail>] List of thumbnails attached to this
|
26
|
+
# media item
|
27
|
+
attr_reader :thumbnails
|
28
|
+
|
29
|
+
# @return [Array<Weasyl::Thumbnail>] List of cover images attached to this
|
30
|
+
# media item
|
31
|
+
attr_reader :covers
|
32
|
+
|
33
|
+
# @return [Array<Weasyl::Thumbnail>] List of media files attached to this
|
34
|
+
# media item
|
35
|
+
attr_reader :media_files
|
36
|
+
|
37
|
+
# Initializer for a Media object
|
38
|
+
# @author Maxine Michalski
|
39
|
+
# @since 0.1.0
|
40
|
+
# @notice Special thumbnail objects are not supported at this point
|
41
|
+
# @return object
|
42
|
+
def initialize(media)
|
43
|
+
@thumbnails = media[:thumbnail].map { |m| Weasyl::Thumbnail.new(m) }
|
44
|
+
unless media[:cover].nil?
|
45
|
+
@covers = media[:cover].map { |m| Weasyl::Cover.new(m) }
|
46
|
+
end
|
47
|
+
return if media[:submission].nil?
|
48
|
+
@media_files = media[:submission].map { |m| Weasyl::MediaFile.new(m) }
|
49
|
+
end
|
50
|
+
|
51
|
+
# Test for the presence of covers
|
52
|
+
# @author Maxine Michalski
|
53
|
+
# @since 0.1.0
|
54
|
+
# @return [Boolean]
|
55
|
+
def covers?
|
56
|
+
!@covers.nil?
|
57
|
+
end
|
58
|
+
|
59
|
+
# Test for the presence of media files
|
60
|
+
# @author Maxine Michalski
|
61
|
+
# @since 0.1.0
|
62
|
+
# @return [Boolean]
|
63
|
+
def media_files?
|
64
|
+
!@media_files.nil?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Base class for media items
|
69
|
+
# @author Maxine Michalski
|
70
|
+
# @since 0.1.0
|
71
|
+
class MediaItem
|
72
|
+
# @return [Integer] Media ID of this item
|
73
|
+
# @notice Can be nil
|
74
|
+
attr_reader :id
|
75
|
+
|
76
|
+
# @return [URI] Media URL to thumbnail
|
77
|
+
attr_reader :url
|
78
|
+
|
79
|
+
# @return [Array<Weasyl::Link>] Links to other media
|
80
|
+
# @notice This attribute can be nil
|
81
|
+
attr_reader :links
|
82
|
+
|
83
|
+
# Shared initializer for all Media items
|
84
|
+
# @author Maxine Michalski
|
85
|
+
# @since 0.1.0
|
86
|
+
def initialize(item)
|
87
|
+
@id = item[:mediaid]
|
88
|
+
@url = URI.parse(item[:url])
|
89
|
+
return if item[:links].nil?
|
90
|
+
@links = item[:links].map do |k, v|
|
91
|
+
Weasyl::Link.new(k, v)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Indicator of if this MediaItem has an ID or not
|
96
|
+
# @author Maxine Michalski
|
97
|
+
# @since 0.1.0
|
98
|
+
# @return [Boolean]
|
99
|
+
def id?
|
100
|
+
!@id.nil?
|
101
|
+
end
|
102
|
+
|
103
|
+
# Indicator of if this MediaItem object has links or not
|
104
|
+
# @author Maxine Michalski
|
105
|
+
# @since 0.1.0
|
106
|
+
# @return [Boolean]
|
107
|
+
def links?
|
108
|
+
!@links.nil?
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Container for Thumbnail information
|
113
|
+
# @author Maxine Michalski
|
114
|
+
# @since 0.1.0
|
115
|
+
class Thumbnail < MediaItem
|
116
|
+
end
|
117
|
+
|
118
|
+
# Container for cover images
|
119
|
+
# @author Maxine Michalski
|
120
|
+
# @since 0.1.0
|
121
|
+
class Cover < MediaItem
|
122
|
+
end
|
123
|
+
|
124
|
+
# Container for Submission media info (the actual file)
|
125
|
+
# @author Maxine Michalski
|
126
|
+
# @since 0.1.0
|
127
|
+
class MediaFile < MediaItem
|
128
|
+
end
|
129
|
+
|
130
|
+
# A relationship between MediaItems
|
131
|
+
# @author Maxine Michalski
|
132
|
+
# @since 0.1.0
|
133
|
+
class Link
|
134
|
+
# @return [Symbol] Type of item this link links
|
135
|
+
attr_reader :type
|
136
|
+
|
137
|
+
# @return [Array<Weasyl::MediaItem>] Items that are linked to
|
138
|
+
attr_reader :links
|
139
|
+
|
140
|
+
# Initializer for link object
|
141
|
+
# @author Maxine Michalski
|
142
|
+
# @since 0.1.0
|
143
|
+
# @return object
|
144
|
+
def initialize(type, link)
|
145
|
+
case @type = type
|
146
|
+
when :cover
|
147
|
+
@links = link.map { |l| Weasyl::Cover.new(l) }
|
148
|
+
when :thumbnail
|
149
|
+
@links = link.map { |l| Weasyl::Thumbnail.new(l) }
|
150
|
+
when :submission
|
151
|
+
@links = link.map { |l| Weasyl::MediaFile.new(l) }
|
152
|
+
else
|
153
|
+
raise ArgumentError, "Unknown type: #{@type}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2018 Maxine Michalski <maxine@furfind.net>
|
4
|
+
#
|
5
|
+
# This file is part of Weasyl.
|
6
|
+
#
|
7
|
+
# Weasyl is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# Weasyl is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with Weasyl. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
module Weasyl
|
21
|
+
# Helper module to fetch information about messages.
|
22
|
+
# @author Maxine Michalski
|
23
|
+
# @since 0.1.0
|
24
|
+
module Messages
|
25
|
+
# Fetch a summary of new message counts
|
26
|
+
# @author Maxine Michalski
|
27
|
+
# @since 0.1.0
|
28
|
+
# @return [Weasyl::Messages::Summary]
|
29
|
+
def self.summary
|
30
|
+
Weasyl::Messages::Summary.new(Weasyl::API.instance
|
31
|
+
.fetch('messages/summary'))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Fetch a list of submissions for the currently logged in user
|
35
|
+
# @author Maxine Michalski
|
36
|
+
# @since 0.1.0
|
37
|
+
# @param count [Integer] max amount of submissions returned (100)
|
38
|
+
# @param backtime [Integer] UNIX EPOCH, only submissions after this time are
|
39
|
+
# returned
|
40
|
+
# @param nexttime [Integer] UNIX EPOCH, only submissions before this time
|
41
|
+
# are returned
|
42
|
+
# @notice This method accepts a block, that goes through all available
|
43
|
+
# submissions. While the non-block version only returns 100 submissions at
|
44
|
+
# all.
|
45
|
+
# @return [Weasyl::SubmissionList]
|
46
|
+
def self.submissions(count = 100, backtime = 0, nexttime = Time.now.to_i)
|
47
|
+
params = { count: count, backtime: backtime, nexttime: nexttime }
|
48
|
+
subs = nil
|
49
|
+
loop do
|
50
|
+
subs = fetch_subs(params)
|
51
|
+
break if subs[:nexttime].nil? || !block_given?
|
52
|
+
subs[:submissions].each { |s| yield s }
|
53
|
+
params[:nexttime] = subs[:nexttime]
|
54
|
+
end
|
55
|
+
subs
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.fetch_subs(params)
|
59
|
+
subs = Weasyl::API.instance.fetch('messages/submissions', params)
|
60
|
+
subs[:submissions].map! do |s|
|
61
|
+
Weasyl::Submission.new(s)
|
62
|
+
end
|
63
|
+
subs
|
64
|
+
end
|
65
|
+
private_class_method :fetch_subs
|
66
|
+
|
67
|
+
# Class that represents a summary of message information.
|
68
|
+
# @author Maxine Michalski
|
69
|
+
# @since 0.1.0
|
70
|
+
class Summary
|
71
|
+
# @return [Integer] Count of unread comments
|
72
|
+
attr_reader :comments
|
73
|
+
|
74
|
+
# @return [Integer] Count of unread journals
|
75
|
+
attr_reader :journals
|
76
|
+
|
77
|
+
# @return [Integer] Count of unread notifications
|
78
|
+
attr_reader :notifications
|
79
|
+
|
80
|
+
# @return [Integer] Count of unseen submissions
|
81
|
+
attr_reader :submissions
|
82
|
+
|
83
|
+
# @return [Integer] Count of unread notes
|
84
|
+
attr_reader :unread_notes
|
85
|
+
|
86
|
+
# Initializer for Message summaries
|
87
|
+
# @author Maxine Michalski
|
88
|
+
# @since 0.1.0
|
89
|
+
# @return object
|
90
|
+
def initialize(sum)
|
91
|
+
sum.each do |k, v|
|
92
|
+
instance_variable_set("@#{k}".to_sym, v)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2018 Maxine Michalski <maxine@furfind.net>
|
4
|
+
#
|
5
|
+
# This file is part of Weasyl.
|
6
|
+
#
|
7
|
+
# Weasyl is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# Weasyl is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with Weasyl. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
require 'time'
|
21
|
+
require_relative 'media'
|
22
|
+
|
23
|
+
module Weasyl
|
24
|
+
# Container for submission information
|
25
|
+
# @author Maxine Michalski
|
26
|
+
# @since 0.1.0
|
27
|
+
class Submission
|
28
|
+
# @return [String] Owner of submission
|
29
|
+
attr_reader :owner
|
30
|
+
|
31
|
+
# @return [String] Owner of submission
|
32
|
+
attr_reader :owner_login
|
33
|
+
|
34
|
+
# @return [Time] Timestamp when submission was uploaded
|
35
|
+
attr_reader :posted_at
|
36
|
+
|
37
|
+
# @return [String] Rating marker
|
38
|
+
attr_reader :rating
|
39
|
+
|
40
|
+
# @return [Integer] Submission id
|
41
|
+
attr_reader :id
|
42
|
+
|
43
|
+
# @return [String] Submission sub-type
|
44
|
+
attr_reader :subtype
|
45
|
+
|
46
|
+
# @return [Array<String>] Tags associated with this submission
|
47
|
+
attr_reader :tags
|
48
|
+
|
49
|
+
# @return [String] Title of submission
|
50
|
+
attr_reader :title
|
51
|
+
|
52
|
+
# @return [String] Type of submission
|
53
|
+
attr_reader :type
|
54
|
+
|
55
|
+
# @return [URI] Link to submision
|
56
|
+
attr_reader :link
|
57
|
+
|
58
|
+
# @return [Weasyl::Media] Media object for current submission
|
59
|
+
attr_reader :media
|
60
|
+
|
61
|
+
# Initializer for Submission containers
|
62
|
+
# @author Maxine Michalski
|
63
|
+
# @since 0.1.0
|
64
|
+
# @return object
|
65
|
+
def initialize(sub)
|
66
|
+
sub.select! do |k, _|
|
67
|
+
%i[rating tags link owner owner_login submitid title media posted_at
|
68
|
+
subtype type].include?(k)
|
69
|
+
end
|
70
|
+
sub_prepared(sub).each do |k, v|
|
71
|
+
instance_variable_set("@#{k}".to_sym, v)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def sub_prepared(sub)
|
78
|
+
sub.map do |k, v|
|
79
|
+
v = Time.parse(v) if k == :posted_at
|
80
|
+
v = URI.parse(v) if k == :link
|
81
|
+
v = Weasyl::Media.new(v) if k == :media
|
82
|
+
k = :id if k == :submitid
|
83
|
+
[k, v]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: weasyl-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Maxine Michalski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-10-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
description: Ruby bindings for the weasyl art board
|
28
|
+
email: maxine@furfind.net
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/weasyl.rb
|
34
|
+
- lib/weasyl/api.rb
|
35
|
+
- lib/weasyl/media.rb
|
36
|
+
- lib/weasyl/messages.rb
|
37
|
+
- lib/weasyl/submission.rb
|
38
|
+
homepage: https://github.com/maxine-red/weasyl
|
39
|
+
licenses:
|
40
|
+
- GPL-3.0
|
41
|
+
metadata: {}
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - "~>"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '2.3'
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 2.5.2.1
|
59
|
+
signing_key:
|
60
|
+
specification_version: 4
|
61
|
+
summary: Ruby bindings for weasyl
|
62
|
+
test_files: []
|