x-resources 1.0.0
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 +9 -0
- data/CHANGELOG.md +254 -0
- data/LICENSE.txt +21 -0
- data/README.md +148 -0
- data/lib/x/resources/abstract_class.rb +29 -0
- data/lib/x/resources/actions/direct_messages.rb +99 -0
- data/lib/x/resources/actions/engagement.rb +95 -0
- data/lib/x/resources/actions/lists.rb +126 -0
- data/lib/x/resources/actions/posts.rb +77 -0
- data/lib/x/resources/actions/relationships.rb +91 -0
- data/lib/x/resources/actions.rb +22 -0
- data/lib/x/resources/api.rb +40 -0
- data/lib/x/resources/attributes.rb +203 -0
- data/lib/x/resources/batch.rb +43 -0
- data/lib/x/resources/batch_finders.rb +185 -0
- data/lib/x/resources/bookmark_folder.rb +23 -0
- data/lib/x/resources/community.rb +137 -0
- data/lib/x/resources/cursor.rb +493 -0
- data/lib/x/resources/direct_message.rb +325 -0
- data/lib/x/resources/direct_message_conversations.rb +147 -0
- data/lib/x/resources/errors.rb +104 -0
- data/lib/x/resources/finders.rb +255 -0
- data/lib/x/resources/identity.rb +65 -0
- data/lib/x/resources/includes.rb +216 -0
- data/lib/x/resources/list.rb +336 -0
- data/lib/x/resources/lookups/communities.rb +56 -0
- data/lib/x/resources/lookups/direct_messages.rb +86 -0
- data/lib/x/resources/lookups/lists.rb +44 -0
- data/lib/x/resources/lookups/media.rb +72 -0
- data/lib/x/resources/lookups/posts.rb +198 -0
- data/lib/x/resources/lookups/spaces.rb +87 -0
- data/lib/x/resources/lookups/trends.rb +38 -0
- data/lib/x/resources/lookups/users.rb +221 -0
- data/lib/x/resources/lookups.rb +25 -0
- data/lib/x/resources/marshalling.rb +93 -0
- data/lib/x/resources/matching_rule.rb +107 -0
- data/lib/x/resources/media.rb +278 -0
- data/lib/x/resources/media_ids.rb +74 -0
- data/lib/x/resources/memo.rb +54 -0
- data/lib/x/resources/page.rb +394 -0
- data/lib/x/resources/page_limit.rb +80 -0
- data/lib/x/resources/pages.rb +270 -0
- data/lib/x/resources/parallel.rb +82 -0
- data/lib/x/resources/personalized_trend.rb +124 -0
- data/lib/x/resources/place.rb +107 -0
- data/lib/x/resources/poll.rb +75 -0
- data/lib/x/resources/post.rb +615 -0
- data/lib/x/resources/post_collections.rb +67 -0
- data/lib/x/resources/post_counts.rb +215 -0
- data/lib/x/resources/post_search.rb +86 -0
- data/lib/x/resources/post_usage.rb +203 -0
- data/lib/x/resources/post_writes.rb +140 -0
- data/lib/x/resources/published_count.rb +31 -0
- data/lib/x/resources/references.rb +121 -0
- data/lib/x/resources/relation_writes.rb +54 -0
- data/lib/x/resources/relationships.rb +77 -0
- data/lib/x/resources/resource.rb +535 -0
- data/lib/x/resources/serialization.rb +58 -0
- data/lib/x/resources/shape.rb +167 -0
- data/lib/x/resources/space.rb +332 -0
- data/lib/x/resources/topic.rb +59 -0
- data/lib/x/resources/trend.rb +130 -0
- data/lib/x/resources/user.rb +502 -0
- data/lib/x/resources/user_collections.rb +213 -0
- data/lib/x/resources/user_finders.rb +282 -0
- data/lib/x/resources/utils.rb +358 -0
- data/lib/x/resources/value_equality.rb +38 -0
- data/lib/x/resources/value_marshalling.rb +89 -0
- data/lib/x/resources/version.rb +25 -0
- data/lib/x/resources.rb +22 -0
- data/sig/manifest.yaml +7 -0
- data/sig/x-resources.rbs +813 -0
- metadata +140 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../post"
|
|
4
|
+
require_relative "../relation_writes"
|
|
5
|
+
require_relative "../utils"
|
|
6
|
+
|
|
7
|
+
module X
|
|
8
|
+
module Resources
|
|
9
|
+
module Actions
|
|
10
|
+
# Like, repost, and bookmark posts as the authenticated user
|
|
11
|
+
#
|
|
12
|
+
# Internal to x-resources: X::Resources::API includes it, and its methods are public API of the client that
|
|
13
|
+
# includes API, but the module is only how they are grouped, and some of them need the methods of another,
|
|
14
|
+
# so include API rather than this module alone.
|
|
15
|
+
#
|
|
16
|
+
# @api semipublic
|
|
17
|
+
module Engagement
|
|
18
|
+
# Like a post as the authenticated user
|
|
19
|
+
#
|
|
20
|
+
# @api public
|
|
21
|
+
# @param post [Post, String, Integer] the post or its identifier
|
|
22
|
+
# @return [Boolean] true if the authenticated user now likes the post
|
|
23
|
+
# @example Like a post
|
|
24
|
+
# client.like("1234567890")
|
|
25
|
+
def like(post)
|
|
26
|
+
RelationWrites.relate(self, current_user_id, "likes", {"tweet_id" => Utils.id_of(post, Post)}, "liked")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Unlike a post as the authenticated user
|
|
30
|
+
#
|
|
31
|
+
# @api public
|
|
32
|
+
# @param post [Post, String, Integer] the post or its identifier
|
|
33
|
+
# @return [Boolean] true if the authenticated user no longer likes the post
|
|
34
|
+
# @example Unlike a post
|
|
35
|
+
# client.unlike("1234567890")
|
|
36
|
+
def unlike(post)
|
|
37
|
+
RelationWrites.unrelate(self, current_user_id, "likes", Utils.id_of(post, Post), "liked")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Repost a post as the authenticated user
|
|
41
|
+
#
|
|
42
|
+
# @api public
|
|
43
|
+
# @param post [Post, String, Integer] the post or its identifier
|
|
44
|
+
# @return [Boolean] true if the authenticated user has reposted the post
|
|
45
|
+
# @example Repost a post
|
|
46
|
+
# client.repost("1234567890")
|
|
47
|
+
def repost(post)
|
|
48
|
+
RelationWrites.relate(self, current_user_id, "retweets", {"tweet_id" => Utils.id_of(post, Post)}, "retweeted")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Undo a repost as the authenticated user
|
|
52
|
+
#
|
|
53
|
+
# @api public
|
|
54
|
+
# @param post [Post, String, Integer] the post or its identifier
|
|
55
|
+
# @return [Boolean] true if the authenticated user no longer reposts the post
|
|
56
|
+
# @example Undo a repost
|
|
57
|
+
# client.unrepost("1234567890")
|
|
58
|
+
def unrepost(post)
|
|
59
|
+
RelationWrites.unrelate(self, current_user_id, "retweets", Utils.id_of(post, Post), "retweeted")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Bookmark a post as the authenticated user
|
|
63
|
+
#
|
|
64
|
+
# Bookmarking a post takes only OAuth 2.0 user context, which the object layer cannot route around, so a
|
|
65
|
+
# client that signs with OAuth 1.0a is refused.
|
|
66
|
+
#
|
|
67
|
+
# @api public
|
|
68
|
+
# @param post [Post, String, Integer] the post or its identifier
|
|
69
|
+
# @return [Boolean] true if the authenticated user has bookmarked the post
|
|
70
|
+
# @example Bookmark a post
|
|
71
|
+
# client.bookmark("1234567890")
|
|
72
|
+
def bookmark(post)
|
|
73
|
+
RelationWrites.relate(self, current_user_id, "bookmarks", {"tweet_id" => Utils.id_of(post, Post)}, "bookmarked")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Remove a bookmark as the authenticated user
|
|
77
|
+
#
|
|
78
|
+
# Removing a bookmark takes only OAuth 2.0 user context, which the object layer cannot route around, so a
|
|
79
|
+
# client that signs with OAuth 1.0a is refused.
|
|
80
|
+
#
|
|
81
|
+
# @api public
|
|
82
|
+
# @param post [Post, String, Integer] the post or its identifier
|
|
83
|
+
# @return [Boolean] true if the authenticated user no longer has the post bookmarked
|
|
84
|
+
# @example Remove a bookmark
|
|
85
|
+
# client.unbookmark("1234567890")
|
|
86
|
+
def unbookmark(post)
|
|
87
|
+
RelationWrites.unrelate(self, current_user_id, "bookmarks", Utils.id_of(post, Post), "bookmarked")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
alias_method :retweet, :repost
|
|
91
|
+
alias_method :unretweet, :unrepost
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../list"
|
|
4
|
+
require_relative "../relation_writes"
|
|
5
|
+
require_relative "../user"
|
|
6
|
+
require_relative "../utils"
|
|
7
|
+
|
|
8
|
+
module X
|
|
9
|
+
module Resources
|
|
10
|
+
module Actions
|
|
11
|
+
# Create, update, delete, follow, and pin lists as the authenticated user
|
|
12
|
+
#
|
|
13
|
+
# Internal to x-resources: X::Resources::API includes it, and its methods are public API of the client that
|
|
14
|
+
# includes API, but the module is only how they are grouped, and some of them need the methods of another,
|
|
15
|
+
# so include API rather than this module alone.
|
|
16
|
+
#
|
|
17
|
+
# @api semipublic
|
|
18
|
+
module Lists
|
|
19
|
+
# Create a list owned by the authenticated user
|
|
20
|
+
#
|
|
21
|
+
# @api public
|
|
22
|
+
# @param name [String] the name of the list
|
|
23
|
+
# @param params [Hash] additional request body fields: description and private
|
|
24
|
+
# @return [List] the created list, holding only its identifier and name
|
|
25
|
+
# @raise [MissingResource] if the API answers without the list
|
|
26
|
+
# @example Create a private list
|
|
27
|
+
# client.create_list("Rubyists", private: true)
|
|
28
|
+
def create_list(name, **params)
|
|
29
|
+
List.create(name, client: self, **params)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Update the name, description, or privacy of a list as the authenticated user
|
|
33
|
+
#
|
|
34
|
+
# @api public
|
|
35
|
+
# @param list [List, String, Integer] the list or its identifier
|
|
36
|
+
# @param params [Hash] the request body fields to change: name, description, and private
|
|
37
|
+
# @return [Boolean] true if the list was updated
|
|
38
|
+
# @raise [ArgumentError] if no field is given to change, before any request
|
|
39
|
+
# @example Make a list private
|
|
40
|
+
# client.update_list("1234567890", private: true)
|
|
41
|
+
def update_list(list, **params)
|
|
42
|
+
List.update(list, client: self, **params)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Delete a list as the authenticated user
|
|
46
|
+
#
|
|
47
|
+
# @api public
|
|
48
|
+
# @param list [List, String, Integer] the list or its identifier
|
|
49
|
+
# @return [Boolean] true if the list was deleted
|
|
50
|
+
# @example Delete a list
|
|
51
|
+
# client.delete_list("1234567890")
|
|
52
|
+
def delete_list(list)
|
|
53
|
+
List.delete(list, client: self)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Add a member to a list as the authenticated user
|
|
57
|
+
#
|
|
58
|
+
# @api public
|
|
59
|
+
# @param list [List, String, Integer] the list or its identifier
|
|
60
|
+
# @param user [User, String, Integer] the user or their identifier
|
|
61
|
+
# @return [Boolean] true if the user is now a member
|
|
62
|
+
# @example Add a member to a list
|
|
63
|
+
# client.add_list_member("1234567890", user)
|
|
64
|
+
def add_list_member(list, user)
|
|
65
|
+
List.from_id(list, client: self).add_member(user)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Remove a member from a list as the authenticated user
|
|
69
|
+
#
|
|
70
|
+
# @api public
|
|
71
|
+
# @param list [List, String, Integer] the list or its identifier
|
|
72
|
+
# @param user [User, String, Integer] the user or their identifier
|
|
73
|
+
# @return [Boolean] true if the user is no longer a member
|
|
74
|
+
# @example Remove a member from a list
|
|
75
|
+
# client.remove_list_member("1234567890", user)
|
|
76
|
+
def remove_list_member(list, user)
|
|
77
|
+
List.from_id(list, client: self).remove_member(user)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Follow a list as the authenticated user
|
|
81
|
+
#
|
|
82
|
+
# @api public
|
|
83
|
+
# @param list [List, String, Integer] the list or its identifier
|
|
84
|
+
# @return [Boolean] true if the authenticated user now follows the list
|
|
85
|
+
# @example Follow a list
|
|
86
|
+
# client.follow_list("1234567890")
|
|
87
|
+
def follow_list(list)
|
|
88
|
+
RelationWrites.relate(self, current_user_id, "followed_lists", {"list_id" => Utils.id_of(list, List)}, "following")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Unfollow a list as the authenticated user
|
|
92
|
+
#
|
|
93
|
+
# @api public
|
|
94
|
+
# @param list [List, String, Integer] the list or its identifier
|
|
95
|
+
# @return [Boolean] true if the authenticated user no longer follows the list
|
|
96
|
+
# @example Unfollow a list
|
|
97
|
+
# client.unfollow_list("1234567890")
|
|
98
|
+
def unfollow_list(list)
|
|
99
|
+
RelationWrites.unrelate(self, current_user_id, "followed_lists", Utils.id_of(list, List), "following")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Pin a list as the authenticated user
|
|
103
|
+
#
|
|
104
|
+
# @api public
|
|
105
|
+
# @param list [List, String, Integer] the list or its identifier
|
|
106
|
+
# @return [Boolean] true if the authenticated user has pinned the list
|
|
107
|
+
# @example Pin a list
|
|
108
|
+
# client.pin_list("1234567890")
|
|
109
|
+
def pin_list(list)
|
|
110
|
+
RelationWrites.relate(self, current_user_id, "pinned_lists", {"list_id" => Utils.id_of(list, List)}, "pinned")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Unpin a list as the authenticated user
|
|
114
|
+
#
|
|
115
|
+
# @api public
|
|
116
|
+
# @param list [List, String, Integer] the list or its identifier
|
|
117
|
+
# @return [Boolean] true if the authenticated user no longer has the list pinned
|
|
118
|
+
# @example Unpin a list
|
|
119
|
+
# client.unpin_list("1234567890")
|
|
120
|
+
def unpin_list(list)
|
|
121
|
+
RelationWrites.unrelate(self, current_user_id, "pinned_lists", Utils.id_of(list, List), "pinned")
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../post"
|
|
4
|
+
|
|
5
|
+
module X
|
|
6
|
+
module Resources
|
|
7
|
+
module Actions
|
|
8
|
+
# Create and delete posts as the authenticated user
|
|
9
|
+
#
|
|
10
|
+
# Internal to x-resources: X::Resources::API includes it, and its methods are public API of the client that
|
|
11
|
+
# includes API, but the module is only how they are grouped, and some of them need the methods of another,
|
|
12
|
+
# so include API rather than this module alone.
|
|
13
|
+
#
|
|
14
|
+
# @api semipublic
|
|
15
|
+
module Posts
|
|
16
|
+
# Create a post as the authenticated user
|
|
17
|
+
#
|
|
18
|
+
# The API bills each post created, and bills a post whose text holds a URL more than ten times as much.
|
|
19
|
+
# A post needs no text when it has something else to show, such as media.
|
|
20
|
+
#
|
|
21
|
+
# @api public
|
|
22
|
+
# @param text [String, nil] the text of the post, or nil for a post without text, such as one of media alone
|
|
23
|
+
# @param params [Hash] additional request body fields, such as reply_to, quote, media_ids, or poll
|
|
24
|
+
# @return [Post] the created post, holding only its identifier and text
|
|
25
|
+
# @raise [ArgumentError] if the post has neither text nor any other field
|
|
26
|
+
# @raise [MissingResource] if the API answers without the post
|
|
27
|
+
# @example Create a post
|
|
28
|
+
# client.create_post("Hello, World!")
|
|
29
|
+
# @example Post an image without text
|
|
30
|
+
# client.create_post(media_ids: [media])
|
|
31
|
+
# @example Reply to a post with an image
|
|
32
|
+
# client.create_post("Hello!", reply_to: post, media_ids: [media["id"]])
|
|
33
|
+
# @example Quote a post
|
|
34
|
+
# client.create_post("Worth reading", quote: post)
|
|
35
|
+
def create_post(text = nil, **params) # steep:ignore DifferentMethodParameterKind
|
|
36
|
+
Post.create(text, client: self, **params)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Delete a post as the authenticated user
|
|
40
|
+
#
|
|
41
|
+
# @api public
|
|
42
|
+
# @param post [Post, String, Integer] the post or its identifier
|
|
43
|
+
# @return [Boolean] true if the post was deleted
|
|
44
|
+
# @example Delete a post
|
|
45
|
+
# client.delete_post("1234567890")
|
|
46
|
+
def delete_post(post)
|
|
47
|
+
Post.delete(post, client: self)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Hide a reply to a post of the authenticated user
|
|
51
|
+
#
|
|
52
|
+
# @api public
|
|
53
|
+
# @param post [Post, String, Integer] the reply or its identifier
|
|
54
|
+
# @return [Boolean] true if the reply is now hidden
|
|
55
|
+
# @example Hide a reply
|
|
56
|
+
# client.hide_reply("1234567890")
|
|
57
|
+
def hide_reply(post)
|
|
58
|
+
Post.hide_reply(post, client: self)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Show a reply to a post of the authenticated user after hiding it
|
|
62
|
+
#
|
|
63
|
+
# @api public
|
|
64
|
+
# @param post [Post, String, Integer] the reply or its identifier
|
|
65
|
+
# @return [Boolean] true if the reply is no longer hidden
|
|
66
|
+
# @example Show a hidden reply
|
|
67
|
+
# client.unhide_reply("1234567890")
|
|
68
|
+
def unhide_reply(post)
|
|
69
|
+
Post.unhide_reply(post, client: self)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
alias_method :create_tweet, :create_post
|
|
73
|
+
alias_method :delete_tweet, :delete_post
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../relation_writes"
|
|
4
|
+
require_relative "../user"
|
|
5
|
+
require_relative "../utils"
|
|
6
|
+
|
|
7
|
+
module X
|
|
8
|
+
module Resources
|
|
9
|
+
module Actions
|
|
10
|
+
# Follow, block, and mute users as the authenticated user
|
|
11
|
+
#
|
|
12
|
+
# Internal to x-resources: X::Resources::API includes it, and its methods are public API of the client that
|
|
13
|
+
# includes API, but the module is only how they are grouped, and some of them need the methods of another,
|
|
14
|
+
# so include API rather than this module alone.
|
|
15
|
+
#
|
|
16
|
+
# @api semipublic
|
|
17
|
+
module Relationships
|
|
18
|
+
# Follow a user as the authenticated user
|
|
19
|
+
#
|
|
20
|
+
# A protected user must accept a request to follow them first, so for a protected user true means the follow
|
|
21
|
+
# was requested, not that the authenticated user follows them: until they accept it, the follows? of the
|
|
22
|
+
# authenticated user, as in client.current_user!.follows?(user), answers false.
|
|
23
|
+
#
|
|
24
|
+
# @api public
|
|
25
|
+
# @param user [User, String, Integer] the user or their identifier
|
|
26
|
+
# @return [Boolean] true if the authenticated user now follows the user, or, for a protected user, has requested
|
|
27
|
+
# to follow them
|
|
28
|
+
# @example Follow a user
|
|
29
|
+
# client.follow("7505382")
|
|
30
|
+
def follow(user)
|
|
31
|
+
RelationWrites.relate(self, current_user_id, "following", {"target_user_id" => Utils.id_of(user, User)}, "following", "pending_follow")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Unfollow a user as the authenticated user
|
|
35
|
+
#
|
|
36
|
+
# @api public
|
|
37
|
+
# @param user [User, String, Integer] the user or their identifier
|
|
38
|
+
# @return [Boolean] true if the authenticated user no longer follows the user
|
|
39
|
+
# @example Unfollow a user
|
|
40
|
+
# client.unfollow("7505382")
|
|
41
|
+
def unfollow(user)
|
|
42
|
+
RelationWrites.unrelate(self, current_user_id, "following", Utils.id_of(user, User), "following")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Block a user as the authenticated user
|
|
46
|
+
#
|
|
47
|
+
# @api public
|
|
48
|
+
# @param user [User, String, Integer] the user or their identifier
|
|
49
|
+
# @return [Boolean] true if the authenticated user now blocks the user
|
|
50
|
+
# @example Block a user
|
|
51
|
+
# client.block("7505382")
|
|
52
|
+
def block(user)
|
|
53
|
+
RelationWrites.relate(self, current_user_id, "blocking", {"target_user_id" => Utils.id_of(user, User)}, "blocking")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Unblock a user as the authenticated user
|
|
57
|
+
#
|
|
58
|
+
# @api public
|
|
59
|
+
# @param user [User, String, Integer] the user or their identifier
|
|
60
|
+
# @return [Boolean] true if the authenticated user no longer blocks the user
|
|
61
|
+
# @example Unblock a user
|
|
62
|
+
# client.unblock("7505382")
|
|
63
|
+
def unblock(user)
|
|
64
|
+
RelationWrites.unrelate(self, current_user_id, "blocking", Utils.id_of(user, User), "blocking")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Mute a user as the authenticated user
|
|
68
|
+
#
|
|
69
|
+
# @api public
|
|
70
|
+
# @param user [User, String, Integer] the user or their identifier
|
|
71
|
+
# @return [Boolean] true if the authenticated user now mutes the user
|
|
72
|
+
# @example Mute a user
|
|
73
|
+
# client.mute("7505382")
|
|
74
|
+
def mute(user)
|
|
75
|
+
RelationWrites.relate(self, current_user_id, "muting", {"target_user_id" => Utils.id_of(user, User)}, "muting")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Unmute a user as the authenticated user
|
|
79
|
+
#
|
|
80
|
+
# @api public
|
|
81
|
+
# @param user [User, String, Integer] the user or their identifier
|
|
82
|
+
# @return [Boolean] true if the authenticated user no longer mutes the user
|
|
83
|
+
# @example Unmute a user
|
|
84
|
+
# client.unmute("7505382")
|
|
85
|
+
def unmute(user)
|
|
86
|
+
RelationWrites.unrelate(self, current_user_id, "muting", Utils.id_of(user, User), "muting")
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "actions/direct_messages"
|
|
4
|
+
require_relative "actions/engagement"
|
|
5
|
+
require_relative "actions/lists"
|
|
6
|
+
require_relative "actions/posts"
|
|
7
|
+
require_relative "actions/relationships"
|
|
8
|
+
|
|
9
|
+
module X
|
|
10
|
+
module Resources
|
|
11
|
+
# Actions taken as the authenticated user, the modules of which X::Resources::API includes
|
|
12
|
+
#
|
|
13
|
+
# Internal to x-resources: a namespace of the modules API includes into a client, and not itself included, so that
|
|
14
|
+
# the modules it holds are not constants of the client, where the name of one, such as Media, would shadow a
|
|
15
|
+
# constant of the same name in a class that inherits from the client. Include API rather than any of them.
|
|
16
|
+
#
|
|
17
|
+
# @api private
|
|
18
|
+
module Actions
|
|
19
|
+
end
|
|
20
|
+
private_constant :Actions
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "actions"
|
|
4
|
+
require_relative "lookups"
|
|
5
|
+
|
|
6
|
+
module X
|
|
7
|
+
module Resources
|
|
8
|
+
# The resource methods mixed into a client that responds to get, post, put, and delete
|
|
9
|
+
#
|
|
10
|
+
# The x gem includes it into X::Client. With x-core and x-resources alone, or with a client of your own,
|
|
11
|
+
# include it yourself: X::Client.include(X::Resources::API).
|
|
12
|
+
#
|
|
13
|
+
# It is the one module of the object layer to include. The modules it includes, those of Lookups and Actions,
|
|
14
|
+
# are internal: their methods are public API of the client that includes API, but how they are grouped can change
|
|
15
|
+
# within 1.x, and some need the methods of another, such as current_user_id. So are the modules the resource
|
|
16
|
+
# classes extend and include, such as Finders and Relationships: the methods they give a resource class are public
|
|
17
|
+
# API, the modules are not.
|
|
18
|
+
#
|
|
19
|
+
# Neither API nor a module it includes holds a constant, as a constant of a module is a constant of each class
|
|
20
|
+
# that includes it: a class that inherits from the client and names Media, or Users, reads the constant the name
|
|
21
|
+
# reads anywhere else, not a module of the object layer.
|
|
22
|
+
#
|
|
23
|
+
# @api public
|
|
24
|
+
module API
|
|
25
|
+
include Lookups::Users
|
|
26
|
+
include Lookups::Posts
|
|
27
|
+
include Lookups::Lists
|
|
28
|
+
include Lookups::Media
|
|
29
|
+
include Lookups::Spaces
|
|
30
|
+
include Lookups::Communities
|
|
31
|
+
include Lookups::DirectMessages
|
|
32
|
+
include Lookups::Trends
|
|
33
|
+
include Actions::Posts
|
|
34
|
+
include Actions::Lists
|
|
35
|
+
include Actions::DirectMessages
|
|
36
|
+
include Actions::Relationships
|
|
37
|
+
include Actions::Engagement
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "shape"
|
|
4
|
+
require_relative "utils"
|
|
5
|
+
|
|
6
|
+
module X
|
|
7
|
+
module Resources
|
|
8
|
+
# Class-level macros for declaring resource attributes and references
|
|
9
|
+
# @api private
|
|
10
|
+
module Attributes
|
|
11
|
+
# The value of a list the response omitted, which reads as empty, as a list of references does, rather than nil
|
|
12
|
+
EMPTY_LIST = [] #: Array[untyped]
|
|
13
|
+
EMPTY_LIST.freeze
|
|
14
|
+
private_constant :EMPTY_LIST
|
|
15
|
+
# The values a flag is read from, which the API gives as true or false, and a response that omits it as nil
|
|
16
|
+
FLAGS = [true, false, nil].freeze
|
|
17
|
+
private_constant :FLAGS
|
|
18
|
+
# Converters keyed by attribute type
|
|
19
|
+
CONVERTERS = {
|
|
20
|
+
raw: ->(value) { value },
|
|
21
|
+
media_key: ->(value) { value },
|
|
22
|
+
conversation_id: ->(value) { Shape.conversation_id(value) },
|
|
23
|
+
boolean: ->(value) { FLAGS.include?(value) ? value : raise(ArgumentError, "#{value.inspect} is not true or false") },
|
|
24
|
+
time: ->(value) { Utils.time(value) },
|
|
25
|
+
integer: ->(value) { Shape.integer(value) },
|
|
26
|
+
integers: ->(value) { (Shape.list(value) || EMPTY_LIST).map { |id| Shape.integer(id) }.freeze },
|
|
27
|
+
list: ->(value) { Shape.list(value) || EMPTY_LIST },
|
|
28
|
+
object: ->(value) { Shape.object(value) },
|
|
29
|
+
objects: ->(value) { Shape.object_list(value) },
|
|
30
|
+
range: ->(value) { Shape.range(value) },
|
|
31
|
+
requested_list: ->(value) { Shape.list(value) }
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
# The names of the attributes declared on this class, which pattern matching reads
|
|
35
|
+
#
|
|
36
|
+
# @api private
|
|
37
|
+
# @return [Array<Symbol>] the attribute names
|
|
38
|
+
# @example Get the attributes of a user
|
|
39
|
+
# X::User.__send__(:attribute_names)
|
|
40
|
+
def attribute_names
|
|
41
|
+
parent = superclass
|
|
42
|
+
@attribute_names ||= parent.respond_to?(:attribute_names, true) ? parent.__send__(:attribute_names).dup : [:id]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# The other names some attributes are read by, which a pattern can ask for
|
|
46
|
+
#
|
|
47
|
+
# @api private
|
|
48
|
+
# @return [Array<Symbol>] the alias names
|
|
49
|
+
# @example Get the attribute aliases of a user
|
|
50
|
+
# X::User.__send__(:attribute_aliases) # => [:tweet_count, :pinned_tweet_id, :most_recent_tweet_id]
|
|
51
|
+
def attribute_aliases
|
|
52
|
+
parent = superclass
|
|
53
|
+
@attribute_aliases ||= parent.respond_to?(:attribute_aliases, true) ? parent.__send__(:attribute_aliases).dup : []
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# The key paths of the identifiers of the resources this class refers to
|
|
57
|
+
#
|
|
58
|
+
# The problems of a resource are read by them. A key path holds an identifier, a list of them, or a list of objects that each hold one as id, as the
|
|
59
|
+
# referenced_posts of a post do.
|
|
60
|
+
#
|
|
61
|
+
# @api private
|
|
62
|
+
# @return [Array<Array<String>>] the key paths
|
|
63
|
+
# @example Get the key paths of the references of a list
|
|
64
|
+
# X::List.__send__(:reference_keys) # => [["owner_id"]]
|
|
65
|
+
def reference_keys
|
|
66
|
+
parent = superclass
|
|
67
|
+
@reference_keys ||= parent.respond_to?(:reference_keys, true) ? parent.__send__(:reference_keys).dup : []
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# The identifiers of the resources that attributes of this class refer to
|
|
71
|
+
#
|
|
72
|
+
# A problem is read to tell which resource it is about, so a key path the attributes hold something other than
|
|
73
|
+
# an object along reads as no identifier, rather than raise as the reader of the reference does.
|
|
74
|
+
#
|
|
75
|
+
# @api private
|
|
76
|
+
# @param attrs [Hash{String => Object}] the attributes
|
|
77
|
+
# @return [Array<Object>] the identifiers, as the attributes hold them
|
|
78
|
+
# @example Get the identifiers a post refers to
|
|
79
|
+
# X::Post.__send__(:referenced_ids, {"id" => "1", "author_id" => "2"}) # => ["2"]
|
|
80
|
+
def referenced_ids(attrs) = reference_keys.flat_map { |path| ids_at(attrs, path) }
|
|
81
|
+
|
|
82
|
+
private :attribute_names, :attribute_aliases, :reference_keys, :referenced_ids
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
# Define another name for an attribute, which a pattern can ask for
|
|
87
|
+
#
|
|
88
|
+
# @api private
|
|
89
|
+
# @param name [Symbol] the alias name
|
|
90
|
+
# @param original [Symbol] the name of the attribute
|
|
91
|
+
# @return [void]
|
|
92
|
+
def attribute_alias(name, original)
|
|
93
|
+
attribute_aliases << name
|
|
94
|
+
alias_method name, original
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Define a reader for an attribute
|
|
98
|
+
#
|
|
99
|
+
# @api private
|
|
100
|
+
# @param name [Symbol] the reader name
|
|
101
|
+
# @param type [Symbol] the attribute type: raw, boolean, time, integer, integers, list, object, objects, range,
|
|
102
|
+
# conversation_id, or requested_list, of which integers, list, and objects read a list the response omitted as empty, and
|
|
103
|
+
# requested_list, the type of a field no lookup asks for unless it is requested, reads it as nil, since a
|
|
104
|
+
# response that omits it does not say the list is empty; object is the type of an object the API nests in a
|
|
105
|
+
# resource, and objects of a list of them
|
|
106
|
+
# @param key [Array<String>] the key path
|
|
107
|
+
# @param tweet_key [Array<String>, nil] the key path of the name the API gave the attribute before it named
|
|
108
|
+
# tweets posts, which it still gives it where it has not renamed it, such as in a stream, read when the
|
|
109
|
+
# response holds nothing at the key path
|
|
110
|
+
# @return [void]
|
|
111
|
+
# @raise [InvalidAttribute] from the reader, if the response holds a value the type cannot be read from
|
|
112
|
+
def attribute(name, type = :raw, key: [name.to_s], tweet_key: nil)
|
|
113
|
+
attribute_names << name
|
|
114
|
+
paths = key_paths(key, tweet_key)
|
|
115
|
+
converter = CONVERTERS.fetch(type)
|
|
116
|
+
define_method(name) do
|
|
117
|
+
# @type self: Resource
|
|
118
|
+
Utils.read("#{self.class}##{name}", Shape.dig_first("#{self.class}##{name}", attrs, paths)) { |value| converter.call(value) }
|
|
119
|
+
end
|
|
120
|
+
return unless type.eql?(:boolean)
|
|
121
|
+
|
|
122
|
+
define_method(:"#{name}?") do
|
|
123
|
+
# @type self: Resource
|
|
124
|
+
public_send(name).eql?(true)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Define a reader that resolves a referenced resource
|
|
129
|
+
#
|
|
130
|
+
# @api private
|
|
131
|
+
# @param name [Symbol] the reader name
|
|
132
|
+
# @param klass_name [Symbol] the referenced resource class name under X
|
|
133
|
+
# @param key [Array<String>] the key path holding the identifier
|
|
134
|
+
# @param tweet_key [Array<String>, nil] the key path of the name the API gave it before it named tweets posts,
|
|
135
|
+
# read when the response holds nothing at the key path
|
|
136
|
+
# @return [void]
|
|
137
|
+
# @raise [InvalidAttribute] from the reader, if the response holds an identifier that is not one, or a key path
|
|
138
|
+
# that passes through something other than an object
|
|
139
|
+
def reference(name, klass_name, key:, tweet_key: nil)
|
|
140
|
+
paths = key_paths(key, tweet_key)
|
|
141
|
+
reference_keys.concat(paths)
|
|
142
|
+
define_method(name) do
|
|
143
|
+
# @type self: Resource
|
|
144
|
+
resolve(X.const_get(klass_name), Shape.dig_first("#{self.class}##{name}", attrs, paths))
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Define a reader that resolves a list of referenced resources
|
|
149
|
+
#
|
|
150
|
+
# @api private
|
|
151
|
+
# @param name [Symbol] the reader name
|
|
152
|
+
# @param klass_name [Symbol] the referenced resource class name under X
|
|
153
|
+
# @param key [Array<String>] the key path holding the identifiers
|
|
154
|
+
# @return [void]
|
|
155
|
+
# @raise [InvalidAttribute] from the reader, if the response holds something other than a list of identifiers
|
|
156
|
+
def references(name, klass_name, key:)
|
|
157
|
+
path = key_path(key)
|
|
158
|
+
reference_keys << path
|
|
159
|
+
define_method(name) do
|
|
160
|
+
# @type self: Resource
|
|
161
|
+
reader = "#{self.class}##{name}"
|
|
162
|
+
ids = Utils.read(reader, Shape.dig(reader, attrs, path)) { |value| Shape.list(value) } || EMPTY_LIST
|
|
163
|
+
ids.map { |id| resolve(X.const_get(klass_name), id) }.freeze
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Check that a key path is an array of keys
|
|
168
|
+
#
|
|
169
|
+
# @api private
|
|
170
|
+
# @param key [Object] the key path to check
|
|
171
|
+
# @return [Array<String>] the key path
|
|
172
|
+
# @raise [ArgumentError] if the key path is not an array
|
|
173
|
+
def key_path(key)
|
|
174
|
+
raise ArgumentError, "key must be an Array of keys, not #{key.inspect}" unless key.is_a?(Array)
|
|
175
|
+
|
|
176
|
+
key
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# The key paths an attribute is read at, in the order they are tried
|
|
180
|
+
#
|
|
181
|
+
# @api private
|
|
182
|
+
# @param key [Object] the key path
|
|
183
|
+
# @param tweet_key [Object, nil] the key path of the name the API gave it before it named tweets posts, if any
|
|
184
|
+
# @return [Array<Array<String>>] the key paths
|
|
185
|
+
# @raise [ArgumentError] if a key path is not an array
|
|
186
|
+
def key_paths(key, tweet_key) = [key, tweet_key].compact.each { |path| key_path(path) }
|
|
187
|
+
|
|
188
|
+
# The identifiers at a key path
|
|
189
|
+
#
|
|
190
|
+
# A key path holds an identifier, a list of them, or a list of objects that each hold one as id.
|
|
191
|
+
#
|
|
192
|
+
# @api private
|
|
193
|
+
# @param attrs [Hash{String => Object}] the attributes
|
|
194
|
+
# @param path [Array<String>] the key path
|
|
195
|
+
# @return [Array<Object>] the identifiers, with nil for a path that holds none
|
|
196
|
+
def ids_at(attrs, path)
|
|
197
|
+
found = path.reduce(attrs) { |value, key| Hash.try_convert(value)&.[](key) }
|
|
198
|
+
(Array.try_convert(found) || [found]).map { |element| Hash.try_convert(element)&.[]("id") || element }
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
private_constant :Attributes
|
|
202
|
+
end
|
|
203
|
+
end
|