redd 0.8.1 → 0.8.2
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 +4 -4
- data/README.md +18 -8
- data/bin/console +42 -16
- data/lib/redd.rb +50 -19
- data/lib/redd/api_client.rb +18 -27
- data/lib/redd/auth_strategies/auth_strategy.rb +4 -3
- data/lib/redd/auth_strategies/script.rb +6 -1
- data/lib/redd/auth_strategies/userless.rb +6 -1
- data/lib/redd/auth_strategies/web.rb +4 -4
- data/lib/redd/models/access.rb +6 -1
- data/lib/redd/models/basic_model.rb +12 -55
- data/lib/redd/models/comment.rb +24 -28
- data/lib/redd/models/front_page.rb +1 -1
- data/lib/redd/models/gildable.rb +13 -0
- data/lib/redd/models/inboxable.rb +3 -3
- data/lib/redd/models/lazy_model.rb +7 -8
- data/lib/redd/models/listing.rb +6 -7
- data/lib/redd/models/live_thread.rb +9 -11
- data/lib/redd/models/mod_mail.rb +19 -24
- data/lib/redd/models/more_comments.rb +1 -1
- data/lib/redd/models/multireddit.rb +9 -13
- data/lib/redd/models/postable.rb +3 -3
- data/lib/redd/models/private_message.rb +18 -9
- data/lib/redd/models/searchable.rb +1 -1
- data/lib/redd/models/session.rb +31 -2
- data/lib/redd/models/submission.rb +26 -14
- data/lib/redd/models/subreddit.rb +115 -19
- data/lib/redd/models/user.rb +21 -9
- data/lib/redd/models/wiki_page.rb +8 -11
- data/lib/redd/utilities/unmarshaller.rb +3 -2
- data/lib/redd/version.rb +1 -1
- data/redd.gemspec +1 -1
- metadata +4 -5
- data/TODO.md +0 -8
- data/lib/redd/auth_strategies/installed.rb +0 -22
@@ -4,37 +4,13 @@ module Redd
|
|
4
4
|
module Models
|
5
5
|
# The base class for all models.
|
6
6
|
class BasicModel
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
|
14
|
-
def from_response(client, hash)
|
15
|
-
new(client, hash)
|
16
|
-
end
|
17
|
-
|
18
|
-
# @abstract Create an instance from a value.
|
19
|
-
# @param _client [APIClient] the api client to initialize the object with
|
20
|
-
# @param _value [Object] the object to coerce
|
21
|
-
# @return [BasicModel]
|
22
|
-
def from_id(_client, _value)
|
23
|
-
# TODO: abstract this out?
|
24
|
-
raise "coercion not implemented for #{name}"
|
25
|
-
end
|
26
|
-
|
27
|
-
# @return [Hash<Symbol, #from_id>] a mapping of keys to models
|
28
|
-
def coerced_attributes
|
29
|
-
@coerced_attributes ||= {}
|
30
|
-
end
|
31
|
-
|
32
|
-
# Mark an attribute to coerce.
|
33
|
-
# @param name [Symbol] the attribute to coerce
|
34
|
-
# @param model [#from_id, nil] a model to coerce it to
|
35
|
-
def coerce_attribute(name, model = nil)
|
36
|
-
coerced_attributes[name] = model
|
37
|
-
end
|
7
|
+
# @abstract Create an instance from a value.
|
8
|
+
# @param _client [APIClient] the api client to initialize the object with
|
9
|
+
# @param _value [Object] the object to coerce
|
10
|
+
# @return [BasicModel]
|
11
|
+
def self.from_id(_client, _value)
|
12
|
+
# TODO: abstract this out?
|
13
|
+
raise "coercion not implemented for #{name}"
|
38
14
|
end
|
39
15
|
|
40
16
|
# @return [APIClient] the client the model was initialized with
|
@@ -46,13 +22,11 @@ module Redd
|
|
46
22
|
def initialize(client, attributes = {})
|
47
23
|
@client = client
|
48
24
|
@attributes = attributes
|
49
|
-
@to_coerce = self.class.coerced_attributes.keys
|
50
25
|
after_initialize
|
51
26
|
end
|
52
27
|
|
53
28
|
# @return [Hash] a Hash representation of the object
|
54
29
|
def to_h
|
55
|
-
coerce_all_attributes
|
56
30
|
@attributes
|
57
31
|
end
|
58
32
|
|
@@ -61,6 +35,11 @@ module Redd
|
|
61
35
|
[self]
|
62
36
|
end
|
63
37
|
|
38
|
+
# @return [String] an easily readable representation of the object
|
39
|
+
def inspect
|
40
|
+
"#{super}\n" + @attributes.map { |a, v| " #{a}: #{v}" }.join("\n")
|
41
|
+
end
|
42
|
+
|
64
43
|
# Checks whether an attribute is supported by method_missing.
|
65
44
|
# @param method_name [Symbol] the method name or attribute to check
|
66
45
|
# @param include_private [Boolean] whether to also include private methods
|
@@ -83,25 +62,6 @@ module Redd
|
|
83
62
|
# @abstract Lets us plug in custom code without making a mess
|
84
63
|
def after_initialize; end
|
85
64
|
|
86
|
-
# Coerces an attribute into a class using the {.from_id} method.
|
87
|
-
# @param attribute [Symbol] the attribute to coerce
|
88
|
-
def coerce_attribute(attribute)
|
89
|
-
return unless @to_coerce.include?(attribute) && @attributes.include?(attribute)
|
90
|
-
klass = self.class.coerced_attributes.fetch(attribute)
|
91
|
-
@attributes[attribute] =
|
92
|
-
if klass.nil?
|
93
|
-
@client.unmarshal(@attributes[attribute])
|
94
|
-
else
|
95
|
-
klass.from_id(@client, @attributes[attribute])
|
96
|
-
end
|
97
|
-
@to_coerce.delete(attribute)
|
98
|
-
end
|
99
|
-
|
100
|
-
# Coerce every attribute that can be coerced.
|
101
|
-
def coerce_all_attributes
|
102
|
-
@to_coerce.each { |a| coerce_attribute(a) }
|
103
|
-
end
|
104
|
-
|
105
65
|
# Remove a trailing '?' from a symbol name.
|
106
66
|
# @param method_name [Symbol] the symbol to "depredicate"
|
107
67
|
# @return [Symbol] the symbol but with the '?' removed
|
@@ -113,9 +73,6 @@ module Redd
|
|
113
73
|
# @param name [Symbol] the attribute to check and get
|
114
74
|
# @return [Object] the value of the attribute
|
115
75
|
def get_attribute(name)
|
116
|
-
# Coerce the attribute if it exists and needs to be coerced.
|
117
|
-
coerce_attribute(name) if @to_coerce.include?(name) && @attributes.key?(name)
|
118
|
-
# Fetch the attribute, raising a KeyError if it doesn't exist.
|
119
76
|
@attributes.fetch(name)
|
120
77
|
end
|
121
78
|
end
|
data/lib/redd/models/comment.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'lazy_model'
|
4
|
+
require_relative 'gildable'
|
4
5
|
require_relative 'inboxable'
|
5
6
|
require_relative 'moderatable'
|
6
7
|
require_relative 'postable'
|
@@ -14,49 +15,44 @@ module Redd
|
|
14
15
|
module Models
|
15
16
|
# A comment.
|
16
17
|
class Comment < LazyModel
|
18
|
+
include Gildable
|
17
19
|
include Inboxable
|
18
20
|
include Moderatable
|
19
21
|
include Postable
|
20
22
|
include Replyable
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# Make a Comment from its id.
|
26
|
-
# @option hash [String] :name the comment's fullname (e.g. t1_abc123)
|
27
|
-
# @option hash [String] :id the comment's id (e.g. abc123)
|
24
|
+
# Create a Comment from its fullname.
|
25
|
+
# @param client [APIClient] the api client to initialize the object with
|
26
|
+
# @param id [String] the fullname
|
28
27
|
# @return [Comment]
|
29
|
-
def self.
|
30
|
-
|
31
|
-
|
32
|
-
# Ensure we have the comment's id.
|
33
|
-
id = hash.fetch(:id) { hash.fetch(:name).sub('t1_', '') }
|
34
|
-
|
35
|
-
# If we have the link_id, we can load the listing with replies.
|
36
|
-
if hash.key?(:link_id)
|
37
|
-
link_id = hash[:link_id].sub('t3_', '')
|
38
|
-
return new(client, hash) do |c|
|
39
|
-
# The second half contains a single-item listing containing the comment
|
40
|
-
c.get("/comments/#{link_id}/_/#{id}").body[1][:data][:children][0][:data]
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# We can only load the comment in isolation if we don't have the link_id.
|
45
|
-
new(client, hash) do |c|
|
46
|
-
# Returns a single-item listing containing the comment
|
47
|
-
c.get('/api/info', id: "t1_#{id}").body[:data][:children][0][:data]
|
48
|
-
end
|
28
|
+
def self.from_id(client, id)
|
29
|
+
new(client, name: id)
|
49
30
|
end
|
50
31
|
|
51
32
|
private
|
52
33
|
|
53
34
|
def after_initialize
|
54
35
|
@attributes[:replies] =
|
55
|
-
if
|
56
|
-
|
36
|
+
if @attributes[:replies] == ''
|
37
|
+
nil
|
57
38
|
else
|
58
39
|
@client.unmarshal(@attributes[:replies])
|
59
40
|
end
|
41
|
+
@attributes[:author] = User.from_id(@client, @attributes.fetch(:author))
|
42
|
+
@attributes[:subreddit] = Subreddit.from_id(@client, @attributes.fetch(:subreddit))
|
43
|
+
end
|
44
|
+
|
45
|
+
def default_loader
|
46
|
+
# Ensure we have the comment's id.
|
47
|
+
id = @attributes.fetch(:id) { @attributes.fetch(:name).sub('t1_', '') }
|
48
|
+
|
49
|
+
# If we have the link_id, we can load the listing with replies.
|
50
|
+
if @attributes.key?(:link_id)
|
51
|
+
link_id = @attributes[:link_id].sub('t3_', '')
|
52
|
+
return @client.get("/comments/#{link_id}/_/#{id}").body[1][:data][:children][0][:data]
|
53
|
+
end
|
54
|
+
# We can only load the comment in isolation if we don't have the link_id.
|
55
|
+
@client.get('/api/info', id: "t1_#{id}").body[:data][:children][0][:data]
|
60
56
|
end
|
61
57
|
end
|
62
58
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Redd
|
4
|
+
module Models
|
5
|
+
# A model that can be gilded.
|
6
|
+
module Gildable
|
7
|
+
# Gift a user one month of reddit gold for their link or comment.
|
8
|
+
def gild
|
9
|
+
@client.post("/api/v1/gold/gild/#{get_attribute(:name)}")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -6,17 +6,17 @@ module Redd
|
|
6
6
|
module Inboxable
|
7
7
|
# Block the user that sent this item.
|
8
8
|
def block
|
9
|
-
@client.post('/api/block', id: get_attribute(:
|
9
|
+
@client.post('/api/block', id: get_attribute(:name))
|
10
10
|
end
|
11
11
|
|
12
12
|
# Mark this thing as read.
|
13
13
|
def mark_as_read
|
14
|
-
@client.post('/api/read_message', id: get_attribute(:
|
14
|
+
@client.post('/api/read_message', id: get_attribute(:name))
|
15
15
|
end
|
16
16
|
|
17
17
|
# Mark one or more messages as unread.
|
18
18
|
def mark_as_unread
|
19
|
-
@client.post('/api/unread_message', id: get_attribute(:
|
19
|
+
@client.post('/api/unread_message', id: get_attribute(:name))
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -18,16 +18,10 @@ module Redd
|
|
18
18
|
@definitely_fully_loaded = false
|
19
19
|
end
|
20
20
|
|
21
|
-
# @return [Boolean] whether the model can be to be lazily initialized
|
22
|
-
def lazy?
|
23
|
-
!@lazy_loader.nil?
|
24
|
-
end
|
25
|
-
|
26
21
|
# Force the object to make a request to reddit.
|
27
22
|
# @return [self]
|
28
23
|
def force_load
|
29
|
-
|
30
|
-
@attributes.merge!(@lazy_loader.call(@client))
|
24
|
+
@attributes.merge!(@lazy_loader ? @lazy_loader.call(@client) : default_loader)
|
31
25
|
@definitely_fully_loaded = true
|
32
26
|
after_initialize
|
33
27
|
self
|
@@ -60,9 +54,14 @@ module Redd
|
|
60
54
|
|
61
55
|
private
|
62
56
|
|
57
|
+
# @abstract A lazy loader to use when one is not provided.
|
58
|
+
def default_loader
|
59
|
+
{}
|
60
|
+
end
|
61
|
+
|
63
62
|
# Make sure the model is loaded at least once.
|
64
63
|
def ensure_fully_loaded
|
65
|
-
force_load
|
64
|
+
force_load unless @definitely_fully_loaded
|
66
65
|
end
|
67
66
|
|
68
67
|
# Gets the attribute and loads it if it may be available from the response.
|
data/lib/redd/models/listing.rb
CHANGED
@@ -9,13 +9,6 @@ module Redd
|
|
9
9
|
class Listing < BasicModel
|
10
10
|
include Enumerable
|
11
11
|
|
12
|
-
# Make a Listing from a basic Hash.
|
13
|
-
# @return [Listing]
|
14
|
-
def self.from_response(client, hash)
|
15
|
-
hash[:children].map! { |el| client.unmarshal(el) }
|
16
|
-
new(client, hash)
|
17
|
-
end
|
18
|
-
|
19
12
|
# @return [Array<Comment, Submission, PrivateMessage>] an array representation of self
|
20
13
|
def to_ary
|
21
14
|
get_attribute(:children)
|
@@ -26,6 +19,12 @@ module Redd
|
|
26
19
|
get_attribute(:children).public_send(method_name, *args, &block)
|
27
20
|
end
|
28
21
|
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def after_initialize
|
26
|
+
@attributes.fetch(:children).map! { |el| @client.unmarshal(el) }
|
27
|
+
end
|
29
28
|
end
|
30
29
|
end
|
31
30
|
end
|
@@ -10,19 +10,11 @@ module Redd
|
|
10
10
|
# An update in a live thread.
|
11
11
|
class LiveUpdate < BasicModel; end
|
12
12
|
|
13
|
-
# Get a Conversation from its id.
|
14
|
-
# @option hash [String] :id the base36 id (e.g. abc123)
|
15
|
-
# @return [Conversation]
|
16
|
-
def self.from_response(client, hash)
|
17
|
-
id = hash.fetch(:id)
|
18
|
-
new(client, hash) { |c| c.get("/live/#{id}/about").body[:data] }
|
19
|
-
end
|
20
|
-
|
21
13
|
# Get a LiveThread from its id.
|
22
14
|
# @param id [String] the id
|
23
15
|
# @return [LiveThread]
|
24
16
|
def self.from_id(client, id)
|
25
|
-
|
17
|
+
new(client, id: id)
|
26
18
|
end
|
27
19
|
|
28
20
|
# Get the updates from the thread.
|
@@ -55,14 +47,14 @@ module Redd
|
|
55
47
|
# @return [Array<User>] the contributors to this thread
|
56
48
|
def contributors
|
57
49
|
@client.get("/live/#{get_attribute(:id)}/contributors").body[0][:data].map do |user|
|
58
|
-
User.
|
50
|
+
User.new(@client, user)
|
59
51
|
end
|
60
52
|
end
|
61
53
|
|
62
54
|
# @return [Array<User>] users invited to contribute to this thread
|
63
55
|
def invited_contributors
|
64
56
|
@client.get("/live/#{get_attribute(:id)}/contributors").body[1][:data].map do |user|
|
65
|
-
User.
|
57
|
+
User.new(@client, user)
|
66
58
|
end
|
67
59
|
end
|
68
60
|
|
@@ -77,6 +69,12 @@ module Redd
|
|
77
69
|
def discussions(**params)
|
78
70
|
@client.model(:get, "/live/#{get_attribute(:id)}/discussions", params)
|
79
71
|
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def default_loader
|
76
|
+
@client.get("/live/#{@attributes.fetch(:id)}/about").body[:data]
|
77
|
+
end
|
80
78
|
end
|
81
79
|
end
|
82
80
|
end
|
data/lib/redd/models/mod_mail.rb
CHANGED
@@ -11,26 +11,11 @@ module Redd
|
|
11
11
|
# Represents a conversation in the new modmail.
|
12
12
|
# TODO: add modmail-specific user type
|
13
13
|
class Conversation < LazyModel
|
14
|
-
# Get a Conversation from its id.
|
15
|
-
# @option hash [String] :id the base36 id (e.g. abc123)
|
16
|
-
# @return [Conversation]
|
17
|
-
def self.from_response(client, hash)
|
18
|
-
id = hash.fetch(:id)
|
19
|
-
new(client, hash) do |c|
|
20
|
-
response = c.get("/api/mod/conversations/#{id}").body
|
21
|
-
response[:conversation].merge(
|
22
|
-
messages: response[:messages].values.map { |m| Message.from_response(c, m) },
|
23
|
-
user: response[:user],
|
24
|
-
mod_actions: response[:modActions]
|
25
|
-
)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
14
|
# Get a Conversation from its id.
|
30
15
|
# @param id [String] the base36 id (e.g. abc123)
|
31
16
|
# @return [Conversation]
|
32
17
|
def self.from_id(client, id)
|
33
|
-
|
18
|
+
new(client, id: id)
|
34
19
|
end
|
35
20
|
|
36
21
|
# Add a reply to the ongoing conversation.
|
@@ -84,6 +69,15 @@ module Redd
|
|
84
69
|
|
85
70
|
private
|
86
71
|
|
72
|
+
def default_loader
|
73
|
+
response = @client.get("/api/mod/conversations/#{@attributes[:id]}").body
|
74
|
+
response[:conversation].merge(
|
75
|
+
messages: response[:messages].values.map { |m| Message.new(@client, m) },
|
76
|
+
user: response[:user],
|
77
|
+
mod_actions: response[:modActions]
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
87
81
|
# Perform an action on a conversation.
|
88
82
|
# @param method [:post, :delete] the method to use
|
89
83
|
# @param action [String] the name of the action
|
@@ -104,21 +98,22 @@ module Redd
|
|
104
98
|
# @return [Array<Subreddit>] moderated subreddits that are enrolled in the new modmail
|
105
99
|
def enrolled
|
106
100
|
@client.get('/api/mod/conversations/subreddits').body[:subreddits].map do |_, s|
|
107
|
-
Subreddit.
|
101
|
+
Subreddit.new(@client, s.merge(last_updated: s.delete(:lastUpdated)))
|
108
102
|
end
|
109
103
|
end
|
110
104
|
|
111
105
|
# Get the conversations
|
112
|
-
# @param after [String] base36 modmail conversation id
|
113
106
|
# @param subreddits [Subreddit, Array<Subreddit>] the subreddits to limit to
|
114
|
-
# @param
|
115
|
-
# @
|
116
|
-
# @
|
117
|
-
#
|
107
|
+
# @param params [Hash] additional request parameters
|
108
|
+
# @option params [String] :after base36 modmail conversation id
|
109
|
+
# @option params [Integer] :limit an integer (default: 25)
|
110
|
+
# @option params [:recent, :mod, :user, :unread] :sort the sort order
|
111
|
+
# @option params [:new, :inprogress, :mod, :notifications, :archived, :highlighted, :all]
|
112
|
+
# :state the state to limit the conversations by
|
118
113
|
def conversations(subreddits: nil, **params)
|
119
114
|
params[:entity] = Array(subreddits).map(&:display_name).join(',') if subreddits
|
120
115
|
@client.get('/api/mod/conversations', **params).body[:conversations].map do |_, conv|
|
121
|
-
Conversation.
|
116
|
+
Conversation.new(@client, conv)
|
122
117
|
end
|
123
118
|
end
|
124
119
|
|
@@ -129,7 +124,7 @@ module Redd
|
|
129
124
|
# @param body [String] the message body
|
130
125
|
# @return [Conversation] the created conversation
|
131
126
|
def create(from:, to:, subject:, body:, hidden: false)
|
132
|
-
Conversation.
|
127
|
+
Conversation.new(@client, @client.post(
|
133
128
|
'/api/mod/conversations',
|
134
129
|
srName: from.display_name, to: to.name,
|
135
130
|
subject: subject, body: body, isAuthorHidden: hidden
|
@@ -24,7 +24,7 @@ module Redd
|
|
24
24
|
# @param sort [String] the sort order of the returned comments
|
25
25
|
# @param lookup [Hash] a hash of comments to add future replies to
|
26
26
|
# @param depth [Number] the maximum recursion depth
|
27
|
-
# @return [Array<Comment, MoreComments>] the expanded comments or
|
27
|
+
# @return [Array<Comment, MoreComments>] the expanded comments or self if past depth
|
28
28
|
def recursive_expand(link:, sort: 'best', lookup: {}, depth: 10)
|
29
29
|
return [self] if depth == 0
|
30
30
|
|
@@ -6,20 +6,12 @@ module Redd
|
|
6
6
|
module Models
|
7
7
|
# A multi.
|
8
8
|
class Multireddit < LazyModel
|
9
|
-
# Make a Multireddit from its path.
|
10
|
-
# @option hash [String] :path the multi's path
|
11
|
-
# @return [Multireddit]
|
12
|
-
def self.from_response(client, hash)
|
13
|
-
path = hash.fetch(:path)
|
14
|
-
new(client, hash) { |c| c.get("/api/multi#{path}").body[:data] }
|
15
|
-
end
|
16
|
-
|
17
9
|
# Create a Multireddit from its path.
|
18
10
|
# @param client [APIClient] the api client to initialize the object with
|
19
|
-
# @param id [String] the multi's path (
|
11
|
+
# @param id [String] the multi's path (with a leading and trailing slash)
|
20
12
|
# @return [Multireddit]
|
21
13
|
def self.from_id(client, id)
|
22
|
-
|
14
|
+
new(client, path: id)
|
23
15
|
end
|
24
16
|
|
25
17
|
# Get the appropriate listing.
|
@@ -56,9 +48,13 @@ module Redd
|
|
56
48
|
private
|
57
49
|
|
58
50
|
def after_initialize
|
59
|
-
|
60
|
-
|
61
|
-
|
51
|
+
@attributes[:subreddits].map! do |subreddit|
|
52
|
+
Subreddit.new(client, display_name: subreddit[:name])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_loader
|
57
|
+
@client.get("/api/multi#{@attributes.fetch(:path)}").body[:data]
|
62
58
|
end
|
63
59
|
end
|
64
60
|
end
|