forem-ruby 0.1.0.beta1
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/LICENSE +21 -0
- data/forem-ruby.gemspec +17 -0
- data/lib/forem/api_operations/create.rb +47 -0
- data/lib/forem/api_operations/delete.rb +88 -0
- data/lib/forem/api_operations/list.rb +70 -0
- data/lib/forem/api_operations/request.rb +83 -0
- data/lib/forem/api_operations/retrieve.rb +43 -0
- data/lib/forem/api_operations/save.rb +53 -0
- data/lib/forem/api_operations/update.rb +47 -0
- data/lib/forem/api_requestor.rb +283 -0
- data/lib/forem/api_resource.rb +77 -0
- data/lib/forem/client.rb +279 -0
- data/lib/forem/configuration.rb +74 -0
- data/lib/forem/connection_manager.rb +75 -0
- data/lib/forem/errors.rb +118 -0
- data/lib/forem/forem_object.rb +264 -0
- data/lib/forem/forem_response.rb +50 -0
- data/lib/forem/list_object.rb +171 -0
- data/lib/forem/resources/admin_concept.rb +169 -0
- data/lib/forem/resources/admin_user.rb +152 -0
- data/lib/forem/resources/agent_session.rb +110 -0
- data/lib/forem/resources/analytics.rb +151 -0
- data/lib/forem/resources/article.rb +256 -0
- data/lib/forem/resources/billboard.rb +76 -0
- data/lib/forem/resources/comment.rb +43 -0
- data/lib/forem/resources/concept.rb +192 -0
- data/lib/forem/resources/follow.rb +78 -0
- data/lib/forem/resources/follower.rb +56 -0
- data/lib/forem/resources/health_check.rb +70 -0
- data/lib/forem/resources/organization.rb +79 -0
- data/lib/forem/resources/page.rb +52 -0
- data/lib/forem/resources/podcast_episode.rb +36 -0
- data/lib/forem/resources/profile_image.rb +44 -0
- data/lib/forem/resources/reaction.rb +61 -0
- data/lib/forem/resources/reading_list.rb +29 -0
- data/lib/forem/resources/recommended_articles_list.rb +45 -0
- data/lib/forem/resources/request_redirect.rb +60 -0
- data/lib/forem/resources/segment.rb +103 -0
- data/lib/forem/resources/survey.rb +96 -0
- data/lib/forem/resources/tag.rb +27 -0
- data/lib/forem/resources/trend.rb +80 -0
- data/lib/forem/resources/user.rb +229 -0
- data/lib/forem/resources/video.rb +28 -0
- data/lib/forem/services/admin_concept_service.rb +138 -0
- data/lib/forem/services/admin_user_service.rb +114 -0
- data/lib/forem/services/agent_session_service.rb +87 -0
- data/lib/forem/services/analytics_service.rb +93 -0
- data/lib/forem/services/article_service.rb +233 -0
- data/lib/forem/services/base_service.rb +45 -0
- data/lib/forem/services/billboard_service.rb +91 -0
- data/lib/forem/services/comment_service.rb +51 -0
- data/lib/forem/services/concept_service.rb +143 -0
- data/lib/forem/services/follow_service.rb +61 -0
- data/lib/forem/services/follower_service.rb +34 -0
- data/lib/forem/services/health_check_service.rb +46 -0
- data/lib/forem/services/organization_service.rb +103 -0
- data/lib/forem/services/page_service.rb +107 -0
- data/lib/forem/services/podcast_episode_service.rb +34 -0
- data/lib/forem/services/profile_image_service.rb +32 -0
- data/lib/forem/services/reaction_service.rb +72 -0
- data/lib/forem/services/reading_list_service.rb +35 -0
- data/lib/forem/services/recommended_articles_list_service.rb +87 -0
- data/lib/forem/services/request_redirect_service.rb +118 -0
- data/lib/forem/services/segment_service.rb +83 -0
- data/lib/forem/services/survey_service.rb +48 -0
- data/lib/forem/services/tag_service.rb +32 -0
- data/lib/forem/services/trend_service.rb +70 -0
- data/lib/forem/services/user_service.rb +61 -0
- data/lib/forem/services/video_service.rb +33 -0
- data/lib/forem/util.rb +43 -0
- data/lib/forem/version.rb +4 -0
- data/lib/forem.rb +91 -0
- metadata +111 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: fe5320646c371a3a708ad3f52c847bb9580341799d60e16624bb731c47a70dac
|
|
4
|
+
data.tar.gz: 28292cfa205edaaa90346520b1b4ae448dea0ee698c67dff48c45cd5cdd8fb4c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 966257cf5cf0b1034a98e227ebdd01801c2fd66667d426c7d07a53bd5d9b0130f3fb82a43b5159126c09afcbbdd83202439504221e499bc7ef62e572457d1be1
|
|
7
|
+
data.tar.gz: '057930af643ab8c4c90a30a44c9e80066626256eadec4917f2e36c565444d00ea0f0dc4456f223b3bbc30cdad9f2288f480e8dd0f3c326da012668ecb51fbc47'
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Forem
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/forem-ruby.gemspec
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require_relative "lib/forem/version"
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = "forem-ruby"
|
|
5
|
+
s.version = Forem::VERSION
|
|
6
|
+
s.required_ruby_version = ">= 3.1.0"
|
|
7
|
+
s.summary = "Ruby client for the Forem API"
|
|
8
|
+
s.description = "A Ruby client library for the Forem API, modeled after stripe-ruby patterns."
|
|
9
|
+
s.authors = ["Forem"]
|
|
10
|
+
s.homepage = "https://github.com/forem/forem-ruby"
|
|
11
|
+
s.license = "MIT"
|
|
12
|
+
s.files = Dir["lib/**/*.rb"] + ["forem-ruby.gemspec", "LICENSE"]
|
|
13
|
+
s.require_paths = ["lib"]
|
|
14
|
+
s.metadata = {
|
|
15
|
+
"rubygems_mfa_required" => "true",
|
|
16
|
+
}
|
|
17
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Forem
|
|
2
|
+
module APIOperations
|
|
3
|
+
# Adds a +create+ class method to any resource that extends this module.
|
|
4
|
+
#
|
|
5
|
+
# Sends a POST request to the resource's collection path with the supplied
|
|
6
|
+
# parameters as a JSON body, and returns a new resource instance populated
|
|
7
|
+
# from the API response.
|
|
8
|
+
#
|
|
9
|
+
# @example Extending a resource class
|
|
10
|
+
# class Forem::Article < Forem::APIResource
|
|
11
|
+
# extend APIOperations::Create
|
|
12
|
+
# end
|
|
13
|
+
module Create
|
|
14
|
+
# Create a new resource via the Forem API.
|
|
15
|
+
#
|
|
16
|
+
# Sends a +POST+ request to {APIResource.resource_path} with +params+
|
|
17
|
+
# serialised as a JSON body. The server response is used to construct
|
|
18
|
+
# and return a new resource instance.
|
|
19
|
+
#
|
|
20
|
+
# @param params [Hash] the attributes for the new resource (e.g.
|
|
21
|
+
# <tt>{ article: { title: "Hello", body_markdown: "..." } }</tt>).
|
|
22
|
+
# @param opts [Hash] per-request options.
|
|
23
|
+
# @option opts [String] :api_key override the API key for this request.
|
|
24
|
+
# @option opts [APIRequestor] :requestor a custom requestor to use.
|
|
25
|
+
# @return [ForemObject] the newly-created resource, constructed from the
|
|
26
|
+
# API response body.
|
|
27
|
+
# @raise [InvalidRequestError] on HTTP 422 (validation errors).
|
|
28
|
+
# @raise [AuthenticationError] on HTTP 401.
|
|
29
|
+
# @raise [AuthorizationError] on HTTP 403.
|
|
30
|
+
# @raise [ForemError] on other API or network errors.
|
|
31
|
+
#
|
|
32
|
+
# @example Creating an article
|
|
33
|
+
# article = client.articles.create(
|
|
34
|
+
# article: { title: "Hello, world!", body_markdown: "**hi**", published: true }
|
|
35
|
+
# )
|
|
36
|
+
# article.id #=> 12345
|
|
37
|
+
# article.title #=> "Hello, world!"
|
|
38
|
+
#
|
|
39
|
+
# @see https://developers.forem.com/api/v1#tag/articles/operation/createArticle
|
|
40
|
+
def create(params = {}, opts = {})
|
|
41
|
+
requestor = opts[:requestor]
|
|
42
|
+
resp = request(:post, resource_path, params, opts)
|
|
43
|
+
construct_from(resp.parsed_body, requestor: requestor)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module Forem
|
|
2
|
+
module APIOperations
|
|
3
|
+
# Adds +delete+ as both a class method and an instance method to any
|
|
4
|
+
# resource that includes this module.
|
|
5
|
+
#
|
|
6
|
+
# The class method deletes by ID; the instance method deletes the receiver
|
|
7
|
+
# using its own {APIResource#resource_url}. Both methods return a resource
|
|
8
|
+
# object if the server responds with a body, otherwise they return +nil+
|
|
9
|
+
# (class method) or +self+ (instance method).
|
|
10
|
+
#
|
|
11
|
+
# The +self.included+ hook extends the including class with {ClassMethods}
|
|
12
|
+
# so the class-level +delete+ is available alongside the instance method.
|
|
13
|
+
#
|
|
14
|
+
# @example Extending a resource class
|
|
15
|
+
# class Forem::Article < Forem::APIResource
|
|
16
|
+
# include APIOperations::Delete
|
|
17
|
+
# end
|
|
18
|
+
module Delete
|
|
19
|
+
# Hook called when {Delete} is included in a class.
|
|
20
|
+
#
|
|
21
|
+
# Extends the including class with {ClassMethods}.
|
|
22
|
+
#
|
|
23
|
+
# @param base [Class] the class including this module.
|
|
24
|
+
# @return [void]
|
|
25
|
+
def self.included(base)
|
|
26
|
+
base.extend(ClassMethods)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Class-level delete helpers.
|
|
30
|
+
module ClassMethods
|
|
31
|
+
# Delete a resource by its ID via the Forem API.
|
|
32
|
+
#
|
|
33
|
+
# Sends a +DELETE+ request to <tt>{resource_path}/{id}</tt>. If the
|
|
34
|
+
# server responds with a non-empty body it is used to construct a
|
|
35
|
+
# resource instance (representing the deleted object); otherwise
|
|
36
|
+
# +nil+ is returned.
|
|
37
|
+
#
|
|
38
|
+
# @param id [Integer, String] the unique identifier of the resource to
|
|
39
|
+
# delete.
|
|
40
|
+
# @param opts [Hash] per-request options.
|
|
41
|
+
# @option opts [String] :api_key override the API key for this request.
|
|
42
|
+
# @option opts [APIRequestor] :requestor a custom requestor to use.
|
|
43
|
+
# @return [ForemObject, nil] the deleted resource (if the API returns
|
|
44
|
+
# a body), or +nil+.
|
|
45
|
+
# @raise [NotFoundError] on HTTP 404.
|
|
46
|
+
# @raise [AuthenticationError] on HTTP 401.
|
|
47
|
+
# @raise [AuthorizationError] on HTTP 403.
|
|
48
|
+
# @raise [ForemError] on other API or network errors.
|
|
49
|
+
#
|
|
50
|
+
# @example Deleting an article by ID
|
|
51
|
+
# client.articles.delete(12345)
|
|
52
|
+
#
|
|
53
|
+
# @see https://developers.forem.com/api/v1
|
|
54
|
+
def delete(id, opts = {})
|
|
55
|
+
requestor = opts[:requestor]
|
|
56
|
+
resp = request(:delete, "#{resource_path}/#{id}", {}, opts)
|
|
57
|
+
resp.parsed_body ? construct_from(resp.parsed_body, requestor: requestor) : nil
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Delete this resource instance via the Forem API.
|
|
62
|
+
#
|
|
63
|
+
# Sends a +DELETE+ request to this instance's {APIResource#resource_url}.
|
|
64
|
+
# If the server responds with a non-empty body it is used to construct
|
|
65
|
+
# a new resource instance (representing the deleted state); otherwise
|
|
66
|
+
# +self+ is returned.
|
|
67
|
+
#
|
|
68
|
+
# @param opts [Hash] per-request options.
|
|
69
|
+
# @option opts [String] :api_key override the API key for this request.
|
|
70
|
+
# @option opts [APIRequestor] :requestor a custom requestor to use.
|
|
71
|
+
# @return [ForemObject] a new resource instance built from the response
|
|
72
|
+
# body, or +self+ if the response has no body.
|
|
73
|
+
# @raise [InvalidRequestError] if the instance has no +id+.
|
|
74
|
+
# @raise [NotFoundError] on HTTP 404.
|
|
75
|
+
# @raise [AuthenticationError] on HTTP 401.
|
|
76
|
+
# @raise [AuthorizationError] on HTTP 403.
|
|
77
|
+
# @raise [ForemError] on other API or network errors.
|
|
78
|
+
#
|
|
79
|
+
# @example Deleting the current instance
|
|
80
|
+
# article.delete
|
|
81
|
+
def delete(opts = {})
|
|
82
|
+
requestor = opts[:requestor] || @requestor
|
|
83
|
+
resp = request(:delete, resource_url, {}, opts)
|
|
84
|
+
resp.parsed_body ? self.class.construct_from(resp.parsed_body, requestor: requestor) : self
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module Forem
|
|
2
|
+
module APIOperations
|
|
3
|
+
# Adds a +list+ class method to any resource that extends this module.
|
|
4
|
+
#
|
|
5
|
+
# Fetches a paginated collection from the resource's path and wraps the
|
|
6
|
+
# result in a {ListObject} that supports manual and automatic pagination.
|
|
7
|
+
#
|
|
8
|
+
# @example Extending a resource class
|
|
9
|
+
# class Forem::Article < Forem::APIResource
|
|
10
|
+
# extend APIOperations::List
|
|
11
|
+
# end
|
|
12
|
+
module List
|
|
13
|
+
# Retrieve a paginated list of resources from the Forem API.
|
|
14
|
+
#
|
|
15
|
+
# Sends a +GET+ request to {APIResource.resource_path} with +params+
|
|
16
|
+
# appended as query-string parameters. The response array is converted
|
|
17
|
+
# into an array of resource instances and wrapped in a {ListObject} that
|
|
18
|
+
# exposes pagination helpers.
|
|
19
|
+
#
|
|
20
|
+
# Pagination defaults: +page+ 1, +per_page+ 30 (matching Forem API
|
|
21
|
+
# defaults). These can be overridden via +params+.
|
|
22
|
+
#
|
|
23
|
+
# @param params [Hash] query parameters for filtering and pagination.
|
|
24
|
+
# @option params [Integer] :page the page number to fetch (default +1+).
|
|
25
|
+
# @option params [Integer] :per_page the number of items per page
|
|
26
|
+
# (default +30+, maximum varies by endpoint).
|
|
27
|
+
# @option params [String] :tag filter articles by tag (articles endpoint).
|
|
28
|
+
# @option params [String] :username filter by username.
|
|
29
|
+
# @option params [String] :state filter by state (e.g. +"fresh"+,
|
|
30
|
+
# +"rising"+, +"all"+).
|
|
31
|
+
# @param opts [Hash] per-request options.
|
|
32
|
+
# @option opts [String] :api_key override the API key for this request.
|
|
33
|
+
# @option opts [APIRequestor] :requestor a custom requestor to use and
|
|
34
|
+
# to forward to subsequent page fetches.
|
|
35
|
+
# @return [ListObject] a paginated list object wrapping the current
|
|
36
|
+
# page's resources.
|
|
37
|
+
# @raise [AuthenticationError] on HTTP 401.
|
|
38
|
+
# @raise [AuthorizationError] on HTTP 403.
|
|
39
|
+
# @raise [ForemError] on other API or network errors.
|
|
40
|
+
#
|
|
41
|
+
# @example Fetching the first page of articles
|
|
42
|
+
# articles = client.articles.list(per_page: 10)
|
|
43
|
+
# articles.map(&:title)
|
|
44
|
+
# #=> ["Article 1", "Article 2", ...]
|
|
45
|
+
#
|
|
46
|
+
# @example Filtering articles by tag
|
|
47
|
+
# client.articles.list(tag: "ruby", per_page: 5).each do |a|
|
|
48
|
+
# puts a.title
|
|
49
|
+
# end
|
|
50
|
+
#
|
|
51
|
+
# @see https://developers.forem.com/api/v1#tag/articles/operation/getArticles
|
|
52
|
+
def list(params = {}, opts = {})
|
|
53
|
+
requestor = opts[:requestor]
|
|
54
|
+
resp = request(:get, resource_path, params, opts)
|
|
55
|
+
data = (resp.parsed_body || []).map { |item| construct_from(item, requestor: requestor) }
|
|
56
|
+
per_page = params[:per_page] || params["per_page"] || 30
|
|
57
|
+
page = params[:page] || params["page"] || 1
|
|
58
|
+
|
|
59
|
+
ListObject.new(
|
|
60
|
+
data: data,
|
|
61
|
+
current_page: page.to_i,
|
|
62
|
+
per_page: per_page.to_i,
|
|
63
|
+
resource_class: self,
|
|
64
|
+
filters: params.reject { |k, _| [:page, :per_page, "page", "per_page"].include?(k) },
|
|
65
|
+
requestor: requestor
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module Forem
|
|
2
|
+
module APIOperations
|
|
3
|
+
# Provides +request+ as both a class method and an instance method on any
|
|
4
|
+
# class that includes this module.
|
|
5
|
+
#
|
|
6
|
+
# Including {Request} is the only prerequisite for a class to issue
|
|
7
|
+
# authenticated HTTP requests. It is included in {APIResource} and
|
|
8
|
+
# therefore inherited by every concrete resource class.
|
|
9
|
+
#
|
|
10
|
+
# The module uses a +self.included+ hook to also extend the including
|
|
11
|
+
# class with {ClassMethods}, making the class-level +request+ available
|
|
12
|
+
# for module methods such as {APIOperations::Create#create} and
|
|
13
|
+
# {APIOperations::List#list}.
|
|
14
|
+
#
|
|
15
|
+
# The +:requestor+ key in +opts+ is consumed here and never forwarded to
|
|
16
|
+
# {APIRequestor#request}.
|
|
17
|
+
module Request
|
|
18
|
+
# Hook called when {Request} is included in a class.
|
|
19
|
+
#
|
|
20
|
+
# @param base [Class] the class including this module.
|
|
21
|
+
# @return [void]
|
|
22
|
+
def self.included(base)
|
|
23
|
+
base.extend(ClassMethods)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Class-level request helpers mixed into every class that includes
|
|
27
|
+
# {Request}.
|
|
28
|
+
module ClassMethods
|
|
29
|
+
# Issue an authenticated HTTP request using the supplied requestor.
|
|
30
|
+
#
|
|
31
|
+
# The +:requestor+ key in +opts+ is required — there is no global
|
|
32
|
+
# default. In normal use this is supplied automatically by a
|
|
33
|
+
# {Forem::Client} via its service objects.
|
|
34
|
+
#
|
|
35
|
+
# @param method [Symbol] the HTTP verb (+:get+, +:post+, +:put+,
|
|
36
|
+
# +:delete+).
|
|
37
|
+
# @param path [String] the API path (e.g. +"/api/articles"+).
|
|
38
|
+
# @param params [Hash] query parameters or JSON body payload.
|
|
39
|
+
# @param opts [Hash] per-request options.
|
|
40
|
+
# @option opts [APIRequestor] :requestor (required) the requestor
|
|
41
|
+
# that issues this call. Normally injected by a {Forem::Client}.
|
|
42
|
+
# @option opts [String] :api_key override the API key for this request.
|
|
43
|
+
# @option opts [String] :api_base override the base URL for this request.
|
|
44
|
+
# @return [ForemResponse] the response wrapper.
|
|
45
|
+
# @raise [ArgumentError] if no +:requestor+ is supplied.
|
|
46
|
+
# @raise [ForemError] on any API or network error.
|
|
47
|
+
def request(method, path, params = {}, opts = {})
|
|
48
|
+
requestor = opts.delete(:requestor)
|
|
49
|
+
unless requestor
|
|
50
|
+
raise ArgumentError,
|
|
51
|
+
"Forem requires an explicit requestor — call this method " \
|
|
52
|
+
"through a Forem::Client (e.g. client.articles.list)."
|
|
53
|
+
end
|
|
54
|
+
requestor.request(method, path, params, opts)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Issue an authenticated HTTP request on behalf of this resource
|
|
59
|
+
# instance.
|
|
60
|
+
#
|
|
61
|
+
# Falls back to the requestor stored on the instance (set when the
|
|
62
|
+
# object was constructed by a service call) if no explicit
|
|
63
|
+
# +:requestor+ is supplied in +opts+. Delegates to the class-level
|
|
64
|
+
# {ClassMethods#request} for the actual transport.
|
|
65
|
+
#
|
|
66
|
+
# @param method [Symbol] the HTTP verb.
|
|
67
|
+
# @param path [String] the API path.
|
|
68
|
+
# @param params [Hash] query parameters or JSON body payload.
|
|
69
|
+
# @param opts [Hash] per-request options.
|
|
70
|
+
# @option opts [APIRequestor] :requestor override the instance's
|
|
71
|
+
# stored requestor for this call.
|
|
72
|
+
# @return [ForemResponse] the response wrapper.
|
|
73
|
+
# @raise [ArgumentError] if neither the instance nor +opts+ supply a
|
|
74
|
+
# requestor.
|
|
75
|
+
# @raise [ForemError] on any API or network error.
|
|
76
|
+
def request(method, path, params = {}, opts = {})
|
|
77
|
+
opts = opts.dup
|
|
78
|
+
opts[:requestor] ||= @requestor if instance_variable_defined?(:@requestor)
|
|
79
|
+
self.class.request(method, path, params, opts)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Forem
|
|
2
|
+
module APIOperations
|
|
3
|
+
# Adds a +retrieve+ class method to any resource that extends this module.
|
|
4
|
+
#
|
|
5
|
+
# Fetches a single resource by its ID with a GET request and returns a
|
|
6
|
+
# resource instance populated from the API response.
|
|
7
|
+
#
|
|
8
|
+
# @example Extending a resource class
|
|
9
|
+
# class Forem::Article < Forem::APIResource
|
|
10
|
+
# extend APIOperations::Retrieve
|
|
11
|
+
# end
|
|
12
|
+
module Retrieve
|
|
13
|
+
# Retrieve a single resource by its ID from the Forem API.
|
|
14
|
+
#
|
|
15
|
+
# Sends a +GET+ request to <tt>{resource_path}/{id}</tt> and constructs
|
|
16
|
+
# a resource instance from the response body.
|
|
17
|
+
#
|
|
18
|
+
# @param id [Integer, String] the unique identifier of the resource to
|
|
19
|
+
# fetch.
|
|
20
|
+
# @param opts [Hash] per-request options.
|
|
21
|
+
# @option opts [String] :api_key override the API key for this request.
|
|
22
|
+
# @option opts [APIRequestor] :requestor a custom requestor to use.
|
|
23
|
+
# @return [ForemObject] the resource instance populated with data from
|
|
24
|
+
# the API response.
|
|
25
|
+
# @raise [NotFoundError] on HTTP 404 (resource does not exist).
|
|
26
|
+
# @raise [AuthenticationError] on HTTP 401.
|
|
27
|
+
# @raise [AuthorizationError] on HTTP 403.
|
|
28
|
+
# @raise [ForemError] on other API or network errors.
|
|
29
|
+
#
|
|
30
|
+
# @example Fetching an article by ID
|
|
31
|
+
# article = client.articles.retrieve(12345)
|
|
32
|
+
# article.title #=> "Hello, world!"
|
|
33
|
+
# article.id #=> 12345
|
|
34
|
+
#
|
|
35
|
+
# @see https://developers.forem.com/api/v1#tag/articles/operation/getArticleById
|
|
36
|
+
def retrieve(id, opts = {})
|
|
37
|
+
requestor = opts[:requestor]
|
|
38
|
+
resp = request(:get, "#{resource_path}/#{id}", {}, opts)
|
|
39
|
+
construct_from(resp.parsed_body, requestor: requestor)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Forem
|
|
2
|
+
module APIOperations
|
|
3
|
+
# Adds a +save+ instance method to any resource that includes this module.
|
|
4
|
+
#
|
|
5
|
+
# +save+ is the instance-level complement to the class-level
|
|
6
|
+
# {APIOperations::Update#update}. It persists the current resource to the
|
|
7
|
+
# API by issuing a PUT request to the instance's own
|
|
8
|
+
# {APIResource#resource_url}, and returns a new resource object reflecting
|
|
9
|
+
# the server's response.
|
|
10
|
+
#
|
|
11
|
+
# @example Including in a resource class
|
|
12
|
+
# class Forem::Article < Forem::APIResource
|
|
13
|
+
# include APIOperations::Save
|
|
14
|
+
# end
|
|
15
|
+
module Save
|
|
16
|
+
# Save (update) this resource instance via the Forem API.
|
|
17
|
+
#
|
|
18
|
+
# Sends a +PUT+ request to {APIResource#resource_url} with +params+
|
|
19
|
+
# serialised as a JSON body. The server response is used to construct
|
|
20
|
+
# and return a new resource instance representing the updated state.
|
|
21
|
+
#
|
|
22
|
+
# Unlike {APIResource#refresh}, which reloads in-place, +save+ returns
|
|
23
|
+
# a *new* instance and does not mutate the receiver.
|
|
24
|
+
#
|
|
25
|
+
# @param params [Hash] the attributes to update (e.g.
|
|
26
|
+
# <tt>{ article: { title: "New title" } }</tt>). Defaults to +{}+.
|
|
27
|
+
# @param opts [Hash] per-request keyword options.
|
|
28
|
+
# @option opts [String] :api_key override the API key for this request.
|
|
29
|
+
# @option opts [APIRequestor] :requestor a custom requestor to use.
|
|
30
|
+
# @return [ForemObject] a new resource instance populated with the
|
|
31
|
+
# updated data returned by the API.
|
|
32
|
+
# @raise [InvalidRequestError] if this instance has no +id+ (via
|
|
33
|
+
# {APIResource#resource_url}).
|
|
34
|
+
# @raise [NotFoundError] on HTTP 404.
|
|
35
|
+
# @raise [InvalidRequestError] on HTTP 422 (validation errors).
|
|
36
|
+
# @raise [AuthenticationError] on HTTP 401.
|
|
37
|
+
# @raise [AuthorizationError] on HTTP 403.
|
|
38
|
+
# @raise [ForemError] on other API or network errors.
|
|
39
|
+
#
|
|
40
|
+
# @example Updating a fetched article
|
|
41
|
+
# article = client.articles.retrieve(12345)
|
|
42
|
+
# updated = article.save(article: { title: "Brand new title" })
|
|
43
|
+
# updated.title #=> "Brand new title"
|
|
44
|
+
#
|
|
45
|
+
# @see https://developers.forem.com/api/v1#tag/articles/operation/updateArticle
|
|
46
|
+
def save(params = {}, **opts)
|
|
47
|
+
requestor = opts[:requestor] || @requestor
|
|
48
|
+
resp = request(:put, resource_url, params, opts)
|
|
49
|
+
self.class.construct_from(resp.parsed_body, requestor: requestor)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Forem
|
|
2
|
+
module APIOperations
|
|
3
|
+
# Adds an +update+ class method to any resource that extends this module.
|
|
4
|
+
#
|
|
5
|
+
# Sends a PUT request to a specific resource URL with the supplied
|
|
6
|
+
# parameters as a JSON body, and returns a new resource instance
|
|
7
|
+
# constructed from the API response.
|
|
8
|
+
#
|
|
9
|
+
# @example Extending a resource class
|
|
10
|
+
# class Forem::Article < Forem::APIResource
|
|
11
|
+
# extend APIOperations::Update
|
|
12
|
+
# end
|
|
13
|
+
module Update
|
|
14
|
+
# Update an existing resource via the Forem API.
|
|
15
|
+
#
|
|
16
|
+
# Sends a +PUT+ request to <tt>{resource_path}/{id}</tt> with +params+
|
|
17
|
+
# serialised as a JSON body, and returns a new resource instance
|
|
18
|
+
# reflecting the updated state as returned by the server.
|
|
19
|
+
#
|
|
20
|
+
# @param id [Integer, String] the unique identifier of the resource to
|
|
21
|
+
# update.
|
|
22
|
+
# @param params [Hash] the attributes to update (e.g.
|
|
23
|
+
# <tt>{ article: { title: "New title" } }</tt>).
|
|
24
|
+
# @param opts [Hash] per-request options.
|
|
25
|
+
# @option opts [String] :api_key override the API key for this request.
|
|
26
|
+
# @option opts [APIRequestor] :requestor a custom requestor to use.
|
|
27
|
+
# @return [ForemObject] a resource instance populated with the updated
|
|
28
|
+
# data returned by the API.
|
|
29
|
+
# @raise [NotFoundError] on HTTP 404 (resource does not exist).
|
|
30
|
+
# @raise [InvalidRequestError] on HTTP 422 (validation errors).
|
|
31
|
+
# @raise [AuthenticationError] on HTTP 401.
|
|
32
|
+
# @raise [AuthorizationError] on HTTP 403.
|
|
33
|
+
# @raise [ForemError] on other API or network errors.
|
|
34
|
+
#
|
|
35
|
+
# @example Updating an article's title
|
|
36
|
+
# article = client.articles.update(12345, article: { title: "Updated title" })
|
|
37
|
+
# article.title #=> "Updated title"
|
|
38
|
+
#
|
|
39
|
+
# @see https://developers.forem.com/api/v1#tag/articles/operation/updateArticle
|
|
40
|
+
def update(id, params = {}, opts = {})
|
|
41
|
+
requestor = opts[:requestor]
|
|
42
|
+
resp = request(:put, "#{resource_path}/#{id}", params, opts)
|
|
43
|
+
construct_from(resp.parsed_body, requestor: requestor)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|