redd 0.8.5 → 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c41f2214b01198dd4f0a44f243bc73364af33b6c
4
- data.tar.gz: a33587b7a36a41c11b3a4faa51814af24d86565f
3
+ metadata.gz: 9143e5925475c5e0143ac3aacb6c10666986c18f
4
+ data.tar.gz: 6c7be0b33836c21aa36765937795cabe6c7aa8e6
5
5
  SHA512:
6
- metadata.gz: ba2ecdabcf4c991710428b08e57437a00e0b5c85a0164d7786fe4363f65f51617e544519393bc48cae3672debaa0b8c2ea4743aae67ae1e23dedfee32e24dfe9
7
- data.tar.gz: ef71ea37b3375b7328f9f6f6298d8cb867dda0aa38eec6b0480fcc12063b935a6fcfea99216d0bac5563577fd8a86c98697ef1a697ebfab09365a3e60390e3a5
6
+ metadata.gz: 541cffb6f3ad7b15b2d1560b30ae400d7d9db898290c7a252ee9ad2c7bae3d1c8aa9591d202e9f32190f28e0fe37539b33443a0d8f5adccc0c3af8aa84f5def4
7
+ data.tar.gz: 707d66c874d7c74fbbe1710d52247f5dbc4e5c5e88fc4ab66b7c739075a30b4cfeb28d59965d762779b61d3bd9ca323432b6e543316f24237f916b694612d12c
data/README.md CHANGED
@@ -1,8 +1,7 @@
1
1
  <div align="center">
2
2
  <p>
3
3
  <!-- Redd -->
4
- <img src="logo.png" width="500"><br>
5
-
4
+ <img src="https://raw.githubusercontent.com/avinashbot/redd/master/logo.png" width="500"><br>
6
5
  <!-- Badges -->
7
6
  <a href="https://rubygems.org/gems/redd">
8
7
  <img src="http://img.shields.io/gem/v/redd.svg?style=flat-square" alt="Gem Version">
@@ -14,7 +13,6 @@
14
13
  <img src="http://img.shields.io/gem/dt/redd.svg?style=flat-square" alt="Gem Downloads">
15
14
  </a>
16
15
  </p>
17
-
18
16
  <!-- Intro Text -->
19
17
  <p>
20
18
  <strong>Redd</strong> is a <strong>batteries-included</strong>
@@ -46,31 +44,67 @@ session = Redd.it(
46
44
 
47
45
  session.subreddit('all').comment_stream do |comment|
48
46
  if comment.body.include?('roll a dice')
49
- comment.reply("I just rolled a dice! It's a #{rand(1..6)}!")
47
+ comment.reply("It's a #{rand(1..6)}!")
50
48
  elsif comment.body.include?('flip a coin')
51
- comment.reply("I just flipped a coin! It's a #{%w(heads tails).sample}!")
49
+ comment.reply("It's a #{%w(heads tails).sample}!")
50
+ end
51
+ end
52
+ ```
53
+
54
+ ```ruby
55
+ require 'sinatra'
56
+ require 'redd/middleware'
57
+
58
+ use Rack::Session::Cookie
59
+ use Redd::Middleware,
60
+ user_agent: 'Redd:Username App:v1.0.0 (by /u/Mustermind)',
61
+ client_id: 'PQgS0UaX9l70oQ',
62
+ secret: 'PsF_kVZrW8nSVCG5kNsIgl-AaXE',
63
+ redirect_uri: 'http://localhost:4567/auth/reddit/callback',
64
+ scope: %w(identity),
65
+ via: '/auth/reddit'
66
+
67
+ get '/' do
68
+ reddit = request.env['redd.session']
69
+
70
+ if reddit
71
+ "Hello /u/#{reddit.me.name}! <a href='/logout'>Logout</a>"
72
+ else
73
+ "<a href='/auth/reddit'>Sign in with reddit</a>"
52
74
  end
53
75
  end
76
+
77
+ get '/auth/reddit/callback' do
78
+ redirect to('/') unless request.env['redd.error']
79
+ "Error: #{request.env['redd.error'].message} (<a href='/'>Back</a>)"
80
+ end
81
+
82
+ get '/logout' do
83
+ request.env['redd.session'] = nil
84
+ redirect to('/')
85
+ end
54
86
  ```
55
87
 
56
88
  ### FAQ
57
89
 
58
- #### Is that bot fully functional?
90
+ #### Are those examples fully functional?
59
91
  **Yes**, that's all there is to it! You don't need to handle rate-limiting, refresh access tokens or protect against issues on reddit's end (like 5xx errors).
60
92
 
61
93
  #### Where can I find the documentation?
62
94
 
63
95
  [**Gem**](http://www.rubydoc.info/gems/redd/Redd/Models/Session) / [**GitHub**](http://www.rubydoc.info/github/avinashbot/redd/master/Redd/Models/Session)
64
96
 
97
+ #### Where can I ask for help if I'm having issues?
98
+ Check out the [**official subreddit**](https://www.reddit.com/r/Redd) or raise a [**GitHub issue**](https://github.com/avinashbot/redd/issues/new).
99
+
65
100
  #### How do I request a feature / contribute?
66
101
 
67
- - The quickest way to get a feature into Redd is to raise a GitHub issue.
102
+ - The quickest way to get a feature into Redd is to raise a [**GitHub issue**](https://github.com/avinashbot/redd/issues/new).
68
103
  - Pull requests are also appreciated!
69
104
  - Don't hesitate! There are no stupid questions!
70
105
 
71
106
  #### How can I contact you?
72
107
  [Reddit](https://www.reddit.com/message/compose/?to=Mustermind) /
73
- [GitHub](https://github.com/avinashbot/redd/issues/new) /
74
108
  [Email](mailto:avinash@dwarapu.me)
75
109
 
76
110
  ---
@@ -87,7 +87,7 @@ module Redd
87
87
  # If access is nil, panic
88
88
  raise 'client access is nil, try calling #authenticate' if @access.nil?
89
89
  # Refresh access if auto_refresh is enabled
90
- refresh if @access.expired? && @auto_refresh
90
+ refresh if @auto_refresh && @access.permanent? && @access.expired?
91
91
  end
92
92
 
93
93
  def handle_retryable_errors
data/lib/redd/error.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Redd
4
+ # An error raised by {Redd::Middleware} when there was an error returned by reddit.
5
+ class TokenRetrievalError < StandardError; end
6
+
4
7
  # An error with the API.
5
8
  class APIError < StandardError
6
9
  attr_reader :response, :name
@@ -42,6 +45,9 @@ module Redd
42
45
  # Returned when reddit raises a 404 error.
43
46
  class NotFound < ResponseError; end
44
47
 
48
+ # Too many requests and not enough rate limiting.
49
+ class TooManyRequests < ResponseError; end
50
+
45
51
  # An unknown error on reddit's end. Usually fixed with a retry.
46
52
  class ServerError < ResponseError; end
47
53
  end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack'
4
+ require 'securerandom'
5
+
6
+ require_relative '../redd'
7
+
8
+ module Redd
9
+ # Rack middleware.
10
+ class Middleware
11
+ # @param opts [Hash] the options to create the object with
12
+ # @option opts [String] :user_agent your app's *unique* and *descriptive* user agent
13
+ # @option opts [String] :client_id the client id of your app
14
+ # @option opts [String] :redirect_uri the provided redirect URI
15
+ # @option opts [String] :secret ('') the app secret (for the web type)
16
+ # @option opts [Array<String>] :scope (['identity']) a list of scopes to request
17
+ # @option opts ['temporary', 'permanent'] :duration ('permanent') the duration to request the
18
+ # code for.
19
+ # @option opts [Boolean] :auto_refresh (true) allow refreshing a permanent access automatically
20
+ # (only if duration is 'permanent')
21
+ # @option opts [String] :via ('/auth/reddit') the relative path in the application that
22
+ # redirects a user to reddit
23
+ def initialize(app, opts = {})
24
+ @app = app
25
+ strategy_opts = opts.select { |k| %i(user_agent client_id secret redirect_uri).include?(k) }
26
+ @strategy = Redd::AuthStrategies::Web.new(strategy_opts)
27
+
28
+ @user_agent = opts.fetch(:user_agent, "Redd:Web Application:v#{Redd::VERSION} (by unknown)")
29
+ @client_id = opts.fetch(:client_id)
30
+ @redirect_uri = opts.fetch(:redirect_uri)
31
+ @scope = opts.fetch(:scope, ['identity'])
32
+ @duration = opts.fetch(:duration, 'permanent')
33
+ @auto_refresh = opts.fetch(:auto_refresh, true) && @duration == 'permanent'
34
+ @via = opts.fetch(:via, '/auth/reddit')
35
+ end
36
+
37
+ def call(env)
38
+ # This is done for thread safety so that each thread has its own copy
39
+ # of the middleware logic.
40
+ dup._call(env)
41
+ end
42
+
43
+ protected
44
+
45
+ def _call(env)
46
+ @request = Rack::Request.new(env)
47
+ return redirect_to_reddit! if @request.path == @via
48
+
49
+ before_call
50
+ response = @app.call(env)
51
+ after_call
52
+ response
53
+ end
54
+
55
+ private
56
+
57
+ # Creates a unique state and redirects the user to reddit for authentication.
58
+ def redirect_to_reddit!
59
+ state = SecureRandom.urlsafe_base64
60
+ url = Redd.url(
61
+ client_id: @client_id,
62
+ redirect_uri: @redirect_uri,
63
+ scope: @scope,
64
+ duration: @duration,
65
+ state: state
66
+ )
67
+ @request.session[:redd_state] = state
68
+ [302, { 'Location' => url }, []]
69
+ end
70
+
71
+ # Do any setup before calling the rest of the application.
72
+ def before_call
73
+ # Convert the code to an access token if returning from authentication.
74
+ create_session! if @request.base_url + @request.path == @redirect_uri
75
+ # Clear the state for any other request.
76
+ @request.session.delete(:redd_state)
77
+ # Load a Session model from the access token in the user's cookies.
78
+ @request.env['redd.session'] = (@request.session[:redd_session] ? parse_session : nil)
79
+ end
80
+
81
+ # Do any cleanup or changes after calling the application.
82
+ def after_call
83
+ env_session = @request.env['redd.session']
84
+ if env_session && env_session.client.access
85
+ # Make sure to flush any changes made to the Session client to the browser.
86
+ @request.session[:redd_session] = env_session.client.access.to_h
87
+ else
88
+ # Clear the session if the app explicitly set 'redd.session' to nil.
89
+ @request.session.delete(:redd_session)
90
+ end
91
+ end
92
+
93
+ # Assigns a single string representing a reddit authentication errors.
94
+ def handle_token_error
95
+ message = nil
96
+ message = 'invalid_state' if @request.GET['state'] != @request.session[:redd_state]
97
+ message = @request.GET['error'] if @request.GET['error']
98
+ raise Redd::TokenRetrievalError, message if message
99
+ end
100
+
101
+ # Store the access token and other details in the user's browser, assigning any errors to
102
+ # the 'redd.error' env variable.
103
+ def create_session!
104
+ # Skip authorizing if there was an error from the authorization.
105
+ handle_token_error
106
+ # Try to get a code (the rescue block will also prevent crazy crashes)
107
+ access = @strategy.authenticate(@request.GET['code'])
108
+ @request.session[:redd_session] = access.to_h
109
+ rescue Redd::TokenRetrievalError, Redd::ResponseError => error
110
+ @request.env['redd.error'] = error
111
+ end
112
+
113
+ # Return a {Redd::Models::Session} based on the hash saved into the browser's session.
114
+ def parse_session
115
+ client = Redd::APIClient.new(
116
+ @strategy,
117
+ user_agent: @user_agent, limit_time: 0, auto_refresh: @auto_refresh
118
+ )
119
+ client.access = Redd::Models::Access.new(@strategy, @request.session[:redd_session])
120
+ Redd::Models::Session.new(client)
121
+ end
122
+ end
123
+ end
@@ -5,10 +5,27 @@ require_relative 'basic_model'
5
5
  module Redd
6
6
  module Models
7
7
  # Models access_token and related keys.
8
+ # @note This model also supports an additional key, called `:created_at` which is a UNIX time
9
+ # representing the time the access was created. The default value is the time the object was
10
+ # initialized.
8
11
  class Access < BasicModel
12
+ # Create a non-lazily initialized Access.
13
+ # @param client [Object] (deprecated) the client to create the object with
14
+ # @param attributes [Hash] the Access's attributes
15
+ # @example
16
+ # access = Redd::Models::Access.new(access_token: ...)
17
+ def initialize(client = nil, attributes = {})
18
+ if client.is_a?(Hash)
19
+ super(nil, client)
20
+ else
21
+ super(client, attributes)
22
+ end
23
+ end
24
+
9
25
  def expired?(grace_period = 60)
26
+ # We're not sure, so we just assume it hasn't expired.
10
27
  return false unless @attributes[:expires_in]
11
- Time.now > @created_at + (@attributes[:expires_in] - grace_period)
28
+ Time.now.to_i > @attributes[:created_at] + (@attributes[:expires_in] - grace_period)
12
29
  end
13
30
 
14
31
  def permanent?
@@ -18,7 +35,7 @@ module Redd
18
35
  private
19
36
 
20
37
  def after_initialize
21
- @created_at = Time.now
38
+ @attributes[:created_at] ||= Time.now.to_i
22
39
  end
23
40
  end
24
41
  end
@@ -14,6 +14,7 @@ module Redd
14
14
  400 => Redd::BadRequest,
15
15
  403 => Redd::Forbidden,
16
16
  404 => Redd::NotFound,
17
+ 429 => Redd::TooManyRequests,
17
18
  500 => Redd::ServerError,
18
19
  502 => Redd::ServerError,
19
20
  503 => Redd::ServerError,
data/lib/redd/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Redd
4
- VERSION = '0.8.5'
4
+ VERSION = '0.8.6'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.5
4
+ version: 0.8.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Avinash Dwarapu
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-14 00:00:00.000000000 Z
11
+ date: 2017-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http
@@ -162,6 +162,7 @@ files:
162
162
  - lib/redd/auth_strategies/web.rb
163
163
  - lib/redd/client.rb
164
164
  - lib/redd/error.rb
165
+ - lib/redd/middleware.rb
165
166
  - lib/redd/models/access.rb
166
167
  - lib/redd/models/basic_model.rb
167
168
  - lib/redd/models/comment.rb