forem-ruby 0.1.0.beta4 → 0.1.0.beta6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70aa093a419b454eae92eb74afdfddcea88acf77f0112c8d6d988874ec142b5c
4
- data.tar.gz: 5e3ce2de3e9302ad7a2c0a05102af264288da4b0836c445c84fbf14d9fe4a7b8
3
+ metadata.gz: f3ad62af106cb7a2949a2a6516b34529e8ccb83b6dbe34d380c5a8c2090a5f84
4
+ data.tar.gz: 68b08dfc2b22d528009a25ac8ad5b56f117c474949287003bc3f1817e7caae31
5
5
  SHA512:
6
- metadata.gz: 86e0016a62e3db3057028d61dd8fb23a29882b67e386a0446bca269eead5bfffa770be8fd67cb194868c43a7980d221255e31ac477a59f3561d7ff8b8bba37d2
7
- data.tar.gz: f915cad693ab839f2f8afdacdc797aecfa4afd8a21ca499eb09f305a8f01289bd0296a891bd612f879f5ac27625469557a015639597f4704c8a3302c831bd245
6
+ metadata.gz: 73f6857ecc8e052613b7fb48efa691d9ab4d521a6a7db37b2a5ced7d86e21e5b2ad8a8d8d895407e713045c86ad4c58397d4b45b01648ac347aa9dc78a745d13
7
+ data.tar.gz: 5008eda4cff82e831d2aaf6fe09f3a60805bc09b44542c1b9cbe36ac0429a6b6fdcabe4cda1275d16b70f1b038a4fafc909a8fa286ebcd65a7a39f03905061b2
data/lib/forem/client.rb CHANGED
@@ -295,5 +295,15 @@ module Forem
295
295
  # @example
296
296
  # client.request_redirects.list
297
297
  def request_redirects; @request_redirects ||= Services::RequestRedirectService.new(@requestor); end
298
+
299
+ # Access the Events API.
300
+ #
301
+ # @return [Services::EventService] the events service
302
+ # @see https://developers.forem.com/api/v1
303
+ #
304
+ # @example
305
+ # client.events.list(type_of: "challenge")
306
+ # client.events.retrieve(12)
307
+ def events; @events ||= Services::EventService.new(@requestor); end
298
308
  end
299
309
  end
data/lib/forem/errors.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require "json"
2
+
1
3
  module Forem
2
4
  # Base error class for all errors raised by the forem-ruby library.
3
5
  #
@@ -45,6 +47,28 @@ module Forem
45
47
  @code = code
46
48
  super(message)
47
49
  end
50
+
51
+ # The response body parsed as JSON.
52
+ #
53
+ # Useful when the API returns structured detail alongside an error. An empty
54
+ # or unparseable body yields +nil+.
55
+ #
56
+ # @return [Hash, Array, nil] the parsed body, or +nil+ when there is
57
+ # nothing parseable to return.
58
+ #
59
+ # @example Reading a field the API returned with the error
60
+ # begin
61
+ # client.badge_achievements.create(user_id: 123, badge_id: 45)
62
+ # rescue Forem::ConflictError => e
63
+ # e.parsed_body&.dig("achievement_id")
64
+ # end
65
+ def parsed_body
66
+ return @parsed_body if defined?(@parsed_body)
67
+
68
+ @parsed_body = (JSON.parse(http_body) if http_body && !http_body.empty?)
69
+ rescue JSON::ParserError
70
+ @parsed_body = nil
71
+ end
48
72
  end
49
73
 
50
74
  # Raised when the server responds with HTTP 401 Unauthorized.
@@ -43,8 +43,12 @@ module Forem
43
43
  # @option params [String] 'article.canonical_url' (nullable)
44
44
  # @option params [String] 'article.main_image' (nullable)
45
45
  # @option params [Integer] 'article.organization_id' (nullable)
46
+ # @option params [Integer] 'article.ai_disclosure_level' AI disclosure level (e.g. 0 for none, 1 for assisted, 2 for generated)
46
47
  #
47
- # @example List published articles
48
+ # == Article Fields
49
+ #
50
+ # Additional response attributes include +ai_disclosure_level+, +ai_disclosure_label+,
51
+ # +cover_image+, +social_image+, +reading_time+, +positive_reactions_count+, etc.
48
52
  # articles = client.articles.list(per_page: 10, tag: "ruby")
49
53
  # articles.data.each { |a| puts a.title }
50
54
  #
@@ -21,6 +21,7 @@ module Forem
21
21
  # - +rewarding_context_message+ (String) — Rendered from
22
22
  # +rewarding_context_message_markdown+ by the API; not writable
23
23
  # - +include_default_description+ (Boolean) — Whether the badge description accompanies the message
24
+ # - +metadata+ (ForemObject) — Arbitrary key/value data supplied for context. Defaults to +{}+.
24
25
  # - +created_at+ / +updated_at+ (String) — ISO 8601 timestamps; not writable
25
26
  #
26
27
  # == Authentication
@@ -38,6 +39,13 @@ module Forem
38
39
  # )
39
40
  # puts achievement.id
40
41
  #
42
+ # @example Award a badge with caller-supplied metadata
43
+ # client.badge_achievements.create(
44
+ # user_id: 123,
45
+ # badge_id: 45,
46
+ # metadata: { entitlement_id: "01a0…", source: "core" }
47
+ # )
48
+ #
41
49
  # @example Revoke a badge
42
50
  # client.badge_achievements.delete(9876)
43
51
  #
@@ -17,6 +17,11 @@ module Forem
17
17
  # - +page+ (Integer) — Page number
18
18
  # - +per_page+ (Integer) — Items per page (default: 50)
19
19
  #
20
+ # == Comment Fields
21
+ #
22
+ # Comment objects expose attributes such as +id_code+, +created_at+, +body_html+,
23
+ # +ai_disclosure_level+, and +ai_disclosure_label+.
24
+ #
20
25
  # Returns threaded conversations with nested replies.
21
26
  #
22
27
  # @example List all comments for an article
@@ -0,0 +1,137 @@
1
+ module Forem
2
+ # Represents an event on a Forem instance.
3
+ #
4
+ # Events can represent live streams, hackathons, takeovers, or community
5
+ # meetups. The API supports full CRUD operations, and the create endpoint
6
+ # behaves as an idempotent upsert when matching slugs or numeric IDs are
7
+ # provided.
8
+ #
9
+ # Available operations (via mixins):
10
+ # - +List+ — GET /api/events (supports +type_of+ filtering)
11
+ # - +Create+ — POST /api/events (idempotent upsert, admin privileges)
12
+ # - +Retrieve+ — GET /api/events/:id_or_slug
13
+ # - +Update+ — PUT /api/events/:id_or_slug (admin privileges)
14
+ # - +Delete+ — class- and instance-level delete (DELETE /api/events/:id_or_slug, admin privileges)
15
+ # - +Save+ — instance-level save (create or update)
16
+ #
17
+ # == List Parameters
18
+ #
19
+ # When calling +Event.list+, the following query parameters are supported:
20
+ #
21
+ # - +type_of+ (String) — Filter by event type: +"live_stream"+, +"takeover"+, +"other"+, +"challenge"+
22
+ # - +page+ (Integer) — Page number (default: 1)
23
+ # - +per_page+ (Integer) — Items per page (default: 30, max: 1000)
24
+ #
25
+ # == Event Fields
26
+ #
27
+ # - +id+ (Integer) — Assigned by the API
28
+ # - +title+ (String, required) — Title of the event
29
+ # - +event_name_slug+ (String, required) — Lowercase slug identifier (e.g. +"hackrice"+)
30
+ # - +event_variation_slug+ (String, required) — Edition/season/year slug (e.g. +"13"+ or +"2026"+)
31
+ # - +description+ (String) — Markdown or plain text description
32
+ # - +full_details+ (String) — Comprehensive plain text / Markdown dump of all event details, agenda, and context notes
33
+ # - +start_time+ (String / ISO 8601, required) — Start timestamp
34
+ # - +end_time+ (String / ISO 8601, required) — End timestamp
35
+ # - +type_of+ (String) — One of: +"live_stream"+, +"takeover"+, +"other"+, +"challenge"+
36
+ # - +broadcast_config+ (String) — One of: +"no_broadcast"+, +"tagged_broadcast"+, +"global_broadcast"+
37
+ # - +primary_stream_url+ (String) — Video streaming URL (YouTube, Twitch, Streamyard)
38
+ # - +manual_broadcast_end+ (Boolean) — Whether broadcasting end is manually handled
39
+ # - +published+ (Boolean) — Whether the event is publicly listed
40
+ # - +elevated+ (Boolean) — Whether the event is featured/elevated
41
+ # - +bg_color_hex+ (String) — 6-digit hex background color code (e.g. +"#7C3AED"+)
42
+ # - +cover_image+ (String) — Uploaded cover image filename
43
+ # - +remote_cover_image_url+ (String) — Remote URL to fetch, crop, and store as cover image
44
+ # - +remove_cover_image+ (Boolean) — Flag to delete the existing cover image
45
+ # - +tag_list+ (String) — Comma-separated list of tags
46
+ # - +user_id+ (Integer) — ID of the host/creator user
47
+ # - +organization_id+ (Integer) — ID of the host organization
48
+ # - +page_id+ (Integer) — ID of an associated page
49
+ # - +delegate_to_page+ (Boolean) — Whether landing page links delegate directly to page
50
+ # - +data+ (Hash) — Structured metadata such as +location+, +format+, +external_registration_url+
51
+ # - +cover_image_url+ (String) — Public URL to the uploaded cover image
52
+ # - +social_image_url+ (String) — Public URL to the 1200x630 social card crop or default fallback
53
+ # - +background_hex_color+ (String) — Effective background hex color
54
+ # - +formatted_date_range+ (String) — Display-ready date range string (e.g. +"AUG 28 - 30"+)
55
+ # - +location+ (String) — Display location string (e.g. +"Everywhere, Worldwide"+)
56
+ # - +format+ (String) — Display format pill label (e.g. +"DIGITAL"+, +"IN-PERSON"+)
57
+ #
58
+ # @example List published events or filter by type_of
59
+ # events = client.events.list(type_of: "challenge")
60
+ # events.each { |e| puts "#{e.title} (#{e.formatted_date_range})" }
61
+ #
62
+ # @example Retrieve an event by numeric ID or slug
63
+ # event = client.events.retrieve(12)
64
+ # puts event.title
65
+ #
66
+ # @example Create or upsert an event
67
+ # event = client.events.create(
68
+ # title: "HackRice XIII",
69
+ # event_name_slug: "hackrice",
70
+ # event_variation_slug: "13",
71
+ # start_time: "2026-09-11T10:00:00Z",
72
+ # end_time: "2026-09-13T16:00:00Z",
73
+ # type_of: "challenge",
74
+ # full_details: "Full hackathon schedule, judging rubric, and prizes breakdown.",
75
+ # published: true,
76
+ # bg_color_hex: "#7C3AED",
77
+ # remote_cover_image_url: "https://assets.example.com/banner.png",
78
+ # data: {
79
+ # location: "Houston, Texas, US",
80
+ # format: "IN-PERSON",
81
+ # external_registration_url: "https://hackrice.com"
82
+ # }
83
+ # )
84
+ #
85
+ # @example Delete an event
86
+ # client.events.delete(12)
87
+ #
88
+ # @see https://developers.forem.com/api/v1
89
+ class Event < APIResource
90
+ extend APIOperations::Create
91
+ extend APIOperations::List
92
+ extend APIOperations::Retrieve
93
+ extend APIOperations::Update
94
+ include APIOperations::Delete
95
+ include APIOperations::Save
96
+
97
+ OBJECT_NAME = "event"
98
+ RESOURCE_PATH = "/api/events"
99
+
100
+ # Create a new event or upsert by matching slugs.
101
+ #
102
+ # Automatically wraps flat parameters under the +:event+ key expected by the API.
103
+ #
104
+ # @param params [Hash] event parameters
105
+ # @param opts [Hash] per-request options
106
+ # @return [Event] the newly created event
107
+ def self.create(params = {}, opts = {})
108
+ payload = params.key?(:event) || params.key?("event") ? params : { event: params }
109
+ super(payload, opts)
110
+ end
111
+
112
+ # Update an existing event.
113
+ #
114
+ # Automatically wraps flat parameters under the +:event+ key expected by the API.
115
+ #
116
+ # @param id [Integer, String] event ID or slug
117
+ # @param params [Hash] attributes to update
118
+ # @param opts [Hash] per-request options
119
+ # @return [Event] the updated event
120
+ def self.update(id, params = {}, opts = {})
121
+ payload = params.key?(:event) || params.key?("event") ? params : { event: params }
122
+ super(id, payload, opts)
123
+ end
124
+
125
+ # Save this event instance via the API.
126
+ #
127
+ # Automatically wraps flat parameters under the +:event+ key expected by the API.
128
+ #
129
+ # @param params [Hash] attributes to update
130
+ # @param opts [Hash] per-request keyword options
131
+ # @return [Event] updated event instance
132
+ def save(params = {}, **opts)
133
+ payload = params.key?(:event) || params.key?("event") ? params : { event: params }
134
+ super(payload, **opts)
135
+ end
136
+ end
137
+ end
@@ -51,6 +51,16 @@ module Forem
51
51
 
52
52
  # Award a badge to a user.
53
53
  #
54
+ # If the badge cannot be awarded more than once and the user already holds
55
+ # it, this raises {ConflictError} (HTTP 409). The response carries
56
+ # +achievement_id+, the id of the award that blocked the request.
57
+ #
58
+ # begin
59
+ # client.badge_achievements.create(user_id: 123, badge_id: 45)
60
+ # rescue Forem::ConflictError => e
61
+ # existing_id = e.parsed_body&.dig("achievement_id")
62
+ # end
63
+ #
54
64
  # @param params [Hash] achievement attributes
55
65
  # @option params [Integer] :user_id the user ID (required)
56
66
  # @option params [Integer] :badge_id the badge ID (required)
@@ -58,14 +68,19 @@ module Forem
58
68
  # shown with the award
59
69
  # @option params [Boolean] :include_default_description whether to show
60
70
  # the badge's own description alongside the message (default true)
71
+ # @option params [Hash] :metadata arbitrary key/value data stored with the
72
+ # achievement for context
61
73
  # @param opts [Hash] per-request options
62
74
  # @return [BadgeAchievement] the newly created achievement
75
+ # @raise [ConflictError] on HTTP 409 when the user already holds a badge
76
+ # that cannot be awarded more than once
63
77
  # @raise [InvalidRequestError] on HTTP 422
64
78
  #
65
79
  # @example
66
80
  # client.badge_achievements.create(
67
81
  # user_id: 123,
68
- # badge_id: 45
82
+ # badge_id: 45,
83
+ # metadata: { entitlement_id: "01a0…" }
69
84
  # )
70
85
  #
71
86
  # @see https://developers.forem.com/api/v1
@@ -0,0 +1,121 @@
1
+ module Forem
2
+ module Services
3
+ # Service for interacting with the Forem Events API.
4
+ #
5
+ # Events represent live streams, hackathons, takeovers, or community
6
+ # meetups. Access via {Client#events}. All methods inject the client's requestor
7
+ # automatically so no additional configuration is required.
8
+ #
9
+ # @example
10
+ # client = Forem::Client.new("your-api-key")
11
+ # events = client.events.list(type_of: "challenge")
12
+ # event = client.events.retrieve(10)
13
+ #
14
+ # @see Event
15
+ # @see https://developers.forem.com/api/v1
16
+ class EventService < BaseService
17
+ # List all events.
18
+ #
19
+ # @param params [Hash] query parameters
20
+ # @option params [String] :type_of filter by event type (e.g. "live_stream", "takeover", "other", "challenge")
21
+ # @option params [Integer] :page page number (default: 1)
22
+ # @option params [Integer] :per_page number of results per page
23
+ # @param opts [Hash] per-request options
24
+ # @return [Forem::ListObject<Event>] list of events
25
+ #
26
+ # @example
27
+ # client.events.list
28
+ # client.events.list(type_of: "challenge")
29
+ #
30
+ # @see https://developers.forem.com/api/v1
31
+ def list(params = {}, opts = {})
32
+ Event.list(params, opts_with_requestor(opts))
33
+ end
34
+
35
+ # Retrieve a single event by its numeric ID or slug.
36
+ #
37
+ # @param id [Integer, String] the event ID or slug
38
+ # @param opts [Hash] per-request options
39
+ # @return [Event] the event with the given ID
40
+ #
41
+ # @example
42
+ # client.events.retrieve(10)
43
+ #
44
+ # @see https://developers.forem.com/api/v1
45
+ def retrieve(id, opts = {})
46
+ Event.retrieve(id, opts_with_requestor(opts))
47
+ end
48
+
49
+ # Create a new event or upsert by matching slugs / ID.
50
+ #
51
+ # Accepts flat attribute hashes or nested under +:event+.
52
+ #
53
+ # @param params [Hash] event attributes
54
+ # @option params [String] :title event title (required)
55
+ # @option params [String] :event_name_slug lowercase slug identifier (required)
56
+ # @option params [String] :event_variation_slug edition/season/year slug (required)
57
+ # @option params [String] :start_time ISO 8601 start timestamp (required)
58
+ # @option params [String] :end_time ISO 8601 end timestamp (required)
59
+ # @option params [String] :type_of event type ("live_stream", "takeover", "other", "challenge")
60
+ # @option params [String] :description Markdown or plain text description
61
+ # @option params [String] :full_details comprehensive plain text / Markdown details dump
62
+ # @option params [String] :primary_stream_url streaming URL
63
+ # @option params [Boolean] :published whether the event is publicly listed
64
+ # @option params [String] :bg_color_hex hex background color code
65
+ # @option params [String] :remote_cover_image_url remote URL to fetch cover image
66
+ # @option params [Integer] :organization_id host organization ID
67
+ # @option params [String] :tag_list comma-separated tags
68
+ # @option params [Hash] :data structured metadata
69
+ # @param opts [Hash] per-request options
70
+ # @return [Event] the newly created event
71
+ #
72
+ # @example
73
+ # client.events.create(
74
+ # title: "Hackathon 2026",
75
+ # event_name_slug: "hackathon",
76
+ # event_variation_slug: "2026",
77
+ # start_time: "2026-09-01T09:00:00Z",
78
+ # end_time: "2026-09-03T18:00:00Z",
79
+ # type_of: "challenge",
80
+ # full_details: "Complete schedule and prize information for agent context",
81
+ # published: true
82
+ # )
83
+ #
84
+ # @see https://developers.forem.com/api/v1
85
+ def create(params = {}, opts = {})
86
+ Event.create(params, opts_with_requestor(opts))
87
+ end
88
+
89
+ # Update an existing event.
90
+ #
91
+ # Accepts flat attribute hashes or nested under +:event+.
92
+ #
93
+ # @param id [Integer, String] the event ID to update
94
+ # @param params [Hash] event attributes to change
95
+ # @param opts [Hash] per-request options
96
+ # @return [Event] the updated event
97
+ #
98
+ # @example
99
+ # client.events.update(10, title: "Updated Hackathon 2026")
100
+ #
101
+ # @see https://developers.forem.com/api/v1
102
+ def update(id, params = {}, opts = {})
103
+ Event.update(id, params, opts_with_requestor(opts))
104
+ end
105
+
106
+ # Delete an event.
107
+ #
108
+ # @param id [Integer, String] the event ID to delete
109
+ # @param opts [Hash] per-request options
110
+ # @return [nil] returns nil on success
111
+ #
112
+ # @example
113
+ # client.events.delete(10)
114
+ #
115
+ # @see https://developers.forem.com/api/v1
116
+ def delete(id, opts = {})
117
+ Event.delete(id, opts_with_requestor(opts))
118
+ end
119
+ end
120
+ end
121
+ end
data/lib/forem/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Forem
2
2
  # The current version of the forem-ruby gem.
3
- VERSION = "0.1.0.beta4"
3
+ VERSION = "0.1.0.beta6"
4
4
  end
data/lib/forem.rb CHANGED
@@ -42,6 +42,7 @@ require "forem/resources/trend"
42
42
  require "forem/resources/concept"
43
43
  require "forem/resources/admin_concept"
44
44
  require "forem/resources/request_redirect"
45
+ require "forem/resources/event"
45
46
  require "forem/services/base_service"
46
47
  require "forem/services/article_service"
47
48
  require "forem/services/badge_service"
@@ -70,6 +71,7 @@ require "forem/services/trend_service"
70
71
  require "forem/services/concept_service"
71
72
  require "forem/services/admin_concept_service"
72
73
  require "forem/services/request_redirect_service"
74
+ require "forem/services/event_service"
73
75
  require "forem/client"
74
76
 
75
77
  # Top-level namespace for the forem-ruby gem.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forem-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.beta4
4
+ version: 0.1.0.beta6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Forem
@@ -43,6 +43,7 @@ files:
43
43
  - lib/forem/resources/billboard.rb
44
44
  - lib/forem/resources/comment.rb
45
45
  - lib/forem/resources/concept.rb
46
+ - lib/forem/resources/event.rb
46
47
  - lib/forem/resources/follow.rb
47
48
  - lib/forem/resources/follower.rb
48
49
  - lib/forem/resources/health_check.rb
@@ -71,6 +72,7 @@ files:
71
72
  - lib/forem/services/billboard_service.rb
72
73
  - lib/forem/services/comment_service.rb
73
74
  - lib/forem/services/concept_service.rb
75
+ - lib/forem/services/event_service.rb
74
76
  - lib/forem/services/follow_service.rb
75
77
  - lib/forem/services/follower_service.rb
76
78
  - lib/forem/services/health_check_service.rb