https://github.com/frodsan/recipes
Ruby on Rails code exercise
https://github.com/frodsan/recipes
ruby rubyonrails stimulusjs tailwindcss
Last synced: about 2 months ago
JSON representation
Ruby on Rails code exercise
- Host: GitHub
- URL: https://github.com/frodsan/recipes
- Owner: frodsan
- Created: 2022-04-03T17:35:28.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2022-05-10T09:15:11.000Z (about 4 years ago)
- Last Synced: 2025-02-12T06:24:31.761Z (over 1 year ago)
- Topics: ruby, rubyonrails, stimulusjs, tailwindcss
- Language: Ruby
- Homepage: https://savepenny.herokuapp.com/
- Size: 7.44 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# recipes
Save a penny and find recipes with ingredients you already have at home 🥘.

## Links:
- Website:
## Prerequisites
- Git
- Ruby 3.1
- Postgres 13
- Heroku CLI
## Setup
##### 1. Check out the repository
```
$ git clone git@github.com:frodsan/recipes.git
```
##### 2. Run setup script
Run the following commands to install dependencies and setup the database.
```
$ bin/setup
```
##### 3. Start the development server
You can start the server using the command given below.
```
$ bin/dev
```
And now you can visit the site with the URL http://localhost:3000.
## Description
The application help users to find recipes by the ingredients they have.

#### Data Seeds
The dataset can be found in [db/seeds/recipes-en.json](db/seeds/recipes-en.json)
and it has been populated into the database by `rails db:seed`. You can find the
code here: [db/seeds.rb](db/seeds.rb).
#### Database Structure
The format of the JSON objects found in the dataset is:
```json
{
"title": "Golden Sweet Cornbread",
"cook_time": 25,
"prep_time": 10,
"ingredients": [
"1 cup all-purpose flour",
"1 cup yellow cornmeal",
"â…” cup white sugar",
"1 teaspoon salt",
"3 ½ teaspoons baking powder",
"1 egg",
"1 cup milk",
"â…“ cup vegetable oil"
],
"ratings": 4.74,
"cuisine": "",
"category": "Cornbread",
"author": "bluegirl",
"image": "https://images.url/image.jpg"
}
```
I've created the `recipes` table with the same structure as the JSON object for simplicity.
I've decided to use `jsonb` as the type for the `ingredients` field, so it's straightforward
to read it from Rails, but also, there is no need to do any data transformation since I planned
to use [Postgres' search features][pgsearch].
#### Searching recipes by ingredients
Since the ingredients field uses `jsonb`, it is possible to parse
each string in the JSON array into a `tsvector`, a sorted list of
normalized lexemes:
```sql
# select to_tsvector('english', '[
# "1 cup all-purpose flour",
# "1 cup yellow cornmeal"
# ]') as ts_vector;
ts_vector
--------------------------------------------------------------------------------
'1':1,7 'all-purpos':3 'cornmeal':10 'cup':2,8 'flour':6 'purpos':5 'yellow':9
```
Then, I decided to use the `websearch_to_tsquery` function, which provides
search capabilities sort of like the ones used by search engines:
```sql
# -- Search by cornmeal and flour
# select to_tsvector('english', '[
# "1 cup all-purpose flour",
# "1 cup yellow cornmeal"
# ]') @@ websearch_to_tsquery('cornmeal flour') as must_be_true;
must_be_true
--------------
t
# -- Search by cornmeal but not flour
# select to_tsvector('english', '[
# "1 cup all-purpose flour",
# "1 cup yellow cornmeal"
# ]') @@ websearch_to_tsquery('cornmeal -flour') as must_be_false;
must_be_false
--------------
f
```
This is how is implemented in the model:
```ruby
class Recipe < ApplicationRecord
scope :by_ingredients, -> (query) {
where("to_tsvector('english', ingredients) @@ websearch_to_tsquery(?)", query)
}
end
```
Also, I've created an index on the function result to speed up the queries:
```ruby
add_index :recipes, "to_tsvector('english', ingredients)", using: :gin
```
Explaining the query shows that the it uses the index:
```
> Recipe.by_ingredients('pasta, eggplant').explain
EXPLAIN for: SELECT "recipes".* FROM "recipes" WHERE (to_tsvector('english', ingredients) @@ websearch_to_tsquery('pasta, eggplant'))
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Index Scan using index_recipes_on_to_tsvector_english_ingredients on recipes (cost=0.11..6.11 rows=2 width=525)
Index Cond: (to_tsvector('english'::regconfig, ingredients) @@ websearch_to_tsquery('pasta, eggplant'::text))
(2 rows)
```
#### Putting everything together
The rest is standard Rails code which can be found here:
- [app/models/recipe.rb](app/models/recipe.rb)
- [app/controllers/recipes_controller.rb](app/controllers/recipes_controller.rb)
- [app/views/recipes/](app/views/recipes/)
[pgsearch]: https://www.postgresql.org/docs/current/textsearch.html