- 投稿日:2020-09-24T23:44:00+09:00
【Rails】ページの上部に戻るボタン
はじめに
・Railsを使用してアプリケーションを開発
・viewsの下部地点から上部に戻るボタンを設置するRuby 2.5.7
Rails 5.2.4.4流れ
・
gem 'jquery-rails'
を導入
・アクションを起こすボタンを用意(画像でなく文字やfont-awesome等でも代用可能)
・viewにボタンを記載しcss及びjavascriptへ追記ボタン画像
適当に準備します・・・
画像ファイルをapp/assets/images配下に格納
viewsへ追加
<span id="top-btn"> <a href="#"> <%= image_tag asset_path('top_btn.png'), class: 'top-btn' %> </a> </span>・asset_path('~~') は、app/assets/images配下の画像を読み込む
css 及び jsファイルへ追記
.top-btn { position: fixed; right: 30px; bottom: 40px; } .top-btn a { width: 50px; height: 50px; }$(function() { $('#top-btn a').on('click',function(event){ $('body, html').animate({ scrollTop:0 }, 1000); event.preventDefault(); }); });初期状態(ページの上部)では表示したくない場合?
$(function() { var topBtn = $('#top-btn a'); topBtn.hide(); $(window).scroll(function(){ if ($(this).scrollTop() > 100) { topBtn.fadeIn(1000); } else { topBtn.fadeOut(); } }); topBtn.click(function () { $('body, html').animate({ scrollTop:0 }, 1000); event.preventDefault(); }); });fadeIn 及びfadeOut のif文にてボタンの表示を設定しています・・
終わり
上記記載内容で、実装できるはず・・・
不明点及び説明がずれている(誤っている等)ございましたら、コメントにてにてお伝えいただければありがたいです。以上、ご参考になれば幸いです。
- 投稿日:2020-09-24T23:23:46+09:00
JavaScript package比較サイトまとめ
openbase
https://openbase.ionpm trends
https://www.npmtrends.com/NPMCompare
https://npmcompare.com/npmcharts
https://npmcharts.com/
- 投稿日:2020-09-24T23:23:46+09:00
ツール比較サイトまとめ
全体
stackshare
https://stackshare.io/npm
openbase
https://openbase.ionpm trends
https://www.npmtrends.com/NPMCompare
https://npmcompare.com/npmcharts
https://npmcharts.com/Awesome Node.js
https://nodejs.libhunt.com/静的サイトジェネレーター
StaticGen
https://www.staticgen.com/
- 投稿日:2020-09-24T22:50:04+09:00
[PlayCanvas]他のEntityのScriptを取得する
概要
例えば、衝突処理などが発生した時に、そのぶつかった対象に影響を与えるなどをするために必要な処理。
実装例
Sample.jsvar Sample = pc.createScript('sample'); Sample.prototype.test = function( param ){ console.log( param ); };Call.jsvar Call = pc.createScript('call'); Call.prototype.initialize = function(){ var entity = ~~~; // 何かしらの手段でEntityを取得したとする // 安全でないアクセス entity.script.sample.test('Unsafe Call.'); // 安全なアクセス方法 // 1. EntityにScriptコンポーネントがあるかを調べる // 2. Scriptコンポーネント内にScriptがあるか調べる if(entity.script && entity.script.has('sample')){ entity.script.sample.test('Safe Call.'); } };PlayCanvasはEntityにScriptというコンポーネントを追加して、そこにJavaScriptで書かれたScriptを追加していく方式なので注意が必要。
- 投稿日:2020-09-24T22:07:41+09:00
GASを使ってスプレッドシートをPDF化してメールに添付する。
やったこと
・特定のシートを複数PDF化けること。
・別途JSONファイルを保存して、そこから個人情報を取得すること
・複数のPDFをメールに添付すること。経緯:
今まで直書きしてたけど、.envfile
のように個人情報は分けたくなりました。下記のようなJSONファイルを用意
{ "user_info": { "mail_adress": [ "hoge@gmail.com", "huge@gmail.com" ] } }コードがこちら
//個人データを保存したJSONファイル。共有用ファイルから確認可能。 const file_id = "1Jo6Dx6QPU_baFnCUzXlFiFyjRpyS71Nq"; //送信内容を保存したスプレッドシートのID const sheet_id = "1x6olwI_XZALOzayxh0vXlxKXqtzhEwT_b92c0QUW7zY"; function sendMail(){ const currentTime = new Date(); //ファイルIDを元にスプレッドシートを開く // 実行ファイルを利用する場合は、SpreadsheetApp.getActiveSpreadsheet();, spreadsheet.getId();でも可。 const spreadsheet = SpreadsheetApp.openById(sheet_id); const seetNameList = ["sample1", "sample2"]; const token = ScriptApp.getOAuthToken(); //PDFファイル配列を作成するためのmap処理 const pdfFiles = seetNameList.map(function(seet){ //seetNameListから取得したseet名を元にsheetを取得 const sheetName = spreadsheet.getSheetByName(seet); const gid = sheetName.getSheetId(); //PDF生成するURLをfetchする。特定のスプレッドシートの特定のシートだけPDF化するため const url = `https://docs.google.com/spreadsheets/d/${sheet_id}/export?gid=${gid}&format=pdf&portrait=false&size=A4&gridlines=false&fitw=true`; const pdf = UrlFetchApp.fetch(url, {headers: {'Authorization': 'Bearer ' + token}}).getBlob().setName(`${currentTime}.pdf`); return pdf }); // Jsonファイルを元にデータを取得 var file = DriveApp.getFileById(file_id); // objectとして取得。 中身は空 var blob = file.getBlob(); var data = blob.getDataAsString(); var jobj = JSON.parse(data); //jsonファイルに保存した連絡先の取得 const contactLists = jobj.user_info.mail_adress; for (let i = 0; i < contactLists.length; i++) { var to = contactLists[i]; var subject = "メールのタイトルを入力"; var body ="メールの本文を入力"; GmailApp.sendEmail(to, subject, body, {attachments: pdfFiles}) } }ファイル名に時間を利用していますが、表記に利用するならmoment ライブラリを利用するのがおすすめです。
- 投稿日:2020-09-24T22:00:13+09:00
非同期処理のタイムアウト時の処理をPromiseで書く方法の考察
背景
JavaScriptで非同期処理をコールバック関数としてセットしたとき、そのコールバック関数がいつまでたっても呼ばれないケースをケアするために、タイムアウト処理を書きたいことがある。
例:
- HTTPのリクエストを送信したがレスポンスがいつまでたっても返ってこない
- WebSocketで返信を期待して送信したが返信が返ってこない (ping を送ったが pong が返ってこない, etc.)
JavaScriptでは非同期処理をうまく扱うためにPromiseが言語組み込みでサポートされている。
したがって、以下のようなPromiseを作るのが言語の作法としても良さそうだしawait
キーワードとも相性がよいだろう。
- 成功したら
fulfilled
になる- 失敗したら
rejected
になる(失敗の原因はタイムアウトに限らないがreason
でわかる)やりたいこと
今回考えるシナリオの概要は
「Node.js上で、処理を移譲するため子プロセスを起動する。子プロセスの準備ができるまで待ち、準備ができたら実際に通信する」
というものだ。
具体的には以下のとおり。
- Node.jsの親プロセス (Main) が、子プロセス (Sub) を起動する
- Main は、Sub から
Ready
の通知を受け取るまで待つ- Sub は、準備が整ったら Main に
Ready
の通知を送る- Main は、一定時間内に Sub から
Ready
の通知を受け取らなかったら、 Sub の起動に失敗したとみなす (タイムアウト)子プロセスのラッパーとなるクラスは以下のようなものを想定することにする。
interface ISomeService { start(): void; stop(): void; onReady(callback: () => void): void; }
onReady
にコールバックを仕掛けておいてstart
で子プロセスを起動する。
子プロセスの準備ができたらonReady
に仕掛けておいたコールバックを発火させる。
このインターフェースだけではstart
とstop
しかできずありがたみが無いが、実際は他にも様々な機能がメソッドとして公開されている情景を想像して欲しい。
今時RPC的な呼び出しは珍しいかもしれないが、ありうるシナリオではあると考えている。トライ1: Promise.race を使う
「Promise タイムアウト」などで検索すると、
Promice.race
を利用する例が多くヒットする。
このアイデアで書いてみる。startService1.jsconst startService = (timeoutSec) => { const service = new SomeService(); const successPromise = new Promise((resolve, _) => { service.onReady(() => resolve(service)); service.start(); }); const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(`Proc did not launch within ${timeoutSec} seconds.`), timeoutSec * 1000); }); return Promise.race([successPromise, timeoutPromise]); };成功時の Promise とタイムアウト時の Promise を競わせるわけだ。これはよいアイデアだ。
このstartService()
を使う時は、以下のようにする。startService(5) .then((service) => { console.log('Proc launched successfully.'); service.xxxxx(); }, () => { console.log('Attempt to launch proc was timed out.'); });ダミーのサービスを作って動作確認してみた。
Pythonで数秒sleepしてファイルをtouchするだけのサービスと、ファイルが touch されたらonReady
コールバックを実行する JavaScript のラッパークラスがこちら。
https://gist.github.com/kosuke-suzuki/95f951901e71c65e232d5993caa4853e欠点
タイムアウトしたときにもサービスが起動したままになってしまう。
タイムアウトしたとき、返す Promise 自体は rejected の状態になっているので、呼び出している側で
then
の第二引数に指定した関数が実行される。
しかし、startService
内でsuccessPromise
として定義した処理自体は止まらない。
サービスの起動がタイムアウトしたとき、サービスプロセスは存在するがReady
状態ではないということになる。このような中途半端なプロセスは殺してしまいたい。タイムアウトとして扱うにも、これを殺したうえでthen
の第二引数の関数を実行したいのだ。トライ2: Promise.race + タイムアウト時の処理
catch
をチェーンしてその中で中途半端なプロセスを殺す処理を書く。Promise.race
が rejected となったときだけこの処理が実行される。startService2.jsconst startService = (timeoutSec) => { const service = new SomeService(); const successPromise = new Promise((resolve, _) => { service.onReady(() => resolve(service)); service.start(); }); const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(`Proc did not launch within ${timeoutSec} seconds.`), timeoutSec * 1000); }); return Promise.race([successPromise, timeoutPromise]) .catch((reason) => { service.stop(); return Promise.reject(reason); // これがないと呼び出し元に返す Promise が fulfilled になってしまう }); };注意点としては、エラーを呼び出し元に伝播させるためには、明示的に rejected な Promise を返す必要があること。
これをやらないと、呼び出し元は fulfilled として扱ってしまう。
これで、動作としては期待通りとなる。欠点
Promise.reject
を明示的に呼び出さないといけないのはまどろっこしく感じる。
そもそも元の Promise コンストラクタに渡す executor の仮引数resolve, reject
も、それぞれ片方しか活かせていない。トライ3: Promiseコンストラクタに渡す executor の中で行う
すべてひとつの Promise でやってしまおう、という案である。
タイムアウト時にサービスを止める処理はsetTimeout
の中で行う。
タイムアウト処理をセットする、というこのようなユースケースではsetTimeout
という名前はピッタリに思えますね。startService3.jsconst startService = (timeoutSec) => { const service = new SomeService(); return new Promise((resolve, reject) => { service.onReady(() => { clearTimeout(onTimedout); // 成功時は監視を止める必要あり resolve(service); }); service.start(); const onTimedout = setTimeout(() => { service.stop(); reject(`Proc did not launch within ${timeoutSec} seconds.`); }, timeoutSec * 1000); }); };これで Promise としてはひとつに集約される。
今度の注意点としては、サービスが正常に起動できた場合、タイムアウト監視を止めなければならない点である。これをしないと、サービスが起動して呼び出し元に返す Promise も fulfilled になっているにもかかわらず、タイムアウト監視によってサービスが止められてしまう。欠点
タイムアウト監視を止めるコードを書き忘れると、成功時もサービスが止まる。
成功時に失敗時のケアをしないといけないのはイマイチな感じがする。まとめ
function(timeoutSec) { const successPromise = new Promise((resolve, _) => { // 成功時の処理のなかで resolve() // 処理開始 }); const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(`Timeout: ${timeoutSec} seconds.`), timeoutSec * 1000); }); return Promise.race([successPromise, timeoutPromise]) .catch((reason) => { // Timeoutの後処理 return Promise.reject(reason); }); }もしくは
function(timeoutSec) => { return new Promise((resolve, reject) => { // 成功時の処理のなかで clearTimeout(onTimedout); resolve(); // 処理開始 const onTimedout = setTimeout(() => { // Timeoutの後処理 reject(`Timeout: ${timeoutSec} seconds.`); }, timeoutSec * 1000); }); };Web API では
XMLHttpRequest
XMLHttpRequest
はtimeout
プロパティ、ontimeout
イベントハンドラを指定できる。
https://developer.mozilla.org/docs/Web/API/XMLHttpRequestしたがってこのような感じになるだろう(動作未確認)。
const request = (timeout) => { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.timeout = timeout * 1000; xhr.ontimeout = () => reject(`Request was not responded within ${timeout} sec.`); xhr.onload = () => resolve(xhr.response); xhr.send(); }); };fetch
Fetch API は、組み込みで Promise を返してくれる使い勝手のよいAPIなのだが、なんとタイムアウトをサポートしていない!こんなことってありますか?
https://developer.mozilla.org/docs/Web/API/Fetch_API見ると、 AbortSignal という実験的なAPIを使えばタイムアウトも書けるようだ。
https://developer.mozilla.org/docs/Web/API/AbortSignal
こんな感じだろうか(動作未確認)。const fetchWithin = (url, timeoutSec) => { const controller = new AbortController(); setTimeout(() => controller.abort(), timeoutSec * 1000); return fetch(url, { signal: controller.signal }); };
- 投稿日:2020-09-24T21:59:54+09:00
varとletとconstの違い
はじめに
自分が忘れないために備忘録的な物として残します。
ES6から変数宣言に利用可能なletとconstが追加されました。
10年選手のWEBアプリの開発をやっていたりすると、世の中から取り残される。
varとletとconstの違いを理解した上で、今後はletやconstを使う。先にまとめ
初期化なしの宣言 再宣言 再代入 スコープ var 可能 可能 可能 関数 let 可能 不可能 可能 ブロック const 不可能 不可能 不可能 ブロック var
初期化なしの宣言
可能
var x;再宣言
可能
var x = 1; var x = 2;再代入
可能
var x = 1; var x = 2; console.log(var); // 2スコープ
関数スコープ
var x = 0; function fn(){ var x = 1; console.log(x); //1 } fn(); console.log(x); //0var x = 0; function fn(){ console.log(x); //0 } fn(); console.log(x); //0function fn(){ var x = 1; console.log(x); //0 } fn(); console.log(x); //ReferenceError: x is not definedlet
初期化なしの宣言
可能
let x;再宣言
不可能
let x = 1; let x = 2; // SyntaxError: Identifier 'x' has already been declared再代入
可能
let x = 1; x = 2; console.log(x); // 2スコープ
ブロックスコープ
{ let x = 1; if(1){ x = 2; console.log(x); // 2 } console.log(x); // 2 }{ if(1){ let x = 1; console.log(x); } console.log(x);// ReferenceError: x is not defined }{ let x = 1; { // この場合はスコープがブロックのため、同じ変数の宣言と判断されない let x = 2; console.log(x); // 2 } console.log(x); // 1 }{ for(let x=0; x<1; x++){ console.log(x); //0 } console.log(x);// ReferenceError: x is not defined }const
初期化なしの宣言
不可能
const x; // SyntaxError: Missing initializer in const declaration再宣言
不可能
const x = 1; const x = 2; // SyntaxError: Identifier 'x' has already been declared再代入
不可能
const x = 1; x = 2; // TypeError: Assignment to constant variable. console.log(x); // 2スコープ
ブロックスコープ
const x = 1; { // この場合はスコープがブロックのため、同じ変数の宣言と判断されない const x = 2; console.log(x); // 2 } console.log(x); // 1変数の巻き上げ
JavaScriptでは、関数内で宣言されたローカル変数は、すべてその関数の先頭で宣言されたものとみなされる。
{ let x = 1; { console.log(x); // ReferenceError: Cannot access 'x' before // この場合はスコープがブロックのためか、同じ変数の宣言と判断されない let x = 2; console.log(x); // 2 } console.log(x); // 1 }
- 投稿日:2020-09-24T21:46:05+09:00
JavaScriptのドット記法とブラケット記法について
プログラミング勉強日記
2020年9月24日
「ブラケット記法」という単語を初めて聞いたので、ブラケット記法について調べていくうちに似たようなドット記法を知ったので両者を比較してみる。ドット記法とブラケット記法
どちらもオブジェクトのプロパティにアクセスする方法である。
ドット記法はその名の通り
.
(ドット)を使ってプロパティにアクセスする。[]
はブラケットと呼ばれるので、ブラケット記法もその名の通り[]
(ブラケット)を使ってプロパティにアクセスする方法である。違い
ブラケット記法はプロパティ名に変数を使うことができるが、ドット記法は変数を使うことができない。また、ブラケット記法はプロパティ名が数字で始まるような不正な文字でもアクセスできる。(ドット記法の場合はエラーになってしまう)
まとめ
ここまで聞くとじゃあブラケット記法で書けばいいのでは?と思うが、基本はドット記法の方が読みやすいのでドット記法で書く。
違いのところでも述べているように、プロパティ名に変数を使う場合や不正なプロパティ名にアクセスしたい場合にはブラケット記法を使う。参考文献
- 投稿日:2020-09-24T21:37:52+09:00
Swiftで取得した画像をWKWebView内のHTMLで表示する方法
WkWebviewのコンテンツ内でSwiftで取得したPNG画像などのデータを使いたいケースがあると思います。
本記事ではWebViewJavascriptBridgeを使用した、PNG画像のSwiftとJavaScript間での通信にて表示する方法について記載します。
ローカルのHTMLをWebViewで読み込み(Swift)
まずはWKWebviewでローカルのhtmlを読み込む。
Swiftclass ViewController: UIViewController { var webView:WKWebView? override func viewDidLoad() { super.viewDidLoad() // WKWebViewのViewへの追加 self.webView = WKWebView(frame: view.frame) view.addSubview(self.webView!) let htmlPath = Bundle.main.path(forResource: "content", ofType: "html") let baseURL = URL.init(fileURLWithPath: htmlPath!) webView!.loadFileURL(baseURL, allowingReadAccessTo: baseURL) } }JavaScriptへのbridge(Swift)
WebViewJavascriptBridgeの初期化と、
registerHandlerにてJavaScript→Swiftへアクセスする受け口作成。Swiftself.bridge = WebViewJavascriptBridge.init(forWebView: webView) // WKWebView内のjavascriptからSwift内のデータを取得 self.bridge!.registerHandler("image") { (data, callback) in }JavaScript側のbridge設定(JavaScript)
読み込むhtml内のJavaScriptにてWebViewJavascriptBridgeでSwift間通信するためのテンプレートを追加後、
bridge.callHandlerにてSwift側への画像取得要求を行う。
※詳細はWebViewJavascriptBridge参照JavaScriptfunction setupWebViewJavascriptBridge(callback) { if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); } if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); } window.WVJBCallbacks = [callback]; var WVJBIframe = document.createElement('iframe'); WVJBIframe.style.display = 'none'; WVJBIframe.src = 'https://__bridge_loaded__'; document.documentElement.appendChild(WVJBIframe); setTimeout(function () { document.documentElement.removeChild(WVJBIframe) }, 0) } setupWebViewJavascriptBridge(function (bridge) { bridge.callHandler('image', { 'key': 'value' }, function responseCallback(responseData) { }) })Base64データへの変換(Swift)
WKWebview側でPNGを読み込むためbase64のstringへ変換してJavaScript側へ返却。
Swiftself.bridge!.registerHandler("image") { (data, callback) in if let imagePath = Bundle.main.path(forResource: "PNG", ofType: "png") { // PNGをUIImageに let image = UIImage(contentsOfFile: imagePath) // Data型へ変換 let data = image?.pngData() // PNGのデータをbase64エンコードしてWebViewで表示できるよう修正 let base64Image = data?.base64EncodedString(options: .endLineWithLineFeed) callback!(base64Image) } }base64形式のPNGデータの読み込み
Swift側から返却されたbase64形式のresponseDataは、
PNGのデータURIとなっていないため、先頭に
data:image/png;base64,
を追加してimgタグで読み込み。JavaScriptbridge.callHandler('image', { 'key': 'value' }, function responseCallback(responseData) { var html = [ ]; html += '<img src="data:image/png;base64,' html += responseData html += '" alt="PNG"/>' document.getElementById("content").innerHTML = html })上記にてWKWebview上でSwiftで取得したPNGが表示される。
サンプルアプリ
- 投稿日:2020-09-24T21:30:39+09:00
2020年で最も需要のあるプログラミング言語
本記事はMost in-demand programming languages in 2020の日本語訳です。翻訳元に報告していますが、もし苦情が来たら消します。
翻訳は不慣れなので変なところもあると思いますが、ご容赦ください。
ソフトウェア開発業界は絶えず変化しており、それは開発者の能力に対する企業のニーズも変化していることを意味します。そのため、あなたが想像できるように、Webアプリケーション、ゲーム、アルゴリズムなどのあらゆる側面の開発をカバーするために、選択できるプログラミング言語はたくさんあります。その上で、私たちは2020年で最も需要のあるプログラミング言語とその主な特徴について触れます。
- JavaScript (回答者の71%がこのスキルに関する求職者を探している)
- Java (57%)
- C# (53%)
- Python (51%)
- PHP (40%)
- Ruby (15%)
JavaScript
JavaScriptが2020年で最も需要のあるプログラミング言語リストのトップであることは全く不思議ではありません。
今日では何らかの方法でJavaScriptを使用することなしに開発者になることは不可能です。調査の回答者の71%以上がJavaScriptでコードを書ける開発者を探しており、JavaScriptが最も人気のあるプログラミング言語のうちの一つであることが想像できます。また、JavaScriptはWebにおける偏在性と私たちのインターネットへの重い依存のため、非常に人気があります。Twitter、Facebook、YouTubeなどの最も人気のあるサイトの多くは、JavaScriptを使ってインタラクティブなWebページを生成したり、コンテンツを動的にユーザに表示したりしています。
JavaScriptはコア言語があり、追加の開発ツールによって柔軟性が保たれています。JavaScriptは寛大で柔軟な構文を持ち、全てのメジャーなブラウザで動作するため、初学者にとって最も簡単なプログラミング言語の一つです。今日、JavaScriptは世界中で広く使用されているプログラミング言語であり、あらゆるところで動作します:コンテナ、マイクロコントローラ、モバイル端末、クラウド、ブラウザ、サーバなど。
主な特徴
- JavaScriptはここ数年で大規模な現代化と徹底的な点検を経てきました。ES5、ES6といったJavaScriptのメジャーなリリースはいくつかのモダンな機能が追加され、今日のJavaScriptは過去10年間のJavaScriptとは完全に別物です
- Node.jsのおかげでJavaScriptはイベント駆動なプログラミングを提供し、特にI/Oの複雑なタスクに適しています。今日では、Node.jsとJavaScriptは、サーバとモバイル端末を含めてほとんど全てのプラットフォームで動作します
- JavaScriptはブラウザプログラミングにおいて、議論の余地のない王様です。今日、Web開発は主にVue.js、Angular、ReactといったJavaScriptベースのSPAフレームワークによって支配されています
Java
Javaは2020年で最も需要のあるプログラミング言語リストで2位の座を手にしています。
Javaは、ビジネスでは最も人気のモバイルコンピューティングプラットフォームであるAndroidのネイティブ言語であることから人気のあるプログラミング言語です。
Javaは過去数年の間に、非常にユーザに優しいモダンな言語にビジネスの一部を奪われました。
Javaは欠陥の改善に取り組んでおり、GraalVMアクションを介してクラウドにフィットさせる努力をしています。Javaはまだエンタープライズではナンバーワンのプログラミング言語です。Javaは誕生してからずっとトップクラスの需要があるプログラミング言語です。大企業の多数がバックエンドWebシステムやデスクトップアプリケーションのためにJavaを使っているため、もし開発者がJavaを知っていれば、その開発者は継続的に需要が高い状態になるでしょう。Javaは静的型付けの言語で、そのためバグが少なく、メンテナンスを速く行えて管理しやすいです。
主な特徴
- Javaはマルチパラダイムを提供し、強力で、多機能で、柔軟な学習曲線と高い生産性を持つインタプリタ型のプログラミング言語です
- Javaは厳格な後方互換性を持ち、これはビジネスアプリケーションにとって重要な要件です。JavaではScalaやPythonのようなメジャーな破壊的変更が取り入れられたことはありません。その結果、Javaはいまだにビジネスにとってナンバーワンの選択肢です
- JavaのランタイムであるJVMはソフトウェア工学の結晶で、ビジネスにおいて最高の仮想マシンの一つです。何年もの技術革新とエンジニアリングの職人技により、JVMは素晴らしい機能と性能をJavaに提供します。JVMはいくつかの優秀なガベージコレクションもJavaに提供しています
C#
C言語は、移植性と、AppleやMicrosoftのような巨大なIT企業から早期に採用されたことのおかげで最も古くて最も人気のあるプログラミング言語のうちの一つとなった言語です。1
C-sharpとしても知られるC#は、2000年にMicrosoftによって開発された言語のスピンオフです。C#はオブジェクト指向言語で、アクションの代わりにオブジェクトを中心に、ロジックの代わりにデータを中心に構築されます。C#の特徴はJavaと似ており、Windowsのデスクトップアプリケーションとゲームを開発するのに特に有効です。しかし、C#はWebアプリケーションとモバイルアプリケーションを開発するのにも使えます。C#はC++のようなC派生の言語に似た構文を使っており、あなたがCファミリーの中の別の言語から来たのであれば簡単に習得できます。C#は銀行のトランザクション処理のような大企業のアプリケーションの開発にしばしば使われます。C#は人気のUnityゲームエンジンを使った2Dや3Dのビデオゲームを作るために推奨される言語です。今日では、C#はWindowsプラットフォームにおいてだけでなくLinuxプラットフォームやiOS/Androidプラットフォームでも幅広く使用される、マルチパラダイムな言語です。
主な特徴
- C#はプラットフォーム非依存でもあり、Linux、Windows、モバイル端末で動作します
- Microsoftの後ろ盾があり、長年に渡り業界にいるC#はライブラリとフレームワークの大きなエコシステムがあります。ASP.NETはWebアプリケーション開発に使われます(主にWindows上での)
- 開発者体験という面では、C#はJavaよりはるかに優れています
Python
Pythonはおそらくこのリストの中で最もユーザに優しいプログラミング言語です。Pythonの構文は明確で、直感的で、ほとんど英語だとよく言われ、初学者にとって本当に良い選択肢です。Pythonは高レベルで、汎用性が高く、Webアプリケーションやデータ解析、アルゴリズムの開発などに使われます。Pythonは、科学計算やエンジニアリング、数学といったフィールドで頻繁に使われるSciPyやNumPyのようなパッケージも持っています。
Pythonはスクレイピングにしばしば使われ、PHPでコーディングするのに何時間もかかるものが、Pythonだと数分しかかかりません。Pythonは、あなたの時間を消費する日々のタスクを含む特定の作業を自動化するためにも使うことができます。もしバックエンドのWeb開発の例に興味があれば、オープンソースの(Pythonで書かれた)Djangoフレームワークは人気で、学ぶのが簡単で、多機能です。そしてJavaのように、Pythonには多様なアプリケーションがあり、あなたのユースケースのために最も良いプログラミング言語を選択する時に、多様で強力な選択肢となります。今日、Pythonは広く行き渡り、ソフトウェア開発の多くの分野で使用され、そしてその勢いが衰えるようには見えません。
主な特徴
- Pythonには非常に活発なコミュニティとサポートがあります。たとえあなたがデータサイエンス、業務アプリケーション、AIのどれで働いているのだとしても、常に十分なPythonの組織2やフレームワークが見つかります
- Pythonには第一級のC++/Cとの統合機能があり、CPU負荷の高いタスクをシームレスにC++/Cにオフロードすることができます。Pythonは、SciPy、Pandas、NumPyなど、統計、Scikit-Learn3、数学、および計算科学のための素晴らしいツールセットも提供します。 結論として、Pythonは機械学習/ディープラーニング/データサイエンスの状況やその他の科学的な領域を支配しています
- Pythonのウリはその言語設計にあります。それは生産性が高く、エレガントで、シンプルで、その上強力です。Pythonは開発者経験という面で黄金律を設定し、Julia、Goといったモダンな言語に対して多大な影響を与えました
PHP
たとえ多くの論争があるとしても、PHPは2020年で最も需要のあるプログラミング言語リストに入っています。
PHPは幅広く使用されているオープンソースの汎用スクリプト言語で、典型的にはWebアプリケーション開発に適しています。たとえ以前ほどではないとしても、PHPは依然として世界中で最も用いられているプログラミング言語のうちの一つです。PHPはFacebookやYahoo!といった多数の大きな会社によって使用されています。PHPは汎用的で、動的な、基本的にはサーバサイドのWebアプリケーションの開発のために使用されているプログラミング言語です。
PHPはJavaScriptのような新しいWeb言語が実現するまでずっと、ほとんど全てのモダンなWebサイトを構築可能にしました。いくつかの調査によると、PHPがWebの3分の1を支えているとのことです。たとえPHPが以前ほどは注目されていないとしても、PHPは今後何年にもわたって進化を継続し、最も人気のあるプログラミング言語のうちの一つとしての地位を維持するでしょう。
主な特徴
- 多くの大きな会社がPHPを使用しており、そのための素晴らしいツールのサポートに繋がっています
- PHPはWebアプリケーション開発に過去25年4に渡って使用されており、強力で安定したPHPフレームワークが数多く市場に存在します
- PHPは非常に生産性が高いサーバサイドWeb開発プログラミング言語のうちの一つです。結果として、Webアプリケーションを素早く開発するために、IT業界で広く使われています。最も有名なSNSの一つであるFacebookはPHPで開発されました
Ruby
特に、Rubyは人気のあるRuby on Rails Webアプリケーションフレームワークのための基盤として使われます。RubyはC言語で実装され、ガベージコレクタがあります。Rubyは90年代半ばに作られましたが、ここ10年ほどの間に人気を獲得しました。Rubyは非常に動的で、オブジェクト指向言語で、プログラマーが使うための様々な機能を持っています。Rubyの経験が6年以上ある開発者は、現在の採用状況では2倍の面接依頼を受けることが期待できます。
Twitter、Shopify、そして多くのスタートアップがいずれかの段階でRuby on Railsを使ってWebサイトを構築しています。Rubyはまた、素晴らしいハイテク企業との関連性のために選び出すには本当に良い言語です。5
Pythonのように、Rubyは開発者の生産性と幸福を非常に重視しています。Rubyは新しい開発者にとって学習曲線がフラットになる非常に優れた言語でもあります。主な特徴
- RubyはTwitter、GitHub、Airbnbのような最大級のソフトウェアプロジェクトで使われ、そして素晴らしいツールとフレームワークの支援があります
- Rubyそれ自体は破壊的ではありませんが、RubyのWeb開発フレームワークであるRuby on Railsはおそらく最も破壊的で、影響力のあるサーバサイドWeb開発フレームワークです
- Rubyはプログラミング言語の最高の機能のうちのいくつかをうまく利用してきました: 簡潔さ、動的、ガベージコレクタのあるオブジェクト指向、そして関数型です
- 投稿日:2020-09-24T19:08:07+09:00
Js:tokenを取得する為に苦労した事象
今回は、token取得が出来ない事象が発生しその原因と対処を忘れない様に覚書していきます。
事象内容
Payjpでのクレジット決済機能(テスト)を実装中にtokenが取得が出来なかった。
カード決済機能js
そこでturbolinksをコメントアウトしてみました。
turbolinksは、たまに発火に悪影響を及ぼす事があるみたです。しかし、tokenが取得出来ない事と、その前に実装していた商品出品機能の販売価格のjsが発火しない様になってしまいました。
※販売価格jsはturbolinksを有効にすると発火することは確認出来ました。そこで、販売価格のjsコードを見直して見た
当初のjsコード
コード変更後の動作確認で、turbolinksコメントアウト後の販売価格js、クレジットカード決済のtokenの取得も出来ました。
今回学んだこと
今の動作確認が良くても、後々エラーが発生する事がある。
コードを見直す際は、今記述しているところだけではなく、前後のコードの流れを考え記述する事以上です。
- 投稿日:2020-09-24T18:38:29+09:00
【Gatsby.js】単一のjsonファイルを元に複数のページを生成する
Gatsby.jsを使って、jsonファイルから複数のページを生成する方法をまとめます。
Gatsby、情報自体はそこそこ豊富なんですが、日本語の情報が少ないですね。。記事データをまとめたjsonファイルを作成
$ mkdir src/data $ touch src/data/articles.jsonsrc/data/articles.json[ { "slug": "how-to-use-gatsby", "title": "【Gatsby.js】「ここだけ押さえれば普通に使える」って知識をまとめてみた", "content": "Gatsby.jsでシンプルな静的サイトを作る際に必要な知識だけをまとめました。CMSとの連携等については今回は扱いません。本文テキスト本文テキスト本文テキスト本文テキスト本文テキスト本文テキスト", "tags": ["React", "Gatsby"] }, { "slug": "learn-react", "title": "Reactの学習、今からやるならこうする", "content": "Reactの学習を初めて約2週間が経ちました。Railsの時と違って「とりあえずこれをやっておけば良い」ってものが見当たらずなかなか大変でしたが、試行錯誤の上色々と見えてきたのでまとめていきます。", "tags": ["React"] }, { "slug": "restore-mac", "title": "Macの初期化→リストアを良い感じにする", "content": "Brewfile、Mackup、Docker、Dropboxを使って良い感じにリストアできる環境を作ったので、まとめます。以下の記事をめちゃくちゃ参考にしました。macOSでの開発環境を全部Docker化したらリストア時間が1時間半になった", "tags": ["Docker", "Brewfile", "Mackup"] } ]記事のテンプレートページのベースを作成
$ mkdir src/templates $ touch src/templates/article.jssrc/templates/article.jsimport React from "react" export default () => ( <> <h1>タイトル</h1> <p>本文</p> <span>タグ1</span> <span>タグ2</span> </> )記事を生成する処理を記述
$ touch gatsby-node.jsgatsby-node.jsconst path = require("path") const data = require("./src/data/articles.json") exports.createPages = ({ actions }) => { const { createPage } = actions const template = path.resolve("./src/templates/article.js") data.forEach(article_object => { const path = `articles/${article_object.slug}` createPage({ path, // 生成されるページのpath component: template, // ページのベースとなるテンプレート context: article_object, // GraphQL経由で受け渡すデータのオブジェクト }) }) }GraphiQLを開き、データを取得するためのクエリを作成
gatsby develop
で開発サーバーを立ち上げ、ブラウザで http://localhost:8000/ にアクセスしてGraphiQLを開く。
allSitePage > edges > node > context
と順に開いていくと、title
、content
、tags
が見つかるのでチェックを入れて実行し、欲しいデータが取得できることを確認。GraphQLで取得したデータをテンプレートページに埋め込む
作成したクエリで必要なデータを取得し、テンプレートに埋め込みます。
GraphQLで取得したデータはdata
というオブジェクトに格納されるので、そこから値を取り出して出力します。src/templates/article.jsimport { graphql } from "gatsby" import React from "react" export default ({ data }) => { const article = data.allSitePage.edges[0].node.context return ( <> <h1>{article.title}</h1> <p>{article.content}</p> {article.tags.map(tag => ( <span>{tag}</span> ))} </> ) } export const query = graphql` query { allSitePage { edges { node { context { content tags title } } } } } `開発サーバーを再起動して確認
開発サーバーを再起動し、存在しないURLを入力して404ページを表示すると、ページ一覧に今作ったページが追加されていることが確認できます。
参考
- 投稿日:2020-09-24T18:33:32+09:00
組み合わせ爆発ハラスメントの処方箋
プログラミング初学者向けの内容です。
今のところ Golang, Ruby, Python, JavaScript, TypeScript による処方箋のみ掲載しています。ある日のこと
知人「店長からさぁ、
『うちはメニューの数が少ないから、
コンビ・メニュー作ることにした』『とりあえず、
今あるメニューを組み合わせて、
単品から全部入りまで
すべての組み合わせのリスト作ってくれ!』って、言われたんだけど…」
俺「え? それって、
???があるとしたら、
↓みたいなやつ?」1:?
2:?
3:? ?
4:?
5:? ?
6:? ?
7:? ? ?知人「そう。そう。それ!それ!」
俺「作れるけど、、
きっとものすごい数になるよ。
単品メニューって何種類くらいあんの?」知人「20種類くらいかなぁ。。
物好きな店長でしょ?!
めんどくせぇ。。」俺「…」
俺「あのさぁ、、
面倒くさいとかの次元じゃないんだけど。。」俺「0.1 mm 厚の紙を 26 回折ったら
富士山より高くなるって知ってる?」\begin{align} 0.1mm\times2^{26} &= 6,710,886.4 mm\\ &\fallingdotseq 6.7 km \end{align}知人「あ、なんか聞いたことあるかも。。」
俺「それと同じなんだけど、、」
\begin{align} 2^{20} - 1 &= 1,048,575 通り\\ &\fallingdotseq 105万通り \end{align}俺「105万通りは、
さすがにメニュー充実しすぎだろ(笑)」知人「へ〜、そんなになるんだぁ!」
俺「…」
Golang による処方箋
取り急ぎ Golang で書いてみます。
menu.gopackage main import ( "flag" "fmt" "strings" ) func comball(in []string) [][]string { n := 1 << len(in) out := make([][]string, n) for i := 0; i < n; i++ { ss := make([]string, 0, len(in)) for j := 0; j < len(in); j++ { if 1<<j&i != 0 { ss = append(ss, in[j]) } } out[i] = ss } return out } func main() { flag.Parse() args := flag.Args() for i, ss := range comball(args) { fmt.Printf("%d:%s\n", i, strings.Join(ss, " ")) } }Go のバージョンです。
version$ go version go version go1.15.2 linux/amd64
実行してみます。
実行$ go run menu.go ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? > menu.txtGolang はコンパイルも実行も速くていいですね。
Generics をサポートしていないので string 型専用の関数になってしまい、そこが残念ポイントですが、LL のような感覚で気軽にいろいろ試せます。
(Generics は来年サポートされるようですね)プログラムは標準出力へ書き出すようにしましたが、そのまま出力するとたぶん大変なことになるので menu.txt という名前のファイルへリダイレクトしました。
ファイルの先頭を見てみます。
ファイルの先頭$ head menu.txt 0: 1:? 2:? 3:? ? 4:? 5:? ? 6:? ? 7:? ? ? 8:? 9:? ?最初の行に「なし」を出すようにしてます。
今度は末尾を見てみます。
ファイルの末尾$ tail menu.txt 1048566:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048567:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048568:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048569:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048570:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048571:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048572:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048573:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048574:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048575:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?1行目の「なし」を除いて、ちゃんと 104 万 8,575 行あります。
ファイルサイズが 56.4 MiB もありますが、、(笑)
(圧縮して 4 Mib くらい)
ひとまず、これで大丈夫そうです。あと、 Golang はサクッとクロスコンパイルしてシングルバイナリが作れるのがいいですね!
とりあえず、AMD64 互換 の linux と Mac と Windows 用を用意して持ち帰ってもらうことにました。build.shGOOS=linux GOARCH=amd64 go build -o ./linux-amd64/menu menu.go GOOS=darwin GOARCH=amd64 go build -o ./darwin-amd64/menu menu.go GOOS=windows GOARCH=amd64 go build -o ./windows-amd64/menu.exe menu.goでも、、
せっかくプログラムを書いてあげたのに、結局、彼は「店長に怒られそう…」という理由で、これを使ってくれませんでした。
遠い昔を思い出す
知人は採用してくれませんでしたが、これってテストデータの生成(フラグの組み合わせとか)にも応用できますよ。
昔、入社 1 年目のとき、まるで野球部の球拾いのごとくテスターをやらされた日々を思い出します。
ある日、明確なテスト仕様書もない中で、先輩 SE から無茶振りされました。
「可能な組み合わせを全部テストするなんて当たり前なの!お前バカなの!?」
と怒鳴られました。
遠い昔のことなので、細かいことはあまり良く覚えてませんが、同期の仲間と計算してみると、1 個 1 分でやったとしても、寝ずにやって何十年かかるとか、そんな途方もないオーダーでした。
現実的な解が思い浮かばなかったので、もっと上の先輩に相談しました。
すると、即答で「バカは相手にしなくていいから(笑)!」と言ってくれ、あっさりとこの問題は解決してしまいました。今考えると完全にパワハラでした。
2〜3日、真剣に悩みましたから(笑)無茶ぶりした先輩 SE はその後しばらくして会社を辞めていきました。
でも、マシンが高速化し自動テストがあたりまえになった現代では、当時できなかったいろんなことができるようになりました。
あのとき、もし今の環境が手元にあったら、この程度の簡単な処方箋であっさりと解決していたのかも。。
そう考えると感慨深いものがあります。先輩 SE が後輩を馬鹿呼ばわりすることもなく、彼がさらに上の先輩から馬鹿呼ばわりされることもなかったかもしれません。
ということで、他の言語の例もいくつか載せておきます。
Ruby で調合する
Ruby はあまり書いたことがないので、らしいコードじゃないかもしれません。
プログラムをみる
menu.rbdef comb(arr) out = [] n = 1 << arr.size n.times do |i| a = [] arr.size.times do |j| if 1 << j & i != 0 a << arr[j] end end out << a end out end comb(ARGV).each.with_index(0) do |a, i| puts i.to_s + ":" + a.join(" ") end実行$ ruby --version ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux] $ ruby menu.rb ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? > menu.txt $ tail menu.txt 1048566:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048567:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048568:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048569:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048570:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048571:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048572:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048573:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048574:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048575:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?まあ、Ruby の場合は組み込み関数を使えば、↓これでもいけますね。
出力順が違いますけど。。menu2.rbdef comball(arr) out = [[]] arr.each_with_index { |s, i| out += arr.combination(i+1).to_a } out end comball(ARGV).each.with_index(0) do |a, i| puts i.to_s + ":" + a.join(" ") endRuby って、書く順番がなんか他の言語と違いますよね。
この感覚が気持ちよくて好きです。。Python で調合する
プログラムをみる
menu.pyimport sys def comball(arr): out = [] n = 1 << len(arr) for i in range(n): a = [] for j in range(len(arr)): if 1 << j & i != 0: a.append(arr[j]) out.append(a) return out arr = sys.argv arr.pop(0) for i, a in enumerate(comball(arr)): s = ' '.join(a) print('{0}:{1}'.format(i, s))実行$ python3 --version Python 3.6.8 $ python3 menu.py ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? > menu.txt $ tail menu.txt 1048566:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048567:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048568:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048569:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048570:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048571:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048572:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048573:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048574:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048575:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?end や } が必要ない分、関数本体が短く書けますね。
JavaScript で調合する
プログラムをみる
menu.jsfunction comball(arr) { const out = [] const n = 1 << arr.length for (let i = 0; i < n; i++) { const a = [] for (let j = 0; j < arr.length; j++) { if ((1 << j & i) != 0) { a.push(arr[j]) } } out.push(a) } return out } const arr = process.argv.slice(2) comball(arr).forEach((a, i) => console.log(i + ":" + a.join(" ")) )実行$ node --version v12.16.1 $ node menu.js ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? > menu.txt $ tail menu.txt 1048566:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048567:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048568:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048569:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048570:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048571:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048572:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048573:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048574:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048575:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?JavaScript って & より != の方が演算子の優先順位が高いんですよね。
だから括弧が付いてます。
なんか理由があるんですかね。。TypeScript で調合する
プログラムをみる
menu.tsfunction comball<T>(arr: T[]): T[][] { const out: T[][] = [] const n = 1 << arr.length for (let i = 0; i < n; i++) { const a: T[] = [] for (let j = 0; j < arr.length; j++) { if ((1 << j & i) != 0) { a.push(arr[j]) } } out.push(a) } return out } const arr: string[] = process.argv.slice(2) comball(arr).forEach((a, i) => console.log(i + ":" + a.join(" ")) )実行$ npx ts-node --version v8.10.1 $ npx ts-node menu.ts ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? > menu.txt $ tail menu.txt 1048566:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048567:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048568:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048569:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048570:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048571:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048572:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048573:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048574:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ? 1048575:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ☕ ?TypeScript は関数シグネチャを見れば何をしそうか分かるところが良いですね。
あと、Generics 使っています。
でも、トランスパイルが遅いところが玉に瑕です。あとがき
本稿で扱ったプログラミング言語は演算子の使い方もほとんど同じなので、みんな似たコードになりましたが、それでも言語の個性が出ている部分もあって面白かったです。
後で他の言語も追記するかもしれません。
要望があればプログラムの解説も付けるかもしれません。あと、各言語のエキスパートの方で、もっとカッコいい書き方を知ってるよ! という人は是非教えてください。
それでは!
- 投稿日:2020-09-24T18:16:49+09:00
セル内改行に対応したcsvの読み込み
前回のあらすじ
プログラム自体は出来ているが記事にするまでに相当時間が空いた。
前回はレイアウトを作ることができた。
今回はリストをつくるために元のデータを読み込む。csvを読み込むために。
ローカルファイルのcsvを読み込みたい。
しかし、調べてみるとボタン操作をせずにファイルを読み込むためにはgoogleChromeではできないようだ。
さらに調べるとNode.jsを使えば操作できるようだ。ついでにデスクトップアプリ作成のためにElectronを導入することになる。今後は
- Node.js
- Electron
が導入されている環境を想定して記事を書いていく。最終的な表示の都合上、読み込むcsv元データはセル内改行を行っているものを想定する。
Excel→csvに変換したものである。csvファイルの読み込みに関しモジュールを導入した。
npm で導入できるので詳細は検索して欲しい。
- モジュール fs :ファイル読み込みのためのモジュール
- モジュール iconv-lite :文字コード変換のためのモジュール
というわけでカキカキした。
//csvセル内での改行コードに対応(行の最後のセルはセル内改行されていないこと) var database =[[]]; //データを格納するための変数 const fs = require('fs'); const iconv = require('iconv-lite'); var filename = __dirname + '\\database.csv' //ファイルパスを記載 const content = fs.readFileSync(filename); //ファイルの読み込み var buf = new Buffer.from(content, 'binary'); //バイナリデータにする var buf2 = iconv.decode(buf,"Shift_JIS"); //文字コード変換 var database_tmp = buf2.split(','); //,で区切り列ごとに配列に格納 var line_no =0; var data_no =0; var line_tmp; for(var i=0;i<database_tmp.length;i++){ //行末尾の改行コードを判定し該当すれば行とする if(database_tmp[i].indexOf('"')==-1 && database_tmp[i].indexOf('\n')>=0){ line_tmp = database_tmp[i].split('\n'); //末尾の改行セルを分ける database[line_no][data_no]=line_tmp[0]; ///分けたセルを挿入 line_no = line_no + 1; //配列の中で計算できないため外で変数計算 database[line_no]=[]; //行要素に列用の配列を挿入する databse[line_no][0]=line_tmp[1]; //行の頭として分割した片割れを挿入 data_no =1; }else{ database[line_no][data_no] = database_tmp[i].replace('"',''); data_no = data_no + 1; } } }これで自分の想定しているデータはセル内改行に対応してした。
が、コメントに書いたようにすべての形には対応していないのでコピペする人は当然だがテストして下さい。
※行の最後のセルはセル内改行されていないこと
- 投稿日:2020-09-24T18:04:48+09:00
Svelteで「XXX has unused export property 'XXX'...」と出たときの対処方法
はじめに
とあるSvelteライブラリのドキュメントサイトをローカル環境で動かしてたところ(Contribute目的で)ログ上で
XXX has unused export property 'XXX'. If it is for external reference only, please consider using export const answer
という警告ログが出ていた。
その時の対処方法を記事にする。再現
unused export propertyと記載されている通り、プロパティ渡し先のコンポーネントで宣言はしてるけど、使用していないときに起こるようです。
index.svelte
<script> import Nested from './Nested.svelte'; </script> <Nested count={10}/>Nested.svelte
<script> export let count; </script>そうすると、、、
警告ログを確認することができました。
対応
今回の場合まったく使用してないプロパティなので、渡してる部分と宣言してる部分を削除することで警告ログは消える。
もちろん警告ログなので、放置していてもおそらく動作上に問題ないかと思います。(すごく気にはなるかと思いますが)
- 投稿日:2020-09-24T17:41:21+09:00
[React] 初心者は必見でしょ。ルーティングのエラー解決と解説
どうもこんにちわ
今回は、Reactのルーティングを設定する上でよく目にする
Error: Invariant failed: You should not use <Link> outside a <Router>というエラーについて、僕自身調べてみて
Typescript中心のエラーの解決法が多いや、、。
と思ったので、シンプルなReactでの説明をしていきたいと思います。
また、解決に至るまでのプロセスで出会った3つの関数?(っていうのかな)たちも
どんどん解説していくので、ぜひ最後まで見ていってください。まず僕がこのエラーに引っかかった時のコードかこちら、
App.jsimport React from "react"; import { Link } from "react-router-dom"; export default function Home() { return ( <div className="container"> <title>Hands on Mania</title> <main className="u-text-center"> <h1 className="font-family-homemade">Hands on Mania</h1> <p className="description">studying</p> <Link to="/Auth/SignIn">signin</Link> </main> </div> ); }策① Memoryrouterを使う
最初は、react-router-domから「Memoryrouter」をインポートして
<Memoryrouter> <Link to="/Auth/SignIn">signin</Link> </Memoryrouter>このようにしましょう。との解決策でした
早速試したのですが、、結果は変わらず。。
まぁまぁ焦らず、ゆっくり行こーや。
ここで解決できてたらQiitaの記事にはしてません。では簡単に解説します。
Memoryrouterについて簡単に調べたところ、
テストおよびReact Nativeのような非ブラウザー環境で役立ちます。
と調べて一番上にあった記事で解説されていたので、
テストコードを書く時。または、ReactNative環境で普通は使うものなのかな?
と思いました。Memoryrouterと似たような(?)ものにhooksのuseMemoが思い出されたので、
こちらも申し訳程度に紹介。React公式によれば
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);このような公式で使い、あくまで最適化のために使うそうだ
初心者にはまだ早い領域かもしれないですね?策② Route と Switch を使う
こちらはgithubに乗っていた解決策だったので、
これはいけるっしょ。と内心ヨユーな表情で思っていたのですが、
あっけなく予想を裏切られました。
(僕のやり方が違う可能性が高い、、。)方法としては、このように記述するのさっ。というやり方です。
import { Route, Switch } from "react-router"; <Switch> <Route exact path="/Auth/SignIn" /> </Switch>しかし、結果は儚くとも同じようなエラー内容。。
Error: Invariant failed: You should not use <Switch> outside a <Router>だめだー、、。
俺の人生終わったー、、。諦めないで!
そうなるのはまだ早い。次で解決策を提示しながら一緒に解説するから、ちょっと落ち着け、モちつけ、、、、
解決策③ (これで解決!)
App.jsimport { BrowserRouter as Router, Route } from "react-router-dom"; import SignIn from "./Auth/SignIn"; <Router> <Header /> <Route exact path="/Auth/SignIn" component={SignIn} /> </Router>components/Header.jsimport { Link } from "react-router-dom"; <Link to="/Auth/SignIn">Signin</Link>今回はこれで解決しました!?
LinkとRouterをつないでいる感じですね
こちら先程の策と比べながら、今回大切にして欲しい
3つのポイント
を解説していきますね。
①react-routerとreact-router-domが似ているようでちょっと違う件についてまず、今回import したのはreact-router-dom です。
先程はreact-routerでしたね。
これは何が違うねん!
まぁまぁ一回落ち着け、モちつけ、、、、、、(お気に入りのボケです。)
簡単な違いはというと、
react-router-domはBrowserRouterをRouterと一緒に使い、
react-routerは historyをRouterと一緒に使うということです。詳しい解決や記述方法はreact-routerとreact-router-domこちらの記事を御覧ください!
まぁあまり大きな違いは無いようです。
② as の効果範囲
今回僕が BrowserRouter をimportする際に用いたasですが、
これの影響範囲は、例えばこの記述だと、どこまでだと思いますか?import { BrowserRouter as Router, Route, Switch } from “react-router-dom”;正解は、、、
Routerだけです。☆彡
以上。
③ componentsについて
<Route exact path="/Auth/SignIn" component={SignIn} />まず、exactの記述に関して
pathに指定したルートと全く同じところに移動する感じです。
exactをつけないと違うルートに行ってしまう可能性があるためつけたほうがいいです。
componentの記述について指定したconponentsを表示します。
今回はSignIn componentsを表示
表示したいcomponentsを import し忘れないように注意。以上で簡単なルーティングの設定方法の解説を終わりにします。
最後に、
この記事を見る限り、v3 から v4への移行期に、色々と新しいのが出たり、
古いのが消えていたりしているそうなので、念の為、検索する際は確認してみたほうが無難かもしれないですね
react-router v3からv4へのマイグレーション
BrowserRouter今回の参考文献【React】ルーティング設定方法
- 投稿日:2020-09-24T17:41:21+09:00
[React] ルーティング設置の仕方に悩んでいる方。ルーティングのエラー解決と解説
どうもこんにちわ
今回は、Reactのルーティングを設定する上でよく目にする
Error: Invariant failed: You should not use <Link> outside a <Router>というエラーについて、僕自身調べてみて
Typescript中心のエラーの解決法が多いや、、。
と思ったので、シンプルなReactでの説明をしていきたいと思います。
また、解決に至るまでのプロセスで出会った3つの関数?(っていうのかな)たちも
どんどん解説していくので、ぜひ最後まで見ていってください。まず僕がこのエラーに引っかかった時のコードかこちら、
App.jsimport React from "react"; import { Link } from "react-router-dom"; export default function Home() { return ( <div className="container"> <title>Hands on Mania</title> <main className="u-text-center"> <h1 className="font-family-homemade">Hands on Mania</h1> <p className="description">studying</p> <Link to="/Auth/SignIn">signin</Link> </main> </div> ); }策① Memoryrouterを使う
最初は、react-router-domから「Memoryrouter」をインポートして
<Memoryrouter> <Link to="/Auth/SignIn">signin</Link> </Memoryrouter>このようにしましょう。との解決策でした
早速試したのですが、、結果は変わらず。。
まぁまぁ焦らず、ゆっくり行こーや。
ここで解決できてたらQiitaの記事にはしてません。では簡単に解説します。
Memoryrouterについて簡単に調べたところ、
テストおよびReact Nativeのような非ブラウザー環境で役立ちます。
と調べて一番上にあった記事で解説されていたので、
テストコードを書く時。または、ReactNative環境で普通は使うものなのかな?
と思いました。Memoryrouterと似たような(?)ものにhooksのuseMemoが思い出されたので、
こちらも申し訳程度に紹介。React公式によれば
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);このような公式で使い、あくまで最適化のために使うそうだ
初心者にはまだ早い領域かもしれないですね?策② Route と Switch を使う
こちらはgithubに乗っていた解決策だったので、
これはいけるっしょ。と内心ヨユーな表情で思っていたのですが、
あっけなく予想を裏切られました。
(僕のやり方が違う可能性が高い、、。)方法としては、このように記述するのさっ。というやり方です。
import { Route, Switch } from "react-router"; <Switch> <Route exact path="/Auth/SignIn" /> </Switch>しかし、結果は儚くとも同じようなエラー内容。。
Error: Invariant failed: You should not use <Switch> outside a <Router>だめだー、、。
俺の人生終わったー、、。諦めないで!
そうなるのはまだ早い。次で解決策を提示しながら一緒に解説するから、ちょっと落ち着け、モちつけ、、、、
解決策③ (これで解決!)
App.jsimport { BrowserRouter as Router, Route } from "react-router-dom"; import SignIn from "./Auth/SignIn"; <Router> <Header /> <Route exact path="/Auth/SignIn" component={SignIn} /> </Router>components/Header.jsimport { Link } from "react-router-dom"; <Link to="/Auth/SignIn">Signin</Link>今回はこれで解決しました!?
LinkとRouterをつないでいる感じですね
こちら先程の策と比べながら、今回大切にして欲しい
3つのポイント
を解説していきますね。
①react-routerとreact-router-domが似ているようでちょっと違う件についてまず、今回import したのはreact-router-dom です。
先程はreact-routerでしたね。
これは何が違うねん!
まぁまぁ一回落ち着け、モちつけ、、、、、、(お気に入りのボケです。)
簡単な違いはというと、
react-router-domはBrowserRouterをRouterと一緒に使い、
react-routerは historyをRouterと一緒に使うということです。詳しい解決や記述方法はreact-routerとreact-router-domこちらの記事を御覧ください!
まぁあまり大きな違いは無いようです。
② as の効果範囲
今回僕が BrowserRouter をimportする際に用いたasですが、
これの影響範囲は、例えばこの記述だと、どこまでだと思いますか?import { BrowserRouter as Router, Route, Switch } from “react-router-dom”;正解は、、、
Routerだけです。☆彡
以上。
③ componentsについて
<Route exact path="/Auth/SignIn" component={SignIn} />まず、exactの記述に関して
pathに指定したルートと全く同じところに移動する感じです。
exactをつけないと違うルートに行ってしまう可能性があるためつけたほうがいいです。
componentの記述について指定したconponentsを表示します。
今回はSignIn componentsを表示
表示したいcomponentsを import し忘れないように注意。以上で簡単なルーティングの設定方法の解説を終わりにします。
最後に、
この記事を見る限り、v3 から v4への移行期に、色々と新しいのが出たり、
古いのが消えていたりしているそうなので、念の為、検索する際は確認してみたほうが無難かもしれないですね
react-router v3からv4へのマイグレーション
BrowserRouter今回の参考文献【React】ルーティング設定方法
- 投稿日:2020-09-24T17:31:40+09:00
GASでプログラミング入門 Vol.3
GASでプログラミング入門 Vol.3
社内サークルにてエンジニアから非エンジニアの方向けにプログラミングを教えるという活動を行っています。
今回はその教材第3弾です。
前回の記事はこちら前回の演習問題の解答例
(1). for文を使用して下記のように実行されるプログラムを記述してください
ループ処理1回目 ループ処理2回目 ループ処理3回目 ループ処理4回目 ループ処理5回目解答例コード
for(let i = 1; i <= 5; i++){ console.log("ループ処理" + i + "回目"); }ポイントとしては、ループ処理を行う回数の条件式
i <= 5
の部分ですね。
今回は5回処理を繰り返したかったので、変数iの値がいくつになるまで繰り返す必要があるのか?についてコードを書く前にきちんと整理しておく必要があります。(2). 変数strを作成し、"条件式1"という文字列で初期化し、下記のような出力になるプログラムを作成してください。
条件式1の処理を実行また、変数strの値を"条件式2"に変更した場合は下記のようになるようにしてください。
条件式2の処理を実行解答例コード
let str = "条件式1"; if (str == "条件式1") { console.log("条件式1の処理を実行"); } else if(str == "条件式2") { console.log("条件式2の処理を実行"); }ポイントとしては、変数strの値に応じて、二つのルートにプログラムを分岐させる必要があるという点ですね。
上記解答例では何度実行しても条件式1の処理を実行と表示されますが、演習問題の仕様ではstrの値を条件式2に変更した場合についても考慮しておく必要が示されていますので、上記解答例のstrの値を書き換えて実行もして、確認するようにしておきましょう。
実務では条件式に用いられる変数の値はプログラム内にベタ書きされることはなく、外部からの入力などで受け取ることが多いので、プログラムの動作試験では取りうる可能性のある値の検証は全て行っておくなどします。なお解答例はあくまで例なので、必ずしも上記のようになっていないといけないわけではありません。
算術演算子について
JavaScriptに限らずの話ですが、プログラミング言語には演算子というものがあります。
まずは算術演算子というものに関して、紹介します。
算術演算子を使用することで、様々な計算をプログラムで行うことが可能です。加算
console.log(1 + 1);実行結果
2減算
console.log(10 - 1);実行結果
9乗算
console.log(10 * 10);実行結果
100除算
console.log(20 / 10);実行結果
2除算の余り(剰余)
console.log(10 % 3);実行結果
1べき乗(2のn乗など)
console.log(2 ** 3);実行結果
8++について(インクリメント演算子)
前回のfor文の箇所で出てきた
++
について紹介します。
++
はインクリメント演算子と呼ばれるもので、簡単に言えば、算術演算子を短い表記で表現することが可能な演算子です。
まず前回のfor文のi++
ですが、算術演算子を用いると下記の式と同じになります。i = i + 1;変数iに+1した結果を変数iに格納しています。
インクリメント演算子を用いると、上記の式に比べ簡潔に変数の値を+1するということが表現できていますので、このような場合には通常インクリメント演算子を使用することが一般的です。
なお、インクリメント演算子と似た演算子としてデクリメント演算子というものも存在します。
デクリメント演算子はi--
のように記述し、下記の式と同じになります。i = i - 1;また、インクリメント、デクリメント演算子には前置型、後置型の2種類があり、ここで紹介したものは後置型になります。
ひとまず入門編の状態では後置型の表記方法を覚えておけば良いかと思います。
どうしても気になる方は「インクリメント演算子 前置 後置」などのキーワードで検索すると解説しているサイトが沢山見つかりますので、参照してみてください。関係演算子について
続いて関係演算子について紹介します。
関係演算子とは左辺と右辺の値が大きい、小さいなどの関係を評価する演算子です。
前回の講義のfor文の箇所で出てきたi <= 5
の部分になります。小なり演算子
let num = 1; if ( num < 2 ) { console.log("numは2より小さいです"); } else { console.log("numは2より小さくないです"); }実行結果
numは2より小さいですnumの値を2に変更した場合の実行結果
numは2より小さくないです大なり演算子
let num = 3; if ( num > 2 ) { console.log("numは2より大きいです"); } else { console.log("numは2より大きくないです"); }実行結果
numは2より大きいですnumの値を2に変更した場合の実行結果
numは2より大きくないです小なりイコール
let num = 2; if ( num <= 2 ) { console.log("numは2以下です"); } else { console.log("numは2以下ではないです"); }実行結果
numは2以下ですnumの値を3に変更した場合の実行結果
numは2以下ではないです大なりイコール
let num = 2; if ( num >= 2 ) { console.log("numは2以上です"); } else { console.log("numは2以上ではないです"); }実行結果
numは2以上ですnumの値を1に変更した場合の実行結果
numは2以上ではないです等価演算子について
let num = 2; if ( num == 2 ) { console.log("numは2です"); } else { console.log("numは2ではないです"); }実行結果
numは2ですnumの値を1に変更した場合の実行結果
numは2ではないです不等価演算子について
let num = 1; if ( num != 2 ) { console.log("numは2ではないです"); } else { console.log("numは2です"); }実行結果
numは2ではないですnumの値を2に変更した場合の実行結果
numは2です演習問題
(1). for文を使用して下記のように実行されるプログラムを記述してください
ループ処理5回目 ループ処理4回目 ループ処理3回目 ループ処理2回目 ループ処理1回目(2). 数値を格納する変数numを宣言して、値を任意の数値で初期化します。
数値の値が4の倍数なら下記のように表示するプログラムを記述してください。numは4の倍数です。また、4の倍数ではなかった場合は下記のように表示してください。
numは4の倍数ではありません。まとめ
いかがでしたでしょうか。
今回は主に演算子について解説していきました。
前回学んだ内容の「逐次」、「反復」、「分岐」と組みわせることで様々な処理を行うことができるので、演算子の種類、記述方法などについて、しっかりと理解しておくことが重要になります。
それではまた次の記事でお会いしましょう。
- 投稿日:2020-09-24T17:29:02+09:00
BFE.dev解答記録 #23. sum()を実装する
https://bfe.dev/ja はFrontEnd版のLeetCode、GAFAの面接を受けるなら練習した方がいいかなと。
以下は自分の練習記録です。BFE.dev#23. sum()を実装するをみてみよう
問題
以下の条件を満たす前提で
sum()
を実装してください。const sum1 = sum(1) sum1(2) == 3 // true sum1(3) == 4 // true sum(1)(2)(3) == 6 // true sum(5)(-1)(2) == 6分析
上記の用件からわかること
sum()
は引数を受けるfunctionを返すsum()
では渡した引数を全部足して、1を繰り返す- type coercionで、function == numberは可能。
valueOf
かtoString
を実装する必要Let's code
まずfunctionを返す部分を作りましょう。引数の和を一時的に保存するために、2番目の引数にしたらいい。
function sum(num, currentSum = 0) { const newCurrentSum = num + currentSum const func = function(arg) { return sum(arg, num + currentSum) } return func }最後に
valueOf
を入れれば完成function sum(num, currentSum = 0) { const newCurrentSum = num + currentSum const func = function(arg) { return sum(arg, num + currentSum) } func.valueOf = () => newCurrentSum // below also works // func.toString = () => newCurrentSum.toString() return func }通った!
もし興味あれば、 BFE.devでやってみましょう https://bigfrontend.dev/ja/problem/create-a-sum
- 投稿日:2020-09-24T17:22:07+09:00
jest-puppeteerでignoreHTTPSErrors:trueを設定する方法
puppeteerにはSSL証明書のエラーを無視するオプション(ignoreHTTPSErrors: true)があります。jest-puppeteerを介して動作させている場合にオプションを設定する渡す方法を調べました。
jest-puppeteer.config.jsを作成してください。
jest-puppeteer.config.jsmodule.exports = { launch: { ignoreHTTPSErrors: true } }参照
- jest-puppetterのリファレンス https://github.com/smooth-code/jest-puppeteer#configure-puppeteer
- puppeteerのリファレンス https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#puppeteerlaunchoptions
- 投稿日:2020-09-24T16:46:59+09:00
プログラミング初めて3ヶ月
- 投稿日:2020-09-24T15:25:53+09:00
【JS学習その⑥】関数とオブジェクト、そしてコールバック関数
JS学習シリーズの目的
このシリーズは、私ジャックが学んだJavaScriptのメカニズムについてアウトプットも兼ねて、
皆さんと知識や理解を共有するためのものです。
(理解に間違いがあればご指摘いただけると幸いです)関数とオブジェクトの関係
「関数は実行可能なオブジェクトである」
まず、非常に大切なコンセプトとして、JavaScriptにおいて関数というのは、あくまでオブジェクトであり、
ただ実行可能である、というのが他のオブジェクトと異なる点です。
それ以外は、通常のオブジェクトと同じ挙動を取ります。関数がオブジェクトであることの証明
例えば、次のような関数を書きます
main.jsfunction a() { console.log('hello'); } a.prop = 0; a.method = function() { console.log('method'); } a(); a.method(); console.log(a.prop);↓実行をConsoleで見た結果
関数に対して、オブジェクトに使用するようにプロパティの追加とメソッドの追加を行うと、↑の画像のように、追加できています。
これで関数はあくまで実行可能なオブジェクトであることがお分かりかと思います。コールバック関数とは
「他の関数に引数として渡される関数」
main.jsfunction hello() { console.log('hello'); } function fn(cb) { cb(); } fn(hello); /*hello*/↑のコードでは、
1.fn()関数の引数としてhelloというオブジェクト[cb]を渡す
2.fn()関数内でhelloという実行可能なオブジェクト[cb()]を実行するという流れになっています。
おまけ
引数・コールバック関数の補足として
1.JavaScriptの関数というのは、宣言する時に設定する仮引数と呼び出す時の実引数の数は一致している必要はない。
2.コールバック関数は、無名関数で定義することもできる。main.jsfunction hello(name) { console.log('hello ' + name); } function bye() { console.log('bye'); } function fn(cb) { cb('Tom'); } fn(hello); /*hello Tom*/ fn(bye); /*bye*/ fn(function(name) { console.log('hello ' + name); }) /*hello Tom*/したがって、↑のコードのように書くこともできます。
まとめ
いかがでしたでしょうか。
関数とオブジェクトの関係、そしてコールバック関数はJavaScriptにおいてとても重要なのでしっかり理解しておきましょう!
- 投稿日:2020-09-24T14:53:46+09:00
Vue.js~Vuex~Jestでのテスト
はじめに
Vue.jsでVuexを使った時のテストの仕方が全然わからなくて
ちょっと前進したので忘れないうちにメモ
間違いがあったら教えていただけると嬉しいです。テスト対象
<template> <div class="hello"> <p>{{nowNumber}}</p> </div> </template> <script> import {mapState} from 'vuex' export default { name: 'HelloWorld', computed: { ...mapState(['nowNumber']) }, } </script>
state
の値を表示するだけのコンポーネントカウント用
インクリメント(足す)
<template> <div class="increment"> <button class="increment__button" @click="incrementButton">足す</button> </div> </template> <script> export default { methods: { incrementButton() { this.$store.dispatch("increment"); }, }, }; </script>ボタンを押したら
actions
経由でmutations
からstate
に加算デクリメント(引く)
<template> <div class="decrement"> <button @click="decrement">引く</button> </div> </template> <script> export default { methods: { decrement() { this.$store.dispatch('decrement') }, }, }; </script>リセット
<template> <div class="reset"> <button @click="reset">リセット</button> </div> </template> <script> export default { methods: { reset() { this.$store.dispatch("reset"); }, }, }; </script>基本的にこの3つはほぼ同じでメソッド名とかが違うくらい
Vuex(Store)
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export const state = { nowNumber: 0 } export const mutations = { increment(state) { state.nowNumber++ }, decrement(state) { if (state.nowNumber <= 0) return state.nowNumber-- }, reset(state) { state.nowNumber = 0 } } export const actions = { increment({ commit }) { commit('increment') }, decrement({ commit }) { commit('decrement') }, reset({ commit }) { commit('reset') } } export default new Vuex.Store({ state, mutations, actions })ストアの部分はこんな感じです
そしてこれをテストする
テストコード
import { createLocalVue } from '@vue/test-utils' import Vuex from 'vuex' import { cloneDeep } from 'lodash' import { actions, mutations } from '../../store/index.js' const state = { nowNumber: 0 } const initStore = () => { return cloneDeep({ state, mutations, actions }) } let store let localVue beforeEach(() => { localVue = createLocalVue() localVue.use(Vuex) store = new Vuex.Store(initStore()) }) describe('store', () => { it('dispatch incremetnt', () => { expect(store.state.nowNumber).toBe(0) store.dispatch('increment') expect(store.state.nowNumber).toBe(1) }) it('dispatch decrement', () => { store.state.nowNumber = 1 expect(store.state.nowNumber).toBe(1) store.dispatch('decrement') expect(store.state.nowNumber).toBe(0) store.dispatch('decrement') expect(store.state.nowNumber).toBe(0) }) it('dispatch reset', () => { store.state.nowNumber = 5 expect(store.state.nowNumber).toBe(5) store.dispatch('reset') expect(store.state.nowNumber).toBe(0) }) it('dispatch まとめテスト', () => { store.dispatch('increment') expect(store.state.nowNumber).toBe(1) store.dispatch('increment') expect(store.state.nowNumber).toBe(2) store.dispatch('decrement') expect(store.state.nowNumber).toBe(1) store.dispatch('reset') expect(store.state.nowNumber).toBe(0) }) })この場合だとストアを
クローン
してそれを実行してテストしている感じになります。
なのでAPIとか使った物だとこれだと毎回通信してしまうと思うのでダメですね
ここは次の課題です
ひとまずこれでテストしたら無事に全部PASSしました。
- 投稿日:2020-09-24T14:00:59+09:00
[JavaScript] 高階関数で break する方法の考察
概要
高階関数の
forEach
はfor
文と異なり、処理途中で break できません。そこで、代替コードで break できる様にする方法を考えてみました。書く動機となった元記事
なぜ我々は頑なにforを避けるのか実装
- 高階関数
find
findによるfor-breakの代替コード;[1, 3, 5, 7].find( it => { console.log(it) return it === 5 // Same as `if (it === 5) break` } ) // 1 // 3 // 5
- 高階関数
reduce
reduceによるfor-breakの代替コード;[1, 3, 5, 7].reduce( (bye, it) => { if (bye) return bye console.log(it) return it === 5 // Same as `if (it === 5) break` }, false ) // 1 // 3 // 5まとめ
- 本来の
find
やreduce
の使い方ではないため、可読性にかなり難があります。これらは本来、代入文の右辺として使われます。右辺に書かれてないなら「本来の使い方ではない」ので、
for-break
の代替で使われてるのかと気づけそうです。「他にもっと好い方法があります!」と云う方は、コメント欄で教えて下さい!
- 投稿日:2020-09-24T13:11:28+09:00
インクリメンタル検索(Incremental Search)についてまとめてみる
インクリメンタル検索とは?
以下の動画のように、検索文字列を入力後に検索候補を表示する検索のことをインクリメンタル検索といいます。
インクリメンタル検索のポイント
インクリメンタル検索は、即座に検索結果を表示せずに遅延して(=入力が確定したあとに)表示することがポイントです。
理由は、以下の動画を見て頂くとわかりやすいのですが、即座に検索結果に表示すると
1、開発者にとって、余計なサーバーへのリクエスト数が多くなる。
2、ユーザーにとって、入力の度に検索結果が変わって不快に思うことがある。
といったデメリットがあるからです。2に関しては、例えば
uniqlo
という単語を検索したいのにも関わらず、
u
を押したら、検索結果表示
n
を押したら、検索結果表示
i
を押したら、検索結果表示
・・・・
と文字ごとに検索結果が表示されて、ユーザーが欲しくない情報が表示されてしまいます。なので、遅延して検索結果を表示することによって、
1、開発者にとって、余計なサーバーへのリクエスト数が減る。
2、ユーザーにとって、確定した単語(もしくは文字列)を表示することができる。
といったメリットが生まれます。
JQueryによるインクリメンタル検索実装例
一定時間入力がない(以下の例だと
500ms
)場合は検索が確定しているので、そのタイミングで検索結果を表示します。addIncrementalSearchEvent() { const self = this; $('#js-incrementalSearch').on('input', function() { let enteringSearchWord = $('#js-incrementalSearch').val(); let requestSearchWord = function() { setTimeout(async function() { let searchWord = $('#js-incrementalSearch').val(); if (enteringSearchWord === searchWord) { // サーバーサイドにリクエストを投げる。 if (!error) { // エラー処理 } } }, 500); }; requestSearchWord(); }); }
- 投稿日:2020-09-24T13:00:25+09:00
LaravelDB.com~操作方法と勘所を書いた〜(2020/09/24メジャーアップデートに対応)
◇LaravelDB.com ?
Laravelのデータベース設計(ER図)するだけで、Migrationファイルがポンって作成できるFreeの「 テーブル設計&Migration作成 」 ツールです。
メイン機能
- Migrationの自動生成
- チームメンバーとの共有可能
- CRUDコード(β版)の自動生成
- CRUDコード(β版)でのValidation自動生成(テーブル設計に合わせて自動で生成)
用途
用途は開発者によってバラバラですね。
・Migrationのみ使う(ほとんどの人はここですかね)
・CRUDファイル全部(数%くらいの人)
・コード生成後、一部(Validationとか)をコピーして使う(結構な割合でいます)
・テーブル設計の共有(Team開発では使ってるようです)◇操作方法(マニュアル)
1.テーブル作成
2.カラム作成
※使いやすくするポイント!! 「列を追加」ボタンを連打で必要な数だけ先に作ると便利!!
3.2020-09-24アップデートから使用可能
「 ENUM, UNSIGNED ] に対応、新規プロジェクトより表示されるようになります
「 UNIQUE, INDEX ] に対応、新規プロジェクトより表示されるようになります
外部キーの設定
外部キー制約を使用する場合( ※1対他、多対多などを分ける機能は無い)
4.ER図の保存
コントロールパネル[Data:セーブ/ロード]から移動します。
Save/Load機能 機能詳細 クラウドへ保存 アカウントに紐づきクラウドに保存します。 クラウドデータ一覧 保存したクラウド側のデータ一覧 ブラウザに保存 使用中のブラウザ「LocalStorage」に一時保存 ブラウザデータ一覧 localStorageに保存したデータ一覧 上記の「データ一覧」選択後、「 -- Data List -- 」の選択肢が表示されます。
そこに表示されるデータ項目を選択するとER図を復元します。5.Migrationを生成
[ER図から出力]内のBuild [Migration]ボタンをクリックしてダウンロードします!
【ER図をTeamでシェア】
1 [送信側:シェアData]作成
見せたい相手にIDを渡しておいて、変更があれば「Create a [Share ID]」ボタンを押すと毎度データ更新されることを知っておきましょう!2 [送信側:シェアData] IDをコピー
3 [受信側:シェアData]読み込み
4 「Read」ボタンをクリックしてデータを受信表示しましょう。
3.シェアデータの複製が完了!!
受信側にデータが入りました!受信側もそのデータを活用できるようになります。
チーム・メンタリング等のケースでも利用可能です。
※受信後は「別名を付けて保存しておくと良いでしょう!」◇ β版のCRUD/リレーションを使用したい場合(まだまだ開発段階の未知の機能)
<重要>この機能はテーブル名の末尾に「s」が無いとうまく動作しません。
※ENUMは必須・未入力のみ出力。
※intは型・必須・未入力のみ出力、sizeが未対応。
※2020-09-24Migrtion機能にアップデートが入ったので、β版機能に影響があるかもしれません。
まずは、複数のテーブルを簡単に作り「外部キーの接続」をして準備してください!!
その後、右メニュー「 [Data]セーブ/ロード 」クリックすると以下画面が表示されます。
BUILD [CRUD CODE] をクリック!!CRUD ファイル一式が生成されます
【ポイント】
Relation(Beta)にチェックを入れておくとリレーションします。
BUILD [MIGRATION] でもリレーション(QueryBuilder)がコメントで生成されますよ。
【このツールでのリレーションのポイント】
- JOINしてるテーブルの全ての項目を最初は表示します(同項目名が存在する場合、片方のみ表示します)。
- CRUDのコードが生成されたら、仕様に合わせてHTMLテーブルの項目を削除してください。
- リレーションは上記画像のように「チェック」を入れないと生成しないようにしています(Beta版のため)。
- Controller内にコードが生成されてますので、確認しておくと良いでしょう!
- 外部キーはこのツールでは非推奨としています(理由:Migrationの実行順序が重要でエラーになりやすい為) プロトタイプ(ベースになるコードを生成してくれる)には十分すぎる機能ですね。
◇Validation → テーブル設計に合わせて生成
Validationって地味に面倒ですよね、完璧では内容ですが、少しの手直しで使用できるなら便利そうです!!
例でざっくりテーブルをER図で書いてみました。
以下"t_gsusers"テーブルを中心に見ていきます。
生成されたコントローラーのcreate(),edit()にはこういったvalidationが挿入されます。
TGsusersController$this->validate($request, [ "name" => "required|max:128", //string('name',128) "lid" => "required|max:128", //string('lid',128) "lpw" => "required|max:128", //string('lpw',128) "m_department_id" => "required|integer", //integer('m_department_id') "m_position_id" => "required|integer", //integer('m_position_id') "m_prefectures_id" => "required|integer", //integer('m_prefectures_id') "m_kanri_id" => "required|integer", //integer('m_kanri_id') "m_lifeflg_id" => "required|integer", //integer('m_lifeflg_id') "m_test_id" => "required|integer", //integer('m_test_id') ]);{{old('name')}} → 入力項目を補完(CRUD:β版を使用した場合に生成されます)
validationで未入力等ではじかれた場合、入力した文字を消さずに表示します。
ベータ版のCRUD/リレーションの利用シーン
上記のようにβ版ではありますが、全てを利用するというよりは、コードを生成して、必要な部分だけ使うのも良いと思います。(実際にそういったケースを耳にしました〜)
◇LaravelDB.com 対応カラム一覧
tinyIncrements
mediumIncrements
smallIncrements
bigIncrements
increments
mediumInteger
smallInteger
bigInteger
tinyInteger
integer
unsignedInteger (2020-09-24対応)
unsignedTinyInteger (2020-09-24対応)
unsignedSmallInteger (2020-09-24対応)
unsignedMediumInteger (2020-09-24対応)
unsignedBigInteger (2020-09-24対応)
decimal
double
float
enum (2020-09-24対応)
geometryCollection
geometry
jsonb
json
char
longText
mediumText
text
multiLineString
lineString
string
multiPoint
multiPolygon
point
polygon
binary
nullableTimestamps
timestamps (2020-09-24対応)
softDeletes (2020-09-24対応)
dateTime
timestamp
time
year
date◇そもそも、なんで作ったの?
私は学校でプログラミング(サービスを作る基本)を教えていて、テーブル設計している学生がその場にいて
「テーブル設計して、そこからMigrationファイル作成して、コードをイチから書いて・・・」普通の作業ではあるんですが、何故か「その時は疑問を感じました」、ER図書いたらMigrationファイル生成したら便利だよね~。
何割かの開発者は喜んでくれるのでは?と思ったのがキッカケでした。
特に「テーブル設計は保存可能」なので、前回作ったテーブル設計から新しい設計を複製できるのは嬉しい機能です。設計してMigration or CRUDコード書き出してができるので、「設計して→コード書いて」という往復作業が無くなるので、是非使ってほしいと思います。今後の展開
2020-09-24以降からは「Migration」を軸にアップデートしていきます
なぜ、↑そう思ったか?
良かれと思った機能が意外に余計だった。。。よくあるパターンですね。
テーブル名に"t_","m_"とかトランザクション・マスターテーブルなどがテーブル名で分かるようにした場合に、自動で「timestamps」「softDeletes」などを挿入する機能が逆に「解りにくくさせていた」という事があります。今回のアップデートで廃止いしたのでご安心ください(余計なことはしません(^^))。
やはりシンプルがベストなんですよね。今はいかに「シンプルにするか」だけ考えてアップデートを考えています。
※必要な機能はどうやって複雑にせずに追加するか?など、悩みが楽しくてしょうがありません。
※CRUD(β版)機能のアップデートはどうなるか未定(Laravel7までは動作確認ずみ)
※Laravel8移行のLTSの仕様で検討予定ですかね~~~かなり変わってるように思ったので。◇LaravelDB.com サイト
◇Twitterアカウント
以上
- 投稿日:2020-09-24T13:00:25+09:00
LaravelDB.com~操作方法と勘所を書いた〜2020/09/24メジャーアップデート版
◇LaravelDB.com ?
Laravelのデータベース設計(ER図)するだけで、Migrationファイルがポンって作成できるFreeの「 テーブル設計&Migration作成 」 ツールです。
メイン機能
- Migrationの自動生成
- チームメンバーとの共有可能
- CRUDコード(β版)の自動生成
- CRUDコード(β版)でのValidation自動生成(テーブル設計に合わせて自動で生成)
用途
用途は開発者によってバラバラですね。
・Migrationのみ使う(ほとんどの人はここですかね)
・CRUDファイル全部(数%くらいの人)
・コード生成後、一部(Validationとか)をコピーして使う(結構な割合でいます)
・テーブル設計の共有(Team開発では使ってるようです)◇操作方法(マニュアル)
1.テーブル作成
2.カラム作成
※使いやすくするポイント!! 「列を追加」ボタンを連打で必要な数だけ先に作ると便利!!
3.2020-09-24アップデートから使用可能
「 ENUM, UNSIGNED ] に対応、新規プロジェクトより表示されるようになります
「 UNIQUE, INDEX ] に対応、新規プロジェクトより表示されるようになります
外部キーの設定
外部キー制約を使用する場合( ※1対他、多対多などを分ける機能は無い)
4.ER図の保存
コントロールパネル[Data:セーブ/ロード]から移動します。
Save/Load機能 機能詳細 クラウドへ保存 アカウントに紐づきクラウドに保存します。 クラウドデータ一覧 保存したクラウド側のデータ一覧 ブラウザに保存 使用中のブラウザ「LocalStorage」に一時保存 ブラウザデータ一覧 localStorageに保存したデータ一覧 上記の「データ一覧」選択後、「 -- Data List -- 」の選択肢が表示されます。
そこに表示されるデータ項目を選択するとER図を復元します。5.Migrationを生成
[ER図から出力]内のBuild [Migration]ボタンをクリックしてダウンロードします!
【ER図をTeamでシェア】
1 [送信側:シェアData]作成
見せたい相手にIDを渡しておいて、変更があれば「Create a [Share ID]」ボタンを押すと毎度データ更新されることを知っておきましょう!2 [送信側:シェアData] IDをコピー
3 [受信側:シェアData]読み込み
4 「Read」ボタンをクリックしてデータを受信表示しましょう。
3.シェアデータの複製が完了!!
受信側にデータが入りました!受信側もそのデータを活用できるようになります。
チーム・メンタリング等のケースでも利用可能です。
※受信後は「別名を付けて保存しておくと良いでしょう!」◇ β版のCRUD/リレーションを使用したい場合(まだまだ開発段階の未知の機能)
<重要>この機能はテーブル名の末尾に「s」が無いとうまく動作しません。
※ENUMは必須・未入力のみ出力。
※intは型・必須・未入力のみ出力、sizeが未対応。
※2020-09-24Migrtion機能にアップデートが入ったので、β版機能に影響があるかもしれません。
まずは、複数のテーブルを簡単に作り「外部キーの接続」をして準備してください!!
その後、右メニュー「 [Data]セーブ/ロード 」クリックすると以下画面が表示されます。
BUILD [CRUD CODE] をクリック!!CRUD ファイル一式が生成されます
【ポイント】
Relation(Beta)にチェックを入れておくとリレーションします。
BUILD [MIGRATION] でもリレーション(QueryBuilder)がコメントで生成されますよ。
【このツールでのリレーションのポイント】
- JOINしてるテーブルの全ての項目を最初は表示します(同項目名が存在する場合、片方のみ表示します)。
- CRUDのコードが生成されたら、仕様に合わせてHTMLテーブルの項目を削除してください。
- リレーションは上記画像のように「チェック」を入れないと生成しないようにしています(Beta版のため)。
- Controller内にコードが生成されてますので、確認しておくと良いでしょう!
- 外部キーはこのツールでは非推奨としています(理由:Migrationの実行順序が重要でエラーになりやすい為) プロトタイプ(ベースになるコードを生成してくれる)には十分すぎる機能ですね。
◇Validation → テーブル設計に合わせて生成
Validationって地味に面倒ですよね、完璧では内容ですが、少しの手直しで使用できるなら便利そうです!!
例でざっくりテーブルをER図で書いてみました。
以下"t_gsusers"テーブルを中心に見ていきます。
生成されたコントローラーのcreate(),edit()にはこういったvalidationが挿入されます。
TGsusersController$this->validate($request, [ "name" => "required|max:128", //string('name',128) "lid" => "required|max:128", //string('lid',128) "lpw" => "required|max:128", //string('lpw',128) "m_department_id" => "required|integer", //integer('m_department_id') "m_position_id" => "required|integer", //integer('m_position_id') "m_prefectures_id" => "required|integer", //integer('m_prefectures_id') "m_kanri_id" => "required|integer", //integer('m_kanri_id') "m_lifeflg_id" => "required|integer", //integer('m_lifeflg_id') "m_test_id" => "required|integer", //integer('m_test_id') ]);{{old('name')}} → 入力項目を補完(CRUD:β版を使用した場合に生成されます)
validationで未入力等ではじかれた場合、入力した文字を消さずに表示します。
ベータ版のCRUD/リレーションの利用シーン
上記のようにβ版ではありますが、全てを利用するというよりは、コードを生成して、必要な部分だけ使うのも良いと思います。(実際にそういったケースを耳にしました〜)
◇LaravelDB.com 対応カラム一覧
tinyIncrements
mediumIncrements
smallIncrements
bigIncrements
increments
mediumInteger
smallInteger
bigInteger
tinyInteger
integer
unsignedInteger (2020-09-24対応)
unsignedTinyInteger (2020-09-24対応)
unsignedSmallInteger (2020-09-24対応)
unsignedMediumInteger (2020-09-24対応)
unsignedBigInteger (2020-09-24対応)
decimal
double
float
enum (2020-09-24対応)
geometryCollection
geometry
jsonb
json
char
longText
mediumText
text
multiLineString
lineString
string
multiPoint
multiPolygon
point
polygon
binary
nullableTimestamps
timestamps (2020-09-24対応)
softDeletes (2020-09-24対応)
dateTime
timestamp
time
year
date◇そもそも、なんで作ったの?
私は学校でプログラミング(サービスを作る基本)を教えていて、テーブル設計している学生がその場にいて
「テーブル設計して、そこからMigrationファイル作成して、コードをイチから書いて・・・」普通の作業ではあるんですが、何故か「その時は疑問を感じました」、ER図書いたらMigrationファイル生成したら便利だよね~。
何割かの開発者は喜んでくれるのでは?と思ったのがキッカケでした。
特に「テーブル設計は保存可能」なので、前回作ったテーブル設計から新しい設計を複製できるのは嬉しい機能です。設計してMigration or CRUDコード書き出してができるので、「設計して→コード書いて」という往復作業が無くなるので、是非使ってほしいと思います。今後の展開
2020-09-24以降からは「Migration」を軸にアップデートしていきます
なぜ、↑そう思ったか?
良かれと思った機能が意外に余計だった。。。よくあるパターンですね。
テーブル名に"t_","m_"とかトランザクション・マスターテーブルなどがテーブル名で分かるようにした場合に、自動で「timestamps」「softDeletes」などを挿入する機能が逆に「解りにくくさせていた」という事があります。今回のアップデートで廃止いしたのでご安心ください(余計なことはしません(^^))。
やはりシンプルがベストなんですよね。今はいかに「シンプルにするか」だけ考えてアップデートを考えています。
※必要な機能はどうやって複雑にせずに追加するか?など、悩みが楽しくてしょうがありません。
※CRUD(β版)機能のアップデートはどうなるか未定(Laravel7までは動作確認ずみ)
※Laravel8移行のLTSの仕様で検討予定ですかね~~~かなり変わってるように思ったので。◇LaravelDB.com サイト
◇Twitterアカウント
以上
- 投稿日:2020-09-24T13:00:25+09:00
LaravelDB.com~操作方法と勘所を書いた〜(基本操作編)
◇LaravelDB.com ?
Laravelのデータベース設計(ER図)するだけで、Migrationファイルがポンって作成できるFreeの「 テーブル設計&Migration作成 」 ツールです。
メイン機能
- Migrationの自動生成
- チームメンバーとの共有可能
- CRUDコード(β版)の自動生成
- CRUDコード(β版)でのValidation自動生成(テーブル設計に合わせて自動で生成)
用途
用途は開発者によってバラバラですね。
・Migrationのみ使う(ほとんどの人はここですかね)
・CRUDファイル全部(数%くらいの人)
・コード生成後、一部(Validationとか)をコピーして使う(結構な割合でいます)
・テーブル設計の共有(Team開発では使ってるようです)◇操作方法(マニュアル)
1.テーブル作成
2.カラム作成
※使いやすくするポイント!! 「列を追加」ボタンを連打で必要な数だけ先に作ると便利!!
3.2020-09-24アップデートから使用可能
「 ENUM, UNSIGNED ] に対応、新規プロジェクトより表示されるようになります
「 UNIQUE, INDEX ] に対応、新規プロジェクトより表示されるようになります
外部キーの設定
外部キー制約を使用する場合( ※1対他、多対多などを分ける機能は無い)
4.ER図の保存
コントロールパネル[Data:セーブ/ロード]から移動します。
Save/Load機能 機能詳細 クラウドへ保存 アカウントに紐づきクラウドに保存します。 クラウドデータ一覧 保存したクラウド側のデータ一覧 ブラウザに保存 使用中のブラウザ「LocalStorage」に一時保存 ブラウザデータ一覧 localStorageに保存したデータ一覧 上記の「データ一覧」選択後、「 -- Data List -- 」の選択肢が表示されます。
そこに表示されるデータ項目を選択するとER図を復元します。5.Migrationを生成
[ER図から出力]内のBuild [Migration]ボタンをクリックしてダウンロードします!
【ER図をTeamでシェア】
1 [送信側:シェアData]作成
見せたい相手にIDを渡しておいて、変更があれば「Create a [Share ID]」ボタンを押すと毎度データ更新されることを知っておきましょう!2 [送信側:シェアData] IDをコピー
3 [受信側:シェアData]読み込み
4 「Read」ボタンをクリックしてデータを受信表示しましょう。
3.シェアデータの複製が完了!!
受信側にデータが入りました!受信側もそのデータを活用できるようになります。
チーム・メンタリング等のケースでも利用可能です。
※受信後は「別名を付けて保存しておくと良いでしょう!」◇ β版のCRUD/リレーションを使用したい場合(まだまだ開発段階の未知の機能)
<重要>この機能はテーブル名の末尾に「s」が無いとうまく動作しません。
※ENUMは必須・未入力のみ出力。
※intは型・必須・未入力のみ出力、sizeが未対応。
※2020-09-24Migrtion機能にアップデートが入ったので、β版機能に影響があるかもしれません。
まずは、複数のテーブルを簡単に作り「外部キーの接続」をして準備してください!!
その後、右メニュー「 [Data]セーブ/ロード 」クリックすると以下画面が表示されます。
BUILD [CRUD CODE] をクリック!!CRUD ファイル一式が生成されます
【ポイント】
Relation(Beta)にチェックを入れておくとリレーションします。
BUILD [MIGRATION] でもリレーション(QueryBuilder)がコメントで生成されますよ。
【このツールでのリレーションのポイント】
- JOINしてるテーブルの全ての項目を最初は表示します(同項目名が存在する場合、片方のみ表示します)。
- CRUDのコードが生成されたら、仕様に合わせてHTMLテーブルの項目を削除してください。
- リレーションは上記画像のように「チェック」を入れないと生成しないようにしています(Beta版のため)。
- Controller内にコードが生成されてますので、確認しておくと良いでしょう!
- 外部キーはこのツールでは非推奨としています(理由:Migrationの実行順序が重要でエラーになりやすい為) プロトタイプ(ベースになるコードを生成してくれる)には十分すぎる機能ですね。
◇Validation → テーブル設計に合わせて生成
Validationって地味に面倒ですよね、完璧では内容ですが、少しの手直しで使用できるなら便利そうです!!
例でざっくりテーブルをER図で書いてみました。
以下"t_gsusers"テーブルを中心に見ていきます。
生成されたコントローラーのcreate(),edit()にはこういったvalidationが挿入されます。
TGsusersController$this->validate($request, [ "name" => "required|max:128", //string('name',128) "lid" => "required|max:128", //string('lid',128) "lpw" => "required|max:128", //string('lpw',128) "m_department_id" => "required|integer", //integer('m_department_id') "m_position_id" => "required|integer", //integer('m_position_id') "m_prefectures_id" => "required|integer", //integer('m_prefectures_id') "m_kanri_id" => "required|integer", //integer('m_kanri_id') "m_lifeflg_id" => "required|integer", //integer('m_lifeflg_id') "m_test_id" => "required|integer", //integer('m_test_id') ]);{{old('name')}} → 入力項目を補完(CRUD:β版を使用した場合に生成されます)
validationで未入力等ではじかれた場合、入力した文字を消さずに表示します。
ベータ版のCRUD/リレーションの利用シーン
上記のようにβ版ではありますが、全てを利用するというよりは、コードを生成して、必要な部分だけ使うのも良いと思います。(実際にそういったケースを耳にしました〜)
◇LaravelDB.com 対応カラム一覧
tinyIncrements
mediumIncrements
smallIncrements
bigIncrements
increments
mediumInteger
smallInteger
bigInteger
tinyInteger
integer
unsignedInteger (2020-09-24対応)
unsignedTinyInteger (2020-09-24対応)
unsignedSmallInteger (2020-09-24対応)
unsignedMediumInteger (2020-09-24対応)
unsignedBigInteger (2020-09-24対応)
decimal
double
float
enum (2020-09-24対応)
geometryCollection
geometry
jsonb
json
char
longText
mediumText
text
multiLineString
lineString
string
multiPoint
multiPolygon
point
polygon
binary
nullableTimestamps
timestamps (2020-09-24対応)
softDeletes (2020-09-24対応)
dateTime
timestamp
time
year
date◇そもそも、なんで作ったの?
私は学校でプログラミング(サービスを作る基本)を教えていて、テーブル設計している学生がその場にいて
「テーブル設計して、そこからMigrationファイル作成して、コードをイチから書いて・・・」普通の作業ではあるんですが、何故か「その時は疑問を感じました」、ER図書いたらMigrationファイル生成したら便利だよね~。
何割かの開発者は喜んでくれるのでは?と思ったのがキッカケでした。
特に「テーブル設計は保存可能」なので、前回作ったテーブル設計から新しい設計を複製できるのは嬉しい機能です。設計してMigration or CRUDコード書き出してができるので、「設計して→コード書いて」という往復作業が無くなるので、是非使ってほしいと思います。今後の展開
2020-09-24以降からは「Migration」を軸にアップデートしていきます
なぜ、↑そう思ったか?
良かれと思った機能が意外に余計だった。。。よくあるパターンですね。
テーブル名に"t_","m_"とかトランザクション・マスターテーブルなどがテーブル名で分かるようにした場合に、自動で「timestamps」「softDeletes」などを挿入する機能が逆に「解りにくくさせていた」という事があります。今回のアップデートで廃止いしたのでご安心ください(余計なことはしません(^^))。
やはりシンプルがベストなんですよね。今はいかに「シンプルにするか」だけ考えてアップデートを考えています。
※必要な機能はどうやって複雑にせずに追加するか?など、悩みが楽しくてしょうがありません。
※CRUD(β版)機能のアップデートはどうなるか未定(Laravel7までは動作確認ずみ)
※Laravel8移行のLTSの仕様で検討予定ですかね~~~かなり変わってるように思ったので。◇LaravelDB.com サイト
◇Twitterアカウント
以上
- 投稿日:2020-09-24T11:54:56+09:00
TypeScript: メソッドを「メソッド定義」で生やした場合と「フィールド宣言」で生やした場合の違い
この投稿では、JavaScriptで
class
構文を用いる際に、メソッドを「メソッド定義」でプロトタイプメソッドとして生やした場合と、「フィールド宣言」を用いてフィールドとして生やした場合の違いについて説明します。メソッド定義とフィールド宣言
JavaScriptでは、ECMAScript 2015からクラス宣言の構文が導入されました。
class Foo { }これと同時に、プロトタイプメソッドを定義するための「メソッド定義」の構文も追加されました。
class Foo { method() { } }また、「フィールド宣言」という新しい構文が提案されており、今後ECMAScriptに導入されることが有力視されています。
class Foo { a = 0 // フィールド宣言 } // 上は下と同じ意味 class Foo { constructor() { this.a = 0 } }TypeScriptでは、この新しいフィールド宣言にも対応しており、コンパイルするとコンストラクタによるフィールド初期化に変換してくれます:
class Foo { a: number = 0 // フィールド宣言 } // target=ES2020でのコンパイル結果↓ class Foo { constructor() { this.a = 0; // フィールド宣言 } }ちなみに、TypeScriptでは上のような変形をしますが、Node.js v12.4以降や最近のブラウザでは、フィールド宣言の文法をそのまま解釈できるようになっています。
本稿で比較するもの
インスタンスにメソッドを持たせる際、TypeScriptでもJavaScriptでもメソッド定義とフィールド宣言の2つの書き方がありえます。
メソッドを持たせるには、メソッド定義を使うのが普通です:
class A { name = 'Aクラス' x(): void { console.log(this.name + 'のxメソッドが呼ばれました。') } } const a = new A() a.x() //=> Aクラスのxメソッドが呼ばれました。一方、あまり一般的ではないですが、フィールド宣言を用いてメソッドを生やすこともできるにはできます:
class A { name = 'Aクラス' x = (): void => { console.log(this.name + 'のxメソッドが呼ばれました。') } } const a = new A() a.x() //=> Aクラスのxメソッドが呼ばれました。どちらの方法でメソッド定義しても、
a.x()
のように呼び出せます。また、this
で自分のインスタンスにアクセスできる点も同じです。では、メソッド定義とフィールド宣言の違いは全くないのでしょうか? この2つ事例別に比較していきたいと思います。
相違1:
super
まず、大きな違いとして、理由は後述しますが、メソッド定義とフィールド宣言では、
super
で継承元のメソッドを呼べる呼べないの違いがあります。メソッド定義では、メソッドをオーバライドした場合に、
super
で親のメソッドを呼び出すことができます:class A { x(): void { console.log('Aクラスのxメソッドが呼ばれました。') } } class B extends A { x(): void { console.log('Bクラスのxメソッドが呼ばれました。') super.x() } } const b = new B() b.x() //=> Bクラスのxメソッドが呼ばれました。 //=> Aクラスのxメソッドが呼ばれました。一方のフィールド宣言では、
super
を用いるとコンパイルは通るものの、実行時エラーが発生するケースがあります:class A { x = (): void => { console.log('Aクラスのxメソッドが呼ばれました。') } } class B extends A { x = (): void => { console.log('Bクラスのxメソッドが呼ばれました。') super.x() } } const b = new B() b.x() //=> TypeError: (intermediate value).x is not a function
(オーバーライドではなく、親クラスのメソッド定義によるメソッドは、子クラスのフィールド宣言メソッドからでも
super
で呼び出すことはできます)class A { x = () => { console.log('Aクラスのxメソッドが実行されました') } y() { console.log('Aクラスのyメソッドが実行されました') } } class B extends A { x = () => { super.y() } y() { throw new Error('これは呼ばれるべきでない') } } const b = new B() b.x() //=> "Aクラスのyメソッドが実行されました"こうした違いがあるのは、メソッド定義で生やしたメソッドが、プロトタイプに入るのに対し、フィールド宣言で生やしたメソッドはインスタンスだけのプロパティになるためです。
class C { method1() {} // メソッド定義 method2 = () => {} // フィールド宣言 } console.log(typeof C.prototype.method1) //=> "function" console.log(typeof C.prototype.method2) //=> "undefined" ← フィールド宣言はプロトタイプにない相違2: 自分が所有するプロパティ・列挙可能プロパティ
2つ目の違いとして、メソッドが自分が所有するプロパティ、かつ、列挙可能プロパティになるかどうかという違いがあります。
まず、メソッド定義の場合は、自分の所有するプロパティにも、列挙可能プロパティにもなりません:
class A { x(): void { } } const a = new A() const 自分のプロパティか = a.hasOwnProperty('x') console.log(自分のプロパティか) //=> false const 列挙可能プロパティか = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(a), 'x')?.enumerable console.log(列挙可能プロパティか) //=> false一方のフィールド宣言によるメソッドは、自分の所有するプロパティになり、かつ、列挙可能プロパティにもなります:
class A { x = () => void { } } const a = new A() const 自分のプロパティか = a.hasOwnProperty('x') console.log(自分のプロパティか) //=> true const 列挙可能プロパティか = Object.getOwnPropertyDescriptor(a, 'x')?.enumerable console.log(列挙可能プロパティか) //=> trueこの違いは、
Object.keys
やObject.values
、Object.entries
などによる列挙可能プロパティを取り出す操作や、スプレッド演算子による操作に実行結果の違いが現れます。class C { method1() {} // メソッド定義 method2 = () => {} // フィールド宣言 } const c = new C() // 列挙可能プロパティを取り出す操作 const keys = Object.keys(c) console.log(keys) //=> ["method2"] ← フィールド宣言によるメソッドの名前 // スプレッド演算子による操作 const c2 = { ...c } console.log(typeof c2.method1) //=> "undefined" console.log(typeof c2.method2) //=> "function" ← フィールド宣言によるメソッド相違3: メモリ使用量
相違1で、メソッド定義は関数がプロトタイプに入るのに対し、フィールド宣言はそうでないということを説明しましたが、これはメモリ使用量にも差が生まれる原因になります。
メモリ使用量を比べるために、メソッド定義版のクラスと、フィールド宣言版のクラスの2つのJavaScriptファイルを用意して、それぞれ100万インスタンス生成して、比較してみました。使用した実行環境はNode.js 14.4.0です。
まず、メソッド定義版のファイルです:
test1.jsclass A { x() {} } const list = [] for (let i = 0; i < 1_000_000; i++) { list.push(new A()) } const used = process.memoryUsage(); for (let key in used) { console.log(`${key} ${Math.round(used[key] / 1024 / 1024 * 100) / 100} MB`); }実行結果は以下のようになりました:
$ node test1.js Using v14.4.0 rss 76.83 MB heapTotal 64.46 MB heapUsed 41.14 MB external 0.73 MB arrayBuffers 0.01 MB次に、フィールド宣言版のファイルです:
test2.jsclass A { x = () => {} } const list = [] for (let i = 0; i < 1_000_000; i++) { list.push(new A()) } const used = process.memoryUsage(); for (let key in used) { console.log(`${key} ${Math.round(used[key] / 1024 / 1024 * 100) / 100} MB`); }実行結果は次のようになりました:
$ node test2.js rss 161.05 MB heapTotal 136.34 MB heapUsed 109.49 MB external 0.73 MB arrayBuffers 0.01 MBメモリ使用量をまとめると次の表のとおりになりました:
メソッド定義版 フィールド宣言版 rss 76.83 MB 161.05 MB heapTotal 64.46 MB 136.34 MB heapUsed 41.14 MB 109.49 MB external 0.73 MB 0.73 MB arrayBuffers 0.01 MB 0.01 MB 以上の結果からも明らかなように、メソッド定義と比べて、フィールド宣言のほうはメモリを多く使うことが分かります。
このひとつの原因は、メソッド定義で生やしたメソッドは、1つしか関数オブジェクトが作られないのに対して、フィールド宣言で生やしたメソッドは、
new
されるごとに新しい関数オブジェクトが作られるためと考えられます。新しい関数オブジェクトが生成されているかは、同一性を比較することで分かります:class C { method1() {} method2 = () => {} } const c1 = new C() const c2 = new C() // メソッド定義によるメソッドの同一性 console.log(c1.method1 === c1.method1) //=> true // フィールド宣言によるメソッドの同一性 console.log(c1.method2 === c2.method2) //=> falseメソッド定義で生やした
method1
はインスタンスが異なっていても、同じ関数オブジェクトになっていますが、フィールド宣言で生やしたmethod2
は、名前と実装が同じでも、インスタンスが異なると、別の関数オブジェクトになっています。まとめ
本稿で行った比較をまとめると、次のようになります。
フィールド宣言で生やしたメソッドは、メソッド定義と比べて、
- メソッドオーバーライドでは
super
が使えない場合がある。- プロトタイプに入らない。
- インスタンス自身が所有するプロパティになる。
- 列挙可能なプロパティになる。
- 複数インスタンスを生成する場合、メモリを多く使う。
以上が、メソッドをメソッド定義で生やした場合と、フィールド宣言で生やした場合の違いです。
特段理由がない限り、クラス構文でインスタンスメソッドを生やすときは、メソッド定義の構文を使ったほうが良さそうです。
- 投稿日:2020-09-24T10:27:27+09:00
Bolt を使って Slack でヘルプデスクチームとのやりとりを効率化するアプリをつくってみよう
Slack アプリ開発へようこそ!
この記事では、ショートカット、モーダルのようなインタラクティブな機能を Bolt for JavaScript を使ってどのように実装すればよいかを解説します。
なお、この記事で紹介するのは、BIT VALLEY 2020 のための用意されたデモアプリです。
https://github.com/seratch/bit-valley-2020-slack-bolt-app
BIT VALLEY での講演は YouTube で公開されていますので、こちらからご覧ください。
この記事で実装するアプリ
ここで扱うサンプルプロジェクトは、ヘルプデスクチームが提供するアプリを想定したサンプルです。シンプルではありつつも、Slack プラットフォームの最新の機能をうまく活用しているので、これをベースに拡張することで実際に使えるアプリに進化させることができるでしょう。
以下のアニメーションで、どのようなアプリなのかをみてみましょう。
ショートカットでモーダルを起動
エンドユーザーは、Slack の最下部の入力ボックスからショットカットを起動し、モーダルウィンドウを開くことができます。
複数画面を行き来できるモーダル
エンドユーザーは、このモーダル上でまずカテゴリを選択します。ユーザーがカテゴリを選択すると、モーダルは即座に自分自身の表示状態を切り替えて、選択されたカテゴリに対応した入力フォームを表示します。「カテゴリ選択に戻る」ボタンを押すと、一つ前の画面に戻ることもできます。
カスタムの入力チェック
このモーダルを送信すると、Slack の UI が標準で持っている必須入力チェックなどに加えて、サーバーサイドがカスタムの入力チェック処理を行います。ここでは件名の桁数と希望納品日が未来日かどうかのチェックをしています。
通知を送信
データ送信を受け付けたら、ヘルプデスクチームのチャンネルや関係者の DM にメッセージを送信することができます。
ホームタブを更新
また、ホームタブというユーザーごとにカスタムの情報を表示できるエリアに、このユーザーのこれまでの申請履歴を表示しています。
使われている Slack プラットフォームの機能
このアプリは以下のような Slack プラットフォームの最新の機能を使って実装されています。
- ブロックキット: ブロックキットはリッチなユーザーインターフェースを実装するための Slack の UI フレームワークです。Slack アプリの開発者の視点で見るとブロックキットは予め決められた JSON のデータ構造です。アプリ側がこれに従うことで Slack がデスクトップ・モバイルに最適な形でレンダリングします。
- ショートカット: グローバルショートカットは Slack のどこからでも呼び出せる処理です。ユーザーは、最下部のメッセージ投稿 UI、または、最上部の検索メニューから呼び出すことができます。
- モーダル: モーダルはブロックキットを使って構成するポップアップウィンドウです。ユーザーから入力内容を収集したり、情報を整理された形で表示したりするために利用されます。また、複数のステップの遷移を実装したり、複数モーダルを重ねたりすることもできます。
- ホームタブ: ユーザーごとに個別の情報を表示するためのエリアです。自分のタスクや予定を表示したり、ダッシュボードのようなものを表示するために利用されます。
Bolt for JavaScript
Bolt for JavaScript (ボルト) は、公式のフルスタックな Slack アプリ開発フレームワークです。全ての新しいプラットフォーム機能がサポートされており、また Slack アプリで必要となる非機能要件に予めフレームワーク側で対応しています:
- Slack からのリクエストを検証: Slack アプリのエンドポイントは公開された URL である必要があるため、受信したリクエストが本当に Slack からのリクエストかどうかを検証することはセキュリティのために重要です。Bolt を使うと Signing Secret の値を設定しておくだけで、この検証が自動的に有効になります。
- リクエストを適切に分岐: Slack からのリクエストを適切なリスナー関数に実行させます。Web アプリフレームワークのルーティング機能のようなインターフェースで簡単にこの分岐を設定することができます。
- ペイロードの形式の差異を吸収: 歴史的経緯から Slack API のペイロードの形式は機能によって若干異なりますが、Bolt はそれらを適切にパーズした上で、その内容をミドルウェアやリスナー関数に統一的な形式で引き渡します。
- 無限ループに陥らないように予め制御: イベント API を使っていると、ボットユーザー自身の発言に再度反応して無限ループしてしまうという失敗がありがちですが、Bolt を使っていると予めループしないよう制御してくれます。
- 複数ワークスペース対応: 開発用ワークスペース以外にもインストールできるアプリとして提供するために必要な OAuth フローの実装や、各リクエストごとに適切なトークンを選択するロジックを簡単に実装できる仕組みを提供しています。
このように Bolt を使うことで、アプリの本質的なロジック以外の様々なことへの対応がシンプルになります。
このアプリを実装するための全ステップ
プロジェクトをセットアップする
git clone git@github.com:seratch/bit-valley-2020-slack-bolt-app.git cd bit-valley-2020-slack-bolt-app/ node --version # Node v10.13 かそれよりも新しいバージョンが必須です npm i # 依存パッケージをインストール code . # Visual Studio Code を使ってプロジェクトを開きますSlack アプリの設定を作る
https://api.slack.com/apps にブラウザでアクセスして、新しい Slack アプリの設定をつくります。
このブラウザで、Slack ワークスペースにログインしているか確認し、その上で先ほどの URL から Slack アプリを作ります。
必要な Bot Token Scopes を設定
アプリを作ったら OAuth & Permissions というページに遷移し、Bot Token Scopes というセクションまでスクロールします。そして、以下の権限を追加してください。
commands
は、新しいショートカットの追加に必要です。chat:write
はchat.postMessage
API というメッセージ投稿の API を使うために必要です。chat:write.public
は、任意のパブリックチャンネルにボットユーザーを招待することなくchat.postMessage
API を実行するために必要です。im:write
はconversations.open
API という新しい DM を開始するための API を使うために必要です。ボットユーザーに適切な名前をつける
インストールする前に App Home ページで、ボットユーザーにわかりやすい名前をつけるとよいでしょう。 Basic Information の最下部でアプリのアイコンや説明をなどをカスタマイズすることもできます。
ホームタブ機能を有効にする
デフォルトではホームタブの機能は有効になっていないので、 App Home ページで有効にしておいてください。
アプリをワークスペースにインストールする
「Development Slack Workspace」で使用する限りにおいては、OAuth フローを実装しなくても OAuth アクセストークンを発行して、アプリをそのワークスペースで有効化することができます。
Install App ページから、インストールボタンをクリックし、OAuth フローを完了させてください。戻ってきた画面で Bot User OAuth Access Token として表されているアクセストークンをアプリ起動時に指定します。
もし、複数ワークスペースにインストール可能なアプリを実装することに興味があれば、Bolt for JavaScript のドキュメントを参考にしてみてください。
Bolt アプリを立ち上げる
ローカルで Bolt アプリを起動するには、以下のステップに従ってください:
- プロジェクトのルートディレクトリに
.env
ファイルを作って、必要な情報を設定- Node.js 10.13 かそれより新しいバージョンを使っているか確認 (
node --version
)npm install
を実行して依存ライブラリをインストールnpm run local
を実行して、アプリを起動- ngrok をインストールしていなければインストール - https://ngrok.com/
- 別のターミナルで
ngrok http 3000
を実行して公開エンドポイントを立ち上げる.env を配置して Bot Token と Signing Secret を設定
SLACK_BOT_TOKEN=xoxb-111-111-xxx SLACK_SIGNING_SECRET=xxxローカルアプリを起動
node --version # v10.13.0 以上 npm install npm run local # 起動すると http://localhost:3000/slack/events でリクエストを受け付けますngrok を Slack からのリクエストをフォワードするために起動
ngrok をまだインストールしていなければ、ウェブサイトからダウンロードして設定します。以下のステップで、適切に設定できたか確認します。
# ローカルでアプリが立ち上がっていることを確認 curl -I -XPOST http://localhost:3000/slack/events # HTTP/1.1 401 Unauthorized が返ってくるはず # 別のターミナルで実行 ./ngrok http 3000 # ngrok の有償プランを使っているなら以下のように固定のサブドメインを指定できます ./ngrok http 3000 --subdomain {whatever-you-want} # 再度、公開エンドポイントからリクエストをして、同じように 401 が返ってくるか確認 curl -I -XPOST https://{your random subdomain here}.ngrok.io/slack/events # HTTP/1.1 401 Unauthorized が返ってくれば OKRequest URL を設定して、ショートカットを追加
- https://api.slack.comc/apps にアクセスしてアプリを選択
- Interactivity & Shortcuts ページへ移動
- Interactivy 機能が無効になっているのを有効にする
https://{your random subdomain here}.ngrok.io/slack/events
を Request URL として設定- Global のショートカットを Callback ID:
new-helpdesk-request
で作成- 最下部の Save Changes ボタンを押すのを忘れずに
リスナー関数を実装する
このアプリでは以下のリスナー関数を実装しています。
- グローバルショートカットのリスナー関数 を
new-helpdesk-request
という Callback ID に反応するよう設定- セレクトメニューでの選択に対するリスナー関数 を
helpdesk-request-modal-category-selection
という Action ID に反応するよう設定- 「カテゴリ選択へ戻る」ボタンクリックに対するリスナー関数 を
helpdesk-request-modal-reset
という Action ID に反応するよう設定- モーダル送信に対するリスナー関数 を
helpdesk-request-modal
という Callback ID に反応するよう設定小技: Block Kit のプレビューツール
ブロックキットビルダー というブラウザから使えるブロックキットのプレビューツールがあります。実際にアプリに
blocks
を組み込む前に、このツールを使って表示を調整すると効率的に UI を改善できるでしょう。まとめ
- https://api.slack.com/apps から Slack アプリの設定を行います
- そのブラウザで Slack ワークスペースにログインしている必要があります
- 必要な権限を設定する
- "OAuth & Permissions" ページで Bot Token Scopes を設定します
- Interactivity, Events Subscriptions, Home tab のような機能を有効にします
- Development Slack Workspace にアプリをインストールします
- "Bot User OAuth Access Token" を取得します
- Bolt アプリをローカルで作成します
SLACK_BOT_TOKEN
,SLACK_SIGNING_SECRET
という環境変数、または .env ファイルを設定しますapp.start()
で Web サーバープロセスを起動します- デフォルトでは
POST http://localhost:3000/slack/events
で全てのリクエストを受けます- 公開されたエンドポイントを設定して、Slack からのリクエストを受け付けられるようにします
- ngrok かその他類似のソリューションを使います
- 必要なリスナー関数を実装します
- ngrok を使っているなら
http://localhost:4040
を確認すると捗るでしょう- 標準出力にエラーメッセージが出ていることもあります
- Bolt for JS のドキュメントは全て日本語化されています
- Slack アプリ開発を楽しんでください!