I'm gonna assume you don't actually want that JSON structure. Simply because it doesn't actually make sense given the overall goal. If you want to render a recipe you want to actually have quantity, name and unit together so that you don't have cobble the needed information together on the consuming end from two seperate arrays.
Ideally you would want JSON that resembles this:
{
"name": "Chocolate Chip Cookies",
"ingredients": [
{
"name": "Butter",
"quantity": 100,
"measurement_unit": "gr"
},
{
"name": "Flour",
"quantity": 300,
"measurement_unit": "gr"
}
]
}
Provided you have the following assocations:
class Recipe < ApplicationRecord
has_many :recipe_ingredients
end
class RecipeIngredient < ApplicationRecord
belongs_to :recipe
belongs_to :ingredient
end
You can load the records off all three tables in a single query with:
@recipe = Recipe.eager_load(recipe_ingredients: :ingredient).last
This generates the following monster query:
SELECT
"recipes"."id" AS t0_r0,
"recipes"."name" AS t0_r1,
"recipes"."created_at" AS t0_r2,
"recipes"."updated_at" AS t0_r3,
"recipe_ingredients"."id" AS t1_r0,
"recipe_ingredients"."recipe_id" AS t1_r1,
"recipe_ingredients"."ingredient_id" AS t1_r2,
"recipe_ingredients"."quantity" AS t1_r3,
"recipe_ingredients"."measurement_unit" AS t1_r4,
"recipe_ingredients"."created_at" AS t1_r5,
"recipe_ingredients"."updated_at" AS t1_r6,
"ingredients"."id" AS t2_r0,
"ingredients"."name" AS t2_r1,
"ingredients"."created_at" AS t2_r2,
"ingredients"."updated_at" AS t2_r3
FROM
"recipes"
LEFT OUTER JOIN
"recipe_ingredients"
ON "recipe_ingredients"."recipe_id" = "recipes"."id"
LEFT OUTER JOIN
"ingredients"
ON "ingredients"."id" = "recipe_ingredients"."ingredient_id"
LIMIT 1
Note wierd the column aliases - thats how ActiveRecord knows what model to stuff each column in the result set into.
This will let you iterate through the associations without N+1 queries - like in this HTML example:
<article class="recipe">
<h1><%= @recipe.name %></h1>
<ul class="ingredients">
<%= @recipe.recipe_ingredients.each do |ri| %>
<li>
<%= ri.quantity %><%= ri.measurement_unit %> <%= ri.ingredient.name %>
</li>
<% end %>
</ul>
</article>
Setting up an indirect assocation as in the other answers is generally a good idea but doing .eager_load(:ingredients) won't actually prevent a n+1 query when what you actually want to do is iterate across the recipe_ingredients assocation and the nested ingredient.
While you could render this as JSON with:
render json: @recipe,
include: { recipe_ingredients: :ingredient }
Its usually a better idea to move any kind of heavy lifting into a seperate serialization layer such as ActiveModel::Serializers or one of the many JSONAPI gems.