20200704のJavaScriptに関する記事は20件です。

Vue.js における Component 間のデータの受け渡しまとめ

Overview

Vue.js を使っていて、この Component 間の関係だとどうやって data 渡すんだっけな...?
となることが多いので方法はいろいろあると思いますが、自分がよくやるやり方をこの記事にまとめておきます。
(他にもこんなやり方あるよ!という知見ありましたら教えていただけますと幸いです :pray: )

親 → 子

親 Component から 子 Component へのデータの受け渡しは、
親 Component で data を v-bind して、子 Component で props で data を引き取ります。

Sample Code

  • Parent.vue ( v-bind で data を渡す)
<template>
  <div id="parent">
    <child v-bind:messageFromParent="this.message" />
  </div>
</template>

<script>
import Child from './Child'

export default {
  name: 'Parent',
  data () {
    return {
      message: 'Hello from Parent!'
    }
  },
  components: {
    child: Child
  },
}
</script>
  • Child.vue ( props で引き取る)
<template>
  <div id="child">
    {{ messageFromParent }}
  </div>
</template>

<script>
export default {
  name: 'Child',
  props: ['messageFromParent']
}
</script>

子 → 親

逆に、子 Component から 親 Component への data の受け渡しは
親 Component で v-on でイベントハンドラを定義しておき、子 Component では $emit で data を持たせて 親 Component で提起しているイベントを発火させます。

※ emit : 放つ、放射する

Sample Code

  • Parent.vue ( v-on でイベントハンドラを定義する)
<template>
  <div id="parent">
    <child v-on:sendMessage="receiveChildMessage" />
    {{ this.childMessage }}
  </div>
</template>

<script>
import Child from './Child'

export default {
  name: 'Parent',
  data () {
    return {
      childMessage: ''
    }
  },
  components: {
    child: Child
  },
  methods: {
    receiveChildMessage (message) {
      this.childMessage = message
    }
  }
}
</script>
  • Child.vue ( $emit で data を持たせつつ、親のイベントを発火する)
<template>
  <div id="child"/>
</template>

<script>
export default {
  name: 'Child',
  data () {
    return {
      message: 'Hello from Child!'
    }
  },
  created () {
    this.$emit('sendMessage', this.message)
  }
}
</script>

子A → 子B

これは実装の方針が人によって分かれそうなのですが、自分は 2つ以上の Component にまたがって data の受け渡しを行う場合 (子A → 親 → 子B 、 親 → 子 → 孫 など) Vuex を使うことが多いです。

※ Vuex についの詳細はこちらをご参照ください。
https://vuex.vuejs.org/ja/

Sample Code

  • store.ts
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

interface State {
  message: string
}

const initState: State = {
    message: ''
};

export default new Vuex.Store({
  state: initState,
  mutations: {
    setMessage(state, { message }: { message: string }) {
      state.message = message;
    }
  }
})
  • Parent.vue ( store を import する)
<template>
  <div id="parent">
    <child-a />
    <child-b />
  </div>
</template>

<script>
import store from './store'
import ChildA from './ChildA'
import ChildB from './ChildB'

export default {
  name: 'Parent',
  store,
  components: {
    child-a: ChildA,
    child-b: ChildB
  },
}
</script>
  • ChildA.vue ( Vuex に data を store する)
<template>
  <div id="child-a"/>
</template>

<script>
export default {
  name: 'ChildA',
  created () {
    this.$store.commit('setMessage', { message: 'Hello from ChildA!' });
  }
}
</script>
  • ChildB.vue ( Vuex から data を参照する)
<template>
  <div id="child-b"/>
  {{ this.message }}
</template>

<script>
export default {
  name: 'ChildB',
  data () {
    return {
      message: ''
    }
  },
  created () {
    this.message = this.$store.state.message
  }
}
</script>

まとめ

  • 親 Component → 子 Component

(親) v-bind:'value'(子) props['value']

  • 子 Component → 親 Component

(子) this.$emit('childHandler', value)(親) v-on:childHandler="parentHandler"

  • 子 Component A → 子 Component B

(子A) Vuex に data を store(子B) Vuex から data を参照

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

[JavaScript]エラーをthrowする場合はちゃんとカスタムエラーを作ろう

エラーをthrowする際は、カスタムエラーを定義してあげましょう。
例えば、意図して処理をスルーさせている場合は、コメントではなくカスタムエラーでやったほうがより良いコードとなります。(併用もOK)

class GetUser404Error extends Error {};

function init() {
  try {
    const user = getUser(123):
    // ...
  } catch(e) {
    if (e.status === 404) {
      throw new GetUser404Error();
    }
     throw e;
  }
}

try {
  init();
  // ...
} catch(e) {
  // データが取得できない場合は、エラーを表示せずに処理を終える
  if (e instanceof GetUser404Error) {
    return;
  }
  errorDialog(e.message);
}

参考

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Error

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

【JavaScript】FileReaderをasync/awaitでエレガントに使いたい

こんにちは。

皆さんもFileReaderを使う機会は多いと思います。
FileReaderは非同期なので、コールバック地獄に陥りがちです。

しかし最近のJavaScriptには、非同期なコードを同期的に扱えるasync/awaitという素晴らしい機能が存在します。

FileReaderもasync/awaitの恩恵を受けたい!!

解決策

class FileReaderEx extends FileReader{
    constructor(){
        super();
    }

    #readAs(blob, ctx){
        return new Promise((res, rej)=>{
            super.addEventListener("load", ({target}) => res(target.result));
            super.addEventListener("error", ({target}) => rej(target.error));
            super[ctx](blob);
        });
    }

    readAsArrayBuffer(blob){
        return this.#readAs(blob, "readAsArrayBuffer");
    }

    readAsDataURL(blob){
        return this.#readAs(blob, "readAsDataURL");
    }

    readAsText(blob){
        return this.#readAs(blob, "readAsText");
    }
}

元のFileReaderクラスをPromiseでラップした拡張クラスを作成して万事解決。
#readAs()は外部から見られたくないのでプライベートメソッドにしてあります。

ビフォーアフター

今までコールバックにまみれていたコードも...

Before
const reader1 = new FileReader();
reader1.addEventListener("load", ()=>{
    const buffer1 = reader1.result;

    const reader2 = new FileReader();
    reader2.addEventListener("load", ()=>{
        const buffer2 = reader2.result;

        const reader3 = new FileReader();
        reader3.addEventListener("load", ()=>{
            const buffer3 = reader3.result;
        });

        reader3.addEventListener("error", ()=>{
            alert(reader3.error.message);
        });

        reader3.readAsArrayBuffer(blob3);
    });

    reader2.addEventListener("error", ()=>{
        alert(reader2.error.message);
    });

    reader2.readAsArrayBuffer(blob2);
});

reader1.addEventListener("error", ()=>{
    alert(reader1.error.message);
});

reader1.readAsArrayBuffer(blob1);

async/awaitの手に掛かればこの通り!!

After
(async()=>{
    const buffer1 = await new FileReaderEx().readAsArrayBuffer(blob1);
    const buffer2 = await new FileReaderEx().readAsArrayBuffer(blob2);
    const buffer3 = await new FileReaderEx().readAsArrayBuffer(blob3);
})();

一目瞭然で見やすくなっています。

FileReader以外にも応用が利く、かなりオススメな手法です。

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

よく耳にするReactを自分なりにまとめてみた...

自己紹介

むちゃんです。
関西を拠点に活動しているフロントエンドエンジニアです。

HAL大阪の2回生です:boy_tone2: (2020.7.4現在)

イベントなど回っているので是非大阪辺りの方は会いましょう!

1...REACTとは?

Facebook社が開発したWebのUIを作るためのJavascriptのライブラリ

[公式ドキュメント]:https://ja.reactjs.org/

2...コンポーネントとは?

[見た目]+[機能]を合わせた物。

  1. 見た目(View)
  2. 機能(controller)

Webの構造(コンポーネントツリー)

・オブジェクトの階層構造のこと

20161026134922.png

なぜコンポーネントを使うのか?

  1. 再利用をするため
  2. 分割統治するため(お互いの依存関係をなくす)
  3. 変数に強くなるため

コンポーネントの種類

・Functional Component(ファンクショナルコンポーネント)
名称未設.png

・Class Component(クラスコンポーネント)
名設定.png

3...DOMについて

そもそもDOMって何?

〜Document Object Model (DOM)=要はインターフェース

HTMLにアクセスする窓口みたいな物
構造や見た目、コンテンツを変更

Virtual DOMとは

〜Reactが管理しているDOM

ブラウザのレンタリングと別管理
->効率よくDOM操作をできる

・通常のDOM操作

document.getElemntById('hoge').innerText='hoge'

・ReactのVirtualDOM操作

render(
<div id="hoge">hoge</div>
)

JSXについて(トランスコンパイル)

〜Javascropt内でHTMLっぽくる

ReactDOM.render(
<div ClassName={hoge}>
  <h1>HelloWorld</h1>
</div>
)

4...JSXの基本文法での注意点!!

・Reactのパッケージインストールが必要

import React from "react"

・ClassはClassNameと記述

・1つで完了するタグは終了タグをつける

あとがき

今回は自分なりにReactについて簡潔にまとめてみした:thumbsup_tone2:

次回はstateやライフサイクルについてより詳しくまとめてみたいと思います。

Twitter @HomgMuchan ぜひフォロー待っています❗️

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

コマンドラインからjavascriptでレンダリングされたHTMLソースを取得する方法

curlからは、javascriptを有効にしたブラウザと同じHTMLソースを取得することが難しいことがあります。このような場合、phantomjsを使用すると便利です。

$ npm i -g phantomjs phantom

公式サイトにてバイナリも配布されてるので、nodeからのインストールが依存関係などで失敗する場合、こちらをダウンロード、解凍して実行権限を与えると良いでしょう。

https://phantomjs.org/download.html

$ curl -sLO https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2
$ aunpack phantomjs-2.1.1-linux-x86_64.tar.bz2
$ cd phantomjs-2.1.1-linux-x86_64/bin/
$ chmod +x phantomjs
$ ./phantomjs --version
set.js
var system = require('system');
var page   = require('webpage').create();argument
var url    = system.args[1];
page.open(url, function () {
  console.log(page.content);
  phantom.exit();
});
$ phantomjs set.js https://google.com
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails s 実行時のCould not find a JavaScript runtime.のエラー対応方法

macOSのバージョンを上げてしまい、開発中のRailsアプリのローカル環境でローカルサーバーが起動出来なくなり、以下のようなエラーが発生したため、忘備録のために投稿します。

macOS Catalina バージョン 10.15.5
環境:Ruby,Ruby on Rails,JavaScript

エラー内容

Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)

解決方法

Node.jsの最新版をインストールする。
以上です。これだけでした。
スクリーンショット 2020-06-22 3.55.41.png

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

webpack.config.js の書き方をしっかり理解しよう

webpack.config.js

僕の中で少し苦手なイメージのあるwebpackについて、改めて向き合って理解していこうと思います。
webpackに苦手意識がある人のほとんどは、このwebpack.config.jsについて理解していない人がほとんど。なので、今回は下記のwebpack.config.jsについて丁寧に説明していきます。

webpack.config.js
const path = require('path')
const webpack = require('webpack')

module.exports = {
  entry: {
    index: path.join(__dirname, 'src', 'index.js')
  },
  output: {
    path: path.join(__dirname, 'out'),
    filename: 'main.js'
  },
  devtool: 'cheap-module-eval-source-map',
  target: 'node',
  module: {
    rules: [
      {
        test: /.js$/,
        loader: 'babel-loader'
      }
    ]
  },
  plugins: [
    externalPlugins
  ]
}

entryoutput

結論から言うと、
entry:は、webpackがビルドする際に開始点となるJSファイルを設定しています。
output:は、ビルドファイルの設定。どこにどのようなファイルを出力すればよいか設定しています。

具体的にみていきます。

webpack.config.js
const path = require('path')
const webpack = require('webpack')

module.exports = {
  entry: {
    index: path.join(__dirname, 'src', 'index.js')
  },
  output: {
    path: path.join(__dirname, 'out'),
    filename: 'main.js'
  },
...
}

entryの設定

早速説明していきます。
簡単に理解できますので、安心してください(笑)

ここでは、webpackがビルドした際に
./src/index.jsというファイルを探して読み込んでね。」という設定をしています。

まずpath.joinでパスを連結してくれていて、上記だと./src/index.jsというパスにしてくれています。

これを使うメリットとしては、
macやwindowsなどの開発環境によって/¥になったりするんですけど、それを防ぐことができます。
そしてNode.jsの標準モジュールのpathを読み込ませて使用しているので、path.joinとして使用できています。

__dirnameNode.jsで用意されているグローバル変数で、現在実行中のソースコードが格納されているディレクトリパスが格納されています。簡単に言うと、手軽に絶対パスの記述ができるものです。

outputの設定

path:filename:がありますね。

こちらも結論から言うと
path:は、どのディレクトリに出力するか指定しています。
filename:は、どのファイルに出力するか指定しています。

なので、
./srcというディレクトリ内のmain.jsというファイルに出力してね。」という設定をしています。

devtool

まず、結論です。
devtool:を設定することによって、ソースマッピングのスタイルを選択して、デバッグプロセスを強化します。指定可能な値はstringfalseです。
(参照:公式ドキュメントのdevtool

分からない用語が出てきた?ソースマッピングですかね?

分からなければ、しっかり調べていきましょう。

ソースマップについて

ソースマップとは、Babelなどトランスパイル後と前のコードの内容を紐付けしてデバッグしやするするもの。

トランスパイルについて
JavaSciprtではwebpackでコンパイルなどをする時に同時にトランスパイルという処理を行います。
具体的に何をしているのかというと、互換性のあるコードに変換をする処理。
(例)「CoffeeScriptとかを記載して、Javascriptへトランスパイルする。」みたいな感じです。
一般的にはBabelというライブラリを使います。

devtool: 'cheap-module-eval-source-map'

まとめると、ここでは'cheap-module-eval-source-map'を設定しており、公式ドキュメントによると、ビルドと再ビルトの処理速度としては遅めに処理されるようです。

webpack.config.js
...
module.exports = {
...
  devtool: 'cheap-module-eval-source-map',
...
}

target

結論から言うと、
target:を設定することによって、特定の環境をターゲットにするようにwebpackに指示してくれます。ここでは、'node'と設定しているので、Node.jsのような環境で使用するためにコンパイルしてます。

webpack.config.js
...
module.exports = {
...
  target: 'node',
...
}

module

module:オプションは、プロジェクト内のさまざまなタイプのモジュールがどのように処理されるかを設定しています。「webpack が特定のmoduleをどう扱うか」を決めるってことです。webpack ではmoduleJavaScriptCSSなどのファイルを指す。
ここでは、Ruleモジュールについて設定されているので、詳しくみていきます。

webpack.config.js
...
module.exports = {
...
  module: {
    rules: [
      {
        test: /.js$/,
        loader: 'babel-loader'
      }
    ]
  },
...
}

Rule

まず結論から、
rule:を設定すると、「条件」、「結果」、「ネスト」されたルール3つの部分に分けることができます。

Rule.test

公式ドキュメントによると、テストアサーションに合格したすべてのモジュールを含めます。 Rule.testオプションを指定した場合、Rule.resourceも指定できません。
と書いてありましたが、ちょっと「??」のままでした。

いろいろ調べてみた結果を簡単にいうと、
Rule.testで拡張子を設定する」というイメージです。
webpackでいうmodule(JavaScriptCSSなど)を設定するってことです。
今回で言うと、拡張子が.jsなので、test: /.js$/と設定しています。

このまま一気に説明した方が理解がしやすいと思うので、一気に説明していきます。

Rule.loader

この2つですが、簡単にいうと
まずloader: 'babel-loader'と設定することで、test: /.js$/で設定したjsファイル'babel-loader'として読み込む設定をしています。

plugins

最後plugins:です。
pluginsオプションは、さまざまな方法でwebpackビルドプロセスをカスタマイズするために使用されます。externalPluginsというプラグインがカスタマイズされています。

公式ドキュメントによると、
webpackには豊富なプラグインインターフェイスがあります。 webpack自体のほとんどの機能は、このプラグインインターフェイスを使用します。これにより、Webpackが柔軟になります。

最後

最後の方は駆け足で説明することになりましたが、少しでも理解の手助けになれば幸いです。
こんなことを最後にいうことではありませんが、webpackについては公式ドキュメントに結構丁寧に説明が書いてあるので、そこをみれば理解できるかと思います。

何か間違い等ありましたら教えていただけると幸いです。

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

【Vue】Vue開発で使えるいくつかの小技

Vueまとめパート4(小技集)

こちらの記事は、Adnan Babakan 氏によりDev.to上で公開された『 Vue advanced tricks cheat sheet 』の邦訳版です(原著者から許可を得た上での公開です)


Cover image for Vue advanced tricks cheat sheet

DEV.toコミュニティの皆さん、こんにちは!

普通にVueで開発をしていると、解決するべき多くの問題に遭遇しますが、開発者にとって長時間苛立たせる問題を解決することほど気持ちいいことはないなと感じています!

ここで、私はいくつかの問題を解決するために使用したいくつかの小技を書いています。これを読んでいるあなたにもいつか役立つかもしれません。

vm.$forceUpdate() - 強制的再レン​​ダリング

場合によっては仮想DOMの一部の変更を反映できないことがあるため、このような場合ではVueにコンポーネントを再レンダリングさせる必要がある。

このような場合には他にもv-ifを使うなどいくつか方法があるが、forceUpdateメソッドを使うのが正しいとされている。

export default {
  methods: {
    forceUpdateMyComponent() {
      this.$forceUpdate()
    },
  },
}

Vueインスタンスでこのメソッドを使用するためには$をメソッド名の前につける必要があることに注意。

使ってみるとわかるがどの算出プロパティも更新されず、コンポーネントのビューが強制的に再レン​​ダリングされるだけだ。

ネストされたデータの監視

場合によっては、ネストされたデータを監視したい場合もあるだろう。このような場合は.チェーンで実現できる。

  export default {
    data() {
      return {
        myInfo: {
          firstName: 'Adnan'
        }
      }
    },

        // `myInfo.firstName`とつなげることで監視プロパティにできる 
    watch: {
      'myInfo.firstName'() {
        console.log('Here')
      }
    }
  }

上記のコードは、オブジェクト内の監視するプロパティどれかがわかっている場合に使える。

しかし、オブジェクト全体とその値を監視したい場合はどうすればいいか?
その場合は次のようにする。

  export default {
    data() {
      return {
        myFruits: {apple: 'red', banana: 'yellow'}
      }
    },

    methods: {
      changeIt() {
        this.myFruits.apple = 'green'
      }
    },

    watch: {
      myFruits: {
        // `deep`, `handler`プロパティを追加する
        deep: true,
        handler() {
          console.log('Fruits updated!')
        }
      }
    }
  }

監視プロパティを関数としてではなくオブジェクトとして定義し、deepキーとその値をtrueにセットする。これによってオブジェクト全体を監視でき、handlerと呼ばれる関数内にデータが変更されたときに発生させたいことを書くことができる。

v-modelをサポートするカスタム(子)コンポーネント

ご存じのとおりv-modelはコンポーネント(input要素)とデータプロパティを介した双方向バインディングを実現するために使われる。

カスタム(子)コンポーネントを作成し、v-modelをサポートさせたい場合は、inputキーワードを発行し、最初にコンポーネントに渡されるものをv-modelで受け取るために、valueと呼ばれるpropを取得する必要がある。

より理解しやすくするために、次のコードを見てみよう。

<template>
    <div>
        <label>My custom test input: <input :value="inputValue" @input="emitTheData" /></label>
    </div>
</template>

<script>
  export default {
    props: ['value'],

    data() {
      return {
        inputValue: value
      }
    },

    methods: {
      emitTheData() {
        this.$emit('input', this.inputValue)
      }
    }
  }
</script>

この単純なコンポーネントは、渡されたデータを2つの方向にバインドする。

注:ご覧の通り、valueというpropをinputValueというデータに割り当てて、それをinputタグで使用した。propをinput要素に直接渡すことができると思うかもしれないが、Vue公式で言われている通り、propsは直接変更しない方がよい

関数型コンポーネント

監視プロパティまたは算出プロパティとメソッドのないコンポーネントがあると想像してみよう。それを使うだけで、propsを使ってコンポーネントをレンダリングすることが可能だ。これが可能であれば、ライフサイクルメソッドもないため描画時間を短縮することができる。

それが関数型コンポーネントだ。

関数型コンポーネントは次のように定義できる。

<!-- `template`要素の中に`functional`含めると関数型コンポーネントになる -->
<template functional>
  <div>{{ props.foo }}</div>
</template>

このコンポーネントは関数型コンポーネントであるため、thisコンテキストは使えないことに注意。

Vueの関数型コンポーネントの詳細については、以下のリンクから確認できる。

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

【Rails】FullCalendarを用いたスケジュール管理昨日の実装

目標

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

開発環境

・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina

前提

下記実装済み。

Slim導入
Bootstrap3導入
Font Awesome導入
ログイン機能実装
投稿機能実装

実装

1.Gemを導入

Gemfile
# 追記
gem 'jquery-rails'
gem 'fullcalendar-rails'
gem 'momentjs-rails'
ターミナル
$ bundle

2.application.scssを編集

application.scss
 *= require_tree .
 *= require_self
 *= require fullcalendar /*追記*/

3.application.jsを編集

application.js
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery
//= require moment // 追記 
//= require fullcalendar // 追記
//= require_tree .

4.JavaScriptファイル作成・編集

ターミナル
$ touch app/assets/javascripts/calendar.js
calendar.js
$(function() {
  function eventCalendar() {
    return $('#calendar').fullCalendar({});
  }

  function clearCalendar() {
    $('#calendar').html('');
  }

  $('#calendar').fullCalendar({
    events: '/events.json',

    titleFormat: 'YYYY年 M月',
    dayNamesShort: ['', '', '', '', '', '', ''],

    header: {
      left: '',
      center: 'title',
      right: 'today prev,next',
    },

    defaultTimedEventDuration: '03:00:00',
    buttonText: {
      prev: '',
      next: '',
      prevYear: '前年',
      nextYear: '翌年',
      today: '今日',
      month: '',
      week: '',
      day: '',
    },
    timeFormat: 'HH:mm',
    eventColor: '#63ceef',
    eventTextColor: '#000000',
  });
});

【解説】

① turbolinks対策の為、カレンダーを読み込む関数と削除する関数をそれぞれ用意する。

function eventCalendar() {
  return $('#calendar').fullCalendar({});
}

function clearCalendar() {
  $('#calendar').html('');
}

② 日本語で表示する。

// カレンダー上部を年月で表示させる
titleFormat: 'YYYY年 M月',

// 曜日を日本語表示
dayNamesShort: ['', '', '', '', '', '', ''],

③ ボタンのレイアウトを整える。

header: {
  left: '',
  center: 'title',
  right: 'today prev,next',
},

④ イベントの設定をする。

// 終了時刻がないイベントの表示間隔を設定
defaultTimedEventDuration: '03:00:00',
buttonText: {
  prev: '',
  next: '',
  prevYear: '前年',
  nextYear: '翌年',
  today: '今日',
  month: '',
  week: '',
  day: '',
},

// イベントの時間表示を24時間にする
timeFormat: 'HH:mm',

// イベントの色を変える
eventColor: '#63ceef',

// イベントの文字色を変える
eventTextColor: '#000000',

4.モデルを作成

ターミナル
$ rails g model Event user_id:integer title:string body:text start_date:datetime end_date:datetime
~_create_events.rb
class CreateEvents < ActiveRecord::Migration[5.2]
  def change
    create_table :events do |t|
      t.integer :user_id
      t.string :title
      t.text :body
      t.datetime :start_date
      t.datetime :end_date

      t.timestamps
    end
  end
end
ターミナル
$ rails db:migrate

5.コントローラーを作成

ターミナル
$ rails g controller events new show edit my_calendar
events_controller.rb
class EventsController < ApplicationController
  before_action :set_event, only: [:show, :edit, :update, :destroy]

  def new
    @event = Event.new
  end

  def create
    @event = Event.new(event_params)
    @event.user_id = current_user.id
    @event.save ? (redirect_to event_path(@event)) : (render 'new')
  end

  def index
    @events = Event.where(user_id: current_user.id)
  end

  def show
  end

  def edit
    unless @event.user == current_user
      redirect_to root_path
    end
  end

  def update
    @event.update(event_params) ? (redirect_to event_path(@event)) : (render 'edit')
  end

  def destroy
    @event.destroy
    redirect_to my_calendar_path
  end

  def my_calendar
  end

  private

    def set_event
      @event = Event.find(params[:id])
    end

    def event_params
      params.require(:event).permit(:user_id, :title, :body, :start_date, :end_date)
    end
end

6.ルーティングを追加

routes.rb
# 追記
resources :events
get 'my_calendar', to: 'events#my_calendar'

7.json.jbuilderファイルを作成・編集

index.json.jbuilder
json.array!(@events) do |event|
  json.extract! event, :id, :title, :body
  json.start event.start_date
  json.end event.end_date
  json.url event_url(event)
end

【解説】

indexアクションで抽出したレコードを繰り返し処理し、配列を作成する。

json.array! @category_children do |children|

② 各データを で作成した配列に格納する。

json.extract! event, :id, :title, :body
json.start event.start_date
json.end event.end_date
json.url event_url(event)

8.ビューを編集

new.html.slimを編集

① HTMLを作成する。

new.html.slim
.row
  .col-xs-3

  .col-xs-6
    = form_with model: @event, url: events_path, local: true do |f|

      = f.label :title, 'スケジュール名'
      br
      = f.text_field :title, class: 'form-control'
      br

      = f.label :body, 'スケジュール内容'
      br
      = f.text_area :body, class: 'form-control'
      br

      = f.label :start_date, '開始日時'
      br
      = f.datetime_select :start_date, {}, class: 'form-control datetime-form'
      br
      br


      = f.label :end_date, '終了日時'
      br
      = f.datetime_select :end_date, {}, class: 'form-control datetime-form'
      br
      br

      = f.submit '新規登録', class: 'btn btn-success btn-block'

  .col-xs-3

② CSSでデザインを整える。

application.scss
// 追記
.datetime-form {
  display: inline-block;
  width: auto;
}

show.html.slimを編集

show.html.slim
.row
  .page-header
    h3
      | スケジュール管理

.row
  #calendar
  br

.row
  table.table.table-bordered
    tbody
      tr
        th.active
          | スケジュール名
        td
          = @event.title
      tr
        th.active
          | スケジュール内容
        td
          = @event.body
      tr
        th.active
          | 開始日時
        td
          = @event.start_date.to_s(:datetime_jp)
      tr
        th.active
          | 終了日時
        td
          = @event.end_date.to_s(:datetime_jp)

.row
  - if @event.user === current_user
    .pull-right
      = link_to 'スケジュール登録', new_event_path, class: 'btn btn-success'
      = link_to '編集', edit_event_path(@event), class: 'btn btn-primary'
      = link_to '削除', event_path(@event), method: :delete, data: { confirm: '本当に削除しますか?' }, class: 'btn btn-danger'

edit.html.slimを編集

edit.html.slim
.row
  .col-xs-3

  .col-xs-6
    = form_with model: @event, url: event_path(@event), local: true do |f|

      = f.label :title, 'スケジュール名'
      br
      = f.text_field :title, class: 'form-control'
      br

      = f.label :body, 'スケジュール内容'
      br
      = f.text_area :body, class: 'form-control'
      br

      = f.label :start_date, '開始日時'
      br
      = f.datetime_select :start_date, {}, class: 'form-control datetime-form'
      br
      br


      = f.label :end_date, '終了日時'
      br
      = f.datetime_select :end_date, {}, class: 'form-control datetime-form'
      br
      br

      = f.submit '保存', class: 'btn btn-primary btn-block'

  .col-xs-3

my_calendar.html.slimを編集

my_calendar.html.slim
.row
  .page-header
    h3
      | マイカレンダー

.row
  = link_to 'スケジュール登録', new_event_path, class: 'btn btn-success'

  #calendar
  br

注意

turbolinksを無効化しないとカレンダーが表示されない事があるので、必ず無効化しておきましょう。

turbolinksを無効化する方法

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

【JavaScript】プログラミングクイズでよく使う記法

結論

  • プログラミングクイズでよく使う記法と知見をメモしています。
  • 初〜中級者向けの内容です。
  • 手短に結論だけ欲しい方は、各タイトルの見出し〜ソースまでをお読みいただければ十分です。

はじめに

プログラミングのクイズを200問解いてみました。
難易度は文を読む時間を含めて1分〜3分ほどで解ける簡単な問題が多かったですが、解き方を調べるうちに

こうしたら短く書けるんだ! 」  とか、

この関数、こんな使い方できたんだ・・・

などといった気づきを得ました。
基本的な文法も改めて学習することができて非常に有意義でした。
その時の知見をここにメモしようと思います。

けっこう好みがわかれる書き方もしてると思います。
暇つぶし感覚で読んでみてください。


1.console.log(値1,値2)

console.log()にカンマ区切りで複数の値を渡すと、記述した順番通りにスペース区切りで値を出力してくれます。

const month = 6;
const date = 27;

console.log(month, date);
// 6 27

console.log(month, '/', date);
// 6 / 27

一応、MDNを読んでみました。
カンマ区切りで渡した値を出力する旨がきちんと書いてありました。

console.log(obj1 [, obj2, ..., objN]);
obj1 ... objN
出力する JavaScript オブジェクトのリスト。
各オブジェクトの文字列表現が記述順で出力されます。
MDN -console.log-

これを知るまでは複数の値を出力する際は 変数1 + " " + 変数2としたり、テンプレート文字${変数1} {変数2}列を使っていましたが、どう頑張ってもカンマで区切るのが一番楽です!

この書き方は超常識なのかもしれませんが、今まで読んだJavaScriptの学習教材には出てきませんでした。もっと早く知りたかった・・・。

2. 配列.map(Number)

配列.map(Number)とすると、配列内の要素をすべてNumberオブジェクトに変換できます。

['1','2','3'].map(Number)
// [ 1, 2, 3 ]

配列内の要素がNumberオブジェクトにできない値であれば、NaNが設定されます。

['1', 'a', '3'].map(Number)
// [ 1, NaN, 3 ]

mapといえばarr.map(x => x * 2);のように引数を受け取って処理したものを返す使い方しか知らなかったのですが、Numberを渡すだけで処理してくれるみたいです。

余談: .map(parseInt)について

.map(Number)と同じ要領でparseIntを使ってもNumberオブジェクトになりそうですが・・・そうはなりません。parseIntをそのまま渡すと思わぬ結果が返ってくるので注意してください。

["1", "2", "3"].map(parseInt);
// [1, NaN, NaN] 

parseIntは引数を2つとるため、このような結果になります。
以下のように、引数を1つだと明示すれば想定通りの結果が得られます。

['1', '2', '3'].map((str) => parseInt(str));
// [ 1, 2, 3 ]

詳しくはMDNにわかりやくす書いてあるのでそちらを参照してください。
MDN - Array.prototype.map() トリッキーな使用例-

3. 配列.map(Math.メソッド)

配列.map(Math.メソッド)とすると、配列内の要素すべてに渡したメソッドを実行してくれます。
例えば小数点以下を切り捨てて整数にしてくれるMath.floorを渡すと・・・

[1.1, '2.2', 3.3].map(Math.floor)
// [1, 2, 3];

きちんと計算した配列を生成します。
こちらはStringオブジェクトでも数字であればよしなにNumberとして扱ってくれます。

4.[,変数名] = 配列

分割代入で[,変数名] = 配列とすると、不必要な値を定義せずに無視することができます。
例えば[, str] = ['a', 'b']とすると、配列の0番目の要素('a')を無視し、配列1番目の要素('b')のみ左辺の変数(str)に格納されます。

const [, str] = ['a', 'b'];
console.log(str);
// b

これは何番目に何の値が入っているのかがわかりきっている場合によく使います。
例えば日時情報などです。
String(new Date()).split(' ') には日付や時間などの情報がたくさん入っています。

String(new Date()).split(' ')
// [ 'Fri', 'Jun', '26', '2020', '29:39:55', 'GMT+0900', '(GMT+09:00)' ]

ここから時間だけを取り出したい場合、const [, , , , time]に代入します。

const [, , , , time] = String(new Date()).split(' ');
console.log(time);
// 19:39:55

このように欲しい変数だけを定義すると、コードの見通しがよくなるのでオススメです。

詳しくはこちらを参照してください。
MDN -分割代入 返値の無視-

余談: 残余パターン(...arr)との併用について

この書き方は残余パターンと併用して使うことが結構ありました。
例えば[人数, 値1, 値2, 値3 ・・・]という入力から、値1以降を変数に格納する場合は以下のように書きます。

const [, ...arr] = ['人数', '値1', '値2', '値3'];
console.log(arr);
// [ '値1', '値2', '値3' ]

ちなみにこれはslice()を使っても同じ結果が得られますが・・・

const arr = ['人数', '値1', '値2', '値3'].slice(1);
console.log(arr);
// [ '値1', '値2', '値3' ]

slice()は文の末尾に出てくるので、そこまで読まないとどこで値を区切るのかがわかりません。
その点分割代入なら、変数定義の時点で「いらない要素があるのか」とすぐに察することができるので、個人的にはこっちの方が好きです。

まとめ

私が取り組んでいたクイズ問題の入力値は、数値なのにStringやスペース区切りの文字列として渡ってくることがほとんどでした・・・。
それを200問解いたら、文字列や配列を操作するお決まり文をスラスラ書けるようになりました。
また、最適な書き方を追求する過程で、この記事に書いたような注意点や良い記法があることを学べました。

知見はまだあるような気がするので、思い出したら追記しようと思います。
(前に全く同じ記事を投稿してましたが、そちらは間違えて消してしまいました。。ストックしていただいてた方、すみません。。)

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

色相(Hue)の回転はHSL色空間よりLCH色空間のほうが自然に見える

色相(Hue)を少しずつずらして別の色を作ることがたまにありますが、その際、HSL色空間よりLCH色空間でずらしたほうが人間の知覚にとって自然に見えます。

それを視覚的に確認するためにデモサイトを作りました。

デモ: https://fujiharuka.github.io/color-rotation/

HSL 色空間で色相をずらした場合

スクリーンショット 2020-07-04 15.48.07.png

左端の色が基準となる色で、HSL 色空間で色相を少しずつずらして色を生成しています。白を背景色とした場合、文字が読みにくい色があることがわかります。

LCH 空間で色相をずらした場合

スクリーンショット 2020-07-04 15.48.02.png

左端の色が基準となる色で、今度は LCH 色空間で色相を少しずつずらして色を生成しています。白の背景色に対して文字が読みにくい色はありません。LCH のほうが自然に色のバリエーションが作れています。なぜこうなるのかというと、LCH色空間は人間の知覚差異を考慮した空間だからです。

参考

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

Ruby on Rails 非同期通信(ajax)についての振り返り

カリキュラム学習・ポートフォリオ作成中、この機能を実装するのに苦戦したので、メモ。
非同期通信にしたかったのは以下の二点。

①特定の商品をお気に入り登録or削除する
お気に入りの作成。削除ボタン.gif

②お気に入り一覧から削除する
お気に入り一覧の削除ボタン.gif

前提条件

テーブル

schema.rb
  create_table "users", force: :cascade do |t|
()
  end
 create_table "products", force: :cascade do |t|
()
  end

  create_table "favorites", force: :cascade do |t|
    t.integer "user_id", null: false
    t.integer "product_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

モデル(アソシエーション)

favorite.rb
belongs_to :user
belongs_to :product

def favorited_by?(user)
  Favorite.where(user_id: user.id).exists?
end
user.rb
has_many :favorites, dependent: :destroy
product.rb
has_many :favorites, dependent: :destroy

def favorited_by?(user)
  Favorite.where(user_id: user.id).exists?
end

ルート

rutes.rb
resources :products do
 resource :favorites, only: [:create, :destroy]
end

実装①(商品のお気に入り追加・削除)

productsコントローラは以下の通り

products_controller.rb
def show
  @product = Product.find(params[:id])
end

次にお気に入りボタンをパーシャル化。(userディレクトリ内に作成)

views/users/products/_favorite_button.html.erb
<% if product.favorites.where(user_id: current_user.id).exists? %>
  <%= link_to "お気に入りから削除",  users_product_favorites_path(product_id: product.id), method: :delete, remote: true %>
<% else %>
  <%= link_to "お気に入りに追加", users_product_favorites_path(product_id: product.id), method: :post, remote: true %>
<% end %>

ここでremote: trueを付けることで、非同期通信が可能になる。

products/show.html.erbで呼び出し

views/users/products/show.html.erb
<div id="favorites_buttons_<%= @product.id %>">
  <%= render 'users/products/favorite_button', product: @product %>
</div>

次はfavoritesのコントローラ。

fovorites_controller.rb
  def create
    @product = Product.find(params[:product_id])
    favorite = current_user.favorites.new(product_id: @product.id)
    favorite.save
  end

  def destroy
    @product = Product.find(params[:product_id])
    @favorites = current_user.favorites
    favorite = current_user.favorites.find_by(product_id: @product.id)
    favorite.destroy
  end

ここでリダイレクト先を指定しなければ、JSの処理を探しに行ってくれる。
最後に作成・削除した場合のJS処理を作成する。

views/users/favorites/create.js.erb
$("#favorites_buttons_<%= @product.id %>").html("<%= j(render 'users/products/favorite_button', product: @product) %>");
views/users/favorites/destroy.js.erb
$("#favorites_buttons_<%= @product.id %>").html("<%= j(render 'users/products/favorite_button', product: @product) %>");

show.html.erb内の、idで設定した範囲のみが、処理後書き換えられるようになる。

実装②(お気に入り一覧から削除)

fovoritesのコントローラは以下の通り作成

favorites_controller.rb
def index
  @favorites = current_user.favorites
end

非同期通信したい範囲をパーシャル化。

view/users/favorites/_form.html.erb
<% if favorites.present? %>
  <table class="table">
    <thead>
      <tr>
        <th colspan="3">商品</th>
      </tr>
    </thead>
    <tbody>
      <% favorites.each do |f| %>
        <tr>
          <td>
            <%= attachment_image_tag f.product, :image, :fill, 80, 80, format: 'jpeg', fallback: "no_image.jpg", size: '80x80' %>
          </td>
          <td><%= link_to f.product.name, users_product_path(f.product.id) %></td>
          <td><%= link_to "お気に入りから削除",  users_product_favorites_path(product_id: f.product.id), method: :delete, remote: true %></td>
        </tr>
      <% end %>
    </tbody>
  </table>
<% else %>
  <h3>
    お気に入りリストがありません。<br>
    商品ページから追加してみましょう
  </h3>
<% end %>

fovorites/index.html.erbで呼び出し

<div class="row">
  <div class="col-sm-6 offset-3" id="favorites_index">
    <%= render 'users/favorites/form', favorites: @favorites %>
  </div>
</div>

JSの処理に以下の1行を追加

views/users/favorites/destroy.js.erb
$("#favorites_index").html("<%= j(render 'users/favorites/form', favorites: @favorites) %>");

これでお気に入り一覧にも非同期機能が実装できた。

実装した上での反省点・感想

  • JSに記載しているインスタンス変数はそれぞれcreate・destroyメソッドから確認しているため、そっちでも定義してあげないといけないということが身にしみた。(考えてみれば当たり前だったが)
  • 実装①についてはググればよく出てくるが、②は見かけないので自力で 解けて感動した。ただ流れを理解していれば迷うとこでも無かったなと感じた。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Identity Functor ってパイプライン演算子っぽい

ふと、「Identity Functorってパイプライン演算子っぽい」と思ったんですよ。何言ってんだって感じですが。とりあえず、JavaScript で下記のような関数を定義するとパイプラインっぽいことができるんじゃないかと思った次第です。

const wrap = x => ({
  unwrap: x,
  map: f => wrap(f(x)),
  tap: f => wrap((f(x), x))
});

tap と unwrap がある理由

Identity Functor にtapは不要です。Scala がpipe(上のmapに対応するメソッド)とともにtapも導入したことを参考に入れてみました。副作用が身近な言語ならtapは確かに便利ですね。

Scala では、pipe/tapを呼び出すと暗黙の変換が発動して元の値がChainingOpsに包まれます。また、pipe/tapの戻り値はChainingOpsに包まれていません。そのため明示的に包んだり、包装をほどいたりする必要はありません。それに比べると、上の JavaScript の実装は明示的なwrap/unwrapが必要になります。なぜなら、暗黙的な変換はされませんし、map/tapの戻り値が包まれた状態で返されるからです。

余談ですが、tapは Ruby が源流です。逆に言うと Ruby のthenはパイプラインっぽいかもしれませんね。

map をパイプラインとして使ってみる

次のコードの「通常の関数適用」と「map を使った関数適用」の処理結果はどちらも202になります。

map使用例.js
const addOne = x => x + 1;
const timesTow = x => 2 * x;

// 通常の関数適用
timesTow(addOne(100));

// map を使った関数適用
wrap(100).map(addOne).map(timesTow).unwrap;

mapを使うと関数適用の入れ子が解消され、処理の順にそって関数を左から右に書けています。パイプラインっぽいことは十分できていますね。

thisを考慮しなければ、fn(…(f1(f0(x))…)の形の関数適用の入れ子は一般に、wrap(x).map(f0).map(f1)….map(fn).unwrapへ書き換え可能です。

tap を使ってみる

上の map 使用例のコードにtapを挟み込むと、処理途中の結果をコンソールに表示できます。具体的に、処理結果は202で変わりませんが、コンソールには100, 101, 202と途中結果が表示されていきます。

tap使用例.js
const addOne = x => x + 1;
const timesTow = x => 2 * x;

// tap を使って処理の途中結果をコンソールに出力
wrap(100)        .tap(console.log)
  .map(addOne)   .tap(console.log)
  .map(timesTow) .tap(console.log)
  .unwrap;

一次変数を用意したり、引数をそのまま返す関数を用意すれば通常の関数適用でもtapと同じ事はできるかもしれません。ただ、tapの方がコードの追加は容易でしょうし、不要になった際の除去も簡単でしょう。

ちなみに、応用すると下記のようなおどろおどろしい FizzBuzz を書くこともできます。

toFizzBuzz.js
const toFizzBuzz = n => wrap(n)
  .map(num => ({num, str: ''}))
  .tap(x => {
    if(x.num % 3 === 0)
      x.str += 'Fizz';
  })
  .tap(x => {
    if(x.num % 5 === 0)
      x.str += 'Buzz';
  })
  .tap(x => {
    if(x.str === '')
      x.str += x.num.toString();
  })
  .map(x => x.str)
  .unwrap;

関数合成

使いどころは不明ですが、同じような考えで関数合成のみを扱うオブジェクトも生成できます。

const unary = (f = x => x) => ({
  run: f,
  mappend: g => unary(x => g(f(x))),
  tappend: g => unary(x => {
    const y = f(x);
    g(y);
    return y;
  })
});
mappend_tappend使用例.js
const addOne = x => x + 1;
const timesTow = x => 2 * x;
const compose = unary()
  .tappend(console.log)
  .mappend(addOne)
  .tappend(console.log)
  .mappend(timesTow)
  .tappend(console.log)
  .run;

compose(100);

型付け

TypeScript で型を意識するなら次のようになるでしょうか。wrapは素直に型が付きますね。unaryは、型が合わないためデフォルト引数を設定できません。別途、emptyUnaryを用意すれば、同じようにemptyUnary()を起点に関数を合成していく事ができます。

type Identity<A> = {
  unwrap: A,
  map: <B>(f: (x: A) => B) => Identity<B>,
  tap: <U>(f: (x: A) => U) => Identity<A>
}

const wrap = <A>(x: A): Identity<A> => ({
  unwrap: x,
  map: f => wrap(f(x)),
  tap: f => wrap((f(x), x))
});

type UnaryBuilder<A, B> = {
  run: (x: A) => B,
  mappend: <C>(g: (x: B) => C) => UnaryBuilder<A, C>,
  tappend: <U>(g: (x: B) => U) => UnaryBuilder<A, B>
}

const emptyUnary = <A>(): UnaryBuilder<A, A> => unary(x => x);

const unary = <A, B>(f: (x: A) => B): UnaryBuilder<A, B> => ({
  run: f,
  mappend: g => unary(x => g(f(x))),
  tappend: g => unary(x => {
    const y = f(x);
    g(y);
    return y;
  })
});

おわり

まあ、ごちゃごちゃ言ってないで、パイプライン演算子|>が使える環境なら、それを使えばいいですし。使えなくても次のような関数を定義すれば十分ですね。

const pipe = (x, ...fs) => fs.reduce((v, f) => f(v), x);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS / Web版Twitterの指定したページへのリンクをホーム画面に追加する

TL;DR

Web版Twitterをホーム画面に追加する
-> PWAになり追加時のURLが反映されない

別のページを経由して開けば解決
-> dataスキーム

data URI scheme

  • dataスキーム
  • 外部リソースと同じようにデータを読み込む方法
  • アドレスバーに入力する

iOSの「ホーム画面に追加」は後からURLを書き換えられない
-> 追加時用のTwitterを開かないパターンが必要
-> 端末がネットに繋がっているかで条件分岐(他にも方法はあると思う)

data:text/html,
<script>
if (navigator.onLine)
  location.href='開くURL'
</script>

実際の手順

  1. 上記のdataスキームの「開くURL」部分を変更
  2. オフラインの状態でブラウザのアドレスバーに入力
  3. ホーム画面に追加

問題点

ホーム画面に表示されるアイコンが真っ白なので複数追加すると分かりにくい

↓↓↓

ページに背景色を設定する

<style>
  html {background: 色}
<style>

アイコンの画像を設定する

<link rel="apple-touch-icon-precomposed" href="画像のURL">

以上

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

初心者なりにスタックとキューの実用例、検索アルゴリズム (深さ検索優先アルゴリズム 、幅検索優先アルゴリズム)についてまとめてみた

目的

  • スタックとキューを前提にデータを取り扱えるようになる。

結論

  • スタック、キューとは、データの取扱い処理方法のひとつである。
  • データの集合を系統立てて管理、検索するために前提となる考え方。

つまり、アプリケーションなどの処理順序、処理効率と、検索のアルゴリズムに利用

※調査時、C言語で説明するサイトがとても多かったのでC言語の本が出てきます。
でも、C、わかりません。世界にこんにちはしたこともないです。
コンピューターサイエンスの基礎の勉強として役に立つ感触があるので概念だけでもそのうちやろうと思います。

背景

クロージャーや再帰などの処理をするにあたって、
スタック、キューという言葉を比較的頻繁に見かけるので
そろそろ理解しておかねばいけないと感じた。

スタック、キューとは

端的で、とてもわかりやすい説明を見つけました。

スタックは、例えば超忙しいときに新しい課題がぶっこまれたときとかにとりあえずそれを先に片付けるような感じ
キューは、人気ラーメン屋に並ぶ人々の待ち行列のように先に並んだ人が先にお店に入る感じ

参考)https://qiita.com/drken/items/6a95b57d2e374a3d3292

上記のままでも十二分にわかりやすいが同世代的には
スタック = お菓子のペッツ
キュー = ロケット鉛筆のイメージをもった。

実用例

世の中における様々な問題は、探索によって、考えられる場合を調べ尽くすことによって原理的には解決できるものが多いです。例えば、現在地から目的地まで最速でたどり着く方法を求める問題は、原理的には、現在地から目的地へ到達する経路をすべて列挙することで解決できます1。将棋やオセロの必勝法を求める問題は、原理的には、考えられる局面と局面遷移をすべて調べ上げることで求めることができます

参考)https://qiita.com/drken/items/4a7869c5e304883f539b

データ構造が違うことでどんなメリットが享受されるのか?
使い分け方は?どんな人に関係があるの?と思いましたw

そこで、スタック、キューがあることで何が実現できるか、調査しました。

  • スタックで実現できること

    • さまざまなソフトウェアの[元に戻す]ボタンに使用される。変更がスタックにプッシュされる。ブラウザーの戻るボタンでは、最近アクセスしたすべてのWebページがスタックにプッシュされるスタックの助けを借りて機能する。
    • 深さ検索:とにかく行けるとこまで行ってそれ以上進めなくなったら1歩戻ってまた探索する(親子検索)
  • キューで実現できること

    • プリンターの印刷順
    • 画像のアップロード順
    • 幅検索:出発点に近い点から順に探索する(兄弟検索)

配列では理解がしやすいですが、
グラフなど多元的な構造をもつデータの場合理解に苦しみました。

そこで、本から問題を読んでみて理解することにしました。

練習問題

例題:スタックというデータ構造がある。スタックは配列のようにデータを蓄積するが、積み上げ(push)と、一番最後にpushされたデータの積み下ろし(pop)の2種類しか操作が存在しない。Stackクラスを実装せよ。また、Stackクラスを実際に使用してみよ。

回答

class Stack {
  constructor() {
    this._data = [];
  }

  push(data) {
    this._data.push(data);
  }

  pop() {
    return this._data.pop();
  }
}

const stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);

console.log(stack.pop());
console.log(stack.pop());
console.log(stack.pop());

例題:ソート済みの配列があるとする。このとき、配列の中からある値を探しだす方法として、二分探索(バイナリサーチ)という手法が使える。

1 : 配列の真ん中の値を見て、同じならtrueを返し終了する。真ん中より小さければ左半分を二分探索し、真ん中より大きければ右半分を二分探索する。
2 : 最後まで見つからなければfalseを返すこれを実装し、動作を確かめよ。

回答

function binarySearch(array, target, start, end) {
  // startとendが逆転したら終わる
  if(start > end) {
    return false;
  }

  // 真ん中のインデックスを計算する
  const center = Math.floor((start + end) / 2);

  // 半分ずつ探す
  if(array[center] > target) {
    return binarySearch(array, target, start, center - 1);
  } else if(array[center] < target) {
    return binarySearch(array, target, center + 1, end);
  } else {
    return true;
  }
}
const sortedArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const arrayIncludes2 = binarySearch(sortedArray, 2, 0, sortedArray.length - 1);
console.log(arrayIncludes2);

その他参考資料(special Thanks!)

https://www.amazon.co.jp/プログラミングコンテスト攻略のためのアルゴリズムとデータ構造-渡部-有隆/dp/4839952957

https://sbfl.net/blog/2018/10/14/javascript-programming-5/

https://ja.wikipedia.org/wiki/深さ優先探索
https://www.iwass.co.jp/column/column-01.html

https://ja.wikipedia.org/wiki/幅優先探索

遅延評価学習 候補

グラフ理論
ソート関係(バブルソートはOK)

遅延評価学習について

本の最初のページから読み始めることは、ひとつのよい勉強法だ。
ただ、そのような先行評価的な勉強は、初学者か、感情的な不安解消のためのものだ。
何かあったときの先行投資、だというが、何かあったときはまずその学習だけじゃ足りないこと。
そして本を最初からよんで網羅的に学んでも忘れている。

それよりは、緊急度や知識欲が高い問題から本気で解く中で、周辺知識としてつけた方が、結果的に良い気がする。
まずは極め横展開する。深さ優先検索的学習法

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

【Rails】jquery を使わずに javascript で flashメッセージをフェードアウトさせる

はじめに

Railsアプリを開発していてflashメッセージをフェードアウトさせたく、javascriptを用いて実装したので書く。

こういったやつ↓↓(Bootstrapのalertを適用させています)
スクリーンショット 2020-07-04 8.59.20.png

これをフェードアウトさせる

環境

Ruby: 2.5.1
Rails: 5.2.4.2

実装

実装イメージとしては徐々に透明度を高くしていき、最終的に非表示にします。

要素のstyle属性
①opacityの値を減少させていく
②opacityが0になったらdisplay: none; にする

viewファイル

erbはこのようになっている

_flash_messages.html.erb
<% flash.each do |message_type, message| %>
  <div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>

jsファイル

flash_message.js
// turbolinks:loadでページ読み込み時に実行
document.addEventListener('turbolinks:load', () => {

  // flashメッセージ要素を取得
  const flashMessage = document.querySelector('.alert');

  // フェードアウトさせる(徐々に透過し,非表示にする)関数を定義
  const fadeOutFlashMessage = () => {
    // setIntervalを特定するために返り値を変数timer_idに格納
    const timer_id = setInterval(() => {
      // flashメッセージのstyle属性 opacityを取得
      const opacity = flashMessage.style.opacity;

      if (opacity > 0) {
      // opacityの値が0より大きければ0.02ずつ値を減少させる
        flashMessage.style.opacity = opacity - 0.02;
      } else {
        // opacityの値が0になったら非表示に
        flashMessage.style.display = 'none';
        // setIntervalをストップさせる
        clearInterval(timer_id);
      };
    }, 50); // 今回は0.05秒ごとにsetIntervalを実行
  }

  // flashメッセージがある場合のみ実行
  if (flashMessage !== null) {
    // style属性opacityをセット
    flashMessage.style.opacity = 1;
    // 今回は表示から3秒後に上記で定義したフェードアウトさせる関数を実行
    setTimeout(fadeOutFlashMessage, 3000);
  };
});

参考文献

下記記事を参考にさせていただきました。
ありがとうございました。

turbolinksチートシート
【JavaScript】Railsのflashをフェードアウトして消す方法

最後に

読んでいただきありがとうございます。
間違いや、より効率的な記述方法等あればご指摘いただけると非常に嬉しいです。

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

npm(パッケージ管理ツール)の使い方

npm(Node Package Manager)とは、JavaScriptで記述されたパッケージ管理システムです。

無料で使えて、世界中のオープンソースソフトウェア(Node.jsのツール、モジュール)を開発に使用することができます。

使い方

1.初期化

Macのターミナルを使った手順を解説します。
まずmkdirで新規ディレクトリを作成し、cdで作成したディレクトリに移動します。

$ mkdir npm_test
$ cd npm_test

npm initで初期化を行います。

$ npm init

package.jsonファイル作成のユーティリティがスタートするので、
pacakge name:にプロジェクト名を入力します。
デフォルトはディレクトリ名になっているので、そのままでよければエンターを押します。

This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (npm_test) 

バージョンが表示されるので、問題なければエンターを押します。

version: (1.0.0) 

プロジェクトのdescription(解説)を入力して、エンターを押します。

description: This is test.

エントリーポイントとは、アプリケーションで一番最初に呼び出される部分のことです。今回の場合は、実際に最初に実行されるJavaScriptのファイルを指します。デフォルトで問題なければ、エンターを押します。

entry point: (index.js) 

テストコマンドを入力することもできますが、未入力でもOKです。エンターを押します。

test command: 

Gitのレポジトリを入力できますが、テストなので未入力でエンターを押します。

git repository: 

npm内で検索する場合のキーワードを入力できます。未入力でエンターを押します。

keywords: 

著者を入力します。任意の名前を入力してエンターを押します。

author: tetsu

ライセンスの設定ができます。デフォルトでよければエンターを押します。

license: (ISC) 

最後に、上記で設定した情報が一覧で表示されます。問題なければyesと入力して実行します。

About to write to /Users/tetsu/Documents/npm_test/package.json:

{
  "name": "npm_test",
  "version": "1.0.0",
  "description": "This is test.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "tetsu",
  "license": "ISC"
}


Is this OK? (yes) yes

lsnpm_testディレクトリの中を確認すると、pacage.jsonファイルが作成されていることがわかります。

iMac:npm_test tetsu$ ls
package.json

catで中身を表示すると、先ほど設定した情報が記述されていることが確認できます。

iMac:npm_test tetsu$ cat package.json
{
  "name": "npm_test",
  "version": "1.0.0",
  "description": "This is test.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "tetsu",
  "license": "ISC"
}

2.インストール

パッケージのインストールには、2種類の選択肢があります。

  • PC自体にインストール → どのプロジェクトからも参照可能(pathの設定が必要)
  • このプロジェクトにインストール → このプロジェクトのみ利用可能(このディレクトリにダウンロードされる)

サンプルとして、chalkをダウンロードしてみます。
chalkは、console.logに色付けできるパッケージです。

$ npm install chalk

少し待つと、実行結果が表示され、インストールの完了を確認できます。

npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN npm_test@1.0.0 No repository field.

+ chalk@4.1.0
added 7 packages from 4 contributors and audited 7 packages in 4.911s

2 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

lsnpm_testディレクトリの中を確認すると、
node_modulesディレクトリと、package-lock.jsonファイルが新たに作成されていることがわかります。

iMac:npm_test tetsu$ ls
node_modules        package-lock.json   package.json

catで先ほどのpackage.jsonを確認すると、dependenciesにインストールしたパッケージが追加されていることがわかります。

package.jsonを読めば「このプロジェクトには、どんなパッケージが使われているか?」がひと目でわかるようになっています。

iMac:npm_test tetsu$ cat package.json
{
  "name": "npm_test",
  "version": "1.0.0",
  "description": "This is test.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "tetsu",
  "license": "ISC",
  "dependencies": {
    "chalk": "^4.1.0"
  }
}

node_modulesの中を見ると、「chalk本体のファイル」と、「chalkと依存関係にあるファイル」がセットになってインストールされています。

iMac:node_modules tetsu$ ls
@types      chalk       color-name  supports-color
ansi-styles color-convert   has-flag

npmの便利なところは、このように「使いたいパッケージをセットとしてまとめてプロジェクトにインストールできる利便性」です。

3.実際に使ってみる

index.jsを作成して、実際に実行してみます。
プロジェクトのディレクトリ直下にファイルを作成します。

iMac:npm_test tetsu$ nano index.js

nanoでファイルを作成して、下記のコードを記述します。ctr + xyで保存して終了します。
※エディターを使ってもOKです。

index.js
const chalk = require('chalk');
console.log(chalk.blue('Hello world!'));

この段階で、一度実行してみます。npm run startを入力してみると、エラーになってしまいます。

iMac:npm_test tetsu$ npm run start
npm ERR! missing script: start

npm ERR! A complete log of this run can be found in:

パッケージを実行するには、package.jsonに実行するためのスクリプトを記述する必要があります。

script内に"start": "node index.js"を追記します。

package.json
{
  "name": "npm_test",
  "version": "1.0.0",
  "description": "This is test.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1", // 「,」を入れて、
    "start": "node index.js" // この行を追記
  },
  "author": "tetsu",
  "license": "ISC",
  "dependencies": {
    "chalk": "^4.1.0"
  }
}

node index.jsは、npm runで実行されるファイルを指しています。"start:"には任意のコマンドを指定できます。

この状態で、もう一度npm run startで実行してみます。

iMac:npm_test tetsu$ npm run start

> npm_test@1.0.0 start /Users/tetsu/Documents/npm_test
> node index.js

Hello world!

下記の画像のようにコンソールに色が付いたらchalkが実行できています。
Screen Shot 0032-07-04 at 6.46.00.png

chalkで遊んでみたい方は、下記のリンクを参照してください。

参考

npm
npm/chalk
progate/npmパッケージを使ってみよう!

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

新Edgeの拡張機能を自作する (コンテキストメニュー編)

Microsoftのブラウザ、新Edgeの拡張機能を自作します。
コンテキストメニューからWikipediaやTwitterを検索する拡張機能を作成します。

名称未定-1.png

manifest.jsonの作成

まずは任意の開発フォルダを作成します。
そしたら中に次のようなmanifest.jsonを作成します。

良く分からないと思うので、コピペでOK。

manifest.json
{
  "name": "Sample Extension",
  "description": "Extension Sample",
  "version": "1.0",
  "background": {
    "scripts": ["script.js"],
    "persistent": false
  },
  "permissions": ["tabs", "contextMenus"],
  "manifest_version": 2
}

"name"と"description"と"vertion"は変更可

本体スクリプトの作成

開発フォルダに、次のようなscript.jsを作成します。本体となるスクリプトです。
メニューを登録するコードと、メニューがクリックされた時のコードを記述します。

script.js
//メニューを登録するコード
chrome.runtime.onInstalled.addListener(function(){
    chrome.contextMenus.create({
        id: "root",
        title: "拡張メニュー",
        contexts: ["all"],
        type: "normal",
    })

    chrome.contextMenus.create({
        parentId: "root",
        id: "wikipedia",
        title: "Wikipediaで開く",
        contexts: ["all"],
        type: "normal",
    })

    chrome.contextMenus.create({
        parentId: "root",
        id: "twitter",
        title: "Twitterで検索する",
        contexts: ["all"],
        type: "normal",
    })
})


//メニューがクリックされた時のコード
chrome.contextMenus.onClicked.addListener(function(event) {
    if(event.menuItemId === "wikipedia"){
        const url = "https://ja.wikipedia.org/wiki/" + event.selectionText
        chrome.tabs.create({url})
    }
    else if(event.menuItemId === "twitter"){
        const url = "https://twitter.com/search?f=realtime&src=typd&q=" + event.selectionText + "%20lang%3Aja"
        chrome.tabs.create({url})
    }
})

「登録コード」はツリー形式にするために、"parentId"で親のidを指定しているのがポイント。
「クリックされた時のコード」はメニューのidで分岐させるのがポイント。

拡張機能を登録する

ブラウザのメニューから拡張機能を開いたら

  1. 「開発者モード」をオンにする
  2. 「展開して読み込み」から開発フォルダを指定する

これでインストール完了です。

拡張機能を更新する

本体コードを変更した時は、更新作業を行う必要があります。

拡張機能のページにインストールした自作のプログラムが表示されます。
その中にある「再読み込み」から拡張機能を更新することができます。

名称未定-2.png

※マニュフェストを変更した時や「再読み込み」が表示されない場合は、登録からやり直してください。

課題

  • ブラウザを起動させた時に警告ダイアログが表示される
  • ブラウザに表示される拡張機能のアイコンを非表示にしたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

新Edgeの拡張機能の作り方 (コンテキストメニュー編)

Microsoftのブラウザ、新Edgeの拡張機能を自作します。
コンテキストメニューからWikipediaやTwitterを検索する拡張機能を作成します。

名称未定-1.png

マニフェストの作成

まずは任意の開発フォルダを作成します。
そしたら開発フォルダに次のようなmanifest.jsonを作成します。

良く分からないと思うので、コピペでOK。

manifest.json
{
  "name": "Sample Extension",
  "description": "Extension Sample",
  "version": "1.0",
  "background": {
    "scripts": ["script.js"],
    "persistent": false
  },
  "permissions": ["contextMenus", "tabs"],
  "manifest_version": 2
}

"name"と"description"と"vertion"は変更可

本体の作成

開発フォルダに、次のようなscript.jsを作成します。本体となるスクリプトです。
メニューを登録するコードと、メニューがクリックされた時のコードを記述します。

script.js
//メニューを登録するコード
chrome.runtime.onInstalled.addListener(function(){
    chrome.contextMenus.create({
        id: "root",
        title: "拡張メニュー",
        contexts: ["all"],
        type: "normal",
    })

    chrome.contextMenus.create({
        parentId: "root",
        id: "wikipedia",
        title: "Wikipediaで開く",
        contexts: ["all"],
        type: "normal",
    })

    chrome.contextMenus.create({
        parentId: "root",
        id: "twitter",
        title: "Twitterで検索する",
        contexts: ["all"],
        type: "normal",
    })
})


//メニューがクリックされた時のコード
chrome.contextMenus.onClicked.addListener(function(event) {
    if(event.menuItemId === "wikipedia"){
        const url = "https://ja.wikipedia.org/wiki/" + event.selectionText
        chrome.tabs.create({url})
    }
    else if(event.menuItemId === "twitter"){
        const url = "https://twitter.com/search?f=realtime&src=typd&q=" + event.selectionText + "%20lang%3Aja"
        chrome.tabs.create({url})
    }
})

「登録コード」はツリー形式にするために、"parentId"で親のidを指定します。
「クリックされた時のコード」はメニューのidで分岐させます。

拡張機能を登録する

作成した拡張機能を登録するには

  1. 「拡張機能」を開く (ブラウザのメニューにある)
  2. 「開発者モード」をオンにする
  3. 「展開して読み込み」から開発フォルダを指定する

これでインストール完了です。

拡張機能を更新する

本体コードを変更した時は、更新作業を行う必要があります。

拡張機能のページにインストールした自作のプログラムが表示されます。
その中にある「再読み込み」で更新できます。

名称未定-2.png

※マニュフェストを変更した時や「再読み込み」が表示されない場合は、登録からやり直してください。

課題

  • ブラウザを起動した時に警告ダイアログが表示される
  • ブラウザに表示される拡張機能のアイコンを非表示にしたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt/Rails】axiosとdevise_token_authを使ってPOSTした実装

Nuxt.jsとRuby on Railsでaxiosとdevise_token_authを利用してPOSTする時に、地味に詰まってしまったので備忘録がてらまとめます。

Ruby on Rails側の実装

devise_token_authの設定諸々は省きます。

以下のURLあたりが参考になりましたので、そちらをご覧いただけると良いかもしれません。

https://github.com/lynndylanhurley/devise_token_auth
https://qiita.com/Masahiro_T/items/6bc49a625b437a7c2f45
https://sainu.hatenablog.jp/entry/2018/08/11/194319

FormObjectを導入してるので、そちらも込みで載せます。

articles_controller.rb
class ArticlesController < ApplicationController
  def create
    @article = ArticleForm.new(article_params)
    if @article.save
      # 省略
    end
  end

  private

  def article_params
    json_request = ActionController::Parameters.new(JSON.parse(request.body.read))
    json_request.permit(
      :title,
      :description,
    ).merge(user_id: current_user.id)
  end
end
articles_form.rb
class ArticleForm
  include ActiveModel::Model
  attr_reader :title, :description

  validates :title, presence: true, length: { maximum: 50 }
  validates :description, length: { maximum: 300 }

  def initialize(article_params)
    @article = article_params
  end

  def save
    return false if valid?
    Article.create(@article)

    true
  end
end

これで、POSTするまでの準備が整いました。

補足

POSTされる時にJsonをごにょごにょすることになったので、JsonをParseする処理を書いてあげる必要がありました。(ここに凄くハマりました。)

def article_params
  json_request = ActionController::Parameters.new(JSON.parse(request.body.read))
  json_request.permit(
    :title,
    :description,
  ).merge(user_id: current_user.id)
end

以下の記事に救われた(というかJsonのParse部分はコピペ)ので載せておきます。

http://tabunmuri.hatenablog.com/entry/2015/07/02/rails%E3%81%A7json%E3%81%AB%E3%80%81permit%E3%81%A7%E8%A6%81%E7%B4%A0%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%81%97%E3%81%9F%E3%81%84%E6%99%82%E3%81%AE%E6%96%B9%E6%B3%95

Nuxt.js側の実装

Componentに書いた実装は省いて、Vuexの中でやってるaxiosでPOSTした部分の実装だけ切り取ります。

article.js
export const actions = {
  async postArticle({ dispatch }, article) {
    await this.$axios
      .post('/articles', article, {
        headers: {
          'Content-Type': 'multipart/form-data', // サムネ画像を送信する想定のため
          'access-token': this.state.user.userToken.accessToken,
          client: this.state.user.userToken.client,
          uid: this.state.user.userToken.uid,
        },
      })
      .then(() => dispatch('getArticleList'))
      .catch((error) => console.log(error))
  },
}

devise_token_authのtokenをCookieに詰めて、nuxtServerInit時にVuexに書き出す実装をしていて、リクエスト時にヘッダーにTokenを突っ込めるようにしました。

ちなみに、Ruby on Railsではありませんが、GOのechoを利用した時に似たような実装をしていて、その備忘録を以下にまとめています。

https://qiita.com/arthur_foreign/items/fadd784610d764419786

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