- 投稿日:2021-01-20T23:31:12+09:00
AppSync & GraphQL 入門
AppSync とは?
GraphQL というAPI仕様を用いて「柔軟なAPI」を提供するAWSのマネジメントサービス
ちなみに、従来の REST API 形式だと AWSは API Gateway を提供している
GraphQL とは?
Facebookが開発しているWeb APIのための規格
「クエリ言語」 と 「スキーマ言語」 からなる
REST API は、1URLに対し1つのAPIや情報を提供できるのに対し、
GraphQL は欲しいデータを以下のようなクエリとして発行すると、欲しいデータを欲しいObject形式で得ることができます
// リクエスト query GetCurrentUser { currentUser { id name } }↓
// レスポンス { id: 'hoge', name: 'yamada' }AppSyncの仕組み
AppSyncは直接DynamoDBの値を取得・更新・削除することができます
従来のAPI Gatewayだと、AWS Lambda が間に必要でしたが、
AppSyncは Lambda レス でDynamoDBへのアクセスが可能です
代わりに、AppSync内のリゾルバーという領域にロジックを記述します
クエリ
実行されるGraphQlのこと
スキーマ
どの型の値をどこで使うかを定義する設計書
リゾルバー
関数のこと。ロジックを記述する。
リゾルバーは、
リクエストマッピングテンプレート
とレスポンスマッピングテンプレート
で構成さる
リクエストマッピングテンプレート
は、「変換」と「実行」のロジックが含まれているリソース
データベースのこと。AppSync では
AWS DynamoDB
に自動的に接続されるAppSyncの料金
使用した分だけ課金されます
クエリとデータ変更操作
4.00USD ≒ 423.87 円 / クエリおよびデータ変更操作 100 万回あたり
リアルタイム更新
データが更新された際に、リアルタイムに更新する機能
2.00USD ≒ 211.94 円 / リアルタイムアップデート 100 万回
最初の12ヶ月の無料利用枠の対象でもあるようなので、登録後12ヶ月は一定回数は無料で使用できます
試してみる
実際に、AppSyncを用いてイベントを取得・登録する処理を実装してみます、とても簡単です
AppSync API を作成
AWS ログインして、AppSyncページへ移動し、「APIを作成」
サンプルプロジェクトから「イベントアプリ」を選んで「開始」
API 名は
[yourname] App
としてください左メニューから「クエリ」ページに移動すると、GraphQL Explorer が表示されます
ここで、GraphQLを試すことが可能です
▶︎ボタンから、実行したいクエリを選択してみると、右側に結果が表示されます
デフォルトで2つのクエリが用意されています
mutation CreateEvent { createEvent( name: "My First Event" when: "Today" where: "My House" description: "Very first event" ) { id name } } query ListEvents { listEvents { items { id name } } }1つめの
mutation CreateEvent
は、新たな Event のデータを作成するための mutation です2つめの
query ListEvents
は、DBに登録されている Event のデータを取得するための query です
CreateEvent
を何度か実行するとListEvents
の結果が変わることがわかりますGraphQLには3種類のクエリがある
| 名前 | 説明 |
| -- | -- |
| query | データ取得 (read) |
| mutation | データ作成/更新/削除 (create / update / delete) |
| subscription | リアルタイムイベントを受け取れる。内部的にはwebsocketが使われている |先ほどのサンプルでは query と mutation を使用しています
実際に Javascript で GraphQL を使ってみる
Axios を使って試してみます
URL と API KEY を AppSyncコンソールの設定ページから見つけて、セットしてみてください
上手くいけば、GraphQLのresultが、consoleに表示されます
const data = await axios.post( API_URL, { query: ` // ここにqueryをかく ` }, { headers: { // header に APIキーを渡す。 appSync設定画面から取得 "x-api-key": "" } } );
request body
にクエリを記述、request header
にx-api-key
として API KEY を持たせることで認証されます、とても簡単ですね認証方法
appSyncでは、4つの認証方法が用意されています
| 名前 | 概要 | ユースケース |
| -- | -- | -- |
| API_KEY | 今回使ったもの。最大 365 日間有効に設定可能で、該当日からさらに最大 365 日、既存の有効期限を延長可 | パブリック API の公開が安全であるユースケース、または開発目的での使用が推奨 |
| AWS_IAM | IAMポリシーを紐づけて使用 | IAMロールごとに、特定の機能のみに制限したい場合 |
| OPENID_CONNECT | OpenID Connect (OIDC) トークンを適用 | OpenID Connectを使いたい場合(未調査) |
| AMAZON_COGNITO_USER_POOLS | Amazon Cognito ユーザープールによって提供される OIDC トークンが使用されます | Amazon Cognito ユーザープールによって提供されるOIDCトークンを使いたい場合(未調査) |基本的には、上2つ
API_KEY
とAWS_IAM
を使うパターンが多いでしょうデータベースの中身を見てみる
左メニューから、
データソース
を選択すると、DynamoDB
へのリンクがあります
AppSync
が自動生成してくれたテーブルが、ここに表示されていますAPIを変更・追加してみる
createEvent に
who
という項目を追加してみるAWSコンソールの左メニューから
スキーマ
を選択すると、定義されている Schema が表示されますこの中から、
Mutation
の下にあるcreateEvent
を見つけ、引数にwho
を追加してみます右上から
スキーマを保存
します
クエリ
ページから、who に適当な値を追加して、実行してみますこれで、dynamoDBに
who
が追加されたか確認してみましょう左メニューから
データソース
->AppSyncEventTable
のリソースを開きますあれ、項目
who
が追加されていると思いましたが、追加されていません?理由は簡単です、
リゾルバー
も変更する必要があります?
リゾルバー
とは、このページの冒頭で表示した図にあるように、ロジックを記述する領域です
リゾルバー
の変更は、スキーマ
ページの右カラムから可能です
createEvent
を見つけましょう⤵︎
リクエストマッピングテンプレート
を以下のようにして、who
を追記します{ "version": "2017-02-28", "operation": "PutItem", "key": { "id": { "S": "$util.autoId()"} }, "attributeValues": { "name": { "S": "$context.arguments.name" }, "where": { "S": "$context.arguments.where" }, "when": { "S": "$context.arguments.when" }, "who": { "S": "$context.arguments.who" }, "description": { "S": "$context.arguments.description" } } }リゾルバーを保存して、実行すると、
who
項目が追加されていることが確認できました?今回は内部の挙動を理解するために、
ブラウザからAWSコンソールを通じてスキーマやリゾルバーの変更を行いましたが、
実際には
AWS Cloudformation
やAmplify Framework
などを用いると良いそうですまとめ
メリット
GraphQL は REST API に比べて欲しいデータを欲しい形式で得ることが可能
GraphQL により、画面や機能ごとに、個別にAPIを定義するコストが削減される
AppSync を使えばリソース(DynamoDB)との連携を楽に行うことができる
既存の REST API を AppSync でラップして、GraphQL を導入することも可能らしい(未調査)
デメリット
GraphQL, Appsync の学習コストがかかる
フロントエンドの都合の良いように、値を返す必要があるため、リゾルバーのロジックが複雑になる
効率的にデータを処理できないので、パフォーマンスが低下し、N+1問題が発生する
※ N+1問題・・・ループ処理の中で都度SQLを発行してしまい、大量のSQLが発行されてパフォーマンスが低下してしまう問題のこと
実際にAppSyncで実装してみたページ
dynamoDB に入っているニュースのデータを AppSync を使って表示しています
これから調べる
AWS_IAM
を使った AppSync 認証方法。Cognito
を使うらしいです参考
https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/welcome.html
- 投稿日:2021-01-20T23:09:24+09:00
モトローラ形式(S-Record)(.motファイル)のチェックサムを付与するツールつくった【自分用】
モトローラ形式の文字列をぶちこむと、末尾のチェックサムをいい感じにしてくれるツールをつくってみた
See the Pen MotCheckSumCalculator by kob58im (@kob58im) on CodePen.
きゃぷちゃ
サンプルmotデータ(参考サイト1より)
S00F000068656C6C6F202020202000003C S11F00007C0802A6900100049421FFF07C6C1B787C8C23783C6000003863000026 S11F001C4BFFFFE5398000007D83637880010014382100107C0803A64E800020E9 S111003848656C6C6F20776F726C642E0A0042 S5030003F9 S9030000FC参考サイト
- 投稿日:2021-01-20T22:45:50+09:00
#UIFlow の BLE UART を使った #M5Stack_Core2 ( #M5Stack )からブラウザへのデータ送信とグラフ化
これまで、UIFlow の BLE UART を試して以下の記事を書いてきました。
- #UIFlow の BLE UART を使った文字のやりとりを #M5Stack_Core2 で試してみた( #M5Stack ) - Qiita
- 【JavaScript 2020】 #UIFlow の BLE UART を使ったブラウザから #M5Stack_Core2 ( #M5Stack )への文字の送信 - Qiita
- #UIFlow の BLE UART を使った #M5Stack_Core2 ( #M5Stack )とブラウザとの双方向無線通信 - Qiita
この記事は、上記で試してきた流れと、以下の記事で使ったグラフ化とを組み合わせた内容になります。
●【JavaScript 2020】 MQTT で受信したデータを Smoothie Charts(smoothie.js)以外でリアルタイムにグラフ化: Chart.js とプラグインを利用 - Qiita
https://qiita.com/youtoy/items/252f255c9d794bf3d964概要
まず、今回試した内容が動作している様子をご覧ください。
M5Stack Core2 のタッチスクリーン上に触れると、その x座標か y座標の値を取得し(どちらを取得するかは、Bボタン押下で変更できる仕組みあり)、それが BLE経由でブラウザに送られます。ブラウザ側では、受け取った値を使ってリアルタイムにグラフを描いています。#UIFlow の BLE UART を引き続きお試し。#M5Stack_Core2 の画面上でタッチされた座標を BLE でブラウザに送り、リアルタイムにグラフとして描画してみた。
— you (@youtoy) January 20, 2021
送る値を途中で 、x座標にするか y座標にするか、入れ替えてます。#M5Stack pic.twitter.com/wHvN4vdoB8プログラム
M5Stack Core2 で動作している UIFlow のプログラムと、ブラウザ側で動いている HTML+JavaScript のソースをそれぞれ記載します。
UIFlow
UIFlow側は、以下のような仕組みにしてます。
- Aボタン押下 ⇒ 300ミリ秒間隔で動くタイマーを開始
- Bボタン押下 ⇒ 取得する値(タッチスクリーン上で触れられた位置の座標)を x/y のどちらにするか切り替え
- Cボタン押下 ⇒ タイマーを止める
- タイマーの処理 ⇒ タッチスクリーン上で触れられた位置の座標 x/y を BLE で送信しつつ、画面上にも表示
タイマーの処理の最後のブロック、「テキストに変換する」を入れないとうまく動かなかったので入れたものです。
その1つ上のブロック、画面のラベルでの表示のほうはそれが不要だったのですが、テキストのプログラムに切り替えてそれぞれを見てみると、画面表示のほうはテキストに変換する処理が裏で入ってました。HTML+JavaScript
ブラウザ側の処理(BLE関連の処理やグラフ描画の処理)は以下のように実装しています。
利用する際には、こちらを HTMLファイルにしてブラウザで開き、最初に画面上の「接続」ボタンを押してペアリングから始めてください。その後は、M5Stack Core2側を操作していく流れです。<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>UIFlow と BLE</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment-with-locales.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming@1.8.0/dist/chartjs-plugin-streaming.min.js"></script> </head> <body> <h1>UIFlow と BLE + グラフ描画</h1> <button onclick="onStartButtonClick()">接続</button> <br /> <canvas id="myChart"></canvas> <script> const UUID_1 = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"; const UUID_2 = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"; // Write、今回は使わない const UUID_3 = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"; // Notify let bluetoothDevice; let characteristic; async function onStartButtonClick() { try { console.log("Requesting Bluetooth Device..."); const device = await navigator.bluetooth.requestDevice({ filters: [{ namePrefix: "m5ble" }], optionalServices: [UUID_1], }); console.log("Connecting to GATT Server..."); const server = await device.gatt.connect(); console.log("Getting Service..."); const service = await server.getPrimaryService(UUID_1); console.log("Getting Characteristic..."); characteristic = await service.getCharacteristic(UUID_3); await characteristic.startNotifications(); console.log("> Notifications started"); characteristic.addEventListener( "characteristicvaluechanged", handleNotifications ); } catch (error) { console.log("Argh! " + error); } } const ctx = document.getElementById("myChart").getContext("2d"); let chart = new Chart(ctx, { type: "line", data: { datasets: [ { data: [], }, { data: [], }, ], }, options: { scales: { xAxes: [ { type: "realtime", realtime: { delay: 200, }, }, ], }, }, }); async function handleNotifications(event) { if (characteristic) { try { const value = event.target.value; const inputValue = new TextDecoder().decode(value); console.log(inputValue); chart.data.datasets[0].data.push({ x: Date.now(), y: inputValue, }); chart.update({ preservation: true, }); } catch (error) { console.log("Argh! " + error); } } } </script> </body> </html>グラフ描画まわりの処理の説明は、記事の上のほうでも掲載した以下の記事のほうをご覧ください。
●【JavaScript 2020】 MQTT で受信したデータを Smoothie Charts(smoothie.js)以外でリアルタイムにグラフ化: Chart.js とプラグインを利用 - Qiita
https://qiita.com/youtoy/items/252f255c9d794bf3d964
- 投稿日:2021-01-20T21:49:17+09:00
画像のプレビュー機能
はじめに
画像投稿時に、画像ファイルの名前だけが表示されるだけで、きちんと出来ている分かりづらいと感じたので、プレビュー機能を実装しました。
1.準備
1. プレビュー機能を実装させるためのjsファイルを作成する。app/javascript/packsにpreview.jsを作成
→ app / javascript / packs / preview.js2.preview.jsを読み込めるようapplication.jsを編集する。
app/javascript/packs/application.jsrequire("@rails/ujs").start() require("@rails/activestorage").start() require("channels") require('./preview') # 追記する3.viewファイルに画像が表示される場所を指定する。
views/ideas/new.html.erb<div class="img-upload"> <div class="left-img-upload"> <div class="weight-bold-text"> 関連画像(関連する画像があれば添付してください) </div> <div class="click-upload"> <p>クリックしてファイルをアップロード</p> <%= f.file_field :image, id:"idea-image" %> </div> </div> <div class="right-img-upload"> <div id="image"></div> <!--追記する--> </div> </div>2.プレビュー機能実装
1 で作成したpreview.jsにプレビュー機能のコードを記述する。
app/javascript/packs/preview.jsif (document.URL.match( /new/ ) || document.URL.match( /edit/ )) { document.addEventListener('DOMContentLoaded', function(){ const ImageList = document.getElementById('image'); const createImageHTML = (blob) => { // 画像を表示するためのdiv要素を生成 const imageElement = document.createElement('div'); // 表示する画像を生成 const blobImage = document.createElement('img'); blobImage.className="preview"; //←createElementで生成したimgにクラス名を付けている blobImage.setAttribute('src', blob); // 生成したHTMLの要素をブラウザに表示させる imageElement.appendChild(blobImage); ImageList.appendChild(imageElement); }; document.getElementById('idea-image').addEventListener('change', function(e){ // 画像が表示されている場合のみ、すでに存在している画像を削除する const imageContent = document.querySelector('img'); if (imageContent){ imageContent.remove(); } const file = e.target.files[0]; const blob = window.URL.createObjectURL(file); createImageHTML(blob); }); }); }
最後にCSSで画像のサイズを指定する。.preview { height: 250px; width: 250px; object-fit: contain; }実装完了
画像が表示されるようになったおかげで、自分が選択した画像が分かりやすくなりました。
最後に
javascriptを用いた実装はあまり行っていなかったため、いい復習となりました。
また、createElementで生成した要素にクラス名をつける方法など学ぶことができ、勉強になりました。
- 投稿日:2021-01-20T20:41:24+09:00
C3jsでリアルタイムグラフ
結果
See the Pen C3js TimeSeries Realtime Graph by Nagitch (@nagitch) on CodePen.
解説
過去1分間の時間軸を表示する
軸の設定をtype: timeseries(時系列), max/minに現在秒, 一分前の秒にしておくと自動的に過去1分間の時間軸を表示してくれます。
一秒刻みでデータを登録して無理やりプロットする、みたいなトリックは使わなくて大丈夫です。
また2021-01-20 19:47:35
のような表示にする&1分前の計算のためにdayjsを使っていますが、同じ形になれば何を使っても問題ありません。const timeNow = () => dayjs().format('YYYY-MM-DD HH:mm:ss'); const timeTail = () => dayjs().subtract(1, 'm').format('YYYY-MM-DD HH:mm:ss'); const chartAxis = { x: { type: 'timeseries', min: timeTail(), max: timeNow(),また等間隔に満遍なく軸表示するために
fit: true
, 表示を倒して見やすくするためrotate:-50
を指定します。tick: { fit: true, rotate: -50, format: '%Y-%m-%d %H:%M:%S', }時間軸(表示範囲)の更新
時間軸の更新には
axis.min()
,axis.max()
関数を使います。直接値を書き換えるだけだと反映されません。load()
を叩いても更新されません。setInterval(() => { chart.axis.min({x: timeTail()}); chart.axis.max({x: timeNow()}); ... }, 1000)サンプルでは毎秒新しいデータをプロットしていますが、歯抜けになっても大丈夫です。
好きなタイミングでどの秒にデータを入れても正しくプロットされます。chartData.columns[0].push(timeNow()); chartData.columns[1].push(Math.random()); chart.load({columns: chartData.columns});
- 投稿日:2021-01-20T20:20:06+09:00
what is the best java website
토토 정보와 토토사이트, 안전놀이터, 먹튀 검증을 제공합니다. 토사랑 사이트 추천은 믿을 수 있습니다. 여러분이 안전히 사설 토토 이용을 할 수 있도록 하며, 양질의 메이저놀이터 분석으로 최고의 토토 커뮤니티 사이트를 지향합니다. 스포츠토토 정보공유 방문을 환영합니다.https://www.tolove24.com
- 投稿日:2021-01-20T18:49:14+09:00
[JavaScript]書き方と基礎文法に関して(変数、四則演算、条件分岐、繰り返し処理)
今回は、JavaScriptの基礎文法について書いていきます!!
JavaScriptの文法はRubyと似ていて少し頭の中がごちゃごちゃします(笑)なので、記憶が新しい内に整理していきます。
Javascriptの書き方(2通り)
scriptタグを使います。
その後からは、下記の2種類の方法で出来ます。
①htmlファイル内に書いていく。
①.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> </head> <body> <script>..........</script> //scriptタグ内に書いていく。 </body> </html>②〇〇.jsファイルに書いていく。
*.js = javascript拡張子ファイルのことです。イメージとしては、cssファイルと同様です。②.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> </head> <body> <script src="app.js"></script> //src=を用いて、jsファイルと繋げる。 </body> </html>②の方が、ファイルないがごちゃごちゃせず、別ファイルにてJavaScriptのみ見れるので、
②で書いていくことが望ましいです。
(むしろ①で書くことなんてあるのかな、、、、。)なので、これからの基礎文法は、.jsファイル内に書いていく事を想定して説明していきます。
変数
今回は、 hello を変数とした場合で書きます。
Rubyでいうと、下記の書き方でした。
Ruby.rbhello = "Hello world"JavaScriptに関しては、少し異なります。
JavaScript.jsvar hello = "Hello World"; // var+変数 = 値(文字もしくは数字)相違点を下記の表でまとめました。
相違点 説明 var variable 英語で変数という意味。その略。 ; 文の末には、;が必須。忘がち。 四則演算
これは、Rubyとほぼ同じですね。
JavaScript.jsalert(2+4); // 結果は、6 alert(10-2); // 結果は、8 alert(3*4); // 結果は、12 alert(10/5); // 結果は、2ちなみに、 alert は、ブラウザの上にアラートを表示させるものです。
おそらくパソコンを使っている皆さんなら一度は見たことがあ流と思います。多分、、、、。
次の条件分岐の項目で、アラートの画像が出てきます。(結果の部分)条件分岐
if構文になります。
こちらもRubyと似ていますが、少しだけ違ったりしてややこしいです。
例を下記に記します。JavaScript.jsvar Tom = 20; var John = 18; if( Tom > John){ // ()の中には条件を記載 alert('Tomは、Johnより年上です。'); // どう処理するかを記載 } else if( Tom == John) { // ()の中には条件を記載 alert('Tomは、Johnと同い年です。'); // どう処理するかを記載 } else { alert('Tomは、Johnより年下です。'); }繰り返し処理
こちらは、 While と for を使います。
・While構文
JavaScript.jsvar max= 100; var num= 1; var count= 0; while(num < max){ num = num * 2; count = count + 1; } alert('2を掛け続けて'+ max + 'を超えるのに必要だった回数は' + count + '回です');・For構文
JavaScript.jsvar i; var num = 0; for(i = 1; i < 101; i++){ //(初期値; 条件式; 増減値)で記載する。 num = num + i; //繰り返す度に+1される。 } alert('1から100まで足し算した結果は' + num + 'です。'); //条件式が当てはまらなくなったらalertが適応される。他にもありますが、一旦はこちらにて終わります。
以上、たにーでした。よかったら、LGTMをよろしくお願いします!
守られるとモチベーションになります
- 投稿日:2021-01-20T18:15:29+09:00
[個人開発]使い捨てチャットアプリ作ってみた
初めに
今回はweb上で動くチャットアプリ 「スコーチチャット」を作りました。
アカウント登録不要
ダウンロード不要
で使い捨てで使われることを想定してます。URL
https://scorch-chat.herokuapp.com/開発環境
rails6
ruby2.7
windows10
heroku
postgresql
ActionCablegem 'ridgepole'
gem 'slim-rails'
gem 'html2slim'
gem 'pry-rails'
gem 'bcrypt'
gem 'activeadmin'
gem 'devise'
gem 'rack-attack'いつもと同じです。
開発期間
5日くらいです。
webアプリは当たる確率が6%程で質より量を打ったほうがいいといわれたのでなるべく早く作りました。チャット画面
[自分流]新しい技術の勉強法
結論から言うと、元あるコードを改造することです。
初めて使う技術のコードを一気に書くのは難しいです。
なので作りたいアプリになるべく似たコードを
githubなどで落としてきてそれで開発を進めるんです。
するとインプットとアウトプットを実践でできるので
いい勉強法になると思ってます。今回はじめてActionCableを使ったのですが最初は全く分かりませんでしたがもう
何となくわかるようになりました終わりに
ここまで読んでくれてありがとうございました。
スコーチチャットをぜひ使ってみてください!
URL
https://scorch-chat.herokuapp.com/
- 投稿日:2021-01-20T18:15:29+09:00
使い捨てのチャットを作成できるサービス「Scorch Chat」をリリースした!
初めに
今回はweb上で動くチャットアプリ 「スコーチチャット」を作りました。
アカウント登録不要
ダウンロード不要
で使い捨てで使われることを想定してます。URL
https://scorch-chat.herokuapp.com/開発環境
rails6
ruby2.7
heroku
postgresql
ActionCable
slimいつもと同じです。
開発期間
5日くらいです。
webアプリは当たる確率が6%程で質より量を打ったほうがいいといわれたのでなるべく早く作りました。チャット画面
[自分流]新しい技術の勉強法
結論から言うと、元あるコードを改造することです。
初めて使う技術のコードを一気に書くのは難しいです。
なので作りたいアプリになるべく似たコードを
githubなどで落としてきてそれで開発を進めます。
するとインプットとアウトプットを実践でできるので
いい勉強法になると思ってます。今回はじめてActionCableを使ったのですが最初は全く分かりませんでしたがもう
何となくわかるようになりましたRailsはオワコンか?
ほかの記事で話題になっていたので取り上げます。
僕はrailsはオワコンではないと思います。
結構昔から言われ続けてますが、まだオワコンにはなってないでしょう?
それが答えな気がします。railsを圧倒するようなフレームワークが出ない限りオワコンにはならない気がします。
終わりに
ここまで読んでくれてありがとうございました。
スコーチチャットをぜひ使ってみてください!
URL
https://scorch-chat.herokuapp.com/
- 投稿日:2021-01-20T18:15:29+09:00
手軽にチャットを作成できるサービスをリリースした!
初めに
今回はweb上で動くチャットアプリ 「スコーチチャット」を作りました。
アカウント登録不要
ダウンロード不要
で使い捨てで使われることを想定してます。URL
https://scorch-chat.herokuapp.com/開発環境
rails6
ruby2.7
heroku
postgresql
ActionCable
slimいつもと同じです。
開発期間
5日くらいです。
webアプリは当たる確率が6%程で質より量を打ったほうがいいといわれたのでなるべく早く作りました。チャット画面
[自分流]新しい技術の勉強法
結論から言うと、元あるコードを改造することです。
初めて使う技術のコードを一気に書くのは難しいです。
なので作りたいアプリになるべく似たコードを
githubなどで落としてきてそれで開発を進めます。
するとインプットとアウトプットを実践でできるので
いい勉強法になると思ってます。今回はじめてActionCableを使ったのですが最初は全く分かりませんでしたがもう
何となくわかるようになりましたRailsはオワコンか?
ほかの記事で話題になっていたので取り上げます。
僕はrailsはオワコンではないと思います。
結構昔から言われ続けてますが、まだオワコンにはなってないでしょう?
それが答えな気がします。railsを圧倒するようなフレームワークが出ない限りオワコンにはならない気がします。
終わりに
ここまで読んでくれてありがとうございました。
スコーチチャットをぜひ使ってみてください!
URL
https://scorch-chat.herokuapp.com/
- 投稿日:2021-01-20T17:28:03+09:00
【Vue.js】コンポーネントを動的に切り替える時とその注意点【動的コンポーネント】
前提
コンポーネントを複数用意した際の切り替え方。
その際注意すること。
- Houseコンポーネント
- Contentコンポーネント
- 親コンポーネント : App.vue
今回は二つ子コンポーネントを用意。
ボタンを押すことで二つのコンポーネントを切り替えることができる。Houseコンポーネント
House.vue<template> <div> <p>House</p> </div> </template>Contentコンポーネント
Content.vue<template> <div> <p>Content</p> <input type="text"> </div> </template> <script> export default { destroyed() { console.log('destoryed'); } } </script>親コンポーネント
App.vue<template> <div> <button @click="currentComponent = 'Content'">Content</button> <button @click="currentComponent = 'House'">House</button> <keep-alive> <component :is="currentComponent"></component> </keep-alive> </div> </template> <script> export default { data() { return { currentComponent: 'House', }; }, } <script>コンポーネントの切り替え
keep-alive
- keep-aliveタグ
- componentタグ
- v-bind:is(:is)
keep-aliveのなかにcomponentをネスト。
componentの中でv-bind:isを使ってプロパティを指定すると完成。App.vueではcurrentComponentプロパティにコンポーネントを値として挿入しているが、
直接指定も可能。App.vue//直接指定 <component :is="House"></component> <component :is="Content"></component>ライフサイクルフック
ライフサイクル
- activated
- deactivated
keep-aliveを使用することによって新たなライフサイクルが二つ追加される。
- created
- destroyed
この二つをイメージするとわかりやすい。
注意点
異なる点として、毎回インスタンス化されないため、実際にcreatedやdestroyedされているわけではない。
そのため、inputなどに入力したデータは切り替え後も消えずに残る。
参照
udemy講座 : https://www.udemy.com/course/vue-js-complete-guide/learn/lecture/15371400#content
講師 : よしぴー(Yoshipi)さん
- 投稿日:2021-01-20T17:19:13+09:00
v-calender でCSSが効かない件
環境
エディタ:VS-CODE
Vue:@vue/cli 4.5.9 vue": "2.6.11",
"v-calendar": "2.1.6",
"bootstrap": "4.5.3",初めての記事なので、わかりにくい場合は申し訳ありません。
カレンダーをVueで利用しようと思い、v-calenderを選択しました。
サンプルとして、GitHubの
https://github.com/nathanreyes/v-calendar/blob/master/docs/.vuepress/components/homepage/custom-calendar.vue
からコードをコピペしてVS-CODEに張り付けたのですが、CSSが適用されず困っていました。
調べたところissueにも回答が見つけられず、自力で調べてみました。
参考issue : https://github.com/nathanreyes/v-calendar/issues/727
まだ、Gitのサンプルと違う?というところはありますが、備忘録的に記載しておきます。1.npm or yarn経由でTailwindをインストールする
https://tailwindcss-ja.entap.app/docs/installation
# Using npm npm install tailwindcss または、 # Using Yarn yarn add tailwindcss2.main.js にてCSSをインポートする。
CSSの該当ファイルが見つからず苦労しました・・・
main.js
import "tailwindcss/dist/tailwind.css"; //VCalender の設定 import VCalendar from 'v-calendar'; Vue.use(VCalendar);より良い方法があればご指導ください。
- 投稿日:2021-01-20T17:16:11+09:00
【Next.js】CSR,SSG,SSR,ISRがあやふやな人へざっくり解説する
前書き
仕事で
Next.js
を書いているのですがSSG
SSR
ISR
らへんの知識があやふやだったので噛み砕いて解説してみました。間違っているところなどあれば、ご指摘していただけるとありがたいです?♂️以下、本題です。
それぞれの基本的な解説
CSR(クライアントサイドレンダリング)
クライアントサイド レンダリング(CSR)は JavaScriptを使用し、直接ブラウザでページをレンダリングすることを意味します。すべてのロジック、データフェッチ、テンプレーティングやルーティングは、サーバーではなくクライアント上で扱われます。
つまりサーバーではなく、(JavaScriptによって)ブラウザ側でレンダリングする方法です。しかし
CSR
(クライアントサイドレンダリング)は大きいアプリケーションの場合、クライアントで処理するJavascriptの量も増えますよね。これに伴い、ユーザーにページを表示させるのが遅くなってしまいます。(ユーザーのデバイススペックに依存してしまう)上記の問題を解決するために、
SSR
(サーバーサイドレンダリング)が出てきます。SSR(サーバーサイドレンダリング)
CSR
(クライアントサイドレンダリング)のようにロジックやデータフェッチをブラウザで行うのではなく、サーバー側で処理(データフェッチ等を)し、HTMLを構築してクライアント側に返す方式です。この方法ではCSR
(クライアントサイドレンダリング)のようにクライアント(ユーザーデバイス)のスペックに依存せず、ハイスペックなサーバーでHTMLを構築することができます。とはいえリクエストごとにサーバーで処理し、HTMLが構築されるため、ユーザーを待たせる時間が長くなってしまいます。そこで、
SSG
が出てきます。SSG(静的サイトジェネレーション)
SSR
の問題(CSR
もですが)として、ユーザーのリクエストを受けてからHTMLを構築するので、時間がかかってしまう、という課題がありました。これを解決してくれるのがSSG
(静的サイトジェネレーション)です。
SSG
(静的サイトジェネレーション)はビルド時にHTMLを構築しておきます。この時、外部APIからのデータフェッチも行います。そしてユーザーからリクエストされた時に事前に構築してあるHTMLを表示します。また、アプリケーションサーバーからHTMLを返すのではなく、CDNにキャッシュしておくことでユーザーに対して高速にページを表示することができます。こうすることにより、SSR
よりもユーザーに対して高速にページを表示させることができます。しかし、またもや問題が出てきます。しかも今回は二つ。
一つ目は、ビルド時に大量のデータを取ってくることは現実的ではありません。例えば、アマゾンのように巨大なECサイトの場合、ビルド時に全てのデータを取ってきて、HTMLを構築することは難しいでしょう。
この問題はフォールバックが解決してくれます。
二つ目は、リソースの更新が頻繁な場合は、どうなるでしょうか?例えば、ツイッターみたいに複数人がコンテンツを更新するような場合(更新が激しい場合)は、このままの
SSG
では情報を表示させることができません。だってビルド時しかデータフェッチしていないのだから。この問題を解決するために
ISR
が使われます。それぞれ解説していきます。
フォールバック
export async function getStaticPaths() { return { paths: [ // 省略 ], //ここ!! fallback: true } }
getStaticPaths()
関数内のfallback
が確認できるかと思います。この値がfalse
の場合、存在しないページにアクセスした時に404ページに飛ばされます。true
にした場合、データフェッチしていない状態のHTMLが返され、その後ブラウザ側でデータフェッチが行われて、HTMLが再構築されます。同時にサーバー側でも同様にデータフェッチが行われ、HTMLの構築が行われます。次回以降のリクエストでは、サーバー側から完全なHTML(データも含まれた)が返されます。
とはいえ、非完全な状態のHTML(データが含まれていない)でクライアントに送られてしまうので、この部分が欠点ですね。この点も解決できます。
blocking
export async function getStaticPaths() { return { paths: [ // 省略 ], //ここ!! fallback: 'blocking' } }
fallback
の値にblocking
を入れることで、データが取得されていないページにアクセスした時、サーバーから不完全な状態でHTMLが送られるなんてことはなく、データフェッチが行われてからHTMLが構築され、クライアント側にHTMLが送られます。ISR(Incremental Static Regeneration)
export async function getStaticProps(hoge) { return { props: { hoge }, revalidate: 10, // ここを追加 } }
getStaticProps()
関数のreturn内にrevalidate
の値として任意の数字を入れてください。その秒数以降にリクエストがきた時に、サーバー側でデータフェッチを再度行い、HTMLを再構築します。ここでポイントになってくるのが、リクエストしたユーザーにはキャッシュしていたHTMLを返すということです。一定期間ごとにサーバーサイドレンダリングを行うことで、高速なページ描画を実現しています。こうすることにより、表示されるデータの更新頻度が高くても新しいデータが表示されるようになりました。
とはいえ、
ISR
は常に最新のものがユーザーに届けられるわけではありません。(最初のリクエスト時にはキャッシュされたHTMLが返されるので。)使い分けとしては下記のようなイメージで良いと思います。
- リクエスト時に最新の情報でなくても良い:
ISR
- リクエスト時に最新の情報が出てほしい:
SSR
かSWR
以上です?♂️
参考記事
- 投稿日:2021-01-20T17:02:33+09:00
switch / if を式として
はじめに
無職 やめ太郎(本名)氏の記事
4歳娘「パパ、constしか使わないで?」
のコメント欄にて、switch
やif
を式として構成する話が盛り上がっているようだった。
こういう話好き。ただ、記事の主旨はタイトル通り「変数を
const
のみにするには」と言うのが主眼なようなので、コメントではなく記事にしてみることにした。汎用的な条件分岐 (conditional branch) は Maybe と Either で構成できるよ、というお話。
若干関数型チックな雰囲気を感じるかもしれないけれど、モナモナ言い出さないので安心してほしい。Maybe (ある / なし)
まずは
Maybe
について。
値が「ある」場合と「ない」場合を表現する構造。
他言語だとOptional
と呼ばれることもしばしば。
ここでは単純に、要素数が 0 または 1 の配列、と言うことにしよう。type Nothing = []; type Just<A> = [A]; type Maybe<A> = Just<A> | Nothing;
Maybe
を扱う関数は色々考えられるけれど、ここではよく使うmaybe
関数を導入しよう。const maybe: <A, B, C>( m: Maybe<A>, f: (a: A) => B, v: C ) => B | C = (m, f, v) => '0' in m ? f(m[0]) : v;
Maybe
m
が値を持っていればそれに関数f
を適用し、そうでなければ代わりの値v
を返す。
使い方は以下のような感じ。usageconst greet = (maybeName: Maybe<string>) => 'ようこそ' + maybe(maybeName, name => name + '様', 'ゲストさん' ); console.log(greet(['ユーザー'])); // ようこそユーザー様 console.log(greet([])); // ようこそゲストさんこれだけでも、単純な分岐は表現できる。
Either (どちらか一方)
Either
は 2 種に場合分けをして、A の場合の値と B の場合の値、どちらか一方、というものを表現する構造。type Left<A> = { left: A }; type Right<B> = { right: B }; type Either<A, B> = Left<A> | Right<B>;
Left
/Right
を構築する関数と、値を取り出して関数を適用するeither
を定義しよう。const Left: <A>(left: A) => Left<A> = left => ({ left }); const Right: <B>(right: B) => Right<B> = right => ({ right }); const either: <A, B, X, Y>( e: Either<A, B>, f: (a: A) => X, g: (b: B) => Y ) => X | Y = (e, f, g) => 'left' in e ? f(e.left) : g(e.right);
Either
e
を受け取って、Left
であればf
を、Right
であればg
を適用する。使い方は、例えば以下のような感じ。
usageconst users = [ { id: 0, name: 'A' }, { id: 1, name: 'B' } ]; const getUser = (idOrName: Either<number, string>) => either(idOrName, id => users.find(x => x.id === id), name => users.find(x => x.name === name) ); console.log(getUser(Left(0))); // { id: 0, name: 'A' } console.log(getUser(Right('B'))); // { id: 1, name: 'B' }実際には、「計算結果」または「エラーメッセージ」など、例外処理的なものを扱うことが多い。
Conditional Branch (条件分岐)
さて、上記
Maybe
とEither
を組み合わせれば、
- 入力値を受け取って
- 複数の処理に分岐し
- 出力値を返す
といった汎用的な条件分岐を構成できる。
考え方としては、
- 未分岐のものは 入力値 (
X
) を持ったLeft<X>
- 分岐済みのものは 出力値 (
Y
) をもったRight<Y>
として扱う、というもの。
Either<X, Y>
が、条件分岐処理中の状態に相当する。まず、初期状態は
Left
(未分岐)。const start: <X>(input: X) => Either<X, never> = x => Left(x);次に、条件分岐は
X
を受け取ってMaybe
を返す関数として表現する。
分岐した場合にはJust
を、分岐しなかった場合にはNothing
を返す。状態(
Either
)と条件分岐(Maybe
を返す関数)から、次の状態を作る。const branch: <X, Y, V>( e: Either<X, Y>, f: (x: X) => Maybe<V> ) => Either<X, Y | V> = (e, f) => either(e, x => maybe(f(x), y => Right(y), Left(x)), y => Right(y) );入力となる
e
そして関数f
の戻り値によって 3 パターンの結果を生む。
e f(x) return Left(x) Just(y) Right(y) Left(x) Nothing Left(x) Right(y) N/A Right(y) 最後に、条件分岐処理の終端は、
X
を受け取って値を返す関数として表現する。状態(
Either
)と終端(値を返す関数)から、最終出力を得る。const otherwise: <X, Y, V>( e: Either<X, Y>, f: (x: X) => V ) => Y | V = (e, f) => either(e, x => f(x), y => y);
Either
がRight
(既に結果がある) ならばその値、
Left
(まだ結果が無い) ならば関数を適用した結果が戻り値となる。この三つの関数を使って例として FizzBuzz を書いてみると以下のような感じ。
const fizzbuzzTest1 = (n: number) => { const e0 = start(n); const e1 = branch(e0, n => n % 15 ? [] : ['FizzBuzz']); const e2 = branch(e1, n => n % 3 ? [] : ['Fizz']); const e3 = branch(e2, n => n % 5 ? [] : ['Buzz']); return otherwise(e3, n => n); };
Either
を状態としたステートマシンだと考えても良いかもしれない。こんな感じで汎用的な条件分岐が構成できる。
......といっても、TypeScript/JavaScript だと中間変数が必要になって明らかに書きにくいので、メソッドチェーンで書けるようにしよう。
Match クラス
クラスとファクトリを定義する。
名前はとりあえずMatch
/match
とした。
ついでにotherwise
は長いのでelse
にしてしまおう。class Match<X, Y> { private constructor(private e: Either<X, Y>) { } static from(): Match<void, never>; static from<X>(x: X): Match<X, never>; static from<X>(x?: X) { return new Match(start(x)); } branch<V>(f: (x: X) => Maybe<V>) { return new Match(branch(this.e, f)); } else<V>(f: (x: X) => V) { return otherwise(this.e, f); } } const match = Match.from;次のように書ける。
const fizzbuzzTest2 = (n: number) => match(n) .branch(n => n % 15 ? [] : ['FizzBuzz']) .branch(n => n % 3 ? [] : ['Fizz']) .branch(n => n % 5 ? [] : ['Buzz']) .else(n => n);おおよそ形にはなったけれど、三項演算子がまだ不格好なので、
barnch
を「条件」と「結果の生成」に分離しよう。
メソッド名はif
としてみる。class Match<X, Y> { /* 略 */ // 追加 if<V>(p: (x: X) => unknown, f: (x: X) => V) { return this.branch(x => p(x) ? [f(x)] : []); } }これでより「らしく」なる。
const fizzbuzz = (n: number) => match(n) .if(n => n % 15 === 0, _ => 'FizzBuzz') .if(n => n % 3 === 0, _ => 'Fizz') .if(n => n % 5 === 0, _ => 'Buzz') .else(n => n);Switch のようなもの
条件式に、関数ではなく入力値と比較する値を渡すものを定義しよう。
名前はcase
にする。class Match<X, Y> { /* 略 */ // 追加 case<V>(p: X, f: (x: X) => V) { return this.branch(this.e, x => x === p ? [f(x)] : []); } }これで
switch
に似た扱いができるようになる。const switchLike = (n: number) => match(n) .case(0, _ => 'Zero') .case(1, _ => 'One') .else(_ => 'More'); // defaultIf のようなもの
入力値を無視すれば普通の
if
-else if
-else
と同じような書き方もできる。const ifLike = (value: number) => match() .if(_ => value > 6, _ => 'High') .if(_ => value > 3, _ => 'Middle') .else(_ => 'Low');もっとこんなユーティリティもあるよ!とか、カリー化しようぜ!とか、まあ、使う人によって色々ありそうだけれど、そこら辺はお好みでどうぞ。
長いおまけ (改良版)
さて、本筋としてはこれで終わりなのだけれど、上記実装はやや物足りない。
まず、素直に
Maybe
/Either
を使って構成しているので、中間オブジェクトを色々と生成してしまっている。
コスト的に気に掛けるほどのものではないが、不要なオーバーヘッドは可能ならば省きたい所。二点目、TypeScript 限定の話として、型ガードが効かない。
例えば以下のようなケース。
const isString = (x: unknown): x is string => typeof x === 'string'; const strOrNum = (x: string | number) => match(x) .if(isString, s => 'String:' + s) .else(n => 'Number:' + n);
if
内のs
はstring
、else
内のn
はnumber
、
と型推論してほしいところだが、残念ながらstring | number
のままである。列挙型の漏れチェックなんかもできないので、どげんかせんといかん。
というわけで、2 点考慮して、ついでにユーティリティなども追加して書き直したのが以下。
やっていることは、
- Left / Right それぞれをクラス化, Maybe 周りはメソッド内で処理
- 型ガードが効くようにメソッドオーバーロードで受け、入力値の型から処理済みの型を除外
- 型だけ変わる部分で自分自身を返し、不要なオブジェクト生成を回避
- 他ユーティリティの追加
といった具合。
match.tstype BooleanLike = unknown; type Throwable = unknown; export interface Match<X, Y> { else<V>(f: (x: X) => V): Y | V; branch<V>(f: (x: X) => [] | [V]): Match<X, Y | V>; // same as .branch(x => p(x) ? [f(x)] : []) if<V, T = X>(p: (x: X | T) => x is T, f: (x: T) => V): Match<Exclude<X, T>, Y | V>; if<V>(p: (x: X) => BooleanLike, f: (x: X) => V): Match<X, Y | V>; // same as .branch(x => p(x) ? [v] : []) ifValue<V, T = X>(p: (x: X | T) => x is T, v: V): Match<Exclude<X, T>, Y | V>; ifValue<V>(p: (x: X) => BooleanLike, v: V): Match<X, Y | V>; // same as .branch(x => x === t ? [f(x)] : []) case<V, T extends X = X>(t: T, f: (x: T) => V): Match<Exclude<X, T>, Y | V>; case<V>(t: X, f: (x: X) => V): Match<X, Y | V>; // same as .branch(x => x === t ? [v] : []) caseValue<V, T extends X = X>(t: T, v: V): Match<Exclude<X, T>, Y | V>; caseValue<V>(t: X, v: V): Match<X, Y | V>; // same as .else(x => v) elseValue<V>(v: V): Y | V; // same as .else(x => { throw newError(x) }) throw(newError?: (x: X) => Throwable): Y; // same as .else(x => { throw t }) throwValue(t: Throwable): Y; // covering check unreachable(this: Match<never, Y>): Y; } export function match(): Match<void, never>; export function match<X>(x: X): Match<X, never>; export function match<X>(x?: X): Match<X | void, never> { return new MatchLeft(x); } const left: <X, Y, T>(m: MatchLeft<X, Y>) => Match<Exclude<X, T>, Y> // = m => new MatchLeft(m._ as Exclude<X, T>); // @ts-ignore ** reinterpret cast to reduce runtime cost ** = m => m; const right: <X, Y>(y: Y) => Match<X, Y> = y => new MatchRight(y); class MatchLeft<X, Y> implements Match<X, Y> { readonly _: X; constructor(target: X) { this._ = target; } // terminates else<V>(f: (x: X) => V) { return f(this._); } elseValue<V>(v: V) { return v; } throw(f?: (x: X) => Throwable): Y { throw f ? f(this._) : new Error('match error: ' + this._); } throwValue(t: Throwable): Y { throw t; } unreachable(): never { throw new Error('match error: ' + this._); } // non-terminates branch<V>(f: (x: X) => [] | [V]): Match<X, Y | V> { const r = f(this._); return '0' in r ? new MatchRight(r[0]) : this; } if<V, T>(p: (x: X | T) => x is T, f: (x: T) => V): Match<Exclude<X, T>, Y | V> { return p(this._) ? right(f(this._)) : left(this); } ifValue<V, T>(p: (x: X | T) => x is T, v: V): Match<Exclude<X, T>, Y | V> { return p(this._) ? right(v) : left(this); } case<V, T extends X>(t: T, f: (x: T) => V): Match<Exclude<X, T>, Y | V> { return this._ === t ? right(f(t)) : left(this); } caseValue<V, T extends X>(t: T, v: V): Match<Exclude<X, T>, Y | V> { return this._ === t ? right(v) : left(this); } } class MatchRight<X, Y> implements Match<X, Y> { readonly _: Y; constructor(result: Y) { this._ = result; } // terminates else() { return this._; } elseValue() { return this._; } throw() { return this._; } throwValue() { return this._; } unreachable() { return this._; } // non-terminates branch() { return this; } if() { return this; } ifValue() { return this; } case() { return this; } caseValue() { return this; } }先ほどの例もちゃんと推論できるようになる(なんでもできるわけではないが)。
const isString = (x: unknown): x is string => typeof x === 'string'; // OK const strOrNum = (x: string | number) => match(x) // Match<string | number, never> .if(isString, s => 'String:' + s) // Match<number, string> .else(n => 'Number:' + n); // NG これはタイプガードであると見抜いてくれない const strOrNum2 = (x: string | number) => match(x) .if(x => typeof x === 'string', s => 'String:' + s) .else(n => 'Number:' + n); // 一応明示すれば OK (長いが) const strOrNum3 = (x: string | number) => match(x) .if((x: any): x is string => typeof x === 'string', s => 'String:' + s) .else(n => 'Number:' + n);いろいろ組み合わせた場合の挙動。
const isNumber = (x: unknown): x is number => typeof x === 'number'; const isString = (x: unknown): x is string => typeof x === 'string'; const getTime = (dateLike: number | string | Date): number => match(dateLike) // Match<number | string | Date, never> .case('now', _ => Date.now()) // Match<number | string | Date, number> .if(isString, Date.parse) // Match<number | Date, number> .if(isNumber, x => x) // Match<Date, number> .else(date => date.getTime());漏れチェックもできるように。
unreachable
メソッドは入力型がnever
でない時、コンパイルエラーとなる。enum MyEnum { Hoge, Foo, Bar } const test1 = (x: MyEnum): string => match(x) // Match<MyEnum, never> .case(MyEnum.Hoge, _ => 'Hoge') // Match<MyEnum.Foo | MyEnum.Bar, string> .case(MyEnum.Foo, _ => 'Foo') // Match<MyEnum.Bar, string> .case(MyEnum.Bar, _ => 'Bar') // Match<never, string> .unreachable(); // ok const test2 = (x: MyEnum): string => match(x) // Match<MyEnum, never> .case(MyEnum.Hoge, _ => 'Hoge') // Match<MyEnum.Foo | MyEnum.Bar, string> .case(MyEnum.Foo, _ => 'Foo') // Match<MyEnum.Bar, string> .unreachable(); // tsc error // 型 'Match<MyEnum.Bar, string>' の 'this' コンテキストを // 型 'Match<never, string>' のメソッドの 'this' に割り当てることはできません。 // 型 'MyEnum' を型 'never' に割り当てることはできません。まあ、このくらいできればある程度は使い物になろう。
おわりに
この手のもの、書いても結局パフォーマンス気にしてグルーコードくらいでしか使わない人なので、やっぱり言語サポートが欲しい。(ちゃぶ台返し)
それはそれとして、この手の話はやっぱり楽しい。
- 投稿日:2021-01-20T14:15:05+09:00
notistack で複数のポップアップを同時に表示する
はじめに
notistack は Material UI の
Snackbar
コンポーネントをラップしたライブラリでうす。
添付した画像のように、いくつかのカラーパターンを出し分けることができます。
また、notistack の最大の特徴は、ポップアップを積み重ねて表示できることです。
スタイルをカスタマイズする
notistack@1.0.0
でいくつかの breaking change がありましたが、私の環境はnotistack@0.9.17
を利用しています。
notistack
からプロバイダーと Custom Hooks をインポートします。import { SnackbarProvider, useSnackbar } from 'notistack'; ...
SnackbarProvider
は下記のように使います。<SnackbarProvider iconVariant={{ success: <SuccessIcon /> error: <ErrorIcon /> }} anchorOrigin={{ vertical: 'top', horizontal: 'center' }} ContentProps={{ style: { minWidth: 0, height: 33, alignContent: 'center' }} className: useCustomStyle().variantDefault, }} classes={useStayle()} > {children} </SnackbarProvider>スタイルは
@material-ui/core
のmakeStyles
を用います。import { makeStyles } from '@material-ui/core'; ... const useCustomStyles = makeStyles((theme) => ({ variantDefault: { backgroundColor: 'black' }, })); const useStyles = makeStyles((theme) => ({ variantSuccess: { backgroundColor: 'green' }, variantError: { backgroundColor: 'red' }, containerAnchorOriginTopCenter: { top: 12, }, collapseWrapper: { marginTop: 4, marginBottom: 4, }, }));今回の例では
- variantSuccess, variantError で
variant
の値によってスタイルを変えていますcontainerAnchorOriginTopCenter
で上からの余白を設定していますcollapseWrapper
で、個々のSnackbar
に余白を設定しています注意すべき点として、
classes
にはvariantDefault
が存在しません。
→ 参考
そのため、className
として設定してあげる必要があります。
このようにして、Snackbar
を自分が使いやすいようにカスタマイズできました。※ 1.0.0 からは
ContentProps
がなくなっているので設定方法が異なります。ポップアップを表示する
Snackbar
を表示させる際は、Custom Hooks を利用します。const { enqueueSnackbar, closeSnackbar } = useSnackbar(); ... const key = enqueueSnackbar(message, options); closeSnackbar(key);
enqueSnackbar
で取得したkey
を渡すことで対象のポップアップを消すことができます。
表示の際には個別にオプションを渡すことも可能です。
これにより、個々のポップアップに違いを出すことができます。おわりに
ユーザの目を引くポップアップを出すシーンは多いと思います。
例えばエラーですが、複数のエラーが出た場合にはすべてを一度に見せてあげたほうがユーザフレンドリーかもしれません。Material UI の Snackbar をきれいに整列して表示することができる
notisnack
。
もし良ければ使ってみてください。
- 投稿日:2021-01-20T14:14:07+09:00
さて、驚くほど簡単にreCapthca認証でBOT、スパム対策を行おう!!
ログインフォームや会員登録を行うフォームにて、色々対策したいなと思い調べていると、Googleが提供しているreCaptchaというものを使うことで対策が出来るらしい。
誰しもが1回は見たことのある、「私はロボットではありません」てきなやつ。
ただ、画像をクリックしたりあの分かりづらい文字列を入力したり色々と面倒。。。
そこで、GoogleはこれらをやらなくてもユーザーがBotやスパムではないと判断する最新「v3」を今提供している。
イメージとしてはこんな感じ
ページの右下にこのようなマークが表示され常にユーザーが人間らしい動きをしているかを確認している。
流石Googleさん!
今回はこのreCaptchaの簡単な使い方を説明していこうと思います!!
調べてみてわかったことが、割とドキュメントが少なく初めての人にとって分かりやすい記事がまりなあったので、書いてみようと思いました。
初めての人に向けて、書いていくのであまり詳しい説明は致しません。(詳しいことは分からない)
なので、reCaptchaを搭載したいという方はぜひ見て下さい!
それでは、一緒に説明を見ていきましょう!!!
Google reCaptchaの登録
まずは、「Google reCaptcha」に登録します。
詳しいやり方は、他の方が説明しているのでそちらを参考にして頂くと良いかと。
ちなみに僕は下記の記事を参考にして登録を行いました。
Google reCAPTCHAのWEBサイト登録とAPIキーの取得方法
jsファイルの読み込み
次に、サイトキーをコピーして、
head
タグに以下のスクリプトファイルの読み込みを行ってください。※注意
body
タグではなくhead
タグです!!!間違えないように!<script src="https://www.google.com/recaptcha/api.js?render={SITE_KEY}" async defer></script>
{SITE_KEY}
にコピーしたサイトキーをペーストしてください。トークンの呼び出し
最後に、認証を行いたいページにてTokenの発行を行います。
トークンの有効時間は2分であるため、入力フォームで多くの入力事項を記載する際は、ページに訪れた時ではなく、例えば「次へ」のボタンを押したときなどにトークンを発行してください。
トークンを呼び出す関数は以下のようになります。
index.jswindow.grecaptcha.ready(() => { window.grecaptcha.execute(process.env.VUE_APP_SITE_KEY, { action: '/signup' }).then(async (token) => { console.log('token') }) })このようにして、Tokenを発行することができます!
ロゴの消去(番外編)
ページ右下の認証ロゴって結構邪魔ですよね。
なので、ロゴを消去したいページでは以下のようなCSSを設定してあげてください。
.grecaptcha-badge { display: none }また、ページによってロゴを消したり表示する場合はこちらの記事にVueRouterで動的にpathを取得(検知)する方法詳しく書いてあるので、ぜひ興味のある方はご覧ください。
今回はこの辺で終わりにしたいと思います。
reCaptchaはまだこれで終わりではないです。
ここで発行したトークンが本当に正しいのかの判断を行わなければいけません。
そして、例えばトークンが正しかったらログインを実行し、正しくなければエラーメッセージを表示するといった形になります。
ただ、これを一気にやってしまうと頭がパンクしてしまうので、まずはこのトークンがちゃんと発行できているかを確認してから次の段階に進んでください。
近々、Laravelでトークンの認証を行う記事を書くのでぜひそちらもご覧ください。
以上、「さて、驚くほど簡単にreCapthca認証でBOT、スパム対策を行おう!!」でした!
良かったら、LGTM、コメントお願いします。
また、何か間違っていることがあればご指摘頂けると幸いです。
他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!
Thank you for reading
- 投稿日:2021-01-20T13:33:10+09:00
AR.jsのLocation Basedで遠くにモデルを表示させようとして困った話。
1.ARで遠くのランドマークをアイコンで表示したい
ARで遠くのランドマークをスマホに表示させたいとの希望がありました。
アクティブなユーザー数は、月100以下を想定していたので、有料のライブラリは無理だなぁってことで、「AR.js」で作成することになりました。AR.jsは、Marker Tracking、Image Tracking、Location Basedに対応している優れもので、htmlに数行のコードでよしなにARを実装してくれます。
Location Based ARは、GPSの位置情報に基づいて指定の位置にコンテンツを表示させる仕組みです。今回はAR.jsのLocation Based とA-Frameバージョンを使用することにしました。
希望としては、こんな感じです。
実際には、ピンの代わりにランドマークのアイコン等を表示させます。2.サンプルコードで検証
AR.jsのドキュメントに記載されているサンプルを少し変えて試します。
サンプルでは、文字を表示しますが、今回は赤いBOXを表示させてみます。
場所は北海道、札幌の時計台にしました。コードは以下です。
非常に簡単ですね。
<a-scene>
の中で<a-box>
でBOXを表示させます。
今回は、コードの詳細解説は本筋でないのでいたしません?。<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>GeoAR.js demo</title> <script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script> <script src="https://unpkg.com/aframe-look-at-component@0.8.0/dist/aframe-look-at-component.min.js"></script> <script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar-nft.js"></script> </head> <body style="margin: 0; overflow: hidden;"> <a-scene vr-mode-ui="enabled: false" embedded arjs="sourceType: webcam; debugUIEnabled: false;" > <a-box material="color: red" gps-entity-place="latitude: 43.062533; longitude: 141.353638;" scale="50 50 50" ></a-box> <a-camera gps-camera rotation-reader></a-camera> </a-scene> </body> </html>あれ?全然表示されないやん?
なぜかBOXは表示されません。
小さすぎるのかな?と思いscaleを大きくしても変わりません。
色々と試行錯誤するも改善されません。自分のiPhoneが壊れてるのか?などど時間を費やし・・・・。
・・・・・・
本家のissuesにありました。おおよそ1km以上遠方のオブジェクトは表示されない。
えぇ? マジですか?
洒落にならないんですけど。
3.AR.jsのLocation Basedは、1km以上離れたオブジェクトは表示されない!
困りました。
そしてドキュメントをよく読むと、こんな記述が。
引用元:AR.js Documentation
Viewing every distant object
If your location-based AR content is distant from the user (around 1km or more), it is recommended to use the new arjs-webcam-texture component (introduced in AR.js 3.2.0), which uses a THREE.js texture to stream the camera feed and allows distant content to be viewed. This component is automatically injected if the videoTexture parameter of the arjs system is set to true and the sourceType is webcam. For example (code snippet only):おい、おい、おいおい、解決策があるじゃぁないか!
良かったぁ。これを読むと
<a-scene vr-mode-ui="enabled: false" embedded arjs="sourceType: webcam; videoTexture: true; debugUIEnabled: false;" >videoTexture: true;を設定すれば大丈夫だそうです。
?設定して、同じコードで試します。
4.確かにオブジェクトは表示されたけど、iPhoneだと画面がフリーズ?。
オブジェクトは、確かに表示されましたけど、カメラが起動した時の景色でフリーズします。
ここでのフリーズとは画面のビューが、どちらにカメラを向けての最初の画面から変わらない状態を指します。これはARコンテンツをビデオ画面にオーバーレイで表示させてるような感じかと。
ログを見るとiOS14だとビデオのオートプレイはユーザーの明示的な許可が必要なため、パーミッションではじかれて、画面が最初の景色で固まってしまうのでは?と推測。もうこれじゃぁ、ARでも何でもないよね。
詰みましたよね。これ。
さてどうしよう。
5.邪道だけれど、全てのオブジェクトを1km以内に表示させればいいのでは。
次回は、指定地点の方角と距離を求めて、1km以内に再配置させようと思います。
③の自分の位置から、①の実際位置との方角を求めて、表示可能な位置②に再配置させる計画です。ひょっとしたら他に良い解決策があるのかもしれないけど。
次回は、位置を再計算させて調整するプランを書いていきます。
- 投稿日:2021-01-20T11:15:01+09:00
[TypeScript]mapの書き方
// 以下2つの書き方は同じ結果を得られる const list:Hogehoge[] = array.map(item => function(item)); const list:Hogehoge[] = array.map(function);
- 投稿日:2021-01-20T10:52:36+09:00
初めて使うReact入門
はじめに
こんにちわ。
この記事は、開発経験は手続き型を少々なプロダクトオーナーが「りあクト! TypeScriptで始めるつらくないReact開発 第3版」を読んで、四苦八苦しながらReactを使えるようになろうと苦戦しながら勉強したものを備忘録したものです。
1回では終わらないので何回か続くと思ので、お付き合いください。因みに、この記事では端折っていますが、本の中では何故そのような技術を使っているのかという背景を踏まえて語られており、非常に理解しやすい内容です。ただ、動かすだけとは段違いです。
少しでもこの記事を見て面白そうだと思った方は、是非とも一度読んでみることをお勧めします。普段は、TESTRUCTUREというテスト設計を支援するツールを作っていたりします。
実施環境
- Windows 10 pro
- core i7-8850H
- Visual Studio Code
- Ubuntu 20.04 LTS
環境準備
まずは、環境を準備します。
Node.jsのインストール
nodenvのインストール
本ではMacなのでこちらも合わせて参考にインストール。
まずは、anyenvをインストールします。
bashgit clone https://github.com/riywo/anyenv ~/.anyenv echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.bash_profile echo 'eval "$(anyenv init -)"' >> ~/.bash_profile anyenv install --init exec $SHELL -lanyenvのインストールが完了したら、nodenvをインストールします。
bashanyenv install nodenv exec $SHELL -l続いて、プラグインをインストールします。
bashmkdir -p $(anyenv root)/plugins git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update mkdir -p "$(nodenv root)"/plugins git clone https://github.com/nodenv/nodenv-default-packages.git "$(nodenv root)/plugins/nodenv-default-packages" touch $(nodenv root)/default-packages/.anyenv/envs/nodenv/plugins/nodenv-default-packagesここにあるdefault-packagesのファイルの中身を以下にする。
yarn typescript ts-node typesyncNode.jsのインストール
これでようやく準備が整ったので、Node.jsをインストールします。
bashnodenv install -l nodenv install 14.4.0 nodenv global 14.4.0以上で、インストールが完了です。
Reactを動かしてみる
まず、お試しで、Hallo Worldしてみる。
bashnpx create-react-app hello-world --tempate typescript
インストールが完了したら、次のコマンドで動作を見る。
bashcd hello-world yarn start
http://localhost:3000 にブラウザでアクセスすると動いてるのが確認できます。
動いているのが見れると少し感動。
「Edit src/App.js and save to reload.」と書いてある通り、
App.jsを編集すると編集した内容を見ることができます。Tutorial: Intro to Reactをやってみる
Hollo worldにある「Learn React」を押すとチュートリアルに案内されるので折角なのでやってみる。
Tutorial: Intro to React最終的には、以下のファイルを書き換えて保存して、更新してみると、〇×ゲームが完成しています。
ここまでは、書いてある通りにやっていくので、途中は端折ります。
追加課題
そして、追加課題があるのでいくつかやってみようと思います。
追加課題を仕様変更だととらえると、各課題という名の仕様変更は、Reactのコンポーネントベースな考え方が、機能単位で分割されていて、それぞれが独立性が高く、ソースコードをいじりやすい気がしました。各移動の場所を移動履歴リストに(col, row)形式で表示します。
どこのボタンを押したかを判別するために押した場所を記憶するstateを追加します。
また、移動履歴リストは、gameクラスのrenderに書かれているので、次ように書き換えます。
正方形は次のように配置されているので、
0 1 2 3 4 5 6 7 8 col:3で割った余りを記載します。
row:3で割った商を記載します。index.jsclass Game extends React.Component { constructor(props) { super(props); this.state = { history: [ { squares: Array(9).fill(null), } ], onClickHistory: [ { onClickNum: 0, } ], stepNumber: 0, xIsNext: true }; } handleClick(i) { const history = this.state.history.slice(0, this.state.stepNumber + 1); const current = history[history.length - 1]; const squares = current.squares.slice(); const onClickHistory = this.state.onClickHistory.slice(0, this.state.stepNumber + 1); if (calculateWinner(squares) || squares[i]) { return; } squares[i] = this.state.xIsNext ? "X" : "O"; this.setState({ history: history.concat([ { squares: squares } ]), onClickHistory: onClickHistory.concat([ { onClickNum: i } ]), stepNumber: history.length, xIsNext: !this.state.xIsNext }); } ... render() { const history = this.state.history; const current = history[this.state.stepNumber]; const winner = calculateWinner(current.squares); const historyNum = this.state.onClickHistory; const moves = history.map((step, move) => { const col = historyNum[move].onClickNum % 3; const row = (historyNum[move].onClickNum - col) / 3; const desc = move ? 'Go to move #' + move + ' (' + col + ',' + row + ')' : 'Go to game start'; return ( <li key={move}> <button onClick={() => this.jumpTo(move)}>{desc}</button> </li> ); }); ... }移動リストで現在選択されている項目を太字にします。
太字のスタイルを追加します。
index.cssbutton.button1 { font-weight: bold; }gameクラスのrenderで返す値を、現在選択されたものと一致している場合は、太字のボタンを使用するように指定します。
index.jsrender() { const history = this.state.history; const current = history[this.state.stepNumber]; const winner = calculateWinner(current.squares); const historyNum = this.state.onClickHistory; const moves = history.map((step, move) => { const col = historyNum[move].onClickNum % 3; const row = (historyNum[move].onClickNum - col) / 3; const desc = move ? 'Go to move #' + move + ' (' + col + ',' + row + ')' : 'Go to game start'; return move === this.state.stepNumber ? ( <li key={move}> <button class="button1" onClick={() => this.jumpTo(move)}>{desc}</button> </li> ) : ( <li key={move}> <button onClick={() => this.jumpTo(move)}>{desc}</button> </li> ); });誰も勝てない場合は、引き分けの結果についてのメッセージを表示します。
勝利ステータスの処理のところで、勝者が決まっておらず、既に9回動いていた場合は、drawを表示してあげるようにします。
index.jslet status; if (winner) { status = "Winner: " + winner; } else if (this.state.stepNumber === 9) { status = "draw"; } else { status = "Next player: " + (this.state.xIsNext ? "X" : "O"); }まとめ
ここまでで、最低限のReactの環境構築と、ソースコードがかけるようになりました。
今後は、中身の要素技術や、応用的な使い方について触れたいと思います。
- 投稿日:2021-01-20T06:07:59+09:00
任意の文字列を出力するなるべく短いBrainf*ckの生成 [JavaScript]
作り終えて暫くするとコード見返してもわからなくなるので備忘録としてここに残すことにした
初投稿なのでかなり見にくいと思います
申し訳ないです…先にこちら実物です https://mcbeeringi.github.io/amuse/bf.html
[21/01/20 14:31]追記
コードに不備があったので改良しました動機
Twitterで突然リプにBrainf*ckがやって来たので
なんとかしてBrainf*ckでスマートに秒で返せるようにしたかった設計
メモリは 255の次が0つまり8bit 書き込める数は無限 とする
アルゴリズム
当時(今も)自身が手書きする際のアルゴリズムをそのまま書き起こすことにした
(もちろん最善とは限らないです)
計算はcpuが頑張ってくれるのでいくらでも試行させられる素晴らしいこの手のコードを生成する↓
++[>++>---<<-]>+.--.大体の流れ
1.ASCIIで近い文字同士をグループにまとめる
2.それぞれのグループで一番最初の文字になるべく近い数の最大公約数を求める
3.最初のループ+端数調整→出力変数は二つ
「近い文字同士」 ……8bitなのでその半分未満→0~127
「なるべく近い数の最大公約数」 ……探索でゴリ押す…?
この二つの数で長さが決定されるポインタの移動によるコスト
ループの後調整をしながら出力をする段階でももちろんポインタは移動する
これに直接影響を与えるのがグループの並び順
これをなんらかの方法で最適化することで最終的な文字数の削減が見込めるインデックス並び替えとでも呼ぼうか
この処理を1と2の間に挟む具体的には
隣接するグループインデックスの組み合わせを頻度順に書き出して
頻度が高いものから結晶が成長するようなイメージで一列に繋げて
繋げられなかったグループ番号を付け足して
既存のグループインデックスをそれで置き換える
という流れ見るからに大変
ここで一番苦労したなおこれでは多数派のインデックスが優先されてしまい
他の少数派の登場回数が多数派を上回っていても無視されるので
おそらく最善ではないひたすら書く
グループにまとめる
最初にBfに出力させたい文字列 s を用意する
このあとこの文字列を生で使う予定もないのでここでは配列で置き換える// s='Hello World!' s=s.split('').map(x=>x.charCodeAt(0)).filter(x=>x<256);// ASCII以外は無視 console.log('raw',s); // >> "raw", [72,101,108,108,111,32,87,111,114,108,100,33]
これを適当な閾値 md(max distance) を設けて0-1=255があることに注意しながらグループ分けする
全体的に変数の名前の付け方がひどいです申し訳ない// md=10; var ind=[0], // それぞれの文字がどのグループに属しているかのindex clm=[[s[0]]], // グループを二次元の配列に入れる (column 直訳で列) // 0グループの最初は一番最初の文字を入れておく tmp={}; // テンポラリ s.slice(1).forEach((x,i)=>{i++; // 最初の文字を切ってforEachで回す tmp.mini=-1; // 最終的に一番近いグループインデックスをここに書く (minimum index) tmp.mind=Number(md)+1; // その距離 これ未満のときにこれとtmp.miniを更新 clm.forEach((y,j)=>{ // 各グループに対して tmp.last=y[y.length-1]; // そのグループの最後の値 tmp.dist=Math.abs(tmp.last-x); // との距離 tmp.dist=Math.min(tmp.dist,256-tmp.dist); //逆回りも考慮 if(tmp.dist<tmp.mind){ // 距離が今までより短かったら更新 tmp.mind=tmp.dist; tmp.mini=j; } }); if(tmp.mini==-1){ // 属せるグループがない場合は新しいグループを作成 ind[i]=clm.length; clm[ind[i]]=[x]; }else{ // 属せるグループがある場合はそこに入れる ind[i]=tmp.mini; clm[ind[i]].push(x); } }); console.log('index',ind); console.log('data',clm); // >> "index", [0,1,1,1,1,2,3,1,1,1,1,2] // >> "data", [[72],[101,108,108,111,111,114,108,100],[32,33],[87]]インデックス並び替え
インデックス並び替えの処理を作る
と言ってもいきなりは辛いのでゴールの確認から
専用の関数 freq を作ってインデックスのみを投げて最適化してもらう// 現状の確認 console.log('index',ind); console.log('data',clm); var freq_=freq(ind); // 最適化したインデックス ind=ind.map(x=>freq_.indexOf(x)); // 置換する clm=freq_.map(x=>clm[x]); // グループごと並び替える //最適化後 console.log('index',ind); console.log('data',clm); //もう一度最適化をする意味がないことの検証 console.log(freq(ind)); // これは0,1,2…になるはず肝心のfreq関数
もいきなりは辛いので形から
頻度順に並び替えた組を再び別の関数に投げる
ちなみに二桁以上のインデックスがある場合はうまく機能しないが
下手な閾値を設定しない限りそこまで大量のインデックスが来ることもないので
直しましたconst freq=inp=>{ var inpm=[...new Array(Math.max(...inp)+1).keys()]; // 0~インデックスの最大値の配列 if(inpm.length<3){ //インデックスが十分に少ないときは無駄な操作になるのでそのまま返す console.log('freq skipped',inpm); return inpm; } // 全隣り合わせを列挙してゾロ目カットと並び順が逆なものは昇順に書き換え var arr=inp.map((x,i,c)=>{if(c[i+1])return[x,c[i+1]];}) .filter(x=>x&&x[0]!=x[1]).map(x=>x[0]>x[1]?[x[1],x[0]]:x); // 頻度の計測 (海外のページ参考 元ページ見つかりませんでした…) var fq={};arr.forEach(x=>fq[x]=0); var s=arr.filter(x=>++fq[x]==1); s.sort((a,b)=>fq[b]-fq[a]); // s に頻度が高かった組み合わせから重複なく入っている // 頻度をもとに繋げる var tmp,out=[]; while(true){ tmp=joinPair(s); // return [ 繋げた結果, 繋げられなかった組 ]; //(1組だけ渡したときはつなげられたものとして返す) s=tmp[1]; // 繋げられなかったものはもう一度試す out=out.concat(tmp[0]); if(tmp[1].length==0)break; } // 途中で欠けたインデックスを補完する out=out.concat(inpm.filter(x=>out.indexOf(x)==-1)); console.log('freq',out); return out; }渡された組を繋げる関数
joinPairを作る
何をもって成功とするかが難しいconst joinPair=x=>{ var indm=Math.max(...(x.join('').split(''))); // 最大値 console.log('joinPair',x,indm); // 一次元の座標x=indmに原点を作ってそこに最頻の組を置く var ind=new Array(indm+1).fill(null), // どの位置にどのインデックスが入ったか s=new Array(indm).concat(x[0].split('')); // すでに配置した最頻は消去 x.shift(); ind[s[indm]]=indm; // 最初の組の位置を記録 ind[s[indm+1]]=indm+1; // 組同士を繋げる var tmp,flag=true; while(flag){ flag=false; x=x.map((e,i)=>{ if(ind[e[0]]&&!ind[e[1]]){ if(!(s[ind[e[0]]+1]+1)){s[ind[e[0]]+1]=e[1];ind[e[1]]=ind[e[0]]+1;}//ok else if(!(s[ind[e[0]]-1]+1)){s[ind[e[0]]-1]=e[1];ind[e[1]]=ind[e[0]]-1;}//ok }//doom else if(!ind[e[0]]&&ind[e[1]]){ if(!(s[ind[e[1]]+1]+1)){s[ind[e[1]]+1]=e[0];ind[e[0]]=ind[e[1]]+1;}//ok else if(!(s[ind[e[1]]-1]+1)){s[ind[e[1]]-1]=e[0];ind[e[0]]=ind[e[1]]-1;}//ok }//doom else if(!ind[e[0]]&&!ind[e[1]])return e;//pass //else if(ind[e[0]]&&ind[e[1]])//doom flag=true;console.log(e,s,ind); }); // 繋げられなかった組も再配置できるか試す x=x.filter(y=>y); console.log(x); } // nullの除去 +1で0抜け回避 return [s.filter(e=>e+1),x]; }以上3つを繋げるとこんなかんじ
ここでの例はHello World!ではないです(短すぎる)
長いほど威力を発揮しますconst freq=inp=>{ const joinPair=x=>{ var indm=x.reduce((a,y)=>Math.max(y[0],y[1],a||0)); console.log('joinPair',x,indm); var ind=new Array(indm+1).fill(null),s=new Array(indm).concat(x[0]); x.shift();ind[s[indm]]=indm;ind[s[indm+1]]=indm+1; var flag=true; while(flag){ flag=false; x=x.map((e,i)=>{ if(ind[e[0]]&&!ind[e[1]]){ if(!(s[ind[e[0]]+1]+1)){s[ind[e[0]]+1]=e[1];ind[e[1]]=ind[e[0]]+1;}//ok else if(!(s[ind[e[0]]-1]+1)){s[ind[e[0]]-1]=e[1];ind[e[1]]=ind[e[0]]-1;}//ok }//doom else if(!ind[e[0]]&&ind[e[1]]){ if(!(s[ind[e[1]]+1]+1)){s[ind[e[1]]+1]=e[0];ind[e[0]]=ind[e[1]]+1;}//ok else if(!(s[ind[e[1]]-1]+1)){s[ind[e[1]]-1]=e[0];ind[e[0]]=ind[e[1]]-1;}//ok }//doom else if(!ind[e[0]]&&!ind[e[1]])return e;//pass //else if(ind[e[0]]&&ind[e[1]])//doom flag=true;console.log(e,s,ind); }); x=x.filter(y=>y);console.log(x); } return [s.filter(e=>e+1),x]; } var inpm=[...new Array(Math.max(...inp)+1).keys()]; if(inpm.length<3){console.log('freq skipped',inpm);return inpm;} var arr=inp.map((x,i,c)=>{if(c[i+1])return[x,c[i+1]];}) .filter(x=>x&&x[0]!=x[1]).map(x=>x[0]>x[1]?[x[1],x[0]]:x); //freq sort var fq={};arr.forEach(x=>fq[x]=0); var s=arr.filter(x=>++fq[x]==1); s.sort((a,b)=>fq[b]-fq[a]); //arr var tmp,out=[]; while(true){ tmp=joinPair(s);s=tmp[1];out=out.concat(tmp[0]); if(tmp[1].length==0)break; } out=out.concat(inpm.filter(x=>out.indexOf(x)==-1));console.log('freq',out); return out; } console.log('index',ind);console.log('data',clm); var freq_=freq(ind);ind=ind.map(x=>freq_.indexOf(x));clm=freq_.map(x=>clm[x]); console.log('index',ind);console.log('data',clm);console.log('test',freq(ind)); // >> "index", [0,1,1,1,1,1,0,2,1,1,0,3,3,0,1,0,0,4,3,2,0,1,0,3,5] // >> "data", [[99,101,103,105,100,101,105,100],[111,110,115,111,108,108,111,110,110],[46,44],[40,39,39,41],[120],[59]] // >> "joinPair", [[0,1],[0,3],[0,2],[1,2],[0,4],[3,4],[2,3],[3,5]], 5 // >> [0,3], [null,null,null,null,3,0,1], [5,6,null,4,null,null] // >> [0,2], [null,null,null,null,3,0,1], [5,6,null,4,null,null] // >> [1,2], [null,null,null,null,3,0,1,2], [5,6,7,4,null,null] // >> [0,4], [null,null,null,null,3,0,1,2], [5,6,7,4,null,null] // >> [3,4], [null,null,null,4,3,0,1,2], [5,6,7,4,3,null] // >> [2,3], [null,null,null,4,3,0,1,2], [5,6,7,4,3,null] // >> [3,5], [null,null,null,4,3,0,1,2], [5,6,7,4,3,null] // >> [] // >> [] // >> "freq", [4,3,0,1,2,5] // >> "index", [2,3,3,3,3,3,2,4,3,3,2,1,1,2,3,2,2,0,1,4,2,3,2,1,5] // >> "data", [[120],[40,39,39,41],[99,101,103,105,100,101,105,100],[111,110,115,111,108,108,111,110,110],[46,44],[59]] // >> "test", [0,1,2,3,4,5]やっとインデックス並べ替えができました
慰労困憊
結晶が育つみたいで綺麗ですね()最大公約数なのか…?
各グループの最初の数を集めて
tmp=clm.map(x=>x[0]);これでうまい具合にループで作るのでした
ここで問題
Q.
0~255までの数A,B,C,D…があって
a*x+a'=A
b*x+b'=B
c*x+c'=C
d*x+d'=D
…
のとき
x+a+b+c+d+…+|a'|+|b'|+|c'|+|d'|+…が最小になるxを求めよ。A.
知るか!
とりあえず4あたりから√256まで探索するぞ!→探索します!!!
// jsのroundが特殊であることと今回必要なのは5捨6入なので用意する const round=x=>-Math.sign(x)*Math.round(-Math.abs(x)), slm=arr=>{ // (search loop minimum) var m=Number.POSITIVE_INFINITY,s,tmp; for(var i=4;i<=16;i++){ tmp=arr.map(x=>Math.abs(x>127?256-x:x)) // 逆回り .map(x=>round(x/i)+x%i) .reduce((a,b)=>a+b)+i; // 合計 if(tmp<m){m=tmp;s=i;} //更新 console.log(i,tmp); } console.log('slm',s); return s; }; // >> 4, 81 // >> 5, 69 // >> 6, 64 // >> 7, 60 // >> 8, 57 // >> 9, 55 // >> 10, 51 // >> 11, 66 // >> 12, 52 // >> 13, 68 // >> 14, 46 // >> 15, 72 // >> 16, 53 // >> "slm", 14どうやらHello World!の場合はx=14が最適のようです
この結果を使ってグループの先頭に端数を除いた値を追加しておきますmd=slm(tmp); tmp=tmp.map((x,i)=>{ var r=round((x>127?x-256:x)/md); clm[i].unshift(r<0?256+r*md:r*md); return r; }); console.log('boot',tmp); // 端数 // >> "boot", [5,7,2,6]ループ+端数→出力
ラストスパート
計算結果をBrainf*ckに落としていきますs='+'.repeat(md)+'['; // さっきのx=14 tmp.forEach(x=>s+='>'+(x>0?'+':'-').repeat(Math.abs(x))); // x*__ s+='<'.repeat(tmp.length)+'-]>'; ind.forEach((x,i)=>{ var p=x-(ind[i-1]||0),to=clm[x][1]-clm[x][0];clm[x].shift(); // 目的のグループまでポインタを移動 s+=(p>0?'>':'<').repeat(Math.abs(p)); // 逆回り注意してメモリに書く p=Math.abs(to);s+=(to>0^p>128?'+':'-').repeat(p>128?256-p:p)+'.'; console.log(i,x,s);console.log(clm); }); console.log('result:',s.length,s); // >> 0, 0, "++++++++++++++[>+++++>+++++++>++>++++++<<<<-]>++." // >> [[72],[98,101,108,108,111,111,114,108,100],[28,32,33],[84,87]] // >> 1, 1, "++++++++++++++[>+++++>+++++++>++>++++++<<<<-]>++.>+++." // >> [[72],[101,108,108,111,111,114,108,100],[28,32,33],[84,87]] // >> 中略 // >> 10, 1, "++++++++++++++[>+++++>+++++++>++>++++++<<<<-]>++.>+++.+++++++..+++.>++++.>+++.<<.+++.------.--------." // >> [[72],[100],[32,33],[87]] // >> 11, 2, "++++++++++++++[>+++++>+++++++>++>++++++<<<<-]>++.>+++.+++++++..+++.>++++.>+++.<<.+++.------.--------.>+." // >> [[72],[100],[33],[87]] // >> "result:", 104, "++++++++++++++[>+++++>+++++++>++>++++++<<<<-]>++.>+++.+++++++..+++.>++++.>+++.<<.+++.------.--------.>+."完成です!
Hello World!が104字で出来ましたまとめ
私が探した限りHello World!の最小は105字までしか見つからなかったので
104字が出て来た瞬間はとても嬉しかったですコードまみれ & とても長い記事になっちゃいました
記事書くの向いてないのがよくわかりました()
最後まで読んでくださりありがとうございます
- 投稿日:2021-01-20T04:45:21+09:00
マウスホバー時の構文覚書
JavaScriptを使った時のマウスホバー時のイベント構文について、自身への覚書きです。
let textChange = document.getElementById('about'); function mouse(word,word2){ textChange.addEventListener('mouseover',()=>{ textChange.innerHTML=word; }); textChange.addEventListener('mouseleave',()=>{ textChange.innerHTML=word2; }); }; mouse("こんにちは","さようなら");
- 投稿日:2021-01-20T04:19:06+09:00
[Vue.js] v-ifの使い方
- 投稿日:2021-01-20T01:39:16+09:00
Jamstack とは何か( 2021 年 1 月時点)
Jamstack 公式サイト
https://jamstack.org/Jamstack とは何か( 2021 年 1 月時点)
Jamstack is the new standard architecture for the web. Using Git workflows and modern build tools, pre-rendered content is served to a CDN and made dynamic through APIs and serverless functions. Technologies in the stack include JavaScript frameworks, Static Site Generators, Headless CMSs, and CDNs.
Jamstack は、ウェブの新しい標準アーキテクチャ。Git ワークフローと最新のビルドツールを使用して、あらかじめレンダリングされたコンテンツを CDN に提供し、API とサーバーレス機能を介して動的なものにする。スタックに含まれるテクノロジーには、JavaScript フレームワーク、スタティックサイトジェネレーター、ヘッドレス CMS 、CDN などがある。
The core principles of pre-rendering, and decoupling, enable sites and applications to be delivered with greater confidence and resilience than ever before.
プリレンダリングとデカップリングの基本原則により、サイトやアプリケーションをこれまで以上に信頼性とレジリエンス(変化に対処する能力)を持って配信することができる。
(参考)これまでの Jamstack / JAMstack
- Matt Biilmann (Netlify 社)が 2016 年頃に提唱し始めたもの。
- もともとは JAMstack 。 JavaScript 、APIs 、Markup の頭文字と言われていた。
- 2019 年 12 月頃から 2020 年 4 月頃 にかけて JAMstack から Jamstack 表記へ変わった。
- 定義/構成要素は JavaScript 、API 、Markup という要素を含むものから、Web サーバを介さないもの、プリレンダリングとデカップリングするもの、といった形で世の中の浸透具合にあわせて広義に変化している模様。
Jamstack とはつまり何か
- Google の Rendering on the Web の以下図における CSR with Prerendering であると言える。
- SSR するのではなく静的 HTML をプリレンダリングするもの。SPA がそうであるように、サーバサイドとクライアントサイドが分離(デカップリング)している構成であるもの。
Jamstack がなぜ理解しにくいのか
- SSR (Server Side Rendering) 、CSR (Client Side Rendering) という概念を理解する必要があること
- 前述の図にもあるように、SSR や CSR は更に細分化され、複数の種類があることを理解する必要があること
- 静的レンダリングとプリレンダリングといったように似た用語で異なる概念があり混乱しやすいこと
- SSR 、CSR 等を理解するのにアプリのビルド・デプロイのフローを理解していないと混乱しやすいこと
- ISR (Incremental Static Regeneration) といった新しい概念も登場していること
- 概念の実装例/具体例(例えば Next.js といったフレームワーク)が日々進化し機能拡張されていることから、一概に定義できず( SSG であり SSR である等)複雑化していること
参考記事
公開時期された時期に注意。
- JAMstackってなに?実践に学ぶ高速表示を実現するアーキテクチャの構成 (2019/12/10)
- Jamstackとは何か?まずは基本を理解しよう! - microCMS (2020/08/07)
- Next.jsにおけるSSG(静的サイト生成)とISRについて(自分の)限界まで丁寧に説明する (2020/11/11)
- Jamstackって何なの?何がいいの? (2020/02/08)
- 投稿日:2021-01-20T01:12:23+09:00
#UIFlow の BLE UART を使った #M5Stack_Core2 ( #M5Stack )とブラウザとの双方向無線通信
最新の UIFlow(M5Stackシリーズのビジュアルプログラミング環境)で BLE通信を実装するためのブロックが追加されたため、以前、M5Stack Core2 とブラウザとの間での BLE通信を試しました(ブラウザ側では Web Bluetooth API を利用)。
●【JavaScript 2020】 #UIFlow の BLE UART を使ったブラウザから #M5Stack_Core2 ( #M5Stack )への文字の送信 - Qiita
https://qiita.com/youtoy/items/3da58570972803134f6cこの時は双方向の通信は試せておらず、ブラウザから M5Stack Core2 への文字列送信のみ試していました。
そこで今回、M5Stack Core2 から ブラウザへの文字列送信も試し、双方向通信を実装してみます。
注意
冒頭で紹介した記事の前に書いた、以下の記事の中で記載しているのですが、現状で UIFlow の BLE UART を利用できるのは、「M5Stack Fire」か「M5Stack Core2」のどちらかになりそうです。
●#UIFlow の BLE UART を使った文字のやりとりを #M5Stack_Core2 で試してみた( #M5Stack ) - Qiita
https://qiita.com/youtoy/items/0aeac01927d60c33f421双方向の通信を実装する
今回、実装した内容の掲載をメインにします。
UIFlow での設定・プログラムの作成
設定
画面上にラベルを 1つ配置し、日本語が扱えるようにフォントは「Unicode 24」を選択しています。
プログラム
ブロックを組み合わせて作ったプログラムは以下のとおりです。
ブラウザ側のソースコード
ブラウザ側で、M5Stack Core2 からデータを受信して画面に表示したり、M5Stack Core2 にデータを送ったりするためのソースは以下のとおりです。これを HTMLファイルとしてローカルに保存します。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>UIFlow と BLE</title> </head> <body> <h1>UIFlow と BLE</h1> <button onclick="onStartButtonClick()">接続</button> <br /> <button onclick="sendMessage()">テキスト書き込み</button> <br /> <textarea id="text"></textarea> <script> const UUID_1 = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"; const UUID_2 = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"; // Write const UUID_3 = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"; // Notify let bluetoothDevice; let characteristic_A, characteristic_B; const textarea = document.getElementById("text"); async function onStartButtonClick() { try { console.log("Requesting Bluetooth Device..."); const device = await navigator.bluetooth.requestDevice({ filters: [{ namePrefix: "m5ble" }], optionalServices: [UUID_1], }); console.log("Connecting to GATT Server..."); const server = await device.gatt.connect(); console.log("Getting Service..."); const service = await server.getPrimaryService(UUID_1); console.log("Getting Characteristic..."); characteristic_A = await service.getCharacteristic(UUID_2); console.log("Getting Characteristic..."); characteristic_B = await service.getCharacteristic(UUID_3); await characteristic_B.startNotifications(); console.log("> Notifications started"); characteristic_B.addEventListener( "characteristicvaluechanged", handleNotifications ); } catch (error) { console.log("Argh! " + error); } } async function sendMessage() { if (!characteristic_A) { return; } const text = "aaa"; const arrayBuffe = new TextEncoder().encode(text); try { await characteristic_A.writeValue(arrayBuffe); } catch (error) { console.log("Argh! " + error); } } async function handleNotifications(event) { if (characteristic_B) { try { let value = event.target.value; const text = new TextDecoder().decode(value); textarea.textContent = text; console.log(text); } catch (error) { console.log("Argh! " + error); } } } </script> </body> </html>この中で設定している 3つの UUID は、冒頭に掲載していた以前の記事を書いた際に調べたものです。
双方向の通信を試す
上記の HTMLファイルをブラウザで開き、ページ内の「接続」ボタンを押して M5Stack Core2 とのペアリングを行ってください。
その後、ブラウザで開いたページ内の「テスト書き込み」ボタンを押すと、M5Stack Core2 の画面の背景の文字・テキストが変化します。なお、M5Stack Core2 の Cボタンを押すと、背景の色が最初の色に戻ります。
また、M5Stack Core2 の Aボタン・Bボタンをそれぞれ押すと、ブラウザで開いたページ内のテキストエリアに文字が表示されます。この文字は、M5Stack Core2 から送信された内容を表示しています。
実際に動作している時の様子は、以下のとおりです。
#UIFlow の BLE UART を使った #M5Stack_Core2 ( #M5Stack )とブラウザとの双方向無線通信。
— you (@youtoy) January 19, 2021
M5Stack Core2 のボタンを押すとテキストデータが送信され、それがブラウザのテキストエリアに表示されたり、
ブラウザ側でボタンを押すと M5Stack Core2 の背景色などが変わるという動作です。 pic.twitter.com/oXLKmIk13Fうまく動作しました!
ただ、プログラムでは「データを受信」という文字を表示するようにしていたはずなのに、端末上の表示で「デタを受信」となってたりするので、マルチバイト文字の部分は要チェックなところがありそう。
【追記】 ブラウザ側で可視化をする処理を入れたものを作ってみた
M5Stack Core2 のタッチスクリーンの触れられた位置座標を送信データにして、それをブラウザ側でリアルタイムに描画する、というものも作ってみました。
●#UIFlow の BLE UART を使った #M5Stack_Core2 ( #M5Stack )からブラウザへのデータ送信とグラフ化 - Qiita
https://qiita.com/youtoy/items/1bf6e9390b5dc5d2ba51#UIFlow の BLE UART を引き続きお試し。#M5Stack_Core2 の画面上でタッチされた座標を BLE でブラウザに送り、リアルタイムにグラフとして描画してみた。
— you (@youtoy) January 20, 2021
送る値を途中で 、x座標にするか y座標にするか、入れ替えてます。#M5Stack pic.twitter.com/wHvN4vdoB8
- 投稿日:2021-01-20T00:51:16+09:00
JavaScript【基本文法】~Part3~
初めに
今回はJavaScriptの中で、自分が大事だと思った配列メソッドについて書いていきたいと思います。
目次
・map()
・filter()
・findIndex()
map()
map
メソッドを使う事で新しい配列を作る事が出来る。入力
const obj = { "size": {text: "big"}, "shape": {text: "square"}, "num": {text: "many"} }; const Array = Object.keys(obj).map(key =>{ let value = obj[key] value['id'] = key return value }); console.log(Array);結果
[ { text: 'big', id: 'size' }, { text: 'square', id: 'shape' }, { text: 'many', id: 'num' } ]filter()
filter
メソッドを使う事で条件に合う要素を抽出する事が出来る。入力
const obj = [ {id: "size", text: "big"}, {id: "shape", text: "square"}, {id: "num", text: "many"} ]; const Result = obj.filter(object =>{ return object.id === "shape" }); console.log(Result);結果
[ { id: 'shape', text: 'square' } ]findIndex()
findIndex
メソッドを使う事で要素の何番目かを知る事が出来る。入力
const obj = [ {id: "size", text: "big"}, {id: "shape", text: "square"}, {id: "num", text: "many"} ]; const Index = obj.findIndex(object =>{ return object.id === "shape" }); console.log(Index);結果
1
参考記事