Goのnet/httpパッケージでHello WorldするだけのWebアプリ
本記事の目的
Goを学ぼうかどうか判断するために、簡単なWebアプリを作ってみます (アクセスしたら、Hello Worldと表示するだけ)。
開発環境
Goを信用して良いのか分からなかったので、環境を汚さないために、Dockerでやります。 面白かったら、デバッカーとかで内部まで見たいので、ローカルで環境構築します。
ディレクトリ構成は以下のようにしました。 compileFileディレクトリ内にgoファイルを置いて、ビルドしていきます。
compileFile (ディレクトリ) Dockerfile docker-compose.yml
# Dockerfile FROM golang:latest RUN mkdir -p /go/src
version: '3' services: go: build: . ports: - "8080:8080" volumes: - ./compileFile:/go/src working_dir: /go/src
コンテナ内に入って、goのコマンドを確かめてみます。
--service-portsを指定しないと、8080ポート同士を繋げることはできないので、注意です。
docker ps
でちゃんとポートフォワードできていることを確認しておきます。
$ docker-compose run --service-ports go /bin/bash $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES af4f9c066ecd golean_go "/bin/bash" 7 seconds ago Up 2 seconds 0.0.0.0:8080->8080/tcp golean_go_run_a79a836040ab
ひとまず、go
と打ってみて、使えるコマンドを確認します。
Go is a tool for managing Go source code. Usage: go <command> [arguments] The commands are: bug start a bug report build compile packages and dependencies clean remove object files and cached files doc show documentation for package or symbol env print Go environment information fix update packages to use new APIs fmt gofmt (reformat) package sources generate generate Go files by processing source get add dependencies to current module and install them install compile and install packages and dependencies list list packages or modules mod module maintenance run compile and run Go program test test packages tool run specified go tool version print Go version vet report likely mistakes in packages Use "go help <command>" for more information about a command. Additional help topics: buildconstraint build constraints buildmode build modes c calling between Go and C cache build and test caching environment environment variables filetype file types go.mod the go.mod file gopath GOPATH environment variable gopath-get legacy GOPATH go get goproxy module proxy protocol importpath import path syntax modules modules, module versions, and more module-get module-aware go get module-auth module authentication using go.sum module-private module configuration for non-public modules packages package lists and patterns testflag testing flags testfunc testing functions Use "go help <topic>" for more information about that topic.
go build
go run
go fmt
は近いうちに使いそうです。
プロジェクト作成
馴染みが深いWebアプリからGoを理解していこうと思いました。 サンプルコードとか見ながら、Hello Worldをブラウザ上に表示するプログラムを書きます。
package main import "io" import "net/http" func mainHandler(w http.ResponseWriter, req *http.Request) { io.WriteString(w, `Hello World!!!`) } func main(){ http.HandleFunc("/main", mainHandler) http.ListenAndServe(":8080", nil) }
1コマンドで整形できるので、整形後ビルドします! ちなみに、ビルド時にリンターも働いているのか、改行の位置や使ってないパッケージがあると怒られます。
ビルドすると、goファイルのあるディレクトリ名の実行ファイルが作られました。
$ go fmt $ go build
$ ls server main.go $ ./server
http://localhost:8080/main にアクセスすると、Hello Worldを確認できました!
ちなみに、Macで動かしたい場合は、以下でMac用にコンパイルできます。
$ GOOS=darwin GOARCH=amd64 go build
net/httpパッケージ
わかりやすいところから見ていきます。 これでサーバーを立てられるようです。 第一引数がポートの指定、第二引数でHandlerを選択できるようで、nilの場合デフォルトのDefaultServeMuxが使われるようです。
http.ListenAndServe(":8080", nil)
DefaultServeMuxって何?
ドキュメントを読み進めると、以下の記述にあたります。
HandleFunc registers the handler function for the given pattern in the DefaultServeMux. The documentation for ServeMux explains how patterns are matched.
HandleFuncでURLのパターンに対応するハンドラーをDefaultServerMuxに登録できる...みたいなことが書いてあります。ルーティング的な何かと思っておきましょうか。
なので、以下は/mainにアクセスしたら、mainHandlerの処理を実行と読めます。
http.HandleFunc("/main", mainHandler)
ioパッケージの方は何してるか分かるので、置いておきます。
Localに導入
brewで簡単に入ります。
$ brew install go $ go version go version go1.15.3 darwin/amd64
REPLも入れておきます。 gore v0.5.0だと動かなかったので、バージョンを落としています。
# これをしてインストールできるようになりましたが、いらないかもです。 $ export GO111MODULE=on $ go get github.com/motemen/gore/cmd/gore@v0.4.0 $ gore -version gore 0.4.0 (rev: HEAD/go1.15.3)
間違えてインストールした場合は以下で削除できます。
$ go clean -i github.com/motemen/gore/cmd/gore
ちなみに、v0.5.0だと以下のエラーメッセージがでます。
gore: could not load 'fmt' package: err: exit status 1: stderr: build flag -mod=mod only valid when using modules
これでローカル環境も整ったので、あとは遊ぶだけです。
総括
Go楽しいですね。 ターミナルアプリ (TUI) とか作ってみたい。
雑多な感想
Qiitaで投稿しようかと思ったけど、Qiitaは怖い人ばかりで治安も悪いので、故郷のHatena Blogでひっそり過ごすことにした。
Terminalで顧客情報を取得する!
What
JSforceを使って、顧客情報をTerminal上で取得してみます。
JSforce https://jsforce.github.io/
How
JSforceの導入
まず、セットアップします。
$ sudo npm install jsforce $ jsforce --version 1.9.3
Salesforceにログイン
パスワードとセキュリティトークンは続け様に書かないと、ログインできません。
$ jsforce $ login(('<ユーザー名 (ログイン時に使用したEmail)>', '<パスワード><セキュリティトークン>')
[参考] https://developer.salesforce.com/forums/?id=906F0000000ApdZIAS
Queryを実行
公式のリファレンスを参考にクエリを書きます。
例えば、リードユーザーの名前、電話番号を取得したい場合、次のように書きます。
$ query("SELECT Name, Phone from Lead LIMIT 3")
結果はJSONで返ります。
{ totalSize: 3, done: true, records: [ { attributes: [Object], Name: 'Dadio Jr Bill', Phone: '(614) 431-xx00' }, { attributes: [Object], Name: 'Luce Eugena', Phone: '(781) 270-xx00' }, { attributes: [Object], Name: 'Eberhard Sandra', Phone: '(626) 4xx-0700' } ] }
遭遇したエラー
API is not enabled for this Organization or Partner
API_DISABLED_FOR_ORG: API is not enabled for this Organization or Partner
APIを許可したユーザーでない場合、発生するエラーです。
管理者がAPIを無効にしているか (デフォルトでは有効)、Trial版の場合発生します。
Trial版で試してる場合は、Developer版に切り替えましょう。無料です。
Developer版はこちらから取得できます。
[参考] https://trailblazers.salesforce.com/answers?id=90630000000hxbhAAA
errorCode: 'MALFORMED_QUERY'
上記のクエリにWHERE句を入れようとすると発生します。
$ query("SELECT Name, Phone from Lead LIMIT 4 WHERE Phone='(626) 4xx-0700'")
項目が多い場合やクエリが複雑な時に発生するようです。
JavaScriptで環境変数を使う in Rails
What
Rails + Vue.jsという構成のアプリにおいて、JavaScript (.jsや.vue) でも環境変数を使えるようにする。
Solution
Rails側で.env以下に環境変数をまとめていることを前提に記述します。
.envファイル
API_KEY=jofdashouhaemfaspo3u9fasnifgfa98
まず、yarnでdotenvをアプリに追加します。
$ yarn add dotenv
次に、config/webpack/development.js や config/webpack/production.jsに以下を追記します (一番上に追記しました)。
require('dotenv').config()
あとは使いたい.vueファイルで以下のようにして、呼び出します。
const { API_KEY } = process.env console.warn(API_KEY)
ng newにてThe Schematic workflow failed.
What
ng newコマンド実行時、以下のエラー群が発生しました。
⠏ Installing packages...npm WARN deprecated tslint@6.1.2: TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information. npm WARN deprecated chokidar@2.1.8: Upgrade to chokidar 3 with 15x less dependencies. Chokidar 2 will break on node v14. npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142 npm WARN deprecated fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2. npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated npm ERR! Unexpected end of JSON input while parsing near '...://registry.npmjs.org' npm ERR! A complete log of this run can be found in: npm ERR! /Users/apple/.npm/_logs/2020-07-03T13_33_59_631Z-debug.log ✖ Package install failed, see above. The Schematic workflow failed. See above.
この状態で、ng serve
を実行すると以下のエラーが発生します。
An unhandled exception occurred: Cannot find module '@angular-devkit/build-angular/package.json' Require stack: - /Users/apple/.config/yarn/global/node_modules/@angular-devkit/architect/node/node-modules-architect-host.js - /Users/apple/.config/yarn/global/node_modules/@angular-devkit/architect/node/index.js - /Users/apple/.config/yarn/global/node_modules/@angular/cli/models/architect-command.js - /Users/apple/.config/yarn/global/node_modules/@angular/cli/commands/serve-impl.js - /Users/apple/.config/yarn/global/node_modules/@angular-devkit/schematics/tools/export-ref.js - /Users/apple/.config/yarn/global/node_modules/@angular-devkit/schematics/tools/index.js - /Users/apple/.config/yarn/global/node_modules/@angular/cli/utilities/json-schema.js - /Users/apple/.config/yarn/global/node_modules/@angular/cli/models/command-runner.js - /Users/apple/.config/yarn/global/node_modules/@angular/cli/lib/cli/index.js - /Users/apple/.config/yarn/global/node_modules/@angular/cli/lib/init.js - /Users/apple/.config/yarn/global/node_modules/@angular/cli/bin/ng See "/private/var/folders/yt/wcyrx93n3jd93smbhblqjrx40000gn/T/ng-TtYLQM/angular-errors.log" for further details.
Solution
Cannot find module '@angular-devkit/build-angular/package.json'とのことなので、以下のコマンドで@angular-devkit/build-angularを導入します。これで、ng newおよびng serveでのエラーが解決されました。
yarn add @angular-devkit/build-angular --dev
もし、npmの認証系でエラーが生じるなら以下も実行しておきます (下記の記事も参考になりました)。
$ sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}
[参考] qiita.com
MySQL8.0インストール時の諸々のエラー解決
What
MySQL8.0インストール時に詰まったので、その対策等を記録しておきます。
MySQL8.0のインストール
Homebrew経由で取得します。デフォルトで最新版をインストールすることができます。
$ brew install mysql
パスワードの再設定
MySQLが起動しているなら、rootユーザーとしてログインできます。初期ではパスワードが設定されていないため、Enterでログインできます。
$ mysql -u root -p
MySQL8.0は5.x以前と比べて関数が変更されているため、以下の手順でパスワードを変更します。
mysql > user mysql; mysql > alter user 'root'@'localhost' identified by '<設定したいパスワード>'; mysql > exit;
[参考]
bundle install
Rails6.0でMySQLを使用する場合、以下のエラーがでて、インストールできないことがあります。
An error occurred while installing mysql2 (0.5.2), and Bundler cannot continue. Make sure that `gem install mysql2 -v '0.5.2' --source 'https://rubygems.org/'` succeeds before bundling.
指示通りコマンドを実行します。
$ gem install mysql2 -v '0.5.2' --source 'https://rubygems.org/'
またしてもエラーです。
Don't know how to set rpath on your system, if MySQL libraries are not in path mysql2 may not load
pathが合ってないみたいなので、以下のコマンドで詳しくみてやります。
$ brew info openssl
ビンゴっぽい文言が出てきました (親切で嬉しい)。
If you need to have openssl@1.1 first in your PATH run: echo 'export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"' >> ~/.zshrc For compilers to find openssl@1.1 you may need to set: export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib" export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"
ホームディレクトリに.zshrcファイルを作成 (または既存のファイルを利用) して以上のものを追記しておきます。
$ sudo vi ~/.zshrc
export PATH="/usr/local/opt/openssl@1.1/bin:$PATH" export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib" export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"
これで、bundle installやrails db:create等ができるはずです。
MySQLの中身を見る
RubyMineだと簡単に見れるらしいのですが、VSCodeを使用している (乗り換えようかなぁ) ので、別の方法で見ます。
結論ですが、TablePlusがオススメです。セキュリティ認証レベルを昔のに戻すと、Sequel Proでも使用できるのですが、せっかく登録したrootユーザーのパスワードがぱーになったりして、困ったので、別ツールを使用することにしました。ちなみに、Sequel Pro build版でもMySQL8.0のセキュリティ認証 (caching_sha2_password) に未対応でした (2020.07.01 現在)。
Selenium::WebDriver::Error::WebDriverError ~Unable to find Mozilla geckodriver.~
What
Capybaraにて、JavaScriptを扱う際、以下のエラーが出てきました。
Selenium::WebDriver::Error::WebDriverError: Unable to find Mozilla geckodriver. Please download the server from https://github.com/mozilla/geckodriver/releases and place it somewhere on your PATH. More info at https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette/WebDriver.
JavaScriptはブラウザに依存するため、上記のようなエラーがでています。
Firefoxを使用する場合は、上記エラー文通り対処すれば解決できそうです。
Chromeで使用したいなら、以下のgemを追加する必要があります。また、Rails5にてデフォルトで入っているgem 'selenium-webdriver'はそのままでも大丈夫です。
group :test do gem 'webdrivers' end
spec/rails_helper.rbにて以下を追記します。
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f}
spec/support/*.rbにて、以下を追記すれば、エラーが解消されます。以下の記述では、実際にChromeが起動し、自動でテストが実行されます。
Capybara.javascript_driver = :selenium_chrome
もし、ヘッドレスで実行させたい場合は、以下のようにします。
Capybara.javascript_driver = :selenium_chrome_headless
配列と連想配列 ~会津オンラインALDS1_4Cを通して~
What
会津オンラインの以下の問題を解きます。 onlinejudge.u-aizu.ac.jp
Solution
配列で書く方法を真っ先に思いついたので、それで実装してみました。
function main(lines) { const input = lines.split('\n').slice(1) const n = input.length let str = [] for(let i=0; i<n; i++){ const judge = input[i].split(' ') if(judge[0] === 'insert'){ str.push(judge[1]) } if(judge[0] === 'find'){ console.log(str.includes(judge[1]) ? 'yes' : 'no') } } } main(require('fs').readFileSync('/dev/stdin', 'utf8'));
for文の代わりに、forEachメソッドを使うこともできます。
for(let i=0; i<n; i++){ const judge = input[i].split(' ') }
input.forEach(function(element){ const judge = element.split(' ') }
ただ、上記の方法では処理に時間がかかるため、問題をクリアできません。 しかし連想配列で実装すると、処理速度が1.5倍速くなり、問題をクリアできます (処理時間はconsole.time, console.timeEndメソッドで簡単に調べています)。
function main(lines) { const input = lines.split('\n').slice(1) const n = input.length let obj = {} input.forEach(function(arr){ const judge = arr.split(' ') if(judge[0] === 'insert'){ obj[judge[1]]= true } if(judge[0] === 'find'){ console.log(obj.hasOwnProperty(judge[1]) ? 'yes' : 'no') } }) } main(require('fs').readFileSync('/dev/stdin', 'utf8'));
Why
なぜ、連想配列にすると処理速度が上がるのでしょうか。
連想配列と配列の違い、内部での処理について調べる必要がありそうです。
それはまた別の機会に調べて記事にします。
予想できるのは、DBにおけるインデックスと同じように、オブジェクト作成時に仕組みがあるのかもしれません。
実際、配列または連想配列に要素を追加するまでの処理時間では、配列の方が1.3倍程度速いです。