5 näidet Ruby parimast kasutamisest
Kas olete kunagi mõelnud, mida me saame teha Ruby'ga? Noh, taevas on ilmselt piirideta, kuid me räägime hea meelega mõnest rohkem või vähem teadaolevast juhtumist...

Me ehitame raamatute riiuli rakenduse, et loetleda raamatuid koos (või ilma) autorite andmetega.
Me ehitame raamatute riiuli rakenduse, et loetleda raamatuid koos (või ilma) autorite andmetega. Seal saab olema üks #index
tegevus ja mõned seemned. See on näidisrakendus, mis näitab, kuidas saab anda kasutajale kontrolli lisatud alamressursid REST-ish APIs.
sisaldab
päringuparameeter seotud ressursside laadimiseks (autor
).sisaldab
päringuparameetril on stringi formaat: komadega eraldatud sõnad, mis esindavad üksteise sisseehitatud ressursse.Me kasutame blueprinter
kui seriaalaja, sest see on formaatidest sõltumatu ja üsna paindlik. See on ainus pärl, mille me lisame rails'i standardse tööriistakomplekti.
Loome näidisrakenduse. Me ei lisa testraamistikku, kuna see ei kuulu meie valdkonda.
rööbasteed uus raamaturiiul -T
Nüüd loo Autor
mudel:
rails g mudeli autori nimi:string
#=> invoke active_record
#=> create db/migrate/20211224084524_create_authors.rb
#=> create app/models/author.rb
Ja Raamat
:
rööbasteed g mudel raamat autor:viited pealkiri:string
# => invoke active_record
# => create db/migrate/20211224084614_create_books.rb
# => luua app/models/book.rb
Me vajame seemneid:
# db/seeds.rb
dumas = Author.create(name: 'Alexandre Dumas')
lewis = Author.create(name: 'C.S. Lewis')
martin = Author.create(name: 'Robert C. Martin')
Book.create(author: dumas, title: 'Kolm musketäri')
Book.create(author: lewis, title: "The Lion, the Witch and the Wardrobe")
Book.create(author: martin, title: 'Clean Code')
Ja nüüd oleme valmis migratsiooni käivitamiseks ja andmebaasi külvamiseks:
rails db:migrate && rails db:seed
Lisame has_many
raamatute jaoks Autor
mudel:
# app/models/author.rb
class Autor < ApplicationRecord
has_many :books
end
On aeg kirjutada kontroller, mis tagastab meie andmed. Me kasutame API
nimeruumi, seega lisame kõigepealt akronüümi inflections:
# config/initializers/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'API'
end
Okei, lisame meie serializer'i, et Gemfile
:
# Lisa Gemfile'ile
gem 'blueprinter'
Ja muidugi paigaldage see:
komplekti paigaldamine
Siis saame ehitada oma plaanid:
# app/blueprints/author_blueprint.rb
class AuthorBlueprint < Blueprinter::Base
identifier :id
väljad :name
end
# app/blueprints/book_blueprint.rb
class BookBlueprint < Blueprinter::Base
identifier :id
väljad :title
association :author, blueprint: AuthorBlueprint
end
Lisage baaskontroller API
:
# app/controllers/api/v1/base_controller.rb
moodul API
moodul V1
class BaseController < ActionController::API
end
end
end
Ja meie eelnõu versioon BooksController
:
# app/controllers/api/v1/books_controller.rb
moodul API
moodul V1
class BooksController < BaseController
def index
books = Book.all
render json: BookBlueprint.render(books)
end
end
end
end
Loomulikult peame määratlema ka marsruutimise:
# config/routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :books, only: :index
end
end
end
Katsetame, mida oleme seni teinud:
rööpad s
curl http://localhost:3000/api/v1/books
# => [{"id":1, "author":{"id":1, "name": "Alexandre Dumas"}, "title": "The Three Musketeers"},{"id":2, "author":{"id":2, "name": "C.S. Lewis"}, "title": "Lõvi, nõid ja riidekapp"},{"id":3, "author":{"id":3, "name": "Robert C. Martin"}, "title": "Clean Kood"}]
Andmed tunduvad olevat korras, aga kuidas on lood logidega?
# taotluse logid (n+1)
Käivitati GET "/api/v1/books" 127.0.0.1 jaoks kell 2021-12-24 10:19:40 +0100
Töötlemine API::V1::BooksController#index poolt nagu */*
Raamatu laadimine (0.1ms) SELECT "books".* FROM "books"
↳ app/controllers/api/v1/books_controller.rb:7:in `index'
Autorite laadimine (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/controllers/api/v1/books_controller.rb:7:in `index'
Autorite laadimine (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
↳ app/controllers/api/v1/books_controller.rb:7:in `index'
Autorite laadimine (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
↳ app/controllers/api/v1/books_controller.rb:7:in `index'
Lõpetatud 200 OK 6ms jooksul (Views: 0.1ms | ActiveRecord: 0.4ms | Allocations: 3134)
Kasutades assotsiatsiooni meie seriaatorites, võtsime kasutusele n+1
probleem. Me tahame selle kõrvaldada, lisades kasutajale kontrolli selle üle, mida ta selles lõpp-punktis taotleb. Nii et ta peaks saama kas laadida ainult raamatuid või anda üle parameeter includes ja saada ka autorid, kuid soovitavalt ilma n+1
.
Määratleme konstandi, mis hoiab teavet selle kohta, milliseid raamatute assocs kasutaja saab lisada books#index
tegevus:
# lib/constants/books/includes.rb
moodul Constants
moodul Books
moodul Includes
ALLOWED = {
indeks: %i[
autor
].freeze
}.freeze
end
end
end
Järgmisena määratleme nimeruumi tühjade objektikonstantide jaoks:
# lib/constants/empty.rb
moodul Konstandid
moodul Tühi
HASH = {}.freeze
end
end
Ja siin on meie peamine teenus lubade andmiseks sisaldab. Ma arvan, et kood on üsna iseenesestmõistetav, mõned tükid magic
eraldatakse ainult #default_resources_key
ja #default_purpose
. Need meetodid on defineeritud selleks, et võimaldada meil kutsuda permit include'i ainult parameetrite edastamisega rails'i kontrollerites. Väljundiks on hash, mis salvestab tõsi
iga lubatud kaasamise puhul.
# app/services/permit_includes.rb
require 'constants/empty'
require 'constants/books/includes'
class 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, välja arvatud juhul, kui 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)
end
private
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 unless 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
Nüüd peame kasutama võtmeid, et laadida include'id ja edastada inlcudes hash ise serializerile:
# app/controllers/api/v1/books_controller.rb
moodul API
moodul V1
class BooksController < BaseController
def index
includes = PermitIncludes.new.call(params)
books = Book.includes(includes.keys).all
render json: BookBlueprint.render(books, includes: includes)
end
end
end
end
Ja see on see, kuidas me peame oma serializer'i sättima - me laadime assotsiatsiooni ainult siis, kui see sisaldub:
# app/blueprints/book_blueprint.rb
class BookBlueprint (_väli_nimi, _raamat, valikud) {
options[:includes] && options[:includes][:author]
}
end
Katsetame seda uuesti:
rööpad s
curl http://localhost:3000/api/v1/books
# => [{"id":1, "title": "Kolm musketäri"},{"id":2, "title": "Lõvi, nõid ja riidekapp"},{"id":3, "title": "Puhas kood"}]
# taotluse logid (me laadime ainult raamatuid)
Käivitati GET "/api/v1/books" jaoks ::1 kell 2021-12-24 10:33:41 +0100
Töötlemine API::V1::BooksController#indeksina */*
(0.1ms) SELECT sqlite_version(*)
↳ app/controllers/api/v1/books_controller.rb:8:in `index'
Raamatu laadimine (0.1ms) SELECT "books".* FROM "books"
↳ app/controllers/api/v1/books_controller.rb:8:in `index'
Lõpetatud 200 OK 9ms jooksul (Views: 0.1ms | ActiveRecord: 0.9ms | Allocations: 4548)
Hea, me ei ole läbinud sisaldab nii sai ainult raamatuid, ilma autorid. Taotleme nüüd neid:
curl 'http://localhost:3000/api/v1/books?includes=author'
# => [{"id":1, "author":{"id":1, "name": "Alexandre Dumas"}, "title": "The Three Musketeers"},{"id":2, "author":{"id":2, "name": "C.S. Lewis"}, "pealkiri": "Lõvi, nõid ja riidekapp"},{"id":3, "autor":{"id":3, "nimi": "Robert C. Martin"}, "pealkiri": "Puhas kood"}]%
# taotluse logid (kõrvaldatud n+1)
Käivitati GET "/api/v1/books?includes=author" jaoks ::1 kell 2021-12-24 10:38:23 +0100
Töötlemine API::V1::BooksController#indeksina */*
Parameetrid: {"includes"=>"author"}
Raamatu laadimine (0.1ms) SELECT "books".* FROM "books"
↳ app/controllers/api/v1/books_controller.rb:8:in `index'
Autorite laadimine (0.2ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" IN (?, ?, ?) [["id", 1], ["id", 2], ["id", 3]]
↳ app/controllers/api/v1/books_controller.rb:8:in `index'
Lõpetatud 200 OK 17ms jooksul (Views: 0.1ms | ActiveRecord: 0.7ms | Allocations: 7373)
Lahe! Me saime assotsiatsiooni koormatud ja kõrvaldatud n+1
probleem. Teenust saab kasutada mis tahes ressursi jaoks, kõik, mida me tahame teha, on lisada lubatud inlcudes konstandid õiges formaadis ja lisada need aadressile PermitIncludes::ALLOWED_INCLUDES
.
Me peame meeles pidama, et seda tuleks ilmselt kasutada koos leheküljeotsinguga (ja ettevaatlikult), sest assotsiatsioonide lisamine võib "süüa" palju mälu.