ortfodb 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/ortfodb/configuration.rb +251 -0
- data/lib/ortfodb/database.rb +439 -0
- data/lib/ortfodb/exporter.rb +120 -0
- data/lib/ortfodb/tags.rb +105 -0
- data/lib/ortfodb/technologies.rb +89 -0
- data/lib/ortfodb/version.rb +3 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d68be6f3abc3321418707238fb49360dd099acd9205f427dcd2aeba47da325cc
|
4
|
+
data.tar.gz: 1d72869648fb3efaeab1234642f1fc1126a8adef8758c6d1687ae300594e5b9a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6369f2be64be4fde1f479cef9cdcc85ff9f7b461998b09bb09294071676b9ee2228295b419ef384ae466c025fc7f1c16a332ab43295a5d51d992cdc51b323784
|
7
|
+
data.tar.gz: 70429cae954a86b7cc1bf2c3bbe7e005ca467958423aa45ebbfb2c88c8aa4b814bf1e7f12d0af1d91bc0f3025726647cb793f911433318ccaefe482b049d5b13
|
@@ -0,0 +1,251 @@
|
|
1
|
+
# This code may look unusually verbose for Ruby (and it is), but
|
2
|
+
# it performs some subtle and complex validation of JSON data.
|
3
|
+
#
|
4
|
+
# To parse this JSON, add 'dry-struct' and 'dry-types' gems, then do:
|
5
|
+
#
|
6
|
+
# configuration = Configuration.from_json! "{…}"
|
7
|
+
# puts configuration.technologies.repository
|
8
|
+
#
|
9
|
+
# If from_json! succeeds, the value returned matches the schema.
|
10
|
+
|
11
|
+
require 'json'
|
12
|
+
require 'dry-types'
|
13
|
+
require 'dry-struct'
|
14
|
+
|
15
|
+
module Ortfodb
|
16
|
+
module Types
|
17
|
+
include Dry.Types(default: :nominal)
|
18
|
+
|
19
|
+
Integer = Strict::Integer
|
20
|
+
Bool = Strict::Bool
|
21
|
+
Hash = Strict::Hash
|
22
|
+
String = Strict::String
|
23
|
+
end
|
24
|
+
|
25
|
+
class ExtractColors < Dry::Struct
|
26
|
+
attribute :default_files, Types.Array(Types::String)
|
27
|
+
attribute :enabled, Types::Bool
|
28
|
+
attribute :extract, Types.Array(Types::String)
|
29
|
+
|
30
|
+
def self.from_dynamic!(d)
|
31
|
+
d = Types::Hash[d]
|
32
|
+
new(
|
33
|
+
default_files: d.fetch("default files"),
|
34
|
+
enabled: d.fetch("enabled"),
|
35
|
+
extract: d.fetch("extract"),
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.from_json!(json)
|
40
|
+
from_dynamic!(JSON.parse(json))
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_dynamic
|
44
|
+
{
|
45
|
+
"default files" => default_files,
|
46
|
+
"enabled" => enabled,
|
47
|
+
"extract" => extract,
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_json(options = nil)
|
52
|
+
JSON.generate(to_dynamic, options)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class MakeGifs < Dry::Struct
|
57
|
+
attribute :enabled, Types::Bool
|
58
|
+
attribute :file_name_template, Types::String
|
59
|
+
|
60
|
+
def self.from_dynamic!(d)
|
61
|
+
d = Types::Hash[d]
|
62
|
+
new(
|
63
|
+
enabled: d.fetch("enabled"),
|
64
|
+
file_name_template: d.fetch("file name template"),
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.from_json!(json)
|
69
|
+
from_dynamic!(JSON.parse(json))
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_dynamic
|
73
|
+
{
|
74
|
+
"enabled" => enabled,
|
75
|
+
"file name template" => file_name_template,
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_json(options = nil)
|
80
|
+
JSON.generate(to_dynamic, options)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class MakeThumbnails < Dry::Struct
|
85
|
+
attribute :enabled, Types::Bool
|
86
|
+
attribute :file_name_template, Types::String
|
87
|
+
attribute :input_file, Types::String
|
88
|
+
attribute :sizes, Types.Array(Types::Integer)
|
89
|
+
|
90
|
+
def self.from_dynamic!(d)
|
91
|
+
d = Types::Hash[d]
|
92
|
+
new(
|
93
|
+
enabled: d.fetch("enabled"),
|
94
|
+
file_name_template: d.fetch("file name template"),
|
95
|
+
input_file: d.fetch("input file"),
|
96
|
+
sizes: d.fetch("sizes"),
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.from_json!(json)
|
101
|
+
from_dynamic!(JSON.parse(json))
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_dynamic
|
105
|
+
{
|
106
|
+
"enabled" => enabled,
|
107
|
+
"file name template" => file_name_template,
|
108
|
+
"input file" => input_file,
|
109
|
+
"sizes" => sizes,
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_json(options = nil)
|
114
|
+
JSON.generate(to_dynamic, options)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class Media < Dry::Struct
|
119
|
+
attribute :at, Types::String
|
120
|
+
|
121
|
+
def self.from_dynamic!(d)
|
122
|
+
d = Types::Hash[d]
|
123
|
+
new(
|
124
|
+
at: d.fetch("at"),
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.from_json!(json)
|
129
|
+
from_dynamic!(JSON.parse(json))
|
130
|
+
end
|
131
|
+
|
132
|
+
def to_dynamic
|
133
|
+
{
|
134
|
+
"at" => at,
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_json(options = nil)
|
139
|
+
JSON.generate(to_dynamic, options)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class Tags < Dry::Struct
|
144
|
+
attribute :repository, Types::String
|
145
|
+
|
146
|
+
def self.from_dynamic!(d)
|
147
|
+
d = Types::Hash[d]
|
148
|
+
new(
|
149
|
+
repository: d.fetch("repository"),
|
150
|
+
)
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.from_json!(json)
|
154
|
+
from_dynamic!(JSON.parse(json))
|
155
|
+
end
|
156
|
+
|
157
|
+
def to_dynamic
|
158
|
+
{
|
159
|
+
"repository" => repository,
|
160
|
+
}
|
161
|
+
end
|
162
|
+
|
163
|
+
def to_json(options = nil)
|
164
|
+
JSON.generate(to_dynamic, options)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class Technologies < Dry::Struct
|
169
|
+
attribute :repository, Types::String
|
170
|
+
|
171
|
+
def self.from_dynamic!(d)
|
172
|
+
d = Types::Hash[d]
|
173
|
+
new(
|
174
|
+
repository: d.fetch("repository"),
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.from_json!(json)
|
179
|
+
from_dynamic!(JSON.parse(json))
|
180
|
+
end
|
181
|
+
|
182
|
+
def to_dynamic
|
183
|
+
{
|
184
|
+
"repository" => repository,
|
185
|
+
}
|
186
|
+
end
|
187
|
+
|
188
|
+
def to_json(options = nil)
|
189
|
+
JSON.generate(to_dynamic, options)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Configuration represents what the ortfodb.yaml configuration file describes.
|
194
|
+
class Configuration < Dry::Struct
|
195
|
+
attribute :build_metadata_file, Types::String
|
196
|
+
|
197
|
+
# Exporter-specific configuration. Maps exporter names to their configuration.
|
198
|
+
attribute :exporters, Types::Hash.meta(of: Types::Hash.meta(of: Types::Any)).optional
|
199
|
+
|
200
|
+
attribute :extract_colors, ExtractColors
|
201
|
+
attribute :make_gifs, MakeGifs
|
202
|
+
attribute :make_thumbnails, MakeThumbnails
|
203
|
+
attribute :media, Media
|
204
|
+
|
205
|
+
# Path to the directory containing all projects. Must be absolute.
|
206
|
+
attribute :projects_at, Types::String
|
207
|
+
|
208
|
+
attribute :scattered_mode_folder, Types::String
|
209
|
+
attribute :tags, Tags
|
210
|
+
attribute :technologies, Technologies
|
211
|
+
|
212
|
+
def self.from_dynamic!(d)
|
213
|
+
d = Types::Hash[d]
|
214
|
+
new(
|
215
|
+
build_metadata_file: d.fetch("build metadata file"),
|
216
|
+
exporters: Types::Hash.optional[d["exporters"]]&.map { |k, v| [k, Types::Hash[v].map { |k, v| [k, Types::Any[v]] }.to_h] }&.to_h,
|
217
|
+
extract_colors: ExtractColors.from_dynamic!(d.fetch("extract colors")),
|
218
|
+
make_gifs: MakeGifs.from_dynamic!(d.fetch("make gifs")),
|
219
|
+
make_thumbnails: MakeThumbnails.from_dynamic!(d.fetch("make thumbnails")),
|
220
|
+
media: Media.from_dynamic!(d.fetch("media")),
|
221
|
+
projects_at: d.fetch("projects at"),
|
222
|
+
scattered_mode_folder: d.fetch("scattered mode folder"),
|
223
|
+
tags: Tags.from_dynamic!(d.fetch("tags")),
|
224
|
+
technologies: Technologies.from_dynamic!(d.fetch("technologies")),
|
225
|
+
)
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.from_json!(json)
|
229
|
+
from_dynamic!(JSON.parse(json))
|
230
|
+
end
|
231
|
+
|
232
|
+
def to_dynamic
|
233
|
+
{
|
234
|
+
"build metadata file" => build_metadata_file,
|
235
|
+
"exporters" => exporters,
|
236
|
+
"extract colors" => extract_colors.to_dynamic,
|
237
|
+
"make gifs" => make_gifs.to_dynamic,
|
238
|
+
"make thumbnails" => make_thumbnails.to_dynamic,
|
239
|
+
"media" => media.to_dynamic,
|
240
|
+
"projects at" => projects_at,
|
241
|
+
"scattered mode folder" => scattered_mode_folder,
|
242
|
+
"tags" => tags.to_dynamic,
|
243
|
+
"technologies" => technologies.to_dynamic,
|
244
|
+
}
|
245
|
+
end
|
246
|
+
|
247
|
+
def to_json(options = nil)
|
248
|
+
JSON.generate(to_dynamic, options)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
@@ -0,0 +1,439 @@
|
|
1
|
+
# This code may look unusually verbose for Ruby (and it is), but
|
2
|
+
# it performs some subtle and complex validation of JSON data.
|
3
|
+
#
|
4
|
+
# To parse this JSON, add 'dry-struct' and 'dry-types' gems, then do:
|
5
|
+
#
|
6
|
+
# database = Database.from_json! "{…}"
|
7
|
+
# puts database["…"].metadata.tags.first
|
8
|
+
#
|
9
|
+
# If from_json! succeeds, the value returned matches the schema.
|
10
|
+
|
11
|
+
require 'json'
|
12
|
+
require 'dry-types'
|
13
|
+
require 'dry-struct'
|
14
|
+
|
15
|
+
module Ortfodb
|
16
|
+
module Types
|
17
|
+
include Dry.Types(default: :nominal)
|
18
|
+
|
19
|
+
Integer = Strict::Integer
|
20
|
+
Bool = Strict::Bool
|
21
|
+
Hash = Strict::Hash
|
22
|
+
String = Strict::String
|
23
|
+
Double = Strict::Float | Strict::Integer
|
24
|
+
end
|
25
|
+
|
26
|
+
# MediaAttributes stores which HTML attributes should be added to the media.
|
27
|
+
class Attributes < Dry::Struct
|
28
|
+
|
29
|
+
# Controlled with attribute character > (adds)
|
30
|
+
attribute :autoplay, Types::Bool
|
31
|
+
|
32
|
+
# Controlled with attribute character = (removes)
|
33
|
+
attribute :controls, Types::Bool
|
34
|
+
|
35
|
+
# Controlled with attribute character ~ (adds)
|
36
|
+
attribute :attributes_loop, Types::Bool
|
37
|
+
|
38
|
+
# Controlled with attribute character > (adds)
|
39
|
+
attribute :muted, Types::Bool
|
40
|
+
|
41
|
+
# Controlled with attribute character = (adds)
|
42
|
+
attribute :playsinline, Types::Bool
|
43
|
+
|
44
|
+
def self.from_dynamic!(d)
|
45
|
+
d = Types::Hash[d]
|
46
|
+
new(
|
47
|
+
autoplay: d.fetch("autoplay"),
|
48
|
+
controls: d.fetch("controls"),
|
49
|
+
attributes_loop: d.fetch("loop"),
|
50
|
+
muted: d.fetch("muted"),
|
51
|
+
playsinline: d.fetch("playsinline"),
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.from_json!(json)
|
56
|
+
from_dynamic!(JSON.parse(json))
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_dynamic
|
60
|
+
{
|
61
|
+
"autoplay" => autoplay,
|
62
|
+
"controls" => controls,
|
63
|
+
"loop" => attributes_loop,
|
64
|
+
"muted" => muted,
|
65
|
+
"playsinline" => playsinline,
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_json(options = nil)
|
70
|
+
JSON.generate(to_dynamic, options)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# ColorPalette reprensents the object in a Work's metadata.colors.
|
75
|
+
class Colors < Dry::Struct
|
76
|
+
attribute :primary, Types::String
|
77
|
+
attribute :secondary, Types::String
|
78
|
+
attribute :tertiary, Types::String
|
79
|
+
|
80
|
+
def self.from_dynamic!(d)
|
81
|
+
d = Types::Hash[d]
|
82
|
+
new(
|
83
|
+
primary: d.fetch("primary"),
|
84
|
+
secondary: d.fetch("secondary"),
|
85
|
+
tertiary: d.fetch("tertiary"),
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.from_json!(json)
|
90
|
+
from_dynamic!(JSON.parse(json))
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_dynamic
|
94
|
+
{
|
95
|
+
"primary" => primary,
|
96
|
+
"secondary" => secondary,
|
97
|
+
"tertiary" => tertiary,
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_json(options = nil)
|
102
|
+
JSON.generate(to_dynamic, options)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# ImageDimensions represents metadata about a media as it's extracted from its file.
|
107
|
+
class Dimensions < Dry::Struct
|
108
|
+
|
109
|
+
# width / height
|
110
|
+
attribute :aspect_ratio, Types::Double
|
111
|
+
|
112
|
+
# Height in pixels
|
113
|
+
attribute :height, Types::Integer
|
114
|
+
|
115
|
+
# Width in pixels
|
116
|
+
attribute :width, Types::Integer
|
117
|
+
|
118
|
+
def self.from_dynamic!(d)
|
119
|
+
d = Types::Hash[d]
|
120
|
+
new(
|
121
|
+
aspect_ratio: d.fetch("aspectRatio"),
|
122
|
+
height: d.fetch("height"),
|
123
|
+
width: d.fetch("width"),
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.from_json!(json)
|
128
|
+
from_dynamic!(JSON.parse(json))
|
129
|
+
end
|
130
|
+
|
131
|
+
def to_dynamic
|
132
|
+
{
|
133
|
+
"aspectRatio" => aspect_ratio,
|
134
|
+
"height" => height,
|
135
|
+
"width" => width,
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
def to_json(options = nil)
|
140
|
+
JSON.generate(to_dynamic, options)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class Thumbnails < Dry::Struct
|
145
|
+
|
146
|
+
def self.from_dynamic!(d)
|
147
|
+
d = Types::Hash[d]
|
148
|
+
new(
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.from_json!(json)
|
153
|
+
from_dynamic!(JSON.parse(json))
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_dynamic
|
157
|
+
{
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
def to_json(options = nil)
|
162
|
+
JSON.generate(to_dynamic, options)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class BlockElement < Dry::Struct
|
167
|
+
attribute :alt, Types::String
|
168
|
+
|
169
|
+
# whether the media has been analyzed
|
170
|
+
attribute :analyzed, Types::Bool
|
171
|
+
|
172
|
+
attribute :anchor, Types::String
|
173
|
+
attribute :attributes, Attributes
|
174
|
+
attribute :caption, Types::String
|
175
|
+
attribute :colors, Colors
|
176
|
+
|
177
|
+
# html
|
178
|
+
attribute :content, Types::String
|
179
|
+
|
180
|
+
attribute :content_type, Types::String
|
181
|
+
attribute :dimensions, Dimensions
|
182
|
+
attribute :dist_source, Types::String
|
183
|
+
|
184
|
+
# in seconds
|
185
|
+
attribute :duration, Types::Double
|
186
|
+
|
187
|
+
attribute :has_sound, Types::Bool
|
188
|
+
attribute :id, Types::String
|
189
|
+
attribute :index, Types::Integer
|
190
|
+
attribute :online, Types::Bool
|
191
|
+
attribute :relative_source, Types::String
|
192
|
+
|
193
|
+
# in bytes
|
194
|
+
attribute :size, Types::Integer
|
195
|
+
|
196
|
+
attribute :text, Types::String
|
197
|
+
attribute :thumbnails, Thumbnails
|
198
|
+
attribute :thumbnails_built_at, Types::String
|
199
|
+
attribute :title, Types::String
|
200
|
+
attribute :database_schema_type, Types::String
|
201
|
+
attribute :url, Types::String
|
202
|
+
|
203
|
+
def self.from_dynamic!(d)
|
204
|
+
d = Types::Hash[d]
|
205
|
+
new(
|
206
|
+
alt: d.fetch("alt"),
|
207
|
+
analyzed: d.fetch("analyzed"),
|
208
|
+
anchor: d.fetch("anchor"),
|
209
|
+
attributes: Attributes.from_dynamic!(d.fetch("attributes")),
|
210
|
+
caption: d.fetch("caption"),
|
211
|
+
colors: Colors.from_dynamic!(d.fetch("colors")),
|
212
|
+
content: d.fetch("content"),
|
213
|
+
content_type: d.fetch("contentType"),
|
214
|
+
dimensions: Dimensions.from_dynamic!(d.fetch("dimensions")),
|
215
|
+
dist_source: d.fetch("distSource"),
|
216
|
+
duration: d.fetch("duration"),
|
217
|
+
has_sound: d.fetch("hasSound"),
|
218
|
+
id: d.fetch("id"),
|
219
|
+
index: d.fetch("index"),
|
220
|
+
online: d.fetch("online"),
|
221
|
+
relative_source: d.fetch("relativeSource"),
|
222
|
+
size: d.fetch("size"),
|
223
|
+
text: d.fetch("text"),
|
224
|
+
thumbnails: Thumbnails.from_dynamic!(d.fetch("thumbnails")),
|
225
|
+
thumbnails_built_at: d.fetch("thumbnailsBuiltAt"),
|
226
|
+
title: d.fetch("title"),
|
227
|
+
database_schema_type: d.fetch("type"),
|
228
|
+
url: d.fetch("url"),
|
229
|
+
)
|
230
|
+
end
|
231
|
+
|
232
|
+
def self.from_json!(json)
|
233
|
+
from_dynamic!(JSON.parse(json))
|
234
|
+
end
|
235
|
+
|
236
|
+
def to_dynamic
|
237
|
+
{
|
238
|
+
"alt" => alt,
|
239
|
+
"analyzed" => analyzed,
|
240
|
+
"anchor" => anchor,
|
241
|
+
"attributes" => attributes.to_dynamic,
|
242
|
+
"caption" => caption,
|
243
|
+
"colors" => colors.to_dynamic,
|
244
|
+
"content" => content,
|
245
|
+
"contentType" => content_type,
|
246
|
+
"dimensions" => dimensions.to_dynamic,
|
247
|
+
"distSource" => dist_source,
|
248
|
+
"duration" => duration,
|
249
|
+
"hasSound" => has_sound,
|
250
|
+
"id" => id,
|
251
|
+
"index" => index,
|
252
|
+
"online" => online,
|
253
|
+
"relativeSource" => relative_source,
|
254
|
+
"size" => size,
|
255
|
+
"text" => text,
|
256
|
+
"thumbnails" => thumbnails.to_dynamic,
|
257
|
+
"thumbnailsBuiltAt" => thumbnails_built_at,
|
258
|
+
"title" => title,
|
259
|
+
"type" => database_schema_type,
|
260
|
+
"url" => url,
|
261
|
+
}
|
262
|
+
end
|
263
|
+
|
264
|
+
def to_json(options = nil)
|
265
|
+
JSON.generate(to_dynamic, options)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
class ContentValue < Dry::Struct
|
270
|
+
attribute :blocks, Types.Array(BlockElement)
|
271
|
+
attribute :footnotes, Types::Hash.meta(of: Types::String)
|
272
|
+
attribute :layout, Types.Array(Types.Array(Types::String))
|
273
|
+
attribute :title, Types::String
|
274
|
+
|
275
|
+
def self.from_dynamic!(d)
|
276
|
+
d = Types::Hash[d]
|
277
|
+
new(
|
278
|
+
blocks: d.fetch("blocks").map { |x| BlockElement.from_dynamic!(x) },
|
279
|
+
footnotes: Types::Hash[d.fetch("footnotes")].map { |k, v| [k, Types::String[v]] }.to_h,
|
280
|
+
layout: d.fetch("layout"),
|
281
|
+
title: d.fetch("title"),
|
282
|
+
)
|
283
|
+
end
|
284
|
+
|
285
|
+
def self.from_json!(json)
|
286
|
+
from_dynamic!(JSON.parse(json))
|
287
|
+
end
|
288
|
+
|
289
|
+
def to_dynamic
|
290
|
+
{
|
291
|
+
"blocks" => blocks.map { |x| x.to_dynamic },
|
292
|
+
"footnotes" => footnotes,
|
293
|
+
"layout" => layout,
|
294
|
+
"title" => title,
|
295
|
+
}
|
296
|
+
end
|
297
|
+
|
298
|
+
def to_json(options = nil)
|
299
|
+
JSON.generate(to_dynamic, options)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
class DatabaseMetadataClass < Dry::Struct
|
304
|
+
|
305
|
+
# Partial is true if the database was not fully built.
|
306
|
+
attribute :partial, Types::Bool
|
307
|
+
|
308
|
+
def self.from_dynamic!(d)
|
309
|
+
d = Types::Hash[d]
|
310
|
+
new(
|
311
|
+
partial: d.fetch("Partial"),
|
312
|
+
)
|
313
|
+
end
|
314
|
+
|
315
|
+
def self.from_json!(json)
|
316
|
+
from_dynamic!(JSON.parse(json))
|
317
|
+
end
|
318
|
+
|
319
|
+
def to_dynamic
|
320
|
+
{
|
321
|
+
"Partial" => partial,
|
322
|
+
}
|
323
|
+
end
|
324
|
+
|
325
|
+
def to_json(options = nil)
|
326
|
+
JSON.generate(to_dynamic, options)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
class Metadata < Dry::Struct
|
331
|
+
attribute :additional_metadata, Types::Hash.meta(of: Types::Any)
|
332
|
+
attribute :aliases, Types.Array(Types::String)
|
333
|
+
attribute :colors, Colors
|
334
|
+
attribute :database_metadata, DatabaseMetadataClass
|
335
|
+
attribute :finished, Types::String
|
336
|
+
attribute :made_with, Types.Array(Types::String)
|
337
|
+
attribute :page_background, Types::String
|
338
|
+
attribute :private, Types::Bool
|
339
|
+
attribute :started, Types::String
|
340
|
+
attribute :tags, Types.Array(Types::String)
|
341
|
+
attribute :thumbnail, Types::String
|
342
|
+
attribute :title_style, Types::String
|
343
|
+
attribute :wip, Types::Bool
|
344
|
+
|
345
|
+
def self.from_dynamic!(d)
|
346
|
+
d = Types::Hash[d]
|
347
|
+
new(
|
348
|
+
additional_metadata: Types::Hash[d.fetch("additionalMetadata")].map { |k, v| [k, Types::Any[v]] }.to_h,
|
349
|
+
aliases: d.fetch("aliases"),
|
350
|
+
colors: Colors.from_dynamic!(d.fetch("colors")),
|
351
|
+
database_metadata: DatabaseMetadataClass.from_dynamic!(d.fetch("databaseMetadata")),
|
352
|
+
finished: d.fetch("finished"),
|
353
|
+
made_with: d.fetch("madeWith"),
|
354
|
+
page_background: d.fetch("pageBackground"),
|
355
|
+
private: d.fetch("private"),
|
356
|
+
started: d.fetch("started"),
|
357
|
+
tags: d.fetch("tags"),
|
358
|
+
thumbnail: d.fetch("thumbnail"),
|
359
|
+
title_style: d.fetch("titleStyle"),
|
360
|
+
wip: d.fetch("wip"),
|
361
|
+
)
|
362
|
+
end
|
363
|
+
|
364
|
+
def self.from_json!(json)
|
365
|
+
from_dynamic!(JSON.parse(json))
|
366
|
+
end
|
367
|
+
|
368
|
+
def to_dynamic
|
369
|
+
{
|
370
|
+
"additionalMetadata" => additional_metadata,
|
371
|
+
"aliases" => aliases,
|
372
|
+
"colors" => colors.to_dynamic,
|
373
|
+
"databaseMetadata" => database_metadata.to_dynamic,
|
374
|
+
"finished" => finished,
|
375
|
+
"madeWith" => made_with,
|
376
|
+
"pageBackground" => page_background,
|
377
|
+
"private" => private,
|
378
|
+
"started" => started,
|
379
|
+
"tags" => tags,
|
380
|
+
"thumbnail" => thumbnail,
|
381
|
+
"titleStyle" => title_style,
|
382
|
+
"wip" => wip,
|
383
|
+
}
|
384
|
+
end
|
385
|
+
|
386
|
+
def to_json(options = nil)
|
387
|
+
JSON.generate(to_dynamic, options)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# AnalyzedWork represents a complete work, with analyzed mediae.
|
392
|
+
class DatabaseValue < Dry::Struct
|
393
|
+
attribute :built_at, Types::String
|
394
|
+
attribute :content, Types::Hash.meta(of: ContentValue)
|
395
|
+
attribute :description_hash, Types::String
|
396
|
+
attribute :id, Types::String
|
397
|
+
attribute :metadata, Metadata
|
398
|
+
attribute :partial, Types::Bool
|
399
|
+
|
400
|
+
def self.from_dynamic!(d)
|
401
|
+
d = Types::Hash[d]
|
402
|
+
new(
|
403
|
+
built_at: d.fetch("builtAt"),
|
404
|
+
content: Types::Hash[d.fetch("content")].map { |k, v| [k, ContentValue.from_dynamic!(v)] }.to_h,
|
405
|
+
description_hash: d.fetch("descriptionHash"),
|
406
|
+
id: d.fetch("id"),
|
407
|
+
metadata: Metadata.from_dynamic!(d.fetch("metadata")),
|
408
|
+
partial: d.fetch("Partial"),
|
409
|
+
)
|
410
|
+
end
|
411
|
+
|
412
|
+
def self.from_json!(json)
|
413
|
+
from_dynamic!(JSON.parse(json))
|
414
|
+
end
|
415
|
+
|
416
|
+
def to_dynamic
|
417
|
+
{
|
418
|
+
"builtAt" => built_at,
|
419
|
+
"content" => content.map { |k, v| [k, v.to_dynamic] }.to_h,
|
420
|
+
"descriptionHash" => description_hash,
|
421
|
+
"id" => id,
|
422
|
+
"metadata" => metadata.to_dynamic,
|
423
|
+
"Partial" => partial,
|
424
|
+
}
|
425
|
+
end
|
426
|
+
|
427
|
+
def to_json(options = nil)
|
428
|
+
JSON.generate(to_dynamic, options)
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
module Ortfodb
|
433
|
+
class Database
|
434
|
+
def self.from_json!(json)
|
435
|
+
Types::Hash[JSON.parse(json, quirks_mode: true)].map { |k, v| [k, DatabaseValue.from_dynamic!(v)] }.to_h
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# This code may look unusually verbose for Ruby (and it is), but
|
2
|
+
# it performs some subtle and complex validation of JSON data.
|
3
|
+
#
|
4
|
+
# To parse this JSON, add 'dry-struct' and 'dry-types' gems, then do:
|
5
|
+
#
|
6
|
+
# exporter = Exporter.from_json! "{…}"
|
7
|
+
# puts exporter.work&.first.log&.first
|
8
|
+
#
|
9
|
+
# If from_json! succeeds, the value returned matches the schema.
|
10
|
+
|
11
|
+
require 'json'
|
12
|
+
require 'dry-types'
|
13
|
+
require 'dry-struct'
|
14
|
+
|
15
|
+
module Ortfodb
|
16
|
+
module Types
|
17
|
+
include Dry.Types(default: :nominal)
|
18
|
+
|
19
|
+
Bool = Strict::Bool
|
20
|
+
Hash = Strict::Hash
|
21
|
+
String = Strict::String
|
22
|
+
end
|
23
|
+
|
24
|
+
class ExporterSchema < Dry::Struct
|
25
|
+
|
26
|
+
# Log a message. The first argument is the verb, the second is the color, the third is the
|
27
|
+
# message.
|
28
|
+
attribute :log, Types.Array(Types::String).optional
|
29
|
+
|
30
|
+
# Run a command in a shell
|
31
|
+
attribute :run, Types::String.optional
|
32
|
+
|
33
|
+
def self.from_dynamic!(d)
|
34
|
+
d = Types::Hash[d]
|
35
|
+
new(
|
36
|
+
log: d["log"],
|
37
|
+
run: d["run"],
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.from_json!(json)
|
42
|
+
from_dynamic!(JSON.parse(json))
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_dynamic
|
46
|
+
{
|
47
|
+
"log" => log,
|
48
|
+
"run" => run,
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_json(options = nil)
|
53
|
+
JSON.generate(to_dynamic, options)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Exporter < Dry::Struct
|
58
|
+
|
59
|
+
# Commands to run after the build finishes. Go text template that receives .Data and
|
60
|
+
# .Database, the built database.
|
61
|
+
attribute :after, Types.Array(ExporterSchema).optional
|
62
|
+
|
63
|
+
# Commands to run before the build starts. Go text template that receives .Data
|
64
|
+
attribute :before, Types.Array(ExporterSchema).optional
|
65
|
+
|
66
|
+
# Initial data
|
67
|
+
attribute :data, Types::Hash.meta(of: Types::Any).optional
|
68
|
+
|
69
|
+
# Some documentation about the exporter
|
70
|
+
attribute :description, Types::String
|
71
|
+
|
72
|
+
# The name of the exporter
|
73
|
+
attribute :exporter_name, Types::String
|
74
|
+
|
75
|
+
# List of programs that are required to be available in the PATH for the exporter to run.
|
76
|
+
attribute :requires, Types.Array(Types::String).optional
|
77
|
+
|
78
|
+
# If true, will show every command that is run
|
79
|
+
attribute :verbose, Types::Bool.optional
|
80
|
+
|
81
|
+
# Commands to run during the build, for each work. Go text template that receives .Data and
|
82
|
+
# .Work, the current work.
|
83
|
+
attribute :work, Types.Array(ExporterSchema).optional
|
84
|
+
|
85
|
+
def self.from_dynamic!(d)
|
86
|
+
d = Types::Hash[d]
|
87
|
+
new(
|
88
|
+
after: d["after"]&.map { |x| ExporterSchema.from_dynamic!(x) },
|
89
|
+
before: d["before"]&.map { |x| ExporterSchema.from_dynamic!(x) },
|
90
|
+
data: Types::Hash.optional[d["data"]]&.map { |k, v| [k, Types::Any[v]] }&.to_h,
|
91
|
+
description: d.fetch("description"),
|
92
|
+
exporter_name: d.fetch("name"),
|
93
|
+
requires: d["requires"],
|
94
|
+
verbose: d["verbose"],
|
95
|
+
work: d["work"]&.map { |x| ExporterSchema.from_dynamic!(x) },
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.from_json!(json)
|
100
|
+
from_dynamic!(JSON.parse(json))
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_dynamic
|
104
|
+
{
|
105
|
+
"after" => after&.map { |x| x.to_dynamic },
|
106
|
+
"before" => before&.map { |x| x.to_dynamic },
|
107
|
+
"data" => data,
|
108
|
+
"description" => description,
|
109
|
+
"name" => exporter_name,
|
110
|
+
"requires" => requires,
|
111
|
+
"verbose" => verbose,
|
112
|
+
"work" => work&.map { |x| x.to_dynamic },
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
def to_json(options = nil)
|
117
|
+
JSON.generate(to_dynamic, options)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/ortfodb/tags.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# This code may look unusually verbose for Ruby (and it is), but
|
2
|
+
# it performs some subtle and complex validation of JSON data.
|
3
|
+
#
|
4
|
+
# To parse this JSON, add 'dry-struct' and 'dry-types' gems, then do:
|
5
|
+
#
|
6
|
+
# tags = Tags.from_json! "[…]"
|
7
|
+
# puts tags.first.detect.search.first
|
8
|
+
#
|
9
|
+
# If from_json! succeeds, the value returned matches the schema.
|
10
|
+
|
11
|
+
require 'json'
|
12
|
+
require 'dry-types'
|
13
|
+
require 'dry-struct'
|
14
|
+
|
15
|
+
module Ortfodb
|
16
|
+
module Types
|
17
|
+
include Dry.Types(default: :nominal)
|
18
|
+
|
19
|
+
Hash = Strict::Hash
|
20
|
+
String = Strict::String
|
21
|
+
end
|
22
|
+
|
23
|
+
class Detect < Dry::Struct
|
24
|
+
attribute :files, Types.Array(Types::String)
|
25
|
+
attribute :made_with, Types.Array(Types::String)
|
26
|
+
attribute :search, Types.Array(Types::String)
|
27
|
+
|
28
|
+
def self.from_dynamic!(d)
|
29
|
+
d = Types::Hash[d]
|
30
|
+
new(
|
31
|
+
files: d.fetch("files"),
|
32
|
+
made_with: d.fetch("made with"),
|
33
|
+
search: d.fetch("search"),
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.from_json!(json)
|
38
|
+
from_dynamic!(JSON.parse(json))
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_dynamic
|
42
|
+
{
|
43
|
+
"files" => files,
|
44
|
+
"made with" => made_with,
|
45
|
+
"search" => search,
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_json(options = nil)
|
50
|
+
JSON.generate(to_dynamic, options)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Tag < Dry::Struct
|
55
|
+
attribute :aliases, Types.Array(Types::String)
|
56
|
+
attribute :description, Types::String
|
57
|
+
attribute :detect, Detect
|
58
|
+
attribute :learn_more_at, Types::String
|
59
|
+
attribute :plural, Types::String
|
60
|
+
attribute :singular, Types::String
|
61
|
+
|
62
|
+
def self.from_dynamic!(d)
|
63
|
+
d = Types::Hash[d]
|
64
|
+
new(
|
65
|
+
aliases: d.fetch("aliases"),
|
66
|
+
description: d.fetch("description"),
|
67
|
+
detect: Detect.from_dynamic!(d.fetch("detect")),
|
68
|
+
learn_more_at: d.fetch("learn more at"),
|
69
|
+
plural: d.fetch("plural"),
|
70
|
+
singular: d.fetch("singular"),
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.from_json!(json)
|
75
|
+
from_dynamic!(JSON.parse(json))
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_dynamic
|
79
|
+
{
|
80
|
+
"aliases" => aliases,
|
81
|
+
"description" => description,
|
82
|
+
"detect" => detect.to_dynamic,
|
83
|
+
"learn more at" => learn_more_at,
|
84
|
+
"plural" => plural,
|
85
|
+
"singular" => singular,
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_json(options = nil)
|
90
|
+
JSON.generate(to_dynamic, options)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
module Ortfodb
|
95
|
+
class Tags
|
96
|
+
def self.from_json!(json)
|
97
|
+
tags = JSON.parse(json, quirks_mode: true).map { |x| Tag.from_dynamic!(x) }
|
98
|
+
tags.define_singleton_method(:to_json) do
|
99
|
+
JSON.generate(self.map { |x| x.to_dynamic })
|
100
|
+
end
|
101
|
+
tags
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# This code may look unusually verbose for Ruby (and it is), but
|
2
|
+
# it performs some subtle and complex validation of JSON data.
|
3
|
+
#
|
4
|
+
# To parse this JSON, add 'dry-struct' and 'dry-types' gems, then do:
|
5
|
+
#
|
6
|
+
# technologies = Technologies.from_json! "[…]"
|
7
|
+
# puts technologies.first.files.first
|
8
|
+
#
|
9
|
+
# If from_json! succeeds, the value returned matches the schema.
|
10
|
+
|
11
|
+
require 'json'
|
12
|
+
require 'dry-types'
|
13
|
+
require 'dry-struct'
|
14
|
+
|
15
|
+
module Ortfodb
|
16
|
+
module Types
|
17
|
+
include Dry.Types(default: :nominal)
|
18
|
+
|
19
|
+
Hash = Strict::Hash
|
20
|
+
String = Strict::String
|
21
|
+
end
|
22
|
+
|
23
|
+
class Technology < Dry::Struct
|
24
|
+
attribute :aliases, Types.Array(Types::String)
|
25
|
+
|
26
|
+
# Autodetect contains an expression of the form 'CONTENT in PATH' where CONTENT is a
|
27
|
+
# free-form unquoted string and PATH is a filepath relative to the work folder.
|
28
|
+
# If CONTENT is found in PATH, we consider that technology to be used in the work.
|
29
|
+
attribute :autodetect, Types.Array(Types::String)
|
30
|
+
|
31
|
+
attribute :by, Types::String
|
32
|
+
attribute :description, Types::String
|
33
|
+
|
34
|
+
# Files contains a list of gitignore-style patterns. If the work contains any of the
|
35
|
+
# patterns specified, we consider that technology to be used in the work.
|
36
|
+
attribute :files, Types.Array(Types::String)
|
37
|
+
|
38
|
+
attribute :learn_more_at, Types::String
|
39
|
+
attribute :technology_name, Types::String
|
40
|
+
attribute :slug, Types::String
|
41
|
+
|
42
|
+
def self.from_dynamic!(d)
|
43
|
+
d = Types::Hash[d]
|
44
|
+
new(
|
45
|
+
aliases: d.fetch("aliases"),
|
46
|
+
autodetect: d.fetch("autodetect"),
|
47
|
+
by: d.fetch("by"),
|
48
|
+
description: d.fetch("description"),
|
49
|
+
files: d.fetch("files"),
|
50
|
+
learn_more_at: d.fetch("learn more at"),
|
51
|
+
technology_name: d.fetch("name"),
|
52
|
+
slug: d.fetch("slug"),
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.from_json!(json)
|
57
|
+
from_dynamic!(JSON.parse(json))
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_dynamic
|
61
|
+
{
|
62
|
+
"aliases" => aliases,
|
63
|
+
"autodetect" => autodetect,
|
64
|
+
"by" => by,
|
65
|
+
"description" => description,
|
66
|
+
"files" => files,
|
67
|
+
"learn more at" => learn_more_at,
|
68
|
+
"name" => technology_name,
|
69
|
+
"slug" => slug,
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_json(options = nil)
|
74
|
+
JSON.generate(to_dynamic, options)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module Ortfodb
|
79
|
+
class Technologies
|
80
|
+
def self.from_json!(json)
|
81
|
+
technologies = JSON.parse(json, quirks_mode: true).map { |x| Technology.from_dynamic!(x) }
|
82
|
+
technologies.define_singleton_method(:to_json) do
|
83
|
+
JSON.generate(self.map { |x| x.to_dynamic })
|
84
|
+
end
|
85
|
+
technologies
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ortfodb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ewen Le Bihan
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-04-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dry-struct
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-types
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 1.7.2
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '1.7'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.7.2
|
47
|
+
description: Client library for working with ortfo/db databases. Generated from ortfo/db's
|
48
|
+
JSON schemas (see https://ortfo.org/db/client-libraries).
|
49
|
+
email:
|
50
|
+
- ortfo@ewen.works
|
51
|
+
executables: []
|
52
|
+
extensions: []
|
53
|
+
extra_rdoc_files: []
|
54
|
+
files:
|
55
|
+
- lib/ortfodb/configuration.rb
|
56
|
+
- lib/ortfodb/database.rb
|
57
|
+
- lib/ortfodb/exporter.rb
|
58
|
+
- lib/ortfodb/tags.rb
|
59
|
+
- lib/ortfodb/technologies.rb
|
60
|
+
- lib/ortfodb/version.rb
|
61
|
+
homepage: https://ortfo.org/db
|
62
|
+
licenses:
|
63
|
+
- MIT
|
64
|
+
metadata:
|
65
|
+
allowed_push_host: https://rubygems.org
|
66
|
+
bug_tracker_uri: https://github.com/ortfo/db/issues
|
67
|
+
changelog_uri: https://github.com/ortfo/db/blob/main/CHANGELOG.md
|
68
|
+
documentation_uri: https://ortfo.org/db
|
69
|
+
homepage_uri: https://ortfo.org/db
|
70
|
+
source_code_uri: https://github.com/ortfo/db/tree/main/packages/ruby
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 2.0.0
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubygems_version: 3.3.25
|
87
|
+
signing_key:
|
88
|
+
specification_version: 4
|
89
|
+
summary: Client library for working with ortfo/db databases
|
90
|
+
test_files: []
|