ruqqus 1.1.0 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb4087c263dae6ef0708a07d756733ea19f95e1e6f02983c658c9cfb40a2f475
4
- data.tar.gz: d9d71b14897ec30ea1f5356916d1eb2cc432db02e7c007fb5f8efcea3a3f37fa
3
+ metadata.gz: aef052e6ba89775ce82f8d1474b12b74a0ec84260ae5abcb41b2c098e1befbf2
4
+ data.tar.gz: 79385f50df81574939d19746d8f36e9e66e075553ec980c5daa0d411cd27e60c
5
5
  SHA512:
6
- metadata.gz: f5864e192920e53c59921cf9bdb7d2f2acaf261d77ed4c0d3184a7f601cf967cf87f358c4b396705b8366fcda6ed3d54a9a125b0f13e7e139d0ce3ff0f67535e
7
- data.tar.gz: 73d7d5b8a4551a7bc83efd54b8be72107a010f4023dc9784396dddce8ceae4808049cc6fed37487b07e259b6efc41a1ff75d8184bf5ce28f18f8c261e2ffd0e4
6
+ metadata.gz: 2039cc29cfde59766c5d249c74b49c132b709db04553848d5e8915df242dc276f2b550289bc43b58b322987851e747feede7dbe7ee89ba5ef9f0e6c096afa546
7
+ data.tar.gz: d62ca8506d89f4c8e772fa24daf57c0dca3e6d90c57a473a82eb6b0e89c10ab564058c198c5fb75d038c573b8792945d51633e440857322b2d64f8dc6867386c
data/.gitignore CHANGED
@@ -168,3 +168,4 @@ modules.xml
168
168
 
169
169
  # JSON file containing the token for test user during development
170
170
  /token.json
171
+ /test.rb
@@ -2,6 +2,12 @@
2
2
 
3
3
  Documentation for library API changes.
4
4
 
5
+ ## Version 1.1.1
6
+
7
+ * BUGFIX: Added acceptance of variable args to `Token#to_json`
8
+ * BUGFIX: Fixed regex validator in `ruqqus-oauth` for the client ID
9
+ * Separated the client ID/secret from the `Token` class, and placed within `Client` to now handle this logic
10
+
5
11
  ## Version 1.1.0
6
12
 
7
13
  * Implemented `Ruqqus::Token` class to handle OAuth2 authentication
data/README.md CHANGED
@@ -67,7 +67,11 @@ code = 'XXXXXX' # The generated code (or the one you obtained via tradi
67
67
 
68
68
  # You must implement a responsible way of storing this token for reuse.
69
69
  token = Ruqqus::Token.new(client_id, client_secret, code)
70
- client = Ruqqus::Client.new(token)
70
+ client = Ruqqus::Client.new(client_id, client_secret, token)
71
+
72
+ # Alternatively, you can create a new token and a client with a single call.
73
+ # This will perform the "grant" action of the code while creating it automatically
74
+ client = Ruqqus::Client.new(client_id, client_secret, code)
71
75
  ```
72
76
 
73
77
  The token will automatically refresh itself as-needed, but you will need to handle storing its new value for repeated
@@ -75,12 +79,16 @@ uses. To facilitate this and make it easier, there is a callback that can be sub
75
79
  time the access key is updated.
76
80
 
77
81
  ```ruby
82
+ # Load an existing token that has already been authorized
78
83
  token = Ruqqus::Token.load_json('./token.json')
79
- token.on_refresh do |t|
84
+
85
+ # Create your client
86
+ client = Ruqqus::Client.new(client_id, client_secret, token)
87
+
88
+ # Set the callback block to automatically update the saved token when it refreshes
89
+ client.token_refreshed do |t|
80
90
  t.save_json('./token.json')
81
91
  end
82
-
83
- client = Ruqqus::Client.new(token)
84
92
  ```
85
93
 
86
94
  The token obtains sensitive material, and due to the security issues of storing it in plain text, this functionality is
@@ -89,7 +97,7 @@ and where you store this information so that it is not compromised.
89
97
 
90
98
  ## Usage
91
99
 
92
- See the [documentation](https://www.rubydoc.info/gems/ruqqus) for a complete API reference.
100
+ See the [documentation](https://www.rubydoc.info/gems/ruqqus) for a complete API reference (100% coverage).
93
101
 
94
102
  ### Features
95
103
 
@@ -41,11 +41,11 @@ class RuqqusOAuth
41
41
  end
42
42
 
43
43
  def ask_client_id(prompt)
44
- prompt.ask('Client ID: ') { |q| q.validate(/^[A-Fa-f0-9]+$/, "Invalid client ID") }
44
+ prompt.ask('Client ID: ') { |q| q.validate(/^[A-Za-z0-9_-]+$/, "Invalid client ID") }
45
45
  end
46
46
 
47
47
  def ask_client_secret(prompt)
48
- prompt.ask('Client Secret: ') { |q| q.validate(/^[A-Fa-f0-9]+$/, "Invalid client secret") }
48
+ prompt.ask('Client Secret: ') { |q| q.validate(/^[A-Za-z0-9_-]+$/, "Invalid client secret") }
49
49
  end
50
50
 
51
51
  def ask_client_redirect(prompt)
@@ -9,7 +9,7 @@ module Ruqqus
9
9
 
10
10
  ##
11
11
  # The user-agent the client identified itself as.
12
- USER_AGENT = "ruqqus-ruby/#{Ruqqus::VERSION} (efreed09@gmail.com)".freeze
12
+ USER_AGENT = "ruqqus-ruby/#{Ruqqus::VERSION}".freeze
13
13
 
14
14
  ##
15
15
  # A collection of valid scopes that can be authorized.
@@ -20,22 +20,39 @@ module Ruqqus
20
20
  DEFAULT_HEADERS = { 'User-Agent': USER_AGENT, 'Accept': 'application/json', 'Content-Type': 'application/json' }.freeze
21
21
 
22
22
  ##
23
- # @return [Token] the OAuth2 token that grants the client authentication.
24
- attr_reader :token
23
+ # @!attribute [rw] token
24
+ # @return [Token] the OAuth2 token that grants the client authentication.
25
25
 
26
26
  ##
27
27
  # @!attribute [r] identity
28
28
  # @return [User] the authenticated user this client is performing actions as.
29
29
 
30
30
  ##
31
- # Creates a new instance of the {Client} class.
32
- #
33
- # @param token [Token] a valid access token to authorize the client.
34
- def initialize(token)
35
- @token = token || raise(ArgumentError, 'token cannot be nil')
31
+ # @overload initialize(client_id, client_secret, token)
32
+ # Creates a new instance of the {Client} class with an existing token for authorization.
33
+ # @param client_id [String] the client ID of your of your application, issued after registration on Ruqqus.
34
+ # @param client_secret [String] the client secret of your of your application, issued after registration on Ruqqus.
35
+ # @param token [Token] a valid access token that has previously been granted access for the client.
36
+ #
37
+ # @overload initialize(client_id, client_secret, code)
38
+ # Creates a new instance of the {Client} class with an existing token for authorization.
39
+ # @param client_id [String] the client ID of your of your application, issued after registration on Ruqqus.
40
+ # @param client_secret [String] the client secret of your of your application, issued after registration on Ruqqus.
41
+ # @param code [String] a the code from the Oauth2 redirect to create a new {Token} and grant access to it.
42
+ def initialize(client_id, client_secret, token)
43
+ @client_id = client_id || raise(ArgumentError, 'client ID cannot be nil')
44
+ @client_secret = client_secret || raise(ArgumentError, 'client secret cannot be nil')
45
+
46
+ @token = token.is_a?(Token) ? token : Token.new(client_id, client_secret, token.to_s)
36
47
  @session = nil
37
48
  end
38
49
 
50
+ attr_reader :token
51
+
52
+ def token=(token)
53
+ @token = token || raise(ArgumentError, 'token cannot be nil')
54
+ end
55
+
39
56
  # @!group Object Querying
40
57
 
41
58
  ##
@@ -358,10 +375,25 @@ module Ruqqus
358
375
 
359
376
  # @!endgroup Object Enumeration
360
377
 
378
+ ##
379
+ # @return [User] the authenticated user this client is performing actions as.
361
380
  def identity
362
381
  @me ||= User.from_json(http_get(Routes::IDENTITY))
363
382
  end
364
383
 
384
+ ##
385
+ # @overload token_refreshed(&block)
386
+ # Sets a callback to be invoked when the token is refreshed, and a new access token is assigned.
387
+ # @yieldparam token [Token] yields the newly refreshed {Token} to the block.
388
+ #
389
+ # @overload token_refreshed
390
+ # When called without a block, clears any callback that was previously assigned.
391
+ #
392
+ # @return [void]
393
+ def token_refreshed(&block)
394
+ @refreshed = block_given? ? block : nil
395
+ end
396
+
365
397
  private
366
398
 
367
399
  ##
@@ -444,7 +476,7 @@ module Ruqqus
444
476
  # @return [Hash] the response deserialized into a JSON hash.
445
477
  # @see http_post
446
478
  def http_get(uri, header = nil)
447
- @token.refresh if @token && @token.expired?
479
+ refresh_token
448
480
  header ||= headers
449
481
  response = RestClient.get(uri.chomp('/'), header)
450
482
  @session = response.cookies['session_ruqqus'] if response.cookies['session_ruqqus']
@@ -463,12 +495,21 @@ module Ruqqus
463
495
  # @return [Hash] the response deserialized into a JSON hash.
464
496
  # @see http_get
465
497
  def http_post(uri, params = {}, header = nil)
466
- @token.refresh if @token && @token.expired?
498
+ refresh_token
467
499
  header ||= headers
468
500
  response = RestClient.post(uri.chomp('/'), params, header)
469
501
  @session = response.cookies['session_ruqqus'] if response.cookies['session_ruqqus']
470
502
  raise(Ruqqus::Error, 'HTTP request failed') if response.code < 200 || response.code >= 300
471
503
  JSON.parse(response, symbolize_names: response.body)
472
504
  end
505
+
506
+ ##
507
+ # @api private
508
+ # Checks if token is expired, and refreshes if so, calling the {#token_refreshed} block as if defined.
509
+ def refresh_token
510
+ return unless @token.expired?
511
+ @token.refresh(@client_id, @client_secret)
512
+ @refreshed&.call(@token)
513
+ end
473
514
  end
474
515
  end
@@ -37,15 +37,11 @@ module Ruqqus
37
37
  headers = { 'User-Agent': Client::USER_AGENT, 'Accept': 'application/json', 'Content-Type': 'application/json' }
38
38
  params = { code: code, client_id: client_id, client_secret: client_secret, grant_type: 'code', permanent: persist }
39
39
  resp = RestClient.post('https://ruqqus.com/oauth/grant', params, headers )
40
-
41
- @client_id = client_id
42
- @client_secret = client_secret
43
40
  @data = JSON.parse(resp.body, symbolize_names: true)
44
41
 
45
42
  raise(Ruqqus::Error, 'failed to grant access for token') if @data[:oauth_error]
46
43
  end
47
44
 
48
-
49
45
  def access_token
50
46
  @data[:access_token]
51
47
  end
@@ -70,32 +66,14 @@ module Ruqqus
70
66
  # Refreshes the access token and resets its time of expiration.
71
67
  #
72
68
  # @return [void]
73
- def refresh
69
+ def refresh(client_id, client_secret)
74
70
  headers = { 'User-Agent': Client::USER_AGENT, Authorization: "Bearer #{access_token}" }
75
- params = { client_id: @client_id, client_secret: @client_secret, refresh_token: refresh_token, grant_type: 'refresh' }
71
+ params = { client_id: client_id, client_secret: client_secret, refresh_token: refresh_token, grant_type: 'refresh' }
76
72
  resp = RestClient.post('https://ruqqus.com/oauth/grant', params, headers )
77
73
 
78
74
  data = JSON.parse(resp.body, symbolize_names: true)
79
75
  raise(Ruqqus::Error, 'failed to refresh authentication token') unless resp.code == 200 || data[:oauth_error]
80
76
  @data.merge!(data)
81
- @refreshed&.call(self)
82
- end
83
-
84
- ##
85
- # Sets a callback block that will be invoked when the token is refresh. This can be used to automate saving the
86
- # token after its gets updated.
87
- #
88
- # @yieldparam token [Token] yields the token to the block.
89
- #
90
- # @return [self]
91
- #
92
- # @example Auto-save updated token
93
- # token = Token.load_json('./token.json')
94
- # token.on_refresh { |t| t.save_json('./token.json') }
95
- def on_refresh(&block)
96
- raise(LocalJumpError, "block required") unless block_given?
97
- @refreshed = block
98
- self
99
77
  end
100
78
 
101
79
  ##
@@ -106,8 +84,8 @@ module Ruqqus
106
84
 
107
85
  ##
108
86
  # @return [String] the object as a JSON-formatted string.
109
- def to_json
110
- { client_id: @client_id, client_secret: @client_secret, data: @data }.to_json
87
+ def to_json(*_unused_)
88
+ @data.to_json
111
89
  end
112
90
 
113
91
  ##
@@ -133,15 +111,13 @@ module Ruqqus
133
111
  ##
134
112
  # Loads the object from a JSON-formatted string.
135
113
  #
136
- # @param json [String] a JSON string representing the object.
114
+ # @param json [String,Hash] a JSON string representing the object, or the parsed Hash of the JSON data.
137
115
  #
138
116
  # @return [Object] the loaded object.
139
117
  def self.from_json(payload)
140
- data = JSON.parse(payload, symbolize_names: true)
118
+ data = payload.is_a?(Hash) ? payload: JSON.parse(payload, symbolize_names: true)
141
119
  token = allocate
142
- token.instance_variable_set(:@client_id, data[:client_id])
143
- token.instance_variable_set(:@client_secret, data[:client_secret])
144
- token.instance_variable_set(:@data, data[:data])
120
+ token.instance_variable_set(:@data, data)
145
121
  token
146
122
  end
147
123
  end
@@ -2,7 +2,7 @@ module Ruqqus
2
2
 
3
3
  ##
4
4
  # The Ruqqus gem version.
5
- VERSION = '1.1.0'.freeze
5
+ VERSION = '1.1.1'.freeze
6
6
 
7
7
  ##
8
8
  # Lulz
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.version = Ruqqus::VERSION
8
8
  spec.authors = ['ForeverZer0']
9
9
  spec.email = ['efreed09@gmail.com']
10
- spec.summary = %q{A Ruby API implementation for Ruqqus, an open-source platform for online communities.}
10
+ spec.summary = %q{A Ruby API implementation for Ruqqus, an open-source platform for online communities}
11
11
  spec.description = %q{A Ruby API implementation for Ruqqus, an open-source platform for online communities, free of censorship and moderator abuse by design.}
12
12
  spec.homepage = 'https://github.com/ForeverZer0/ruqqus'
13
13
  spec.license = 'MIT'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruqqus
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ForeverZer0
@@ -145,5 +145,5 @@ rubygems_version: 3.1.4
145
145
  signing_key:
146
146
  specification_version: 4
147
147
  summary: A Ruby API implementation for Ruqqus, an open-source platform for online
148
- communities.
148
+ communities
149
149
  test_files: []