20210910のGoに関する記事は3件です。

【Golang】構造体の並び順を自動で整える(govet の fieldalignment エラーの自動修正)

Struct構造体 の Field Alignmentフィールドの並び で叱られる Go 言語の総合 lint ツールの golangci-lint で、govet の linter が「構造体の並び順にメモリの無駄がある」と指摘してきます。 ほらgolang叱られた fieldalignment: struct with N pointer bytes could be M (govet) この、govet の fieldalignment エラーが出るたび、構造体のフィールドの並びをコピペピピックするトライ&エラーでわけわかめになるのが面倒なのです。「"go" golang fieldalignment govet フィールド 並び順 修正」でググっても出てこなかったので、自分のググラビリティとして。 TL; DR (今北産業) fieldalignment には -fix オプションがあるので、それ(自動修復)を使うと便利。 golangci-lint run --fix すると、他の linter の自動修正も適用されてしまうので注意。fieldalignment コマンドを別途入れておくと勝手が良い。 単体コマンドとしてもインストールできる。 golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest fieldalignment -fix ./lib/mypackage 当然ですがフィールドの並び順が変わるので、既存のオブジェクト作成箇所に注意。 テストなど、構造体の新規オブジェクトを a := &myStruct{"foo","bar", 123} のように「フィールド名指定なし」で指定しているとエラーになるので、並びを合わせます。 動作確認 go version go1.17 linux/amd64 @ golang:1.17-alpine Docker image 参考文献 github.com/mdempsky/maligned (fieldalignment のオリジナル) @ GitHub fieldalignment package @ pkg.go.dev x/tools/go/analysis/passes/fieldalignment: add a command? | #43446 | go | golang @ github.com
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GOPATHとGOROOTになにを指定したらいいの?

GOPATH ホーム直下に「go」という名前のディレクトリを作成し、それを指定する。 調べてみたところ指すところはなんでもよいみたい。しかし、GOPATHと被らないように注意する。 「go」のようなディレクトリをホーム直下に置く人が多いみたいだ。 僕の指定例 GOPATH : C:\Users\<ユーザ名>\go GOROOT インストールしたときに指定したgoディレクトリを指す。 僕の指定例 GOPATH : C:\Program Files\Go ←これは僕がGoをインストールしたときの参照先 なぜこのような記事を? goをインストールしてすぐ、環境構築を進めていたところ思うように進まなかった。 GOPATH set to GOROOT (C:~~~~~~~~) has no effectというエラー文が出た。 原因 1 GOPATHとGOROOTの指す先が同じであった。 2 GOROOTをインストールディレクトリにせず全く関係のないディレクトリにしていた。 とくにインストール後これらをいじった覚えはないが、もしかしたら自分が知らず知らずのうちに触っていたのかもしれません…。 あとがき みなさんもGOの環境構築をされる際はお気を付けください! Macがとてもうらやましく感じます…。 また、自分なりの解釈なのでおかしな点等ありましたらコメントよろしくお願いします!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【業務システム向け】とにかく実装量が少ないWEBテンプレート作った(フロントエンド編)

はじめに 業務システムあるあるの機能がそろった ログイン→一覧→登録、編集機能のWEBテンプレートを作りました。 ログイン画面は非常によくあるIDとパスワードによる認証画面です。 デザインはSemantic-UIを使用しているためとてもスタイリッシュに仕上がっています。 フォームのバリデーションとそのメッセージ表示機能を備えています。 トップ画面では機能の紹介を載せていますが、ここはプロジェクトによってダッシュボードに変わったりするかと思います。 全画面共通のヘッダからサイドメニュー、ログイン情報の閲覧ができます。 全画面共通のヘッダ等は当然ソースが共通化されているのでそれらをincludeするだけで使用することができます。 一覧画面はag-gridを使用しているので行選択やフィルタ機能等をそのまま使用することができます。 一覧の削除、編集、新規登録、excel出力の機能がありますが、個々の画面での記述量は極力少なくなるようにしています! 登録画面も記述量はかなり少なくなっています。 Semantic-UIのモーダルがやはりスタイリッシュ。 余談ですがSemantic-UIは個人的にお気に入りなCSSフレームワークです。 諸事情で開発が止まっているみたいですが、コミュニティメンバによりFomantic-UIという名前で 分岐して開発されているようです。いずれは本家にマージするみたいですね。 https://fomantic-ui.com/ 開発の経緯 業務システムではマスタメンテナンス画面などをよく作りますよね? 一覧→登録の流れが50画面とかあったりします。 その時のあるあるなのが 人によって実装の仕方が違う 同じ実装をそれぞれで書いている ですね。 これは工数的にも無駄ですしメンテナンス性も最悪です。 このWEBテンプレートはそれらの解消を目的としてとにかく実装の共通化、 個々の画面の実装のみになるような作りにしました。 諸事情によりgithub等でソースの公開ができないので 設計の参考、自分の設計思想の共有程度にご覧頂ければと思います。 技術要素 ◆フロントエンド - pug - TypeScript - Semantic-UI - ag-grid ◆バックエンド - go beego 機能一覧 ◆基本機能 - ログイン、ログアウト(有効/無効切り替えあり) - ajaxでの送受信(ag-grid含む) - バリデーションチェック ◆フロントエンドの機能 - csv&excel出力(sheetJS) - ファイルアップロード(バックエンド未実装) ◆バックエンドの機能 - DB接続 - DB種類の切替 - ログ出力 - パンくずリスト(状態維持) ◆セキュリティ機能 - CSRF対策(有効/無効切り替えあり) - ロールによる画面アクセスコントロール(有効/無効切り替えあり) ◆対応端末 - スマホ、タブレット - PC(chrome、firefox、IE11) ※いろいろ機能がありますが今回はフロントエンドのみ解説しています! 構成 構成図 ├── conf ├── app.conf //バックエンドの設定 ├── config.json //バックエンド、フロンドエンド共通の設定 ├── messages.json //バックエンド、フロンドエンド共通のメッセージの設定 ├── src //コンパイル前ソース ├── css ├── common.scss ├── login.scss ├── templateList.scss ├── temolateRegist.scss ├── top.scss ├── js ├── common.ts ├── login.ts ├── templateList.ts ├── temolateRegist.ts ├── top.ts ├── ...//その他 共通ファイル省略 ├── pug ├── common ├── layout.pug ├── mixin.pug ├── ...//その他 共通ファイル省略 ├── login.pug ├── templateList.pug ├── temolateRegist.pug ├── top.pug ├── static //コンパイル後ソース ├── views //コンパイル後ソース(pug) 見てのとおり1画面につきscss,ts,pugが1ファイルずつ存在していて それらはそれぞれのcommon.〇〇を継承する作りになっています。 共通部分はcommonがよしなにしているので、個々の画面で記述する実装は非常に少ないです。 設定ファイルはconfにまとめられていてフロントエンド、バックエンド共通で使えるようになっています! ありがちなフロントエンド、バックエンドそれぞれで設定ファイルを持つなんてことはありません。 config.json { "baseDir": "src", "distDir": "static", "jsDir": "js", "cssDir": "css", "agGrid_contains": "を含む", "agGrid_endsWith": "で終わる", "agGrid_equals": "と等しい", "agGrid_filterOoo": "フィルタ...", "agGrid_greaterThan": "より大きい", "agGrid_greaterThanOrEqual": "以上", "agGrid_inRange": "範囲", "agGrid_lessThan": "より少ない", "agGrid_lessThanOrEqual": "以下", "agGrid_noRowsToShow": " ", "agGrid_notContains": "を含まない", "agGrid_notEqual": "と等しくない", "agGrid_startsWith": "で始まる", "requestParameter": { "isValid": "isValid", "data": "data", "validationSummary": "validationSummary" }, "routingURL": { "login": "/Login", "loginDoLogout": "/Login/DoLogout", "loginDoLogin": "/Login/DoLogin", "top": "/Top", "templateList": "/TemplateList", "templateListGetRoleMaster": "/TemplateList/GetRoleMaster", "templateListDoDelete": "/TemplateList/DoDelete", "templateRegist": "/TemplateRegist", "templateRegistDoRegist": "/TemplateRegist/DoRegist" }, "imageFolder": "/static/img/", "validationMode": { "doLogin": "DoLogin", "doLogout": "DoLogout", "doDelete": "DoDelete", "doRegist": "DoRegist", "doSearch": "DoSearch" } } それでは一覧画面を例に見ていきましょう。 scss templateList.scss .ag-grid { height: 350px; } /*スマホ用の記述*/ @media screen and (max-width: 480px) { .ag-grid { height: 360px; } } 画面のスタイルはcommon.scssに書かれているので、個別の画面定義にはag-gridのスタイルのみを記述します。 TypeScript templateList.ts import { AjaxOptions } from './ajax-options'; import { AgGrid } from './ag-grid'; import { Common, CommonViewModel } from './common'; import { ButtonRenderer } from './components'; const config = require('/conf/config.json'); import "../css/common.scss"; import "../css/templateList.scss"; /** * 個別ビュークラス * */ class TemplateListViewModel extends CommonViewModel { RoleMasterGrid: string = "RoleMasterGrid"; } /** * 個別クラス * */ class TemplateList extends Common { readonly model: TemplateListViewModel; readonly gridId: string; /** * コンストラクタ * */ constructor() { super(); this.model = new TemplateListViewModel(); this.gridId = this.model.RoleMasterGrid; } /** * バインドを実行する * * @returns 実行結果 */ async bind(): Promise<any> { await super.bind(); //検索ボタン $(this.model.SearchBtn.id()).on("click", (event) => { //スクロール位置をリセットする this.grid[this.gridId].resetScrollTop(); this.GetRoleMaster(); return false; }); //新規登録ボタン $(this.model.NewRegistBtn.id()).on("click", (event) => { this.startLoading(); //現在のスクロール位置をセットする this.grid[this.gridId].setScrollTop(); //登録画面へ遷移 location.href = config.routingURL.templateRegist; return false; }); //削除ボタン $(this.model.DeleteBtn.id()).on("click", (event) => { let modalOptions = this.getUsualRegistModalOptions( this.messageFormat("C_000") , config.validationMode.doDelete , this.messageFormat("I_000") , returnData => config.routingURL.templateList , config.routingURL.templateListDoDelete ); this.startConfirmModal(modalOptions); return false; }); //excel出力ボタン $(this.model.EXCELOutputBtn.id()).on("click", (event) => { this.grid[this.gridId].exportDataAsExcel(); return false; }); return true; } /** * 初期化を実行する * * @returns 実行結果 */ async init(): Promise<any> { await super.init(); this.grid[this.gridId] = new AgGrid(this.gridId); var grid = this.grid[this.gridId]; //列定義を設定する var columnDefs = [ grid.getCheckColumnDef(), { headerName: "編集" , field: "Edit" , width: 85 , pinned: "left" //ボタンコンポーネントを設定する , cellRenderer: ButtonRenderer , cellRendererParams: { buttonValue: "編集" } , onButtonClicked: (params) => { this.startLoading(); //現在のスクロール位置をセットする grid.setScrollTop(); location.href = config.routingURL.templateRegist + "?id=" + params.data.RoleId; } }, { headerName: "ロールID", field: "RoleId", width: 100 }, { headerName: "ロール名", field: "RoleName" }, { headerName: "ロール説明", field: "RoleDescription", width: 250 } ]; grid.setColumnDefs(columnDefs); grid.init(); await this.GetRoleMaster(); return true; } /** * ロールマスタを取得する * * @returns プロミス */ async GetRoleMaster(): Promise<any> { return this.submitSetGrid(config.validationMode.doSearch, this.gridId, config.routingURL.templateListGetRoleMaster); } } $(function () { let templateList = new TemplateList(); }); 個々の画面のTSはCommonを継承した画面のクラス(個別クラス)を持ちます。 個別クラスには画面の項目を定義したクラス(個別ビュークラス)を持っています。 個別ビュークラスはCommonViewModelを継承していて共通の画面の項目はそちらに定義されています。 ビュークラスがあることによって、よくある項目名の記述ミスを防ぐことができます。 (項目名の最初が大文字だったとかで書き間違えて動かないとかあるあるを防ぎます) 個別クラスをインスタンス化するとCommonのコンストラクタでbindとinitが実行されて共通の処理が実行されます。 個々の画面の初期処理はbindとinitに書き込むだけになっています。 面倒なDBから一覧取得→gridに反映の部分も共通メソッドを呼ぶだけなので 人によって違う実装がされることもあり得ません。 return this.submitSetGrid( config.validationMode.doSearch, //バリデーションチェックの指定 this.gridId, //反映するgridのID config.routingURL.templateListGetRoleMaster //一覧取得先のURL ); 削除ボタン押下時には 確認メッセージ表示 →削除処理 →処理結果メッセージ表示 →画面再読み込み 上記のように意外とイベントが多いのですが、共通メソッドを呼ぶだけで完結するように処理をまとめました。 let modalOptions = this.getUsualRegistModalOptions( this.messageFormat("C_000") //確認メッセージ , config.validationMode.doDelete //バリデーションチェックの指定 , this.messageFormat("I_000") //処理結果メッセージ , returnData => config.routingURL.templateList //処理結果メッセージ表示後の移動先 , config.routingURL.templateListDoDelete //削除処理のURL ); this.startConfirmModal(modalOptions); このgetUsualRegistModalOptionsには定型のボタン処理が定義されていて 可変部分の確認メッセージ等を引数として渡すだけで定型のボタン処理が完成します。 定型のボタン処理以外にしたい場合はstartConfirmModalに渡す変数を独自に定義すればOKです。 参考までに関連するCommon部分を抜粋するとこのような感じになってます。 common.ts export class Common { async submitSetGrid(validationMode: string, gridId: string, url: string, extraDone: Function = () => { }): Promise<any> { let options = this.getUsualSearchOptions(validationMode, gridId, extraDone); return this.submit(url, options); } getUsualSearchOptions(validationMode: string, gridId: string, extraDone: Function = () => { }): AjaxOptions { let options = new AjaxOptions(); options.validationMode = validationMode; options.done = (returnData) => { this.grid[gridId].setRowData(returnData[config.requestParameter.data]); extraDone(returnData); }; return options; } } pug templateList.pug extends ./common/layout.pug block vars - var Title = "一覧系テンプレート" - var jsSrc = "templateList.js" block main-contents +form("") div(class="ui two column stackable grid search") div(class="six wide column field") div(class="ui labeled input") label(class="ui label" for="RoleName") ロール名 +inputText("RoleName", "{{.viewmodel.RoleName}}") +button("SearchBtn", "teal") +icon("search") | 検索 div(class="ui horizontal divider") +icon("search-hide-show angle up") h4(class="ui dividing header") ロールマスタ div(id="RoleMasterGrid" class="ag-theme-balham ag-grid") block footer-contents div(class="ui item right") +iconButton("EXCELOutputBtn", "primary", "file", "excel出力") +iconButton("NewRegistBtn", "positive", "edit", "新規登録") +iconButton("DeleteBtn", "negative", "trash", "削除") pugのincludeとmixinを活用して記述量を極力少なくしました。 大半のコードはlayout.pugに書かれています。 layout.pugでさらにいろんなpugファイルをincludeしており別のレイアウトにしたいときも再利用しやすくなっています。 mixinはpugの独自の機能でhtmlを吐き出す関数のようなものです。 繰り返し出てくるボタン等をmixinにすることで記述量を少なくし記述ミスも減らすことができます。 mixin iconButton(_id,_addClass,_iconAddClass,_text) +button(`${_id}`, `${_addClass}`) +icon(`${_iconAddClass}`) !{_text} if block block まとめ 今回はフロントエンドのみを解説しました。 とにかくCommonに処理を集めて個々の記述を減らす設計にした結果こうなりました。 今までの経験として個々の記述で同じことを何回も書かされてきたので それをなんとかしたいという思いから作ってみましたが、実際に案件で使っているわけではありません。 次の改善案としてはVue.js化をしてみたいのですがなかなか手が出せず。 次回はバックエンド編を解説します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む