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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +38 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +5 -0
  5. data/.travis.yml +10 -0
  6. data/.yardopts +1 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +10 -0
  10. data/Rakefile +10 -0
  11. data/github/redd.png +0 -0
  12. data/lib/redd.rb +14 -0
  13. data/lib/redd/RedditKit.LICENSE.md +13 -0
  14. data/lib/redd/base.rb +35 -0
  15. data/lib/redd/client/authenticated.rb +88 -0
  16. data/lib/redd/client/authenticated/account.rb +9 -0
  17. data/lib/redd/client/authenticated/apps.rb +12 -0
  18. data/lib/redd/client/authenticated/flair.rb +9 -0
  19. data/lib/redd/client/authenticated/gold.rb +12 -0
  20. data/lib/redd/client/authenticated/links_comments.rb +9 -0
  21. data/lib/redd/client/authenticated/live.rb +12 -0
  22. data/lib/redd/client/authenticated/moderation.rb +9 -0
  23. data/lib/redd/client/authenticated/multis.rb +9 -0
  24. data/lib/redd/client/authenticated/private_messages.rb +40 -0
  25. data/lib/redd/client/authenticated/subreddits.rb +9 -0
  26. data/lib/redd/client/authenticated/users.rb +9 -0
  27. data/lib/redd/client/authenticated/wiki.rb +9 -0
  28. data/lib/redd/client/oauth2.rb +9 -0
  29. data/lib/redd/client/unauthenticated.rb +104 -0
  30. data/lib/redd/client/unauthenticated/account.rb +21 -0
  31. data/lib/redd/client/unauthenticated/links_comments.rb +15 -0
  32. data/lib/redd/client/unauthenticated/listing.rb +44 -0
  33. data/lib/redd/client/unauthenticated/subreddits.rb +13 -0
  34. data/lib/redd/client/unauthenticated/utilities.rb +61 -0
  35. data/lib/redd/client/unauthenticated/wiki.rb +9 -0
  36. data/lib/redd/error.rb +93 -0
  37. data/lib/redd/object/comment.rb +41 -0
  38. data/lib/redd/object/listing.rb +24 -0
  39. data/lib/redd/object/submission.rb +94 -0
  40. data/lib/redd/object/subreddit.rb +10 -0
  41. data/lib/redd/rate_limit.rb +48 -0
  42. data/lib/redd/response/parse_json.rb +31 -0
  43. data/lib/redd/response/raise_error.rb +20 -0
  44. data/lib/redd/thing.rb +27 -0
  45. data/lib/redd/version.rb +5 -0
  46. data/redd.gemspec +32 -0
  47. data/spec/redd_spec.rb +7 -0
  48. data/spec/spec_helper.rb +33 -0
  49. metadata +234 -0
@@ -0,0 +1,9 @@
1
+ module Redd
2
+ module Client
3
+ class Authenticated
4
+ module Wiki
5
+
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require "redd/client/authenticated"
2
+
3
+ module Redd
4
+ module Client
5
+ # The client to connect using OAuth2.
6
+ class OAuth2 < Redd::Client::Authenticated
7
+ end
8
+ end
9
+ end
@@ -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,13 @@
1
+ module Redd
2
+ module Client
3
+ class Unauthenticated
4
+ module Subreddits
5
+ def subreddit(name)
6
+ meth = :get
7
+ path = "/r/#{name}/about.json"
8
+ object_from_response(meth, path)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ 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
@@ -0,0 +1,9 @@
1
+ module Redd
2
+ module Client
3
+ class Unauthenticated
4
+ module Wiki
5
+
6
+ end
7
+ end
8
+ end
9
+ 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 &lt;'s and &gt;'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