NeonRAW 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/NeonRAW.gemspec +5 -4
  4. data/lib/NeonRAW/clients/base.rb +9 -7
  5. data/lib/NeonRAW/clients/base/objectbuilder.rb +8 -5
  6. data/lib/NeonRAW/clients/base/utilities.rb +36 -3
  7. data/lib/NeonRAW/clients/installed.rb +2 -0
  8. data/lib/NeonRAW/clients/script.rb +2 -0
  9. data/lib/NeonRAW/clients/web.rb +2 -0
  10. data/lib/NeonRAW/errors.rb +49 -58
  11. data/lib/NeonRAW/objects/all.rb +20 -2
  12. data/lib/NeonRAW/objects/comment.rb +19 -5
  13. data/lib/NeonRAW/objects/inboxcomment.rb +4 -1
  14. data/lib/NeonRAW/objects/me.rb +17 -28
  15. data/lib/NeonRAW/objects/modlogaction.rb +3 -0
  16. data/lib/NeonRAW/objects/modloguser.rb +3 -0
  17. data/lib/NeonRAW/objects/morecomments.rb +15 -12
  18. data/lib/NeonRAW/objects/multireddit.rb +4 -1
  19. data/lib/NeonRAW/objects/popular.rb +57 -0
  20. data/lib/NeonRAW/objects/privatemessage.rb +6 -3
  21. data/lib/NeonRAW/objects/rule.rb +3 -0
  22. data/lib/NeonRAW/objects/submission.rb +44 -5
  23. data/lib/NeonRAW/objects/subreddit.rb +9 -10
  24. data/lib/NeonRAW/objects/subreddit/flair.rb +7 -4
  25. data/lib/NeonRAW/objects/subreddit/moderation.rb +8 -8
  26. data/lib/NeonRAW/objects/subreddit/utilities.rb +44 -27
  27. data/lib/NeonRAW/objects/thing/inboxable.rb +1 -1
  28. data/lib/NeonRAW/objects/thing/moderateable.rb +7 -4
  29. data/lib/NeonRAW/objects/thing/refreshable.rb +1 -3
  30. data/lib/NeonRAW/objects/thing/votable.rb +1 -1
  31. data/lib/NeonRAW/objects/user.rb +51 -19
  32. data/lib/NeonRAW/objects/wikipage.rb +5 -2
  33. data/lib/NeonRAW/objects/wikipagerevision.rb +4 -1
  34. data/lib/NeonRAW/version.rb +1 -1
  35. metadata +25 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 970a9bd9f4ba95e3afe11b528a54eb5f0d295694
4
- data.tar.gz: d9e3a3db3364aeddf7c5ec1ffe3b33c20a7066ac
3
+ metadata.gz: 258edd85169bec9950d3370ab238087bac331a55
4
+ data.tar.gz: b1b1e668cf8f36d2a32597c2cbb5e6233e90e3ea
5
5
  SHA512:
6
- metadata.gz: b55607b79d30c14d976989585a4bd4b356a1487a16060623e3313df7c79c9b9bda13eea60b9b264a7c77c01e7bc9950f13c6b15ea1f45cc17992887ee8dd597b
7
- data.tar.gz: 705d32e891bf11ba949af1bce88c5f4c89a3804fc8357dd9a5240cc92c06832e1022a2fd30e9eb5febb21ec0ba1ed5ac5dfaa6ec9deb7e7c9db5eeb1436bc7f9
6
+ metadata.gz: e9be8fd451588cb2de972a140273f6cf57934319523d6141dddef76259213789e2124bbe4590353ad9243710eb4006af4dbf3e5d1e30e592b097553c61fa587e
7
+ data.tar.gz: b36701af359541c021ff5b2fef8e4efb768cb208e0a133fc2b70071ede231ff8c7f77f82de133112dc0922f81c1725617a15c57fab573000eee83a2bf02cd75e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## 0.1.7
2
+
3
+ * Changed Me#purge! so you can now edit archived posts.
4
+ * Error handling has been refactored a bit.
5
+ * Refactored how subreddit#submit buildings submission objects.
6
+ * Renamed Comment#link_id and Comment#parent_id to link_name and parent_name.
7
+ * \#distinguished renamed to distinguished!
8
+ * Subreddit#info was moved to client#info and returns a listing now.
9
+ * User/Me objects now have a \#username attribute and \#name returns the object's fullname now.
10
+
11
+ ### Added
12
+
13
+ * Content streams have been added to user and subreddit objects.
14
+ * Added support for /r/popular.
15
+ * You can now sticky distinguished comments.
16
+ * MoreComments object has a comment? method now to make sifting through listings easier.
17
+ * Hashie is now a runtime dependency.
18
+ * You can now fetch duplicates of submissions.
19
+ * Comments now have a permalink method.
20
+ * User objects have a \#suspended? attribute now.
21
+ * Subreddits have a \#moderators method now.
22
+
1
23
  ## 0.1.6
2
24
 
3
25
  ### Added
data/NeonRAW.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.email = ['sirneon618@gmail.com']
12
12
  spec.summary = 'A Reddit API wrapper for Ruby.'
13
13
  spec.description = 'SirNeon\'s wonderful API wrapper for Reddit. BETA'
14
- spec.homepage = 'https://gitlab.com/SirNeon/NeonRAW'
14
+ spec.homepage = 'https://github.com/SirNeon618/NeonRAW'
15
15
  spec.license = 'MPL-2.0'
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
17
  spec.bindir = 'exe'
@@ -19,8 +19,9 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
  spec.required_ruby_version = '>= 2.1.0'
21
21
 
22
- spec.add_development_dependency 'bundler', '~> 1.11'
23
- spec.add_development_dependency 'rake', '~> 10.0'
22
+ spec.add_development_dependency 'bundler', '~> 1.13'
23
+ spec.add_development_dependency 'rake', '~> 12.0'
24
24
  spec.add_development_dependency 'rspec', '~> 3.0'
25
- spec.add_dependency 'typhoeus', '~> 0.7'
25
+ spec.add_dependency 'typhoeus', '~> 1.0'
26
+ spec.add_dependency 'hashie', '~> 3.5'
26
27
  end
@@ -25,13 +25,15 @@ module NeonRAW
25
25
  end
26
26
 
27
27
  # Connects to Reddit for oAuth2 requests.
28
- # @!method api_connection(path, meth, params)
28
+ # @!method api_connection(path, meth, params, opts = {}, json = true)
29
29
  # @param path [String] The API path.
30
30
  # @param meth [Symbol] The request method.
31
31
  # @param params [Hash] The parameters.
32
32
  # @param opts [Hash] Optional parameters for the request body.
33
+ # @param json [Boolean] Whether or not the expected response will be JSON.
33
34
  # @return [Typhoeus::Response] Returns the response.
34
- def api_connection(path, meth, params, opts = {})
35
+ def api_connection(path, meth, params, opts = {}, json = true)
36
+ sleep(@ratelimit_reset) if @requests_remaining <= 0
35
37
  response = Typhoeus::Request.new(
36
38
  'https://oauth.reddit.com' + path,
37
39
  method: meth,
@@ -39,9 +41,9 @@ module NeonRAW
39
41
  headers: api_headers,
40
42
  params: params
41
43
  ).run
42
- error = assign_errors(response)
44
+ error = assign_errors(response, json)
43
45
  raise error unless error.nil?
44
- handle_ratelimit(response.headers)
46
+ update_ratelimit_info(response.headers)
45
47
  response
46
48
  end
47
49
 
@@ -59,7 +61,7 @@ module NeonRAW
59
61
  headers: { 'User-Agent' => @user_agent },
60
62
  params: params
61
63
  ).run
62
- error = assign_errors(response)
64
+ error = assign_errors(response, true)
63
65
  raise error unless error.nil?
64
66
  response
65
67
  end
@@ -77,7 +79,7 @@ module NeonRAW
77
79
  end
78
80
 
79
81
  # Requests data from Reddit.
80
- # @!method request_data(path, meth, params = {})
82
+ # @!method request_data(path, meth, params = {}, opts = {})
81
83
  # @param path [String] The API path to connect to.
82
84
  # @param meth [Symbol] The request method to use.
83
85
  # @param params [Hash] Parameters for the request.
@@ -102,7 +104,7 @@ module NeonRAW
102
104
  # via the request body.
103
105
  def request_nonjson(path, meth, params = {}, opts = {})
104
106
  refresh_access! if @access.expired?
105
- api_connection(path, meth, params, opts).body
107
+ api_connection(path, meth, params, opts, false).body
106
108
  end
107
109
  end
108
110
  end
@@ -4,6 +4,7 @@ require_relative '../../objects/me'
4
4
  require_relative '../../objects/multireddit'
5
5
  require_relative '../../objects/wikipage'
6
6
  require_relative '../../objects/all'
7
+ require_relative '../../objects/popular'
7
8
 
8
9
  module NeonRAW
9
10
  module Clients
@@ -38,13 +39,15 @@ module NeonRAW
38
39
  # Fetches a subreddit.
39
40
  # @!method subreddit(name)
40
41
  # @param name [String] The name of the subreddit.
41
- # @return [NeonRAW::Objects::Subreddit/All] Returns the subreddit/all
42
- # object.
42
+ # @return [NeonRAW::Objects::Subreddit/All/Popular] Returns the
43
+ # subreddit/all/popular object.
43
44
  def subreddit(name)
44
45
  if name == 'all'
45
46
  Objects::All.new(self)
47
+ elsif name == 'popular'
48
+ Objects::Popular.new(self)
46
49
  else
47
- data = request_data("/r/#{name}/about.json", :get)[:data]
50
+ data = request_data("/r/#{name}/about", :get)[:data]
48
51
  Objects::Subreddit.new(self, data)
49
52
  end
50
53
  end
@@ -54,7 +57,7 @@ module NeonRAW
54
57
  # @param name [String] The name of the user.
55
58
  # @return [NeonRAW::Objects::User] Returns the user object.
56
59
  def user(name)
57
- data = request_data("/user/#{name}/about.json", :get)[:data]
60
+ data = request_data("/user/#{name}/about", :get)[:data]
58
61
  Objects::User.new(self, data)
59
62
  end
60
63
 
@@ -162,7 +165,7 @@ module NeonRAW
162
165
  # @return [NeonRAW::Objects::WikiPage] Returns the wiki page object.
163
166
  def wikipage(page)
164
167
  params = { page: page }
165
- path = "/wiki/#{page}.json"
168
+ path = "/wiki/#{page}"
166
169
  data = request_data(path, :get, params)
167
170
  data[:data][:name] = page
168
171
  Objects::WikiPage.new(self, data[:data])
@@ -27,7 +27,7 @@ module NeonRAW
27
27
  # @option params :show [String] Literally the string 'all'.
28
28
  # @return [NeonRAW::Objects::Listing] Returns a listing of all the
29
29
  # subreddits.
30
- %w(popular new gold defaults).each do |type|
30
+ %w[popular new gold defaults].each do |type|
31
31
  define_method :"#{type}" do |params = { limit: 25 }|
32
32
  type.chop! if type == 'defaults'
33
33
  build_listing("/subreddits/#{type}", params)
@@ -45,7 +45,7 @@ module NeonRAW
45
45
  stack = comments.dup
46
46
  until stack.empty?
47
47
  comment = stack.shift
48
- if comment.is_a?(Objects::Comment)
48
+ if comment.is_a?(Objects::Comment) # MoreComments can be mixed in.
49
49
  replies = comment.replies
50
50
  stack = replies + stack unless replies.nil?
51
51
  end
@@ -58,8 +58,41 @@ module NeonRAW
58
58
  # @!method wikipages
59
59
  # @return [Array<String>] Returns a list of wiki pages.
60
60
  def wikipages
61
- request_data('/wiki/pages.json', :get)[:data]
61
+ request_data('/wiki/pages', :get)[:data]
62
62
  end
63
+
64
+ # Streams listing items continuously.
65
+ # @!method stream(path, params)
66
+ # @param path [String] The API path for the listing you want streamed.
67
+ # @param params [Hash] The optional parameters for the request.
68
+ # @return [Enumerator] Returns an enumerator for the streamed data.
69
+ def stream(path, params)
70
+ Enumerator.new do |data_stream|
71
+ before = params[:before]
72
+ loop do
73
+ params[:before] = before
74
+ listing = build_listing(path, params)
75
+ listing.each { |thing| data_stream << thing }
76
+ before = listing.first.name unless listing.empty?
77
+ sleep(1)
78
+ end
79
+ end
80
+ end
81
+
82
+ # Get info on a link/comment/subreddit.
83
+ # @!method info(params = {})
84
+ # @param params [Hash] The parameters.
85
+ # @option params :name [String] The fullname of the thing.
86
+ # @option params :url [String] The URL of the thing.
87
+ # @return [NeonRAW::Objects::Listing] Returns a listing with the items.
88
+ # @note :name and :url can take multiple fullnames separated by commas.
89
+ # @see https://www.reddit.com/dev/api#fullnames
90
+ def info(params = {})
91
+ params[:id] = params[:name]
92
+ params.delete(:name)
93
+ build_listing('/api/info', params)
94
+ end
95
+ private :stream
63
96
  end
64
97
  end
65
98
  end
@@ -8,6 +8,8 @@ module NeonRAW
8
8
  def initialize(client_id, redirect_uri, opts = {})
9
9
  @client_id = client_id
10
10
  @redirect_uri = redirect_uri
11
+ @requests_remaining = 1
12
+ @ratelimit_reset = 0
11
13
  @user_agent = opts[:user_agent] ||
12
14
  "Powered by NeonRAW v#{NeonRAW::VERSION}"
13
15
  end
@@ -10,6 +10,8 @@ module NeonRAW
10
10
  @client_id = client_id
11
11
  @secret = secret
12
12
  @redirect_uri = opts[:redirect_uri] || 'http://127.0.0.1:'
13
+ @requests_remaining = 1
14
+ @ratelimit_reset = 0
13
15
  @user_agent = opts[:user_agent] ||
14
16
  "Powered by NeonRAW v#{NeonRAW::VERSION}"
15
17
  authorize!
@@ -9,6 +9,8 @@ module NeonRAW
9
9
  @client_id = client_id
10
10
  @secret = secret
11
11
  @redirect_uri = redirect_uri
12
+ @requests_remaining = 1
13
+ @ratelimit_reset = 0
12
14
  @user_agent = opts[:user_agent] ||
13
15
  "Powered by NeonRAW v#{NeonRAW::VERSION}"
14
16
  end
@@ -1,35 +1,43 @@
1
1
  # rubocop:disable all
2
2
 
3
+ require 'hashie'
4
+
3
5
  module NeonRAW
4
6
  # Methods and classes for handling errors.
5
7
  module Errors
6
8
  # Reads the HTTP status of the Typhoeus response and gives an exception to
7
9
  # raise.
8
- # @!method assign_errors(response)
10
+ # @!method assign_errors(response, json)
9
11
  # @param response [Typhoeus::Response] The response object.
12
+ # @param json [Boolean] Whether or not the response is JSON.
10
13
  # @return [StandardError, nil] Returns either the exception or nil if there
11
14
  # is none.
12
- def assign_errors(response)
15
+ def assign_errors(response, json)
13
16
  code = response.code
14
17
  body = response.body
15
18
  case code
16
19
  when 200
17
- case body
18
- when /access_denied/i then OAuth2AccessDenied
19
- when /unsupported_response_type/i then InvalidResponseType
20
- when /unsupported_grant_type/i then InvalidGrantType
21
- when /invalid_scope/i then InvalidScope
22
- when /invalid_request/i then InvalidRequest
23
- when /invalid_grant/i then ExpiredCode
24
- when /wrong_password/i then InvalidCredentials
25
- when /bad_captcha/i then InvalidCaptcha
26
- when /ratelimit/i then RateLimited
27
- when /quota_filled/i then QuotaFilled
28
- when /bad_css_name/i then InvalidClassName
29
- when /too_old/i then Archived
30
- when /too_much_flair_css/i then TooManyClassNames
31
- when /user_required/i then AuthenticationRequired
32
- when /bad_flair_target/i then BadFlairTarget
20
+ if json
21
+ errors = JSON.parse(body, symbolize_names: true)
22
+ errors.extend(Hashie::Extensions::DeepFind)
23
+ errors = errors.deep_find(:error) || errors.deep_find(:errors)
24
+ errors = errors.first if errors.is_a?(Array)
25
+ case errors
26
+ when /access_denied/i then OAuth2AccessDenied
27
+ when /unsupported_response_type/i then InvalidResponseType
28
+ when /unsupported_grant_type/i then InvalidGrantType
29
+ when /invalid_scope/i then InvalidScope
30
+ when /invalid_request/i then InvalidRequest
31
+ when /invalid_grant/i then InvalidCredentials
32
+ when /wrong_password/i then InvalidCredentials
33
+ when /bad_captcha/i then InvalidCaptcha
34
+ when /ratelimit/i then RateLimited
35
+ when /quota_filled/i then QuotaFilled
36
+ when /bad_css_name/i then InvalidClassName
37
+ when /too_much_flair_css/i then TooManyClassNames
38
+ when /user_required/i then AuthenticationRequired
39
+ when /bad_flair_target/i then BadFlairTarget
40
+ end
33
41
  end
34
42
  when 302 then UnexpectedRedirect
35
43
  when 400 then BadRequest
@@ -57,39 +65,30 @@ module NeonRAW
57
65
  # @param data [Array, Hash] The data.
58
66
  def parse_errors(data)
59
67
  # handles returns from toggleable methods
60
- assign_data_errors([]) if data.empty?
61
- if data.is_a?(Array) # handles returns from some flair methods
62
- # handles multireddits
63
- return assign_data_errors([]) unless data[0].key?(:errors)
64
- messages = []
65
- errors = data[0][:errors]
66
- errors.each { |_key, error| messages << error } unless errors.empty?
67
- assign_data_errors(messages)
68
- elsif data.key?(:json) # handles pretty much everything else
69
- assign_data_errors([]) unless data[:json].key?(:errors)
70
- if data[:json][:errors].is_a?(Array)
71
- errors = data[:json][:errors][0] || []
72
- assign_data_errors(errors)
73
- else
74
- errors = data[:json][:errors] || []
75
- assign_data_errors(errors)
76
- end
77
- elsif data.key?(:errors) # handles image uploading
78
- errors = data[:errors] || []
79
- assign_data_errors(errors)
80
- elsif data.key?(:jquery) # handles submitting submissions
81
- errors = data[:jquery][-7][3]
82
- assign_data_errors(errors)
68
+ return assign_data_errors([]) if data.empty?
69
+ data.extend(Hashie::Extensions::DeepFind)
70
+
71
+ errors = data.deep_find(:errors)
72
+ return assign_data_errors([]) if errors.nil? || errors.empty?
73
+ return assign_data_errors(errors.first) if errors.first.is_a?(Array)
74
+ if errors.is_a?(Hash)
75
+ messages = errors.map { |_key, error| error } # handles multireddits
76
+ return assign_data_errors(messages)
83
77
  end
78
+ return assign_data_errors(errors)
84
79
  end
85
80
 
86
- # Checks data for any errors that wouldn't have otherwise thrown an
81
+ # Checks data for any errors that wouldn't have otherwise raised an
87
82
  # exception.
88
83
  # @!method assign_data_errors(errors)
89
84
  # @param errors [Array<String>] The errors.
90
85
  def assign_data_errors(errors)
91
86
  return nil if errors.empty?
92
- error = errors.first
87
+ error = if errors.first == :row # handles flair errors
88
+ errors[1]
89
+ else
90
+ errors.first
91
+ end
93
92
  case error
94
93
  when /improperly formatted row/i then BadFlairRowFormat
95
94
  when /no_subject/i then NoSubject
@@ -121,16 +120,16 @@ module NeonRAW
121
120
  when /unable to resolve user/i then CouldntResolveUser
122
121
  when /sr_rule_exists/i then RuleExists
123
122
  when /sr_rule_too_many/i then TooManyRules
123
+ when /too_old/i then Archived
124
124
  end
125
125
  end
126
126
 
127
127
  # Manages the API ratelimit for requesting stuff from Reddit.
128
- # @!method handle_ratelimit(headers)
128
+ # @!method update_ratelimit_info(headers)
129
129
  # @param headers [Hash] The Typhoeus response headers.
130
- def handle_ratelimit(headers)
131
- requests_remaining = headers['X-Ratelimit-Remaining'].to_i
132
- ratelimit_reset = headers['X-Ratelimit-Reset'].to_i
133
- sleep(ratelimit_reset) if requests_remaining <= 0
130
+ def update_ratelimit_info(headers)
131
+ @requests_remaining = headers['X-Ratelimit-Remaining'].to_i
132
+ @ratelimit_reset = headers['X-Ratelimit-Reset'].to_i
134
133
  end
135
134
 
136
135
  # That URL has already been submitted.
@@ -142,7 +141,7 @@ module NeonRAW
142
141
 
143
142
  # Thing is archived and can't be edited/replied to.
144
143
  class Archived < StandardError
145
- def initialize(msg = 'This thing is too old to edit/reply to.')
144
+ def initialize(msg = 'This thing is too old to reply to.')
146
145
  super(msg)
147
146
  end
148
147
  end
@@ -232,14 +231,6 @@ module NeonRAW
232
231
  end
233
232
  end
234
233
 
235
- # You already received an access token using this code. They're only good
236
- # for one use.
237
- class ExpiredCode < StandardError
238
- def initialize(msg = 'The code used to get the access token has expired.')
239
- super(msg)
240
- end
241
- end
242
-
243
234
  # Only gold-only subreddits can do that.
244
235
  class GoldOnlySrRequired < StandardError
245
236
  def initialize(msg = 'Only gold-only subreddits can do that.')
@@ -284,7 +275,7 @@ module NeonRAW
284
275
 
285
276
  # Your username/password is wrong.
286
277
  class InvalidCredentials < StandardError
287
- def initialize(msg = 'Invalid username/password')
278
+ def initialize(msg = 'Invalid username/password/client id/secret')
288
279
  super(msg)
289
280
  end
290
281
  end
@@ -28,12 +28,30 @@ module NeonRAW
28
28
  # @option params :limit [1..1000] The number of items to fetch.
29
29
  # @option params :show [String] Literally the string 'all'.
30
30
  # @return [NeonRAW::Objects::Listing] Returns the listing object.
31
- %w(hot rising top old new controversial comments).each do |type|
31
+ %w[hot rising top old new controversial comments].each do |type|
32
32
  define_method :"#{type}" do |params = { limit: 25 }|
33
- path = "/r/all/#{type}/.json"
33
+ path = "/r/all/#{type}"
34
34
  @client.send(:build_listing, path, params)
35
35
  end
36
36
  end
37
+
38
+ # Streams content from /r/all.
39
+ # @!method stream(queue, params = { limit: 25 })
40
+ # @param queue [Symbol] The queue to get data from [hot, top, new,
41
+ # controversial, gilded, comments]
42
+ # @param params [Hash] The parameters for the request.
43
+ # @option params :t [String] Time for relevant sorting [hour, day, week,
44
+ # month, year, all]
45
+ # @option params :after [String] The name of the next data block.
46
+ # @option params :before [String] The name of the previous data block.
47
+ # @option params :count [Integer] The number of items already in the
48
+ # listing.
49
+ # @option params :limit [1..1000] The number of items to fetch.
50
+ # @option params :show [String] Literally the string 'all'.
51
+ # @return [Enumerator] Returns an enumerator for the streamed data.
52
+ def stream(queue, params = { limit: 25 })
53
+ @client.send(:stream, "/r/all/#{queue}", params)
54
+ end
37
55
  end
38
56
  end
39
57
  end