Keyboard App (work in progress)
Tags: rails, react, tailwindcss
Published: 2022-11-17 01:12:26

After going from typing test to typing test aimlessly clicking away, I decided to create my own incorporating all the elements that would fall under my "perfect" website. This project also happened to be the ideal host to learn an assortment of new tech, which occupied the majority of the project's week-long build-time. This post will be a synopsis of the most difficult issues this Rails-React app threw at me and the solutions to fix them. Feel free to let me know where my code could be improved!

## Core Mechanics

### Words

What are the key factors for a typing speed test? The words, the timer, and the logic to handle input. To begin, the backbone of the project: The words. Originally, I tried to incorporate an external API to get a vast assortment of words, but this proved to involve multiple steps; It included doing things like creating a jumbled sentence of words from multiple returned paragraphs, and while attempting to use a free API this became a somewhat impossible move. I decided to switch to storing my words directly in my own API. Using rails as my backend, the most light-weight, fast option to me was storing an array of words in my `Word.rb` model. Then, after thinking about how many words a normal website uses for their random modes, it must only be around 75-100. This is due to the fact that more complex your words get, the more you deviate from the "average" typing speed with "average"-length words. To give my website its own personality I went out searching for my own, bland assortment of words, and after doing so, and storing them in a constant like such:

```ruby
#./app/models/user.rb

class Word < ApplicationRecord

  WORDS = ["allow","else", ...]

end
```

Since I wanted my words to be returned in a string (you'll see why later), this was how I set up my controller and route to by dynamic:

```ruby

#./app/controllers/word_controller.rb

def random_words
  @words = [""]
  params[:count].to_i.times { @words[0] << Word::WORDS.sample + " " }
  render json: @words
end

#./config/routes.rb

get '/random/:count', to: 'word#random_words'

```

The string being in an array is to send usable json, and this will also send a string ending with a space, which will easily be handled on the frontend. With this, my fetch route can request a string of any length, using the random assortment of words. E.X.

```
http://localhost:3000/words/50
```
This route would now return a randomized string with 50 characters.

The second difficulty with getting words became getting random quotes for my feature that incorporates sentences with punctuation, also including multiple languages. From the beginning I knew I wanted to use PaperQuotes as my AI for this task, but actually sending the request turned out to require some weird (at the time) syntax. A different API request for Bacon Ipsum I'm using looks like this:

```ruby
#./app/controllers/word_controller.rb

require 'open-uri'

...

def fetch
  uri = URI.parse(URL)
  response = Net::HTTP.get_response(uri)
  @words = response.body
  render json: @words.split(' ')
end
```
However, with the PaperQuotes API and using the Net/Http gem to incorporate my API key, it would fail every time and return this:

```
<html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>The plain HTTP request was sent to HTTPS port</center>
<hr><center>cloudflare</center>
</body>
</html>
```

This was beyond confusing to me and stack overflow was almost instantly checked. Luckily, an answer was there, and this was a common issue where an ssl configuration must be defined in the request. So, the updated, working request is like such:

```ruby
def get_quote
  url = URI.parse("https://api.paperquotes.com/apiv1/quotes/?minlength=200&lang=#{params[:lang]}")
  req = Net::HTTP::Get.new(url)
  req["Authorization"] = "Token ----"
  response = Net::HTTP.start(url.host, url.port, use_ssl: true) do |http|
    http.request(req)
  end

  @quote = response.body
  render json: @quote, status: :ok
end
```

Coming Soon