20190819のJavaScriptに関する記事は21件です。

ジャンケンで勝ち続けないと涼めないファン by Obniz

?️便利なハンディファンは不便にしよう

この暑い夏、ハンディファンを持って歩く人をよく見ますね。そんな便利で欠かせないファンを不便にします。理由は特にありません。

使用したもの

作りたかったもの

市販のハンディファンを分解したところ
「M+ M-」「L+ L-」 と明らかにモーターとLEDに繋がる導線が出てきたので、あとはObnizでコントロールしてしまうだけかと思っていましたが・・・
IMG_6841.JPG
IMG_5376.JPG
IMG_9121.JPG

?できたもの

IMG_6480.JPG
んっっっっ??!???!??!???!

違うものができました。
市販のファン(ステッパーモーターやら何やら)をどうやったらObnizでコントロールできるのか試行錯誤した結果、破壊と創造が行われたのです。

ソースコード

index.html(クリックで開く)
index.html
<!DOCTYPE html>
<html lang="ja">

<head>
  <title>じゃんけんファン</title>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
  <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
  <link rel="stylesheet" type="text/css" href="dist/jquery.convform.css">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
    integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
  <link rel="stylesheet" type="text/css" href="demo.css">
  <script src="https://unpkg.com/obniz@2.2.0/obniz.js"></script>
</head>

<body>
  <section id="demo">
    <div class="vertical-align">
      <div class="container">
        <div class="row">
          <div class="col-sm-6 col-sm-offset-3 col-xs-offset-0">
            <div class="card no-border">
              <div id="chat">
                <form action="" method="GET" class="hidden">
                  <select data-conv-question="なんてことだ!!<br>じゃんけんに勝たないと扇風機の前からどいてくれない猫が現れてしまった!!
                    <br><img src='animal_stand_neko_white.png' style='width:80px;'>
                    <img src='senpuuki_handyfan.png' style='width:60px;'>
                    <br><br>このままだとこの殺人的な猛暑のなか涼むことができない!!
                    <br><br>どうする?じゃんけん勝負を挑む?
                  " name="first-question">
                    <option value="3">涼むために挑む</option>
                    <option value="end">涼むのを諦める</option>
                  </select>
                </form>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </section>
  <script type="text/javascript" src="jquery-1.12.3.min.js"></script>
  <script type="text/javascript" src="dist/autosize.min.js"></script>
  <script type="text/javascript" src="dist/jquery.convform.js"></script>

  <script>
    const answersArray = ['グー✊?', 'チョキ✌?', 'パー??', 'では行くぞ!'];
    let resultMsg = '・・・';
    let power = 0;
    let status = 0;
    const obniz = new Obniz("8484-4863");
    obniz.onconnect = async function () {
      const motor = obniz.wired("DCMotor", { forward: 0, back: 1 });
      jQuery(function ($) {
        let count = 0;
        var convForm = $('#chat').convform({
          eventList: {
            onInputSubmit: function (convState, ready) {

              if (convState.current.answer.value === 'end') {
                convState.current.next = convState.newState({
                  type: 'select',
                  name: 'dynamic-question-' + count,
                  questions: ['猫「ニャ…(ならば消えるがいい)」'],
                });
                setTimeout(ready, Math.random() * 300 + 100);
              } else {

                // プレイヤーがグーチョキパー選択したか否か
                if (convState.current.answer.value != '3') {
                  var playerRoll = Number(convState.current.answer.value);
                  console.log('playerRoll', playerRoll);

                  // コンピューターが何出すか
                  var computerRoll = Math.floor(Math.random() * 3);
                  console.log('computerRoll', computerRoll)

                  // 勝敗
                  if (playerRoll == computerRoll) {
                    // あいこ
                    resultMsg = '<<あいこ>>';
                  } else if (playerRoll === 0 && computerRoll === 1 ||
                    playerRoll === 1 && computerRoll === 2 ||
                    playerRoll === 2 && computerRoll === 0) {
                    // プレイヤーの勝ち
                    resultMsg = '<<プレイヤーの勝ち>>';
                    status += 1;
                  } else {
                    // コンピュータの勝ち
                    resultMsg = '<<コンピュータの勝ち>>';
                    status -= 1;
                  }
                }
                else {
                  console.log('挑むを選択');
                }

                // モーター稼働処理
                if (status == 0) {
                  power = 0;
                  motor.power(power);
                  motor.move(false);

                } else if (status >= 1 && status <= 5) {
                  power = status * 10;
                  motor.power(power);
                  motor.move(false);

                } else if (status > 5) {
                  status = 5;
                  motor.power(75);
                  motor.move(false);

                } else {
                  status = 0;
                  power = 0;
                  motor.power(power);
                  motor.move(false);
                }

                if (computerRoll == undefined) computerRoll = 3;
                convState.current.next = convState.newState({
                  type: 'select',
                  noAnswer: true,
                  name: 'dynamic-question-' + count,
                  questions: ['猫「ニャ!!(' + answersArray[computerRoll] + ')」'],
                });
                convState.current.next.next = convState.newState({
                  type: 'select',
                  noAnswer: true,
                  name: 'dynamic-question-' + count,
                  questions: [resultMsg],
                });
                convState.current.next.next.next = convState.newState({
                  type: 'select',
                  name: 'dynamic-question-' + count,
                  questions: ['猫「ニャンニャン…(じゃんけん…)」'],
                  answers: [
                    { text: 'グー✊?', value: '0' },
                    { text: 'チョキ✌?', value: '1' },
                    { text: 'パー??', value: '2' }
                  ]
                });
                setTimeout(ready, Math.random() * 500 + 0);
              }
              count++;
            }
          }
        });
      });
    };
  </script>
</body>

</html>

勝つとポイントアップ、負けるとポイントダウン。ポイントに比例してDCモーターの回転数が上がりますが、全然涼める気がしません。

チャット風デザイン

静的ページですがLINEのようなチャット風デザインにしてみようと、jQueryプラグインのconvFormを使ってみました。
Image from Gyazo

チャチャっとLINE Botを作れるほどその方面の筋肉が養われていないので、
筋肉があるであろうjQueryプラグインで省エネでチャット風レイアウトを実現しよう!と思いましたが
使用したconvFormは一癖二癖もあって意外と躓きました。根本的に利用用途が違うので当たり前ですが…。普通にLINE Botで作ればよかったですが。。。
(いつかWebサイトのデザインに使えそうなのでポジティブに考えて良し)

まとめ

このトライアンドエラーのしやすさもObnizの良いところですね!

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

obnizで夏っぽいことしてみた

概要

夏の思い出に何か夏っぽいことしようと思い、
obnizで何かできないかなと考えました。
夏といえばHANABI。
というわけで、HANABI打ち上げましょう

実装

obnizでボタンを押したら、アニメーションでHANABIが打ち上がる仕組みに

失敗した点

・暗闇の中、音入れないでやったらちょっと虚しかった。
もう少し五感をフルに使って楽しめるようにしたい
・最初obnizのディスプレイに描画しようと思ったら、画面サイズ的に難しかった。
・花火観に行こうと思った

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

初学者がRuby on Railsでポートフォリオを作成しました。

はじめに

今回の内容は就職活動中で作成しているポートフォリオの説明です。
アドバイスなどありましたらよろしくお願いいたします。

開発環境

  • Ruby 2.5.1
  • Rails 5.2.3
  • DB:Mysql5.6
  • AWS/S3
  • 少しだけVue.js

アプリ概要

・業者などに頼むほどではない作業や困りごとを得意な人に助けてもらう。
・使う機会がないスキルや自分の得意なことを活かして、困っている人を助ける。
そのような人と人が助け合うアプリがあればいいなという思いから作成しました。

接続先情報

⚠️URLはデプロイなどで接続できないタイミングがございます。その際は少し時間をおいてから接続してください。
⚠︎簡単ログインでログインした方は終わる時にログアウトもお願いいたします。

何ができるか?(機能)

Koto_Kotoは、以下のことができます。

・困りごと,スキルの作成/編集/削除

ezgif.com-video-to-gif.gif

困りごとを作成したいときは、demandボタン、スキルを提案したいときはsupplyボタンから作成することができ、目的別に分けて投稿ができます。
編集や削除は下の画像の赤丸のところからできます。
スクリーンショット 2019-08-19 21.52.53.png

・クレジット登録機能

クレジット登録機能

ezgif.com-video-to-gif (1).gif

Test用としてこちらを入力して頂ければ使用できます。

  • カード番号 4242424242424242
  • 有効期限 12/22
  • CVC番号 123
  • 名前   適当に入力してください

カードの削除も可能です。

・スキル,困りごとの助け合い機能(購入機能)

ezgif.com-video-to-gif (2).gif

カードを登録した場合のみ購入,助けることが可能です。(ポートフォリオなので、万が一購入しても、仮想的に作っているので、お支払いは発生しません。)

あと、助けるボタンを押した場合は、購入ではないので、クレジットからお支払いは発生しないような作りになっています。

・購入後のチャットのやりとり

ezgif.com-video-to-gif (3).gif

スキル購入、困りごとへの助けるボタンを押したら、チャットルームが作成される仕様になっており、お互いでやりとりが可能です。

・契約完了後の評価

ezgif.com-video-to-gif (4).gif
契約完了したら、スキル購入の場合は購入者,困りごと解決の場合は困りごと出品者がその契約内容に関して、評価する仕様になっています。その評価に応じて対象者の信頼スコア(各々が持っている評価指標。マイページの右側の円に囲まれたもの)が加算されます。信頼スコアが高ければ高いほど、その人の評価が高いことになります。

⚠︎評価画面の見た目部分は後々、モーダルで作成したいため、いじっておりません。

・ポイント機能

スクリーンショット 2019-08-18 15.13.28.png

評価されて信頼スコアが上がるだけだとモチベーションupには繋がらないと思ったので、ポイント機能を作成しました。
信頼スコアをあげればあげるほど、ポイント倍率が高くなって、ポイントをためやすくなる仕様にしています。
今後、このポイントを使用して、スキル購入の時に割引として使えるなどの機能を実装したいと考えています。

・ユーザー登録/編集/退会機能

deviseは使用せずに作成しました。(Session&Cookieの概要を学ぶため)
ユーザー登録をすると、確認メールが届くようにしており、そのメールに書かれているリンクを押して初めて、ユーザーが登録される仕様になっています。

・簡単ログイン機能

スクリーンショット 2019-08-19 22.41.08.png

就活用に簡単ログイン機能を実装しました。
ボタン一つでログインできれば、就活の際に見てもらいやすくなるのではないかと考え実装に至りました。

・スキルや困りごとのコメント機能

スキルや困りごとに対してコメントができます。
ezgif.com-video-to-gif (5).gif

・レスポンシブ対応

ezgif.com-video-to-gif (6).gif

スマートフォンが主流なので、そちらでも使いやすくするためにレスポンシブ対応にしました。

課題&追加したい機能

  • vueでのモーダル実装(評価する部分)
  • ランキング機能(頑張った人ほど目に止まる率が高くなる機能を作りたいため)
  • ポイント機能の使用(スキル購入の時に割引として使えるなど)
  • カテゴリー別一覧表示
  • Rspecテスト

最後に

記事を読んでいただきありがとうございました。
また機能を追加次第、更新していきたいと思います。

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

【Node.js】Custome Search APIを使って何か作ってみる

初投稿です。※初投稿っぽい前置きや自己紹介は割愛
なんとなくプライベートでもなんか作っていけたらなあという浅い考えで作ってみました。

目的(テキトー)  

Node.jsを使ってREST APIとの連携をなんか作ってみたい。
そこでテキトーな練習台を思いついた。
「なんか料理屋さんをCustome Search APIで検索してみて、食べログURLがあれば抜き出す。」

使っているもの

Node.js
Custome Search API

Custome Search APIって?

Custome Search APIはGCP上でGoogleが提供しているGoogle検索のAPIです。
これを使うにも何かと手順があります。※気が向いた時にこちらも軽くまとめるかも
参考になる情報は数多くありますので、自分が参考にさせていただいたものを抜粋して貼っておきます。
Custom Search APIを使ってGoogle検索結果を取得する
Google Custom Search API を使ってみる

ここからが作ったものについて

APIに必要なパラメータは大前提にURL、あとは検索エンジンID、APIキー、検索ワードが必要です。
詳細なAPIの情報は公式のページを参照してください。
簡単に三つのパラメータを説明すると、

検索エンジンID

ユーザーが作成したカスタム検索エンジンのユニークIDのこと。Custome Search APIの利用にはこのカスタム検索エンジンの作成が必要です。

APIキー

APIコール時のユーザー認証に必要な情報です。
本来は漏洩等による不正利用を防ぐため、API呼び出すアプリケーションの制限(IPアドレスやドメインなどで設定可能)や呼び出せるAPIの制限といった制限をかけるべきです。今回は遊びなので特に制限は設定していません。※生成したAPIキーは削除済み

検索ワード

そのまんま。ググる時同様の検索したいワードです。今回はラーメン界に名を轟かす「中華蕎麦とみた」さんを検索ワードに。

クエリパラメータの外出し

さっきのパラメータはメインのソースコードにベタ書いても遊びとしては良かったのですが、あまりにも工夫がないなと思い、設定ファイルに外出ししました。node-configというモジュールを使い、下記のようにdefault.jsonというJSONファイルを作成し、そこに各パラメータを記述しています。

default.json
{
    "url":"https://www.googleapis.com/customsearch/v1",
    "key":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "cx":"000000000000000000000:xxxxxxxxxxx",
    "q":"中華蕎麦とみた"
}

ちなみに、node-configは下記のコマンドでインストールします。
npm install --save-dev config

requestモジュールを使ったhttp通信

Node.jsのhttp標準モジュール(http/httpsモジュール)でもいいのですが、せっかくなのでより楽に処理を実現できるrequestモジュールを今回は使用しました。optionとして引数にURLやhttpメソッドを渡します。また、json:trueとすることでレスポンスのJSONをパースしてくれます。
下記のコマンドでインストールします。
npm install request --save
このモジュールを使いながら処理を書いて、実行したらこんなエラーが。
path contains unescaped characters
どうやら原因は検索ワードの2バイト。初歩的なミスですね。URLエンコードが必要ですので、javascriptではencodeURIComponentを使います。

コード完成!

そんなこんなで完成したコードがこちら。

getGoogleResult.js
var req = require('request');
var conf = require('config');
var options = {
    url: conf.url + "?key=" + conf.key + "&cx=" + conf.cx + "&q=" + encodeURIComponent(conf.q),//設定ファイルから各パラメータを取得し、URLを生成している
    method: 'GET',//GETでリクエスト
    json: true//これでJSONをパースしてくれる
}
req(options, function (error, response, body) {
    if (error) {
        console.log('Error: ' + error.message);
        return;
    }
    var items  = body.items;
    for (var i in items) {
        var itemLink = items[i].displayLink;
        var strLink= String(itemLink);
        var itemTitle = items[i].title;
        var strTitle = String(itemTitle);
        if(strLink.indexOf('tabelog.com') != -1 ){
            console.log(conf.q + "の食べログは\nURL:" + strLink + "です。\n" + "タイトル:" + strTitle)
        }
    }
})

ざっくり言うと、コールバック関数の中でerrorがあれば、ログに出して終了、errorが無ければ、bodyからitemリスト(Custome Search APIの検索結果リスト)からURLに「tabelog.com」が含まれるものを判定してログ出力!って感じです。
注意点としては、ループでitemsから取得できるもの(コード中のiのこと)はインデックスなので、ループの中ではこのインデックスを使ってリストの要素を取り出す必要があります。

いざ、実行!

node getGoogleResult.js

結果は、

中華蕎麦とみたの食べログは
URL:tabelog.comです。
タイトル:中華蕎麦 とみ田 - 松戸/ラーメン [食べログ]
中華蕎麦とみたの食べログは
URL:tabelog.comです。
タイトル:日本の中華そば富田 - 成田空港/ラーメン [食べログ]

富田さんの食べログサイトが取れました。(ほんとは松戸本店だけのつもりだった・・・)
あと、displayLinkだとどちらもtabelog.comになっちゃいますね。普通のlinkにしなければいけなかった・・・
まあ、なんとなく遊べたので満足です。これをなんらか使い勝っていい感じに発展させたら面白そうだけど、それは気が向いたらやります。笑

終わりに

何かしら自分が面白いと思ったことを発信できたら、それ自体が面白そうだなと思って投稿してみました。
どれだけ役立つ情報発信ができるか分からないし、そもそもそんなマメに投稿するかも怪しいけど、また何か思いついたら自分の備忘録も兼ねて書きます。

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

一番簡単なreact-reduxアプリケーション

初めに

モダンなフロントエンド開発だと結構必須になってきているreact-redux
しかしながら、reactだけでも慣れが必要なのにそれに加えてreduxとなると余計混乱します。
僕はめっちゃ混乱しました。チュートリアルをやってみてもなかなか理解が深まらず、使えぬまま放置してしまったことがあります。

そういう人も多からずいらっしゃると思うのでそんな方や、reduxよくわかんないけどともかく慣れたいという人のために、今回はものすごく簡単なアプリケーションを作成することによって、もっとも簡単なreduxに触れてみたいと思います。reduxのハードルが少しでも下がれば幸いです。

これぐらいの大きさのアプリケーションだと正直reduxを実装する意味は皆無ですが練習です!

※図を使った細かなデータの行き来は他の方が詳しく解説されているので、今回はそういったものは用意しておりませんのでご了承ください

作成目標

+を押したら数字が増えて
-を押したら数字が減る
そんな簡単なアプリケーション
作成したコードはgitにあげております
https://github.com/keyyang0723/easiest-react-redux/tree/master/easiest-react-redux

環境構築

reactをやるうえで最も簡単な
creacte-react-appを使います

$npm install -g creacte-react-app
$creacte-react-app easiest-react-redux

これでアプリケーションが作成されます。
ここからはコンソールコマンドは基本的にアプリのホームディレクトリで行っていると思ってください

$npm run start

でReactのマークが回転していればOK

reduxの導入

$npm install react-redux -s
$npm install redux -s
不要ファイルの削除

次に不要なファイルを削除します
srcの中身はIndex.js,App.js以外は不要なので任意の方法で削除してください。

必要なフォルダの作成

必要なフォルダを作成します。
それぞれreduxで使用する
actions
reducers
components
containers
をsrc直下に作成します。
この時ついでにApp.jsをcomponents直下に移動させておきます。

$mkdir src/actions
$mkdir src/reducers
$mkdir src/components
$mkdir src/containers
$mv src/App.js src/components/App.js

これで作成の準備ができました。お疲れ様です。

実装

Viewの作成

まずはひとまずreduxを無視してreactを使いviewを作成しましょう

index.js
import React from "react";
import { render } from "react-dom";
import App from './components/App'

const rootElement = document.getElementById("root");
render(<App />, rootElement);
App.js
import React,{ Component } from "react";

class App extends Component{
render(){
  return (
    <div>
      <p>0</p>
      <button>+</button>
      <button>-</button>
    </div>
  );}   
}

export default App

なんの変哲もないreactのコンポーネントです。

$npm run start 

すれば表示はされますが、もちろん値の更新などは行われません。

従来のreactであればここにApp.jsのstateを使い,buttonにイベントを用意してそれらを着火させstateを更新していきます。(このアプリケーションはそれだけの機能なので従来ならばそのやり方でOKですが今回は特別です)
ではここからはreduxを実際に利用していきましょう。

reduxの導入

storeの作成を実装する

まずはデータの置き場であるstoreを初期化する部分を作成していきましょう。
storeを通じて画面を表示させるためにはactions=>reducersのデータの流れが作成されていないとエラーが出てしまうので、エラーが出ない程度に実装します。

actionsの作成

まずはactionを作成します。actionですが今回のアプリケーションは数字の増加と減少二つの機能があるので両方追加しておきます。
ここではaction typeの指定とdispatchされてきた初期値の受け渡しのみ実装しておきます(typeを指定しないと怒られる)

actions/index.js
/*actionの作成*/
export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";

export const increment = (number) => {
  return {
      type: INCREMENT,
      number
  };
};

export const decrement = () => {
  return {
  };
};

またtypeは文字列のまま使うこともできますが、constで定義しておくのがイカした書き方のようです。

reducersの作成

次にreducerの作成です。
従来ならばactionsから送られてきたtypeによって分岐分けし、データ作成の機能を作成しますが、今回は表示だけなので受け取った値をそのまま渡します。

reducers/index.js
/*reducerの作成*/
const reducer = (state, action) => {
  switch (action.type) {
    default:
      return state;
  }
};

export default reducer

createStoreでstoreの初期化の実装

storeを作成するためにreduxのcreateStore()を使用します。こちらはreducerを引数に渡す必要があるので、import作成します。

index.js
import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import App from './components/App'
import reducer from './reducers/index'

const store = createStore(reducer);

const rootElement = document.getElementById("root");
render(
  <Provider store={store}>
    <App />
  </Provider>
, rootElement);

またこの際初期値の挿入のため普段はここには配置しないactionsを配置しています。後で消します

react-reduxのProviderを利用を利用しここにラップされているコンポーネントすべてからstoreの値を参照できるようにします。

これで問題がなければ

$npm run star

をたたけば先ほど作成したviewと同じ画面が表示されます。
これでstoreを作成することに成功しました!
ここから、actionとreducerを実装していき、console.logを使い正しいデータの流れができているか確認します。

actionの実装

actionはdispatchから送られてくるstateを受け取り、そこから値を抽出してreducerに送ります。ここでは値に触りません。

actions/index.js
/*actionの作成*/
export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";

export const increment = state => {
  return {
    type: INCREMENT,
    number: state.number
  };
};

export const decrement = state => {
  return {
    type: DECREMENT,
    number: state.number
  };
};

rudexの書式に沿っていればこれだけ書くだけで自動的に値をreducerに送ってくれます。

reducerの実装

次にreducerを実装してみます。

reducer/indexl.js
/*reducerの作成*/
const reducer = (state, action) => {
console.log(action)//あとでデータの確認に使います。本来ならば不要です。
  switch (action.type) {
    case "INCREMENT":
      return {
        number: action.number + 1
      };
    case "DECREMENT":
      return {
        number: action.number - 1
      };
    default:
      return state;
  }
};

export default reducer;

reducerはactionsから流れてきたstate, actionを受けとり値を変更しstoreを更新します。
今回では+を押したときにINCREMENTで値を+1し、-を押したときにDECREMENTで値を-1します。

これでいったんactionsとreducersの作成が終わりました。この二つによってstoreがうまく変更されるかを確認してみましょう。

index.js
import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import App from './components/App'
import reducer from './reducers/index'
import { increment, decrement } from './actions/index'

const store = createStore(reducer);
store.dispatch(increment({number: 0}))//テスト用コードです
store.dispatch(decrement({number: 0}))//テスト用コードです


const rootElement = document.getElementById("root");
render(
  <Provider store={store}>
    <App />
  </Provider>
, rootElement);

今回はデータの確認だけですのでindex.jsに直接actionsをimportし先ほどreducersに用意していたconsole.logにデータが正しく表示されているかを確認しましょう。

store.dispatch(increment({number: 0}))
//store.dispatch(decrement({number: 0}))

のようにコメントアウトを切り替えることで値がそれぞれ
{type: "INCREMENT", number: 0}
{type: "DECREMENT", number: 0}
のふたつに切り替わっていれば無事実装されています。

確認ができたらreducers/index.jsとindex.jsにあるテストようのコードを削除しておきましょう。

おめでとうございます!これであなたはstore, actions, reducersを用いてデータの流れを作成することに成功しました!あとはこれらを着火するイベントと変更されてた値を表示する機能を作成すれば完成です!

componetsの作成

さて、いよいよ今まで作成したstore, actions, reducersを用いて画面を作成していきましょう。
storeで値を保持してそれらを表示させ、イベントなどでactionに値とaction typeを流し込みreducerでstoreを更新するというサイクルを作成します。

mapStateToPropsとmapDispatchToPropsに関して

ここで新たにmapStateToPropsとmapDispatchToPropsというreduxで用いられる関数が登場します。
個人的にここが結構ややこしいと思うので
今回では
mapStateToPropsはstoreの値を監視しそれらを適切な形に変化させて、表示させるもの
mapDispatchToPropsはstoreの値を変化させるためのイベントを宣言するもの
くらいに考えておいてもらってよいと思います。
特に後者はdispatch(increment({ number: number }));などという見慣れないものが現れますがdispatch()のカッコ内に記載したactionsの関数に値を送りこむものくらいで結構です。

componets部分の変更

まずはApp.jsを少し書き換えます、今回はClassである必要はないので

const  App = () =>(
    <React.Fragment>
      <p>0</p>
      <button>+</button>
      <button>-</button>
    </React.Fragment>
  )

のように書き換えます

mapStateToPropsを使ってstoreの値を表示する

そしてこのAppにstoreからの値を流し込むためにmapStateToPropsを記入し、react-reduxのconnectを用いてAppをとつないでやります。

App.js
import React from "react";
import { connect } from "react-redux"

const  App = (state) =>(
    <React.Fragment>
      {console.log(state)}//テスト用です。
      <p>{state.number}</p>
      <button>+</button>
      <button>-</button>
    </React.Fragment>
  )

function mapStateToProps(state) {
  return {
    number: state ? state.number : 0
  };
}

export default connect(
  mapStateToProps,
  null
)(App);

このような感じです。今回の場合、初期表示時のためstoreから流れてくるstateはnullですが、

number: state ? state.number : 0

によって0に変えています
コンソールに{number: 0, dispatch: ƒ}が来ていれば問題なくいっています。
さぁ、これでstoreの値をviewで表示することができました!!
あとはstoreの値を変更するイベントをmapDispatchToPropsを用いて実装すれば完成です!

mapDispatchToPropsを実装する

mapDispatchToPropsではstateの状態を変更するイベントを作成しAppに渡してやります。そしてApp内でそれを着火することでstateの変更を行います。
まずは+のボタンのイベントから実装していきます。

function mapDispatchToProps(dispatch){
    return {
      onClick(number) {
        dispatch(increment({ number: number }));
      }
    }
}

関数として表示されている数字のnumberを受け取りincrementにdispatchしてやるだけです。

components/App.js
import React from "react";
import { connect } from "react-redux"
import { increment } from '../actions/index'

const  App = (state) =>(
    <React.Fragment>
      {console.log(state)}
      <p>{state.number}</p>
      <button>+</button>
      <button>-</button>
    </React.Fragment>
  )

function mapStateToProps(state) {
  return {
    number: state ? state.number : 0
  };
}

function mapDispatchToProps(dispatch){
    return {
      onClick(number) {
        dispatch(increment({ number: number }));
      }
    }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

最後にconnectでAppに忘れずにつないでやります。
これでconsole.logを見てやると、stateの中のdispatchに記載したonClickが表記されています。後はdecrement部分も実装しのonClickにわたしてやればOKです。

<button onClick={() => {state.onClick(state.number,INCREMENT)}}>+</button>

これで+ボタンの実装が完了しました。同様に―ボタンを作成すれば機能の実装は完了です!!
おめでとうございます!!これでかなり小さいものですがreact-reduxアプリケーションが完成しました!!
最後に今回は小さいアプリケーションなので分けずに書いてしまっている部分を分断していきます。

containersの作成

components/App.jsに直接書いてしまっているactionへの値のやり取りやstoreからの値の取り込みを行う部分をcontainers/dispayNumber.jsとして作成していきます。また今回は一機能なので必要ないように感じますが
機能が増えたときにcomponents/App.jsでまとめてからindex.jsに渡したいので、components/displayNumber.jsを作成しcomponents/App.jsでimportしてやります

components/App.js
import React from "react";
import DisplayNumber from "../containers/displayNumber"

export default function App(){
  return(
    <React.Fragment>
      <DisplayNumber />
    </React.Fragment>
  )
}
components/Counter.js
import React from 'react';
import { INCREMENT, DECREMENT } from "../actions/index";

const  Counter = (state) =>(
  <React.Fragment>
    <p>{state.number}</p>
    <button onClick={() => {state.onClick(state.number,INCREMENT)}}>+</button>
    <button onClick={() => {state.onClick(state.number,DECREMENT)}}>-</button>
  </React.Fragment>
)

export default Counter
containers/dispayNumber.js
import { connect } from "react-redux"
import Counter from "../components/Counter"
import { INCREMENT, increment, DECREMENT, decrement } from "../actions/index";

function mapStateToProps(state) {
  return {
    number: state ? state.number : 0
  };
}

function mapDispatchToProps(dispatch) {
  return {
    onClick(number, types) {
      switch (types) {
        case INCREMENT:
          dispatch(increment({ number: number }));
          break;
        case DECREMENT:
          dispatch(decrement({ number: number }));
          break;
        default:
      }
    }
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter);

お疲れ様です!これで形も整えることができ無事react-reduxアプリケーションが完成しました!!

今回は非常に機能の少ないアプリケーションであり、まだまだ使用していない機能もたくさんありますが、redux理解への第一歩としてお役に立っていれば幸いです。

初期値の追加

この状態だと初期stateが導入されておらず、初期表示を行いたいときに若干不都合でした

index.js
import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import App from './components/App'
import reducer from './reducers/index'

const store = createStore(reducer,initialState);

const rootElement = document.getElementById("root");
render(
  <Provider store={store}>
    <App />
  </Provider>
, rootElement);

const initialState = {
  number: 0
};
const store = createStore(reducer,initialState);
const initialState = {
  number: 0
};

を加え初期stateを追加してやりましょう。

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

非エンジニアの社長にTypescriptとは何か説明する

非エンジニアの社長にTypeScriptとは何か説明する

以前ReactでやったシリーズのTypescript編です。

現在進めているProjectではTypeScriptを利用しているのですが、導入時に若干揉めました。
JavaScriptはともかくTypeScriptを業務レベルで書いたことのあるメンバーがほぼいなかったためです。
社長には「長期的に利用するコードだったらこっちの方がいいんだ」とだけ説明して、
CTO権限で押し切ってしまったのですが、大分雑な説明だったので一度きちんとまとめておこうと思います。

そもそもTypeScriptとは何か?

プログラミング言語の一つで、TypeScriptで書いたコードにコンパイルという処理を加えると、javascriptのコードが生成されます。
一般にコンパイルというのはプログラムのソースコードから機械語などの人には厳しいものの機械が実行するには都合のいいものを生成することを指しますが、TypeScriptをコンパイルするとJavaScriptのコードが生成されます。
これはブラウザ上で画面を動かすために使えるものがほぼJavaScriptに限定されるためです。1

なぜ、TypeScriptで書く手間をかけるのか?

他のコンパイルが必要な言語の生成物違って、JavaScriptは元々人が読み書きするために作られた言語なので、当然JavaScriptでコードを書くことは出来ますし、実際、直接JavaScriptで書かれて動いているサービスも世の中に沢山あります。

それにも関わらず、なぜわざわざコンパイルという手間と学習コスト2までかけてTypeScriptで書くのか。
これは、TypeScriptを使うことで静的型付けというメリットが得られるためです。

静的型付けとは何か?

そもそも型とは?

およそプログラムと言うものは、どこかから値を取ってきて、
そこに何らかの処理や計算を加えてどこかしらに結果を出力するものです。
(例えば、このQiitaの記事を読む場合、記事のIDから記事の情報をデータベースに取りに行って、
格納されているデータをHTMLの形に加工して出力しています。)

この時に扱う値の種類が「型」です。
「文字」とか「数値」、「日付」であったり、あるいはユーザーが定義したもっと複雑な型(申し込みデータなど)もあります。
プログラムはそれぞれの値が何の型であるかによって処理ができるか判断したり、処理を切り替えたりします。

//javascriptでの具体例
3 + 3 // この結果は6。型が「数値」なので普通に足し算します。
'3' + '3' //この結果は'33'。シングルクォートで囲まれた数値は「文字列」なので、+は足し算ではなく文字としてくっつけます。

静的型付けと動的型付け

プログラムにおける型の扱いには大き分けてく2つあります。
静的型付けと動的型付けです。

前者は、コードを書く時点でその値に「特定の型でなくてはならない」という制限を設けておくもので、
そのルールに反したコードを書くとコンパイルの時点でエラーが発生します。
後者は「動的」の名の通り実行時にその都度判断します。
実際に実行してみて問題が発生しない限りエラーは起きません。

//typescriptの場合
var a:number = 1;///変数aの型はnumber(数値 )
a = 'aaa';//aの値を文字列にしようとするとエラーが起こってコンパイルできない
//javascriptの場合
var b = 1;  ///javascriptはコード上で型宣言をしない
b = 'bbb'; ///型宣言がないのでbには何を代入してもいい

生のjavascriptは動的型付け言語で、typescriptは静的型付け言語に当たります。

静的型付けだと何がうれしいのか

なお、静的型付けと動的型付け一般について語ると広くなりすぎて色々と例外も出てくるので、
静的型付けといいつつ、TypeScriptとJavaScriptの比較を想定した文脈で語ります。悪しからず。

不具合の早期発見

静的型付けの良いところは、バグの一部を早い段階で発見できることです。

例えば、二つの料金の合計額を求めて、消費税をかける処理があるとします。3

javascriptで書くと以下のような処理です。

function calc (priceA,priceB){
  return (priceA + priceB) * 1.08;
}

これは一見問題なさそうに見えます。実際、100100を渡すときちんと216が返ります。

が、数字の100を渡すつもりで間違えて文字列の'100'を二つ上記の関数に渡してしまうとどうなるでしょうか。
なんと、数値の108108が返ります。金額が500倍以上になる非常にまずい状態です。

これは文字列の'100'同士を+すると文字列として結合されて'100100'になり、そこに1.08をかけようとすると、
javascriptの処理系が「掛け算するってことはきっと数値として扱いたいんだな」と要らない空気を読んで
'100100'を数値に変換(暗黙型変換)した上で1.08倍するためです。

このように、実際の処理と言うものは特定の型でない限り、まともに動かないものがほとんどです。

では上記をTypeScriptで書くとどうなるでしょうか?

function calc (priceA:number,priceB:number){
  return (priceA + priceB) * 1.08;
}

TypeScriptの場合、足し合わせる二つの値に「数値型でなくてはならない」という制限を設けることが出来ます。
この制限があるため、文字列の'100'を渡そうとすると、「数値ではないからダメです。」とエラーを出してくれます。
実際に処理を回さなくても、コードを書く時点でバグに気づいて修正できるわけです。
(最近のエディタは賢いので、わざわざコンパイルを回さなくてもこういうレベルのエラーは書いた瞬間に指摘してくれます)

早い段階でエラーが検出できるというのは、開発効率に大いに寄与します。
というのも、バグの修正コストは発見されるフェーズが後になればなるほど指数関数的に修正コストが増すからです。
(書いている時に気づけば一瞬ですが、本番環境に当たった後だとそう簡単に修正できませんし、データ復旧などの面倒な付随作業が必要になる可能性があります)

他にも、色々な場所から参照されるロジックを修正した結果想定外の所が動かなくなるバグ(影響範囲の見落とし)など、静的型付けによるチェックで防げるバグは多くあります。

小規模なプログラムの場合は、それぞれの処理で要求される型を覚えていられたり、影響範囲を完全に把握したりできますが、大規模開発や長期間に渡ってメンテナンスしていくコードになるとまず無理です。
型によって事前にチェックしてもらった方が安心です。

エディタによるアシストの強化

プログラマはコードを書くのが仕事ですが、その生成物のコードが全て手で打たれたものかと言うとそんなことはありません。
プログラマは怠慢な生き物なので可能な限りエディタに補完してもらいます。
(いちいち手打ちしていたら腱鞘炎になってしまいますし、タイポばっかりでいつまで経ってもコードが完成しません。)
そして、このコードアシストですが、型情報があった方が確実によく働きます。
例えば、先ほど例として挙げた金額を合計する処理ですが、JavasSriptの場合だと処理に必要な要素として何をサジェストするのが適当かを判断する材料がエディタ側にはほどんどありません。
仕方ないので使えそうなものを全て提示する形になり、当然その中には実際には使えない別の型の値も混じってしまいます。
一方でTypeScriptの場合、型が決まっているので実際に使えるものに絞り込んだ上でサジェストできます。

がっつり開発する場合は型情報とそれを利用した適切なアシストがあったほうが楽です。

型がある方がコードが読みやすい

大規模な開発になると人の書いたコードを読んで仕様を理解したり、外部のライブラリを利用することが増えます。
この時、型があると内容を理解するのが楽になります。
例えば、何らかの申し込み処理がある場合、JavaScriptの場合どのような項目が必要になるのか値の名前でしかわかりません。
それぞれの値をどのような型で渡せばいいのか、渡す値がなかった場合にどうすればいいのか、成功・失敗の場合それぞれどのように値が返ってくるのか、と言った開発者が欲しい情報はコード上には表現されません。
そのため、ドキュメントやコメントを参照する必要がありますし、最悪それらがなければコードを読んで仕様を解読する必要があります。
これは中々に骨が折れる作業です。

一方で、きちんと型定義がされていると、型情報を見るだけで仕様がほぼわかりますし、(尤も、雑に型定義されていると分からない場合もありますが…)
それに基づいて前述のコードアシストも働きます。これも開発効率の向上に寄与する要素です。

まとめ

  • TypeScriptはバグの早期発見やコーディングの効率・可読性の向上に寄与する
  • 一方で、上記のメリットはプロジェクトが大規模化・長期化するほど大きくなるもので常に最適解というわけではない

  1. 近年はjavascript以外にもWebAssemblyが出てきていますが、今回の話にはあまり関係がないので省略します。 

  2. Typescriptを書くにはJavaScriptとTypeScript双方の知識が必要です。 

  3. そもそもJavaScriptで消費税計算するのってどうなの?というのは話がややこしくなるので一旦見逃してください 

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

TypeScript勉強記録:型まとめ

Boolian

真偽値
True,Falseのこと

Number

数値型
TypeScriptのすべての数値は浮動小数点(コンピュータ世界における小数の扱い方)

String

文字列/テキスト
二重引用符(")または一重引用符(')を使用して文字列データを囲む
テンプレート(変数を埋め込んだテキスト)は、「${ example }」で表示させることが可能

Array

配列
書き方が2種類ある。

let list: number[] = [1, 2, 3];
let list: Array = [1, 2, 3];

Tuple

配列に複数の型のものを入れることができる型
ここの解説が最高にわかりやすい。

Enum

列挙型
(いくつかの定数をひとまとめにすることができるらしい)[https://qiita.com/igm50/items/8c9788d4ba5868642c69]

Any

なんでもありな型

void

全くない
値を返さない関数の戻り型

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

jQueryの「document」の意味とは?

documentってどんな役割?

JavaScriptでdocumentっていう記述がよく出てきますよね。

$(document).on('click', '【セレクタ】', function ()
 {
 // 何かの処理
});

あれってどんな役割をしているのでしょうか?

documentとは?

JavaScriptでdocumentオブジェクトを活用することで、HTML要素へ簡単にアクセスすることができます。

documentを使ってできること

documentを使ってできることをいくつか紹介します。

URL取得や画面遷移をする:location

locationプロパティを使うと、現在開いているWebページのURLを取得することができます。

//現在開いているWebページのURLを取得する
var url = document.location.href;

hrefプロパティでアドレスバーに表示されているURLを取得することができます。

HTMLの読み込み完了後に処理を実行する

JavaScriptでdocumentオブジェクトを使ってHTML要素を操作する場合、HTMLが読み込まれた後に処理を実行する必要があります

そのため、HTMLが読み込まれてからJavaScriptを実行するために以下のような記述をします。

$(document).ready(function() {
    //処理を実行する
});

この場合、HTMLソースだけを読み込んだあとにすぐJavaScriptの処理が実行されます。画像などを含めて、すべてのリソースが読み込まれてから実行されるわけではないのがポイントでしょう。

まとめ

・documentを使うことで、任意のHTMLを取得したり追加したりできる。

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

react-hooksでreduxを使う

react-hooksでreduxを使う

案件でreactの改修が起きそうなので、react,redux,typescript,react-routerで気持ちよく書きたいと思い殴り書きだがサンプルを作って試してみました。

作ったリポジトリ

https://github.com/numa999/react-redux-example

ざっくり解説

router.tsx
import React from 'react'
import { BrowserRouter, Route, Link } from 'react-router-dom'

import App from './App'
import Counter from './counter'

const Router: React.FC = () => (
  <BrowserRouter>
    <nav>
      <ul>
        <li><Link to="/">HOME</Link></li>
        <li><Link to="/counter">COUNTER</Link></li>
      </ul>
    </nav>
    <hr/>
    <div className="content">
      <Route exact path="/" component={ App } />
      <Route path="/counter" component={ Counter } />
    </div>
  </BrowserRouter>
)

export default Router

ここは特に工夫していないです。ナビゲーションも適当。

counter.tsx
import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { RootState, incrementActionCreator, decrementActionCreator } from './store';

const Counter: React.FC = () => {
  const [count, setCount] = useState(0)
  const dispatch = useDispatch()
  const counterState = useSelector((state: RootState) => state.counter)

  return (
    <div>
      react hooks count { count }
      <div>
        <button onClick={() => setCount(count + 1)}>+</button>
        <button onClick={() => setCount(count - 1)}>-</button>
      </div>
      redux count { counterState.count }
      <div>
        <button onClick={() => dispatch(incrementActionCreator())}>+</button>
        <button onClick={() => dispatch(decrementActionCreator())}>-</button>
      </div>
    </div>
  )
}

export default Counter

react-hooksもそんな使ってないのでちょっと余計なコードも追加。

useSelector

前までの mapStateToProps の代わり
https://react-redux.js.org/next/api/hooks#useselector

useDispatch

storeのdispatchを生成してくれる。アクションの中身を指定して渡してあげる。useCallbackは一旦省略。
https://react-redux.js.org/next/api/hooks#usedispatch

store

store.ts
import { combineReducers, createStore } from 'redux'

// Counter State
interface CounterState {
  count: number
}

const counterInitialState: CounterState = {
  count: 0
}

// actions
export const INCREMENT = 'COUNTER/INCREMENT'
export const DECREMENT = 'COUNTER/DECREMENT'

interface IncrementAction {
  type: typeof INCREMENT
  payload: null
}

interface DecrementAction {
  type: typeof DECREMENT
  payload: null
}

type CounterActionTypes = IncrementAction | DecrementAction

export const incrementActionCreator = (): CounterActionTypes => {
  return {
    type: INCREMENT,
    payload: null
  }
}

export const decrementActionCreator = (): CounterActionTypes => {
  return {
    type: DECREMENT,
    payload: null
  }
}
//---

// reducers
export const counterReducer = (
  state = counterInitialState,
  action: CounterActionTypes
): CounterState => {
  switch (action.type) {
    case INCREMENT:
      return { count: state.count + 1 }
    case DECREMENT:
      return { count: state.count - 1 }
    default:
      return state
  }
}
//---

// combine
export const rootReducer = combineReducers({
  counter: counterReducer
})

export type RootState = ReturnType<typeof rootReducer>

export const store = createStore(rootReducer)

ReturnType 知らなかった。
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#example-4

感想

ここにreset用のアクションとか付け足したいけど省略。最近vuexばっかり使っていたので、比較でもないですが僕はやっぱりreactの方が好きだなと思った。

参考

https://react-redux.js.org/next/api/hooks
https://redux.js.org/recipes/usage-with-typescript
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html
https://qiita.com/seya/items/8291f53576097fc1c52a

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

曜日を求める関数

はじめに

jsで曜日を求めたい時、getDay()を使うと下記のような整数が返ってきます。

整数 en ja
0 Sun
1 Mon
2 Tue
3 Wed
4 The
5 Fri
6 Sat

いや、Sunとか日とかを取得したいんだけど…という時に使いまわしたい関数を作ってみました。

ソース

test.js
function getWeekday(date, lang) {

    var list = {
        ja: ["", "", "", "", "", "", ""],
        en: ["Sun", "Mon", "Tue", "Wed", "The", "Fri", "Sat"]
    }

    //dateチェック
    if (date == undefined || date == "") {
        date = new Date().getDay();
    } else if (date.constructor.name == "Date") {
        date = date.getDay();
    } else if (!Number.isInteger(date)) {
        return "第一引数が不正です。";
    } else if (date < 0 || date > 6) {
        return "第一引数が不正です。";
    } else {
        date = Number(date);
    }

    //langチェック
    if (lang == undefined || lang == "") {
        lang = "ja";
    } else if (lang != "ja" && lang != "en") {
        return "第二引数が不正です。";
    }

    return list[lang][date];

}

使い方

第一引数(date)
Dateオブジェクト、または0~6の整数を入力します。
省略または空白にすると今の曜日を求めます。

第二引数(lang)
jaまたはenを入力します。
省略または空白にするとja形式で出力されます。

console
//今日の日本語
getWeekday()
""
getWeekday("","ja")
""

//今日の英語
getWeekday("","en")
"Mon"

//整数2に対応する日本語
getWeekday(2)
""
getWeekday("2")
""

//特定の日の日本語
getWeekday(new Date("2019-8-15"))
""

//エラーパターン

getWeekday("今日は何曜日?")
"第一引数が不正です。"

getWeekday(10)
"第一引数が不正です。"

getWeekday(1.1)
"第一引数が不正です。"

getWeekday("","jp")
"第二引数が不正です。"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptな人のためのRustのFuture入門

RustでI/Oを扱うプログラムを書く機会がありました。非同期I/Oのほうがパフォーマンスがよくなるらしく、tokio というフレームワークがよく使われているとのこと。tokio では Future をベースとして非同期処理を書くようです。明るい Future を生み出していけばよいプログラムがかけそうですね :thumbsup: 。しかし、 tokioやFutureがなんもわからんという問題がありました。そこで、JavaScriptのFuture、Promiseと対比させてRustのFutureについてまとめます。

JavaScriptのPromise

昔のJavaScript(Node.js)では、ファイル読み込みやネットワークアクセス等のI/O待ちが発生するときはコールバックという仕組みを用いていました。人々は辛くなり、ES2015ではPromiseが導入されました。さらにES2017ではasync/awaitが導入され、一層I/Oの処理が書きやすくなりました。

ではまずはJavaScriptで簡単なPromiseを書いてみましょう。

const promise = new Promise((resolve, reject) => {
    console.log("promise called");
    resolve('success');
});

promise の中身のコードはPromiseを定義した時点でランタイムにより実行されはじめます。そのため、上記のコードを実行するとすぐに promise called と表示されます。

Promise が成功したときの処理は then、失敗したときの処理は catch でつなげます。50%の確率で成功、50%の確率で失敗する promise を作ってみましょう。

const promise = new Promise((resolve, reject) => {
    console.log("promise called");

    if (Math.random() * 10 < 5) {
        resolve('success');
    } else {
        reject('failure'); 
    }
});

promise
    .then(res => console.log(res))
    .catch(err => console.error(err));

Wandbox で実行する

RustのFuture

JavaScriptのPromiseに対応するオブジェクトはFutureです。

Promise内では、処理が完了したときに resolveもしくはrejectを呼び結果を通知することになっています。一方で、Futureはポーリングモデルを採用しており、tokio等の非同期ランタイムがFutureの結果を取得しにいきます。tokioはFutureのpollメソッドを定期的に呼び出します。pollはまだ実行中だったら Poll::Pending を、処理が完了したら Poll::Ready を返します。

Futureは futures クレート1のものを使います。現時点(2019/08/14)では stable 版の Rust で利用できる futures クレートの最新版は 0.1.28 です。

futures バージョン 0.1 における Future は次のようなシグネチャをもちます1

pub trait Future {
    type Item;
    type Error;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error>;
}

では Promise で書いた処理を Rust で書いてみましょう。 poll メソッドを実装すれば OK です。 50% の確率で Future が正常終了し "success" が出力され、 50% の確率で Future がエラー "failure" を返します。

struct MyFuture;
impl Future for MyFuture {
    type Item = String;
    type Error = String;

    fn poll(&mut self) -> Poll<String, String> {
        println!("poll called");

        let mut rng = rand::thread_rng();
        let i: i32 = rng.gen_range(0, 10);
        if i < 5 {
            Ok(Async::Ready("success".to_string()))
        } else {
            Err("failure".to_string())
        }

    }
}

Rust の Future を実行するには、 Future を executor にわたす必要があります。 futures クレートにも executor があるので、とりあえずこちらを使ってみましょう。

Promise の then は and_then に、 catch は map_err に対応します。 executor に突っ込む future は Item = (), Error = () にする必要があるため、 Ok(())() を返して型を合わせます。

fn main() {
    let future = (MyFuture{}).and_then(|res| {
        println!("{}", res);
        Ok(())
    }).map_err(|err| {
        println!("{}", err); // () を返す
    });

    // futures クレートの executor
    futures::executor::spawn(future).wait_future().unwrap();
    println!("finished");
}

これでも動きますが、wait_future は Future の実行が完了するまでスレッドをブロックしてしまうので、通常は tokio などのランタイムを使います。

impl Future for MyFuture {
    ...
}

fn main() {
    let future = (MyFuture{}).and_then(|res| {
        println!("{}", res);
        Ok(())
    }).map_err(|err| {
        println!("{}", err); // () を返す
    });

    tokio::run(future);
    println!("finished");
}

Rust Playground で実行する

RustのFutureのFuture

Rust においても async/await が使えるようにするため、 Rust 1.37 で Future が std::future に収録されました。さらに、Rust 1.38 では async/await が stable になる予定です。近い将来には、 std::future の Future が広く使われるようになるでしょう。 tokio も master ブランチでは std::future をデフォルトで使うように開発が進められているようです。

futures 0.1 の Future と futures 0.3 の Future は相互変換できるため、環境を移行するのも難しくはないと思います(希望をこめて)。

futures 0.3 や std::future における Future は次のようなシグネチャを持ちます。

pub trait Future {
    type Output;
    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>;
}

std::future では成功/失敗の概念を Future から切り離しました。そのため、成功/失敗を表現するためには、Result を返すようにする必要があります。では、さきほどの処理を std::future の Future で書き換えてみましょう。

struct MyFuture;
impl Future for MyFuture {
    type Output = Result<String, String>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        println!("poll called");

        let mut rng = rand::thread_rng();
        let i: i32 = rng.gen_range(0, 10);
        if i < 5 {
            Poll::Ready(Ok("success".to_string()))
        } else {
            Poll::Ready(Err("failure".to_string()))
        }
    }
}

この Future を実行するには、 Future を executor にわたす必要があります。 ひとまずは futures::executor::block_on を使ってみましょう。and_then はもとの Future と同じ型を返す必要がありますが、 map_ok ではもとの Future と違う型を返すことができます。

let future = (MyFuture{}).map_ok(|res| {
    println!("{}", res);
}).map_err(|err| {
    println!("{}", err);
});
let _ = block_on(future);

futures 0.3 では ThreadPool という executor もあります。ThreadPool は複数のスレッドを用いて Future を実行してくれるため、通常はこちらの executor を使ったほうがいいと思います。いずれは tokio 等のランタイムでも気軽に std::future も使えるようになるでしょう。

fn get_future() -> impl Future<Output=Result<(), ()>> {
    (MyFuture{}).map_ok(|res| {
        println!("{}", res);
    }).map_err(|err| {
        println!("{}", err);
    })
}

let future = get_future();
ThreadPool::new().expect("Failed to create threadpool").run(future);

Future の途中結果を print したいことは多々あると思います。このために、 inspect_okinspect_err が提供されています2。 print するだけだったら map ではなくて inspect を使ったほうがいいでしょう。

let future = (MyFuture{}).inspect_ok(|res| {
    println!("{}", res); 
}).inspect_err(|err| {
    println!("{}", err);
});

let _ = block_on(future);

ソースコードの全文は Github にあります。

ドキュメントについて

Rust に async/await を取り入れるためには、Future を言語のコア機能に取り入れる必要がありました。一方で、言語のコア機能に Future を取り入れてしまうと、 Future の変更が困難になってしまいます。そのため、 std::future::Future の機能は最小限になっています。
https://doc.rust-lang.org/std/future/index.html

Future の便利機能は今後も futures クレートで提供されるようです。std::future と互換性がある futures 0.3 のドキュメントを参照してください。
https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.18/futures/index.html

then 等の便利メソッドは FutureExt トレイトで提供されます。これは use futures::FutureExt すると使えるようになります。

type Output = Result<String, String> のように Future の値を Result 型にした場合、 use TryFuture とすると TryFuture トレイト の機能が利用できるようになります。
また、 use futures::TryFutureExt すると、 TryFutureExt トレイト の機能が使えるようになります。map_okinspect_err はこちらで定義されています。

まとめ

この記事では JavaScript の Promise と対比させて、Rust の Future について解説しました。 Rust の Future はポーリングモデルを採用し、ランタイムが poll を呼び出すことで処理が進んでいきます。

Future には 0.1 系と 0.3 系があり、近い将来は 0.3 系が広く使われていくかと思いますが、これらは相互変換できます。現状は tokio 等のランタイムがデフォルトでサポートしており、ドキュメントの多い 0.1 系を使っていくのがいいでしょう。

ツッコミ、感想等コメントお待ちしています :sunglasses:

謝辞

この記事の一部は @__pandaman64__ 氏に助言をいただき執筆しました。この場を借りて謝意を表します。

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

Ajax通信を行う際のparamsのキーの設定について

Ajax通信を行う際のparamsのキーは、jQueryの方ではどのように設定しているのかが理解できたので、アウトプットします。

今回は、インクリメンタルサーチの実装におけるAjax通信のコードとコントローラのコードの一部のみを書きます。

Ajax通信のコード

search.js
$(function() {
  $(".search__products").on("keyup", function() {
    var input = $(".search__products").val();

    $.ajax({
      type: 'GET',
      url: '/products/search',
      data: { keyword: input },
      dataType: 'json'
    })
  });
});

3行目で、検索窓に入力された文字列のデータが、inputという変数に代入される。
8行目で、inputの変数をコントローラのparamsとして送るためのキーを設定する必要がある。このキーの名前は、コントローラのキーの名前と一致させる必要はある。その理由は、一致させなければ、paramsのキーが食い違ってしまい、Ajax通信ができなくなってしまうからである。

今回は、コントローラの方であらかじめ設定した以下のparamsのキーをAjax通信のdataという引数に渡しす。

products_controller.rb
params[:keyword]

コントローラのコード

products_controller.rb
def search
   @products = Product.where('title LIKE(?)', "%#{params[:keyword]}%")
   respond_to do |format|
     format.html
     format.json
   end
 end

2行目で、Ajax通信で送られてきたdataのキーが、コントローラのキーと一致するため、フロント側とコントローラ間のAjax通信が成功するようになる。
そして、その後paramsのキーに一致した複数のデータが@ productsに代入され、jsonの方に処理が行われる。

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

Material UI の Shadow を完全にオフる方法(React)

はじめに

Button をオーバーライドして

shadow = 'none'

にしておけば、オフにできるかなと思ったけど
押下した時に現れる影は消せていないことが判明したので、ちょこっと付け加えることにした

押下前

image.png

押下時

image.png

解決

これだけ
none の付け方に注目

import {createMuiTheme, makeStyles} from '@material-ui/core/styles';
import {MuiThemeProvider} from "@material-ui/core";

const theme = createMuiTheme({
  shadows: ["none"]
});

const Index = () => {
  return(
    <MuiThemeProvider theme={theme}>
      <Button variant={valiant} className={classes.button}>
        ボタンだよん    
      </Button>
    </MuiThemeProvider>
  )
}

最後に

「この画面では、俺はリプルエフェクトだけが欲しいんだ!!!影はいらん!!」
という方は試してみてくださいな

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

グローバル変数を扱いやすくする工夫

はじめに

グローバル変数を沢山作ると結構ややしくなる

test.html
<script>
    var now = new Date(),
        current_full_year = now.getFullYear(),
        current_month = now.getMonth(),
        current_date = now.getDate(),
        current_hours = now.getHours(),
        current_minutes = now.getMinutes(),
        current_seconds = now.getSeconds();
</script>

コンソールで確認してみる

console
//今の年は?
current_full_year
2019

//じゃ、今の分は?
durrent_minutes
Uncaught ReferenceError: current_minute is not defined
    at <anonymous>:1:13

//なぬ!!変数名違ったっけ?タイプミスか?…あ、最初の文字はdじゃなくてcだ!
current_minutes
33

//よしよし!そんじゃ気分一新!グローバル変数をまとめて消しちゃおう!!

//…簡単にはできない…

//ならば全てのグローバル変数に格納されている値を一望してみよう

//…簡単にはできない…

//…

と、ちょっと扱いにくいことがある。

全てのグローバル変数を1つの変数に集約してみる

test2.html
<script>
    var data = {
        now : new Date(),
        current_full_year : now.getFullYear(),
        current_month : now.getMonth(),
        current_date : now.getDate(),
        current_hours : now.getHours(),
        current_minutes : now.getMinutes(),
        current_seconds : now.getSeconds()
    };
</script>

コンソールで確認してみる

console
//今の年は時は?
data.current_full_year
2019

//じゃ、今の分は?※今度はスペルミスしたくない
data.
//全てのプロパティが予測変換できるので、入力も楽だし入力ミスもない
data.current_minutes
47

//よしよし。それじゃ全てのグローバル変数に格納されている値を一望してみよう
data
{now: Mon Aug 19 2019 10:47:48 GMT+0900 (日本標準時), current_full_year: 2019, current_month: 7, current_date: 19, current_hours: 10, }

//試しに全部消してみっか!
data=undefined
undefined

//消えていることを確認
data
undefined

data.current_full_year
VM961:1 Uncaught TypeError: Cannot read property 'current_full_year' of undefined
    at <anonymous>:1:6

少し書き方を変えるだけで頭すっきりしました(^^)

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

タブーの小技 パスワードクリップ BAT/VBA/Javascript

趣旨

職場などで使うと便利だけども
見つかると怒られる技術

そんなこつ言ったって面倒くさいものは面倒くさい
そのパスワードに意味ある?ほんとに?
ということはたびたびありますよね

クリップボードにパスワード入れてしまえばこっちのもんじゃーい!!
というあれこれです。

遊ぶ時間欲しさでやったなどと欲望直結の供述を繰り返しており
当局では精神鑑定も含め動機の解明にあたる方針です

Batファイル

一行で実装可能

秘密の鍵.bat
SET /P DUMMY="paswwwwwwwww"<NUL|clip

IE Javascript

IE専用ですが十分じゃろ

秘密の鍵.html
<html>
<head>
 <title></title>
 <script language="javascript" type="text/javascript">
  function SetClip() {
   window.clipboardData.setData("Text" , "paswwwwwwwww");
  }
 </script>
</head>
<body>
<input type="button" value="クリップボード" onclick="SetClip();"/>
</html>

Excel VBA

Microsoft Forms 2.0 Object Libraryを参照設定します。

※参照設定に存在しない場合は
c:\Windows\System32\FM20.DLLを参照してあげる。

Module1.bas
Sub SetClip()

Dim clipB As Object
Set clipB = New DataObject

With clipB
  .SetText "paswwwwwwwww"
  .PutInClipboard
End With

End Sub

秀丸エディタ

たまたま秀丸の入ってる環境だったので追記
カっとしてやった、反省はしていない。

秘密の鍵.mac
setclipboard "paswwwwwwwww";

先達 参考 Special Thanks

検索でひっかかるとは何事だ、大変ありがとうございます

Excel VBA から、クリップボードにテキストをセットする
https://qiita.com/rohinomiya/items/1b719bc90d7497688699

Office TANAKA クリップボードを操作する
http://officetanaka.net/excel/vba/tips/tips20.htm

VBEの参照設定で『Microsoft Forms 2.0 Object Library』が見つからない
http://www.tomodachihiroba.org/2016/01/vbe-microsoft-forms-2-0-object-library/

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

js メソッド名 find,map 備忘録

find

findメソッドとは、コールバック関数の処理部分に記述した条件式に合う1つ目の要素を配列の中から取り出すメソッドです。配列の要素がオブジェクトの場合もfindメソッドを使うことができます。

const numbers = [1,3,6,9,100]
const FoundNumber = numbers.find((number)=>{
   return number > 3;
)
console.log(FoundNumber);
//出力5=> 条件に合う最初の要素が指定される。

map

mapメソッドとは、配列内のすべての要素に処理を行い、その戻り値から新しい配列を作成するメソッドです。

const numbers = [1, 2, 3, 4];

// 定数numbersにmapメソッドを使って配列を作り、定数doubledNumbersに代入してください
const doubledNumbers = numbers.map((number)=>{
  return number*2;
});

// 定数doubledNumbersを出力してください
console.log(doubledNumbers);


const names = [
  {firstName: "Kate", lastName: "Jones"},
    {firstName: "John", lastName: "Smith"},
    {firstName: "Denis", lastName: "Williams"},
    {firstName: "David", lastName: "Black"}
];

// 定数namesにmapメソッドを使って新しい配列を作り、定数fullNamesに代入してください
const fullNames = names.map((name)=>{
  return name.firstName + name.lastName;
});

// 定数fullNamesを出力してください
console.log(fullNames);

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

JavaScript: ローカルのJSからfetch APIでGETしたJSONをFileSaver.jsを使ってファイルに保存する

ローカルでfetchを使うにはCORS対応が一工夫必要なので、下記の記事を参考までにどうぞ
Qiita - ローカルのHTMLファイルでfetchを試す & CORSとその回避策

やりたいこと

  • ローカルのJSからfetch APIを使って公開APIを叩き、GETしたJSONをファイルに保存する
  • FileSaver.jsというnpmのパッケージをnode.jsとかの無いローカル環境で動かしてみる

FileSaver.js

コード

test.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <script type="text/javascript" src="FileSaver.js"></script>
  </head>
  <body>
    <input type="button" value="取得" onclick="apiRequest()" />
    <div id="export"></div>
    <script>
      async function apiRequest() {
        const downloadUrl =
          "http://zipcloud.ibsnet.co.jp/api/search?zipcode=1620825";
        const response = await fetch(downloadUrl);
        const blob = response.blob();
        const fileName = "test.json";
        saveAs(blob, fileName);
      }
    </script>
  </body>
</html>

ファイルの中身

test.json
{
        "message": null,
    "results": [
        {
            "address1": "東京都",
            "address2": "新宿区",
            "address3": "神楽坂",
            "kana1": "トウキョウト",
            "kana2": "シンジュクク",
            "kana3": "カグラザカ",
            "prefcode": "13",
            "zipcode": "1620825"
        }
    ],
    "status": 200
}

まとめ

  • 簡単だった
  • 今度何やろう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsを仮想ウインドウ化するモジュールをnpmに登録してみる

Vue.jsを仮想ウインドウ化するモジュールをnpmに登録してみる

1.動作画面

動作画面

 
見ての通り、Vue.jsを仮想ウインドウ化してみました。
内容的には大したことはしていません。
以前作成したJavaScript-Window-Framework(JWF)にVueのコンポーネントをマウントする処理を入れただけです。

2.使い方

モジュールのインストール

npm -D i @jswf/vue

 npmでjwfスコープを取りたかったのですが、すでに取得済みだったようなのでjswfになっています。
 そのうちJWF本体をこちらのスコープに移すかもしれません。
 ちなみにnpmのスコープ登録の仕方を知ったのは最近になってからです。

サンプルコード

以下はrouterを有効にしたテンプレートに、必要なコードを追加したものです。

main.ts
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import { VueWindow } from '@jswf/vue';

Vue.config.productionTip = false;

new Vue({router, render: (h) => h(App)}).$mount('#app');

//---------------------
//Create Virtual Window
const win = new VueWindow(
  new Vue({router,render: h => h(App)})
);

win.setTitle('WindowTest');  //タイトル設定
win.setPos();               //位置を中心に揃える
//---------------------

3.まとめ

 JWF自体はウインドウ生成や子ウインドウの配置を主にしているフレームワークなので、クライアント領域に表示するコンテンツには干渉しない形になっています。そのためJWFに標準機能として付いているTreeViewやListViewは、かなり大きめのコードになっています。この状態で機能を追加しようと思ったら、自由はききますが、がっつりコードを書かなければなりません。今回はVue.jsを入れることによって、Vueのコンポーネントを簡単に持ってこられるようにしました。これでユーザからの入出力が頻繁に起こる部分の作成がかなり楽になると思います。

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

EcmaScriptまわりを整理

ECMAScript Editions

Official Name a.k.a. a.k.a
ECMAScript2009 ECMAScript5 ES5
ECMAScript2015 ECMAScript6 ES6

Versions

ES6

  • let, const
  • Default parameter values
  • Arrow Functions

周辺ツールなどをざっくりと

Babel

  • JSのコンパイラ
  • ES6で書いたコードをES5に変換してくれる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript jquery.datetimepicker.js

datetimepicer の簡易サンプル、オプションはてきとうに設定

download (zip)

https://xdsoft.net/jqplugins/datetimepicker/

html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>

<link rel="stylesheet" type="text/css" href="./jquery.datetimepicker.css"/>
<script src="./jquery.js"></script>
<script src="./build/jquery.datetimepicker.full.js"></script>
<script>
$(function(){
$('#datetimepicker1').datetimepicker({
    timepickerScrollbar:true,
    scrollTime:true,
    scrollInput:true,
    scrollMonth:true
});
$('#datetimepicker2').datetimepicker({datepicker:false});
$('#datetimepicker3').datetimepicker({timepicker:false});
});
</script>
</head>

<body>
    <h3>DateTimePicker</h3>
    <input type="text" id="datetimepicker1"/><br><br>
    <h3>TimePicker</h3>
    <input type="text" id="datetimepicker2"/><br><br>
    <h3>DatePicker</h3>
    <input type="text" id="datetimepicker3"/><br><br>
</body>
</html>

  • scrollTime のオプションきかない...
    build/jquery.datetimepicker.full.js 2087行目に以下を追加すれば...
            if (!options.scrollTime) {
                timeboxparent.off('mousewheel');
            }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ある要素が表示されるまで待つJSライブラリを書いた(MutationObserver)

インストール

ブラウザで使う

<script src="https://gistcdn.githack.com/GitHub30/1b13ed6687a22385a81cf98fefdd5a13/raw/42012a345cc5cb1e4f773300e99765b644a40164/async$.js"></script>

Greasemonkey(Tampermonkey)で使う

下記を追加する。

// @require  https://gist.github.com/GitHub30/1b13ed6687a22385a81cf98fefdd5a13/raw/42012a345cc5cb1e4f773300e99765b644a40164/async$.js

使い方

querySelector

const title = await async$('title')
console.log(title.textContent)

Usage

async$(selector, [startNode])

querySelectorAll

const links = await async$$('a')
console.log(links)

Usage

async$$(selector, [startNode])

XPath

const links = await async$x('//a')
console.log(links)

// 要素を1つだけ取得
const h1 = await async$x('//h1', document, XPathResult.FIRST_ORDERED_NODE_TYPE)
console.log(h1.textContent)

Usage

async$x(selector, [startNode], [XPathResult#Constants])

XPathResult#Constants
https://developer.mozilla.org/ja/docs/Web/API/XPathResult#Constants

タイムアウト

// 1秒でタイムアウト
window.ASYNC$_TIMEOUT = 1000
await async$x('//a')

ASYNC$_TIMEOUTにミリ秒でタイムアウトを設定すると、要素が見つからない場合、エラーになります。よしなにtry catchしてください。
デフォルトはタイムアウトしません。

引用

ある要素が表示されるまで待つJSライブラリを書いた(MutationObserver)
https://efcl.info/2015/04/06/wait-for-element/

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