redd 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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