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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +5 -0
  3. data/CHANGELOG.md +72 -0
  4. data/LICENSE.md +16 -0
  5. data/README.md +517 -0
  6. data/exe/sferik +6 -0
  7. data/lib/sferik/api/code_endpoints.rb +37 -0
  8. data/lib/sferik/api/profile_endpoints.rb +113 -0
  9. data/lib/sferik/api/resume_endpoints.rb +41 -0
  10. data/lib/sferik/api/site_endpoints.rb +247 -0
  11. data/lib/sferik/api/talk_endpoints.rb +45 -0
  12. data/lib/sferik/api.rb +23 -0
  13. data/lib/sferik/block.rb +25 -0
  14. data/lib/sferik/body.rb +54 -0
  15. data/lib/sferik/cache.rb +361 -0
  16. data/lib/sferik/cli.rb +369 -0
  17. data/lib/sferik/client.rb +453 -0
  18. data/lib/sferik/collection.rb +82 -0
  19. data/lib/sferik/configuration.rb +129 -0
  20. data/lib/sferik/connections.rb +151 -0
  21. data/lib/sferik/contributions.rb +86 -0
  22. data/lib/sferik/day.rb +35 -0
  23. data/lib/sferik/dependency.rb +26 -0
  24. data/lib/sferik/deployment.rb +36 -0
  25. data/lib/sferik/errors.rb +234 -0
  26. data/lib/sferik/figure.rb +83 -0
  27. data/lib/sferik/finger.rb +74 -0
  28. data/lib/sferik/freshness.rb +54 -0
  29. data/lib/sferik/home/pages.rb +27 -0
  30. data/lib/sferik/home/profile.rb +51 -0
  31. data/lib/sferik/home/section.rb +27 -0
  32. data/lib/sferik/home.rb +38 -0
  33. data/lib/sferik/json_parsing.rb +28 -0
  34. data/lib/sferik/name_change.rb +49 -0
  35. data/lib/sferik/place.rb +33 -0
  36. data/lib/sferik/podcast.rb +41 -0
  37. data/lib/sferik/project.rb +49 -0
  38. data/lib/sferik/projects.rb +138 -0
  39. data/lib/sferik/push.rb +33 -0
  40. data/lib/sferik/resource.rb +373 -0
  41. data/lib/sferik/resume/award.rb +43 -0
  42. data/lib/sferik/resume/basics.rb +77 -0
  43. data/lib/sferik/resume/education.rb +59 -0
  44. data/lib/sferik/resume/location.rb +35 -0
  45. data/lib/sferik/resume/meta.rb +35 -0
  46. data/lib/sferik/resume/patent.rb +43 -0
  47. data/lib/sferik/resume/profile.rb +35 -0
  48. data/lib/sferik/resume/project.rb +27 -0
  49. data/lib/sferik/resume/skill.rb +27 -0
  50. data/lib/sferik/resume/speaking.rb +19 -0
  51. data/lib/sferik/resume/volunteer.rb +59 -0
  52. data/lib/sferik/resume/work.rb +51 -0
  53. data/lib/sferik/resume.rb +125 -0
  54. data/lib/sferik/session.rb +41 -0
  55. data/lib/sferik/social_profile.rb +49 -0
  56. data/lib/sferik/status/github.rb +38 -0
  57. data/lib/sferik/status/loaded.rb +48 -0
  58. data/lib/sferik/status.rb +33 -0
  59. data/lib/sferik/talk.rb +73 -0
  60. data/lib/sferik/talks.rb +79 -0
  61. data/lib/sferik/validation.rb +119 -0
  62. data/lib/sferik/version.rb +7 -0
  63. data/lib/sferik/web_finger/link.rb +46 -0
  64. data/lib/sferik/web_finger.rb +43 -0
  65. data/lib/sferik/who.rb +53 -0
  66. data/lib/sferik/whoami.rb +36 -0
  67. data/lib/sferik/wrapping.rb +114 -0
  68. data/lib/sferik.rb +246 -0
  69. data/sig/manifest.yaml +13 -0
  70. data/sig/sferik.rbs +825 -0
  71. metadata +118 -0
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resource"
4
+
5
+ module Sferik
6
+ class Resume < Resource
7
+ # A profile elsewhere, as returned in {Basics#profiles}
8
+ # @api public
9
+ class Profile < Resource
10
+ # @!method network
11
+ # The network
12
+ # @api public
13
+ # @return [String] the network
14
+ # @example
15
+ # profile.network # => "GitHub"
16
+ attribute :network
17
+
18
+ # @!method username
19
+ # The username there
20
+ # @api public
21
+ # @return [String] the username there
22
+ # @example
23
+ # profile.username # => "sferik"
24
+ attribute :username
25
+
26
+ # @!method url
27
+ # The profile
28
+ # @api public
29
+ # @return [String] the profile
30
+ # @example
31
+ # profile.url # => "https://github.com/sferik"
32
+ attribute :url
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resource"
4
+
5
+ module Sferik
6
+ class Resume < Resource
7
+ # Open source work, as returned in {Resume#projects}
8
+ # @api public
9
+ class Project < Resource
10
+ # @!method name
11
+ # The projects
12
+ # @api public
13
+ # @return [String] the projects
14
+ # @example
15
+ # project.name # => "RubyGems.org"
16
+ attribute :name
17
+
18
+ # @!method description
19
+ # What Erik did
20
+ # @api public
21
+ # @return [String] what Erik did
22
+ # @example
23
+ # project.description # => "top contributor (1,900+ commits)..."
24
+ attribute :description
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resource"
4
+
5
+ module Sferik
6
+ class Resume < Resource
7
+ # A group of skills, as returned in {Resume#skills}
8
+ # @api public
9
+ class Skill < Resource
10
+ # @!method name
11
+ # The group
12
+ # @api public
13
+ # @return [String] the group
14
+ # @example
15
+ # skill.name # => "Languages"
16
+ attribute :name
17
+
18
+ # @!method keywords
19
+ # The skills
20
+ # @api public
21
+ # @return [Array<String>] the skills
22
+ # @example
23
+ # skill.keywords # => ["Ruby", "Rust", ...]
24
+ list :keywords
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resource"
4
+
5
+ module Sferik
6
+ class Resume < Resource
7
+ # Speaking, as returned by {Resume#speaking} (an addition to JSON Resume)
8
+ # @api public
9
+ class Speaking < Resource
10
+ # @!method summary
11
+ # A summary of talks, with *italic* titles
12
+ # @api public
13
+ # @return [String] a summary of talks, with *italic* titles
14
+ # @example
15
+ # speaking.summary # => "Spoke at 17 conferences in 13 countries..."
16
+ attribute :summary
17
+ end
18
+ end
19
+ 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 volunteer role, as returned in {Resume#volunteer}
8
+ # @api public
9
+ class Volunteer < Resource
10
+ # @!method position
11
+ # The role
12
+ # @api public
13
+ # @return [String] the role
14
+ # @example
15
+ # role.position # => "Board of Directors"
16
+ attribute :position
17
+
18
+ # @!method organization
19
+ # The organization
20
+ # @api public
21
+ # @return [String] the organization
22
+ # @example
23
+ # role.organization # => "Pacific Primary"
24
+ attribute :organization
25
+
26
+ # @!method start_date
27
+ # When it started, as the first day of the month or year given
28
+ # @api public
29
+ # @return [Date] when it started, as the first day of the month or year given
30
+ # @example
31
+ # role.start_date # => #<Date: 2023-09-01>
32
+ timestamp :start_date, PartialDate
33
+
34
+ # @!method end_date
35
+ # When it ended, or nil if it hasn't
36
+ # @api public
37
+ # @return [Date, nil] when it ended, or nil if it hasn't
38
+ # @example
39
+ # role.end_date # => #<Date: 2026-01-01>
40
+ timestamp :end_date, PartialDate
41
+
42
+ # @!method summary
43
+ # What Erik did
44
+ # @api public
45
+ # @return [String, nil] what Erik did
46
+ # @example
47
+ # role.summary # => "President 2025–2026, Treasurer 2024–2025..."
48
+ attribute :summary
49
+
50
+ # @!method url
51
+ # The organization's site
52
+ # @api public
53
+ # @return [String, nil] the organization's site
54
+ # @example
55
+ # role.url # => "https://pacificprimary.org"
56
+ attribute :url
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resource"
4
+
5
+ module Sferik
6
+ class Resume < Resource
7
+ # A job, as returned in {Resume#work}
8
+ # @api public
9
+ class Work < Resource
10
+ # @!method position
11
+ # The title
12
+ # @api public
13
+ # @return [String] the title
14
+ # @example
15
+ # work.position # => "Software Engineering Manager"
16
+ attribute :position
17
+
18
+ # @!method name
19
+ # The company
20
+ # @api public
21
+ # @return [String] the company
22
+ # @example
23
+ # work.name # => "Twitter"
24
+ attribute :name
25
+
26
+ # @!method start_date
27
+ # When it started, as the first day of the month or year given
28
+ # @api public
29
+ # @return [Date] when it started, as the first day of the month or year given
30
+ # @example
31
+ # work.start_date # => #<Date: 2021-01-01>
32
+ timestamp :start_date, PartialDate
33
+
34
+ # @!method end_date
35
+ # When it ended, or nil if it hasn't
36
+ # @api public
37
+ # @return [Date, nil] when it ended, or nil if it hasn't
38
+ # @example
39
+ # work.end_date # => #<Date: 2023-01-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
+ # work.highlights # => ["Led the engineering team that built Subscriptions..."]
48
+ list :highlights
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "resource"
4
+ require_relative "resume/award"
5
+ require_relative "resume/basics"
6
+ require_relative "resume/education"
7
+ require_relative "resume/meta"
8
+ require_relative "resume/patent"
9
+ require_relative "resume/project"
10
+ require_relative "resume/skill"
11
+ require_relative "resume/speaking"
12
+ require_relative "resume/volunteer"
13
+ require_relative "resume/work"
14
+
15
+ module Sferik
16
+ # The resume, a JSON Resume document (https://jsonresume.org/schema), as returned by {API::ResumeEndpoints#resume}
17
+ #
18
+ # Its sections are objects of their own, under this class: {Basics}, {Work}, {Award}, and so on.
19
+ # @api public
20
+ class Resume < Resource
21
+ # @!method basics
22
+ # The name, contact details, summary, and location
23
+ # @api public
24
+ # @return [Basics] the name, contact details, summary, and location
25
+ # @example
26
+ # resume.basics # => #<Sferik::Resume::Basics ...>
27
+ attribute :basics, "basics", type: Basics
28
+
29
+ # @!method work
30
+ # Jobs, newest first
31
+ # @api public
32
+ # @return [Array<Work>] jobs, newest first
33
+ # @example
34
+ # resume.work # => [#<Sferik::Resume::Work ...>, ...]
35
+ list :work, "work", type: Work
36
+
37
+ # @!method projects
38
+ # Open source work
39
+ # @api public
40
+ # @return [Array<Project>] open source work
41
+ # @example
42
+ # resume.projects # => [#<Sferik::Resume::Project ...>, ...]
43
+ list :projects, "projects", type: Project
44
+
45
+ # @!method skills
46
+ # Skills, by group
47
+ # @api public
48
+ # @return [Array<Skill>] skills, by group
49
+ # @example
50
+ # resume.skills # => [#<Sferik::Resume::Skill ...>]
51
+ list :skills, "skills", type: Skill
52
+
53
+ # @!method awards
54
+ # Honors
55
+ # @api public
56
+ # @return [Array<Award>] honors
57
+ # @example
58
+ # resume.awards # => [#<Sferik::Resume::Award ...>, ...]
59
+ list :awards, "awards", type: Award
60
+
61
+ # @!method patents
62
+ # Patents
63
+ # @api public
64
+ # @return [Array<Patent>] patents
65
+ # @example
66
+ # resume.patents # => [#<Sferik::Resume::Patent ...>, ...]
67
+ list :patents, "patents", type: Patent
68
+
69
+ # @!method speaking
70
+ # A summary of talks
71
+ # @api public
72
+ # @return [Speaking] a summary of talks
73
+ # @example
74
+ # resume.speaking # => #<Sferik::Resume::Speaking ...>
75
+ attribute :speaking, "speaking", type: Speaking
76
+
77
+ # @!method education
78
+ # Schools
79
+ # @api public
80
+ # @return [Array<Education>] schools
81
+ # @example
82
+ # resume.education # => [#<Sferik::Resume::Education ...>]
83
+ list :education, "education", type: Education
84
+
85
+ # @!method volunteer
86
+ # Service
87
+ # @api public
88
+ # @return [Array<Volunteer>] service
89
+ # @example
90
+ # resume.volunteer # => [#<Sferik::Resume::Volunteer ...>, ...]
91
+ list :volunteer, "volunteer", type: Volunteer
92
+
93
+ # @!method meta
94
+ # What the resume says about itself, or nil if it says nothing
95
+ # @api public
96
+ # @return [Meta, nil] what the resume says about itself, or nil if it says nothing
97
+ # @example
98
+ # resume.meta # => #<Sferik::Resume::Meta ...>
99
+ attribute :meta, "meta", type: Meta
100
+
101
+ inspect_with :name
102
+
103
+ # The name on the resume, from {#basics}
104
+ #
105
+ # @api public
106
+ # @return [String, nil] the name
107
+ # @example
108
+ # resume.name # => "Erik Berlin"
109
+ def name
110
+ basics&.name
111
+ end
112
+ record :name
113
+
114
+ # When the resume last changed, from {#meta}
115
+ #
116
+ # @api public
117
+ # @return [Date, nil] when the resume last changed
118
+ # @example
119
+ # resume.last_modified # => #<Date: 2026-10-01>
120
+ def last_modified
121
+ meta&.last_modified
122
+ end
123
+ record :last_modified
124
+ end
125
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "resource"
4
+
5
+ module Sferik
6
+ # A reader's terminal, one per browser tab, as returned in {Who#users}
7
+ # @api public
8
+ class Session < Resource
9
+ # @!method tty
10
+ # The terminal's name
11
+ # @api public
12
+ # @return [String] the terminal's name
13
+ # @example
14
+ # session.tty # => "ttys001"
15
+ attribute :tty
16
+
17
+ # @!method page
18
+ # The page the tab is on
19
+ # @api public
20
+ # @return [String] the page the tab is on
21
+ # @example
22
+ # session.page # => "/talks"
23
+ attribute :page
24
+
25
+ # @!method login
26
+ # When the tab opened
27
+ # @api public
28
+ # @return [Time] when the tab opened
29
+ # @example
30
+ # session.login # => 2026-10-06 12:00:00 UTC
31
+ timestamp :login, Time
32
+
33
+ # @!method idle
34
+ # The seconds since the tab last checked in
35
+ # @api public
36
+ # @return [Integer] the seconds since the tab last checked in
37
+ # @example
38
+ # session.idle # => 42
39
+ attribute :idle
40
+ end
41
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "resource"
4
+
5
+ module Sferik
6
+ # A profile elsewhere, such as GitHub or Mastodon, as listed by {API::ProfileEndpoints#finger}
7
+ # @api public
8
+ class SocialProfile < Resource
9
+ # @!method network
10
+ # The name of the network
11
+ # @api public
12
+ # @return [String] the name of the network
13
+ # @example
14
+ # profile.network # => "GitHub"
15
+ attribute :network
16
+
17
+ # @!method username
18
+ # The username on that network
19
+ # @api public
20
+ # @return [String] the username on that network
21
+ # @example
22
+ # profile.username # => "sferik"
23
+ attribute :username
24
+
25
+ # @!method url
26
+ # The URL of the profile
27
+ # @api public
28
+ # @return [String] the URL of the profile
29
+ # @example
30
+ # profile.url # => "https://github.com/sferik"
31
+ attribute :url
32
+
33
+ # @!method icon
34
+ # The id of the profile's icon in https://sferik.net/icons.svg
35
+ # @api public
36
+ # @return [String] the id of the profile's icon in https://sferik.net/icons.svg
37
+ # @example
38
+ # profile.icon # => "github"
39
+ attribute :icon
40
+
41
+ # @!method aliases
42
+ # The names the site's shell accepts for `open`
43
+ # @api public
44
+ # @return [Array<String>] the names the site's shell accepts for `open`
45
+ # @example
46
+ # profile.aliases # => ["github", "gh"]
47
+ list :aliases
48
+ end
49
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resource"
4
+
5
+ module Sferik
6
+ class Status < Resource
7
+ # What became of asking GitHub with the site's token, as returned by {Status#github}
8
+ #
9
+ # A copy of the site that has no token, or hasn't asked yet, has nil for each.
10
+ #
11
+ # @api public
12
+ class GitHub < Resource
13
+ # @!method asked
14
+ # When GitHub was last asked with the token
15
+ # @api public
16
+ # @return [Time, nil] when GitHub was last asked with the token
17
+ # @example
18
+ # github.asked # => 2026-10-08 21:45:07 UTC
19
+ timestamp :asked, Time
20
+
21
+ # @!method answered
22
+ # When GitHub last answered: when it was asked, if the last answer came
23
+ # @api public
24
+ # @return [Time, nil] when GitHub last answered: when it was asked, if the last answer came
25
+ # @example
26
+ # github.answered # => 2026-10-08 21:45:07 UTC
27
+ timestamp :answered, Time
28
+
29
+ # @!method error
30
+ # What went wrong the last time GitHub was asked
31
+ # @api public
32
+ # @return [String, nil] what went wrong the last time GitHub was asked, or nil if it answered
33
+ # @example
34
+ # github.error # => "https://api.github.com/graphql: 401"
35
+ attribute :error
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resource"
4
+
5
+ module Sferik
6
+ class Status < Resource
7
+ # When the site last loaded each of its live values, as returned by {Status#loaded}
8
+ #
9
+ # {Projects#as_of} and {Contributions#as_of} say so of the downloads and the contributions, and nothing else does
10
+ # of the stars and the latest push. One that has never been loaded is nil: on a copy of the site that's offline,
11
+ # each is.
12
+ #
13
+ # @api public
14
+ class Loaded < Resource
15
+ # @!method gems
16
+ # When the downloads were last loaded, from RubyGems
17
+ # @api public
18
+ # @return [Time, nil] when the downloads were last loaded, from RubyGems
19
+ # @example
20
+ # loaded.gems # => 2026-10-08 21:45:07 UTC
21
+ timestamp :gems, Time
22
+
23
+ # @!method stars
24
+ # When the stars were last loaded, from GitHub
25
+ # @api public
26
+ # @return [Time, nil] when the stars were last loaded, from GitHub
27
+ # @example
28
+ # loaded.stars # => 2026-10-08 21:45:07 UTC
29
+ timestamp :stars, Time
30
+
31
+ # @!method contributions
32
+ # When the year of contributions was last loaded, from GitHub
33
+ # @api public
34
+ # @return [Time, nil] when the year of contributions was last loaded, from GitHub
35
+ # @example
36
+ # loaded.contributions # => 2026-10-08 21:45:07 UTC
37
+ timestamp :contributions, Time
38
+
39
+ # @!method push
40
+ # When the latest push was last loaded, from GitHub
41
+ # @api public
42
+ # @return [Time, nil] when the latest push was last loaded, from GitHub
43
+ # @example
44
+ # loaded.push # => 2026-10-08 21:45:07 UTC
45
+ timestamp :push, Time
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "resource"
4
+ require_relative "status/github"
5
+ require_relative "status/loaded"
6
+
7
+ module Sferik
8
+ # Whether the site's live numbers come as they should, as returned by {API::SiteEndpoints#status}
9
+ #
10
+ # The site asks GitHub for the contributions, the stars, and the latest push in one request, with a token. If that
11
+ # fails, it asks for each another way, without the token, and the numbers come all the same: {Contributions#live?}
12
+ # and {Projects#live?} are still true. So this says what became of asking with the token, which nothing else does.
13
+ # And it says when each live value was last loaded: the stars and the latest push too, which say so nowhere else.
14
+ #
15
+ # @api public
16
+ class Status < Resource
17
+ # @!method github
18
+ # What became of asking GitHub with the site's token
19
+ # @api public
20
+ # @return [GitHub, nil] what became of asking GitHub with the site's token
21
+ # @example
22
+ # status.github.answered # => 2026-10-08 21:45:07 UTC
23
+ attribute :github, type: GitHub
24
+
25
+ # @!method loaded
26
+ # When the site last loaded each of its live values
27
+ # @api public
28
+ # @return [Loaded, nil] when the site last loaded each of its live values
29
+ # @example
30
+ # status.loaded.stars # => 2026-10-08 21:45:07 UTC
31
+ attribute :loaded, type: Loaded
32
+ end
33
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "resource"
4
+
5
+ module Sferik
6
+ # A conference talk, as returned in {Talks#talks}
7
+ # @api public
8
+ class Talk < Resource
9
+ # @!method title
10
+ # The talk's title
11
+ # @api public
12
+ # @return [String] the talk's title
13
+ # @example
14
+ # talk.title # => "Writing Fast Ruby"
15
+ attribute :title
16
+
17
+ # @!method event
18
+ # The conference
19
+ # @api public
20
+ # @return [String] the conference
21
+ # @example
22
+ # talk.event # => "Baruco"
23
+ attribute :event
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
+ # talk.date # => #<Date: 2014-09-01>
31
+ timestamp :date, PartialDate
32
+
33
+ # @!method location
34
+ # Where the conference was
35
+ # @api public
36
+ # @return [String] where the conference was
37
+ # @example
38
+ # talk.location # => "Barcelona"
39
+ attribute :location
40
+
41
+ # @!method slides
42
+ # The slides, if they're online
43
+ # @api public
44
+ # @return [String, nil] the slides, if they're online
45
+ # @example
46
+ # talk.slides # => "https://speakerdeck.com/sferik/writing-fast-ruby"
47
+ attribute :slides
48
+
49
+ # @!method video
50
+ # The video, if there is one
51
+ # @api public
52
+ # @return [String, nil] the video, if there is one
53
+ # @example
54
+ # talk.video # => "https://www.youtube.com/watch?v=fGFM_UrSp70"
55
+ attribute :video
56
+
57
+ # @!method link
58
+ # The talk's page on the event's site, if it has one
59
+ # @api public
60
+ # @return [String, nil] the talk's page on the event's site, if it has one
61
+ # @example
62
+ # talk.link # => "https://schedule.sxsw.com/2015/events/event_IAP35000"
63
+ attribute :link
64
+
65
+ # @!method featured?
66
+ # Whether the home page shows it
67
+ # @api public
68
+ # @return [Boolean] whether the home page shows it
69
+ # @example
70
+ # talk.featured? # => true
71
+ predicate :featured
72
+ end
73
+ end