ruqqus 1.0.0 → 1.1.0

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
  SHA256:
3
- metadata.gz: a01559e849bc401630ad22455035ae0d00891f34e294eb00ea9780b82c932620
4
- data.tar.gz: 56f99a8360fd71eb431dcf3d263f106412766f2afd51cdcda237aa643b9bb76d
3
+ metadata.gz: bb4087c263dae6ef0708a07d756733ea19f95e1e6f02983c658c9cfb40a2f475
4
+ data.tar.gz: d9d71b14897ec30ea1f5356916d1eb2cc432db02e7c007fb5f8efcea3a3f37fa
5
5
  SHA512:
6
- metadata.gz: 1417f32ba0e67970c1305c0f2cf48873fd6c4a6ffe5c6df98cb799f1459bf0ec364762a78f3eff4fb756b367e13650eeea7b8a62b516623e476b4d8488b7a167
7
- data.tar.gz: 485fadd5ed20a3d6b1290ce709c9d5881f0f4a5ec890196fa16deff6b500ecfeb0f1f030a4dcc5eaeff853c9d2ac22a6180d080ed3cf583bf7679d2b9a10aa7c
6
+ metadata.gz: f5864e192920e53c59921cf9bdb7d2f2acaf261d77ed4c0d3184a7f601cf967cf87f358c4b396705b8366fcda6ed3d54a9a125b0f13e7e139d0ce3ff0f67535e
7
+ data.tar.gz: 73d7d5b8a4551a7bc83efd54b8be72107a010f4023dc9784396dddce8ceae4808049cc6fed37487b07e259b6efc41a1ff75d8184bf5ce28f18f8c261e2ffd0e4
data/.gitignore CHANGED
@@ -164,4 +164,7 @@ modules.xml
164
164
  # Ignore all local history of files
165
165
  .history
166
166
 
167
- # End of https://www.toptal.com/developers/gitignore/api/ruby,rubymine+all,visualstudiocode
167
+ # End of https://www.toptal.com/developers/gitignore/api/ruby,rubymine+all,visualstudiocode
168
+
169
+ # JSON file containing the token for test user during development
170
+ /token.json
@@ -2,6 +2,29 @@
2
2
 
3
3
  Documentation for library API changes.
4
4
 
5
- ## Version 1.0
5
+ ## Version 1.1.0
6
+
7
+ * Implemented `Ruqqus::Token` class to handle OAuth2 authentication
8
+ * Added `Ruqqus::Client` class as the primary object for API usage
9
+ * Implemented post creation
10
+ * Implemented comment creation and deletion
11
+ * Implementing querying for reserved usernames/guilds
12
+ * Implemented retrieving posts of guilds
13
+ * Implemented retrieving posts and comments of users
14
+ * Implemented voting on posts and comments as a user
15
+ * Implemented enumerating all guilds
16
+ * Implemented enumerating through all posts
17
+ * Refactored `Ruqqus.user_info` to `Ruqqus::Client#user`
18
+ * Refactored `Ruqqus.guild_info` to `Ruqqus::Client#guild`
19
+ * Refactored `Ruqqus.post_info` to `Ruqqus::Client#post`
20
+ * Refactored `Ruqqus.comment_info` to `Ruqqus::Client#comment`
21
+ * Many improvements in code and better adhesion to DRY principles
22
+ * Added helper method to automate uploading and creating image posts on Ruqqus via Imgur
23
+
24
+ ## Version 1.0.1
25
+
26
+ * Changed validation for `fomr_url` methods in `Post` and `Comment` to also accept relative URIs.
27
+
28
+ ## Version 1.0.0
6
29
 
7
30
  * Initial release, 100% coverage of existing Ruqqus API
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in ruqqus.gemspec
3
+ # Dependencies specified in ruqqus.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -6,14 +6,16 @@
6
6
 
7
7
  # Ruqqus
8
8
 
9
- A Ruby API implementation for [Ruqqus](https://ruqqus.com/), an [open-source](https://github.com/ruqqus/ruqqus) platform for online communities, free of censorship and moderator abuse by design.
10
-
11
- While platform is still in Beta at this time and the public API for it is still quite limited, this gem will be actively updated as it continues to grow and is developed.
9
+ A Ruby API implementation for [Ruqqus](https://ruqqus.com/), an [open-source](https://github.com/ruqqus/ruqqus) platform for online communities, free of censorship and moderator abuse by design. [Sign up](https://ruqqus.com/signup?ref=foreverzer0
10
+ ) if you haven't yet!
12
11
 
13
12
  [![Build Status](https://travis-ci.org/ForeverZer0/ruqqus.svg?branch=master)](https://travis-ci.org/ForeverZer0/ruqqus)
14
- [![Gem Version](https://badge.fury.io/rb/ruqqus.svg)](https://badge.fury.io/rb/ruqqus)
13
+ [![Gem Version](https://badge.fury.io/rb/ruqqus.svg)](https://rubygems.org/gems/ruqqus)
15
14
  [![Inline docs](http://inch-ci.org/github/ForeverZer0/ruqqus.svg?branch=master)](http://inch-ci.org/github/ForeverZer0/ruqqus)
16
15
  [![Maintainability](https://api.codeclimate.com/v1/badges/c39f44a706302e4cd340/maintainability)](https://codeclimate.com/github/ForeverZer0/ruqqus/maintainability)
16
+ [![OpenIssues](https://img.shields.io/github/issues/ForeverZer0/ruqqus)](https://github.com/ForeverZer0/ruqqus/issues)
17
+ [![License](https://img.shields.io/github/license/ForeverZer0/ruqqus)](https://opensource.org/licenses/MIT)
18
+ [![Ruby](https://img.shields.io/badge/powered%20by-ruby-red)](https://www.ruby-lang.org/en/)
17
19
 
18
20
  ## Installation
19
21
 
@@ -31,23 +33,148 @@ Or install it yourself as:
31
33
 
32
34
  $ gem install ruqqus
33
35
 
36
+ To use the `ruqqus-oauth` helper to generate user tokens for desktop development:
37
+
38
+ $ gem install ruqqus --development
39
+
40
+ ## Authentication
41
+
42
+ Ruqqus enables 3rd-party client authorization using the [OAuth2 protocol](https://oauth.net/2/). Before it is possible
43
+ to interact with the API, you will need to first [register an application](https://ruqqus.com/settings/apps), which can
44
+ be supply you with an API key/secret pair. This key will allow you to authorize users and grant privileges with a an
45
+ assortment of scopes to fit your needs.
46
+
47
+ ### Desktop Development
48
+
49
+ This gem includes a tool to automate obtaining a client code for users, primarily aimed for desktop developers who do
50
+ not have a server running to receive the redirect URL. The tool requires the `mechanize` and `tty-prompt` gems, which
51
+ are not included by default.
52
+
53
+ To install the tool with its dependencies, install this gem with the following flag.
54
+
55
+ $ gem insall ruqqus --development
56
+
57
+ Once installed, simply run `ruqqus-oauth`. You will be prompted to input the API key that was issued by Ruqqus to your
58
+ approved application, and the user credentials for the account you with to authorize, such as that for a bot. Once
59
+ executed, an authorization code will be displayed on-screen, which you can then use to create a token.
60
+
61
+ ```ruby
62
+ require 'ruqqus'
63
+
64
+ client_id = 'XXXXXX' # Received after registering application
65
+ client_secret = 'XXXXXX' # Received after registering application
66
+ code = 'XXXXXX' # The generated code (or the one you obtained via traditional means)
67
+
68
+ # You must implement a responsible way of storing this token for reuse.
69
+ token = Ruqqus::Token.new(client_id, client_secret, code)
70
+ client = Ruqqus::Client.new(token)
71
+ ```
72
+
73
+ The token will automatically refresh itself as-needed, but you will need to handle storing its new value for repeated
74
+ uses. To facilitate this and make it easier, there is a callback that can be subscribed to which will be called each
75
+ time the access key is updated.
76
+
77
+ ```ruby
78
+ token = Ruqqus::Token.load_json('./token.json')
79
+ token.on_refresh do |t|
80
+ t.save_json('./token.json')
81
+ end
82
+
83
+ client = Ruqqus::Client.new(token)
84
+ ```
85
+
86
+ The token obtains sensitive material, and due to the security issues of storing it in plain text, this functionality is
87
+ left to the user. The token is essentially the equivalent of your user credentials for Ruqqus, so bear that in mind how
88
+ and where you store this information so that it is not compromised.
89
+
34
90
  ## Usage
35
91
 
36
- At this time, there are only four publicly exposed functions to utilize for acquiring information on the following items:
92
+ See the [documentation](https://www.rubydoc.info/gems/ruqqus) for a complete API reference.
93
+
94
+ ### Features
95
+
96
+ The bulk of the API is obviously related to performing actions as a user, such as posting, commenting, voting, etc.. The
97
+ following highlights some of these features.
98
+
99
+ * Vote on posts and comments
100
+ * Create posts (text/link/image)
101
+ * Automated image upload via anonymous upload to Imgur ([API Key](https://imgur.com/account/settings/apps) required)
102
+ * Create/edit/delete comments
103
+ * Enumerate all existing guilds
104
+ * Enumerate all posts in a guild
105
+ * Enumerate all posts on the "front page", "all", etc., with sorting and filtering
106
+ * Enumerate all posts/comments of users (excluding private/banned/blocked accounts)
107
+
108
+ #### Misc. Examples
109
+
110
+ Some random examples displaying the ease in performing various operations.
111
+
112
+ ##### Let's do some voting!
113
+ ```ruby
114
+ client.each_user_post('captainmeta4') do |post|
115
+ # Upvote our fearless leader's posts
116
+ client.vote_post(post, 1)
117
+ end
118
+
119
+ client.each_user_comment('captainmeta4') do |comment|
120
+ # ...and his comments.
121
+ client.vote_comment(comment, 1)
122
+ end
123
+ ```
124
+
125
+ ##### Monitor For New Posts
126
+ ```ruby
127
+ delay = 10
128
+ puts 'Watching for new posts, press Ctrl+C to quit'
129
+ loop do
130
+ time = Time.now
131
+ sleep(delay)
132
+
133
+ puts "Checking the front page for new posts since #{time}"
134
+ client.each_post(sort: :new, filter: :all) do |post|
135
+ # Stop checking post once we encounter one older than this iteration
136
+ break if post.created < time
137
+ # Do something about this new post showing up in All
138
+ puts "Found a new post: '#{post.title}'"
139
+ end
140
+ end
141
+ ```
142
+ ##### Download all images from a guild
143
+ ```ruby
144
+ require 'open-uri'
145
+ domains = %w[i.ruqqus.com i.imgur.com]
146
+
147
+ Dir.mkdir('./tree_pics') unless Dir.exist?('./tree_pics')
148
+ client.each_guild_post('Trees', sort: :new) do |post|
149
+ next unless domains.include?(post.domain)
150
+ ext = File.extname(post.url)
151
+ ext = '.jpg' if ext.empty? # We don't care, just an example
152
+ path = File.join('./tree_pics', post.id + ext)
153
+ URI.open(post.url) { |src| File.open(path, 'wb') { |dst| dst.write(src.read) } }
154
+ end
155
+ ```
156
+
157
+ ### Types
158
+
159
+ The Ruqqus API exposes four primary types:
37
160
 
38
161
  * Users
39
162
  * Guilds
40
163
  * Posts
41
164
  * Comments
42
165
 
43
- The full documentation can be found [here](https://www.rubydoc.info/gems/ruqqus), but the API is rather intuitive. Here is some basic examples to give a taste of its basic usage.
166
+ Nearly all client operations interact with one or more of these entities in some way. Each of these types also has an
167
+ `#id` property that can be used with other related API functions, such as voting, replying, deleting, etc. The full
168
+ documentation listing all properties they obtain can be found [here](https://www.rubydoc.info/gems/ruqqus), but the API
169
+ is rather intuitive. Here are some samples of their basic
170
+ usage.
44
171
 
45
- ### Users
172
+ #### Users
46
173
 
47
174
  Obtain information about users.
48
175
 
49
176
  ```ruby
50
- user = Ruqqus.user('foreverzer0')
177
+ user = client.user_info('foreverzer0')
51
178
 
52
179
  # Get user's total rep (as well as separate for comments/posts)
53
180
  user.total_rep
@@ -62,12 +189,12 @@ user.created
62
189
  #=> 2020-06-16 21:59:04 -0400
63
190
  ```
64
191
 
65
- ### Guilds
192
+ #### Guilds
66
193
 
67
194
  Obtain information about guilds.
68
195
 
69
196
  ```ruby
70
- guild = Ruqqus.guild('Ruby')
197
+ guild = client.guild('Ruby')
71
198
 
72
199
  # Query the number of members, description, accent color, etc.
73
200
  guild.member_count
@@ -82,14 +209,16 @@ guild.nsfw?
82
209
  #=> false
83
210
  ```
84
211
 
85
- ### Posts
212
+ #### Posts
86
213
 
87
- Obtain information about posts. The API functions for querying comments from a post are not yet implemented on the backend, so you will have to settle for web-scraping with `mechanize`, `nokogiri`, etc. if that information is needed.
214
+ Obtain information about posts.
88
215
 
89
216
  ```ruby
90
- post = Ruqqus.post('2e0x')
91
- # ...or alternatively
92
- post = Ruqqus::Post.from_url('https://ruqqus.com/post/2e0x/made-this-project-in-ruby-on')
217
+ # Post IDs can be found within any link to a post.
218
+ # https://ruqqus.com/post/<POST ID>/<POST TITLE>
219
+
220
+ post_id = '2e0x'
221
+ post = client.post(post_id)
93
222
 
94
223
  # Obtain relevant information pertaining the guilds on Ruqqus
95
224
 
@@ -106,27 +235,27 @@ post.score
106
235
  #=> 10
107
236
  ```
108
237
 
109
- ### Comments
238
+ #### Comments
110
239
 
111
240
  Obtain information about comments. Comments are very similar to posts, but have a few unique methods for obtaining their nesting level, parent post/comment, etc.
112
241
 
113
242
  ```ruby
114
- comment = Ruqqus.comment('67mt')
115
- # ...or alternatively
116
- comment = Ruqqus::Comment.from_url('https://ruqqus.com/post/1wbo/hi-im-josh-roehl-singer-and/67mt')
243
+ # Post IDs can be found within any link to a post.
244
+ # https://ruqqus.com/post/<POST ID>/<POST TITLE>/<COMMENT ID>
117
245
 
118
- comment.post.title
246
+ comment_id = '67mt'
247
+ comment = client.comment(comment_id)
248
+
249
+ client.post(comment.post).title
119
250
  #=> "Hi. I'm Josh Roehl, singer and songwriter of the hit song \"Endless Summer\". I am hosting an AMA here."
120
251
 
121
252
  comment.body
122
253
  #=> "I'm fully aware that I'm not a very good singer. Let's call it half-singing, half-rapping."
123
254
 
124
- comment.author.ban_reason
255
+ client.user(comment.author_name).ban_reason
125
256
  #=> "Spam"
126
257
  ```
127
258
 
128
- [Documentation](https://www.rubydoc.info/gems/ruqqus)
129
-
130
259
  ## Contributing
131
260
 
132
261
  Bug reports and pull requests are welcome on GitHub at https://github.com/ForeverZer0/ruqqus.
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
- # Tests not implemented yet...
4
- task(:test) { |_| true }
3
+ task(:test) { true }
5
4
 
6
5
  task default: :test
data/TODO.md ADDED
@@ -0,0 +1,27 @@
1
+ # TODO
2
+
3
+ A scratch pad for things to do and ideas to look into
4
+
5
+ * Create and centralize all API endpoints into `Routes` module
6
+ * Check for `json[:error]` in `http_get` and `http_post` for all calls?
7
+ * Create examples in documentation
8
+ * Update README with more examples
9
+ * Create wiki on GitHub
10
+ * Finish and cleanup and `ruqqus-oauth` app
11
+ * Groups in documentation
12
+ * Front page method?
13
+ * Embed comment/posts API
14
+
15
+ # Missing API features
16
+
17
+ Some API features that would be beneficial to have implemented
18
+
19
+ * Flagging
20
+ * Post deletion/edit (!)
21
+ * Notifications (!)
22
+ * NSFW/NSFW toggling
23
+ * Kicking/Yanking
24
+ * User Settings
25
+ * Guild Settings
26
+ * Guild join/leave
27
+ * Follow/Unfollow users
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mechanize'
4
+ require 'tty-prompt'
5
+
6
+ #noinspection RubyResolve
7
+ class RuqqusOAuth
8
+
9
+ def initialize
10
+ prompt = TTY::Prompt.new
11
+ agent = Mechanize.new
12
+
13
+ prompt.warn('After inputting your client information and credentials of user')
14
+ prompt.warn('to authorize, and a code for token creation will be generated.')
15
+ puts
16
+ url = authorization_url(prompt)
17
+ login(agent, prompt)
18
+ prompt.say('Generating code...')
19
+ puts
20
+ code = generate_code(url, agent)
21
+ STDOUT << 'CODE: '
22
+ prompt.ok(code)
23
+ end
24
+
25
+ def authorization_url(prompt)
26
+ url = 'https://ruqqus.com/oauth/authorize'
27
+ url << "?client_id=#{ask_client_id(prompt)}"
28
+ url << "&redirect_uri=#{ask_client_redirect(prompt)}"
29
+ url << "&scope=#{ask_scopes(prompt)}"
30
+ url << "&state=#{SecureRandom.uuid.gsub('-', '')}"
31
+ url << '&permanent=true'
32
+ url
33
+ end
34
+
35
+ def generate_code(url, agent)
36
+ agent.get(url) do |page|
37
+ page.form_with(action: '/oauth/authorize').submit
38
+ /.*\?code=([a-zA-Z0-9_-]+)&?/.match(agent.history.last.uri.to_s)
39
+ return $1
40
+ end
41
+ end
42
+
43
+ def ask_client_id(prompt)
44
+ prompt.ask('Client ID: ') { |q| q.validate(/^[A-Fa-f0-9]+$/, "Invalid client ID") }
45
+ end
46
+
47
+ def ask_client_secret(prompt)
48
+ prompt.ask('Client Secret: ') { |q| q.validate(/^[A-Fa-f0-9]+$/, "Invalid client secret") }
49
+ end
50
+
51
+ def ask_client_redirect(prompt)
52
+ prompt.ask('Redirect URI: ', default: 'https://www.google.com') do |q|
53
+ proc = Proc.new { |v| URI.regexp =~ v && URI(v).scheme == 'https' }
54
+ q.validate(proc, 'Invalid HTTPS URI/scheme (must be HTTPS, not localhost)')
55
+ end
56
+ end
57
+
58
+ def ask_scopes(prompt)
59
+ msg = 'Select scopes the client is authorized to perform:'
60
+ choices = %i(identity create read update delete vote guildmaster)
61
+ scopes = prompt.multi_select(msg, choices, per_page: choices.size) do |q|
62
+ defaults = (1..choices.size).to_a
63
+ q.default(*defaults)
64
+ end
65
+ scopes.join(',')
66
+ end
67
+
68
+ def login(agent, prompt)
69
+ agent.get('https://ruqqus.com/login') do |page|
70
+ page.form_with(action: '/login') do |form|
71
+ form['username'] = prompt.ask('Username: ') do |q|
72
+ q.validate(/^[a-zA-Z0-9_]{5,25}$/, 'Invalid username')
73
+ end
74
+ form['password'] = prompt.mask("Password: ") do |q|
75
+ q.validate(/^.{8,100}$/, 'Invalid password')
76
+ end
77
+ puts 'Logging into Ruqqus...'
78
+ end.submit
79
+ end
80
+ end
81
+ end
82
+
83
+ begin
84
+ RuqqusOAuth.new
85
+ rescue Interrupt
86
+ puts
87
+ # Ignored
88
+ end
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
@@ -1,10 +1,10 @@
1
1
  require 'rest-client'
2
+ require 'json'
2
3
 
3
- require_relative 'ruqqus/item_base'
4
- require_relative 'ruqqus/comment'
5
- require_relative 'ruqqus/guild'
6
- require_relative 'ruqqus/post'
7
- require_relative 'ruqqus/user'
4
+ require_relative 'ruqqus/token'
5
+ require_relative 'ruqqus/routes'
6
+ require_relative 'ruqqus/client'
7
+ require_relative 'ruqqus/types'
8
8
  require_relative 'ruqqus/version'
9
9
 
10
10
  ##
@@ -15,10 +15,6 @@ module Ruqqus
15
15
  # The base Ruqqus URL.
16
16
  HOME = 'https://ruqqus.com'.freeze
17
17
 
18
- ##
19
- # The Ruqqus API version.
20
- API_VERSION = 1
21
-
22
18
  ##
23
19
  # A regular expression used for username validation.
24
20
  VALID_USERNAME = /^[a-zA-Z0-9_]{5,25}$/.freeze
@@ -35,91 +31,78 @@ module Ruqqus
35
31
  # A regular expression used for post/comment ID validation.
36
32
  VALID_POST = /[A-Za-z0-9]+/.freeze
37
33
 
34
+ ##
35
+ # Captures the ID of a post from a Ruqqus URL
36
+ POST_REGEX = /\/post\/([A-Za-z0-9]+)\/?.*/.freeze
37
+
38
+ ##
39
+ # Captures the ID of a comment from a Ruqqus URL
40
+ COMMENT_REGEX = /\/post\/.+\/.+\/([A-Za-z0-9]+)\/?/.freeze
41
+
38
42
  ##
39
43
  # Generic error class for exceptions specific to the Ruqqus API.
40
44
  class Error < StandardError
41
45
  end
42
46
 
43
47
  ##
44
- # Retrieves the {User} with the specified username.
45
- #
46
- # @param username [String] the username of the Ruqqus account to retrieve.
48
+ # Helper function to automate uploading images to Imgur anonymously and returning the direct image link.
47
49
  #
48
- # @return [User] the requested {User}.
50
+ # @param client_id [String] an Imgur client ID
51
+ # @param image_path [String] the path to an image file.
52
+ # @params opts [Hash] the options hash.
53
+ # @option opts [String] :title a title to set on the Imgur post
54
+ # @option opts [String] :description a description to set on the Imgur post
49
55
  #
50
- # @raise [ArgumentError] when `username` is `nil` or value does match the {VALID_USERNAME} regular expression.
51
- # @raise [Error] thrown when user account does not exist.
52
- def self.user(username)
53
- raise(ArgumentError, 'username cannot be nil') unless username
54
- raise(ArgumentError, 'invalid username') unless VALID_USERNAME.match?(username)
55
- api_get("#{HOME}/api/v1/user/#{username}", User)
56
- end
56
+ # @return [String] the direct image link from Imgur.
57
+ # @note To obtain a free Imgur client ID, visit https://api.imgur.com/oauth2/addclient
58
+ # @note No authentication is required for anonymous image upload, though rate limiting (very generous) applies.
59
+ # @see Client.post_create
60
+ def self.imgur_upload(client_id, image_path, **opts)
61
+ #noinspection RubyResolve
62
+ raise(Errno::ENOENT, image_path) unless File.exist?(image_path)
63
+ raise(ArgumentError, 'client_id cannot be nil or empty') if client_id.nil? || client_id.empty?
57
64
 
58
- ##
59
- # Retrieves the {Guild} with the specified name.
60
- #
61
- # @param guild_name [String] the name of the Ruqqus guild to retrieve.
62
- #
63
- # @return [Guild] the requested {Guild}.
64
- #
65
- # @raise [ArgumentError] when `guild_name` is `nil` or value does match the {VALID_GUILD} regular expression.
66
- # @raise [Error] thrown when guild does not exist.
67
- def self.guild(guild_name)
68
- raise(ArgumentError, 'guild_name cannot be nil') unless guild_name
69
- raise(ArgumentError, 'invalid guild name') unless VALID_POST.match?(guild_name)
70
- api_get("#{HOME}/api/v1/guild/#{guild_name}", Guild)
65
+ header = { 'Content-Type': 'application/json', 'Authorization': "Client-ID #{client_id}" }
66
+ params = { image: File.new(image_path), type: 'file', title: opts[:title], description: opts[:description] }
67
+
68
+ response = RestClient.post('https://api.imgur.com/3/upload', params, header)
69
+ json = JSON.parse(response.body, symbolize_names: true)
70
+ json[:data][:link]
71
71
  end
72
72
 
73
73
  ##
74
- # Retrieves the {Post} with the specified name.
75
- #
76
- # @param post_id [String] the ID of the post to retrieve.
74
+ # Checks if the specified guild name is available to be created.
77
75
  #
78
- # @return [Post] the requested {Post}.
76
+ # @param guild_name [String] the name of a guild to query.
79
77
  #
80
- # @raise [ArgumentError] when `post_id` is `nil` or value does match the {VALID_POST} regular expression.
81
- # @raise [Error] thrown when a post with the specified ID does not exist.
82
- def self.post(post_id)
83
- raise(ArgumentError, 'post_id cannot be nil') unless post_id
84
- raise(ArgumentError, 'invalid post ID') unless VALID_POST.match?(post_id)
85
- api_get("#{HOME}/api/v1/post/#{post_id}", Post)
78
+ # @return [Boolean] `true` is name is available, otherwise `false` if it has been reserved or is in use.
79
+ def self.guild_available?(guild_name)
80
+ available?(guild_name, VALID_GUILD, "#{Routes::GUILD_AVAILABLE}#{name}")
86
81
  end
87
82
 
88
83
  ##
89
- # Retrieves the {Comment} with the specified name.
84
+ # Checks if the specified username is available to be created.
90
85
  #
91
- # @param comment_id [String] the ID of the comment to retrieve.
86
+ # @param username [String] the name of a user to query.
92
87
  #
93
- # @return [Comment] the requested {Comment}.
94
- #
95
- # @raise [ArgumentError] when `comment_id` is `nil` or value does match the {VALID_POST} regular expression.
96
- # @raise [Error] when a comment with the specified ID does not exist.
97
- def self.comment(comment_id)
98
- raise(ArgumentError, 'comment_id cannot be nil') unless comment_id
99
- raise(ArgumentError, 'invalid comment ID') unless VALID_POST.match?(comment_id)
100
- api_get("#{HOME}/api/v1/comment/#{comment_id}", Comment)
88
+ # @return [Boolean] `true` is name is available, otherwise `false` if it has been reserved or is in use.
89
+ def self.username_available?(username)
90
+ available?(username, VALID_USERNAME, "#{Routes::USERNAME_AVAILABLE}#{name}")
101
91
  end
102
92
 
93
+ private
94
+
103
95
  ##
104
- # @api private
105
- # Calls the GET method at the specified API route, and returns the deserializes JSON response as an object.
106
- #
107
- # @param route [String] the full API route to the GET method.
108
- # @param klass [Class] a Class instance that is inherited from {ItemBase}.
96
+ # Checks if the specified guild or user name is available to be created.
109
97
  #
110
- # @return [Object] an instance of the specified class.
98
+ # @param name [String] the name of a guild or username to query.
99
+ # @param regex [Regex] a validation regex for the name.
100
+ # @param route [String] the API endpoint to invoke.
111
101
  #
112
- # @raise [Error] thrown when the requested item is not found.
113
- # @raise [ArgumentError] when the specified class is not inherited from {ItemBase}.
114
- def self.api_get(route, klass)
115
- raise(ArgumentError, 'klass is not a child class of Ruqqus::ItemBase') unless klass < ItemBase
116
- #noinspection RubyResolve
117
- begin
118
- response = RestClient.get(route)
119
- klass.from_json(response.body)
120
- rescue RestClient::BadRequest
121
- raise(Error, 'invalid search parameters, object with specified criteria does not exist')
122
- end
102
+ # @return [Boolean] `true` is name is available, otherwise `false` if it has been reserved or is in use.
103
+ def self.available?(name, regex, route)
104
+ return false unless name && regex.match?(name)
105
+ json = JSON.parse(RestClient.get(route))
106
+ !!json[name]
123
107
  end
124
- end
125
-
108
+ end