5 geriausi "Ruby" naudojimo pavyzdžiai
Ar kada nors susimąstėte, ką galime padaryti su "Ruby"? Ko gero, dangus yra beribis, bet mes mielai papasakosime apie kai kuriuos daugiau ar mažiau žinomus atvejus...
Sukursime knygų lentynos programą, kuri leis sudaryti knygų sąrašą su autorių duomenimis (arba be jų).
Sukursime knygų lentynos programėlę, kuri leis sudaryti knygų sąrašą su autoriais (arba be jų). duomenys. Bus vienas #index veiksmų ir kai kurių sėklų. Tai bus pavyzdinė programa, kurioje parodysime, kaip galima suteikti naudotojui galimybę valdyti įtrauktus sub-šaltinių REST tipo API.
apima užklausos parametras, kad būtų įkelti susiję ištekliai (autorius).apima užklausos parametro formatas yra eilutė: kableliais atskirti žodžiai, reiškiantys įterptus išteklius.Naudosime mėlynasis spausdintuvas kaip serializatorių, nes jis nepriklauso nuo formato ir yra gana lankstus. Tai vienintelis brangakmenis, kuriuo papildysime standartinį rails įrankių rinkinį.
Sukurkime pavyzdinę programą. Testų sistemos nepridėsime, nes tai neįeina į mūsų taikymo sritį.
bėgiai nauja knygų lentyna -T
Dabar sukurkite Autorius modelis:
rails g modelio autoriaus vardas:string
#=> invoke active_record
#=> create db/migrate/20211224084524_create_authors.rb
#=> create app/models/author.rb
Ir Knyga:
rails g modelis knyga autorius:nuorodos pavadinimas:string
# => invoke active_record
# => create db/migrate/20211224084614_create_books.rb
# => create app/models/book.rb
Mums reikės sėklų:
# db/seeds.rb
dumas = Author.create(vardas: 'Alexandre Dumas')
lewis = Author.create(vardas: 'C.S. Lewis')
martin = Author.create(vardas: 'Robert C. Martin')
Book.create(autorius: dumas, pavadinimas: 'Trys muškietininkai')
Book.create(autorius: lewis, pavadinimas: 'The Lion, the Witch and the Wardrobe')
Book.create(autorius: martin, pavadinimas: 'Clean Code')
Dabar esame pasiruošę paleisti migracijas ir įdiegti db:
rails db:migrate && rails db:seed
Pridėkime has_many knygų Autorius modelis:
# app/models/author.rb
klasė Author < ApplicationRecord
has_many :books
end
Laikas parašyti valdiklį, kuris grąžins mūsų duomenis. Naudosime API vardų erdvę, todėl pirmiausia pridėkime santrumpą prie skiemenų:
# config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'API'
end
Gerai, pridėkime savo serializatorių prie Gemfile:
# Pridėti prie brangakmenių rinkmenos
gem 'blueprinter'
Ir, žinoma, jį įdiekite:
įdiegti paketą
Tada galėsime kurti savo planus:
# app/blueprints/author_blueprint.rb
klasė AuthorBlueprint < Blueprinter::Base
identifikatorius :id
laukai :name
end
# app/blueprints/book_blueprint.rb
klasė BookBlueprint < Blueprinter::Base
identifikatorius :id
laukai :title
asociacija :author, blueprint: AuthorBlueprint
pabaiga
Pridėti bazinį valdiklį API:
# app/controllers/api/v1/base_controller.rb
API modulis
modulis V1
klasė BaseController < ActionController::API
pabaiga
end
end
Ir mūsų projekto versija BooksController:
# app/controllers/api/v1/books_controller.rb
API modulis
modulis V1
klasė BooksController < BaseController
def index
books = Book.all
atvaizduoti json: BookBlueprint.render(books)
end
end
end
end
Žinoma, taip pat turime apibrėžti maršruto parinkimą:
# config/routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :books, only: :index
end
end
end
Patikrinkime, ką iki šiol padarėme:
bėgiai s
garbanoti http://localhost:3000/api/v1/books
# => [{"id":1, "author":{"id":1, "name": "Alexandre Dumas"}, "title": "Trys muškietininkai"},{"id":2, "author":{"id":2, "name": "C.S. Lewis"}, "pavadinimas": "The Lion, the Witch and the Wardrobe"},{"id":3, "autorius":{"id":3, "vardas": "Robert C. Martin"}, "pavadinimas": "Clean Kodas"}]
Atrodo, kad duomenys yra tvarkingi, o kaip su žurnalais?
# užklausų žurnalai (n+1)
Pradėta GET "/api/v1/books" 127.0.0.1 2021-12-24 10:19:40 +0100
API::V1::BooksController#index apdorojama kaip */*
Knygų įkėlimas (0,1 ms) SELECT "books".* FROM "books"
↳ app/controllers/api/v1/books_controller.rb:7:in `index'
Autorių įkėlimas (0.1 ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ? LIMIT ? [[["id", 1], ["LIMIT", 1]]]
↳ app/controllers/api/v1/books_controller.rb:7:in `in index'
Autorių įkėlimas (0.1 ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ? LIMIT ? [[["id", 2], ["LIMIT", 1]]]
↳ app/controllers/api/v1/books_controller.rb:7:in `in index'
Autorių įkėlimas (0.1 ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ? LIMIT ? [[["id", 3], ["LIMIT", 1]]]
↳ app/controllers/api/v1/books_controller.rb:7:in `in index'
Užbaigta 200 OK per 6 ms (Peržiūros: 0,1 ms | ActiveRecord: 0,4 ms | Alokacijos: 3134)
Naudodami asociaciją savo serializatoriuose įvedėme n+1 problema. Norime ją pašalinti suteikdami vartotojui galimybę kontroliuoti, ko jis prašo šiame galutiniame taške. Taigi jis turėtų galėti įkelti tik knygas arba perduoti parametrą includes ir gauti autorius, bet pageidautina be n+1.
Apibrėžkime konstantą, kuri saugos informaciją apie tai, kokius knygų asocijuotuosius narius vartotojas gali įtraukti į books#index veiksmai:
# lib/constants/books/includes.rb
modulis Constants
modulis Books
modulis Includes
ALLOWED = {
indeksas: %i[
autorius
].freeze
}.freeze
pabaiga
pabaiga
end
Toliau apibrėžiame vardų erdvę, skirtą tuščių objektų konstantoms:
# lib/constants/empty.rb
modulis Constants
modulis Empty
HASH = {}.freeze
pabaiga
pabaiga
O štai mūsų pagrindinė leidimų išdavimo paslauga apima. Manau, kad kodas yra gana aiškus, kai kurios dalys magija skiriami tik #default_resources_key ir #default_purpose. Šie metodai apibrėžti tam, kad būtų galima mus iškviesti leidimą apima tik params perdavimą rails valdikliuose. Išvestis bus hash, kuriame saugoma tiesa kiekvienam leidžiamam įtraukimui.
# app/services/permit_includes.rb
require 'constants/empty'
require 'constants/books/includes'
klasė PermitIncludes
Empty = Constants::Empty
COMMA = ','
SLASH = '/'
INCLUDES_FORMAT = /A[a-z]+(,[a-z]+)*z/.freeze
ALLOWED_INCLUDES = {
books: Constants::Books::Includes::ALLOWED
}.freeze
def call(params, resources: default_resources_key(params), purpose: default_purpose(params))
return Empty::HASH unless includes_sent?(params)
return Empty::HASH unless includes_valid?(params)
requested_includes = parse_includes(params)
allowed_includes = filter_includes(requested_includes, resources, purpose)
allowed_includes.index_with(true)
pabaiga
privatus
def default_resources_key(params)
raise(ArgumentError, 'params :controller key must be a string') unless params[:controller].is_a?(String)
params[:controller].split(SLASH).last&.to_sym
end
def default_purpose(params)
raise(ArgumentError, 'params :action key must be a string') unless params[:action].is_a?(String)
params[:action].to_sym
end
def includes_sent?(params)
params.key?(:includes)
end
def includes_valid?(params)
return false nebent params[:includes].is_a?(String)
params[:includes].match?(INCLUDES_FORMAT)
end
def parse_includes(params)
params[:includes].split(COMMA).map(&:to_sym)
end
def filter_includes(requested_includes, resources_key, purpose)
requested_includes & ALLOWED_INCLUDES[resources_key][purpose]
end
end
Dabar turime naudoti raktus, kad įkeltume "Includes" ir perduotume patį "Inlcudes" hash serijalizatoriui:
# app/controllers/api/v1/books_controller.rb
API modulis
modulis V1
klasė BooksController < BaseController
def index
includes = PermitIncludes.new.call(params)
books = Book.includes(includes.keys).all
atvaizduoti json: KnygosBlueprint.render(books, includes: includes)
pabaiga
end
end
end
Būtent taip turime pritaikyti savo serializatorių - asociaciją įkeliame tik tuo atveju, jei ji įtraukta:
# app/blueprints/book_blueprint.rb
klasė BookBlueprint (_field_name, _book, options) {
options[:includes] && options[:includes][:author]
}
end
Išbandykime tai dar kartą:
bėgiai s
garbanoti http://localhost:3000/api/v1/books
# => [{"id":1, "title": "Trys muškietininkai"},{"id":2, "title": "Liūtas, ragana ir drabužių spinta"},{"id":3, "title": "Švarus kodas"}]
# užklausų žurnalai (įkeliame tik knygas)
Pradėta GET "/api/v1/knygos" ::1 2021-12-24 10:33:41 +0100
API::V1::BooksController#index apdorojama kaip */*
(0.1 ms) SELECT sqlite_version(*)
↳ app/controllers/api/v1/books_controller.rb:8:in `index'
Knygų įkėlimas (0.1 ms) SELECT "books".* FROM "books"
↳ app/controllers/api/v1/books_controller.rb:8:in `index'
Užbaigta 200 OK per 9 ms (Peržiūros: 0,1 ms | ActiveRecord: 0,9 ms | Alokacijos: 4548)
Gerai, mes neperdavė apima, todėl gavo tik knygas, be autorių. Dabar jų paprašykime:
curl 'http://localhost:3000/api/v1/books?includes=author'
# => [{"id":1, "author":{"id":1, "name": "Alexandre Dumas"}, "title": "Trys muškietininkai"},{"id":2, "author":{"id":2, "name": "C.S. Lewis"}, "title": "The Lion, the Witch and the Wardrobe"},{"id":3, "author":{"id":3, "name": "Robert C. Martin"}, "title": "Clean Code"}]%
# užklausų žurnalai (pašalinta n+1)
Pradėta GET "/api/v1/books?includes=author" ::1 2021-12-24 10:38:23 +0100
API::V1::BooksController#index apdorojama kaip */*
Parametrai: {"includes"=>"author"}
Knygos įkėlimas (0,1 ms) SELECT "books".* FROM "books"
↳ app/controllers/api/v1/books_controller.rb:8:in `in index'
Autorių įkėlimas (0.2 ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" IN (?, ?, ?) [["id", 1], ["id", 2], ["id", 3]]]
↳ app/controllers/api/v1/books_controller.rb:8:in `in index'
Užbaigta 200 OK per 17 ms (Peržiūros: 0,1 ms | ActiveRecord: 0,7 ms | Alokacijos: 7373)
Šaunu! Asociacija įkelta ir pašalinta n+1 problema. Paslauga gali būti naudojama bet kokiam ištekliui, viskas, ką norime padaryti, tai pridėti leistinas konstantas tinkamu formatu ir pridėti jas prie PermitIncludes::ALLOWED_INCLUDES.
Reikia nepamiršti, kad šią funkciją tikriausiai reikėtų naudoti kartu su puslapiais (ir atsargiai), nes asociacijų įtraukimas gali “suvalgyti” daug atminties.