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,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "resource"
|
|
4
|
+
require_relative "social_profile"
|
|
5
|
+
|
|
6
|
+
module Sferik
|
|
7
|
+
# Contact details and profiles, as returned by {API::ProfileEndpoints#finger}
|
|
8
|
+
# @api public
|
|
9
|
+
class Finger < Resource
|
|
10
|
+
# @!method name
|
|
11
|
+
# The full name
|
|
12
|
+
# @api public
|
|
13
|
+
# @return [String] the full name
|
|
14
|
+
# @example
|
|
15
|
+
# finger.name # => "Erik Berlin"
|
|
16
|
+
attribute :name
|
|
17
|
+
|
|
18
|
+
# @!method login
|
|
19
|
+
# The handle
|
|
20
|
+
# @api public
|
|
21
|
+
# @return [String] the handle
|
|
22
|
+
# @example
|
|
23
|
+
# finger.login # => "sferik"
|
|
24
|
+
attribute :login
|
|
25
|
+
|
|
26
|
+
# @!method mail
|
|
27
|
+
# The email address
|
|
28
|
+
# @api public
|
|
29
|
+
# @return [String] the email address
|
|
30
|
+
# @example
|
|
31
|
+
# finger.mail # => "sferik@gmail.com"
|
|
32
|
+
attribute :mail
|
|
33
|
+
|
|
34
|
+
# @!method directory
|
|
35
|
+
# The home directory, in the site's shell
|
|
36
|
+
# @api public
|
|
37
|
+
# @return [String] the home directory, in the site's shell
|
|
38
|
+
# @example
|
|
39
|
+
# finger.directory # => "/Users/sferik"
|
|
40
|
+
attribute :directory
|
|
41
|
+
|
|
42
|
+
# @!method shell
|
|
43
|
+
# The login shell, in the site's shell
|
|
44
|
+
# @api public
|
|
45
|
+
# @return [String] the login shell, in the site's shell
|
|
46
|
+
# @example
|
|
47
|
+
# finger.shell # => "/opt/homebrew/bin/fish"
|
|
48
|
+
attribute :shell
|
|
49
|
+
|
|
50
|
+
# @!method plan
|
|
51
|
+
# The .plan
|
|
52
|
+
# @api public
|
|
53
|
+
# @return [String] the .plan
|
|
54
|
+
# @example
|
|
55
|
+
# finger.plan # => "..."
|
|
56
|
+
attribute :plan
|
|
57
|
+
|
|
58
|
+
# @!method profiles
|
|
59
|
+
# Profiles elsewhere
|
|
60
|
+
# @api public
|
|
61
|
+
# @return [Array<SocialProfile>] profiles elsewhere
|
|
62
|
+
# @example
|
|
63
|
+
# finger.profiles # => [#<Sferik::SocialProfile ...>, ...]
|
|
64
|
+
list :profiles, "profiles", type: SocialProfile
|
|
65
|
+
|
|
66
|
+
# @!method command
|
|
67
|
+
# The shell command the home page shows it as
|
|
68
|
+
# @api public
|
|
69
|
+
# @return [String] the shell command the home page shows it as
|
|
70
|
+
# @example
|
|
71
|
+
# finger.command # => "finger sferik"
|
|
72
|
+
attribute :command
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sferik
|
|
4
|
+
# Reads what an answer says about keeping a response: for how long it's good, and whether to keep it at all
|
|
5
|
+
#
|
|
6
|
+
# An answer is the response itself, or a 304 that says a response kept hasn't changed. It says how long the response
|
|
7
|
+
# is good for (Cache-Control: max-age), and how long it has been kept somewhere on its way already (Age), as one
|
|
8
|
+
# from Cloudflare's cache has.
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
module Freshness
|
|
12
|
+
# The seconds a Cache-Control says a response is good for
|
|
13
|
+
MAX_AGE = /\bmax-age=(\d+)/i
|
|
14
|
+
private_constant :MAX_AGE
|
|
15
|
+
|
|
16
|
+
# A Cache-Control that says to check a response each time it's used
|
|
17
|
+
NO_CACHE = /\bno-cache\b/i
|
|
18
|
+
private_constant :NO_CACHE
|
|
19
|
+
|
|
20
|
+
# A Cache-Control that says not to keep a response
|
|
21
|
+
NO_STORE = /\bno-store\b/i
|
|
22
|
+
private_constant :NO_STORE
|
|
23
|
+
|
|
24
|
+
# The seconds an answer says a response is good for
|
|
25
|
+
#
|
|
26
|
+
# @api private
|
|
27
|
+
# @param answer [Net::HTTPResponse] the answer
|
|
28
|
+
# @return [Integer] the seconds: none if it says to check each time, or doesn't say
|
|
29
|
+
def self.lifetime(answer)
|
|
30
|
+
control = answer["cache-control"].to_s
|
|
31
|
+
(control[MAX_AGE, 1] unless NO_CACHE.match?(control)).to_i
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The seconds a response is still good for, when its answer comes
|
|
35
|
+
#
|
|
36
|
+
# @api private
|
|
37
|
+
# @param answer [Net::HTTPResponse] the answer
|
|
38
|
+
# @return [Integer] the seconds it's good for, less the whole seconds it says it has been kept already: none, or
|
|
39
|
+
# fewer, for one that's no longer good
|
|
40
|
+
def self.left(answer)
|
|
41
|
+
lifetime(answer) - answer["age"].to_i
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Whether an answer lets a response be kept
|
|
45
|
+
#
|
|
46
|
+
# @api private
|
|
47
|
+
# @param answer [Net::HTTPResponse] the answer
|
|
48
|
+
# @return [Boolean] false if it says not to keep the response
|
|
49
|
+
def self.keepable?(answer)
|
|
50
|
+
!answer["cache-control"]&.match?(NO_STORE)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
private_constant :Freshness
|
|
54
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
class Home < Resource
|
|
7
|
+
# The paths of the site's other pages, as returned in {Home#pages}
|
|
8
|
+
# @api public
|
|
9
|
+
class Pages < Resource
|
|
10
|
+
# @!method talks
|
|
11
|
+
# The talks page
|
|
12
|
+
# @api public
|
|
13
|
+
# @return [String] the talks page
|
|
14
|
+
# @example
|
|
15
|
+
# pages.talks # => "/talks"
|
|
16
|
+
attribute :talks
|
|
17
|
+
|
|
18
|
+
# @!method resume
|
|
19
|
+
# The resume page
|
|
20
|
+
# @api public
|
|
21
|
+
# @return [String] the resume page
|
|
22
|
+
# @example
|
|
23
|
+
# pages.resume # => "/resume"
|
|
24
|
+
attribute :resume
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
class Home < Resource
|
|
7
|
+
# Who the home page is about, as returned in {Home#profile}
|
|
8
|
+
# @api public
|
|
9
|
+
class Profile < Resource
|
|
10
|
+
# @!method name
|
|
11
|
+
# The name
|
|
12
|
+
# @api public
|
|
13
|
+
# @return [String] the name
|
|
14
|
+
# @example
|
|
15
|
+
# profile.name # => "Erik Berlin"
|
|
16
|
+
attribute :name
|
|
17
|
+
|
|
18
|
+
# @!method location
|
|
19
|
+
# Where Erik lives
|
|
20
|
+
# @api public
|
|
21
|
+
# @return [String] where Erik lives
|
|
22
|
+
# @example
|
|
23
|
+
# profile.location # => "San Francisco, California"
|
|
24
|
+
attribute :location
|
|
25
|
+
|
|
26
|
+
# @!method tagline
|
|
27
|
+
# The tagline
|
|
28
|
+
# @api public
|
|
29
|
+
# @return [String] the tagline
|
|
30
|
+
# @example
|
|
31
|
+
# profile.tagline # => "I build libraries and tools software engineers depend on."
|
|
32
|
+
attribute :tagline
|
|
33
|
+
|
|
34
|
+
# @!method handle
|
|
35
|
+
# The handle used everywhere
|
|
36
|
+
# @api public
|
|
37
|
+
# @return [String] the handle used everywhere
|
|
38
|
+
# @example
|
|
39
|
+
# profile.handle # => "sferik"
|
|
40
|
+
attribute :handle
|
|
41
|
+
|
|
42
|
+
# @!method url
|
|
43
|
+
# The site
|
|
44
|
+
# @api public
|
|
45
|
+
# @return [String] the site
|
|
46
|
+
# @example
|
|
47
|
+
# profile.url # => "https://sferik.net"
|
|
48
|
+
attribute :url
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
class Home < Resource
|
|
7
|
+
# One of the home page's modules, as returned in {Home#modules}
|
|
8
|
+
# @api public
|
|
9
|
+
class Section < Resource
|
|
10
|
+
# @!method id
|
|
11
|
+
# The module's name
|
|
12
|
+
# @api public
|
|
13
|
+
# @return [String] the module's name
|
|
14
|
+
# @example
|
|
15
|
+
# section.id # => "whoami"
|
|
16
|
+
attribute :id
|
|
17
|
+
|
|
18
|
+
# @!method url
|
|
19
|
+
# Its path, which {Client#get} takes
|
|
20
|
+
# @api public
|
|
21
|
+
# @return [String] its path, which {Client#get} takes
|
|
22
|
+
# @example
|
|
23
|
+
# section.url # => "/whoami"
|
|
24
|
+
attribute :url
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/sferik/home.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "resource"
|
|
4
|
+
require_relative "home/pages"
|
|
5
|
+
require_relative "home/profile"
|
|
6
|
+
require_relative "home/section"
|
|
7
|
+
|
|
8
|
+
module Sferik
|
|
9
|
+
# The profile and the home page's modules, as returned by {API::ProfileEndpoints#home}
|
|
10
|
+
# @api public
|
|
11
|
+
class Home < Resource
|
|
12
|
+
# @!method profile
|
|
13
|
+
# Who the site is about
|
|
14
|
+
# @api public
|
|
15
|
+
# @return [Profile] who the site is about
|
|
16
|
+
# @example
|
|
17
|
+
# home.profile # => #<Sferik::Home::Profile ...>
|
|
18
|
+
attribute :profile, "profile", type: Profile
|
|
19
|
+
|
|
20
|
+
# @!method modules
|
|
21
|
+
# The home page's modules, in order
|
|
22
|
+
# @api public
|
|
23
|
+
# @return [Array<Section>] the home page's modules, in order
|
|
24
|
+
# @example
|
|
25
|
+
# home.modules # => [#<Sferik::Home::Section id="whoami" ...>, ...]
|
|
26
|
+
list :modules, "modules", type: Section
|
|
27
|
+
|
|
28
|
+
# @!method pages
|
|
29
|
+
# The paths of the other pages
|
|
30
|
+
# @api public
|
|
31
|
+
# @return [Pages] the paths of the other pages
|
|
32
|
+
# @example
|
|
33
|
+
# home.pages # => #<Sferik::Home::Pages ...>
|
|
34
|
+
attribute :pages, "pages", type: Pages
|
|
35
|
+
|
|
36
|
+
inspect_with :profile
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "errors"
|
|
5
|
+
|
|
6
|
+
module Sferik
|
|
7
|
+
# Parses the API's JSON responses, for the endpoints that return objects
|
|
8
|
+
# @api private
|
|
9
|
+
module JSONParsing
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
# Parse a JSON response body
|
|
13
|
+
#
|
|
14
|
+
# @api private
|
|
15
|
+
# @param body [String] the response body
|
|
16
|
+
# @return [Hash] the parsed body, deep-frozen
|
|
17
|
+
# @raise [InvalidResponse] if the body isn't a JSON object, or isn't in the charset the response names (or one JSON can be read in)
|
|
18
|
+
def parse_json(body)
|
|
19
|
+
value = JSON.parse(body, freeze: true)
|
|
20
|
+
raise InvalidResponse, "Expected a JSON object, got #{value.class}" unless value.instance_of?(Hash)
|
|
21
|
+
|
|
22
|
+
value
|
|
23
|
+
rescue JSON::ParserError, EncodingError => e
|
|
24
|
+
raise InvalidResponse, "Couldn't parse the response as JSON: #{e}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
private_constant :JSONParsing
|
|
28
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
# The name change, as a git commit, as returned by {API::ProfileEndpoints#name_change}
|
|
7
|
+
# @api public
|
|
8
|
+
class NameChange < Resource
|
|
9
|
+
# @!method subject
|
|
10
|
+
# The commit's subject
|
|
11
|
+
# @api public
|
|
12
|
+
# @return [String] the commit's subject
|
|
13
|
+
# @example
|
|
14
|
+
# name_change.subject # => "Rename Erik Michaels-Ober to Erik Berlin"
|
|
15
|
+
attribute :subject
|
|
16
|
+
|
|
17
|
+
# @!method year
|
|
18
|
+
# The year of the change
|
|
19
|
+
# @api public
|
|
20
|
+
# @return [Integer] the year of the change
|
|
21
|
+
# @example
|
|
22
|
+
# name_change.year # => 2017
|
|
23
|
+
attribute :year
|
|
24
|
+
|
|
25
|
+
# @!method commit
|
|
26
|
+
# The commit's abbreviated hash
|
|
27
|
+
# @api public
|
|
28
|
+
# @return [String] the commit's abbreviated hash
|
|
29
|
+
# @example
|
|
30
|
+
# name_change.commit # => "8c0d698"
|
|
31
|
+
attribute :commit
|
|
32
|
+
|
|
33
|
+
# @!method notes
|
|
34
|
+
# More about it, as HTML
|
|
35
|
+
# @api public
|
|
36
|
+
# @return [Array<String>] more about it, as HTML
|
|
37
|
+
# @example
|
|
38
|
+
# name_change.notes # => ["..."]
|
|
39
|
+
list :notes
|
|
40
|
+
|
|
41
|
+
# @!method command
|
|
42
|
+
# The shell command the home page shows it as
|
|
43
|
+
# @api public
|
|
44
|
+
# @return [String] the shell command the home page shows it as
|
|
45
|
+
# @example
|
|
46
|
+
# name_change.command # => "git log --follow --oneline -- name"
|
|
47
|
+
attribute :command
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/sferik/place.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
# Where a talk's location is, as returned in {Talks#places}
|
|
7
|
+
# @api public
|
|
8
|
+
class Place < Resource
|
|
9
|
+
# @!method lat
|
|
10
|
+
# The latitude, in degrees
|
|
11
|
+
# @api public
|
|
12
|
+
# @return [Numeric] the latitude, in degrees
|
|
13
|
+
# @example
|
|
14
|
+
# place.lat # => 41.39
|
|
15
|
+
attribute :lat
|
|
16
|
+
|
|
17
|
+
# @!method lon
|
|
18
|
+
# The longitude, in degrees
|
|
19
|
+
# @api public
|
|
20
|
+
# @return [Numeric] the longitude, in degrees
|
|
21
|
+
# @example
|
|
22
|
+
# place.lon # => 2.17
|
|
23
|
+
attribute :lon
|
|
24
|
+
|
|
25
|
+
# @!method country
|
|
26
|
+
# The country it's in
|
|
27
|
+
# @api public
|
|
28
|
+
# @return [String] the country it's in
|
|
29
|
+
# @example
|
|
30
|
+
# place.country # => "Spain"
|
|
31
|
+
attribute :country
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
# A podcast appearance, as returned in {Talks#podcasts}
|
|
7
|
+
# @api public
|
|
8
|
+
class Podcast < Resource
|
|
9
|
+
# @!method title
|
|
10
|
+
# The episode's title
|
|
11
|
+
# @api public
|
|
12
|
+
# @return [String] the episode's title
|
|
13
|
+
# @example
|
|
14
|
+
# podcast.title # => "The Crystal Programming Language"
|
|
15
|
+
attribute :title
|
|
16
|
+
|
|
17
|
+
# @!method show
|
|
18
|
+
# The podcast
|
|
19
|
+
# @api public
|
|
20
|
+
# @return [String] the podcast
|
|
21
|
+
# @example
|
|
22
|
+
# podcast.show # => "Ruby Rogues, episode 248"
|
|
23
|
+
attribute :show
|
|
24
|
+
|
|
25
|
+
# @!method date
|
|
26
|
+
# When it was, to the month: the first day of that month
|
|
27
|
+
# @api public
|
|
28
|
+
# @return [Date] the first day of the month it was in
|
|
29
|
+
# @example
|
|
30
|
+
# podcast.date # => #<Date: 2016-02-01>
|
|
31
|
+
timestamp :date, PartialDate
|
|
32
|
+
|
|
33
|
+
# @!method url
|
|
34
|
+
# The episode
|
|
35
|
+
# @api public
|
|
36
|
+
# @return [String] the episode
|
|
37
|
+
# @example
|
|
38
|
+
# podcast.url # => "https://..."
|
|
39
|
+
attribute :url
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
# A project, as returned in {Projects#projects}
|
|
7
|
+
# @api public
|
|
8
|
+
class Project < Resource
|
|
9
|
+
# @!method name
|
|
10
|
+
# The project's name
|
|
11
|
+
# @api public
|
|
12
|
+
# @return [String] the project's name
|
|
13
|
+
# @example
|
|
14
|
+
# project.name # => "multi_json"
|
|
15
|
+
attribute :name
|
|
16
|
+
|
|
17
|
+
# @!method description
|
|
18
|
+
# What it is
|
|
19
|
+
# @api public
|
|
20
|
+
# @return [String] what it is
|
|
21
|
+
# @example
|
|
22
|
+
# project.description # => "One interface to every Ruby JSON library."
|
|
23
|
+
attribute :description
|
|
24
|
+
|
|
25
|
+
# @!method downloads
|
|
26
|
+
# Its RubyGems downloads, or nil if it isn't a gem
|
|
27
|
+
# @api public
|
|
28
|
+
# @return [Integer, nil] its RubyGems downloads, or nil if it isn't a gem
|
|
29
|
+
# @example
|
|
30
|
+
# project.downloads # => 1_170_000_000
|
|
31
|
+
attribute :downloads
|
|
32
|
+
|
|
33
|
+
# @!method stars
|
|
34
|
+
# Its GitHub stars, or nil if it isn't on GitHub
|
|
35
|
+
# @api public
|
|
36
|
+
# @return [Integer, nil] its GitHub stars, or nil if it isn't on GitHub
|
|
37
|
+
# @example
|
|
38
|
+
# project.stars # => 27
|
|
39
|
+
attribute :stars
|
|
40
|
+
|
|
41
|
+
# @!method url
|
|
42
|
+
# Its home page
|
|
43
|
+
# @api public
|
|
44
|
+
# @return [String] its home page
|
|
45
|
+
# @example
|
|
46
|
+
# project.url # => "https://github.com/sferik/multi_json"
|
|
47
|
+
attribute :url
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "resource"
|
|
4
|
+
require_relative "collection"
|
|
5
|
+
require_relative "project"
|
|
6
|
+
|
|
7
|
+
module Sferik
|
|
8
|
+
# Projects with their downloads and stars, as returned by {API::CodeEndpoints#projects}
|
|
9
|
+
#
|
|
10
|
+
# It's Enumerable over its {#projects}, so `Sferik.projects.first` is the most downloaded, and
|
|
11
|
+
# `Sferik.projects.map(&:name)` lists them all. It also has what an Array adds to that: {#size}, {#length},
|
|
12
|
+
# {#empty?}, {#last}, and {#[]}.
|
|
13
|
+
#
|
|
14
|
+
# @api public
|
|
15
|
+
class Projects < Resource
|
|
16
|
+
include Enumerable
|
|
17
|
+
include Collection
|
|
18
|
+
|
|
19
|
+
# ActiveSupport's Enumerable#as_json would make a list of them; keep the JSON it came from, as for any resource
|
|
20
|
+
define_method(:as_json, Resource.instance_method(:as_json))
|
|
21
|
+
|
|
22
|
+
# @!method projects
|
|
23
|
+
# The projects, most downloaded first, with related ones together
|
|
24
|
+
# @api public
|
|
25
|
+
# @return [Array<Project>] the projects, most downloaded first, with related ones together
|
|
26
|
+
# @example
|
|
27
|
+
# projects.projects # => [#<Sferik::Project ...>, ...]
|
|
28
|
+
list :projects, "projects", type: Project
|
|
29
|
+
|
|
30
|
+
# The RubyGems downloads of every gem @sferik owns, not only those listed
|
|
31
|
+
#
|
|
32
|
+
# @api public
|
|
33
|
+
# @return [Integer, nil] the RubyGems downloads of every gem @sferik owns, not only those listed
|
|
34
|
+
# @example
|
|
35
|
+
# projects.total_downloads # => 5_460_234_129
|
|
36
|
+
def total_downloads
|
|
37
|
+
total["downloads"]
|
|
38
|
+
end
|
|
39
|
+
record :total_downloads
|
|
40
|
+
|
|
41
|
+
# The number of gems @sferik owns
|
|
42
|
+
#
|
|
43
|
+
# @api public
|
|
44
|
+
# @return [Integer, nil] the number of gems @sferik owns
|
|
45
|
+
# @example
|
|
46
|
+
# projects.total_gems # => 65
|
|
47
|
+
def total_gems
|
|
48
|
+
total["gems"]
|
|
49
|
+
end
|
|
50
|
+
record :total_gems
|
|
51
|
+
|
|
52
|
+
# The GitHub stars of the projects listed
|
|
53
|
+
#
|
|
54
|
+
# @api public
|
|
55
|
+
# @return [Integer, nil] the GitHub stars of the projects listed
|
|
56
|
+
# @example
|
|
57
|
+
# projects.total_stars # => 38_000
|
|
58
|
+
def total_stars
|
|
59
|
+
total["stars"]
|
|
60
|
+
end
|
|
61
|
+
record :total_stars
|
|
62
|
+
|
|
63
|
+
# @!method more
|
|
64
|
+
# Where to find the rest
|
|
65
|
+
# @api public
|
|
66
|
+
# @return [String] where to find the rest
|
|
67
|
+
# @example
|
|
68
|
+
# projects.more # => "https://github.com/sferik"
|
|
69
|
+
attribute :more
|
|
70
|
+
|
|
71
|
+
# @!method live?
|
|
72
|
+
# Whether the downloads are what RubyGems says now
|
|
73
|
+
#
|
|
74
|
+
# They aren't when they're the snapshot, which the site falls back to, or the last that were fetched, more than
|
|
75
|
+
# two hours ago, which it goes on with when it can't fetch them again: {#as_of} says when they're from.
|
|
76
|
+
#
|
|
77
|
+
# @api public
|
|
78
|
+
# @return [Boolean] false when the downloads are a snapshot, or were last fetched more than two hours ago
|
|
79
|
+
# @example
|
|
80
|
+
# projects.live? # => true
|
|
81
|
+
predicate :live
|
|
82
|
+
|
|
83
|
+
# @!method as_of
|
|
84
|
+
# When the downloads are from
|
|
85
|
+
#
|
|
86
|
+
# That's the hour they were fetched in, or the day of the snapshot, if they're that. It's to the hour so that
|
|
87
|
+
# downloads that are fetched again, and haven't changed, are the same response, which a {Client#cached} client
|
|
88
|
+
# isn't sent again: {Status::Loaded#gems} is when they were fetched, to the second.
|
|
89
|
+
#
|
|
90
|
+
# @api public
|
|
91
|
+
# @return [Time] when the downloads are from
|
|
92
|
+
# @example
|
|
93
|
+
# projects.as_of # => 2026-10-08 01:00:00 UTC
|
|
94
|
+
timestamp :as_of, Time
|
|
95
|
+
|
|
96
|
+
# @!method command
|
|
97
|
+
# The shell command the home page shows it as
|
|
98
|
+
# @api public
|
|
99
|
+
# @return [String] the shell command the home page shows it as
|
|
100
|
+
# @example
|
|
101
|
+
# projects.command # => "ls -lS ~/src"
|
|
102
|
+
attribute :command
|
|
103
|
+
|
|
104
|
+
inspect_with :size, :total_downloads, :total_stars
|
|
105
|
+
|
|
106
|
+
# Yield each project, most downloaded first (related projects together)
|
|
107
|
+
#
|
|
108
|
+
# @api public
|
|
109
|
+
# @yieldparam project [Project] a project
|
|
110
|
+
# @return [self, Enumerator<Project>] the projects themselves, or an Enumerator without a block
|
|
111
|
+
# @example
|
|
112
|
+
# Sferik.projects.each { |project| puts project.name }
|
|
113
|
+
def each(&block)
|
|
114
|
+
return to_enum { size } unless block
|
|
115
|
+
|
|
116
|
+
projects.each(&block)
|
|
117
|
+
self
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
# The totals, which are empty when the response has none, and are checked when the projects are built
|
|
123
|
+
prepare(:total) do
|
|
124
|
+
value = given(attributes["total"], NONE)
|
|
125
|
+
raise InvalidResponse, "#{self.class}#total: expected a JSON object, got #{value.class}" unless value.instance_of?(Hash)
|
|
126
|
+
|
|
127
|
+
value
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# The totals, which are empty when the response has none
|
|
131
|
+
#
|
|
132
|
+
# @api private
|
|
133
|
+
# @return [Hash{String => Object}] the totals
|
|
134
|
+
def total
|
|
135
|
+
prepared(:total)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
data/lib/sferik/push.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "resource"
|
|
4
|
+
|
|
5
|
+
module Sferik
|
|
6
|
+
# A push to GitHub, as returned by {Contributions#last_push}
|
|
7
|
+
# @api public
|
|
8
|
+
class Push < Resource
|
|
9
|
+
# @!method repo
|
|
10
|
+
# The repository
|
|
11
|
+
# @api public
|
|
12
|
+
# @return [String] the repository
|
|
13
|
+
# @example
|
|
14
|
+
# push.repo # => "sferik/x-ruby"
|
|
15
|
+
attribute :repo
|
|
16
|
+
|
|
17
|
+
# @!method sha
|
|
18
|
+
# The commit at the head of the push
|
|
19
|
+
# @api public
|
|
20
|
+
# @return [String] the commit at the head of the push
|
|
21
|
+
# @example
|
|
22
|
+
# push.sha # => "abc1234def5678"
|
|
23
|
+
attribute :sha
|
|
24
|
+
|
|
25
|
+
# @!method at
|
|
26
|
+
# When it was pushed
|
|
27
|
+
# @api public
|
|
28
|
+
# @return [Time] when it was pushed
|
|
29
|
+
# @example
|
|
30
|
+
# push.at # => 2026-10-01 12:00:00 UTC
|
|
31
|
+
timestamp :at, Time
|
|
32
|
+
end
|
|
33
|
+
end
|