In my Ruby on Rails app, bike rental companies can manage all their bikes (reservations, payments etc.).
Goal
I would like to offer a bike rental companies the option to implement a booking form on their own website, so they can let customers create a reservation for a specific bike.
This booking form would then take available bikes from my Rails app to their website and consequently send new reservation data back to the Rails app.
Question
How can I link the website of a shop, to a specific Shop
within my application. Thereby, making sure a specific website indeed should be able to GET information belonging to a Shop
.
Code
models
class Shop < ApplicationRecord
has_many :bike_categories, dependent: :destroy
has_many :bikes, through: :bike_categories
has_many :user_shops, dependent: :destroy
has_many :users, through: :user_shops
has_many :reservations, dependent: :destroy
accepts_nested_attributes_for :users, allow_destroy: true, reject_if: ->(attrs) { attrs['email'].blank? || attrs['role'].blank?}
end
class User < ApplicationRecord
has_many :user_shops, dependent: :destroy
has_many :shops, through: :user_shops
accepts_nested_attributes_for :user_shops
enum role: [:owner, :admin, :employee, :accountant, :demo, :app_owner]
end
class Reservation < ApplicationRecord
belongs_to :shop
belongs_to :bike
end
controllers/api/v1/reservations_controller
def create
# How to know/specify which shop?
@shop = Shop.new(shop_params)
authorize @shop
if @shop.save
render :show, status: :created
else
render_error
end
end
This is your textbook example of a nested resource. In REST a nested resouces is nested in the path of another resource:
/authors/1/books
/countries/uk/cities
/blogs/1/posts/2
The genius here is that the path itself describes the relation between the resources.
You can make the route nested by passing a block to resources
:
namespace :api do
namespace :v1 do
resources :shops do
resources :reservations, shallow: true
end
end
end
The shallow
option makes only the collection routes nested (new, create, index) which is usually a good thing since records have a unique id anyways which they can be fetched through.
/blogs/1/posts/2
is an example of deeply nested route. If the id is unique we should be able to get the exact same resource through /posts/2
which simplefies the code greatly as it does not need to know about the blog
.
module API
module V1
class ReservationsController < ApiController
before_action :set_shop, only: [:create, :index]
# GET /api/v1/shops/1/resevations
def index
@reservations = @shop.reservations
end
# POST/api/v1/shops/1/resevations
def create
@reservation = @shop.reservations.new(reservation_params)
# ...
end
# ...
private
def set_shop
@shop = Shop.includes(:reservations).find(params[:shop_id])
end
# ...
end
end
end