Context
I would like to offer a bike rental companies (shops
) the option to redirect guests to an online reservation process on my platform, so they can let customers make a reservation for a bike
of the shop
. (e.g. online reservation process is just for their shop)
Current step
My first step is to create an embedded javascript form (form_availability
), where shop
website visitors can select an arrival
, departure
date and subsequently see the available bike_categories
. To clarify, I would like to show the initial form with arrival and departure options for tthat shop
.
Approach
api/v1/shops
(routes, controllers and views)embed.js
using webpacker:vue<%= javascript_pack_tag "embed" %>
in my own app to test (url: http://localhost:3000/en/shops/58/website_integration
)website
of the respective shop is http://localhost:3000
Question
It's the first time I'm doing this, and I get an error messages I don't know how to deal with/solve.
console
embed.js:23 GET http://localhost:3000/api/v1/shops/http%3A%2F%2Flocalhost%3A3000%2Fen%2Fshops%2F3%2Fwebsite_integration/form_availability 500 (Internal Server Error)
./app/javascript/packs/embed.js @ embed.js:23
__webpack_require__ @ bootstrap:19
(anonymous) @ bootstrap:83
(anonymous) @ bootstrap:83
{type: "NoMethodError", message: "undefined method `url' for ["http://localhost:3000/en/shops/3/website_integration"]:Array", backtrace: Array(76)}
type: "NoMethodError"
message: "undefined method `url' for ["http://localhost:3000/en/shops/3/website_integration"]:Array"
backtrace: (76) ["/Users/robtuinte/code/Tumble25/camping_pms/app/models/shop.rb:41:in `block in <class:Shop>'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…/active_record/relation.rb:288:in `instance_exec'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ib/active_record/relation.rb:288:in `_exec_scope'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…e_record/scoping/named.rb:186:in `block in scope'", "/Users/robtuinte/code/Tumble25/camping_pms/app/con…/v1/shops_controller.rb:16:in `form_availability'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…metal/basic_implicit_render.rb:6:in `send_action'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…stract_controller/base.rb:194:in `process_action'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…troller/metal/rendering.rb:30:in `process_action'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ller/callbacks.rb:42:in `block in process_action'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ctive_support/callbacks.rb:132:in `run_callbacks'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ct_controller/callbacks.rb:41:in `process_action'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…controller/metal/rescue.rb:22:in `process_action'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…nstrumentation.rb:34:in `block in process_action'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ort/notifications.rb:168:in `block in instrument'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…/notifications/instrumenter.rb:23:in `instrument'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…tive_support/notifications.rb:168:in `instrument'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…r/metal/instrumentation.rb:32:in `process_action'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…r/metal/params_wrapper.rb:256:in `process_action'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ties/controller_runtime.rb:24:in `process_action'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…/lib/abstract_controller/base.rb:134:in `process'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge….2.3/lib/action_view/rendering.rb:32:in `process'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…/lib/action_controller/metal.rb:191:in `dispatch'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…/lib/action_controller/metal.rb:252:in `dispatch'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…on_dispatch/routing/route_set.rb:52:in `dispatch'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ction_dispatch/routing/route_set.rb:34:in `serve'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…dispatch/journey/router.rb:52:in `block in serve'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ib/action_dispatch/journey/router.rb:35:in `each'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…b/action_dispatch/journey/router.rb:35:in `serve'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ction_dispatch/routing/route_set.rb:840:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…n-js-3.5.1/lib/i18n/js/middleware.rb:14:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…1.2.8/lib/warden/manager.rb:36:in `block in call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…/warden-1.2.8/lib/warden/manager.rb:34:in `catch'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…s/warden-1.2.8/lib/warden/manager.rb:34:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ck-2.0.7/lib/rack/tempfile_reaper.rb:15:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…5.0/gems/rack-2.0.7/lib/rack/etag.rb:25:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ck-2.0.7/lib/rack/conditional_get.rb:25:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…5.0/gems/rack-2.0.7/lib/rack/head.rb:12:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…atch/http/content_security_policy.rb:18:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…/lib/rack/session/abstract/id.rb:232:in `context'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…0.7/lib/rack/session/abstract/id.rb:226:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…tion_dispatch/middleware/cookies.rb:670:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge….2.3/lib/active_record/migration.rb:559:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…tch/middleware/callbacks.rb:28:in `block in call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…active_support/callbacks.rb:98:in `run_callbacks'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ion_dispatch/middleware/callbacks.rb:26:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…tion_dispatch/middleware/executor.rb:14:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…patch/middleware/debug_exceptions.rb:61:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…0/lib/web_console/middleware.rb:135:in `call_app'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…b/web_console/middleware.rb:30:in `block in call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…3.7.0/lib/web_console/middleware.rb:20:in `catch'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…-3.7.0/lib/web_console/middleware.rb:20:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…spatch/middleware/show_exceptions.rb:33:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…s-5.2.3/lib/rails/rack/logger.rb:38:in `call_app'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge….3/lib/rails/rack/logger.rb:26:in `block in call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…support/tagged_logging.rb:71:in `block in tagged'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…b/active_support/tagged_logging.rb:28:in `tagged'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…b/active_support/tagged_logging.rb:71:in `tagged'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…lties-5.2.3/lib/rails/rack/logger.rb:26:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…/lib/sprockets/rails/quiet_assets.rb:13:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ion_dispatch/middleware/remote_ip.rb:81:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge….5.0/lib/request_store/middleware.rb:19:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…on_dispatch/middleware/request_id.rb:27:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ck-2.0.7/lib/rack/method_override.rb:22:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…/gems/rack-2.0.7/lib/rack/runtime.rb:22:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…e/strategy/local_cache_middleware.rb:29:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…tion_dispatch/middleware/executor.rb:14:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ction_dispatch/middleware/static.rb:127:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ems/rack-2.0.7/lib/rack/sendfile.rb:111:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…acker/dev_server_proxy.rb:29:in `perform_request'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…s/rack-proxy-0.6.5/lib/rack/proxy.rb:57:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…/railties-5.2.3/lib/rails/engine.rb:524:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…uma-4.1.0/lib/puma/configuration.rb:228:in `call'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…-4.1.0/lib/puma/server.rb:664:in `handle_request'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…-4.1.0/lib/puma/server.rb:467:in `process_client'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…ma-4.1.0/lib/puma/server.rb:328:in `block in run'", "/Users/robtuinte/.rbenv/versions/2.5.3/lib/ruby/ge…uma/thread_pool.rb:135:in `block in spawn_thread'"]
__proto__:
logs
Started GET "/api/v1/shops/http%3A%2F%2Flocalhost%3A3000%2Fen%2Fshops%2F3%2Fwebsite_integration/form_availability" for ::1 at 2020-02-18 10:24:21 +0100
Processing by Api::V1::ShopsController#form_availability as JSON
Parameters: {"id"=>"http://localhost:3000/en/shops/3/website_integration"}
Completed 500 Internal Server Error in 2ms (Views: 0.7ms | ActiveRecord: 0.0ms)
Code
routes
namespace :api, defaults: { format: :json } do
namespace :v1 do
resources :shops, only: [:show] do
get :form_availability , on: :member
end
end
end
controller/api/v1/shops_controller.rb
class Api::V1::ShopsController < Api::V1::BaseController
def form_availability
binding.pry
@shop = Shop.by_url(params[:id])
render "shops/form_availability"
end
end
models
class Shop < ApplicationRecord
has_many :bike_categories, dependent: :destroy
has_many :bikes, through: :bike_categories
has_many :reservations, dependent: :destroy
def self.by_url(url)
binding.pry
uri = url.split("?").first
uri.sub!(/\/$/, '')
where(website: uri)
end
end
class Reservation < ApplicationRecord
belongs_to :shop
belongs_to :bike
end
class Bike < ApplicationRecord
belongs_to :bike_category
has_many :reservations, dependent: :destroy
end
class BikeCategory < ApplicationRecord
belongs_to :shop
has_many :bikes, dependent: :destroy
end
javascript/packs/embed.js
let url = window.location.href
fetch(`http://localhost:3000/api/v1/shops/${encodeURIComponent(url)}/form_availability`,{
headers: {accept: 'application/json'}
})
.then(response => response.json())
.then(data => console.log(data))```
views/api/vi/shop/form_availability.json.jbuilder
json.extract! @shop, :id, :name, :slug, :description, :street, :street_number, :zipcode, :city, :country, :email, :phone, :website, :vat_number, :currency, :photo, :test_modus, :default_vat, :price_notation, :paytime, :billing_id, :default_shop_language, :default_age_table, :inv_pay_deadline
The reason why you are seeing this error, is because the server is responding with a 404 and this is being delivered in HTML rather than JSON format.
You will find some guidance to get error messages in JSON for api requests here (might be helpful for better error handling and debugging):
Need to return JSON-formatted 404 error in Rails
The problem why you are getting back a 404 in this particular case though, is that the URL in the fetch
call is composed incorrectly. There are two issues here:
There is a typo - http://localhost:3000/api/vi/...
- it should be v1
as defined in your routes, not vi
.
The URI schema in the javascript is composed incorrectly - you can see all available routes per your route definitions by executing rails routes
. You should be able to find the correct URI pattern in the output - /api/v1/dummies/:id/form_availability(.:format)
in your case.
So, your embed.js should look something like this:
let url = window.location.href
fetch(`http://localhost:3000/api/v1/shops/${encodeURIComponent(url)}/form_availability`,{
headers: {accept: 'application/json'}
})
.then(response => response.json())
.then(data => console.log(data))
One comment to the class method self.by_url(url)
that you defined in the Shop
model. The standard way of doing this, is using scopes (https://guides.rubyonrails.org/active_record_querying.html#scopes).
So in your model it should be something like this instead of the class method:
scope :by_url, ->(url) { where(website: url.split('?').first.sub(/\/$/, '')) }