A Rails app that implements:
- Google OAuth
- asking and answering questions (with Markdown support)
- full-text search in titles and bodies of the questions
On your command line run:
git clone [email protected]:mediafinger/qanda.git
Change to the newly created directory and run:
bundle install
Which needs Ruby (the app was tested with the Ruby versions 2.5
, 2.6
, 2.7
and 3.0
) and the gem bundler.
When all gems have been installed, execute:
bin/rails db:setup
This will create the development and test database. You need to have Postgres installed.
bin/rails ci
will execute:
rubocop
- to ensure a consistent stylerspec
- to run all existing specsbundle_audit
- to check for vulnerabilities in the gems
You can also just run rspec alone: bundle exec rspec spec
The models
are well covered in the tests. And one feature test ensures that the whole workflow
does work in the browser:
- login
- asking a question
- answering a question
- searching a question
- logout
Writing 'unit' and 'integration' tests are both important to me. At the same time I am willing to skip most of the other kinds of tests Rails does offer, as they usually do not add enough value for the time invested (e.g. controller tests).
Before starting the server, you will have to add your Google Developer credentials to a new file which you have to name: .env.development.local
This file is listed in .gitignore so you won't accidentally publish your secrets. As the name suggests, it will only be loaded in development mode.
Please add this code together with your client_id
and your secret
to the new file:
export GOOGLE_OAUTH2_CLIENT_ID=
export GOOGLE_OAUTH2_CLIENT_SECRET=
If you don't have a client_id and secret yet and don't know how to obtain it, please follow this instructions:
https://www.themarketingtechnologist.co/google-oauth-2-enable-your-application-to-access-data-from-a-google-user/
Once this is done, you can start the server:
bin/rails server
will make the app available at http://localhost:3000/
You will be redirected to Google to authenticate yourself. On success the list of existing questions will be displayed.
Actually I hope it is self-explanatory. You can:
- ask a question (using markdown in the body)
- list questions
- open a question
- answer the opened question (using markdown in the body)
- search in questions
- logout
- search in titles (on / off)
- search in bodies (on / off)
- allow any word to match, or expect the result to contain all words
- it is case-insensitive
- special characters like
* _ - / ...
are ignored - only full words match:
bristo
orBristols
would not matchBristol
Some notes that didn't fit into the preceding sections.
I've built this app the Rails (5) way. And to my own surprise I don't had to add an app/services
folder (but it is still a small app). Apart from that I followed what I think are best practices and idiomatic Ruby (enforced by rubocop
). In total I spent around three working days on it (I forgot to use my time-tracking app).
- This app was developed and tested under
Ruby 2.5.0
and later updated to2.6.1
,2.7.2
and3.0.0
. Other versions might or might not work. - It used
Rails 5.2
, was then updated toRails 6.1
and needs aPostgres database
, as the fulltext search relies on Postgres features. - Google authentication is done with
omniauth-google-oauth2
which depends onomniauth
. - The configuration relies on the
dotenv-rails
gem. It was not tested in a Docker environment (but PRs are always welcome). - Markdown rendering is done with the
redcarpet
gem and therouge
plugin is used for code syntax highlighting. - Tests are written with
rspec
and the feature tests usecapybara
.factory_bot
builds and creates instances.
As this app was created with limited time, it does not include some things a production app should include.
- OAuth tokens do not expire
- The OAuth image URL is fetched and stored, but never used
- The design is functional at best
- There are no tests to ensure that the markdown is rendered
The Search get's it's own section, as it feels like the most incomplete part. Some search features had been added, but excluded again, as they seemed to be incompatible with the other desired features (and I didn't invest the time to find a solution).
- Search terms can not be excluded, a pending test is left, as the feature was working already.
- Stemming is unfortunately not included in this version. The feature was working already, but I didn't like the side effects. Some commented out code and pending tests are left. This does mean, that the search terms must match exactly. There is no support for synonyms.
- While non-words are removed from the query, stop-words are not.
- Accents should be ignored in searches, in this version they are significant.
- And lastly - and for a production app most importantly - there is no search index being created. I wanted to save
GIN
basedtsvector
indexes to the database, but didn't find the time to do so.
For an application that does not come with very high load or special demands to the search, I would always feel comfortable to implement it with Postgres (and indexes). It definitely comes with a much lower overhead than a specialized search system like ElasticSearch / Solr / Sphinx.
A drawback of the Postgres fulltext-search is that it works best with English. Very international apps would have to measure if this solution would be good enough for their audience and use cases.
I wasted a lot of time trying to get the workflow tested in a system test
. For reasons that are still unknown to me the combination of rspec
and omniauth
led always to a CSRF
issue. Only when I refactored the spec to the feature/workflow_spec
it was successfully executing.
I also spent a lot of time reading documentation about Postgres' GIN tsvector indexes for fulltext-search
and how to set them up in a Rails app - only to then not finding the time to actually implement them.