redd 0.7.10 → 0.8.0.pre.1

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -30
  3. data/.rspec +1 -1
  4. data/.rubocop.yml +16 -3
  5. data/.travis.yml +13 -7
  6. data/Gemfile +3 -1
  7. data/LICENSE.txt +21 -0
  8. data/README.md +40 -126
  9. data/Rakefile +10 -3
  10. data/TODO.md +11 -0
  11. data/bin/console +84 -0
  12. data/bin/setup +8 -0
  13. data/lib/redd.rb +84 -46
  14. data/lib/redd/api_client.rb +109 -0
  15. data/lib/redd/auth_strategies/auth_strategy.rb +60 -0
  16. data/lib/redd/auth_strategies/installed.rb +22 -0
  17. data/lib/redd/auth_strategies/script.rb +23 -0
  18. data/lib/redd/auth_strategies/userless.rb +17 -0
  19. data/lib/redd/auth_strategies/web.rb +29 -0
  20. data/lib/redd/client.rb +88 -0
  21. data/lib/redd/error.rb +19 -142
  22. data/lib/redd/models/access.rb +20 -0
  23. data/lib/redd/models/basic_model.rb +124 -0
  24. data/lib/redd/models/comment.rb +51 -0
  25. data/lib/redd/models/front_page.rb +71 -0
  26. data/lib/redd/models/inboxable.rb +23 -0
  27. data/lib/redd/models/lazy_model.rb +63 -0
  28. data/lib/redd/models/listing.rb +26 -0
  29. data/lib/redd/models/messageable.rb +20 -0
  30. data/lib/redd/models/moderatable.rb +41 -0
  31. data/lib/redd/models/more_comments.rb +10 -0
  32. data/lib/redd/models/multireddit.rb +32 -0
  33. data/lib/redd/models/postable.rb +70 -0
  34. data/lib/redd/models/private_message.rb +29 -0
  35. data/lib/redd/models/replyable.rb +16 -0
  36. data/lib/redd/models/session.rb +86 -0
  37. data/lib/redd/models/submission.rb +40 -0
  38. data/lib/redd/models/subreddit.rb +201 -0
  39. data/lib/redd/models/user.rb +72 -0
  40. data/lib/redd/models/wiki_page.rb +24 -0
  41. data/lib/redd/utilities/error_handler.rb +35 -0
  42. data/lib/redd/utilities/rate_limiter.rb +21 -0
  43. data/lib/redd/utilities/stream.rb +63 -0
  44. data/lib/redd/utilities/unmarshaller.rb +39 -0
  45. data/lib/redd/version.rb +4 -3
  46. data/logo.png +0 -0
  47. data/redd.gemspec +26 -22
  48. metadata +73 -99
  49. data/LICENSE.md +0 -22
  50. data/RedditKit.LICENSE.md +0 -9
  51. data/lib/redd/access.rb +0 -76
  52. data/lib/redd/clients/base.rb +0 -188
  53. data/lib/redd/clients/base/account.rb +0 -20
  54. data/lib/redd/clients/base/identity.rb +0 -22
  55. data/lib/redd/clients/base/none.rb +0 -27
  56. data/lib/redd/clients/base/privatemessages.rb +0 -33
  57. data/lib/redd/clients/base/read.rb +0 -113
  58. data/lib/redd/clients/base/stream.rb +0 -81
  59. data/lib/redd/clients/base/submit.rb +0 -19
  60. data/lib/redd/clients/base/utilities.rb +0 -104
  61. data/lib/redd/clients/base/wikiread.rb +0 -33
  62. data/lib/redd/clients/installed.rb +0 -57
  63. data/lib/redd/clients/script.rb +0 -41
  64. data/lib/redd/clients/userless.rb +0 -32
  65. data/lib/redd/clients/web.rb +0 -58
  66. data/lib/redd/objects/base.rb +0 -39
  67. data/lib/redd/objects/comment.rb +0 -22
  68. data/lib/redd/objects/labeled_multi.rb +0 -13
  69. data/lib/redd/objects/listing.rb +0 -29
  70. data/lib/redd/objects/more_comments.rb +0 -11
  71. data/lib/redd/objects/private_message.rb +0 -28
  72. data/lib/redd/objects/submission.rb +0 -139
  73. data/lib/redd/objects/subreddit.rb +0 -330
  74. data/lib/redd/objects/thing.rb +0 -26
  75. data/lib/redd/objects/thing/editable.rb +0 -22
  76. data/lib/redd/objects/thing/hideable.rb +0 -18
  77. data/lib/redd/objects/thing/inboxable.rb +0 -25
  78. data/lib/redd/objects/thing/messageable.rb +0 -34
  79. data/lib/redd/objects/thing/moderatable.rb +0 -43
  80. data/lib/redd/objects/thing/refreshable.rb +0 -14
  81. data/lib/redd/objects/thing/saveable.rb +0 -21
  82. data/lib/redd/objects/thing/votable.rb +0 -33
  83. data/lib/redd/objects/user.rb +0 -52
  84. data/lib/redd/objects/wiki_page.rb +0 -15
  85. data/lib/redd/rate_limit.rb +0 -88
  86. data/lib/redd/response/parse_json.rb +0 -18
  87. data/lib/redd/response/raise_error.rb +0 -16
  88. data/spec/redd/objects/base_spec.rb +0 -1
  89. data/spec/redd/response/raise_error_spec.rb +0 -11
  90. data/spec/redd_spec.rb +0 -5
  91. data/spec/spec_helper.rb +0 -71
@@ -1,81 +0,0 @@
1
- require "set"
2
-
3
- module Redd
4
- module Clients
5
- class Base
6
- # Methods that stream delicious content into your bot's lazy mouth.
7
- module Stream
8
- # A class similar to PRAW's implementation of a BoundedSet.
9
- class PRAWBoundedQueueSet < Set
10
- def initialize(max, *args, &block)
11
- @max = max
12
- @queue = []
13
- super(*args, &block)
14
- end
15
-
16
- # Add an element to the front if it isn't already in the queue.
17
- # @param element
18
- # @return [PRAWBoundedQueueSet] self
19
- def enqueue(element)
20
- @queue.push(element) if add?(element)
21
- dequeue! if size > @max
22
- self
23
- end
24
- alias_method :enq, :enqueue
25
-
26
- # Add an element to the front if it isn't already in the queue.
27
- # @param element
28
- # @return [Boolean] Whether the element was added to the queue.
29
- def enqueue?(element)
30
- added = add?(element)
31
- if added
32
- @queue.push(element)
33
- dequeue if size > @max
34
- end
35
- added
36
- end
37
- alias_method :enq?, :enqueue?
38
-
39
- # Remove the last element of the queue.
40
- # @return The removed element.
41
- def dequeue
42
- element = @queue.shift
43
- delete(element)
44
- element
45
- end
46
- alias_method :deq, :dequeue
47
- end
48
-
49
- # Stream the results of a method call to the given block.
50
- # @param [Symbol] meth A method that returns a listing and has a
51
- # keyword parameter named `:before`.
52
- # @param [Array] args The arguments supplied to the method.
53
- # @param [Hash] kwargs The keyword arguments supplied to the method.
54
- # @yield An element of the returned listing.
55
- def stream(meth = :get_new, *args, **kwargs)
56
- bset = PRAWBoundedQueueSet.new(10)
57
- before = ""
58
- loop do
59
- begin
60
- # Get the latest comments from the subreddit.
61
- params = kwargs.merge(before: before)
62
- listing = send(meth, *args, **params)
63
- # Run the loop for each of the item in the listing
64
- listing.reverse_each do |thing|
65
- yield thing if bset.enqueue?(thing.fullname)
66
- end
67
- # Set the latest comment.
68
- before = listing.first.fullname unless listing.empty?
69
- rescue Redd::Error::RateLimited => error
70
- # If this error pops up, you probably have an issue with your bot.
71
- sleep(error.time)
72
- rescue Redd::Error => error
73
- # 5-something errors are usually errors on reddit's end.
74
- raise error unless (500...600).include?(error.code)
75
- end
76
- end
77
- end
78
- end # <- See, this is why I sometimes prefer Python.
79
- end # <- Thank god for code folding.
80
- end
81
- end
@@ -1,19 +0,0 @@
1
- module Redd
2
- module Clients
3
- class Base
4
- # Methods that require the "submit" scope
5
- module Submit
6
- # Add a comment to a link, reply to a comment or reply to a message.
7
- # Bit of an all-purpose method, this one.
8
- # @param thing [Objects::Submission, Objects::Comment,
9
- # Objects::PrivateMessage] A thing to add a comment to.
10
- # @param text [String] The text to comment.
11
- # @return [Objects::Comment, Objects::PrivateMessage] The reply.
12
- def add_comment(thing, text)
13
- response = post("/api/comment", text: text, thing_id: thing.fullname)
14
- object_from_body(response.body[:json][:data][:things][0])
15
- end
16
- end
17
- end
18
- end
19
- end
@@ -1,104 +0,0 @@
1
- require_relative "../../objects/base"
2
- require_relative "../../objects/thing"
3
- require_relative "../../objects/listing"
4
- require_relative "../../objects/wiki_page"
5
- require_relative "../../objects/labeled_multi"
6
- require_relative "../../objects/more_comments"
7
- require_relative "../../objects/comment"
8
- require_relative "../../objects/user"
9
- require_relative "../../objects/submission"
10
- require_relative "../../objects/private_message"
11
- require_relative "../../objects/subreddit"
12
-
13
- module Redd
14
- module Clients
15
- class Base
16
- # Internal methods that make life easier.
17
- # @todo Move this out to Redd::Utils?
18
- module Utilities
19
- # The kind strings and the objects that should be used for them.
20
- OBJECT_KINDS = {
21
- "Listing" => Objects::Listing,
22
- "wikipage" => Objects::WikiPage,
23
- "LabeledMulti" => Objects::LabeledMulti,
24
- "more" => Objects::MoreComments,
25
- "t1" => Objects::Comment,
26
- "t2" => Objects::User,
27
- "t3" => Objects::Submission,
28
- "t4" => Objects::PrivateMessage,
29
- "t5" => Objects::Subreddit
30
- }
31
-
32
- # Request and create an object from the response.
33
- # @param [Symbol] meth The method to use.
34
- # @param [String] path The path to visit.
35
- # @param [Hash] params The data to send with the request.
36
- # @return [Objects::Base] The object returned from the request.
37
- def request_object(meth, path, params = {})
38
- body = send(meth, path, params).body
39
- object_from_body(body)
40
- end
41
-
42
- # Create an object instance with the correct attributes when given a
43
- # body.
44
- #
45
- # @param [Hash] body A JSON hash.
46
- # @return [Objects::Thing, Objects::Listing]
47
- def object_from_body(body)
48
- return nil unless body.is_a?(Hash)
49
- object = object_from_kind(body[:kind])
50
- flat = flatten_body(body)
51
- object.new(self, flat)
52
- end
53
-
54
- # @param [Objects::Submission, Objects::Comment] base The start of the
55
- # comment tree.
56
- # @author Bryce Boe (@bboe) in Python
57
- # @return [Array<Objects::Comment, Objects::MoreComments>] A linear
58
- # array of the submission's comments or the comments' replies.
59
- def flat_comments(base)
60
- meth = (base.is_a?(Objects::Submission) ? :comments : :replies)
61
- stack = base.send(meth).dup
62
- flattened = []
63
-
64
- until stack.empty?
65
- comment = stack.shift
66
- if comment.is_a?(Objects::Comment)
67
- replies = comment.replies
68
- stack = replies + stack if replies
69
- end
70
- flattened << comment
71
- end
72
-
73
- flattened
74
- end
75
-
76
- # Get a given property of a given object.
77
- # @param [Objects::Base, String] object The object with the property.
78
- # @param [Symbol] property The property to get.
79
- def property(object, property)
80
- object.respond_to?(property) ? object.send(property) : object.to_s
81
- end
82
-
83
- private
84
-
85
- # Take a multilevel body ({kind: "tx", data: {...}}) and flatten it
86
- # into something like {kind: "tx", ...}
87
- # @param [Hash] body The response body.
88
- # @return [Hash] The flattened hash.
89
- def flatten_body(body)
90
- data = body[:data] || body
91
- data[:kind] = body[:kind]
92
- data
93
- end
94
-
95
- # @param [String] kind A kind in the format /t[1-5]/.
96
- # @return [Objects::Base, Objects::Listing] The appropriate object for
97
- # a given kind.
98
- def object_from_kind(kind)
99
- OBJECT_KINDS.fetch(kind, Objects::Base)
100
- end
101
- end
102
- end
103
- end
104
- end
@@ -1,33 +0,0 @@
1
- module Redd
2
- module Clients
3
- class Base
4
- # Methods that require the "wikiread" scope.
5
- # @note This method is not limited to {Objects::Subreddit} because there
6
- # are also top-level wiki pages.
7
- module Wikiread
8
- # Get a list of pages in the subreddit wiki.
9
- # @param subreddit [Objects::Subreddit, String] The subreddit to
10
- # look in.
11
- # @return [Array<String>] An array of wikipage titles.
12
- def get_wikipages(subreddit = nil)
13
- path = "/wiki/pages.json"
14
- name = property(subreddit, :display_name)
15
- path.prepend("/r/#{name}") if subreddit
16
- get(path).body[:data]
17
- end
18
-
19
- # Get a wiki page.
20
- # @param page [String] The title of the wiki page.
21
- # @param subreddit [Objects::Subreddit, String] The subreddit to
22
- # look in.
23
- # @return [Objects::WikiPage] A wiki page.
24
- def wikipage(page, subreddit = nil)
25
- path = "/wiki/#{page}.json"
26
- name = property(subreddit, :display_name)
27
- path.prepend("/r/#{name}") if subreddit
28
- request_object(:get, path)
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,57 +0,0 @@
1
- require "cgi"
2
- require_relative "base"
3
-
4
- module Redd
5
- module Clients
6
- # The client for installed apps that can't keep a secret.
7
- # It might even work with Rubymotion (fingers crossed).
8
- class Installed < Base
9
- # @!attribute [r] client_id
10
- attr_reader :client_id
11
-
12
- # @!attribute [r] redirect_uri
13
- attr_reader :redirect_uri
14
-
15
- # @param [Hash] options The options to create the client with.
16
- # @see Base#initialize
17
- # @see Redd.it
18
- def initialize(client_id, redirect_uri, **options)
19
- @client_id = client_id
20
- @redirect_uri = redirect_uri
21
- super(**options)
22
- end
23
-
24
- # @param [String] state A random string to double-check later.
25
- # @param [Array<String>] scope The scope to request access to.
26
- # @param [:temporary, :permanent] duration
27
- # @return [String] The url to redirect the user to.
28
- def auth_url(state, scope = ["identity"], duration = :temporary)
29
- query = {
30
- response_type: "token",
31
- client_id: @client_id,
32
- redirect_uri: @redirect_uri,
33
- state: state,
34
- scope: scope.join(","),
35
- duration: duration
36
- }
37
-
38
- url = URI.join(auth_endpoint, "/api/v1/authorize")
39
- url.query = URI.encode_www_form(query)
40
- url.to_s
41
- end
42
-
43
- # Authorize using the url fragment.
44
- # @param [String] fragment The part of the url after the "#".
45
- # @return [Access] The access given by reddit.
46
- def authorize!(fragment)
47
- parsed = CGI.parse(fragment)
48
- @access = Access.new(
49
- access_token: parsed[:access_token].first,
50
- expires_in: parsed[:expires_in].first,
51
- scope: parsed[:scope]
52
- )
53
- reset_connection!
54
- end
55
- end
56
- end
57
- end
@@ -1,41 +0,0 @@
1
- require_relative "base"
2
-
3
- module Redd
4
- module Clients
5
- # The client for an account you own (e.g. bots).
6
- class Script < Base
7
- # @!attribute [r] client_id
8
- attr_reader :client_id
9
-
10
- # @!attribute [r] username
11
- attr_reader :username
12
-
13
- # @param [Hash] options The options to create the client with.
14
- # @see Base#initialize
15
- # @see Redd.it
16
- def initialize(client_id, secret, username, password, **options)
17
- @client_id = client_id
18
- @secret = secret
19
- @username = username
20
- @password = password
21
- super(**options)
22
- end
23
-
24
- # Authorize using the given data.
25
- # @return [Access] The access given by reddit.
26
- def authorize!
27
- response = auth_connection.post(
28
- "/api/v1/access_token",
29
- grant_type: "password",
30
- username: @username,
31
- password: @password
32
- )
33
-
34
- @access = Access.new(response.body)
35
- reset_connection!
36
- end
37
-
38
- alias_method :refresh_access!, :authorize!
39
- end
40
- end
41
- end
@@ -1,32 +0,0 @@
1
- require_relative "base"
2
-
3
- module Redd
4
- module Clients
5
- # The client that doesn't need a user to function.
6
- # @note Of course, that means many editing methods throw an error.
7
- class Userless < Base
8
- # @!attribute [r] client_id
9
- attr_reader :client_id
10
-
11
- # @param [Hash] options The options to create the client with.
12
- # @see Base#initialize
13
- # @see Redd.it
14
- def initialize(client_id, secret, **options)
15
- @client_id = client_id
16
- @secret = secret
17
- super(**options)
18
- end
19
-
20
- # Authorize using the given data.
21
- # @return [Access] The access given by reddit.
22
- def authorize!
23
- response = auth_connection.post(
24
- "/api/v1/access_token",
25
- grant_type: "client_credentials"
26
- )
27
- @access = Access.new(response.body)
28
- reset_connection!
29
- end
30
- end
31
- end
32
- end
@@ -1,58 +0,0 @@
1
- require "uri"
2
- require_relative "base"
3
-
4
- module Redd
5
- module Clients
6
- # The client for a web-based flow (e.g. "login with reddit")
7
- class Web < Base
8
- # @!attribute [r] client_id
9
- attr_reader :client_id
10
-
11
- # @!attribute [r] redirect_uri
12
- attr_reader :redirect_uri
13
-
14
- # @param [Hash] options The options to create the client with.
15
- # @see Base#initialize
16
- # @see Redd.it
17
- def initialize(client_id, secret, redirect_uri, **options)
18
- @client_id = client_id
19
- @secret = secret
20
- @redirect_uri = redirect_uri
21
- super(**options)
22
- end
23
-
24
- # @param [String] state A random string to double-check later.
25
- # @param [Array<String>] scope The scope to request access to.
26
- # @param [:temporary, :permanent] duration
27
- # @return [String] The url to redirect the user to.
28
- def auth_url(state, scope = ["identity"], duration = :temporary)
29
- query = {
30
- response_type: "code",
31
- client_id: @client_id,
32
- redirect_uri: @redirect_uri,
33
- state: state,
34
- scope: scope.join(","),
35
- duration: duration
36
- }
37
-
38
- url = URI.join(auth_endpoint, "/api/v1/authorize")
39
- url.query = URI.encode_www_form(query)
40
- url.to_s
41
- end
42
-
43
- # Authorize using the code given.
44
- # @param [String] code The code from the get params.
45
- # @return [Access] The access given by reddit.
46
- def authorize!(code)
47
- response = auth_connection.post(
48
- "/api/v1/access_token",
49
- grant_type: "authorization_code",
50
- code: code,
51
- redirect_uri: @redirect_uri
52
- )
53
- @access = Access.new(response.body)
54
- reset_connection!
55
- end
56
- end
57
- end
58
- end
@@ -1,39 +0,0 @@
1
- require "hashie"
2
- require "forwardable"
3
-
4
- module Redd
5
- # A bunch of objects that can hold properties.
6
- module Objects
7
- # A base for all objects to inherit from.
8
- class Base < Hashie::Hash
9
- include Hashie::Extensions::MergeInitializer
10
- include Hashie::Extensions::MethodReader
11
- include Hashie::Extensions::MethodQuery
12
- include Hashie::Extensions::DeepMerge
13
-
14
- # The `delete` method is called `delete_path` because it conflicts with
15
- # Hash#delete.
16
- extend Forwardable
17
- def_delegators :@client, :get, :post, :put, :delete_path
18
-
19
- # @!attribute [r] client
20
- # @return [Clients::Base] The client that used to make requests.
21
- attr_reader :client
22
-
23
- # @param [Clients::Base] client The client instance.
24
- # @param [Hash] attributes A hash of attributes.
25
- def initialize(client, attributes = {})
26
- @client = client
27
- super(attributes)
28
- end
29
-
30
- # Define an alias for a property.
31
- # @param [Symbol] new_name The alias.
32
- # @param [Symbol] old_name The existing property.
33
- def self.alias_property(new_name, old_name)
34
- define_method(new_name) { send(old_name) }
35
- define_method(:"#{new_name}?") { send(old_name) }
36
- end
37
- end
38
- end
39
- end