20200908のJavaScriptに関する記事は30件です。

高階関数 Higher-Order Functions とは

この記事を読むとわかること

JavaScript初心者が高階関数(Higher-Order Functions)を理解できます。

高階関数 Higher-Order Functions とは

高階関数 Higher-Order Functions とは

高階関数とは、次のいずれかを実行する関数です。

  • 関数を引数として扱う
  • 関数を戻り値として扱う

高階関数を使うことのメリット

  • 抽象化により、人間の読者が簡単に再利用、デバッグ、理解できる方法で複雑なコードを書くことができます
  • 新しい変数に再割り当てするなど、他のタイプのデータと同じように関数を操作できます
  • 機能のまとまりを細切れに保つことにより、テストが容易になります

第一級関数 first-class function とは

高階関数を理解する上でJavaScriptの関数の持つ基本の性質を知る必要があり、第一級関数 first-class function について触れておきます。

第一級関数とは、第一級オブジェクトとして扱うことのできる関数のことであり、JavaScriptがサポートしています。
第一級関数は他のすべての変数と同じように扱われます。 よってこれらを引数として関数に渡したり、他の関数から値として返したり、変数やデータ構造に割り当てたりすることができます。

第一級関数の複雑さ(関数の引数が関数..)に特に制限が設けられていない時,プログラミング言語が高階関数(higher-order function) をサポートしている、と表現できます。第一級関数は、高階関数のような形で日常的に用いられます。

以下は、関数を変数に割り当てる例です。

関数を変数に割り当てる例
function greet(name) { 
 console.log('Hello ' + name) 
}
greet(John) // “Hello John”
var sayHello = greet 
sayHello(Alex) // “Hello Alex”

高階関数の例

以下にて高階関数の例を2つ見てみましょう。

1. 配列の内容を順に処理するforEachメソッド

以下は、配列の要素を順番に取り出すための高階関数です。

配列の内容を順に処理するforEachメソッド
array.forEach(callback [,that])
// array : 配列オブジェクト
// callback : 個々の要素を処理するための関数
// 関数callbackの中でthisが示すオブジェクト

以下は処理用の関数として、配列の要素を二乗するコールバック関数です。
コールバック関数とは、呼び出し先の関数の中で呼び出される関数のことです。
この関数では配列の中身を受け取れるように引数value(要素の値),index(インデックス番号),array(元の配列)を指定する必要があります。

配列の要素を二乗するコールバック関数
var data = [1, 2, 3, 4];
data.forEach(function(value,index,array){
  console.log(value * value);// 結果 1, 4, 9, 16
});

2. 配列を指定されたルールで加工するmapメソッド

以下は、配列の要素を順番に取り出すための高階関数です。

配列を指定されたルールで加工するmapメソッド
array.map(callback [,that])
// array : 配列オブジェクト
// callback : 個々の要素を加工するための関数
// 関数callbackの中でthisが示すオブジェクト

以下は処理用の関数として、配列の要素を加工した結果をまとめて新たな配列として返すコールバック関数です。
この関数では配列の中身を受け取れるように引数value(要素の値),index(インデックス番号),array(元の配列)を指定する必要があります。

配列の要素を二乗した結果を新たな配列として返すコールバック関数
var data = [1, 2, 3, 4];
var result = data.map(function(value,index,array) {
  return value * value ;
});

console.log(result); // 結果 : [1, 4, 9, 16]

上記の2例に共通して言える事ですが、高階関数から呼ばれるコールバック関数は自由に差し替える事ができます。
これこそが高階関数を使う理由です。

高階関数を使わない場合と使う場合の違い

元の配列の各値を2倍した新しい配列を作成するコードで違いを見てみます。

高階関数を使わない場合
const arr1 = [1, 2, 3];
const arr2 = [];
for(let i = 0; i < arr1.length; i++) {
  arr2.push(arr1[i] * 2);
}
// prints [ 2, 4, 6 ]
console.log(arr2);
高階関数を使う場合
const arr1 = [1, 2, 3];
const arr2 = arr1.map(function(item) {
  return item * 2;
});
console.log(arr2);

参考情報

Higher Order Functions

Higher Order Functions in JavaScript

Higher-order functions in Javascript

Understanding Higher-Order Functions in JavaScript

改訂新版JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用まで

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript 高階関数 Higher-Order Functions とは

この記事を読むとわかること

JavaScript初心者が高階関数(Higher-Order Functions)を理解できます。

高階関数 Higher-Order Functions とは

高階関数 Higher-Order Functions とは

高階関数とは、次のいずれかを実行する関数です。

  • 関数を引数として扱う
  • 関数を戻り値として扱う

高階関数を使うことのメリット

  • 抽象化により、人間の読者が簡単に再利用、デバッグ、理解できる方法で複雑なコードを書くことができます
  • 新しい変数に再割り当てするなど、他のタイプのデータと同じように関数を操作できます
  • 機能のまとまりを細切れに保つことにより、テストが容易になります

第一級関数 first-class function とは

高階関数を理解する上でJavaScriptの関数の持つ基本の性質を知る必要があり、第一級関数 first-class function について触れておきます。

第一級関数とは、第一級オブジェクトとして扱うことのできる関数のことであり、JavaScriptがサポートしています。
第一級関数は他のすべての変数と同じように扱われます。 よってこれらを引数として関数に渡したり、他の関数から値として返したり、変数やデータ構造に割り当てたりすることができます。

第一級関数の複雑さ(関数の引数が関数..)に特に制限が設けられていない時,プログラミング言語が高階関数(higher-order function) をサポートしている、と表現できます。第一級関数は、高階関数のような形で日常的に用いられます。

以下は、関数を変数に割り当てる例です。

関数を変数に割り当てる例
function greet(name) { 
 console.log('Hello ' + name) 
}
greet(John) // “Hello John”
var sayHello = greet 
sayHello(Alex) // “Hello Alex”

高階関数の例

以下にて高階関数の例を2つ見てみましょう。

1. 配列の内容を順に処理するforEachメソッド

以下は、配列の要素を順番に取り出すための高階関数です。

配列の内容を順に処理するforEachメソッド
array.forEach(callback [,that])
// array : 配列オブジェクト
// callback : 個々の要素を処理するための関数
// 関数callbackの中でthisが示すオブジェクト

以下は処理用の関数として、配列の要素を二乗するコールバック関数です。
コールバック関数とは、呼び出し先の関数の中で呼び出される関数のことです。
この関数では配列の中身を受け取れるように引数value(要素の値),index(インデックス番号),array(元の配列)を指定する必要があります。

配列の要素を二乗するコールバック関数
var data = [1, 2, 3, 4];
data.forEach(function(value,index,array){
  console.log(value * value);// 結果 1, 4, 9, 16
});

2. 配列を指定されたルールで加工するmapメソッド

以下は、配列の要素を順番に取り出すための高階関数です。

配列を指定されたルールで加工するmapメソッド
array.map(callback [,that])
// array : 配列オブジェクト
// callback : 個々の要素を加工するための関数
// 関数callbackの中でthisが示すオブジェクト

以下は処理用の関数として、配列の要素を加工した結果をまとめて新たな配列として返すコールバック関数です。
この関数では配列の中身を受け取れるように引数value(要素の値),index(インデックス番号),array(元の配列)を指定する必要があります。

配列の要素を二乗した結果を新たな配列として返すコールバック関数
var data = [1, 2, 3, 4];
var result = data.map(function(value,index,array) {
  return value * value ;
});

console.log(result); // 結果 : [1, 4, 9, 16]

上記の2例に共通して言える事ですが、高階関数から呼ばれるコールバック関数は自由に差し替える事ができます。
これこそが高階関数を使う理由です。

高階関数を使わない場合と使う場合の違い

元の配列の各値を2倍した新しい配列を作成するコードで違いを見てみます。

高階関数を使わない場合
const arr1 = [1, 2, 3];
const arr2 = [];
for(let i = 0; i < arr1.length; i++) {
  arr2.push(arr1[i] * 2);
}
// prints [ 2, 4, 6 ]
console.log(arr2);
高階関数を使う場合
const arr1 = [1, 2, 3];
const arr2 = arr1.map(function(item) {
  return item * 2;
});
console.log(arr2);

参考情報

Higher Order Functions

Higher Order Functions in JavaScript

Higher-order functions in Javascript

Understanding Higher-Order Functions in JavaScript

改訂新版JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用まで

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

form_tag内のsubmitが反応しないときの対処

前提

Ruby on Railsでローカル開発中、Chromeで動作確認をしていたとき。
form_tag内にJavaScriptとsubmitが含まれているページにlink_toを使って移動したときに発生した。

症状

form_tag内のsubmitをクリックしてもリアクションを返さない。
(URLを直打ちした場合や、link_toを通してページを読み込んでも再リロードした場合に限りsubmitが反応するため、バグかもしれない。)

対策

form_tag内にJavaScriptを記述しないこと。
JavaScriptが記述されている場合にのみ、上記のような不具合が発生するした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsのformを素のJavaScriptで送信する

やりたかったこと

form送信後にJS側で何かしらの処理をしたい。

方法

fetchAPIのコールバックチェーンで行うことができる。

turbolinksrails-ujs は使用しないので、 remote: true を使ったAjax通信とも異なるやり方。

「form送信後のJS処理」を、
当初 form.submit() ~ setTimeout() のような感じで書いたが、実行順序が担保されなかったので書き直した。

ポイント

fetch では
1. credentials'same-origin'
2. bodynew FormData(form)
を指定。

= f.submit や %button{ type: 'submit' } を使うと Railsによってリクエストが飛ばされてしまうので、%button{ type: 'button' } を使って送信ボタンをクリックしたときにJavaScriptを呼び出すようにしている。

サンプル

あまりサンプルがなかった気がするのでさらっと残す。
送信ボタンを押した後に二度押せないようにする処理も入れてるが、もっとちゃんとした書き方はあるのかも。

const userForm = document.getElementById('user-form');
const submitBtn = document.getElementById('submit-btn');

submitBtn.addEventListener('click', () => {
  submitBtn.textContent = '送信中...';
  submitBtn.disabled = true;

  fetch('/users', {
    method: 'POST',
    credentials: 'same-origin',
    body: new FormData(userForm),
  }).then(response => {
    // 送信後の処理があればここに書く
  }).catch(error => {
    // 例外処理
  });
});
= form_with(model: @user, id: 'user-form') do |f|
  = f.text_field :name
  = f.text_field :mail
  %button{ type: 'button', id: 'submit-btn' }
    送信

追記

コントローラ側でリクエストを受け取るアクションに、 protect_from_forgery を適切に使わないとうまくいかないかも。

Rails で CSRF トークンの検証を制御する

参考リンク

他の参考リンクは忘れてしまったので思い出したら書く。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ブラウザに表示させるための事前処理(コンパイラ)

複数の静的ファイルがブラウザに適用されるまでの仕組みをまとめました。
大まかに言うと、
ブラウザでは認識できる言語が決まっており、どんな言語も認識できる言語に翻訳する必要があります。
この翻訳作業をコンパイルといい、コンパイルできないものは事前に処理をする必要があります。この事前処理はプリコンパイルと呼ばれ、細かい機能1つ1つをモジュールという処理のまとまりにし、コンパイルしてブラウザに返すといった流れです。

前提
プログラミング初学者(2ヶ月)が学んだ内容をまとめたものです。
実際の現場では通用しないことや間違った情報がある可能性があります。
お気づきの方はコメントにてご指摘いただければ幸いです。

ブラウザは認識できる言語が決まっている

ブラウザはHTML,CSS,JavaScript,WebAssemblyという言語のみを認識することができます。
サーバーサイドでどんな記述をしていたとしても、最終的にはこの4つでブラウザに返されています。
これ以外の言語ではブラウザはページを描画することができません。

開発を便利にする言語

ブラウザでは、上記4つだけを認識してくれますが、開発ではより書きやすく読みやすいように設計されたプログラミング言語があります。
このプログラミング言語を高級言語といいます。

高級言語

機械よりも人間が理解しやすいように設計されたプログラミング言語
機械が認識しやすい言語は低級言語と呼ばれます。
高級言語の例
CSS:SCSS、SASS
JavaScript:TypeScript、CoffeeScript

ブラウザが認識できる言語に変換する仕組み

開発に便利な言語をブラウザが認識できるように翻訳する作業をコンパイルといいます。

コンパイル
プログラミング言語を動作する機械が理解できるように翻訳する作業
コンパイルはコンパイラというプログラムによって行われる。
コンパイラで認識できない言語がある場合は予めプリコンパイルしておく必要があります。

プリコンパイル
コンパイラが翻訳できない言語を翻訳できるようにする事前コンパイルのこと
メインとなる処理に対して行う前処理のこと
このプリコンパイルを行うための手法として、アセットパイプラインという仕組みがある。

アセットパイプライン
JavaScriptやCSSなどのアセットと呼ばれる静的ファイルを小さくまとめてくれる機能
アセットパイプラインの処理は、プリコンパイル→連結→圧縮→配置の流れで行われます。
複数の静的ファイルをプリコンパイルして連結したのち、圧縮して軽量化したものをpublicディレクトリに配置して、ブラウザへ渡せるようにします。
プリコンパイルはモジュールバンドラを使って行われます。

モジュールバンドラ
モジュールバンドラはJavaScriptのモジュールの依存関係を考慮しながら管理するツール
モジュールは機能を1つずつ分けて他のファイルから読み込めるようにした処理のまとまりのこと
機能のまとまりをモジュール、この1つ1つの機能が依存関係にある場合にモジュールバンドラはこれらを考慮しながら管理してくれます。
モジュールで管理せず、機能をファイルごとに分割してしまうと、最終的に1つのファイルにまとめるときに不具合が生じるためモジュールパンドラが使われています。

webpack
モジュールバンドラの中で主流なツール
webpackが行うことは大きく4つ
・Entry
依存関係を解決するためにどのファイルを基準(エントリーポイント)とするかを決める。
・Output
エントリーポイントにされ、webpackによってまとめられたファイルをどこへどのような名前で出力(アウトプット)するのか指定する。
・Loaders
JavaScript以外のCSSやHTMLなどのファイルをモジュールに変換する方法を読み込み(ロード)、指定した処理を行う。
・Plugins
圧縮などのファイルをまとめる以外でローダーが実行できないタスクを導入し、拡張(プラグイン)する。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Raspberry PiとOpenCVでWEB監視カメラを作成する

Raspberry PiとOpenCVでWEB監視カメラを作成する

はじめに

Mac環境の記事ですが、Windows環境も同じ手順になります。環境依存の部分は読み替えてお試しください。

目的

ブラウザにストリーミング動画を表示します。

この記事を最後まで読むと、次のことができるようになります。

No. 概要 キーワード
1 REST API Flask
2 OpenCV cv2

完成イメージ

ストリーミング
IMG_4814.PNG

実行環境

環境 Ver.
macOS Catalina 10.15.6
Raspberry Pi 4 Model B 4GB RAM -
Raspberry Pi OS (Raspbian) 10
Python 3.7.3
Flask 1.1.2
opencv-python 4.4.0.42

ソースコード

実際に実装内容やソースコードを追いながら読むとより理解が深まるかと思います。是非ご活用ください。

GitHub

関連する記事

Camera設定

Raspberry Pi Software Configuration Tool起動

command.sh
~$ sudo raspi-config

Camera有効化

  1. 5 Interfacing Optionsを選択
  2. P1 Cameraを選択

再起動

command.sh
~$ sudo reboot

OpenCV依存関係

HDF5

HDF5 is a file format and library for storing scientific data.

command.sh
~$ sudo apt-get install -y libhdf5-dev libhdf5-serial-dev libhdf5-103

ATLAS

ATLAS is an approach for the automatic generation and optimization of numerical software.

command.sh
~$ sudo apt-get install -y libatlas-base-dev

JasPer

JasPer is a collection of software (i.e., a library and application programs) for the coding and manipulation of images.

command.sh
~$ sudo apt-get install -y libjasper-dev

ハンズオン

ダウンロード

command.sh
~$ git clone https://github.com/nsuhara/raspi-streaming.git -b master

セットアップ

command.sh
~$ cd raspi-streaming
~$ python -m venv .venv
~$ source .venv/bin/activate
~$ pip install -r requirements.txt
~$ source config

サービス起動

command.sh
~$ flask run --host=0.0.0.0 --port=5000

アクセス

command.sh
~$ open "http://{host}:5000/raspi-streaming/api?process=front_end&request=app_form&secret_key=M7XvWE9fSFg3"

サービス終了

command.sh
~$ Control Key + C

アプリ構成

target.sh
/app
├── __init__.py
├── apis
│   ├── templates
│   │   └── app_form.html
│   └── views
│       ├── __init__.py
│       ├── back_end_handler.py
│       ├── camera.py
│       ├── front_end_handler.py
│       └── main_handler.py
├── common
│   ├── __init__.py
│   └── utility.py
├── config
│   ├── __init__.py
│   ├── localhost.py
│   └── production.py
└── run.py

front-end

target.sh
/app
└── apis
     ├── templates
     │   └── app_form.html
     └── views
         └── front_end_handler.py

front-end制御

front_end_handler.py
"""app/apis/views/front_end_handler.py
"""
from flask import jsonify, render_template

from app import secret_key


def handler(req):
    """handler
    """
    param1 = req.get('param1')
    param2 = req.get('param2')

    if param1 == 'app_form':
        return _app_form(req=param2)

    return jsonify({'message': 'no route matched with those values'}), 200


def _app_form(req):
    """_app_form
    """
    if req.get('secret_key', '') != secret_key:
        return jsonify({'message': 'no route matched with those values'}), 200
    return render_template('app_form.html', secret_key=req.get('secret_key', ''))

front-endレイアウト

app_form.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>raspi-streaming</title>

    <style type="text/css">
        html,
        body {
            -webkit-user-select: none;
            width: 100%;
            height: 100%;
        }

        table {
            width: 100%;
            height: 100%;
        }

        table,
        td {
            border: 1px gray solid;
            padding: 10px;
        }

        img.img-option {
            width: 100%;
            /* height: 100%; */
        }
    </style>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script type="text/javascript">
    // nothing to do
    </script>
</head>

<body>
    <table>
        <tr height="5%">
            <td>
                <h1>raspi-streaming</h1>
            </td>
        </tr>
        <tr>
            <td>
                <img class="img-option"
                    src="{{ url_for('raspi-streaming.api', process='back_end', request='video_feed', secret_key=secret_key) }}">
            </td>
        </tr>
    </table>
</body>

</html>

back-end

target.sh
/app
└── apis
     └── views
         ├── back_end_handler.py
         └── camera.py

back-end制御

back_end_handler.py
"""app/apis/views/back_end_handler.py
"""
from flask import Response, jsonify

from app import secret_key
from app.apis.views.camera import Camera


def handler(req):
    """handler
    """
    param1 = req.get('param1')
    param2 = req.get('param2')

    if param1 == 'video_feed':
        return _video_feed(req=param2)

    return jsonify({'message': 'no route matched with those values'}), 200


def _video_feed(req):
    """_video_feed
    """
    if req.get('secret_key', '') != secret_key:
        return jsonify({'message': 'no route matched with those values'}), 200
    return Response(_generator(Camera()), mimetype='multipart/x-mixed-replace; boundary=frame')


def _generator(camera):
    """_generator
    """
    while True:
        frame = camera.frame()
        yield b'--frame\r\n'
        yield b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n'

カメラ制御

camera.py
"""app/apis/views/camera.py
"""
import cv2


class Camera():
    """Camera
    """

    def __init__(self):
        self.video_capture = cv2.VideoCapture(-1)

    def __del__(self):
        self.video_capture.release()

    def frame(self):
        """frame
        """
        _, frame = self.video_capture.read()
        _, image = cv2.imencode('.jpeg', frame)
        return image.tobytes()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

簡単レシート印刷 receiptline で行間隔を調整してみた

前回は 80 ミリカメラと 80 ミリフィルムで動画を撮影していました。
「役に立たない機械」感を醸し出していたかもしれないですね。

01.gif

連続で印刷するため、変換ライブラリを少し変更して、自動用紙カットを解除しました。
今回もこの変換ライブラリ lib/receiptline.js に手を入れてみようと思います。

用紙節約か、読みやすさか

receiptline に添付されているサンプルデータを印刷してみました。
左は TM-T88V で、右は mC-Print3 です。

行間隔が狭いので、文字が詰まって見えます。
今どきのレシートプリンターには用紙節約機能があるので、そういう時代だと思いますが。

02.jpg

行間隔を空ける

行間隔を空けて読みやすくしてみようと思います。
レシートプリンターのコマンドを調べて、一部のコマンドを置き換えてみます。

ESC/POS コマンドリファレンス
https://reference.epson-biz.com/pos/reference_ja/

  • 変更前
    • 行間隔なしコマンド ESC 3 n
  • 変更後
    • 行間隔ありコマンド ESC 2

StarPRNTモード コマンド仕様書
http://sp-support.star-m.jp/SDKDocumentation.aspx

  • 変更前
    • 行間隔なしコマンド ESC 0
  • 変更後
    • 行間隔ありコマンド ESC z n
lib/receiptline.js
//
// ESC/POS
//
const _escpos = {
    // start printing: ESC @ GS a n ESC M n FS ( A pL pH fn m ESC SP n FS S n1 n2 ESC 3 n ESC { n
    // open: printer => '\x1b@\x1da\x00\x1bM0\x1c(A' + $(2, 0, 48, 0) + '\x1b \x00\x1cS\x00\x00\x1b3\x00\x1b{' + $(printer.upsideDown),
    open: printer => '\x1b@\x1da\x00\x1bM0\x1c(A' + $(2, 0, 48, 0) + '\x1b \x00\x1cS\x00\x00\x1b2\x1b{' + $(printer.upsideDown),
    ...
};

//
// StarPRNT MBCS
//
const _starmbcs = {
    // start printing: ESC @ ESC RS a n ESC RS F n ESC SP n ESC s n1 n2 ESC 0 (SI) (DC2)
    // open: printer => '\x1b@\x1b\x1ea0\x1b\x1eF\x00\x1b 0\x1bs00\x1b0' + (printer.upsideDown ? '\x0f' : '\x12'),
    open: printer => '\x1b@\x1b\x1ea0\x1b\x1eF\x00\x1b 0\x1bs00\x1bz1' + (printer.upsideDown ? '\x0f' : '\x12'),
    ...
};

Node を再起動して再印刷。
行間隔は広がりましたが、縦罫線が途切れてしまっています。

03.jpg

縦罫線を接続する

変換ライブラリをさらに変更。
縦罫線を引く領域では、行間隔を空けないようにします。

ESC/POS

  • 縦罫線の始まり
    • 行間隔なしコマンドを追加 ESC 3 n
  • 縦罫線の終わり
    • 行間隔ありコマンドを追加 ESC 2

StarPRNT

  • 縦罫線の始まり
    • 行間隔なしコマンドを追加 ESC 0
  • 縦罫線の終わり
    • 行間隔ありコマンドを追加 ESC z n
lib/receiptline.js
//
// ESC/POS
//
const _escpos = {
    ...
    // start rules: FS C n ESC t n ... LF
    // vrstart: (widths, left, right) => '\x1cC0\x1bt\x01' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x91', left ? '\x9c' : '\x98').slice(0, -1) + (right ? '\x9d' : '\x99'),
    vrstart: (widths, left, right) => '\x1b3\x00\x1cC0\x1bt\x01' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x91', left ? '\x9c' : '\x98').slice(0, -1) + (right ? '\x9d' : '\x99'),
    // stop rules: FS C n ESC t n ... LF
    // vrstop: (widths, left, right) => '\x1cC0\x1bt\x01' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x90', left ? '\x9e' : '\x9a').slice(0, -1) + (right ? '\x9f' : '\x9b'),
    vrstop: (widths, left, right) => '\x1b2\x1cC0\x1bt\x01' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x90', left ? '\x9e' : '\x9a').slice(0, -1) + (right ? '\x9f' : '\x9b'),
    ...
};

//
// StarPRNT MBCS
//
const _starmbcs = {
    ...
    // start rules: ESC $ n ... LF
    // vrstart: (widths, left, right) => '\x1b$0' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x91', left ? '\x9c' : '\x98').slice(0, -1) + (right ? '\x9d' : '\x99'),
    vrstart: (widths, left, right) => '\x1b0\x1b$0' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x91', left ? '\x9c' : '\x98').slice(0, -1) + (right ? '\x9d' : '\x99'),
    // stop rules: ESC $ n ... LF
    // vrstop: (widths, left, right) => '\x1b$0' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x90', left ? '\x9e' : '\x9a').slice(0, -1) + (right ? '\x9f' : '\x9b'),
    vrstop: (widths, left, right) => '\x1bz1\x1b$0' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x90', left ? '\x9e' : '\x9a').slice(0, -1) + (right ? '\x9f' : '\x9b'),
    ...
};

もう一度 Node を再起動して再印刷。
縦罫線がくっつきました!

04.jpg

動作条件

実は、これが動作するのは printers.json の upsideDown が false の場合です。
この値が true で、印刷の向きを上下反転している場合はうまく動きません。

printers.json
{
    "tm_t88v": {
        "host": "192.168.1.2",
        "port": 9100,
        "cpl": 42,
        "encoding": "cp932",
        "gamma": 1.8,
        "upsideDown": false,
        "command": "escpos"
    },
    "mc_print3": {
        "host": "192.168.1.3",
        "port": 9100,
        "cpl": 48,
        "encoding": "cp932",
        "gamma": 1.8,
        "upsideDown": false,
        "command": "starmbcs"
    }
}

奥が深いです。

また何か作ったら投稿します。ではまた!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSのProxyでアルゴリズムを可視化する

JSのProxyを使ってアルゴリズムの実行中の各ステップを可視化する仕組みを作る記事です。
(inspired by アルゴリズム図鑑
初めに完成品のキャプチャとデモのリンクを貼っておきます。
screen-recording.gif
DEMO: https://codesandbox.io/s/heuristic-morse-1kc2d?file=/src/index.js
※ スマホだとシンタックスハイライトでエラーが出てるのでコード記載無し版も置いておきます。
https://codesandbox.io/s/happy-sun-211bq?file=/src/index.js

JavaScriptのProxyとは

Proxy - JavaScript | MDN
最初にJSのProxyとは何か簡単に説明すると、
特定の操作に対するオブジェクトの振る舞いを拡張/変更できるオブジェクトです。
拡張されるオブジェクト(ターゲット)と、操作を受けた時の挙動を定義するオブジェクト(ハンドラ)をコンストラクタに渡すことで生成できます。

const proxy = new Proxy(target, handler);

振る舞いを変更できる操作の種類は

  • プロパティ値の読み出し/書き込み/削除
  • 関数オブジェクトに対する関数呼び出しやnew付きの呼び出し
  • その他(プロパティやプロトタイプに関する様々な操作)

などがあります。
ハンドラに各操作に対応するトラップという関数を持たせることで挙動を定義できます。定義できるトラップの一覧はこちら。
Proxy handler - JavaScript | MDN

以下はプロパティ値の書き込み操作に対応するsetトラップを定義する例。
代入された値を10倍にしてターゲットに設定しています。

const t = {};
const p = new Proxy(t, {
  set(target, property, value, receiver) {
    target[property] = value * 10;
    return true; // 値の設定に成功した場合はtrueを返す
  }
});
p.n = 5;
console.log(t.n); // 50

setトラップの引数はターゲット、設定するプロパティの名前、設定するプロパティの新しい値、操作を受けたオブジェクト(プロキシ自身、またはプロキシを継承しているオブジェクト)です。

receiverについてはReflectと併用するのが主な用途なのかなと思います。
参考: JavaScript(ES2015〜)のProxyで、プロパティにフックする正しいやり方

プログラムの流れ

それではコードを見ていきます。

視覚化したいデータを初期化関数に渡す

今回は以下の内容の挿入ソートを視覚化します。
(ちなみにアルゴリズムはプログラミングコンテスト攻略のためのアルゴリズムとデータ構造を参考に実装しています)

const a = [5, 2, 4, 6, 1, 3];
insertionSort(a);

function insertionSort(a) {
  const n = a.length;
  for (let i = 1; i < n; i++) {
    const v = a[i];
    let j = i-1;
    while (j >= 0 && a[j] > v) {
      a[j+1] = a[j];
      j--;
    }
    a[j+1] = v;
  }
}

視覚化する値としては操作対象の配列a、ループ変数のi, j、一時変数のvにします。
それらを初期化関数に渡すことでプロキシに置き換えて値の変更を捕捉可能にします。
ただし、プロキシはオブジェクトに対する操作しか捕捉できないため、ローカル変数i, j, vに関してはローカル変数保持用のオブジェクト$のプロパティとして実装します。

index.js
// 初期化関数に{変数名: 値}の形で渡すことでプロキシが返される
const { a, $ } = initVisualization({
  a: [5, 2, 4, 6, 1, 3],
  $: { i: null, j: null, v: null } // {}でもいいけど分かりやすいようにnullで初期化
});

...

// アルゴリズム実装側もローカル変数の代わりに$を使うよう書き換え
function insertionSort(a) {
  const n = a.length;
  for ($.i = 1; $.i < n; $.i++) {
    $.v = a[$.i];
    $.j = $.i - 1;
    while ($.j >= 0 && a[$.j] > $.v) {
      a[$.j + 1] = a[$.j];
      $.j--;
    }
    a[$.j + 1] = $.v;
  }
}

できればアルゴリズム実装側には手を入れずに実現したかったけど、止むを得ません…。

初期化関数で渡されたデータのプロキシを生成

受け取った観測対象のデータ(a$)それぞれに対して、プロパティ値が設定された時に全てのデータのスナップショットを取るようsetトラップを定義します。

visualize.js
import { takeSnapshot } from "./snapshot";

export function initVisualization(targets) {
  const proxies = {};
  Object.entries(targets).forEach(([key, target]) => {
    proxies[key] = new Proxy(target, {
      set(t, p, v) {
        t[p] = v;
        takeSnapshot(targets);
        return true;
      }
    });
  });

  takeSnapshot(targets);

  return proxies;
}

スナップショットの実装

snapshot.js
const snapshots = [];

export function takeSnapshot(variables) {
  const snapshot = {};
  for (const k in variables) {
    snapshot[k] = deepCopy(variables[k]);
  }
  snapshots.push(snapshot);
}

返されたプロキシを使ってアルゴリズムの処理を実行し、UI描画

あとは返されたプロキシを使ってアルゴリズムを実行するだけです。代入やインクリメントのタイミングでスナップショットが取られます。
完了してからViewを描画します。

index.js
insertionSort(a);
render();

この時点でのデモはこちらです。

代入操作にアニメーションを付ける

ここまでで値の可視化はできました。
ここからさらに、変数間での値の受け渡しが分かりやすいように代入元から代入先に要素が移動するアニメーションを付けてみます。
そのためには渡された値はどの変数に入っていたかという情報が要りますが、setトラップには値そのものしか渡ってきません。
そこで、ちょっとhackyですが初期化関数を以下のように書き換えてみました。

visualize.js
export function initVisualization(targets) {
  const proxies = {};
  const name = Symbol("variable name"); // 変数名を保持するためのシンボル
  const from = Symbol("assign from"); // 代入元を保持するためのシンボル
  Object.entries(targets).forEach(([key, target]) => {
    target[name] = key; // 変数名を持たせる
    proxies[key] = new Proxy(target, {
      get(t, p) {
        if (t[name] === "a" || p === "v")
          return {
            valueOf: () => t[p],
            [from]: t[name] === "$" ? p : `${t[name]}.${p}` // 代入元情報
          };
        else return t[p];
      },
      set(t, p, v) {
        if (v[from]) { // fromを持っていれば代入されたと判定
          t[p] = v.valueOf();
          takeSnapshot(targets, {
            assign: {
              from: v[from],
              to: t[name] === "$" ? p : `${t[name]}.${p}` // 代入先情報
            }
          });
        } else {
          t[p] = v;
          takeSnapshot(targets);
        }
        return true;
      }
    });
  });

  ...
}

まずプロキシ生成時にターゲットに自身の名前を持たせておきます。
getトラップを使用してアニメーション対象である配列aの要素か変数vが参照された場合には値をオブジェクトでラップして、どの変数の値を読み出したかを示すfromプロパティを添えて返します。この時、元の値をvalueOfの戻り値とすることで比較演算など数値としての振る舞いが期待される文脈では元の値が自動的に返されます。
setトラップでは渡された値がfromプロパティを持っていれば別変数からの代入と判断して、自身の名前を代入先toとしてfromと合わせてtakeSnapshotの第2引数に渡します。

これで値と共に操作もスナップショットに記録されます。

snapshot.js
export function takeSnapshot(variables, operation) {
  const snapshot = {};
  for (const k in variables) {
    snapshot[k] = deepCopy(variables[k]);
  }
  snapshot[Symbol.for("operation")] = operation; // シンボルキーで操作内容を保存
  snapshots.push(snapshot);
}

後はその情報を元にアニメーションを組み立てます。

animation.js
export function getAnimation(operation) {
  if (!operation || !operation.assign) return null;

  const { from, to } = operation.assign;
  const fromSelector = `[data-variable-name="${from}"]`;
  const toSelector = `[data-variable-name="${to}"]`;
  const fromRect = document.querySelector(fromSelector).getBoundingClientRect();
  const toRect = document.querySelector(toSelector).getBoundingClientRect();
  const startX = fromRect.left - toRect.left;
  const startY = fromRect.bottom - toRect.bottom;
  return `
    ${toSelector} {
      animation: assign .3s;
    }

    @keyframes assign {
      0% {
        transform: translate(${startX}px, ${startY}px);
      }
    }
  `;
}

今回はやりませんが、比較演算など代入以外の操作も_.gtのように関数でラップしたスタイルで実装し、applyトラップと組み合わせることで操作の記録ができると思います。

View側(React)は省略しますが、ソース全体はCodeSandboxGithubからご覧いただけます。

おわり

プロキシによるロギングと、それを利用したアルゴリズムの可視化の試みでした。
プロキシにはログを取る以外にもバリデーションや値の加工、変更の通知など様々な活用方法があるので以下の記事も参考になると思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Object等の呼び出しでkeyに動的な変数を使う [TypeScript]

やりたいこと

object等の呼び出しで Object[key] のようにするとき、key に動的な変数を入れたい。
上記をより美しく。

問題・エラー

今回の記事では、こちらのコードを修正します。
関数receivedStringValue()は、ランダムでStringの値を返す関数とします。

const object = {
    aaa: 'aaa',
    bbb: 'bbb',
    ccc: 'ccc',
};
// keyには動的に生成された値
const key: string = receivedStringValue();
const value = object[key];

こちらのコードでは、下記のエラーが出ます。

Element implicitly has an 'any' type because expression of type 'string' can't be used to

解決策

object に対して型を定義する

結論

key の型がわかるような object の型を定義する

interface StringKeyObject {
    // 今回はstring
    [key: string]: any;
}
const object: StringKeyObject = {
    aaa: 'aaa',
    bbb: 'bbb',
    ccc: 'ccc',
};

// keyには動的に生成された値
const key: keyof typeof object = receivedStringValue();
const value = object[key];

keyの型がstringだとあいまいなので、 keyof typeof object でしっかりと制限をかけることがポイントです。

最後まで読んでくださって、ありがとうございます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

keyof typeofの使い方 [TypeScript]

やりたいこと

object等の呼び出しで Object[key] のようにするとき、key に動的な変数を入れたい。
上記をより美しく。

問題・エラー

今回の記事では、こちらのコードを修正します。
関数receivedStringValue()は、ランダムでStringの値を返す関数とします。

const object = {
    aaa: 'aaa',
    bbb: 'bbb',
    ccc: 'ccc',
};
// keyには動的に生成された値
const key: string = receivedStringValue();
const value = object[key];

こちらのコードでは、下記のエラーが出ます。

Element implicitly has an 'any' type because expression of type 'string' can't be used to

解決策

object に対して型を定義する

結論

key の型がわかるような object の型を定義する

interface StringKeyObject {
    // 今回はstring
    [key: string]: any;
}
const object: StringKeyObject = {
    aaa: 'aaa',
    bbb: 'bbb',
    ccc: 'ccc',
};

// keyには動的に生成された値
const key: keyof typeof object = receivedStringValue();
const value = object[key];

keyの型がstringだとあいまいなので、 keyof typeof object でしっかりと制限をかけることがポイントです。

最後まで読んでくださって、ありがとうございます。

参考記事

https://qiita.com/tktcorporation/items/051bb03bb4d72930d8e9

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java Script 基礎文法編 1

詳細

今日から本格的にJava Scriptを学習しようと思う。
スクールでも学習したが基礎からコードを一つ一つ確実に理解する為アウトプットをしていく!

定数

const price = 150;
price に 150を代入する事ができる。
ただ、constを使った場合このpriceに再代入する事ができない

変数

let price = 150;
price に 150を代入する事ができる。
letは再代入する事ができる。
注 基本はconstを使いどうしてもという時だけlet を使うようにする。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascriptで配列の要素がオブジェクトの場合に、全て値渡しでコピーしたいとき

vue.js等を使っていると、dataに定義している配列を、参照渡しじゃなくて、値のみコピーしたい場合ってよくありますよね。

例. 下記のような配列の要素がオブジェクトの場合。

const array = [{a: 1}, {a: 2}]

下記のようにmapで各要素ごとをスプレッド構文で展開して、pushしていけばおk。

let newArray = []
array.map(a => {
  newArray.push({...a})
})

また、そのタイミングで、新しいプロパティを追加したい場合は、下記のようにすればおk

let newArray = []
array.map(a => {
  newArray.push({...a, b: 0})
})

もっといいコピーの仕方を知っている方がいれば教えて下さい!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】ボタンの切り替えテクニック

モダンなサイトだと、ボタンをクリックしたらアイコンが切り替わって、内容が見れたりするようなUIも増えてきていますよね。

そんなボタンの切り替えの構造をVue.js(Nuxt.js)で表現していこうと思います!

ボタンを押すと、記事内容の表示・非常時を切り替えられる

ボタンをそして、あるデータの真偽値を変化させる

<v-btn @click="show = !show" icon>
</v-btn>
<script>
expprt default {
  data () {
    return {
      show: false
    }
  }
}
</script>

ボタンを押すと、falseの真偽値が切り替わる

showの真偽値によって変化する者たち

<v-btn @click="show = !show" icon>
  <v-icon>{{ show ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
</v-btn>

showの真偽値、そして三項演算子によって、アイコンの見た目が変わるようになっている。

<v-expand-transition>
  <div v-show="show">
    <v-divider></v-divider>
    <v-card-text>
      <ul>
        <li v-for="item in items" :key="item.id">
          <a href="item.url">{{ item.title }}</a>
        </li>
      </ul>
    </v-card-text>
  </div>
</v-expand-transition>

v-showの真偽値がtrueの場合は、div以下のコンポーネントが表示されて、falseの時は隠れるようになっている。

パスワードの入力内容を見たり、隠したりする

<v-text-field 
  label="パスワード"
  v-model="signupForm.password"
  @click:append="show = !show"
></v-text-field>

appendはボタンの配置を設定している

<script>
expprt default {
  data () {
    return {
      show: false
    }
  }
}
</script>

例の如く、showの真偽値をボタンによって切り替えている。

showの真偽値によって、テキスト内容を見たり隠したりする

<v-text-field 
  label="パスワード"
  v-model="signupForm.password"
  :type="show ? 'text' : 'password'"
  :append-icon="show ? 'mdi-eye' : 'mdi-eye-off'"
  @click:append="show = !show"
></v-text-field>

type属性をshowによって紐づけ(bind)て、入力フォームのタイプを切り替えている
iconもshowによって紐づけ(bind)て、アイコンの見た目を切り替えている

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt.js】Nuxt文法編:component①

? この記事はWP専用です
https://wp.me/pc9NHC-DX

前置き

componentとは
使いまわしができる、
再利用ができるものです?

button.gif

シンプルなボタンでも
CSSが長くなったりして
まいかい書くのは面倒ですよね…?
それを解決してくれるのが
componentです?

作り方、表示のさせ方を解説していきます✍️

目次

前置き
componentの種類と使用箇所
基本コード
Step1: 使い回すコンポーネントを作る
Step2: コンポーネントをインポートする
自動でインポートする
まとめ

componentの種類と使用箇所

種類

まずは種類を把握しましょう。
おおまかに全部で3つあります?
今回は1の自作のコンポーネント
について解説していきます?
前置きに書いたボタンのような場合は
自作のコンポーネントに該当します?‍♀️

  1. <好きに命名した名前 />  =自作のコンポーネント  importが必要!
  2.  layouts内でのみ使用
  3.  ネストされたルート内で使用

nuxtとnuxt-childについては
別記事でご紹介します?

使用箇所

componentsが使える場所
(インポートできる場所)は
ページを構成する部分です!

・layout
・他のコンポーネント
・pageコンポーネント
自作のコンポーネントの場合は
この中のどれでも使用可能です?

基本コード

基本コードがこちら。
あとは使いまわしたい物に
作り替えていきましょう?✨

? 続きはWPでご覧ください?
https://wp.me/pc9NHC-DX

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

History APIでルーティングしてる時の存在しないページへのフォールバックってどうすんの?

JavaScriptでSPAのルーティングを実装するためHistory APIを使っていたところ、操作した後のパスでリロードをした時にこんな画面になりました。

image.png

404です(WARNINGは拡張機能起因のものなのでスルーします)。
ちなみにサーバーはVSCodeのLive Server Extensionを利用しています。

なぜ?と思う前に冷静にこのアプリのディレクトリ構成を確認します。

|-- index.html
|-- style.css
|-- js
    |-- main.js
    |-- router.js

よく見れば当たり前の話です。newPost.htmlなんて存在せず、index.htmlしかないので、ルートとなるhttp://127.0.0.1:5500/以外はそもそも存在しません。そりゃ404にもなります。

あれ、でもVueとかNuxt使ってる時に直接このパス見に行ってもページ表示できたよな?あれどうなってんだと思い調べてみることにしました。

Vue Routerのドキュメントをちゃんと読む

Vueのルーティングを司っているVue Routerのドキュメントを確認すると答えはありました。

HTML5 History モード

どうやらhashとhistoryという2つのモードがあるみたいです。

hashモード

vue-router のデフォルトは hash モード です - 完全な URL を hash を使ってシミュレートし、 URL が変更された時にページのリロードが起きません。

hashモードってアレだ!URLがhttps://example.com/#/userみたいに#が間に含まれてるやつだ。思い出した。昔Angular.jsで開発してたときもHistory APIがどうのこうのってこのhashモードを使ってました。

hashモードが動く理由は、URLのアンカー#の動きを利用してhttps://example.com/#/userの例で言えば実際はhttps://example.comを見に行くという理解を得ました。となるとその中で更にアンカーでのスクロールを実装したいときどうなるんだろう?https://example.com/#/user#profileみたいなケース。……これは別の機会に調査します。

historyモード

history モードを使用する時は、URL は "普通" に見えます e.g. http://oursite.com/user/id。美しいですね!

しかしながら一点問題があります。シングルページのクライアントサイドアプリケーションなので、適切なサーバーの設定をしないと、ユーザーがブラウザで直接 http://oursite.com/user/id にアクセスした場合に 404 エラーが発生します。

まんまこの問題ですね。

心配する必要はありません。この問題を直すためには、単純な catch-all フォールバックのためのルートをサーバー側で追加するだけです。もし URL がどの静的なアセットにもマッチしなかった時はあなたのアプリケーションが動作しているのと同じ index.html ページで受け付けましょう。これも美しいですね!

なるほど!フロント側ではやはりどう足掻いても駄目みたいですね。Vue CLIやらNuxtでの開発は便利ですが、こういうのも隠蔽して裏側でやってくれていたということっぽい。たまにはフレームワーク抜きでJavaScript触るのも学びがあって良いですね。

History API利用時のフォールバックを設定する

HTML5 History モード

上記のページに以下のケースでのフォールバックは設定例があるのでこのまま使えそうです。

  • Apache
  • nginx
  • Node.js
  • Express
  • IIS
  • Cady
  • Firebase

これ以外で今回私が個人的に使ってるLive Server ExtensionとNetlifyでの実装も調べたのでここに残しておきます。

Liver Server Extension

VS Codeの設定ファイルに以下を追記します。

{
  "liveServer.settings.file": "index.html"
}

念の為、個人設定ではなくワークスペース単位での設定をオススメします。

Netlify

_redirectsファイルに以下を追記します。

/*   /index.html   200

こによりどのパスでアクセスしてもindex.htmlが参照されます。

サーバーにフォールバックを設定した際の注意点

サーバーに上記のフォールバックを設定した時点で、あらゆるアクセスはindex.htmlにリダイレクトされます。つまり、以下のようなディレクトリ構成で:

|-- index.html
|-- user.html
|-- item.html

https://example.com/userにアクセスしようが、https://example.com/itemにアクセスしようが,
実態はhttps://example.com/にアクセスがリダイレクトされます(ただしURLは/user//itemがついた状態)。なので、以下のようなSPAを前提とした実装が必要になります。

  • 完全なるルーティングの制御
    • ページロード時(初期表示時)、現在のパスを取得し対応するコンポーネントを表示する
    • 対応するコンポーネントが存在しないパスを表示時、404を表すページを表示する

完全なるルーティングの制御の実装はなかなか骨が折れます。Historyの状態をpush/popする度にコンポーネントの描画を切り替える実装が必要になってきます。VueやReactなどのSPA作れるフレームワークを使う時はセットとなるルーターのライブラリを使いましょう。勉強以外の目的で素のJavaScriptでSPAつくることなんかせず、おとなしくフレームワーク使うことをオススメします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

失敗なんか気にせずとにかくPromise.all()でぜんぶやる

こんなことがあった

  1. あるAPIにアクセスすると本日執筆された記事IDの一覧が帰ってくる
  2. その記事IDを使って記事の内容を取得できるAPIにアクセスして、記事を取得したい

とりあえず実装する

前提: 以下で登場するインスタンス、メソッドには架空のものがあります。このようなことがしたいのだろう。程度に解釈してください。

何も考えず実装するとこのようになった。

const fetchEntryIds = (): Promise<Array<string>> => {
  return request.get('xxxxx/entries/new');
};

const fetchEntry = (entryId: string): Promise<Entry> => {
  return request.get(`xxxxx/entries/${entryId}`);
};

const fetchTodaysEntries = async (): Promise<Array<Entry>> => {
  const entryIds: Array<string> = await fetchEntryIds();

  return Promise.all<Entry>(entryIds.map<Promise<Entry>>((entryId: string) => {
    return fetchEntry(entryId);
  }));
};

期待したこと

Promise.all()Array<Promise<?>>Promise<Array<?>>にしてくれる便利なメソッドであり、内部のPromise<?>の動作を並列に行ってくれる。

-> 記事をまとめて取得できるのでは!?

?今回起きたこと

  1. 当たり前だがAPIリクエストは失敗することがある
  2. Promise.all()は内包するすべてのPromiseがfulfilledにならないとfulfilledにならない

そのためリクエストがひとつでもrejectedになってしまうとPromise.all()はrejectedになり、その値が返却される。悲しいことにそれまでいくつかfulfilledになっているPromiseがあったとしてもなかったことになる

図解

以下の記号を使います。

  • pending = ?
  • fulfilled = ?
  • rejected = ?

以下は左辺にこれらの絵文字を配列状に筆記したもので、右辺はそれをPromise.all()で実行した場合の結果です。

[?, ?, ?, ?, ?] => ?
[?, ?, ?, ?, ?] => ?
[?, ?, ?, ?, ?] => ?
[?, ?, ?, ?, ?] => ?
[?, ?, ?, ?, ?] => ?
[?, ?, ?, ?, ?] => ?
[?, ?, ?, ?, ?] => ?
[?, ?, ?, ?, ?] => ?

今回やりたいこと

失敗でいいからPromise.all()の動作を遮らずにすべて実行してほしい。

"実装": "0.1.0"

今回の実装は外部パッケージを一切使わずにできました。
あとで見返せば当たり前じゃないかって思いますが、思いついたときは若干感動しました。

const fetchTodaysEntries = async (): Promise<Array<void | Entry>> => {
  const entryIds: Array<string> = await fetchEntryIds();

  return Promise.all<void | Entry>(entryIds.map<Promise<void | Entry>>((entryId: string) => {
    return fetchEntry(entryId).catch(() => {});
  }));
};

こつ

一度rejectedになったPromiseをfulfilledに戻します。そのためにpromise.catch()で例外を捕捉し、その捕捉にしかるべき処理を施します。今回は握り潰しましたごめん

問題点

promise.catch()の戻り値が記事のクラスEntryではないためPromise.all()の戻り値のジェネリクスが<Entry>から<void | Entry>に変わりました。fetchTodaysEntries()を使う人はその戻り値についてundefinedかどうかの判定が必要です。

他にNull Object patternや番兵を使って動作を制御させる方法もありますが、以下はそれよりもより良いだろう方法の紹介です。

"実装": "1.0.0"

Discriminated unionsを使います。

Discriminated unions

複数の型が同名のプロパティを持ち、そのプロパティの型が違うことがわかっていれば、それらから構成されるUnion typeはそのプロパティの型が何かを判定できればTypeScriptはUnion typeがどの型であるかを確定できます。

よくある例

人気No.1モナドのOptional<T>を部分的に実装する例が最も有名です。

type Some<T> = {
  present: true;
  value: T;
};

type None = {
  present: false;
};

type Optional<T> = Some<T> | None;

このOptional<T>において複数の型が同名のプロパティを持ち、そのプロパティ型が違うことがわかっていればとはpresentです。presentSome<T>, Noneでは異なるLiteral typeを持っているので、この値の判定で変数がSome<T>Noneのどちらかであることを判定できます。

if (optional.present) {
  // TypeScriptはoptional = Some<T>と解釈している
  optional.value // ?
}
else {
  // TypeScriptはoptional = Noneと解釈しているため以下はできない
  optional.value // ?
}

今回用意したDiscriminated unions

今回はTry<T>のモナドを模しました。

type Success<T> = {
  success: true;
  value: T;
};

type Failure = {
  success: false;
  err: unknown;
};

type Try<T> = Success<T> | Failure;

あとはこれに沿うように成功時はSuccess<T>、例外発生時はFailureのオブジェクトリテラルで包みます。

これが1.0.0です。

const fetchTodaysEntries = async (): Promise<Array<Try<Entry>>> => {
  const entryIds: Array<string> = await fetchEntryIds();

  return Promise.all<Try<Entry>>(entryIds.map<Promise<Try<Entry>>>((entryId: string) => {
    return fetchEntry(entryId).then((entry: Entry) => {
      return {
        success: true,
        value: entry
      };
    }, (err: unknown) => {
      return {
        success: false,
        err
      };
    });
  }));
};

promise.then()

使用例が珍しいですがpromise.then()は第2引数を受けることができます。この第2引数のコールバックは例外発生時のことで、じつはpromise.catch()の第1引数と同じです。そのため正常時と例外発生時をpromise.then()ひとつで処理できます。

もちろんpromise.catch()を使う方法も可能ですが、オブジェクト生成コストが内部的には少し増えます。気持ちの問題程度の差だとは思うので読みやすさを考慮して適宜選択してください。

const fetchTodaysEntries = async (): Promise<Array<Try<Entry>>> => {
  const entryIds: Array<string> = await fetchEntryIds();

  return Promise.all<Try<Entry>>(entryIds.map<Promise<Try<Entry>>>((entryId: string) => {
    return fetchEntry(entryId).then((entry: Entry) => {
      return {
        success: true,
        value: entry
      };
    }).catch((err: unknown) => {
      return {
        success: false,
        err
      };
    });
  }));
};

?async/awaitでやりたい!

できます。手を加えなければいけないところが少々変わります。

fetchEntry()

const fetchEntry = async (entryId: string): Promise<Try<Entry>> => {
  try {
    const entry: Entry = await request.get(`xxxxx/entries/${entryId}`);

    return {
      success: true,
      value: entry
    };
  }
  catch (err: unknown) {
    return {
      success: false,
      err
    };
  }
};

array.filter()できれいにできる

Type predicateを戻り値に持つarray.filter()がTypeScriptのArrayにはoverloadで定義されているのでSuccess<T>だけ、Failureだけが必要ならそれを使います。

const extractSuccess = (arr: Array<Try<Entry>>): Array<Success<Entry>> => {
  return arr.filter((t: Try<Entry>): t is Success<Entry> => {
    return t.success;
  });
};

const extractFailure = (arr: Array<Try<Entry>>): Array<Failure> => {
  return arr.filter((t: Try<Entry>): t is Failure => {
    return !t.success;
  });
};
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript if文の書き方

初投稿兼投稿の練習になります。プログラミングもまだまだ初心者ですので、内容も恐らく初歩的な内容になります。暖かく見守って頂けると幸いです。

if文の書き方

if(条件式) {
  条件式がtrueの時に実行する処理
} else {
  条件式がfalseの時に実行する時の処理
}

上記の書き方をすることで、条件式が成立するか否かで処理を分けることができます。

例えば

if(window.confirm('確認できましたか?')) {
  console.log('確認済です');
} else {
  console.log('まだ確認できていません');
}

 window.confirmは確認ダイアログボックスを表示します。確認ダイアログボックスにはOKボタンとキャンセルボタンがあり、OKをクリックするとtrue、キャンセルをクリックするとfalseの値が返ってきます。
 なので、OKをクリックすると条件式がtrueとなり、console.log('確認済です')が実行され、キャンセルをクリックすると条件式がfalseとなり、console.log('まだ確認できていません')が実行されます。

ちなみに

if(true) {
  console.log('確認済です');
} else {
  console.log('まだ確認できていません');
}

とすると、初めから条件式がtrueとなっているので、処理は分岐せず、true時の処理を実行することになります。条件式をfalseとすると同様に、false時の処理が実行されます。

以上です。ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自分用メモ Fetch API javascript

はじめに

javascript含め、プログラミングを学び始めた初心者なのですが、javascriptのfetchのメソッドについて勉強したのでここでアウトプットしておこうと思います。

自分は周りにプログラミングをしている人がいないので情報収集やアウトプットをする機会がないのですが、この記事がそのような問題を解決してくれるようにQiitaを通して、アウトプットしておりますので、何か間違った点やアドバイスなどをしていただければ、大変助かれます。

FETCH APIとは

fetchとは、とってくるや呼び出す、引き出すなどの意味を持つ英単語
ITの分野だと情報などのデータを取ってくる動作のことを指すことが多い。

FETCH APIとは、ページの外部からリソースを取得するためのインターフェースを定義した、Webブラウザの標準API。リソースを取得するためのAPIを定義している。

javascriptなどのいくつかあるHTTPメソッドの中の一つ。
そしてfetchを利用するとリクエストやレスポンスといったHTTPのパイプラインを構成する要素を操作できるようになる。また非同期のネットワーク通信を簡単にわかりやすく記述できるようになる。
往来ではXMLHttpRequestを使用して実現されていたが、fetchはそれの上位互換的存在で。他の技術から簡単に利用することができる。
そのためXMLHttpRequestに変わる、モダンで他の技術と連携しやすい非同期通信といえる。

特徴

1.fetchが返されるPromiseはレスポンスがHTTP404や500だったとしてもrejectされない。そして代わりにResponse.okがboolean型で返される。
fetch自体エラー受け取ってエラーを表示するレスポンスを受け取って表示する役割がある。

2.fetchはクッキーを受信できる
cookieとは
webサイトの提供者が、webブラウザを通じて訪問者のコンピューターに一時的にデータを書き込んで保存させる仕組み

3.fetchはサーバ間でクッキーの送受信は行われない。サイトがユーザーのセッションに頼っている場合未認証のリクエストとなる。
headerの追加のカスタムで解決できる。

使い方

一般的な利用方法

fetch('https://example.com/example.json')
  .then(response => response.json()
  .then(data => console.log(data)

fetchした後の引数はresponseになる。
responseをJSON型で抽出するにはjsonメソッドを使う。

引数にinitオブジェクト

initオブジェクトhttpのヘッダやメソッド名bodyの設定などの多数の設定が行えるオブジェクト。

async postData (url = "", data = {}) => {
  const response = await fetch(url, {
    method: 'POST',
    mode: 'cors',
    cache: 'no-cache',
    credentials: 'same-origin',
    headers: {
      content-type: 'application/x-www0form urlenceded'
    },
    redirect: 'follow',
    referrerPolicy: 'no-referrer',
    body: 'Json.stringify(data)'
  })
  return response.json()
} 
postData(`https://example.com/answer`, {anser: 42})
  .then(data => {
    console.log(data)
  });

postDataでは最後にresponseをjsonにしてreturnしているので、呼び出し元ではjsonメソッドを使うことなくそのまま引数にとってコンソールで表示している。

認証情報付きリクエスト

initオブジェクトで

credentials: 'include'

リクエストURLと呼び出し元が同一のオリジンの場合は、
credentials:'same-origin'

この代わりにブラウザがcredentialを含んでいないことを保証させたい時は、そして常にクッキーの送信をしたい時は、
credentials: 'omit'

オリジン
ドメイン名、プロコル、ポート番号

CORS
オリジン間リソース共有...あるオリジンで利用されているアプリケーションのアクセス権を異なるオリジンである選択されたリソースへのアクセス権を与えるようにブラウザに指示する仕組み。

アップロード

メソッドはPOST
bodyはJson.stringify()でエンコードする。

ファイルアップロード

のinput要素、FormData(),fetch()を使ってアップロードする

const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');

formData.append('title', 'My Vegas Vacation');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/users/search',{
  method: 'PUT',
  body: formData
})
.then(response =>//以下省略)

複数のファイルのアップロードをする時は, input要素にmultipleを入れる。

fetchの成功判断

上記で述べたようにfetchには通信やネットワークなどの外部の妨げによるエラー以外では、エラーが返ってこないようになっている。
fetch()が成功したかどうかは、返ってきたresponseのなかのresponse.okプロパティを見て判断する。trueになっていた場合は、成功となる。

fetch(url)
.then(response => {
  if(!response.ok){
    throw new Error('Network response was not ok');
  }
  return response.json();
 });

上記のようにして、エラーが返ってこないためresponse.okプロパティをチェックして、falseの時はエラーを吐くようにしておく!

独自のrequestオブジェクトをfetchに渡す

fetchには、独自で作ったrequestのオブジェクトを引数として渡せられるようになっている。

const myHeaders = new Headers();

const myRequest = new Request('url', {
  method: 'GET',
  headers: myHeaders,
  mode: 'cors',
  cache: 'default',
});

fetch(myRequest)
  .then(response => response.json())
  .then(data => {
    mySrc.src = URL.createObjectURL(data)
  });

fetch()メソッドの引数と全く同じ引数をRequest()に適用させることができる。

const anotherRequest = new Request(myRequest, myInit);

これはリクエストとレスポンスの本文を一つだけ使用するのでとても有用。
必要に応じてinitオプションを変化させながらリクエスト/レスポンスを再利用できるようにコピーする。
一つ目の引数にはrequestをそして二つ目のinitオブジェクトにはresponseを書くことができる。

Headers

Headersインターフェースでは、Headers()コンストラクターを使って、headerオブジェクトを作成することができる。
入ってくるのは、配列の配列かオブジェクトリテラルになる。

//配列の配列
const myHeaders = new Headers();
  myHeaders.append('Content-Type': 'text/plain');
  myHeaders.append('Content-length': content.length.toString());
  myHeaders.append('x-Custom-Header', 'ProcessThisImmediately');

//オブジェクトリテラル
const myHeaders = new Headers({
  'Content-Type': 'text/plain',
  'Content-Length': content.length.toString();
  'X-Custom-Header': 'ProcessThisImmediately'
});

これで追加することができる。そしてheaderを参照できるgetや削除するdeleteやあるかないかをbooleanで返すhasそしてsetなどもできる。

headerの使用例
fetch(myRequest){
  .then(response => {
    const contentType = response.headers.get('content-type');
    if(!contentType || !contentType.includes('application/json')){
      throw new TypeError("Error");
    }
    return response.json();
  }); 

上記のように処理を行う前にコンテントタイプが正しいか判断するときなどに使う!

Responseオブジェクト

fetch()のプロミスが解決(resolve)されたときに返り値としてResponseインスタンスが渡される。
以下がresponseで必ず生成されるプロパティ

Response.status
HTTPステータスコード(デフォルト200)
Response.statusText
HTTPステータスコードのメッセージと一致する文字列(デフォルトはOK)
Response.ok
HTTPのステータスコードが200から299だったときにtrueを返すboolean型のプロパティ(fetchが成功したかの判断材料になる)

そしてResponseオブジェクトはjavascriptで動的に作ることもできる。respondWith()メソッドを使ってカスタマイズされたレスポンスを返すようなときに役立つ。

const myBody = new Blob();

addEventListener('fetch', fucntion(event){
  event.respondWidth(
    new Response(myBody, {
      headers: {'Content-Type': 'text/plain'}
    })
  );
});

Body

リクエストもレスポンスもボディを持っている。

ArrayBuffer
ArrayBufferView
Blob/File
文字列
URLSearchParams
FormData

各コンテンツを抜き出すメソッド。プロミスを返す。

arrayBuffer()
blob()
json()
text()
formData()

まとめ

まだまだわからないところも多く、知識も浅いためAPIの知識を深めるためにもfetchだけでなく、apiについては、深い知識がつけられるよう勉強を続けていきたいと思っております。多々至らない点多くあったと思いますが、暖かく見ていただければと思います。
何か自分自身気づいたことがあれば更新していきたいと思います。

参照

Fetchの使用 MDN

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

日付をYYYY-MM-DDの書式で返す関数を理解しよう

書籍で解説もしてくれないので、自分でまとめてみた

どんな関数を使うのか

関数

formatDate.js
#日付をYYYY-MM-DDの書式で返すメソッド

date = new Date();

function formatDate(date) {
  var y = date.getFullYear();
  var m = ('00' + (date.getMonth()+1)).slice(-2);
  var d = ('00' + date.getDate()).slice(-2);
  return (y + '-' + m + '-' + d);
}

formatDate(new Date());
=> '2020-09-08'

解説

  • new Date()
    指定した日時を表す日付オブジェクトを生成する。指定をしなかったら今日の日付で生成されちゃう。

  • date.getFullYear();
    年を4桁の整数で取得する

  • ('0' + (date.getMonth()+1)).slice(-2);
    getMonth()で0-11を生成。0→1月、11→12月となるので、生成された数字に+1で正確な月にできる。また今回は「9」ではなく「09」という形で表示させたいので、先頭で「0」を付ける。
    .slice(-2)は後ろから2文字を取得でき、9月の「009」が生成されば「09」となってくれる。

  • ('0' + date.getDate()).slice(-2);
    date.getDatedate.getMonthと違って、数字は「1」から始まる。

  • return (y + '-' + m + '-' + d);
    最後にそれぞれを連結させて返すことで、「2020-09-08」が表示される。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript~配列の操作~その2

はじめに

JavaScriptでの配列の操作
filter,map,reduce,joinについて
この4つは、全て非破壊的(元の配列は変更しない)メソッド

前回:JavaScript~配列の操作~その1

使用する配列

const users = [
  { name: "A", age: 23, country: "ja" },
  { name: "J", age: 27, country: "usa" },
  { name: "R", age: 64, country: "usa" },
  { name: "Y", age: 48, country: "ja" },
  { name: "B", age: 33, country: "ja" },
]

※これ以外の配列を使う場合はコード内に記載

filter (配列内から一致するものを配列で返す)

const countryFilter = users.filter(user => user.country === "usa")
console.log(countryFilter)
/*
[
  { name: 'J', age: 27, country: 'usa' },
  { name: 'R', age: 64, country: 'usa' }
]
 */
const ageFilter = users.filter((user) => {
  if (user.age > 30) {//ageが30より大きい
    return user
  }
})
console.log(ageFilter)
/*
[
  { name: 'R', age: 64, country: 'usa' },
  { name: 'Y', age: 48, country: 'ja' },
  { name: 'B', age: 33, country: 'ja' }
]
 */

第一引数に配列要素、第二引数mapと同様にインデックスが取れる

map (元の配列から新しい配列を作ったり、変更を加えたり)

const nameMap = users.map(user => user.name)//nameだけの配列を作る
console.log(nameMap)//[ 'A', 'J', 'R', 'Y', 'B' ]

const ageMap = users.map(user => user.age)//ageだけの配列を作る
console.log(ageMap)//[ 23, 27, 64, 48, 33 ]

const countryMap = users.map(user => user.country.toUpperCase())//大文字に変換
console.log(countryMap)// [ 'JA', 'USA', 'USA', 'JA', 'JA' ]

const ageAddintion = ageMap.map(age => age + 10)//ageに10足す
console.log(ageAddintion)//[ 33, 37, 74, 58, 43 ]

//変更を加えた配列を合わせて新しい配列を作る
const nameAndAge = nameMap.map((name, i) => ({name: name, age: ageAddintion[i], country: countryMap[i]}))
console.log(nameAndAge)
/*
[
  { name: 'A', age: 33, country: 'JA' },
  { name: 'J', age: 37, country: 'USA' },
  { name: 'R', age: 74, country: 'USA' },
  { name: 'Y', age: 58, country: 'JA' },
  { name: 'B', age: 43, country: 'JA' }
]
*/
console.log(users)//元の配列はそのまま
/*
[
  { name: 'A', age: 23, country: 'ja' },
  { name: 'J', age: 27, country: 'usa' },
  { name: 'R', age: 64, country: 'usa' },
  { name: 'Y', age: 48, country: 'ja' },
  { name: 'B', age: 33, country: 'ja' }
]
*/

長さの違う配列をいれると

const test = [ 33, 37, 74, 58, 43, 21, 40 ]//21, 40を増やした

const sample1 = nameMap.map((name, i) => ({name: name, age: test[i], country: countryMap[i]}))//age部分を変える
console.log(sample1)//nameMapの長さが基準になるので増やした二つは表示されない
/*
[
  { name: 'A', age: 33, country: 'JA' },
  { name: 'J', age: 37, country: 'USA' },
  { name: 'R', age: 74, country: 'USA' },
  { name: 'Y', age: 58, country: 'JA' },
  { name: 'B', age: 43, country: 'JA' }
]
*/
//testを基準にすると
const sample2 = test.map((test, i) => ({name: nameMap[i], age: test, country: countryMap[i]}))
console.log(sample2)
/*
[
  { name: 'A', age: 33, country: 'JA' },
  { name: 'J', age: 37, country: 'USA' },
  { name: 'R', age: 74, country: 'USA' },
  { name: 'Y', age: 58, country: 'JA' },
  { name: 'B', age: 43, country: 'JA' },
  { name: undefined, age: 21, country: undefined },
  { name: undefined, age: 40, country: undefined }
]
*/
//[5][6]にname, countryの値はないのでundefinedになる

第一引数に配列要素、第二引数にインデックスが取れる、第三引数には配列そのものが入る

reduce (配列内を合計したりとか)

const numbers = [3, 7, 1, 8, 15]
const sum1 = numbers.reduce((a, num) => a + num, 0)//aの初期値は0
console.log(sum1)//34

//第一引数が初期値になりnum, 0としている部分の0がaに入る事になる
const sum2 = numbers.reduce((a, num) => a + num, 100)//aの初期値は100
console.log(sum2)//134

//初期値が0などの場合省略できる
const sum3 = numbers.reduce((a, num) => a + num)//aの初期値は配列の[0]の3になり、そこに順番に足していくイメージ
console.log(sum3)//34

const hello = ["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"]//この様な文字列の配列も一つの値にできる
const world = hello.reduce((a, b) => a + b)
console.log(world)//HelloWorld

イメージとしては

let a = 0
    a += 3//0 + 3
    a += 7//3 + 7
    a += 1//10 + 1
    a += 8//11 + 8
    a += 15//19 + 15
console.log(a)//34

join (配列内の文字列を一つの文字列にまとめる)

const hello = ["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"]//この様な文字列の配列も一つの値にできる
const world = hello.join()
console.log(world)//H,e,l,l,o,W,o,r,l,d

第一引数はセパレータで、デフォルトは,
文字列同士の間になにを入れるか指定できる

const world1 = hello.join("-")//-で区切る
console.log(world1)//H-e-l-l-o-W-o-r-l-d

const world2 = hello.join("")//空文字
console.log(world2)//HelloWorld

以上です!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

viewportでレスポンシブしている時にPC版表示をする

環境
初書:2020/09/08

タイトルの付け方が適当な気がしてならないが、いいのが思いつかなかった

前提

<meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1.0">

viewportdevice-widthを使用していて、cssで@media screen and (max-width: 600px)のような感じでレスポンシブウェブデザインに対応させている場合

javascriptで動的変更する

…ことが出来る。しかも一行で

document.getElementsByName("viewport")[0].setAttribute('content','width=1080, initial-scale=1.0,minimum-scale=1.0');

document.getElementsByName("viewport")で、metaタグを取得する事が可能なのでsetAttributecontent箇所を書き換える。
[0]なのは、viewportという名前はおそらくこれしかないだろうという観点から。もし他の要素にviewportという名前を付ける場合は、単純に[0]にするとバグが生じるかもしれない)

注意点

viewportを書き換える形の場合、スマホの横幅に合わせてサイズが細かくなるわけではなく、
画面外に伸びてサイズが変更されるので(下の図の感じ)、cssでvwを使ってサイズを指定している箇所はレイアウトが崩れる。

 |---------------------------------------------| //width=1080に設定した時
 |------------------| // スマホ画面(400くらい?左右にスクロールが出来るようになる)

参考

もう逃げない。HTMLのviewportをちゃんと理解する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyの古いgemで自動ファイルアップロード Watirでの対処法

本稿について

WatirでWebサイトに対してファイルアップロードするスクリプトを書いていましたが、
古い環境で情報が見つけづらく困ったところ解決したので備忘録です。

環境

 CentOS Linux 7
 Ruby 2.0.0p648
 仕様gemはWatir
 ブラウザはFirefoxを使っていますが、Chromeなど、別のブラウザでも

やりたいこと

あるWebサイトに対して、Watirをつかって自動スクリプト起動でファイルアップロードさせたい。
アップロード画面ではSelect file というボタンを押して、ファイル選択画面が出た際にそこからファイルを選んでアップロードさせる仕様

躓いたこと

アップロード画面からファイル選択をする挙動ができておりません。
OS標準のファイル選択画面が開いた時点で、ブラウザ外の動作になってしまうのかスクリプトの操作が及びません。

試したこと

  1. watir内部でjavascriptを実行できるので、 クリップボードなどを使ってコピペができないか など試しましたがそもそも画面の仕様がスクリプトでのクリップボード経由の ペーストを受け付けていないでいるらしく、ペーストがエラーも吐かずに静かに何も起こらず終了してしまいます。
  2. 強引にキーイベントをjavascriptから発動させる。 下記のようなコマンドを実行して強制的にEngerキーやTabキーなど打つことできないか試しましたが、 ブラウザ自体にフォーカスがあたってしまっている?ようで、ファイル選択画面がキーコマンドを受け付けないでいます。
    browser.execute_script("
                document.dispatchEvent( new KeyboardEvent( 'keydown', { keyCode: 86 , ctrlKey: true , key: 'V' }) );
    ")

(参考:https://ameblo.jp/personwritep/entry-12456996738.html)
などなど試しましたが、対象のファイルを読みに行くなどの動作は行ってくれません。

Webサイトの仕様・画面イメージ

ファイルアップロード用の画面が下記で、Select fileボタン押す事ができます。

image.png

すると下記のようなOS標準のファイル選択画面が出て、そこからファイルをアップロードするような作りになっています。
image.png

人の手で操作する際はSelect fileボタンを押してからファイルを選択するか、ドラッグ&ドロップでファイルアップロードすることも可能です。

出来た

結果、これでできてしまいました。

$browser.file_field(:id, //).set("filepath\/...\/file.png")

参考にした記事

https://stackoverflow.com/questions/15163816/automating-a-file-upload-with-watir-in-chrome-on-osx
https://www.rubydoc.info/gems/watir/1.8.1/Watir%2FContainer:file_field

最後に一言

いやWatirて。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

無料でSSR・ホスティング・API鯖を立てれるVercel。TypeScript・ExpressでAPI鯖を立てる。

Vercel
https://vercel.com

無料で有名なFaaS(Function as a Service)の
Firebase FunctionsNetlify Functionsより使い勝手がいいと思う。

GitHubの捨てアカ作ってログインしようとしたら弾かれた
たぶんアカを作ってからある程度時間が経たないと弾かれるっぽい。

ソースコード

package.json
{
  "scripts": {
    "ts-build": "webpack --mode production"
  },
  "devDependencies": {
    "@types/express": "^4.17.8",
    "ts-loader": "^8.0.3",
    "typescript": "^4.0.2",
    "webpack": "^4.44.1",
    "webpack-cli": "^3.3.12",
    "webpack-node-externals": "^2.5.2"
  },
  "dependencies": {
    "express": "^4.17.1",
    "vercel": "^20.1.0"
  }
}
src/index.ts
import * as Express from 'express';

const app = Express();

// postリクエスト使えるようにする
app.use(Express.json());
app.use(Express.urlencoded({ extended: true }));

app.get('/get/:name', (req: Express.Request, res: Express.Response) => {
  try {
    res.send({ name: req.params.name });
  } catch (error) {
    res.sendStatus(500);
  }
});

app.post('/post', (req: Express.Request, res: Express.Response) => {
  try {
    res.send({ name: req.body.name });
  } catch (error) {
    res.sendStatus(500);
  }
});

if (!process.env.NOW_REGION) {
  app.listen(process.env.PORT || 3000);
}

export default app;
tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "src/*": ["src/*"]
    },
    "strict": true
  }
}
vercel.json
{
  "version": 2,
  "builds": [
    {
      "src": "index.js",
      "use": "@now/node-server"
    }
  ],
  "routes": [
    {
      "src": "/.*",
      "dest": "/index.js"
    }
  ]
}
webpack.config.js
const path = require('path');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  // モード値を production に設定すると最適化された状態で、
  // development に設定するとソースマップ有効でJSファイルが出力される
  mode: 'development',
  entry: './src/index.ts',

  // ファイルの出力設定
  output: {
    path: `${__dirname}/`,
    filename: 'index.js',
    libraryTarget: 'this',
  },

  module: {
    rules: [
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        loader: 'ts-loader',
        options: {
          configFile: path.resolve(__dirname, 'tsconfig.json'),
        },
      },
    ],
  },
  // import 文で .ts ファイルを解決するため
  // これを定義しないと import 文で拡張子を書く必要が生まれる。
  resolve: {
    // 拡張子を配列で指定
    extensions: ['.ts', '.js'],
    alias: {
      src: path.resolve(__dirname, 'src'),
    },
  },

  externals: [nodeExternals()],
};
.gitignore
.vercel
node_modules
index.js

コマンド

TypeScriptをビルド

$ npm run ts-build

ローカルで実行 ポート番号3000で鯖が立ち上がる

$ npx vercel dev

デプロイ

$ npx vercel --prod

ワイの成果物

https://qiita.com/yuzuru2/items/b5a34ad07d38ab1e7378

①コード共有サイト(SPA) React
https://code.itsumen.com

②画像共有サイト(SPA) React
https://gazou.itsumen.com

③チャット(SSR) Nuxt.js
https://nuxtchat.itsumen.com

④チャット(SPA) React
https://chat4.itsumen.com

⑤掲示板(SSR) Next.js
https://board.itsumen.com

⑥掲示板(SPA) Vue
https://board.itsumen.com

⑦レジの店員を呼ぶスマホアプリ(Android)
https://play.google.com/store/apps/details?id=com.itsumen.regi&hl=ja

⑧ブログ(静的サイトジェネレータ) Hugo
https://yuzuru.itsumen.com

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript・関数学習・output】

◆JavaScriptの関数の種類や違い。

▼関数定義の種類:特徴

・関数宣言/関数式:標準的な関数の定義。

・無名関数:関数を多く使用する時にコードである時に使用する。関数名の重複を避けることができる。

・即時関数:流用する可能性がない関数を定義する時に使用する。別途関数を定義する必要がない。

・アロー関数:無名関数または即時関数において、より省力したい時に使用する。

以降は具体的な記述式を書いていきます。

▼関数宣言/関数式
関数宣言の記述↓

function 関数名 ( 引数 ) {
  //関数内の処理
}

関数式の記述↓

変数 = function ( 引数 ) {
  //関数内の処理
}

・上記の関数宣言と関数式の違いは読み込まれるタイミングが異なることにあります。関数宣言が先に読み込まれます。実害はあまりないそうですが特徴として覚えておきます。

▼無名関数

無名関数の記述↓

const hello = function(){
console.log('hello')
}

・関数名無しで関数を定義できるためより簡潔にコードを記述できる。

▼即時関数

即時関数の記述↓

(function countNum(num) {
console.log(num)
})(1)

・()の中にfunctionから始まる関数定義そのものを配置することで、配置した関数を即実行する。
関数を呼び出す手間がはぶけます。

▼アロー関数

アロー関数の記述↓

const 変数名 = () => {
処理
}

・近年採用される事例が増えた、関数式の記述様式・・・・らしい。
functionを省力し()=>と記述し関数を定義する構文。より短いコードで関数を定義できる。

◆end
・私が学んだJavaScriptにある関数の種類outputは以上です。
最初ははてなブログをすすめられましたが、学んだ内容をoutputするにはQiitaが良いとあらためて感じました。
知らないメソッド、オブジェクトの概念、変数、関数、私はRuby,Ruby on Rails,JavaScript,GitHubを現時点で(2020/9/8)学んでいます。
学んだ内容をまとめたい時にまた書かせていただきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

論理演算子はboolean以外も返す const = AA | BB みたいなの

JavaScriptの「&&」「||」について盛大に勘違いをしていた件 - Qiita

const _perview = Number($body.getAttribute('data-perview')) || 'auto';

えっなにこれ、って思ってた。

Number($body.getAttribute('data-perview'))trueであればNumber($body.getAttribute('data-perview'))を返す。そうでなければ'auto'を返す。

そういう使い方出来るのね…if文的な時でしか使えないと思ってたよ本当に…

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

論理演算子はboolean以外も返す const = AA || BB みたいなの

JavaScriptの「&&」「||」について盛大に勘違いをしていた件 - Qiita

const _perview = Number($body.getAttribute('data-perview')) || 'auto';

えっなにこれ、って思ってた。

Number($body.getAttribute('data-perview'))trueであればNumber($body.getAttribute('data-perview'))を返す。そうでなければ'auto'を返す。

そういう使い方出来るのね…if文的な時でしか使えないと思ってたよ本当に…

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Microsoft Teamsにメッセージと画像を投稿(Node.js)

Teamsのチャンネルに投稿用アカウント(Incoming Webhook)を追加して、
チャンネルにメッセージや画像を投稿する

1.投稿したいチャンネルにIncoming Webhookを追加

  • Teamsの投稿したいチャンネルで右クリックして [コネクタ] を選択
  • [incoming Webhook] を検索して [追加] をクリック、次画面で再度 [追加] をクリック
  • incoming Webhookの名前とアイコン画像を設定して [作成] をクリック
  • Webhook用のURLが表示されるのでコピーして [完了] をクリック

2.base64にデコードした画像をメッセージに埋め込んでpostで送信

  • node.jsの環境つくり、"fs"と"request"をインストール
  • index.jsと同じ階層に画像を準備
  • 以下のindex.jsを実行
index.js
const fs = require('fs');
const request = require('request');

fs.readFile('画像名.png', 'base64', (date) => {
    let base64date = date;
    let message = "魔法陣ぐるぐる" //画像の上に書きたいメッセージ

    let options = {
        uri: "teamsからコピーしたURL",
        headers: {
            "Content-type": "application/json",
        },
        json: {
            "text": message + "<br>" + "![]" + "(" + base64date + ")"
        }
    };
    request.post(options);  // postリクエスト送信
});


こんな感じで送られる
image.png

注意

  • プライベートチャンネルにはこの方法で送信できない
  • 画像サイズが大きすぎると、base64にエンコードしたときに文字列が長過ぎて送れない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Microsoft Teamsにメッセージと画像を送る(Node.js)

Teamsのチャンネルに投稿用アカウント(Incoming Webhook)を追加して、
チャンネルにメッセージや画像を投稿する

1.投稿したいチャンネルにIncoming Webhookを追加

  • Teamsの投稿したいチャンネルで右クリックして [コネクタ] を選択
  • [incoming Webhook] を検索して [追加] をクリック、次画面で再度 [追加] をクリック
  • incoming Webhookの名前とアイコン画像を設定して [作成] をクリック
  • Webhook用のURLが表示されるのでコピーして [完了] をクリック

2.base64にエンコードした画像をメッセージに埋め込んでpostで送信

  • node.jsの環境つくり、"fs"と"request"をインストール
  • index.jsと同じ階層に画像を準備
  • 以下のindex.jsを実行
index.js
const fs = require('fs');
const request = require('request');

fs.readFile('画像名.png', 'base64', (date) => {
    let base64date = date;
    let message = "魔法陣ぐるぐる" //画像の上に書きたいメッセージ

    let options = {
        uri: "teamsからコピーしたURL",
        headers: {
            "Content-type": "application/json",
        },
        json: {
            "text": message + "<br>" + "![]" + "(" + base64date + ")"
        }
    };
    request.post(options);  // postリクエスト送信
});


こんな感じで送られる
image.png

注意

  • プライベートチャンネルにはこの方法で送信できない
  • 画像サイズが大きすぎると、base64にエンコードしたときに文字列が長過ぎて送れない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gatsby入門 ブログ作ってサーバーにアップしてみる

gatsbyの作業履歴

gatsby入門 チュートリアルをこなす 0.開発環境をセットアップする
gatsby入門 チュートリアルをこなす 1. ギャツビービルディングブロックについて知る(1)
gatsby入門 チュートリアルをこなす 1. ギャツビービルディングブロックについて知る(2)
gatsby入門 チュートリアルをこなす 2. ギャツビーのスタイリングの概要
gatsby入門 チュートリアルをこなす 3. ネストされたレイアウトコンポーネントの作成
gatsby入門 チュートリアルをこなす 4. ギャツビーのデータ
gatsby入門 チュートリアルをこなす 5. ソースプラグインとクエリされたデータのレンダリング
gatsby入門 チュートリアルをこなす 6. 変圧器プラグイン※Transformer pluginsのgoogle翻訳
gatsby入門 チュートリアルをこなす 7. プログラムでデータからページを作成する
gatsby入門 チュートリアルをこなす 8. 公開するサイトの準備
今回:gatsby入門 ブログ作ってサーバーにアップしてみる

基本のチュートリアルはこなした。。。

さて何しましょう?とチュートリアルを見ているとgithubにブログ用のソースが上がっているとの記載が
https://github.com/gatsbyjs/gatsby-starter-blog
とりあえずブログでも作ってみっか!
んで、netlifyとgithubを使うのが主流みたいだけど、nginxの勉強もしたいので
とりあえず自分のGCPのVM環境にアップしてみようと思います。
飽きたらGCPから移行するかな?

ブログを構築

22:00作業開始 ローカルにgatsbyサイトを作成

まずは、ローカルにgatsbyサイトを作成します。
以下コマンドを好きなディレクトリで実行。そして風呂。
gatsby new 3s-laboo-blog https://github.com/gatsbyjs/gatsby-starter-blog
※3s-laboo-blogは任意です。
2020-09-07_22h03_24.jpg
なんか、ダウンロードすげぇ時間かかったな。
ダウンロード後サイトを開発モードで起動しながら、ビールをプシュっと。
cd 3s-laboo-blog
gatsby develop
2020-09-07_22h48_42.jpg
http://localhost:8000/
2020-09-07_22h50_15.jpg
おー。。。超シンプル。
まぁ、こっからアレンジすればいいが、今回は最低限にだけとどめよう。
ビール、グビグビ。

22:50 ブログをちょっとだけ修正

まず、顔が私じゃない!
content/assets/profile-pic.jpgを変更
あとアイコン
content/assets/gatsby-icon.pngを変更
2020-09-07_22h54_06.jpg
確認するとアイコンだけ出ていない。
gatsby-config.jsを少しいじってみる。
2020-09-07_23h35_21.jpg
2020-09-07_23h37_08.jpg
リスタートされたみたい。
改行されてないな。
src/stylesディレクトリを追加
src/styles/global.cssを追加し、以下を記述

src/styles/global.css
html {
    background-color: #e0ffff;
    white-space: pre-wrap;
}

pre-wrapは改行コードが反映されるよ!
gatsby-browser.jsを修正

gatsby-browser.js
// custom typefaces
import "typeface-montserrat"
import "typeface-merriweather"
import "prismjs/themes/prism.css"
import "./src/styles/global.css"←これ追加

2020-09-07_23h46_45.jpg
あと、マークダウンファイルをちょいと直して。。。
2020-09-07_23h52_34.jpg
とりあえずはこんなとこでしょ!
(後でかなり修正しないとな。。。)
ビールなくなった。
とりあえずコンパイル・起動
gatsby build
gatsby serve
動確問題ない。

00:00 winscpでgcpにログインし、nginxのフォルダにアップロード

nginxの配置フォルダはnginxで設定したフォルダです。
アップロードするものは、newしたディレクトリ(ここでいう3s-laboo-blog直下)にあるpublicディレクトリ内のファイル・ディレクトリ。
https://3s-laboo.com/
2020-09-08_00h43_41.jpg
イエーイ!
ということでわたくし3S Labooのブログを立ち上がりました。
時刻は00:46
風呂入ってビール飲んで歯を磨いたのも含めて3時間位で作れたー

今後修正していきまーす。
今回はここまで。

ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

fitbitのapi連携と集中力判定(PHP,Javascript)

記事の概要

作成したポートフォリオの解説です。以下をまとめています。

  • 背景
  • 主な機能
  • 開発手順
  • 工夫点
  • 課題

背景

「ポモドーロ・テクニック」という時間管理術が話題になっており、そのためのアプリなどもあるが、実際に集中しているかどうかを判断するのは単純なポモドーロタイマーだけでは難しい。
今回は心拍数と集中力の関係に着目し、集中力を判定するアプリを作成しました。

スペック

言語
PHP 7.4.2
javascript
DBMS
MySQL 5.7.26
開発環境
MacOS Catalina 10.15.5
MAMP 5.7
ライブラリ
jquery
フレームワーク
Bootstrap 4.2
バージョン管理
Git 2.24.3
本番環境
xserver
ウェアラブル端末
fitbit inspireHR

主な機能

ポモドーロタイマー

・ホーム画面
スタートを押すと25分タイマーが作動します。
タイマーの下には今日と今週の作業記録が表示されます。
スクリーンショット 2020-09-07 22.13.10.png
・25分が終わると
「お疲れ様です。5分休憩しましょう!」が表示されます。
スクリーンショット 2020-09-07 22.14.39.png

データ

・データ画面
「1週間のポモドーロ回数の推移」と「直近の作業の心拍数の推移が表示されます。
スクリーンショット 2020-09-07 23.00.03.png

開発手順

  1. 要件定義
  2. 環境設定
  3. データベース設計
  4. コーディング
  5. xserverデプロイ

1.用件定義

今回作成するアプリに必要な機能
・タイマー機能
・データ記録
・データ表示
・チャート表示
・fitbitのAPIからのデータ取得

php,Javascriptで開発を行う。

2.環境選定

本番環境では知名度の高い「xserver」を使用する。
GitとGitHubは練習として使っていく。

3.データベース設計

カラムはシンプルに
・ID
・start_at(作業開始時間)
・stop_at(作業終了時間)

4.コーディング

コーディングを実施

4.1データベース作成

MAMPのphpMyAdminを使ってデータベースを作成。

4.2ホーム画面

タイマーのカウント表示はJavasciptで動的に実装。
「START」、「STOP」、「RESET」ボタンを押した時の処理を実装。
作業終了時、DBに自動的に登録される。(Ajax)
画面のデザインはBootstrapを用いて時間短縮。

4.3データ画面

グラフはchartistというjsのライブラリで作成。
作業記録はホーム画面同様、MAMPのDBから取得する。

心拍数の記録はfitbitのAPIから取得。
以下サイトを参考にカスタマイズ
https://qiita.com/RINYU_DRVO/items/6607a0aa7ca3294f8e47

MAMPのDBから直近のデータを取り出してAPIに渡すことで
直近の作業時間あたりの心拍数の推移を取得。
(ここが一番苦労した、、、)

取得した心拍数のデータをもとに
平均心拍数と集中力判定を実施。

集中力判定は以下を基準に作成。
1.高集中:平均心拍数が高く、心拍数の分散(以下、分散)が低い
2.高ストレス:平均心拍数が高く、分散が高い
3.安定集中:平均心拍数が低く、分散が低い
4.集中していない:平均心拍数が低く、分散が高い

※閾値については今後調整します。

5.xserverデプロイ

本番環境ではxserverを使用。
xserver上にDBを立て、プログラムもDB接続部分の修正。

工夫点

・世の中にない機能を考え、実装まで持って行った
・自分で使いたいと思うようなアプリにすることができた

今後の課題

一通りの動作ができた時点で完成としました。
主な課題は以下の通りです。

  • チャートの追加(1週間ごとの比較など)
  • 作業内容の記録(どんな作業の時にどんな集中状態なのか評価するため)

参考文献

Fitbit Web APIとPHP7で心拍数を取得しよう

GitHubアカウント

freedog1
https://github.com/freedog1/pomodoro

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む