- 投稿日:2019-11-30T20:36:36+09:00
Rails10年選手がGoの勉強会に参戦してきた
はじめに
静岡でRailsのWebエンジニアをしているkazuomatzです。2019年も残り1ヶ月を切りましたが、どうやら最近は業界的にRailsはオワコンらしいです。
僕は、2007年くらいからRailsを使ってきたので、かれこれ10年以上Railsを触ってきたことになるわけですが、オワコンなんて言われると、このままRailsを使い続けるか、それとも新しい言語に乗り換えるかの岐路に立たされているような気にもなってきます。
Railsが世の中に登場したのが2004年、2019年現在、Railsのバージョンは6.0です。2007年の12月にRailsのバージョン2が公開されていますから、かれこれ、5世代のRailsを使ってきたことになります。
Web2.0とRailsとの出会い
ちょっと昔話になりますが、2000年半ばに「Web」はバージョン1からバージョン2にバージョンバップされました。「Web 2.0」です。
これまでのWeb(=Web 1.0)は、ユーザーが情報を取得するだけのものでした。それがWeb 2.0になると、ユーザーがWebを通じて情報を発信することができるようになりました。そして、Webをプラットフォームとした様々なサービスやビジネスが生み出される時代に向かっていきました。
今となっては、Web 2.0とは結局何だったのかという議論もありますが、ともかく2007年11月に「Web 2.0 Expo」が盛大に開催されました。Web 2.0を提唱したティム・オライリーの講演をはじめ、当時の先進的なWeb関連のセッションが盛りだくさんでした。
当時、僕はJavaプログラマでマッシュアップ関連のWebサービスをいろいろ作っていましたが、そのWeb 2.0 Expoの会場で、Sun Microsystems(当時)のティム・ブレイの衝撃的な一言を聞くことになります。
「Ruby on Railsに触れたJavaプログラマーは、もうJavaには戻ってこないだろう」
Javaの開発元であるSun Microsystemsの中の人がこれ言っちゃったわけですから、大変なことです。少なくともWebが大きく変わるぞって時に、この発言によってRailsは脚光を浴びることになります。恐らく、これきっかけでRails始めた人はかなりいたはずです。
僕もその一人です。あの時からこの10年あまり、Railsに魅了され、RailsでいろいろなWebサービスを開発してきました。
Go勉強会へ
前置きが長くなりました。
ともあれ、Railsオワコン説を聞く中で、10年Railsを触ってきた身として、確かにいろいろ辛いことが多くなってきたなぁとも思います。そんな中見つけたのが、Shizuoka.goの勉強会でした。
勉強会の運営者も以前の仕事仲間(@secondarykey)だったので、Rails10年選手がGoを模索しているというネタで何かしゃべらせてとお願いして、参加することにしました。
ここからは、発表した内容の抜粋です。
Railsのここがよかった
- 設定より規約 / DRY ( Don’t Repeat Yourself )の哲学
- フルスタックなWebフレームワーク MVC / ORM / Asset
- 便利なライブラリ(Ruby Gems)が豊富!!
- お手軽な動的型付言語 Ruby
- 開発効率が半端ない ような気がしてた・・・
とにかくRailsに魅了されていた過去の自分。
Railsがオワコンの原因について
- フロントエンドのフレームワークがいろいろ出てきた
- Webpackなどのモジュールバンドラーが便利
- Railsでイケてたところのほころびが出てきた
僕もVue.jsは結構使うんですが、フロントエンドのフレームワークとRailsとがうまく調和しなくなってきてますね。Webpackについては、Rails向けのWebpackerがありますが、これもいまいち、むしろWebpackをそのまま使う人も多しね。Sprocketsもnode対応したり、ES6対応したりでこれでいけないこともないし・・・。
むしろ、僕が困ったと思っているのは、イケてたところのほころびが出てきたと書いたところ。
RailsってRubyGemsにものすごく多くの便利なライブラリーたちがあって、これをサクッと導入することで、いろいろなことが簡単にできます。もちろん、Ruby/Rails以外の言語やフレームワークでもそのような仕組みはあるわけだけど、Railsはちょっと探すと大抵のやりたいことは、Gem入れれば実現できちゃった。それが、当時のRailsって開発効率いいねという神話を作っていたのかもしれません。
こんなの見ちゃうと、Railsのレールに乗っているイケてる感に拍車かかりますしね・・・。
ところが、10年もいろいろWebサービスを作ってくると、サービス中止になるものもあれば(まぁこちらの方が多いわけですが)、セキュリティ対応やRailsのバージョンアップをしながら保守し続けるサービスもあります。この後者のサービスの延命が辛いんです。
Railsのバージョンアップは痛みを伴います。むしろ作り替えてしまった方が早い場合もあります。いや、作り替えられればまだいいかもしれません。データベースに貯まった様々なデータが特定のライブラリー依存の形で保存されているとかなり辛いです。
例えば、PaperClipという便利なGemがあります。画像ファイルをアップロードすると、Amazon S3などのクラウドストレージにアップロードしてくれる便利なライブラリです。これ本当によく使いました。でも、このPaperClipは、Rails 5.2以降では使えません。開発者の方が開発中止を宣言して、Rails5.2以降では、Railsに標準装備されたActiveStorageを使ってねということになっています。
これから新規に開発するサービスであれば、Rails 6でActiveStorageを使えばよいですが、すでにPaperClipを使ってデータも保存されているサービスをRails 6に移行することは簡単にはできません。
これは、Railsが悪いわけでも、開発を止めてしまったデベロッパーが悪いわけではありません。むしろ便利なGemを使うことでイケてる感を出してきたツケが回ってきたのかなと思います。
今のRuby / Railsの状況
- 2〜3年前に開発したWebサービスがサポート外のRailsで動いている
- とはいえ、Railsのバージョンアップする時間・費用がない
- 慣れ親しんだライブラリーが更新されず、最新のRails環境で使えない
こんな状況をGoは救ってくれるのか
ここからやっとGoの話。こんな状況をGoは救ってくれるのかを検証してみた。
これからもWebで食べていく僕がやっていくことは、こんなこと。
- Webサービスの開発(バックエンドもフロントエンドもやる)
- アプリ開発におけるバックエンドの開発(iOS / Androidアプリも作る)
- AWSを上手に使ってマイクロサービス指向で行く(Azure、GCPにも手を出す)
願うこと
- Web開発に必要なよいフレームワーク
- シンプルに開発ができること
- リファクタリングしやすく保守性が高いこと
発表しながら気づいた。第一にフレームワークを求めちゃうのは、フルスタックな全部入りのRailsにずいぶん甘やかされてきたんだなぁと。Rubyの世界ではWebフレームワークと言ったらほぼRails一択だけど、Goの場合は、軽量級なものからフルスタックなものまで、いろいろなフレームワークが存在する。Routerはこれで、ORMはこれをといった選択することも可能。Railsやってるとこの感覚は確かにない。
デモアプリをGoで作ったよ
僕は、この発表をするにあたり、簡単なWebサイトのモックをGoで作ってみた。
静岡市が運営している市民協働のポータルサイト「ここからネット」。このサイトには市民活動団体の活動情報や開催する講座やイベント情報、ボランティア情報などが掲載されている。そしてこれらデータはオープンデータとして公開されており、Web APIでデータを取得できる。APIのリファレンスもGithubで公開されている。
ここからネットに登録されているデータの中から静岡市の保育施設の一覧を取得して表示するだけのシンプルなものを作ってみた。
できあがりはこんな感じ。
APIからの戻りはこんな感じのJSON。
search.json{ "status": 200, "page": 1, "per_page": 2, "all_page_num": 133, "count": 2, "all_count": 266, "pois": [ { "id": 3450, "name": "静岡大学教育学部附属幼稚園", "prefecture_name": "静岡県", "city_name": "静岡市葵区", "address1": "大岩町1-10", "address2": "", "tel": "054-245-1191", "url": "http://fzk.ed.shizuoka.ac.jp/youchien/", "option_items": [ { "display_name": "分類", "attribute_name": "分類", "value": "従来どおりの幼稚園" }, { "display_name": "設立", "attribute_name": "設立", "value": "国立" }, { "display_name": "受入", "attribute_name": "受入", "value": "3歳~就学前" }, { "display_name": "情報登録日", "attribute_name": "情報登録日", "value": "H29.9.1" } ], "updated_at": "2018/05/09 22:01:39 +0900", "lat": 34.9913406, "lng": 138.3794399 } ] }GoでAPIをリクエストしてJSON Parseしてクライアントに返すプログラムはこんな感じ。
動的言語のRubyと違って静的型付言語のGoにおいては、APIで受け取るJSONの構造体をキチンと書く。/controllers/kindergarten.go// PoiData 受信データ type PoiData struct { Status int `json:"status"` Page int `json:"page"` PerPage int `json:"per_page"` AllPageNum int `json:"all_page_num"` Count int `json:"count"` AllCount int `json:"all_count"` Pois []struct { ID int `json:"id"` Name string `json:"name"` Kana string `json:"kana"` Description string `json:"description"` ZipCode string `json:"zip_code"` PrefectureName string `json:"prefecture_name"` CityName string `json:"city_name"` Address1 string `json:"address1"` Address2 string `json:"address2"` Tel string `json:"tel"` URL string `json:"url"` ImageURL string `json:"image_url"` OptionItems []struct { DisplayName string `json:"display_name"` AttributeName string `json:"attribute_name"` Value string `json:"value"` } `json:"option_items"` StartAt string `json:"start_at"` EndAt string `json:"end_at"` LocationID string `json:"location_id"` ActivityID string `json:"activity_id"` PostPhotoID string `json:"post_photo_id"` InformationID string `json:"information_id"` UpdatedAt string `json:"updated_at"` OrganizationID string `json:"organization_id"` Lat float64 `json:"lat"` Lng float64 `json:"lng"` Marker2X string `json:"marker2x"` Marker string `json:"marker"` Type string `json:"type"` } `json:"pois"` } // GetKinderGarten 保育園情報を取得する func (controller *KinderGartenController) GetKinderGarten() { url := beego.AppConfig.String("endPointURL") + "/map/search.json" page := controller.GetString("page") url += "?page=" + page dataSet := controller.GetString("type") if len(dataSet) == 0 { dataSet = "1,2,3,4,5" } url += "&data_set=" + dataSet response, err := http.Get(url) if err != nil { controller.CustomAbort(500, "Internal server error") return } defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { controller.CustomAbort(500, "Internal server error") return } jsonBytes := ([]byte)(body) data := new(PoiData) if err := json.Unmarshal(jsonBytes, data); err != nil { controller.CustomAbort(500, "Internal server error") return } controller.Data["json"] = data controller.ServeJSON() }フロントエンドはVue.jsを使っている。モジュールバンドルはWebpackだ。ここはGoとは直接関係ない世界。
選択したWebフレームワーク
そして、肝心のWebフレームワークはいろいろ探した結果、最終的にBeego Frameworkを選んだ。
BeegoはRubyのWebフレームワークSinatraを意識して開発されたとのこと。SinatraはRubyのWebフレームワークではRailsの次に使われているもので、フルスタックなRailsよりも軽量なもの。
Beegoは、ORM、Routerも使えて、MVCモデルもいける。CLIもあってコマンド叩いていろいろできる。
実は、自分の発表の前に、元仕事仲間(@secondarykey)がGoのWebフレームワークをいろいろ紹介してくれていた(資料)。彼の所感では、Beegoは「老舗っぽくフルスッタックで色々機能がありそう。重そう。」とのこと。やっぱり、Railsのフルスタック脳がBeegoを選んだ結果は納得できる。
まとめ
Go言語とRubyを比較すると、静的と動的の議論も確かに出てきます。やっぱり静的の方が安全だよねという風潮もありますし、動的の柔軟さが開発効率をあげているのも事実です。
このあたりはトレードオフなので、要件に合わせてどちらが最適かを見極めていくことも必要かなと思います(今まで動的万歳だった自戒も込めて)。
今、自分がサクッとWebサービスを作るとしたら、やはりRailsを選ぶと思います。Rails 6のActiveStorageはよくできていると思いますし、まだまだ使えるGemも多い。自前のライブラリーやTipsも豊富にあったりするので、ユーザビリティや品質の高いWebサービスはまだまだRailsで作れます。
ただ、今回、Shizuoka.goに参加して、Goの魅力にも気づけたことも事実です。
自分が今後やっていきたいことの中で、「AWSを上手に使ってマイクロサービス指向で行く」をあげていますが、こちらはRubyよりもGoが向いているんだろうと思います。AWSでなくて、GCPなのかもしれませんが・・・。ということで、今までやってきたアーキテクチャーとは違うものにチャレンジする際に、Goを選択することをここに誓います。
今回のShizuoka.goではWebフレームワークの話のほかに、@hogedigoさんのGoでのテスト手法についての話(資料)、@cupperさんのAWSとGoの話(資料)、@hrs_sano645
さんのGo製便利ツールの紹介(資料)など、興味深い話がたくさん聞けました。最後に、Shizuoka.goのみなさんありがとうございました。また参加します。Shizuoka.go最高!
- 投稿日:2019-11-30T16:46:01+09:00
大文字小文字を区別しないという不幸な世界のワイルドカード(macOS)
この記事について
macOS のファイルシステムは、デフォルトでは殆どのディレクトリで大文字小文字を区別しない。
そのような世界で、ワイルドカードを用いると何がマッチするか、という話。大文字小文字を区別しないということ
例えばこんなスクリプトを実行すると
bash#!/bin/bash set -eu # ASCII の英数字 touch foo.txt FOO.txt ls *.txt && rm *.txt # 所謂全角文字 touch zen.txt ZEN.txt ls *.txt && rm *.txt # ギリシャ文字・キリル文字・ローマ数字 touch ωяⅶ.txt ΩЯⅦ.txt ls *.txt && rm *.txt # DZ, NJ touch dz.txt # U+01F3 Latin Small Letter DZ touch Dz.txt # U+01F2 Latin Capital Letter D with Small Letter z touch DZ.txt # U+01F1 Latin Capital Letter DZ touch nj.txt # U+01CC Latin Small Letter NJ touch Nj.txt # U+01CB Latin Capital Letter N with Small Letter J touch NJ.txt # U+01CA Latin Capital Letter NJ ls *.txt && rm *.txt # i witout dot, etc. touch ı.txt # U+0131 Small I without dot touch İ.txt # U+0130 Capital I with dot touch i.txt # U+0069 Small I touch I.txt # U+0049 Capital I ls *.txt && rm *.txt # 囲み文字 touch ⓐ.txt # U+24D0 touch Ⓐ.txt # U+24B6 ls *.txt && rm *.txtこんな結果になる
foo.txt zen.txt ωяⅶ.txt nj.txt dz.txt i.txt İ.txt ı.txt ⓐ.txt
FOO
とfoo
は簡単。大文字小文字を区別しないのでどちらかしか生き残れない。
zen
、ωяⅶ
やⓐ
のあたりを見て分かる通り、7bit-ASCII の範囲外の文字にも大文字小文字があり、それらは区別されない。
Dz
は「Titlecase Letter」というカテゴリの文字で、大文字でも小文字でもない。
対応する小文字はdz
で、対応する大文字はDZ
になる。
この文字も大文字小文字を区別しないので、touch dz.txt Dz.txt DZ.txt
としても、ひとつしか生き残れない。
ı
,İ
,i
,I
の件は下表のとおり。
言語 I
の小文字i
の大文字英語 i
I
トルコ語 ı
İ
ı.txt
,İ.txt
,i.txt
,I.txt
を touch すると、
i.txt
,İ.txt
,ı.txt
が生き残る。unicode.org を見ると、dot のない小文字
ı
は、大文字にすると普通のI
になる。
にも関わらず、APFS 上ではı.txt
とI.txt
は別の名前だとみなされる。様々な環境での対応
いくつかの環境で何が起こるのか試してみた。
shell script(bash) 他
以下の環境でこの結果になる:
- shell script(bash)
- C(POSIX glob)
- Go(filepath.Glob)
- Python3(glob.glob)
- PHP7(glob)
POSIX glob や bash と同じなのでこれが基本っぽいんだけど、わりと気持ち悪い動作になっている。
基本的には、
- ワイルドカードがない場合は大文字小文字を区別しない。
- ワイルドカードがある場合は大文字小文字を区別する。
となっている。
Foo.txt
だと1件マッチしてF*.txt
だと 0件マッチするというのはかなり意外だった。ワイルドカードがない部分については、(気付いた範囲では)ファイルシステムと同じ意見になっている。
F*/f*/Foo
wildcard foo.txt fred.txt F*.txt ❌ ❌ f*.txt ✅ ✅ Foo.txt ✅ ❌ i / I / ı / İ
wildcard i-lat-lo.txt I-lat-up.txt ı-tur-lo.txt İ-tur-up.txt i*.txt ✅ ❌ ❌ ❌ I*.txt ❌ ✅ ❌ ❌ ı*.txt ❌ ❌ ✅ ❌ İ*.txt ❌ ❌ ❌ ✅ İ-tur-lo.txt ❌ ❌ ❌ ❌ I-tur-lo.txt ❌ ❌ ❌ ❌ ı-lat-up.txt ❌ ❌ ❌ ❌ DZ / Dz / dz
wildcard DZ-uu.txt Dz-ul.txt dz-ll.txt DZ*.txt ✅ ❌ ❌ Dz*.txt ❌ ✅ ❌ dz*.txt ❌ ❌ ✅ dz-uu.txt ✅ ❌ ❌ dz-ul.txt ❌ ✅ ❌ Dz-uu.txt ✅ ❌ ❌ ruby(Dir.glob)
ruby の動作は POSIX glob とはだいぶ異なる。
基本的には「大文字と小文字を区別しない」という動作で一貫しているような感じ。
Foo.txt
だとfoo.txt
のみがマッチして、F*.txt
だとfoo.txt
とfred.txt
がマッチする。わかりやすい。しかし、
rubyDir.glob("dz-u*.txt") #=> [] Dir.glob("dz-uu.txt") #=> ["files/DZ-uu.txt"]と、ワイルドカードを入れるとマッチする件数が減るというパターンもある。
バグ?F*/f*/Foo
wildcard foo.txt fred.txt F*.txt ✅ ✅ f*.txt ✅ ✅ Foo.txt ✅ ❌ i / I / ı / İ
wildcard i-lat-lo.txt I-lat-up.txt ı-tur-lo.txt İ-tur-up.txt i*.txt ✅ ✅ ❌ ❌ I*.txt ✅ ✅ ❌ ❌ ı*.txt ❌ ❌ ✅ ❌ İ*.txt ❌ ❌ ❌ ✅ İ-tur-lo.txt ❌ ❌ ❌ ❌ I-tur-lo.txt ❌ ❌ ❌ ❌ ı-lat-up.txt ❌ ❌ ❌ ❌ DZ / Dz / dz
wildcard DZ-uu.txt Dz-ul.txt dz-ll.txt DZ*.txt ✅ ❌ ❌ Dz*.txt ❌ ✅ ❌ dz*.txt ❌ ❌ ✅ dz-uu.txt ✅ ❌ ❌ dz-ul.txt ❌ ✅ ❌ Dz-uu.txt ✅ ❌ ❌ Java(PathMatcher)
java.nio.file
にPathMatcher
という インターフェイスがあるので、それを使ってみた。
これも POSIX glob とはだいぶ異なる。
常に大文字小文字を区別するように見える。
ファイルシステムのファイル名とは異なる動作だが、一貫性はある。F*/f*/Foo
wildcard foo.txt fred.txt F*.txt ❌ ❌ f*.txt ✅ ✅ Foo.txt ❌ ❌ i / I / ı / İ
wildcard i-lat-lo.txt I-lat-up.txt ı-tur-lo.txt İ-tur-up.txt i*.txt ✅ ❌ ❌ ❌ I*.txt ❌ ✅ ❌ ❌ ı*.txt ❌ ❌ ✅ ❌ İ*.txt ❌ ❌ ❌ ✅ İ-tur-lo.txt ❌ ❌ ❌ ❌ I-tur-lo.txt ❌ ❌ ❌ ❌ ı-lat-up.txt ❌ ❌ ❌ ❌ DZ / Dz / dz
wildcard DZ-uu.txt Dz-ul.txt dz-ll.txt DZ*.txt ✅ ❌ ❌ Dz*.txt ❌ ✅ ❌ dz*.txt ❌ ❌ ✅ dz-uu.txt ❌ ❌ ❌ dz-ul.txt ❌ ❌ ❌ Dz-uu.txt ❌ ❌ ❌ C#(.NET Core / Directory.GetFiles)
ruby の動きと似ている。
ruby と異なり、DZ*.txt
でdz-ll.txt
にちゃんと(?)マッチする。
しかし逆に、dz-uu.txt
ではDZ-uu.txt
が取れない。F*/f*/Foo
wildcard foo.txt fred.txt F*.txt ✅ ✅ f*.txt ✅ ✅ Foo.txt ✅ ❌ i / I / ı / İ
wildcard i-lat-lo.txt I-lat-up.txt ı-tur-lo.txt İ-tur-up.txt i*.txt ✅ ✅ ❌ ❌ I*.txt ✅ ✅ ❌ ❌ ı*.txt ❌ ❌ ✅ ❌ İ*.txt ❌ ❌ ❌ ✅ İ-tur-lo.txt ❌ ❌ ❌ ❌ I-tur-lo.txt ❌ ❌ ❌ ❌ ı-lat-up.txt ❌ ❌ ❌ ❌ DZ / Dz / dz
wildcard DZ-uu.txt Dz-ul.txt dz-ll.txt DZ*.txt ✅ ✅ ✅ Dz*.txt ✅ ✅ ✅ dz*.txt ✅ ✅ ✅ dz-uu.txt ❌ ❌ ❌ dz-ul.txt ❌ ❌ ❌ Dz-uu.txt ❌ ❌ ❌ C#(Mono / Directory.GetFiles)
意外にも、 .NET Core と Mono で動作が異なる。
大文字でも小文字でもないDz
という文字に負けている感じがする。F*/f*/Foo
wildcard foo.txt fred.txt F*.txt ✅ ✅ f*.txt ✅ ✅ Foo.txt ✅ ❌ i / I / ı / İ
wildcard i-lat-lo.txt I-lat-up.txt ı-tur-lo.txt İ-tur-up.txt i*.txt ✅ ✅ ❌ ❌ I*.txt ✅ ✅ ❌ ❌ ı*.txt ❌ ❌ ✅ ❌ İ*.txt ❌ ❌ ❌ ✅ İ-tur-lo.txt ❌ ❌ ❌ ❌ I-tur-lo.txt ❌ ❌ ❌ ❌ ı-lat-up.txt ❌ ❌ ❌ ❌ DZ / Dz / dz
wildcard DZ-uu.txt Dz-ul.txt dz-ll.txt DZ*.txt ✅ ❌ ✅ Dz*.txt ❌ ✅ ❌ dz*.txt ✅ ❌ ✅ dz-uu.txt ✅ ❌ ❌ dz-ul.txt ❌ ❌ ❌ Dz-uu.txt ❌ ❌ ❌ Perl(glob)
POSIX glob とよく似た動作になっているが、ドットなしの小文字 i の扱いが異なる。
F*/f*/Foo
wildcard foo.txt fred.txt F*.txt ❌ ❌ f*.txt ✅ ✅ Foo.txt ✅ ❌ i / I / ı / İ
wildcard i-lat-lo.txt I-lat-up.txt ı-tur-lo.txt İ-tur-up.txt i*.txt ✅ ❌ ❌ ❌ I*.txt ❌ ✅ ❌ ❌ ı*.txt ❌ ❌ ✅ ❌ İ*.txt ❌ ❌ ❌ ✅ İ-tur-lo.txt ❌ ❌ ❌ ❌ I-tur-lo.txt ❌ ❌ ✅ ❌ ı-lat-up.txt ❌ ✅ ❌ ❌ DZ / Dz / dz
wildcard DZ-uu.txt Dz-ul.txt dz-ll.txt DZ*.txt ✅ ❌ ❌ Dz*.txt ❌ ✅ ❌ dz*.txt ❌ ❌ ✅ dz-uu.txt ✅ ❌ ❌ dz-ul.txt ❌ ✅ ❌ Dz-uu.txt ✅ ❌ ❌ まとめ
POSIX glob は、ワイルドカードがない部分についてはファイルシステムと同じ意見になっているものの、ワイルドカードが入ると大文字小文字を区別するようになるところがわかりにくい。
- Go(filepath.Glob)
- Python3(glob.glob)
- PHP7(glob)
は、POSIX glob と同じ意見になっている。
一方
- ruby(Dir.glob)
- Java(PathMatcher)
- C#(.NET Core GetFiles)
- C#(Mono Directory.GetFiles)
- Perl(glob)
は独自のアルゴリズムで処理しているらしく、POSIX glob とは異なる結果を返す。
「アルファベット2文字をセットにした文字で一文字目だけ大文字に出来るもの」や「小文字のiからドットを除いたもの」のあたりで不穏な動作になりやすい。
- 投稿日:2019-11-30T15:45:52+09:00
Docker - How to Get Private Git Repository Inner Golang Container?
This method didn't need to generate ssh key.
FROM golang:1.13.4-alpine ... ... ... ARG GIT_URL ARG GIT_GROUP ARG GIT_ACCOUNT ARG GIT_PASSWORD RUN apk update -qq && apk --no-cache add git RUN printf "machine ${GIT_URL}\n\ login ${GIT_ACCOUNT}\n\ password ${GIT_PASSWORD}\n"\ >> /root/.netrc RUN chmod 600 /root/.netrc RUN go env -w GOPRIVATE=${GIT_URL}/${GIT_GROUP} ... ... ...https://medium.com/@jwenz723/fetching-private-go-modules-during-docker-build-5b76aa690280
- 投稿日:2019-11-30T15:45:52+09:00
Docker - How to Get Private Git Repository Inner Golang Container via HTTP?
You may be put your golang modules in a self-hosting git repository which likes gitlab. The most ways to tell us using ssh method and it is not easy enough (need to generate ssh key). I am just want to use https to get golang modules.
FROM golang:1.13.4-alpine ... ... ... ARG GIT_URL ARG GIT_GROUP ARG GIT_ACCOUNT ARG GIT_PASSWORD RUN apk update -qq && apk --no-cache add git RUN printf "machine ${GIT_URL}\n\ login ${GIT_ACCOUNT}\n\ password ${GIT_PASSWORD}\n"\ >> /root/.netrc RUN chmod 600 /root/.netrc RUN go env -w GOPRIVATE=${GIT_URL}/${GIT_GROUP} ... ... ...https://medium.com/@jwenz723/fetching-private-go-modules-during-docker-build-5b76aa690280
- 投稿日:2019-11-30T08:37:37+09:00
Goでつくるdaemon処理レシピ集
本記事は、アドベントカレンダー Go4 の1日目の記事です。
これは何?
みなさんは、どんなdaemon処理を書いていますか?
ここでは、Go の daemon 処理サンプルを紹介します.
daemon とは、バックグラウンドで動作するプロセス実行し続けるアレで、typoに注意なやつです(いつもdeamonと書いてしまう).Goではその言語特性から、様々な処理をシンプルに記述しやすいと感じており、daemonの実装例を通してGoらしさを学ぶことにも役立つと思います.
他にも役立つレシピがあれば、教えて下さいまずは基本から
単純に無限実行させる
The Go Playground で実行してみる
func main() { timeout := 5 * time.Second ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() f := func() error { // some task return nil } simple(ctx, f) } func simple(ctx context.Context, task func() error) { for { err := task() if err != nil { log.Printf("[ERROR] err: %v", err) } time.Sleep(500 * time.Millisecond) } }context を使ったタイムアウト
The Go Playgroundで実行してみる
func withTimeout(ctx context.Context, task func() error) { child, childCancel := context.WithCancel(ctx) defer childCancel() for { err := task() if err != nil { log.Printf("[ERROR] err: %v", err) } select { case <-child.Done(): log.Printf("[DEBUG] timeout") return default: time.Sleep(500 * time.Millisecond) } } }応用編
特定時刻にだけ処理をする
The Go Playground で実行してみる
func runDaemon(ctx context.Context, f func(context.Context) error) { daemonHour := "7-10" waitDuration := 500 * time.Millisecond for { now := time.Now() if len(daemonHour) != 0 { //起動時刻の指定があったら isExec := isExecHour(now, daemonHour) log.Printf("[DEBUG] daemon起動時間かどうかの判定 now:%v, daemonHour:%s, isExec:%v", now, daemonHour, isExec) if !isExec { time.Sleep(1 * time.Minute) continue } } err := f(ctx) if err != nil { log.Printf("[ERRROR] err:%v", err) } time.Sleep(waitDuration) } } func isExecHour(now time.Time, dHour string) bool { delimitor := "-" dh := strings.Split(dHour, delimitor) if len(dh) <= 1 { return false } start, err := strconv.Atoi(dh[0]) if err != nil { return false } end, err := strconv.Atoi(dh[1]) if err != nil { return false } h := now.Hour() if start <= h && h <= end { return true } return false }一定周期ごとに(前の処理が終わっていなくても)処理を実行する
The Go Playground で実行してみる
func timeTicker(ctx context.Context, task func(context.Context) error) { counter := 0 waitTime := 1 * time.Second ticker := time.NewTicker(waitTime) defer ticker.Stop() child, childCancel := context.WithCancel(ctx) defer childCancel() for { // deamon化するため無限実行 select { case t := <-ticker.C: counter++ requestID := counter log.Println("[DEBUG] START taskNo=", requestID, "t=", t) errCh := make(chan error, 1) go func() { // 登録したタスクをブロックせずに実行 errCh <- task(ctx) }() go func() { // error channelにリクエストの結果が返ってくるのを待つ select { case err := <-errCh: if err != nil { // Deamonの強制終了 log.Println("[ERROR] ", err) } log.Println("[DEBUG] END requestNo=", requestID) } }() case <-child.Done(): return } } }みなさんも、よき daemon Life をお過ごしください!
- 投稿日:2019-11-30T02:24:51+09:00
Goのstruct(構造体)はフィールドなしでも定義できる
概要
タイトルのままです。
フィールドなしでメソッドだけ定義したかったのですが、パッと調べてもわからなかったので、実際に試して知りました。
『そんなもん知ってるわ!!』という方はブラウザバックどうぞ
検証結果
main.gopackage main import ( "log" ) func main() { n := Network{} log.Print(n.CreateURL()) } type Network struct { } func (n *Network) CreateURL() string { // TODO: 引数を受け取ってURLを返す return "https://github.com/" }実行結果# go run main.go 2009/11/10 23:00:00 https://github.com/
実際に動かせる
こちら =>