ortfodb 1.2.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 +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: []
|