sferik 0.0.1
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/.yardopts +5 -0
- data/CHANGELOG.md +72 -0
- data/LICENSE.md +16 -0
- data/README.md +517 -0
- data/exe/sferik +6 -0
- data/lib/sferik/api/code_endpoints.rb +37 -0
- data/lib/sferik/api/profile_endpoints.rb +113 -0
- data/lib/sferik/api/resume_endpoints.rb +41 -0
- data/lib/sferik/api/site_endpoints.rb +247 -0
- data/lib/sferik/api/talk_endpoints.rb +45 -0
- data/lib/sferik/api.rb +23 -0
- data/lib/sferik/block.rb +25 -0
- data/lib/sferik/body.rb +54 -0
- data/lib/sferik/cache.rb +361 -0
- data/lib/sferik/cli.rb +369 -0
- data/lib/sferik/client.rb +453 -0
- data/lib/sferik/collection.rb +82 -0
- data/lib/sferik/configuration.rb +129 -0
- data/lib/sferik/connections.rb +151 -0
- data/lib/sferik/contributions.rb +86 -0
- data/lib/sferik/day.rb +35 -0
- data/lib/sferik/dependency.rb +26 -0
- data/lib/sferik/deployment.rb +36 -0
- data/lib/sferik/errors.rb +234 -0
- data/lib/sferik/figure.rb +83 -0
- data/lib/sferik/finger.rb +74 -0
- data/lib/sferik/freshness.rb +54 -0
- data/lib/sferik/home/pages.rb +27 -0
- data/lib/sferik/home/profile.rb +51 -0
- data/lib/sferik/home/section.rb +27 -0
- data/lib/sferik/home.rb +38 -0
- data/lib/sferik/json_parsing.rb +28 -0
- data/lib/sferik/name_change.rb +49 -0
- data/lib/sferik/place.rb +33 -0
- data/lib/sferik/podcast.rb +41 -0
- data/lib/sferik/project.rb +49 -0
- data/lib/sferik/projects.rb +138 -0
- data/lib/sferik/push.rb +33 -0
- data/lib/sferik/resource.rb +373 -0
- data/lib/sferik/resume/award.rb +43 -0
- data/lib/sferik/resume/basics.rb +77 -0
- data/lib/sferik/resume/education.rb +59 -0
- data/lib/sferik/resume/location.rb +35 -0
- data/lib/sferik/resume/meta.rb +35 -0
- data/lib/sferik/resume/patent.rb +43 -0
- data/lib/sferik/resume/profile.rb +35 -0
- data/lib/sferik/resume/project.rb +27 -0
- data/lib/sferik/resume/skill.rb +27 -0
- data/lib/sferik/resume/speaking.rb +19 -0
- data/lib/sferik/resume/volunteer.rb +59 -0
- data/lib/sferik/resume/work.rb +51 -0
- data/lib/sferik/resume.rb +125 -0
- data/lib/sferik/session.rb +41 -0
- data/lib/sferik/social_profile.rb +49 -0
- data/lib/sferik/status/github.rb +38 -0
- data/lib/sferik/status/loaded.rb +48 -0
- data/lib/sferik/status.rb +33 -0
- data/lib/sferik/talk.rb +73 -0
- data/lib/sferik/talks.rb +79 -0
- data/lib/sferik/validation.rb +119 -0
- data/lib/sferik/version.rb +7 -0
- data/lib/sferik/web_finger/link.rb +46 -0
- data/lib/sferik/web_finger.rb +43 -0
- data/lib/sferik/who.rb +53 -0
- data/lib/sferik/whoami.rb +36 -0
- data/lib/sferik/wrapping.rb +114 -0
- data/lib/sferik.rb +246 -0
- data/sig/manifest.yaml +13 -0
- data/sig/sferik.rbs +825 -0
- metadata +118 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "date"
|
|
4
|
+
require "json"
|
|
5
|
+
require "time"
|
|
6
|
+
require_relative "errors"
|
|
7
|
+
require_relative "wrapping"
|
|
8
|
+
|
|
9
|
+
module Sferik
|
|
10
|
+
# Parses the dates the API gives to the year, month, or day ("2026", "2015-11", "2014-04-22"), as the first day of
|
|
11
|
+
# the period. ISO 8601 allows all three, but Date.iso8601 doesn't take a year alone, and on JRuby not a month either.
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
module PartialDate
|
|
15
|
+
# Parse a date given to the year, month, or day
|
|
16
|
+
#
|
|
17
|
+
# @api private
|
|
18
|
+
# @param value [String] the date
|
|
19
|
+
# @return [Date] the date, or the first day of the year or month
|
|
20
|
+
# @raise [Date::Error] if the value isn't an ISO 8601 date
|
|
21
|
+
# @example
|
|
22
|
+
# Sferik::PartialDate.iso8601("2015-11") # => #<Date: 2015-11-01>
|
|
23
|
+
def self.iso8601(value)
|
|
24
|
+
Date.iso8601(
|
|
25
|
+
case value
|
|
26
|
+
when /\A\d{4}\z/ then "#{value}-01-01"
|
|
27
|
+
when /\A\d{4}-\d{2}\z/ then "#{value}-01"
|
|
28
|
+
else value
|
|
29
|
+
end
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
private_constant :PartialDate
|
|
34
|
+
|
|
35
|
+
# Base class for objects that wrap sferik.net API responses
|
|
36
|
+
#
|
|
37
|
+
# A resource is immutable: its attributes are deep-frozen, and two resources with the same attributes are equal.
|
|
38
|
+
# It works with pattern matching, with the names of its readers as keys: a predicate's without its question mark,
|
|
39
|
+
# so that a pattern can bind it. A reader of a list returns an empty one when the response has none.
|
|
40
|
+
#
|
|
41
|
+
# The resources inside one are built when it is, and so are its dates and times, so a response that isn't what the
|
|
42
|
+
# API documents raises {InvalidResponse} from the endpoint that got it, not from a reader later on. A reader returns
|
|
43
|
+
# the same frozen object each time it's called.
|
|
44
|
+
#
|
|
45
|
+
# @api public
|
|
46
|
+
# @example Pattern matching
|
|
47
|
+
# case Sferik.contributions
|
|
48
|
+
# in {total:, longest_streak:, live:} then puts "#{total} contributions, #{longest_streak}-day streak"
|
|
49
|
+
# end
|
|
50
|
+
class Resource
|
|
51
|
+
include Wrapping
|
|
52
|
+
|
|
53
|
+
# What a reader of a list returns when the response has none, and the keys of a resource that reads none
|
|
54
|
+
EMPTY = [].freeze # steep:ignore UnannotatedEmptyCollection
|
|
55
|
+
private_constant :EMPTY
|
|
56
|
+
|
|
57
|
+
# The readers of a resource that declares none, and what a reader of a dictionary returns when the response has none
|
|
58
|
+
NONE = {}.freeze # steep:ignore UnannotatedEmptyCollection
|
|
59
|
+
private_constant :NONE
|
|
60
|
+
|
|
61
|
+
# The raw attributes from the API response
|
|
62
|
+
# @api public
|
|
63
|
+
# @return [Hash{String => Object}] the raw attributes, frozen
|
|
64
|
+
# @example
|
|
65
|
+
# talk.attributes["title"]
|
|
66
|
+
attr_reader :attributes
|
|
67
|
+
|
|
68
|
+
class << self
|
|
69
|
+
# The names of the readers this resource declares
|
|
70
|
+
#
|
|
71
|
+
# They are the keys of {#to_h}, and of pattern matching. A predicate goes by its name without the question mark: `featured?` is `:featured`.
|
|
72
|
+
#
|
|
73
|
+
# @api public
|
|
74
|
+
# @return [Array<Symbol>] the names, frozen
|
|
75
|
+
# @example
|
|
76
|
+
# Sferik::Talk.attribute_names # => [:title, :event, :date, :location, :slides, :video, :link, :featured]
|
|
77
|
+
def attribute_names
|
|
78
|
+
readers.keys.freeze
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
# The reader each name is read with
|
|
84
|
+
#
|
|
85
|
+
# A resource that declares none has those of the nearest resource it inherits from that does.
|
|
86
|
+
#
|
|
87
|
+
# @api private
|
|
88
|
+
# @return [Hash{Symbol => Symbol}] the readers by name, frozen
|
|
89
|
+
def readers
|
|
90
|
+
ancestors.filter_map { |ancestor| ancestor.instance_variable_get(:@readers) }.first || NONE
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# The keys of the response this resource's readers read
|
|
94
|
+
#
|
|
95
|
+
# A resource that declares no readers has those of the nearest resource it inherits from that does.
|
|
96
|
+
#
|
|
97
|
+
# @api private
|
|
98
|
+
# @return [Array<String>] the keys, frozen
|
|
99
|
+
def keys
|
|
100
|
+
ancestors.filter_map { |ancestor| ancestor.instance_variable_get(:@keys) }.first || EMPTY
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# The blocks that make what this resource prepares when it's built
|
|
104
|
+
#
|
|
105
|
+
# A resource that prepares none has those of the nearest resource it inherits from that does.
|
|
106
|
+
#
|
|
107
|
+
# @api private
|
|
108
|
+
# @return [Hash{Symbol => Proc}] the blocks by name, frozen
|
|
109
|
+
def preparations
|
|
110
|
+
ancestors.filter_map { |ancestor| ancestor.instance_variable_get(:@preparations) }.first || NONE
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Name a value to prepare when a resource is built
|
|
114
|
+
#
|
|
115
|
+
# The resource then has it as prepared(name).
|
|
116
|
+
#
|
|
117
|
+
# @api private
|
|
118
|
+
# @param name [Symbol] the name of the value
|
|
119
|
+
# @yield what makes the value, run as the resource once it has its attributes
|
|
120
|
+
# @yieldreturn [Object] the value
|
|
121
|
+
# @return [Hash{Symbol => Proc}] the blocks by name, frozen
|
|
122
|
+
def prepare(name, &value)
|
|
123
|
+
@preparations = preparations.merge(name => value).freeze
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Define a reader of a value that's prepared when a resource is built
|
|
127
|
+
#
|
|
128
|
+
# @api private
|
|
129
|
+
# @param name [Symbol] the name of the reader
|
|
130
|
+
# @param key [String] the key in the response
|
|
131
|
+
# @yield what makes the value, run as the resource once it has its attributes
|
|
132
|
+
# @yieldreturn [Object] the value
|
|
133
|
+
# @return [Symbol] the name of the reader
|
|
134
|
+
def reader(name, key, &value)
|
|
135
|
+
prepare(name, &value)
|
|
136
|
+
define_method(name) { prepared(name) } # steep:ignore NoMethod
|
|
137
|
+
record(name, key)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Define a reader for an attribute
|
|
141
|
+
#
|
|
142
|
+
# @api private
|
|
143
|
+
# @param name [Symbol] the name of the reader
|
|
144
|
+
# @param key [String] the key in the response (defaults to the name, camelCased)
|
|
145
|
+
# @param type [Class, nil] a resource class to wrap the value in
|
|
146
|
+
# @return [Symbol] the name of the reader
|
|
147
|
+
def attribute(name, key = camelize(name), type: nil)
|
|
148
|
+
reader(name, key) { wrap(attributes[key], type) }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Define a reader for a list, which is empty when the response has none
|
|
152
|
+
#
|
|
153
|
+
# @api private
|
|
154
|
+
# @param name [Symbol] the name of the reader
|
|
155
|
+
# @param key [String] the key in the response (defaults to the name, camelCased)
|
|
156
|
+
# @param type [Class, nil] a resource class to wrap each value of the list in
|
|
157
|
+
# @return [Symbol] the name of the reader
|
|
158
|
+
def list(name, key = camelize(name), type: nil)
|
|
159
|
+
reader(name, key) { wrap_list(name, given(attributes[key], EMPTY), type) }
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Define a predicate for a boolean attribute
|
|
163
|
+
#
|
|
164
|
+
# @api private
|
|
165
|
+
# @param name [Symbol] the name of the attribute (the reader is suffixed with a question mark)
|
|
166
|
+
# @param key [String] the key in the response
|
|
167
|
+
# @return [Symbol] the name of the attribute
|
|
168
|
+
def predicate(name, key = camelize(name))
|
|
169
|
+
reader = :"#{name}?"
|
|
170
|
+
define_method(reader) do
|
|
171
|
+
# @type self: Resource
|
|
172
|
+
attributes[key].eql?(true)
|
|
173
|
+
end
|
|
174
|
+
record(name, key, reader:)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Define a reader for a dictionary, which is empty when the response has none
|
|
178
|
+
#
|
|
179
|
+
# A dictionary is a JSON object of resources by name.
|
|
180
|
+
#
|
|
181
|
+
# @api private
|
|
182
|
+
# @param name [Symbol] the name of the reader
|
|
183
|
+
# @param key [String] the key in the response (defaults to the name, camelCased)
|
|
184
|
+
# @param type [Class] a resource class to wrap each value of the dictionary in
|
|
185
|
+
# @return [Symbol] the name of the reader
|
|
186
|
+
def dictionary(name, key = camelize(name), type:)
|
|
187
|
+
reader(name, key) { wrap_dictionary(name, given(attributes[key], NONE), type) }
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Define a reader that parses an ISO 8601 date or time
|
|
191
|
+
#
|
|
192
|
+
# @api private
|
|
193
|
+
# @param name [Symbol] the name of the reader
|
|
194
|
+
# @param parser [#iso8601] Time, Date, or {PartialDate} for a date that may be given to the year or month
|
|
195
|
+
# @param key [String] the key in the response
|
|
196
|
+
# @return [Symbol] the name of the reader
|
|
197
|
+
def timestamp(name, parser, key = camelize(name))
|
|
198
|
+
reader(name, key) { wrap_timestamp(name, attributes[key], parser) }
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Name the readers that #inspect shows (the first three, unless declared)
|
|
202
|
+
#
|
|
203
|
+
# A resource whose first readers are long lists names shorter ones, to be read at a glance in a console.
|
|
204
|
+
#
|
|
205
|
+
# @api private
|
|
206
|
+
# @param names [Array<Symbol>] the readers, or any other public methods
|
|
207
|
+
# @return [Array<Symbol>] the readers
|
|
208
|
+
def inspect_with(*names)
|
|
209
|
+
@inspected = names
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# The readers #inspect shows
|
|
213
|
+
#
|
|
214
|
+
# A resource that names none has those of the nearest resource it inherits from that does.
|
|
215
|
+
#
|
|
216
|
+
# @api private
|
|
217
|
+
# @return [Array<Symbol>] the readers
|
|
218
|
+
def inspected
|
|
219
|
+
ancestors.filter_map { |ancestor| ancestor.instance_variable_get(:@inspected) }.first || attribute_names.first(3)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Note a reader and the key it reads
|
|
223
|
+
#
|
|
224
|
+
# @api private
|
|
225
|
+
# @param name [Symbol] the name the reader goes by
|
|
226
|
+
# @param key [String, nil] the key, or nil for a reader that reads nested keys itself
|
|
227
|
+
# @param reader [Symbol] the reader, if it isn't the name: a predicate's has a question mark
|
|
228
|
+
# @return [Symbol] the name
|
|
229
|
+
def record(name, key = nil, reader: name)
|
|
230
|
+
@readers = readers.merge(name => reader).freeze
|
|
231
|
+
@keys = [*keys, key].freeze if key
|
|
232
|
+
name
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# The key a reader reads by default: its name, camelCased
|
|
236
|
+
#
|
|
237
|
+
# @api private
|
|
238
|
+
# @param name [Symbol] the reader
|
|
239
|
+
# @return [String] the key
|
|
240
|
+
def camelize(name)
|
|
241
|
+
name.to_s.gsub(/_[a-z]/) { |match| match.delete_prefix("_").upcase }
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Initialize a new resource
|
|
246
|
+
#
|
|
247
|
+
# The attributes are copied, and the copy frozen: what's given is left as it was. Their keys are the keys of the
|
|
248
|
+
# API's JSON, which are strings, at every depth: "startDate", not the name of the reader that reads it.
|
|
249
|
+
#
|
|
250
|
+
# The resources inside this one are built here, and its dates and times parsed, so what the readers return is
|
|
251
|
+
# checked once, now, rather than each time one is called.
|
|
252
|
+
#
|
|
253
|
+
# @api public
|
|
254
|
+
# @param attributes [Hash{String => Object}] the attributes from the API response
|
|
255
|
+
# @return [Resource] a new resource
|
|
256
|
+
# @raise [ArgumentError] if the attributes aren't a Hash, or a key of theirs isn't a String, at any depth
|
|
257
|
+
# @raise [InvalidResponse] if a value isn't what the API documents, at any depth: a list that isn't a JSON array, a
|
|
258
|
+
# resource that isn't a JSON object, or a date or time that isn't an ISO 8601 one
|
|
259
|
+
# @example
|
|
260
|
+
# Sferik::Talk.new("title" => "Writing Fast Ruby")
|
|
261
|
+
def initialize(attributes)
|
|
262
|
+
@attributes = deep_freeze(check(:attributes, attributes, Hash))
|
|
263
|
+
@prepared = self.class.__send__(:preparations).transform_values { |value| instance_exec(&value) }.freeze
|
|
264
|
+
freeze
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# The attributes, read through the readers this resource declares
|
|
268
|
+
#
|
|
269
|
+
# @api public
|
|
270
|
+
# @return [Hash{Symbol => Object}] the values of the readers
|
|
271
|
+
# @example
|
|
272
|
+
# talk.to_h # => {title: "Writing Fast Ruby", ...}
|
|
273
|
+
def to_h
|
|
274
|
+
self.class.attribute_names.to_h { |name| [name, read(name)] }
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# The resource as JSON: the JSON it came from
|
|
278
|
+
#
|
|
279
|
+
# @api public
|
|
280
|
+
# @param args [Array<Object>] what JSON.generate passes on: the state of the generator
|
|
281
|
+
# @return [String] the raw attributes, as JSON
|
|
282
|
+
# @example
|
|
283
|
+
# talk.to_json # => "{\"title\":\"Writing Fast Ruby\",...}"
|
|
284
|
+
def to_json(*args)
|
|
285
|
+
attributes.to_json(*args) # steep:ignore UnexpectedPositionalArgument
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# The resource as ActiveSupport makes JSON of it: the JSON it came from
|
|
289
|
+
#
|
|
290
|
+
# ActiveSupport asks for this when a resource is inside something else it makes JSON of. Without it, a resource
|
|
291
|
+
# in a Hash that Rails renders as JSON would be its instance variables, and one that's Enumerable would be a list.
|
|
292
|
+
#
|
|
293
|
+
# @api public
|
|
294
|
+
# @param _options [Array<Object>] the options ActiveSupport passes on, which make no difference
|
|
295
|
+
# @return [Hash{String => Object}] the raw attributes, frozen
|
|
296
|
+
# @example
|
|
297
|
+
# talk.as_json # => {"title" => "Writing Fast Ruby", ...}
|
|
298
|
+
def as_json(*_options)
|
|
299
|
+
attributes
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# The keys pattern matching asks for, from {#to_h}
|
|
303
|
+
#
|
|
304
|
+
# @api public
|
|
305
|
+
# @param keys [Array<Symbol>, nil] the keys asked for, or nil for all of them
|
|
306
|
+
# @return [Hash{Symbol => Object}] the values
|
|
307
|
+
# @example
|
|
308
|
+
# case talk
|
|
309
|
+
# in {title:, event:, featured:} then "#{title} at #{event}#{" (featured)" if featured}"
|
|
310
|
+
# end
|
|
311
|
+
def deconstruct_keys(keys)
|
|
312
|
+
names = keys ? self.class.attribute_names & keys : self.class.attribute_names
|
|
313
|
+
names.to_h { |name| [name, read(name)] }
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Whether another object is a resource of the same class with the same attributes
|
|
317
|
+
#
|
|
318
|
+
# @api public
|
|
319
|
+
# @param other [Object] the other object
|
|
320
|
+
# @return [Boolean]
|
|
321
|
+
# @example
|
|
322
|
+
# Sferik.whoami == Sferik.whoami # => true
|
|
323
|
+
def ==(other)
|
|
324
|
+
other.instance_of?(self.class) && other.attributes.eql?(attributes)
|
|
325
|
+
end
|
|
326
|
+
alias_method :eql?, :==
|
|
327
|
+
|
|
328
|
+
# A hash code, so equal resources can be keys of the same Hash entry
|
|
329
|
+
#
|
|
330
|
+
# @api public
|
|
331
|
+
# @return [Integer] the hash code
|
|
332
|
+
# @example
|
|
333
|
+
# {talk => true}
|
|
334
|
+
def hash
|
|
335
|
+
[self.class, attributes].hash
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# A short description of the resource
|
|
339
|
+
#
|
|
340
|
+
# A date is shown as its day alone, without the Julian day numbers Date#inspect adds.
|
|
341
|
+
#
|
|
342
|
+
# @api public
|
|
343
|
+
# @return [String] the description
|
|
344
|
+
# @example
|
|
345
|
+
# talk.inspect # => "#<Sferik::Talk title=\"Writing Fast Ruby\" ...>"
|
|
346
|
+
def inspect
|
|
347
|
+
shown = self.class.__send__(:inspected).map do |name|
|
|
348
|
+
"#{name}=#{read(name).then { |value| value.instance_of?(Date) ? "#<Date: #{value}>" : value.inspect }}"
|
|
349
|
+
end
|
|
350
|
+
"#<#{[self.class, *shown].join(" ")}>"
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
private
|
|
354
|
+
|
|
355
|
+
# A value that was prepared when the resource was built
|
|
356
|
+
#
|
|
357
|
+
# @api private
|
|
358
|
+
# @param name [Symbol] the name the value was prepared under
|
|
359
|
+
# @return [Object] the value
|
|
360
|
+
def prepared(name)
|
|
361
|
+
@prepared.fetch(name)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# Read the value a name goes by
|
|
365
|
+
#
|
|
366
|
+
# @api private
|
|
367
|
+
# @param name [Symbol] the name of a reader, or of any other public method
|
|
368
|
+
# @return [Object] the value
|
|
369
|
+
def read(name)
|
|
370
|
+
public_send(self.class.__send__(:readers).fetch(name, name))
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
class Resume < Resource
|
|
7
|
+
# An honor, as returned in {Resume#awards}
|
|
8
|
+
# @api public
|
|
9
|
+
class Award < Resource
|
|
10
|
+
# @!method title
|
|
11
|
+
# The honor
|
|
12
|
+
# @api public
|
|
13
|
+
# @return [String] the honor
|
|
14
|
+
# @example
|
|
15
|
+
# award.title # => "Ruby Hero Award"
|
|
16
|
+
attribute :title
|
|
17
|
+
|
|
18
|
+
# @!method date
|
|
19
|
+
# When it was given
|
|
20
|
+
# @api public
|
|
21
|
+
# @return [Date] when it was given
|
|
22
|
+
# @example
|
|
23
|
+
# award.date # => #<Date: 2014-04-22>
|
|
24
|
+
timestamp :date, PartialDate
|
|
25
|
+
|
|
26
|
+
# @!method awarder
|
|
27
|
+
# Who gave it
|
|
28
|
+
# @api public
|
|
29
|
+
# @return [String] who gave it
|
|
30
|
+
# @example
|
|
31
|
+
# award.awarder # => "Ruby Heroes"
|
|
32
|
+
attribute :awarder
|
|
33
|
+
|
|
34
|
+
# @!method summary
|
|
35
|
+
# What it was for
|
|
36
|
+
# @api public
|
|
37
|
+
# @return [String] what it was for
|
|
38
|
+
# @example
|
|
39
|
+
# award.summary # => "Presented at RailsConf..."
|
|
40
|
+
attribute :summary
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../resource"
|
|
4
|
+
require_relative "location"
|
|
5
|
+
require_relative "profile"
|
|
6
|
+
|
|
7
|
+
module Sferik
|
|
8
|
+
class Resume < Resource
|
|
9
|
+
# Name, contact details, and summary, as returned by {Resume#basics}
|
|
10
|
+
# @api public
|
|
11
|
+
class Basics < Resource
|
|
12
|
+
# @!method name
|
|
13
|
+
# The name
|
|
14
|
+
# @api public
|
|
15
|
+
# @return [String] the name
|
|
16
|
+
# @example
|
|
17
|
+
# basics.name # => "Erik Berlin"
|
|
18
|
+
attribute :name
|
|
19
|
+
|
|
20
|
+
# @!method label
|
|
21
|
+
# The title
|
|
22
|
+
# @api public
|
|
23
|
+
# @return [String] the title
|
|
24
|
+
# @example
|
|
25
|
+
# basics.label # => "Software Engineer"
|
|
26
|
+
attribute :label
|
|
27
|
+
|
|
28
|
+
# @!method former_name
|
|
29
|
+
# The name before 2017 (an addition to JSON Resume)
|
|
30
|
+
# @api public
|
|
31
|
+
# @return [String] the name before 2017 (an addition to JSON Resume)
|
|
32
|
+
# @example
|
|
33
|
+
# basics.former_name # => "Erik Michaels-Ober"
|
|
34
|
+
attribute :former_name
|
|
35
|
+
|
|
36
|
+
# @!method email
|
|
37
|
+
# The email address
|
|
38
|
+
# @api public
|
|
39
|
+
# @return [String] the email address
|
|
40
|
+
# @example
|
|
41
|
+
# basics.email # => "sferik@gmail.com"
|
|
42
|
+
attribute :email
|
|
43
|
+
|
|
44
|
+
# @!method url
|
|
45
|
+
# The site
|
|
46
|
+
# @api public
|
|
47
|
+
# @return [String] the site
|
|
48
|
+
# @example
|
|
49
|
+
# basics.url # => "https://sferik.net"
|
|
50
|
+
attribute :url
|
|
51
|
+
|
|
52
|
+
# @!method summary
|
|
53
|
+
# Paragraphs separated by blank lines, with **bold** and *italic*
|
|
54
|
+
# @api public
|
|
55
|
+
# @return [String] paragraphs separated by blank lines, with **bold** and *italic*
|
|
56
|
+
# @example
|
|
57
|
+
# basics.summary # => "Entrepreneur, engineer, and manager..."
|
|
58
|
+
attribute :summary
|
|
59
|
+
|
|
60
|
+
# @!method location
|
|
61
|
+
# Where Erik lives
|
|
62
|
+
# @api public
|
|
63
|
+
# @return [Location] where Erik lives
|
|
64
|
+
# @example
|
|
65
|
+
# basics.location # => #<Sferik::Resume::Location ...>
|
|
66
|
+
attribute :location, "location", type: Location
|
|
67
|
+
|
|
68
|
+
# @!method profiles
|
|
69
|
+
# Profiles elsewhere
|
|
70
|
+
# @api public
|
|
71
|
+
# @return [Array<Profile>] profiles elsewhere
|
|
72
|
+
# @example
|
|
73
|
+
# basics.profiles # => [#<Sferik::Resume::Profile ...>, ...]
|
|
74
|
+
list :profiles, "profiles", type: Profile
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
class Resume < Resource
|
|
7
|
+
# A school, as returned in {Resume#education}
|
|
8
|
+
# @api public
|
|
9
|
+
class Education < Resource
|
|
10
|
+
# @!method institution
|
|
11
|
+
# The school
|
|
12
|
+
# @api public
|
|
13
|
+
# @return [String] the school
|
|
14
|
+
# @example
|
|
15
|
+
# school.institution # => "Carnegie Mellon University"
|
|
16
|
+
attribute :institution
|
|
17
|
+
|
|
18
|
+
# @!method location
|
|
19
|
+
# Where it is
|
|
20
|
+
# @api public
|
|
21
|
+
# @return [String] where it is
|
|
22
|
+
# @example
|
|
23
|
+
# school.location # => "Pittsburgh"
|
|
24
|
+
attribute :location
|
|
25
|
+
|
|
26
|
+
# @!method start_date
|
|
27
|
+
# When Erik started, as the first day of the month or year given
|
|
28
|
+
# @api public
|
|
29
|
+
# @return [Date] when Erik started, as the first day of the month or year given
|
|
30
|
+
# @example
|
|
31
|
+
# school.start_date # => #<Date: 2001-08-01>
|
|
32
|
+
timestamp :start_date, PartialDate
|
|
33
|
+
|
|
34
|
+
# @!method end_date
|
|
35
|
+
# When Erik finished
|
|
36
|
+
# @api public
|
|
37
|
+
# @return [Date, nil] when Erik finished
|
|
38
|
+
# @example
|
|
39
|
+
# school.end_date # => #<Date: 2005-05-01>
|
|
40
|
+
timestamp :end_date, PartialDate
|
|
41
|
+
|
|
42
|
+
# @!method highlights
|
|
43
|
+
# What Erik did there
|
|
44
|
+
# @api public
|
|
45
|
+
# @return [Array<String>] what Erik did there
|
|
46
|
+
# @example
|
|
47
|
+
# school.highlights # => ["Student Body President"]
|
|
48
|
+
list :highlights
|
|
49
|
+
|
|
50
|
+
# @!method courses
|
|
51
|
+
# Courses
|
|
52
|
+
# @api public
|
|
53
|
+
# @return [Array<String>] courses
|
|
54
|
+
# @example
|
|
55
|
+
# school.courses # => []
|
|
56
|
+
list :courses
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
class Resume < Resource
|
|
7
|
+
# Where Erik lives, as returned in {Basics#location}
|
|
8
|
+
# @api public
|
|
9
|
+
class Location < Resource
|
|
10
|
+
# @!method city
|
|
11
|
+
# The city
|
|
12
|
+
# @api public
|
|
13
|
+
# @return [String] the city
|
|
14
|
+
# @example
|
|
15
|
+
# location.city # => "San Francisco"
|
|
16
|
+
attribute :city
|
|
17
|
+
|
|
18
|
+
# @!method region
|
|
19
|
+
# The state
|
|
20
|
+
# @api public
|
|
21
|
+
# @return [String] the state
|
|
22
|
+
# @example
|
|
23
|
+
# location.region # => "California"
|
|
24
|
+
attribute :region
|
|
25
|
+
|
|
26
|
+
# @!method country_code
|
|
27
|
+
# The ISO 3166 country code
|
|
28
|
+
# @api public
|
|
29
|
+
# @return [String] the ISO 3166 country code
|
|
30
|
+
# @example
|
|
31
|
+
# location.country_code # => "US"
|
|
32
|
+
attribute :country_code
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
class Resume < Resource
|
|
7
|
+
# What the resume says about itself, as returned by {Resume#meta}
|
|
8
|
+
# @api public
|
|
9
|
+
class Meta < Resource
|
|
10
|
+
# @!method canonical
|
|
11
|
+
# Where the resume lives, if it says
|
|
12
|
+
# @api public
|
|
13
|
+
# @return [String, nil] where the resume lives, if it says
|
|
14
|
+
# @example
|
|
15
|
+
# meta.canonical # => "https://sferik.net/resume"
|
|
16
|
+
attribute :canonical
|
|
17
|
+
|
|
18
|
+
# @!method version
|
|
19
|
+
# The version of the resume, if it has one
|
|
20
|
+
# @api public
|
|
21
|
+
# @return [String, nil] the version of the resume, if it has one
|
|
22
|
+
# @example
|
|
23
|
+
# meta.version # => "v1.0.0"
|
|
24
|
+
attribute :version
|
|
25
|
+
|
|
26
|
+
# @!method last_modified
|
|
27
|
+
# When the resume last changed, if it says
|
|
28
|
+
# @api public
|
|
29
|
+
# @return [Date, nil] when the resume last changed, if it says
|
|
30
|
+
# @example
|
|
31
|
+
# meta.last_modified # => #<Date: 2026-10-01>
|
|
32
|
+
timestamp :last_modified, PartialDate
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
class Resume < Resource
|
|
7
|
+
# A patent, as returned in {Resume#patents} (an addition to JSON Resume)
|
|
8
|
+
# @api public
|
|
9
|
+
class Patent < Resource
|
|
10
|
+
# @!method title
|
|
11
|
+
# The title
|
|
12
|
+
# @api public
|
|
13
|
+
# @return [String] the title
|
|
14
|
+
# @example
|
|
15
|
+
# patent.title # => "Method and system for dynamic advertising based on user actions"
|
|
16
|
+
attribute :title
|
|
17
|
+
|
|
18
|
+
# @!method number
|
|
19
|
+
# The publication number
|
|
20
|
+
# @api public
|
|
21
|
+
# @return [String] the publication number
|
|
22
|
+
# @example
|
|
23
|
+
# patent.number # => "US20110153414A1"
|
|
24
|
+
attribute :number
|
|
25
|
+
|
|
26
|
+
# @!method date
|
|
27
|
+
# When it was published
|
|
28
|
+
# @api public
|
|
29
|
+
# @return [Date] when it was published
|
|
30
|
+
# @example
|
|
31
|
+
# patent.date # => #<Date: 2011-06-23>
|
|
32
|
+
timestamp :date, PartialDate
|
|
33
|
+
|
|
34
|
+
# @!method url
|
|
35
|
+
# The patent on Google Patents
|
|
36
|
+
# @api public
|
|
37
|
+
# @return [String] the patent on Google Patents
|
|
38
|
+
# @example
|
|
39
|
+
# patent.url # => "https://patents.google.com/patent/US20110153414A1"
|
|
40
|
+
attribute :url
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|