Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,5 @@ coverage
config/settings.local.yml
config/settings/*.local.yml
config/environments/*.local.yml

.byebug_history
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,5 @@ end
group :development do
gem 'web-console'
end

gem "alba", "~> 3.5"
5 changes: 4 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ GEM
uri (>= 0.13.1)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
alba (3.5.0)
ostruct (~> 0.6)
arbre (1.7.0)
activesupport (>= 3.0.0)
ruby2_keywords (>= 0.0.2)
Expand Down Expand Up @@ -448,6 +450,7 @@ DEPENDENCIES
activeadmin
activeadmin_addons
activerecord-import
alba (~> 3.5)
bundler-audit
capybara
config
Expand Down Expand Up @@ -479,4 +482,4 @@ DEPENDENCIES
web-console

BUNDLED WITH
2.5.18
2.5.3
16 changes: 16 additions & 0 deletions app/controllers/api/v1/books_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Api
module V1
class BooksController < ApplicationController
def index
permitted_params = index_params.to_h
books = Books::IndexInteractor.call(page: permitted_params[:page])
render json: { data: Books::Index::BookResource.new(books).to_h }
end

private
def index_params
params.permit(:page)
end
end
end
end
25 changes: 25 additions & 0 deletions app/interactors/api/v1/books/index_interactor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module Api
module V1
module Books
class IndexInteractor < ApplicationInteractor
def initialize(page:)
@page = Integer(page, exception: false) || 1
end

def call
Book.includes(:authors).order(created_at: :asc).offset(offset).limit(per_page)
end

private

def offset
per_page * (@page - 1)
end

def per_page
Settings.app.items_per_page
end
end
end
end
end
5 changes: 5 additions & 0 deletions app/interactors/application_interactor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ApplicationInteractor
def self.call(*, **)
new(*, **).call
end
end
13 changes: 13 additions & 0 deletions app/serializers/api/v1/books/index/author_resource.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Api
module V1
module Books
module Index
class AuthorResource
include Alba::Resource

attributes :id, :first_name, :middle_name, :last_name
end
end
end
end
end
15 changes: 15 additions & 0 deletions app/serializers/api/v1/books/index/book_resource.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Api
module V1
module Books
module Index
class BookResource
include Alba::Resource

attributes :id, :title, :series, :serno

many :authors, resource: AuthorResource
end
end
end
end
end
28 changes: 28 additions & 0 deletions bin/rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'rspec' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
ENV["RAILS_ENV"] = "test"

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("rspec-core", "rspec")
1 change: 1 addition & 0 deletions config/database.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ development:

test:
<<: *default
<<: *postgre
database: library_test

production:
Expand Down
1 change: 1 addition & 0 deletions config/initializers/alba.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Alba.inflector = nil
6 changes: 6 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@
# Render dynamic PWA files from app/views/pwa/*
get 'service-worker' => 'rails/pwa#service_worker', as: :pwa_service_worker
get 'manifest' => 'rails/pwa#manifest', as: :pwa_manifest

namespace :api do
namespace :v1 do
resources :books, only: [:index]
end
end
end
55 changes: 55 additions & 0 deletions docs/arch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
```plantuml
@startuml
actor Client as C
participant Routing as R
participant Controller as Cntr
participant ServiceObject as Inter

entity Model as M


participant Serializer as S
database DB as DB


C -> R
activate R


R -> Cntr
activate Cntr
deactivate R


Cntr -> Inter
activate Inter


Inter -> M
activate M

M -> DB
activate DB

return
return
return

...


Cntr -> S
activate S

return
Cntr -> C
deactivate Cntr






@enduml

```
73 changes: 73 additions & 0 deletions spec/controllers/api/v1/books_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
describe Api::V1::BooksController do
describe "GET #index" do
subject(:call) { get :index }

before do
Settings.app.items_per_page = 2
end

context "when there are no books" do
it "returns empty array" do
subject

expect(response).to have_http_status 200
expect(JSON.parse(response.body, symbolize_names: true)).to eq(data: [])
end
end

context "when there are 6 books" do
let!(:books) { create_list(:book, 6) }

it "returns first 2 books" do
subject

expect(response).to have_http_status 200
expect(JSON.parse(response.body, symbolize_names: true)).to eq(
data: [
{
authors: [],
id: books[0].id,
title: books[0].title,
series: nil,
serno: nil
},
{
authors: [],
id: books[1].id,
title: books[1].title,
series: nil,
serno: nil
}
])
end

context "when requested page is 2" do
subject(:call) { get :index, params: { page: 2 } }


it "returns third and fourth books" do
subject

expect(response).to have_http_status 200
expect(JSON.parse(response.body, symbolize_names: true)).to eq(
data: [
{
authors: [],
id: books[2].id,
title: books[2].title,
series: nil,
serno: nil
},
{
authors: [],
id: books[3].id,
title: books[3].title,
series: nil,
serno: nil
}
])
end
end
end
end
end