redd 0.1.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/.gitignore +38 -0
- data/.rspec +2 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +10 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +10 -0
- data/Rakefile +10 -0
- data/github/redd.png +0 -0
- data/lib/redd.rb +14 -0
- data/lib/redd/RedditKit.LICENSE.md +13 -0
- data/lib/redd/base.rb +35 -0
- data/lib/redd/client/authenticated.rb +88 -0
- data/lib/redd/client/authenticated/account.rb +9 -0
- data/lib/redd/client/authenticated/apps.rb +12 -0
- data/lib/redd/client/authenticated/flair.rb +9 -0
- data/lib/redd/client/authenticated/gold.rb +12 -0
- data/lib/redd/client/authenticated/links_comments.rb +9 -0
- data/lib/redd/client/authenticated/live.rb +12 -0
- data/lib/redd/client/authenticated/moderation.rb +9 -0
- data/lib/redd/client/authenticated/multis.rb +9 -0
- data/lib/redd/client/authenticated/private_messages.rb +40 -0
- data/lib/redd/client/authenticated/subreddits.rb +9 -0
- data/lib/redd/client/authenticated/users.rb +9 -0
- data/lib/redd/client/authenticated/wiki.rb +9 -0
- data/lib/redd/client/oauth2.rb +9 -0
- data/lib/redd/client/unauthenticated.rb +104 -0
- data/lib/redd/client/unauthenticated/account.rb +21 -0
- data/lib/redd/client/unauthenticated/links_comments.rb +15 -0
- data/lib/redd/client/unauthenticated/listing.rb +44 -0
- data/lib/redd/client/unauthenticated/subreddits.rb +13 -0
- data/lib/redd/client/unauthenticated/utilities.rb +61 -0
- data/lib/redd/client/unauthenticated/wiki.rb +9 -0
- data/lib/redd/error.rb +93 -0
- data/lib/redd/object/comment.rb +41 -0
- data/lib/redd/object/listing.rb +24 -0
- data/lib/redd/object/submission.rb +94 -0
- data/lib/redd/object/subreddit.rb +10 -0
- data/lib/redd/rate_limit.rb +48 -0
- data/lib/redd/response/parse_json.rb +31 -0
- data/lib/redd/response/raise_error.rb +20 -0
- data/lib/redd/thing.rb +27 -0
- data/lib/redd/version.rb +5 -0
- data/redd.gemspec +32 -0
- data/spec/redd_spec.rb +7 -0
- data/spec/spec_helper.rb +33 -0
- metadata +234 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
require "faraday"
|
2
|
+
require "redd/version"
|
3
|
+
require "redd/rate_limit"
|
4
|
+
require "redd/response/parse_json"
|
5
|
+
require "redd/response/raise_error"
|
6
|
+
require "redd/client/unauthenticated/account"
|
7
|
+
require "redd/client/unauthenticated/links_comments"
|
8
|
+
require "redd/client/unauthenticated/listing"
|
9
|
+
require "redd/client/unauthenticated/subreddits"
|
10
|
+
require "redd/client/unauthenticated/utilities"
|
11
|
+
require "redd/client/unauthenticated/wiki"
|
12
|
+
|
13
|
+
module Redd
|
14
|
+
module Client
|
15
|
+
# The Client used to connect without needing login credentials.
|
16
|
+
class Unauthenticated
|
17
|
+
include Redd::Client::Unauthenticated::Account
|
18
|
+
include Redd::Client::Unauthenticated::LinksComments
|
19
|
+
include Redd::Client::Unauthenticated::Listing
|
20
|
+
include Redd::Client::Unauthenticated::Subreddits
|
21
|
+
include Redd::Client::Unauthenticated::Utilities
|
22
|
+
include Redd::Client::Unauthenticated::Wiki
|
23
|
+
|
24
|
+
# @!attribute [r] api_endpoint
|
25
|
+
# @return [String] The site to connect to.
|
26
|
+
attr_accessor :api_endpoint
|
27
|
+
|
28
|
+
# @!attribute [r] user_agent
|
29
|
+
# @return [String] The user-agent used to communicate with reddit.
|
30
|
+
attr_accessor :user_agent
|
31
|
+
|
32
|
+
# @!attribute [r] rate_limit
|
33
|
+
# @return [#after_limit] The handler that takes care of rate limiting.
|
34
|
+
attr_accessor :rate_limit
|
35
|
+
|
36
|
+
# Set up an unauthenticated connection to reddit.
|
37
|
+
#
|
38
|
+
# @param [Hash] options A hash of options to connect using.
|
39
|
+
# @option options [#after_limit] :rate_limit The handler that takes care
|
40
|
+
# of rate limiting.
|
41
|
+
# @option options [String] :user_agent The User-Agent string to use in the
|
42
|
+
# header of every request.
|
43
|
+
# @option options [String] :api_endpoint The main domain to connect
|
44
|
+
# to, in this case, the URL for reddit.
|
45
|
+
def initialize(options = {})
|
46
|
+
@rate_limit = options[:rate_limit] || Redd::RateLimit.new
|
47
|
+
@user_agent = options[:user_agent] || "Redd/Ruby, v#{Redd::VERSION}"
|
48
|
+
@api_endpoint = options[:api_endpoint] || "http://www.reddit.com/"
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Gets the Faraday connection or creates one if it doesn't exist yet.
|
54
|
+
#
|
55
|
+
# @return [Faraday] A new Faraday connection.
|
56
|
+
def connection
|
57
|
+
@connection ||= Faraday.new(url: api_endpoint) do |faraday|
|
58
|
+
faraday.use Faraday::Request::UrlEncoded
|
59
|
+
faraday.use Redd::Response::ParseJson
|
60
|
+
faraday.use Redd::Response::RaiseError
|
61
|
+
faraday.adapter Faraday.default_adapter
|
62
|
+
|
63
|
+
faraday.headers["User-Agent"] = user_agent
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Send a request to the given path.
|
68
|
+
#
|
69
|
+
# @param [#to_sym] method The HTTP verb to use.
|
70
|
+
# @param [String] path The path under the api endpoint to request from.
|
71
|
+
# @param [Hash] params The additional parameters to send (defualt: {}).
|
72
|
+
# @return [Faraday::Response] A Faraday Response.
|
73
|
+
def request(method, path, params = {})
|
74
|
+
rate_limit.after_limit do
|
75
|
+
connection.send(method.to_sym, path, params)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Performs a GET request via {#request}.
|
80
|
+
# @see #request
|
81
|
+
def get(*args)
|
82
|
+
request(:get, *args)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Performs a POST request via {#request}.
|
86
|
+
# @see #request
|
87
|
+
def post(*args)
|
88
|
+
request(:post, *args)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Performs a PUT request via {#request}.
|
92
|
+
# @see #request
|
93
|
+
def put(*args)
|
94
|
+
request(:put, *args)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Performs a DELETE request via {#request}.
|
98
|
+
# @see #request
|
99
|
+
def delete(*args)
|
100
|
+
request(:delete, *args)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Redd
|
2
|
+
module Client
|
3
|
+
class Unauthenticated
|
4
|
+
module Account
|
5
|
+
def login(username, password, remember = false)
|
6
|
+
meth = :post
|
7
|
+
path = "/api/login"
|
8
|
+
params = {
|
9
|
+
api_type: "json", user: username,
|
10
|
+
passwd: password, rem: remember
|
11
|
+
}
|
12
|
+
response = send(meth, path, params)
|
13
|
+
data = response.body[:json][:data]
|
14
|
+
|
15
|
+
require "redd/client/authenticated"
|
16
|
+
Redd::Client::Authenticated.new(data[:cookie], data[:modhash])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Redd
|
2
|
+
module Client
|
3
|
+
class Unauthenticated
|
4
|
+
module LinksComments
|
5
|
+
# @note Reddit does accept a subreddit, but with fullnames and urls, I
|
6
|
+
# assumed that was unnecessary.
|
7
|
+
def get_info(params = {})
|
8
|
+
meth = :get
|
9
|
+
path = "/api/info.json"
|
10
|
+
object_from_response(meth, path, params)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Redd
|
2
|
+
module Client
|
3
|
+
class Unauthenticated
|
4
|
+
module Listing
|
5
|
+
def by_id(*fullnames)
|
6
|
+
names = fullnames.join(",")
|
7
|
+
|
8
|
+
meth = :get
|
9
|
+
path = "/by_id/#{names}.json"
|
10
|
+
object_from_response(meth, path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_hot(*args)
|
14
|
+
get_listing(:hot, *args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_new(*args)
|
18
|
+
get_listing(:new, *args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_random(*args)
|
22
|
+
get_listing(:random, *args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_top(*args)
|
26
|
+
get_listing(:top, *args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_controversial(*args)
|
30
|
+
get_listing(:controversial, *args)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def get_listing(type, subreddit = nil, params = {})
|
36
|
+
meth = :get
|
37
|
+
path = "/#{type}.json"
|
38
|
+
path = path.prepend("/r/#{subreddit}") if subreddit
|
39
|
+
object_from_response(meth, path, params)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "redd/thing"
|
2
|
+
require "redd/object/listing"
|
3
|
+
require "redd/object/comment"
|
4
|
+
require "redd/object/submission"
|
5
|
+
require "redd/object/subreddit"
|
6
|
+
|
7
|
+
module Redd
|
8
|
+
module Client
|
9
|
+
class Unauthenticated
|
10
|
+
module Utilities
|
11
|
+
private
|
12
|
+
|
13
|
+
def extract_fullname(object)
|
14
|
+
object.is_a?(String) ? object : extract_attribute(object, :fullname)
|
15
|
+
end
|
16
|
+
|
17
|
+
def extract_attribute(object, attribute)
|
18
|
+
object.send(attribute) if object.respond_to?(attribute)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @todo "more"
|
22
|
+
def object_from_kind(kind) # rubocop:disable Style/MethodLength
|
23
|
+
case kind
|
24
|
+
when "Listing"
|
25
|
+
Redd::Object::Listing
|
26
|
+
when "t1"
|
27
|
+
Redd::Object::Comment
|
28
|
+
when "t3"
|
29
|
+
Redd::Object::Submission
|
30
|
+
when "t5"
|
31
|
+
Redd::Object::Subreddit
|
32
|
+
else
|
33
|
+
Redd::Thing
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def objects_from_listing(thing)
|
38
|
+
thing[:data][:children].map do |child|
|
39
|
+
get_object_from_body(child)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_object_from_body(body)
|
44
|
+
object = object_from_kind(body[:kind])
|
45
|
+
|
46
|
+
if object == Redd::Object::Listing
|
47
|
+
things = objects_from_listing(body)
|
48
|
+
object.new(things)
|
49
|
+
else
|
50
|
+
object.new(self, body)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def object_from_response(*args)
|
55
|
+
body = request(*args).body
|
56
|
+
get_object_from_body(body)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/redd/error.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
module Redd
|
2
|
+
class Error < StandardError
|
3
|
+
class << self
|
4
|
+
# @note I ripped off RedditKit.rb :|
|
5
|
+
def from_response(response) # rubocop:disable Style/CyclomaticComplexity Style/MethodLength
|
6
|
+
status = response[:status]
|
7
|
+
body = parse_error(response[:body])
|
8
|
+
case status
|
9
|
+
when 200
|
10
|
+
case body
|
11
|
+
when /WRONG_PASSWORD/i
|
12
|
+
InvalidCredentials
|
13
|
+
when /BAD_CAPTCHA/i
|
14
|
+
InvalidCaptcha
|
15
|
+
when /RATELIMIT/i
|
16
|
+
RateLimited
|
17
|
+
when /BAD_CSS_NAME/i
|
18
|
+
InvalidClassName
|
19
|
+
when /TOO_OLD/i
|
20
|
+
Archived
|
21
|
+
when /TOO_MUCH_FLAIR_CSS/i
|
22
|
+
TooManyClassNames
|
23
|
+
when /USER_REQUIRED/i
|
24
|
+
AuthenticationRequired
|
25
|
+
end
|
26
|
+
when 400
|
27
|
+
BadRequest
|
28
|
+
when 403
|
29
|
+
case body
|
30
|
+
when /USER_REQUIRED/i
|
31
|
+
AuthenticationRequired
|
32
|
+
else
|
33
|
+
PermissionDenied
|
34
|
+
end
|
35
|
+
when 404
|
36
|
+
NotFound
|
37
|
+
when 409
|
38
|
+
Conflict
|
39
|
+
when 500
|
40
|
+
InternalServerError
|
41
|
+
when 502
|
42
|
+
BadGateway
|
43
|
+
when 503
|
44
|
+
ServiceUnavailable
|
45
|
+
when 504
|
46
|
+
TimedOut
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_error(body)
|
51
|
+
return nil unless body.is_a?(Hash)
|
52
|
+
|
53
|
+
if body.key?(:json) && body[:json].key?(:errors)
|
54
|
+
body[:json][:errors].first
|
55
|
+
elsif body.key?(:jquery)
|
56
|
+
body[:jquery].to_s
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class AuthenticationRequired < Error; end
|
62
|
+
|
63
|
+
class InvalidCaptcha < Error; end
|
64
|
+
|
65
|
+
class BadGateway < Error; end
|
66
|
+
|
67
|
+
class InvalidMultiredditName < Error; end
|
68
|
+
|
69
|
+
class Conflict < Error; end
|
70
|
+
|
71
|
+
class InternalServerError < Error; end
|
72
|
+
|
73
|
+
class InvalidClassName < Error; end
|
74
|
+
|
75
|
+
class InvalidCredentials < Error; end
|
76
|
+
|
77
|
+
class NotFound < Error; end
|
78
|
+
|
79
|
+
class PermissionDenied < Error; end
|
80
|
+
|
81
|
+
class RateLimited < Error; end
|
82
|
+
|
83
|
+
class RequestError < Error; end
|
84
|
+
|
85
|
+
class ServiceUnavailable < Error; end
|
86
|
+
|
87
|
+
class TooManyClassNames < Error; end
|
88
|
+
|
89
|
+
class Archived < Error; end
|
90
|
+
|
91
|
+
class TimedOut < Error; end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "redd/thing"
|
2
|
+
|
3
|
+
module Redd
|
4
|
+
module Object
|
5
|
+
# A comment made on links.
|
6
|
+
class Comment < Redd::Thing
|
7
|
+
# @!attribute [r] subreddit
|
8
|
+
# @return [String] The name of the subreddit this comment belongs to.
|
9
|
+
# @todo Convert to a Subreddit object?
|
10
|
+
attr_reader :subreddit
|
11
|
+
|
12
|
+
# @!attribute [r] parent_id
|
13
|
+
# @return [String] The id of the parent comment.
|
14
|
+
# @todo parent - get the parent comment directly.
|
15
|
+
attr_reader :parent_id
|
16
|
+
|
17
|
+
# @!attribute [r] body
|
18
|
+
# @return [String] The text of the comment in markdown.
|
19
|
+
attr_reader :body
|
20
|
+
|
21
|
+
# @!attribute [r] body_html
|
22
|
+
# @return [String] The text of the comment in html.
|
23
|
+
# @note Be warned: this isn't actual html, but escaped html. So all the
|
24
|
+
# <'s and >'s are converted to <'s and >'s.
|
25
|
+
attr_reader :body_html
|
26
|
+
|
27
|
+
# @!attribute [r] author_flair_text
|
28
|
+
# @return [String] The user's flair.
|
29
|
+
attr_reader :author_flair_text
|
30
|
+
|
31
|
+
# @!attribute [r] author_flair_css_class
|
32
|
+
# @return [String] The CSS class of the user's flair.
|
33
|
+
attr_reader :author_flair_css_class
|
34
|
+
|
35
|
+
# @return [Boolean] Whether the comment is a root rather than a reply.
|
36
|
+
def root?
|
37
|
+
!parent_id || parent_id == fullname
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module Redd
|
4
|
+
module Object
|
5
|
+
# A listing of various reddit things.
|
6
|
+
# @see http://www.reddit.com/dev/api#listings
|
7
|
+
# @see http://stackoverflow.com/a/2080118
|
8
|
+
class Listing
|
9
|
+
include Enumerable
|
10
|
+
extend Forwardable
|
11
|
+
def_delegators :@things, :[], :length, :size, :each, :map, :empty?
|
12
|
+
|
13
|
+
# @!attribute [r] things
|
14
|
+
# @return [Array] A list of things in the listing.
|
15
|
+
attr_reader :things
|
16
|
+
|
17
|
+
# Create a new listing with the given things.
|
18
|
+
# @param [Array] things A list of things to generate a Listing out of.
|
19
|
+
def initialize(things)
|
20
|
+
@things = things
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|