Vaporの生みの親である Tanner が来日し参加してくれます。Vaporの紹介や他のプログラミング言語に比べる優位性、Vaporの未来や、USやヨーロッパでの浸透状況について話していただきます。
/// public var SERVER_MORE_RESULTS_EXISTS: MySQLStatusFlag = 0x0008
^ この辺の空のドキュメンテーションコメントが自動生成っぽい。NOW()
を使うのが間違ってる様に思える。 https://github.com/vapor/fluent/issues/464UTC_TIMESTAMP()
を使うべきでは。TIMESTAMP
カラムではなくDATETIME
カラムなのか。Swift.Date
はUTCですよね。vapor-clean
に、docker-compse
とHerokuの設定を追加したテンプレートを作った。 https://github.com/norio-nomura/vapor-cleanFuture
を表に出さない実装になってますね。rename
システムコールをまず試してダメだったら FileManager.moveItem
を試すというのをやったけど、corelibs-Foundationではそのケースが未実装なのかー。 https://github.com/Carthage/Carthage/pull/713 (edited)rename
使ってるのか(これはcontributionチャンス)root@7214eacbf30e:/var/vapor# swift a.swift Fatal error: enumerateSubstrings(in:options:using:) is not yet implemented: file Foundation/NSString.swift, line 791 /usr/bin/swift[0x3f24d54] /usr/bin/swift[0x3f25096] /lib/x86_64-linux-gnu/libpthread.so.0(+0x11390)[0x7f5a99805390] /usr/lib/swift/linux/libswiftCore.so(+0x2baf79)[0x7f5a94a43f79] /usr/lib/swift/linux/libswiftCore.so(_T0s17_assertionFailures5NeverOs12StaticStringV_SSAE4fileSu4lines6UInt32V5flagstF+0x2c)[0x7f5a948d646c] /usr/lib/swift/linux/libFoundation.so(+0x501c3f)[0x7f5a8dac7c3f] /usr/lib/swift/linux/libFoundation.so(+0x501b0a)[0x7f5a8dac7b0a] /usr/lib/swift/linux/libFoundation.so(_T010Foundation8NSStringC14enumerateLinesyySS_SpyAA8ObjCBoolVGtcF+0x77)[0x7f5a8db559c7] /usr/lib/swift/linux/libFoundation.so(_T0s14StringProtocolP10FoundationSS5IndexVADRtzrlE14enumerateLinesyySS_Sbztc8invoking_tF+0xa0)[0x7f5a8db6bed0] [0x7f5a99c350c3] /usr/bin/swift[0xfed1ce] /usr/bin/swift[0xff1692] /usr/bin/swift[0x4d9076] /usr/bin/swift[0x4c35d3] /usr/bin/swift[0x4beecc] /usr/bin/swift[0x4778c4] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f5a97f2f830] /usr/bin/swift[0x475179] Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret a.swift -disable-objc-interop -color-diagnostics -module-name a Illegal instruction
(edited)root@7214eacbf30e:/var/vapor# swiftc a.swift root@7214eacbf30e:/var/vapor# ./a Fatal error: enumerateSubstrings(in:options:using:) is not yet implemented: file Foundation/NSString.swift, line 791 Illegal instruction
libunwind
.swift
コマンドだとswift
コマンドが設定したシグナルハンドラが受け取ってくれます。swiftc
で作成された成果物には、スタックトレースを出力するシグナルハンドラは設定されません。fatalError()
はシグナル経由ではなくて、fatalError()
> _assertionFailure()
> _swift_stdlib_reportFatalError*()
> swift_reportError()
> reportNow()
> printCurrentBacktrace()
なのかな。fatalError()
をユーザーコードで呼び出した場合は https://github.com/apple/swift/blob/master/stdlib/public/runtime/Errors.cpp#L275 のprintCurrentBacktrace()
が呼ばれ、NSUnimplemented()
経由だとprintCurrentBacktrace()
は呼ばれずシグナルハンドラの挙動に依存する、という違いがあると知りました。-assert-config
でコントロールできそうにも見える。 -assert-config <value> Specify the assert_configuration replacement. Possible values are Debug, Release, Unchecked, DisableReplacement.
(edited)fatalError()
Fatal error: file <stdin>, line 1 #0 0x0000000003f25074 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x3f25074) #1 0x0000000003f253b6 SignalHandler(int) (/usr/bin/swift+0x3f253b6) #2 0x00007f662e570390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #3 0x00007f6629e5a0b9 _T0s17_assertionFailures5NeverOs12StaticStringV_SSAE4fileSu4lines6UInt32V5flagstFTf4nxnnn_n (/usr/lib/swift/linux/libswiftCore.so+0x2bb0b9) #4 0x00007f6629cec41c _T0s17_assertionFailures5NeverOs12StaticStringV_SSAE4fileSu4lines6UInt32V5flagstF (/usr/lib/swift/linux/libswiftCore.so+0x14d41c) #5 0x00007f662e9a006e #6 0x0000000000fed3be llvm::MCJIT::runFunction(llvm::Function*, llvm::ArrayRef<llvm::GenericValue>) (/usr/bin/swift+0xfed3be) #7 0x0000000000ff1882 llvm::ExecutionEngine::runFunctionAsMain(llvm::Function*, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, char const* const*) (/usr/bin/swift+0xff1882) #8 0x00000000004d9076 swift::RunImmediately(swift::CompilerInstance&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, swift::IRGenOptions&, swift::SILOptions const&) (/usr/bin/swift+0x4d9076) #9 0x00000000004c35d3 performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*) (/usr/bin/swift+0x4c35d3) #10 0x00000000004beecc swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) (/usr/bin/swift+0x4beecc) #11 0x00000000004778c4 main (/usr/bin/swift+0x4778c4) #12 0x00007f662cc9a830 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x20830) #13 0x0000000000475179 _start (/usr/bin/swift+0x475179) Stack dump: 0. P
fatalError()
Fatal error: file <stdin>, line 1 Current stack trace: 0 libswiftCore.so 0x00007f18b22e0680 _swift_stdlib_reportFatalErrorInFile + 221 1 libswiftCore.so 0x00007f18b204f38c <unavailable> + 1368972 2 libswiftCore.so 0x00007f18b22892f2 <unavailable> + 3703538 3 libswiftCore.so 0x00007f18b228a759 <unavailable> + 3708761 4 libswiftCore.so 0x00007f18b204ea86 <unavailable> + 1366662 5 libswiftCore.so 0x00007f18b22890bb <unavailable> + 3702971 6 libswiftCore.so 0x00007f18b204ea86 <unavailable> + 1366662 7 libswiftCore.so 0x00007f18b21bc0b9 <unavailable> + 2863289 8 libswiftCore.so 0x00007f18b204e3f0 _assertionFailure(_:_:file:line:flags:) + 44 10 swift 0x0000000000fed3be <unavailable> + 12506046 11 swift 0x0000000000ff1882 <unavailable> + 12523650 12 swift 0x00000000004d9076 <unavailable> + 888950 13 swift 0x00000000004c35d3 <unavailable> + 800211 14 swift 0x00000000004beecc <unavailable> + 782028 15 swift 0x00000000004778c4 <unavailable> + 489668 16 libc.so.6 0x00007f18b4ffc740 __libc_start_main + 240 17 swift 0x0000000000475179 <unavailable> + 479609 #0 0x0000000003f25074 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x3f25074) #1 0x0000000003f253b6 SignalHandler(int) (/usr/bin/swift+0x3f253b6) #2 0x00007f18b68d2390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #3 0x00007f18b21bc0b9 _T0s17_assertionFailures5NeverOs12StaticStringV_SSAE4fileSu4lines6UInt32V5flagstFTf4nxnnn_n (/usr/lib/swift/linux/libswiftCore.so+0x2bb0b9) #4 0x00007f18b204e41c _T0s17_assertionFailures5NeverOs12StaticStr
-assert-config Release
でprintCurrentBacktrace()
は呼ばれなくなる。@usableFromInline @_transparent internal func _fatalErrorFlags() -> UInt32 { // The current flags are: // (1 << 0): Report backtrace on fatal error #if os(iOS) || os(tvOS) || os(watchOS) return 0 #else return _isDebugAssertConfiguration() ? 1 : 0 #endif }
(edited)(lldb) attach 572 error: attach failed: Failed to connect port
// Linux root@031ab2443e1a:/var/vapor# lldb-server v lldb version 5.0.0 (git@github.com:apple/swift-lldb.git revision 76dfa56ed35eaa392f7e51088c08f08f1150d142) Swift-4.1 (revision f01501c324876fc07820dc28923d7088fb7af847) clang revision cd84be6c4294f9ec302c20c63a601cbaeaa6a017 llvm revision cf364153438b3ac07a4a7d721159936e439ba2e7
(edited)[omochi@omochi-iMac-PC43 ~]$ lldb --version lldb-360.0.0 (buildbot 2018-03-29) Swift-4.1 (revision f01501c324876fc07820dc28923d7088fb7af847) clang revision cd84be6c4294f9ec302c20c63a601cbaeaa6a017 llvm revision cf364153438b3ac07a4a7d721159936e439ba2e7
lldb-server
試した。docker-compose.yml
にprivileged: true
を追加。lldb-server
起動オプションに--min-gdbserver-port 31167 --max-gdbserver-port 31266
を追加。docker-compose.yml
のports
に- "31166-31266:31166-31266"
を追加。docker exec -it vapor lldb-server platform --listen "*:31166" --server --min-gdbserver-port 31167 --max-gdbserver-port 31266
でlldb-server
を起動。$ xcrun lldb (lldb) platform select remote-linux Platform: remote-linux Connected: no (lldb) platform connect connect://172.16.241.138:31166 Platform: remote-linux Triple: x86_64-*-linux-gnu OS Version: 4.9.93 (4.9.93-boot2docker) Kernel: #1 SMP Thu Jul 19 18:29:50 UTC 2018 Hostname: 88958a83a317 Connected: yes WorkingDir: /var/vapor (lldb) platform process list 4 matching processes were found on "remote-linux" PID PARENT USER TRIPLE NAME ====== ====== ========== ======================== ============================ 1 0 (null) x86_64-*-linux dash 338 1 x86_64-*-linux vapor 563 338 x86_64-*-linux Run 568 0 x86_64-*-linux lldb-server (lldb) platform process attach --pid 563 Process 563 stopped * thread #1, name = 'Run', stop reason = signal SIGSTOP frame #0: 0x00007f8f41cfe360 libpthread.so.0`__pthread_cond_wait + 192 libpthread.so.0`__pthread_cond_wait: -> 0x7f8f41cfe360 <+192>: movl (%rsp), %edi 0x7f8f41cfe363 <+195>: callq 0x7f8f41d011c0 ; __pthread_disable_asynccancel 0x7f8f41cfe368 <+200>: movq 0x8(%rsp), %rdi 0x7f8f41cfe36d <+205>: movl $0x1, %esi thread #2, name = 'Run', stop reason = signal SIGSTOP frame #0: 0x00007f8f41d00827 libpthread.so.0`do_futex_wait.constprop.3 + 55 …
(edited)docker-machine
を使ってるので、macOSのlldb
からの接続先が127.0.0.1ではないのが違うくらいかな。--min-gdbserver-port
を指定してるコマンドラインは見かけていたのですが、それについて調べても情報が出てこなかったのでスルーしていました。gdbの互換モードみたいな特殊な使い方があるのかな?みたいな。swift build --build-tests
すると、Xcode 12.01.1版は2分14秒、swift-5.3 OSS ToolchainのmacOS版は8分43秒かかった。 (edited)$ du -m $(DEVELOPER_DIR=/Applications/Xcode_11.7.app xcrun --find swift) $(xcrun --toolchain org.swift.525202008051a --find swift) $(xcrun --find swift) $(xcrun --toolchain org.swift.530202009161a --find swift) 39 /Applications/Xcode_11.7.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift 162 /Library/Developer/Toolchains/swift-5.2.5-RELEASE.xctoolchain/usr/bin/swift 97 /Applications/Xcode_12.0.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift 179 /Library/Developer/Toolchains/swift-5.3-RELEASE.xctoolchain/usr/bin/swift
(edited)--preset="buildbot_osx_package"
だからアサーションついてるぽい。 https://ci.swift.org/view/Swift%205.3/job/oss-swift-5.3-package-osx/
[preset: buildbot_osx_package] mixin-preset= mixin_osx_package_base mixin_osx_package_test mixin_lightweight_assertions,no-stdlib-asserts … # A mixin that enables 'lightweight' assertions that don't slow down the # compiler significantly. [preset: mixin_lightweight_assertions] assertions
https://github.com/apple/swift/blob/main/utils/build-presets.ini#L1306-L1310mixin_lightweight_assertions
が使われてる。 (edited)mixin_lightweight_assertions
のコメント、4倍遅いのはsignificantlyではないということなのか… Uncaught TypeError: Cannot read property 'getChildren' of undefined
JSがTypeErrorになったのでLeafがなんかおかしいのかな、、と思ったのですが、、、document.querySelector("#some-id")
とかがあるなら、この#に\を付ける必要があります。$('#loading')
のがいっぱいあるんですけど、それにエスケープをしていく必要があるんですね。。。#FAFAFA
はそのままでOKですか?<li class="nav-item" style="width: 140px;"> <a href="#tab1" class="nav-link py-1 active" data-toggle="tab"> <h6 style="font-size: large;"><i class="far fa-folder-tree"></i> Structure</h6> </a> </li> <li class="nav-item" style="width: 140px;"> <a href="#tab2" class="nav-link py-1" data-toggle="tab"> <h6 style="font-size: large;"><i class="fal fa-file-code"></i> Syntax</h6> </a> </li>
^ Bootstrapでこういうタブを作る指定があるんですけどこれもダメでしたね。"""
文字列で書いたほうが楽そう。\( )
で変数埋め込みすればほぼ衝突しないだろうし、#
を\#
に変える必要がある。 (edited)#
を\#
だからまだマシ (edited)app.get(/^\/([a-f0-9]{32})$/i, async (req, res) => {
^ Node.jsでいうこういうのを書きたいのですが、ちょっと調べた限り無理そう。class
が className
とかapp.get("*") { req -> EventLoopFuture<EventLoopFuture<View>> in let pattern = try! NSRegularExpression(pattern: #"^\/([a-f0-9]{32})$"#, options: [.caseInsensitive]) let matches = pattern.matches(in: req.url.path, options: [], range: NSRange(location: 0, length: NSString(string: req.url.path).length)) guard matches.count == 1 && matches[0].numberOfRanges == 2 else { throw Abort(.notFound) } let gistId = NSString(string: req.url.path).substring(with: matches[0].range(at: 1)) let promise = req.eventLoop.makePromise(of: EventLoopFuture<View>.self) let session = URLSession(configuration: .default) let request = URLRequest(url: URL(string: "https://api.github.com/gists/\(gistId)")!) session.dataTask(with: request) { (data, response, error) in if let error = error { promise.fail(error) return } guard let data = data else { promise.fail(Abort(.notFound)) return } guard let contents = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], let files = contents["files"] as? [String: Any], let filename = files.keys.first, let file = files[filename] as? [String: Any], let content = file["content"] as? String else { promise.fail(Abort(.notFound)) return } promise.succeed( req.view.render( "index", [ "title": "Swift AST Explorer", "defaultSampleCode": content, "swiftVersion": swiftVersion, ] ) ) } .resume() return promise.futureResult }
VaporでAsyncでビューを返すのってこれであってます?動いてるんですけど EventLoopFuture<EventLoopFuture<View>>
となってるところがホントに合ってるのかな?と。EventLoopFuture<View>
じゃないですかね?コンパイル通りますか?// ここをこうして let promise = req.eventLoop.makePromise(of: View.self) // ここをこう req.view.render( "index", [ "title": "Swift AST Explorer", "defaultSampleCode": content, "swiftVersion": swiftVersion, ] ).cascase(to: promise)
(edited).cascase(to: promise)
っていうのがあるんですね!request.client
がありますよ。/build/Sources/App/routes.swift:31:23: error: 'URLSession' is unavailable: This type has moved to the FoundationNetworking module. Import that module to use it. let session = URLSession(configuration: .default) ^~~~~~~~~~ Foundation.URLSession:2:18: note: 'URLSession' has been explicitly marked unavailable here public typealias URLSession = AnyObject
確かにFoundationNetworkingに移ったって書いてありましたわ。 return Response( status: .ok, headers: ["Content-Type": "text/html; charset=UTF-8"], body: Response.Body(string: html) )
View
型だとどうなってるんだろうな。public struct View: ResponseEncodable { public var data: ByteBuffer public init(data: ByteBuffer) { self.data = data } public func encodeResponse(for request: Request) -> EventLoopFuture<Response> { let response = Response() response.headers.contentType = .html response.body = .init(buffer: self.data) return request.eventLoop.makeSucceededFuture(response) } }
return view.encodeResponse(for: request).map { (response) in var response = response response.status = .notFound return response }
@discardableResult public func get<Response>( _ path: PathComponent..., use closure: @escaping (Request) throws -> Response ) -> Route where Response: ResponseEncodable { return self.on(.GET, path, use: closure) }
Response
は型パラメータ名だけど、 同じ名前の Response
型 もある )struct CSRFProtection { struct Key: StorageKey { typealias Value = CSRFProtection } struct Post: Decodable { var csrfToken: UUID? } let sessionKey = "csrfToken" struct Middleware: Vapor.Middleware { func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> { let eventLoop = request.eventLoop guard request.method == .POST else { return next.respond(to: request) } let verify = eventLoop.future { try request.verifyCSRFToken() } return verify.flatMap { () in next.respond(to: request) } } } var middleware: Middleware { Middleware() } }
respond (to:, chainingTo)
メソッドを実装するだけなので。next.respond(to: request).flatMapError{ (error) in ... switch error { // errorの型をがんばって404と500を振り分ける case ... }
(edited)http://localhost:8080/b4f866efb1c1dc63b0a9cce000cf5688
こういうPathを受けるために /*
で受けてると難しい気がするな。struct RouteNotFound: Error { } extension RouteNotFound: AbortError { static var typeIdentifier: String { "Abort" } var status: HTTPResponseStatus { .notFound } }
final class ErrorMiddleware: Middleware { func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> { return next.respond(to: request).flatMapError { (error) in let headers: HTTPHeaders let status: HTTPResponseStatus let reason: String let title: String switch error { case let abort as AbortError: headers = abort.headers status = abort.status title = "Not Found" reason = status == .notFound ? "Sorry, an error has occured, Requested page not found!" : abort.reason default: headers = [:] status = .internalServerError title = "Internal Server Error" reason = "Something went wrong." } return request.view.render("error", [ "title": title, "status": "\(status.code)", "reason": reason, ]) .encodeResponse(status: status, headers: headers, for: request) } } }
ミドルウェアはとりあえずこんな感じにしました。error.leaf
っていうテンプレートがあって、タイトルとかが変わります。<html><body>hello world</body></html>
static var updateDatePropertyKeyPath: KeyPath<PasswordResetModel, TimestampProperty<PasswordResetModel, DefaultTimestampFormat>> { \.$updateDate }
app.post("update") { req -> EventLoopFuture<SyntaxResponse> in
を app.on(.POST, "update", body: .collect(maxSize: "10mb")) { req -> EventLoopFuture<SyntaxResponse> in
でいいのかな。 そうするとルーティングは全部 app.on(.POST
形式に合わせた方が見やすいかな。public final class Routes: RoutesBuilder, CustomStringConvertible { public var all: [Route] /// Default value used by `HTTPBodyStreamStrategy.collect` when `maxSize` is `nil`. public var defaultMaxBodySize: ByteCount /// Default routing behavior of `DefaultResponder` is case-sensitive; configure to `true` prior to /// Application start handle `Constant` `PathComponents` in a case-insensitive manner. public var caseInsensitive: Bool public var description: String { return self.all.description } public init() { self.all = [] self.defaultMaxBodySize = "16kb" self.caseInsensitive = false } public func add(_ route: Route) { self.all.append(route) } } extension Application: RoutesBuilder { public func add(_ route: Route) { self.routes.add(route) } }
on(.POST, "update", body: .collect(maxSize: "10mb"))
の一択っぽい.stream
にして、リクエストハンドラでチマチマ読み出すことはできます#
にエスケープがいる仕様はリファクタリングの過程で別のファイル(〜.cssや〜.js)に分けると今度は戻さないといけないんだけど、忘れると静かに表示が壊れる。@resultBuilder
を使った HTML のテンプレートエンジン的なものがあれば使いやすかったりするでしょうか?目的が違うから微妙?でも API が似てたら両方を触りやすい気も。 (edited)div(id: "foos") { for foo in foos { img(src: "img/\(foo.id).png", alt: "foo") } }
tag("img", ["src": "img/foo.png", "alt": "foo"])
みたいに逃げられる。 (edited)@resultBuilder
で書けたらうれしいこともありそうな?@resultBuilder
でいい気もします。@resultBuilder
だと思うんですけどねぇ・・・。#for(version in versions): <option value="#(version)">#(version)</option> #endfor
こう書くみたいだな。 ドキュメント(1つ前のバージョンしかない)に載ってるのは <ul> #for(planet in planets) { <li>#(planet)</li> } </ul>
#if(version == stableVersion): <option value="#(version)" selected>#(version)</option> #else: <option value="#(version)">#(version)</option> #endif
Ifもこんな感じっぽい。 https://forums.swift.org/t/pitch-new-leaf-body-syntax/18188#
を使うのをやめてほしいと思っているが。。。[]
もJSで使うから厳しそう…"\\\\\\\\"
になりそう{{}}
もだめかも。!
が使えるくらいですね。 あとはカスタムタグで頑張るしかない。カスタムタグ使ったことないですが"Hello #custom(name)"
うーむ。思ってたのと違うなあ。 app.get("*") { req -> EventLoopFuture<View> in guard let pattern = try? NSRegularExpression(pattern: #"^\/([a-f0-9]{32})$"#, options: [.caseInsensitive]) else { throw Abort(.internalServerError) }
^ みたいな正規表現を使ったルーティング、増えてくると大変だから1つ1つ書きたい。swift build
したらどの辺りにエラーが出てくるでしょうね。FIR
プレフィックス付きのが見え隠れしてました。async/await
が来て、 Python の async/await
と連携して、さらに Firebase SDK for Python が async/await
に対応してて、違いがうまく吸収されるようになるという夢のような状況なら連携しやすそうですが、なかなか難しそうですね・・・。Promise
に包むのと剥がすのだけでうまくいく?async
は JS の世界に閉じてるから、単に Promise
を返す関数というだけだから、 JS で Promise
を返す関数を await
したときに、うまく剥がしてやれば良い?async
になっちゃうのか。throws
はどうなってるんですっけ?let divElement = document.createElement!("div").object!
この createElement
とか try
なしで呼べているのはなぜ??async
であることをアノテートする、になりますね。let divElement = document.createElement!.throwing("div").object!
let result = fetch!.async("...")
throwing
も throws
の方が対応がわかりやすそうだけど(英語としてどうかは別として)。throwing
フィールドがあるかも知れないんですよねthrowing
は JSValue
のメソッドではなく、 JS 側で持ってるってことですか?throwing
が出てこないですが、どこに実装あるんでしょうか?JSValue
を受ける関数にするのはどうでしょうか? let foo = try jsTry(jsFoo)
JSValue
の中に throw
されたエラーの情報を閉じ込めておいて。 (edited)func suspendAsync<T>(
^ こういう文字列をLeafで置換すると func suspendAsync(
みたいに <T>
が消えるんだけどどういうことなの<T>
こういう形になってない <
や >
はOKだし何のためにやってるのかわからない。#raw()
ってのが使えたらしいけどすでになく、カスタムタグを作るという方法があるらしいけど、よくわからなかった。カスタムタグを通しても消える。t2.micro
インスタンスではメモリ不足でクラッシュしてしまったt2.small
にしたらビルドできた。$ top
で見てたら swiftプロセスが 900M ぐらいもっていく瞬間があるのは見えたfunc suspendAsync<T>(
の埋め込み こちらではソースには<T>
出てました。@Parent
リレーションは、 self.$parent.id
が non optional で self.parent.id
が optional なのはうまいことできてて良いな。self.parent.id
も non optional になっててほしいけどParentModel.id
そのものが optional なのは レコードの新規作成を考えると正しい。RUN groupadd docker -g ${HOST_DOCKER_GROUP_ID} && \ usermod -a -G docker vapor
HOST_DOCKER_GROUP_ID
はビルドするマシンは別だから一回SSHにログインして取る。TERM=xterm ssh -t -t -o 'StrictHostKeyChecking no' "${{ secrets.SSH_LOGIN_USER }}@${{ secrets.SSH_HOST }}" "grep "^docker:" /etc/group | sed -e 's/^[^:]\+:[^:]\+:\([^:]\+\):[^:]\+$/\1/g'" > gid REMOTE_DOCKER_GID=$(echo $(tail -n 1 gid) | sed -e 's/\r//g')
CIの最初の方でこんな感じでとってる。 デプロイ先は getent
も入ってないOSだからグループIDを取るだけなのに結構ややこしいことをしている。 volumes: - /var/run/docker.sock:/var/run/docker.sock:ro
で、それ以外のDockerのCLIとかはコンテナに別途インストールするんですけど、docker.sockがrootかdockerグループにいないと読めないんですけど、rootかホストと同じGIDのdockerグループ、でしたね。私の環境では。services: app: image: kishikawakatsumi/swift-playground:latest container_name: swift-playground depends_on: - letsencrypt env_file: - .env environment: HOST_PWD: $PWD/
このボリュームを孫にマウントできるようにするのをポータブルにするためにdocker-compose.ymlでホストのカレントディレクトリ を渡しています。こういう書き方ができるのがよかった。let tanner = User(name: "Tanner", age: 23, pets: ["Zizek", "Foo"], dict: ["a": 1, "b": 2], foos: [.baz], nums: [3.14], isCool: true) let ravneet = User(name: "Ravneet", age: 33, pets: ["Piku"], dict: ["a": -3, "b": 99], foos: [.baz, .bar], nums: [3.14, 144], isCool: true) let usersToEncode = Users(users: [tanner, ravneet]) let result = try URLEncodedFormEncoder().encode(usersToEncode) XCTAssert(result.contains("users[0][pets][]=Zizek")) XCTAssert(result.contains("users[0][pets][]=Foo")) XCTAssert(result.contains("users[0][age]=23")) XCTAssert(result.contains("users[0][name]=Tanner")) XCTAssert(result.contains("users[0][dict][a]=1")) XCTAssert(result.contains("users[0][dict][b]=2")) XCTAssert(result.contains("users[0][foos][]=baz")) XCTAssert(result.contains("users[0][nums][]=3.14")) XCTAssert(result.contains("users[0][isCool]=true"))
[ ]
を使ってパスを表すスタイル自体はPHPからあるんですが・・・x-www-form-urlencoded
で規格になってるけど。[
をパーセントエスケープするところがちょっと違うな。<xmp>...</xmp>
で囲うことで<T>みたいなタグっぽい部分の出るようになりました。 ということでLeafは濡れ衣でしたpublic protocol Middleware { func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> } public protocol Responder { func respond(to request: Request) -> EventLoopFuture<Response> }
(edited) func add(_ collection: RouteCollection, csrf: Bool = true, login: Bool = true) throws { var middlewares: [Middleware] = [] if csrf { middlewares.append(csrfProtection.middleware) } if login { middlewares += userLoginGuard(doesRedirect: true) } try routes.group(middlewares) { (routes) in try routes.register(collection: collection) } }
DocumentSymbolRequest.Response
は Codable
なんですけど Conditional conformance of type 'Optional<Wrapped>' to protocol 'Content' does not imply conformance to inherited protocol 'RequestDecodable'
というエラーで、 定義は public struct DocumentSymbolRequest: TextDocumentRequest, Hashable { public static let method: String = "textDocument/documentSymbol" public typealias Response = DocumentSymbolResponse? /// The document in which to lookup the symbol location. public var textDocument: TextDocumentIdentifier public init(textDocument: TextDocumentIdentifier) { self.textDocument = textDocument } }
なので typealias Response = DocumentSymbolResponse?
のところが問題なのかな? ちなみに、 DocumentSymbolResponse
の方は extension DocumentSymbolResponse: Content {}
でOK。 (edited)extension DocumentSymbolRequest.Response: ResponseEncodable {} extension DocumentSymbolRequest.Response: RequestDecodable {} extension DocumentSymbolRequest.Response: Content {}
上の二行を追加するとコンパイルできます。コンパイラのバグっぽい? (edited)protocol Content: Codable, RequestDecodable, ResponseEncodable
ContentっていうのはそもそもCodable + RequestDecodable + ResponseEncodable なんですね。extension DocumentSymbolRequest.Response: Content, RequestDecodable, ResponseEncodable {}
はコンパイルできるけど、 extension DocumentSymbolRequest.Response: RequestDecodable, ResponseEncodable {}
は同じエラーでダメ。does not imply conformance to inherited protocol 'RequestDecodable'
なのでRequestDecodableを書く必要があるって言ってる気がするんですが、一般には Content
だけで済むはずですよね。public protocol ResponseEncodable { func encodeResponse() } public protocol Content: Codable, ResponseEncodable { } extension Content { func encodeResponse() {} } struct MyType: Codable {} extension Optional: Content where Wrapped == MyType {}
<stdin>:13:1: error: conditional conformance of type 'Optional<Wrapped>' to protocol 'Content' does not imply conformance to inherited protocol 'ResponseEncodable' extension Optional: Content where Wrapped == MyType {} ^ <stdin>:13:1: note: did you mean to explicitly state the conformance like 'extension Optional: ResponseEncodable where ...'? extension Optional: Content where Wrapped == MyType {} ^ <stdin>:13:1: error: method 'encodeResponse()' must be declared public because it matches a requirement in public protocol 'ResponseEncodable' extension Optional: Content where Wrapped == MyType {} ^ <stdin>:9:10: note: mark the instance method as 'public' to satisfy the requirement func encodeResponse() {} ^ public
protocol ResponseEncodable {} protocol Content: ResponseEncodable {} //extension Optional: Content {} // OK extension Optional: Content where Wrapped == String {} // NG
(edited)<stdin>:4:1: error: conditional conformance of type 'Optional<Wrapped>' to protocol 'Content' does not imply conformance to inherited protocol 'ResponseEncodable' extension Optional: Content where Wrapped == String {} // NG ^ <stdin>:4:1: note: did you mean to explicitly state the conformance like 'extension Optional: ResponseEncodable where ...'? extension Optional: Content where Wrapped == String {} // NG ^
(edited)Codable
も関係無さそうです。 protocolの親子関係とextensionのwhere句が条件と思われます。app.on(.POST, "shared_link", body: .collect(maxSize: "10mb")) { (req) -> EventLoopFuture<[String: String]> in
VaporはPOSTボディのデフォルトのサイズ制限が意外と小さいっていうのに当たった。app.on(.POST, "shared_link", body: .collect(maxSize: "10mb"))
を現在はすべて指定していくのが正しい、であってます?application.routes.defaultMaxBodySize
routes.on(.POST, body: .collect(maxSize: setting.largePostSize), use: submit)
vapor/queues
は Redisが必要でめんどくさいので、ジョブ実行機を自作した。スケジュール実行も対応。 https://github.com/omochi/VaporJobRunnerDispatchQueue.global().async
がほしいという感じswift build
または swift run
で実行するときとで違う気がするから(後者は.buildを使う)実は普通に使ってると2GB使うはず。。。<
が<
など)されるようになった。 (edited)<pre>
の中)があって困った。 で、これはPackage.resolvedにしか出てこない変更だから見逃した。#
で置き換える部分、HTMLのIDを参照やColor Hexと衝突して500エラーになってしまうから \#
ってしないといけなかったの、しなくてよくなってる。EventLoopFuture<T>
が async T
になっているだけでEventLoop
上での実行になっている+コールバックが戻ってくる点は特に変わらないかType of expression is ambiguous without more context
出まくって大変なので期待send(_ request: ClientRequest) async throws
私の書いてるコードだとこれが入ったのめっちゃ助かる。これでかなりの部分をFutureからAsyncに書き換えられそう。
app.get("runner", ":version", "health") { (req) -> EventLoopFuture<Response> in let promise = req.eventLoop.makePromise(of: Response.self) let process = Process() process.executableURL = URL(fileURLWithPath: "/usr/bin/env") process.terminationHandler = { (process) in let status: HTTPResponseStatus = process.terminationStatus == 0 ? .ok : .internalServerError HealthCheckResponse(status: status) .encodeResponse( status: status, headers: HTTPHeaders([("Cache-Control", "no-store")]), for: req ) .cascade(to: promise) } process.launch() return promise.futureResult }
非同期に呼ばれるCallbackクロージャが普通のクロージャで、その中でさらに非同期処理を呼んでいるというコードがあって、コールバックの中でawaitできない、と思ったけど非同期処理の前でいったん値を返して再度awaitする、っていうのでいけた。 合ってるかな?app.get("runner", ":version", "health") { (req) -> Response in guard let version = req.parameters.get("version") else { throw Abort(.badRequest) } let process = Process() process.executableURL = URL(fileURLWithPath: "/usr/bin/env") let status: HTTPResponseStatus = await withCheckedContinuation { (continuation) in process.terminationHandler = { (process) in let status: HTTPResponseStatus = process.terminationStatus == 0 ? .ok : .internalServerError continuation.resume(returning: status) } process.launch() } return try await HealthCheckResponse(status: status) .encodeResponse( status: status, headers: HTTPHeaders([("Cache-Control", "no-store")]), for: req ) }
こうなった。 encodeResponse
がさらに呼んでいる非同期関数で、それは最初のawaitを抜けた後に呼ぶ、というふうに分解してうまくいった、ということ。app.get("runner", ":version", "health") { (req) -> Response in guard let version = req.parameters.get("version") else { throw Abort(.badRequest) } let process = Process() process.executableURL = URL(fileURLWithPath: "/usr/bin/env") let status: HTTPResponseStatus = await withCheckedContinuation { (continuation) in process.terminationHandler = { (process) in let status: HTTPResponseStatus = process.terminationStatus == 0 ? .ok : .internalServerError continuation.resume(returning: status) } process.launch() } return try await HealthCheckResponse(status: status) .encodeResponse( status: status, headers: HTTPHeaders([("Cache-Control", "no-store")]), for: req ) }
こうなった。 encodeResponse
がさらに呼んでいる非同期関数で、それは最初のawaitを抜けた後に呼ぶ、というふうに分解してうまくいった、ということ。 launch()
の呼び出しと terminationHandler
からの処理の継続を、Continuation APIで async/await にしている。terminationHandler
を上書きするという副作用を extension func asyncLaunch() async
に持たせてしまっていいかはちょっと気になるけど、まあしょうがないかな (edited)Process
型が、 launch
のオプション設定を instance property で代替してるビルダーパターン的なAPIなのでextension Process { static func run(...[Process args]) async -> TerminationResult }
こういうのがあればオッケーな世界観actor LanguageServer {}
としてコールバックを登録するみたいな。return live.and(isLiked).and(participants).and(likeCount)...
↑このコードを+ let likeCount = try await LiveLike.query(on: db) .filter(\.$live.$id == id.rawValue) .count()
↑こんな感じに書き換えている部分がありますけど、.and
で待ち合わせをしているので、await
しているので、async let
の使い所かと思います。flatMapEach
使ってるところもTaskGroup使ってやらないとフローが変わりそう for (index, future) in futures.enumerated() { if future.eventLoop.inEventLoop, let result = future._value { // Fast-track already-fulfilled results without the overhead of calling `whenComplete`. This can yield a // ~20% performance improvement in the case of large arrays where all elements are already fulfilled. processResult(index, result) if case .failure = result { return // Once the promise is failed, future results do not need to be processed. } } else { future.hop(to: eventLoop) .whenComplete { result in processResult(index, result) } } }
self.flatMap { .reduce(into: [], $0.map(transform), on: eventLoop) { $0.append($1) } }
public static func reduce<InputValue>(_ initialResult: Value, _ futures: [EventLoopFuture<InputValue>], on eventLoop: EventLoop, _ nextPartialResult: @escaping (Value, InputValue) -> Value) -> EventLoopFuture<Value> {
@escaping
なクロージャで受け取るのズコー感ありますよねfile:line:
引数が消えてたulimit -n 1024
だとだめで ulimit -n 2048
した記憶があるので、その間かとSystem.coreCount
は10くらいですか/// `NonBlockingFileIO` is a helper that allows you to read files without blocking the calling thread. /// /// It is worth noting that `kqueue`, `epoll` or `poll` returning claiming a file is readable does not mean that the /// data is already available in the kernel's memory. In other words, a `read` from a file can still block even if /// reported as readable. This behaviour is also documented behaviour: /// /// - [`poll`](http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html): "Regular files shall always poll TRUE for reading and writing." /// - [`epoll`](http://man7.org/linux/man-pages/man7/epoll.7.html): "epoll is simply a faster poll(2), and can be used wherever the latter is used since it shares the same semantics." /// - [`kqueue`](https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2): "Returns when the file pointer is not at the end of file." /// /// `NonBlockingFileIO` helps to work around this issue by maintaining its own thread pool that is used to read the data /// from the files into memory. It will then hand the (in-memory) data back which makes it available without the possibility /// of blocking.
kqueue
, epoll
or poll
returning claiming a file is readable does not mean that the data is already available in the kernel's memory. In other words, a read
from a file can still block even if reported as readable. This behaviour is also documented behaviour/usr/share/zoneinfo/
とか読もうとするし、フリースタンディングで完全なFoundation使うのはすごい難しい#if canImport(FoundationNetworking)
が良い感じになってほしい/usr/share/zoneinfo/
とか読もうとするし、フリースタンディングで完全なFoundation使うのはすごい難しい #if canImport(FoundationNetworking)
が良い感じになってほしい import SwiftFoundation
でなんとか闇
ついてて草 ロンドン開催だったのか、行けたら良かったですねDate
はessentialで Calendar
は i18n だと、Date
だけで何の役に立つんだろ? 経過秒数の計測とか?timeIntervalSinceReferenceDate
でunixtime取れそうですし、何かしら時刻の記録だけするような処理はできそうですね。 (資源の限られた環境でログ取りたいときとか?)Date
を使いたい場面は多いから、Entityはessentialsだけをimportして Date
を使い、Presenterがi18nをimportするというのは理に適った分割な気がします。Date
を使いたい場面は多いから、Entityはessentialsだけをimportして Date
を使い、Presenterがi18nをimportするというのは理に適った分割な気がします。 var lastModification: Double
だと、どういう単位でいつを原点にしているのかわからない。 Date
ならそれが明確。 一方で、その絶対時刻をどう表現するか(年月日や文字列表記)はそれとは独立しているので、それらがi18nに分離されてるのはクリーンだと思います。#if
で入ってくるっぽいSocket+WinSock2.swift
あるのウケる (edited)