NeonRAW 0.1.6 → 0.1.7

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 (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