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

Fullcalendarでイベント作成するも、なぜか月表示した時に、9時以降にならないとその日に表示されない問題

カレンダー界のパケットモンスター、きゃりーぱみゅぱみゅこと、FullCalendarには、イベント作成機能がある。
しかし、こいつはなぜか、9時を一日の終わりとみなしているらしく、20日8時30分とかにイベント作成しても、19日に表示されて困った。

これを解決する方法は、optionsにnextDayThreshold: '00:00:00'を明記することだったが、公式ドキュメントには、defaultが'00:00:00'であると書いてある
ところで、ご存知の通り、日本時間9時は世界標準時で0時なのである。
だから当然タイムゾーン設定が悪いのだと思う人がいるだろうが、timezoneを東京にしても結果は同じなのである。
この問題、わかる人はコメントをください。

参考サイト

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

iCheckを使用したチェックボックスでチェックの数を制限する方法

iCheck.jsを使用したチェックボックスでチェックの数を制限したいと思ったのですが、
iCheckだと普通のclickやchangeイベントなどは使えないんですよね。

そこでiCheckの場合のチェックボックスのチェックの数を制限する方法をご紹介します。

前提として、jQueryとiCheck.jsは既に読み込んでいるものとします。

index.html
<div class="limit-checkbox-group">
  <label><input name="" value="その1" type="checkbox" class="flat">その1</label><br>
  <label><input name="" value="その2" type="checkbox" class="flat">その2</label><br>
  <label><input name="" value="その3" type="checkbox" class="flat">その3</label><br>
  <label><input name="" value="その4" type="checkbox" class="flat">その4</label><br>
  <label><input name="" value="その5" type="checkbox" class="flat">その5</label><br>
</div>
script.js
// ここはiCheckのカラー設定
$(function () {
    $('.limit-checkbox-group input[type="checkbox"].flat').iCheck({
      checkboxClass: 'icheckbox_flat-red',
      radioClass: 'iradio_flat-red'
    });
});

// ここのコードに注目(チェックは3つ制限)
$(".limit-checkbox-group").on("ifToggled", function() {
    checkboxes = $(this).find("input:checkbox");  
    if (checkboxes.filter(":checked").length >= 3) { 
        checkboxes.not(":checked").iCheck('disable'); 
    } else { 
        checkboxes.not(":checked").iCheck('enable');
    } 
});

解説

onchangeイベントではチェック状態変更の検知ができないので、ifToggledというイベントを使用します。
これでlimit-checkbox-groupクラスの範囲内で入力のチェック状態が変更されたのを検知できます。

次は$(this).find("input:checkbox")でチェックボックスを取得してcheckboxesに格納しています。
このcheckboxesfilterメソッドとlengthプロパティ使い、チェックされたチェックボックスの数を検出してif文で比較します。

チェックが既に3つであればiCheckのチェックボックスをdisableにして無効化に、
3つ未満であればiCheckのチェックボックスをenableにして有効化にしています。

参考にさせていただいたURL

iCheck.jsを使ってる時のchangeイベント
https://stackoverflow.com/questions/18906657/limit-number-of-checked-checkboxes-using-icheck

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

ES2015以降をお勉強するのに、svelte REPLが楽しかったので紹介しておく

Svelte?

HTML+JavascriptをJavascriptにコンパイルしてくれるsvelte。ナンノコッチャという人は、意識高めで解説してくださっているReactとVueを改善したSvelteというライブラリーについてを斜めすると良いかと。

要するに、素のjavascriptへの回帰を可能とするのがSvelte(日本語読みするならば、「滑る手」?)というわけ。実行速度的には素のjavascriptがvueやreactよりも速い。が、普通に素のjsを書くとある程度の規模で破綻する。私の理解としては、Svelteによる『簡潔なjs->素のjs』のコンパイルにより、普通のエンジニアでもそこそこの規模感までは破綻せずに、素のjavascriptで行けるようにすることがメリット。

これにより、Svelte経由で生成された素のjavascriptは、軽量なことで有名なelmやvueよりなども、よりファイルサイズが小さく高速に動作する、ものらしい。

出典 Medium.comクローンフロントエンドフレーム実装をベンチマークでランク付けする

こちらに紹介されているように、シンプルなJSのフロントエンド フレームワークとして、フロントエンド開発者の支持を集めつつあるらしい。

...うーむ、投稿してから思ったが、svelte(滑る手?)とstelve(捨てるべ?)の綴りが相当ごっちゃになってるme(修正済?)。。。

フロントエンド素人にとってのsvelte REPL

さて、このsvelteにはインタラクティブ環境(REPL)が用意されている。
https://svelte.dev/repl

ふだんJSをいじくることがない人間として、軽く試してみたのだが、svelte REPL上でjavascriptを書くのがけっこう楽しくて、かつ今風のES20xxを学んでおくのによさそうだったので以下に紹介しておく。
※ モダンなjavascriptに触れる機会がない人は、以下を読む前に、es2015あたりでググっておくと良いかと。

良い点1 インタラクティブに実行可。

web言語であるjavascriptをコンソール上で動かしてもあまり楽しくない。他方で、ブラウザ上でインタラクティブに実行するためにHTML+JS+CSSをきちんと書くのは、普段JSに馴染んでいない人間にとっては結構大変。特にecmascript自体を学ぶ際には、お気楽さがなくなってしまう。

対して、svelte REPLでは、雰囲気を掴みながら、以下のように書くと、インタラクティブに実行することができる。コードは、githubアカウントでsvelteにログインしておくと、保存することができる。

<script>
  let name = 'カウントは'
  let count = 0
  let diff = 1

  const increase_diff =
                () => diff++
  const increase =
                () => count =count +diff
  const increase2 = () => {
            count = count* count;
            console.log("べき乗だぞ"+count)

            if (count > 10000000){
                    name =name + "な、なんと"
            }
    }
</script>

<button on:click={increase_diff}>差分増やす:{diff}</button>
<button on:click={increase}>差分を足す: {count}</button>
<button on:click={increase2}>べき乗{count}</button>

<h1>{name} {count}!</h1>

実行結果例:
なんとも.PNG

良い点2 es20xxにけっこう追従してくれている。

↑に書いた、es2015はもちろん、その後のecmascriptのアップデートにもSvelteは追従してくれている。

例① es2018

<script>
let { a, b, ...c } = 
        { a: 1, b: 2, x: 3, y: 4, z: 5}
const output = () => {
        console.log(a)  // 1
        console.log(b)  // 2
        console.log(c)  // {x: 3, y: 4, z: 5}       
        a ++
}

</script>

<button on:click={output}>コンソール出力</button>

2018.PNG

例② es2019

<script>
let array = [[1, 2], 3,"es2019"].flat()
let 説明 = Symbol("日本語").description;
</script>


<h1>{array}!</h1>
<h2>{説明}</h2>

2019.PNG

今年6月に正式版となるらしい、es2020のコードは現時点では動かなかったが、まぁ、es20xxの入門編にはそこそこ十分なのではないかと。

プロダクションで採用うんぬん

ES20xxの入門者が、プロダクションで採用うんぬんを語るは微妙な気もするが、昨年に注目を集めて以来、sveleのエコシステムは現在進行形で発展中のようなので、数年後にはsvelteもしくはsvelte的なアプローチはフロントエンド開発の選択肢の一つになっていくのではないかと期待。なにしろ、svelteでコンパイルされた後は,モダンなブラウザ上で動く素のjavascriptなのだから。
少なくとも、数年後のweb assemblyのためにモダンなjsをキャッチアップしておこうと思っている私には、svelteはアリな選択肢。

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

ES2015以降をお勉強するのに、Svelte REPLが楽しかったので紹介しておく

Svelte?

HTML+JavascriptをJavascriptにコンパイルしてくれるsvelte。ナンノコッチャという人は、意識高めで解説してくださっているReactとVueを改善したSvelteというライブラリーについてを斜めすると良いかと。

要するに、素のjavascriptへの回帰を可能とするのがSvelte(日本語読みするならば、「滑る手」?)というわけ。実行速度的には素のjavascriptがvueやreactよりも速い。が、普通に素のjsを書くとある程度の規模で破綻する。私の理解としては、Svelteによる『簡潔なjs->素のjs』のコンパイルにより、普通のエンジニアでもそこそこの規模感までは破綻せずに、素のjavascriptで行けるようにすることがメリット。

これにより、Svelte経由で生成された素のjavascriptは、軽量なことで有名なelmやvueよりなども、よりファイルサイズが小さく高速に動作する、ものらしい。

出典 Medium.comクローンフロントエンドフレーム実装をベンチマークでランク付けする

こちらに紹介されているように、シンプルなJSのフロントエンド フレームワークとして、フロントエンド開発者の支持を集めつつあるらしい。

...うーむ、投稿してから思ったが、svelte(滑る手?)とstelve(捨てるべ?)の綴りが相当ごっちゃになってるme(修正済?)。。。

フロントエンド素人にとってのsvelte REPL

さて、このsvelteにはインタラクティブ環境(REPL)が用意されている。
https://svelte.dev/repl

ふだんJSをいじくることがない人間として、軽く試してみたのだが、svelte REPL上でjavascriptを書くのがけっこう楽しくて、かつ今風のES20xxを学んでおくのによさそうだったので以下に紹介しておく。
※ モダンなjavascriptに触れる機会がない人は、以下を読む前に、es2015あたりでググっておくと良いかと。

良い点1 インタラクティブに実行可。

web言語であるjavascriptをコンソール上で動かしてもあまり楽しくない。他方で、ブラウザ上でインタラクティブに実行するためにHTML+JS+CSSをきちんと書くのは、普段JSに馴染んでいない人間にとっては結構大変。特にecmascript自体を学ぶ際には、お気楽さがなくなってしまう。

対して、svelte REPLでは、雰囲気を掴みながら、以下のように書くと、インタラクティブに実行することができる。コードは、githubアカウントでsvelteにログインしておくと、保存することができる。

<script>
  let name = 'カウントは'
  let count = 0
  let diff = 1

  const increase_diff =
                () => diff++
  const increase =
                () => count =count +diff
  const increase2 = () => {
            count = count* count;
            console.log("べき乗だぞ"+count)

            if (count > 10000000){
                    name =name + "な、なんと"
            }
    }
</script>

<button on:click={increase_diff}>差分増やす:{diff}</button>
<button on:click={increase}>差分を足す: {count}</button>
<button on:click={increase2}>べき乗{count}</button>

<h1>{name} {count}!</h1>

実行結果例:
なんとも.PNG

良い点2 es20xxにけっこう追従してくれている。

↑に書いた、es2015はもちろん、その後のecmascriptのアップデートにもSvelteは追従してくれている。

例① es2018

<script>
let { a, b, ...c } = 
        { a: 1, b: 2, x: 3, y: 4, z: 5}
const output = () => {
        console.log(a)  // 1
        console.log(b)  // 2
        console.log(c)  // {x: 3, y: 4, z: 5}       
        a ++
}

</script>

<button on:click={output}>コンソール出力</button>

2018.PNG

例② es2019

<script>
let array = [[1, 2], 3,"es2019"].flat()
let 説明 = Symbol("日本語").description;
</script>


<h1>{array}!</h1>
<h2>{説明}</h2>

2019.PNG

今年6月に正式版となるらしい、es2020のコードは現時点では動かなかったが、まぁ、es20xxの入門編にはそこそこ十分なのではないかと。

プロダクションで採用うんぬん

ES20xxの入門者が、プロダクションで採用うんぬんを語るは微妙な気もするが、昨年に注目を集めて以来、sveleのエコシステムは現在進行形で発展中のようなので、数年後にはsvelteもしくはsvelte的なアプローチはフロントエンド開発の選択肢の一つになっていくのではないかと期待。なにしろ、svelteでコンパイルされた後は,モダンなブラウザ上で動く素のjavascriptなのだから。
少なくとも、数年後のweb assemblyのためにモダンなjsをキャッチアップしておこうと思っている私には、svelteはアリな選択肢。

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

エンジニア向けオープンプロジェクトトレース

https://www.reiwarss.com/OpenProject

Top tags
python
swift
javascript
go
C
C++
C#
Ruby
TypeScript
PHP

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

Chrome Ver.80系でJavaScriptを無効化する方法

はじめに

JavaScriptのテスト時にChromeのJavaScriptを無効化したかったが、前のChromeのバージョンでの紹介記事は多かったがChrome Ver.80系での記事が少なかったため、Chrome Ver.80系でJavaScriptを無効化する方法をご紹介します。

JavaScriptを無効化する方法

  1. Chromeの右上にあるメニューボタンを押す。
  2. メニューバー中の 設定 を押下する。
  3. 検索窓で JavaScript と入力する。 スクリーンショット 2020-03-17 22.05.44.png
  4. サイトの設定 を押下する。
  5. JavaScript を押下する。
  6. ステータスバーを 許可(推奨) を ブロック中 に変更する。 スクリーンショット 2020-03-17 22.10.58.png

これでChrome上でJavaScriptが無効化されます。

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

JavascriptのPromiseを解説します

User.query({where: {name:nm}, andWhere: {password: pw}}).fetch()

これの戻り値が Promise オブジェクトが返ります。

(new Promise()).then(成功時の関数).catch(失敗時の関数)

みたいな

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

HTMLのリアルタイムプレビュー環境を雑に整える

はじめに

HTMLファイルを書くとき、以前は一回一回VSCodeをターミナルで起動し、ファイルを編集してからChromeで確認するという面倒な方法を取っていました。しかも、普段はVimを使ってるのでVSCodeを使う機会はそうありません。しかし先日、N予備校のプログラミング学習コースが無料で提供されているということで、Webを一回しっかりと触ってみたいと思うようになりました。そういうわけで、快適にHTMLファイルのブラウザ表示の確認をするためにVSCodeの設定をしたいと思います。

HTML Previewのインストール

といっても、やることは至極簡単です。VSCode左側のScreenshot from 2020-03-17 16-16-14.pngマークを選択して、「html preview」と検索すれば簡単にVSCodeの拡張機能がヒットします
Screenshot from 2020-03-17 16-19-10.png
Installをクリックです
Screenshot from 2020-03-17 16-20-27.png
This extension is enabled globally「この拡張機能はグローバルに使用可能です」と表示されましたので、早速使ってみましょう!Screenshot from 2020-03-17 16-42-29.png

VSCode内でHTMLファイルを選択します。

この状態でctrlを押しながらk、その後ctrlkを離して単体でvを叩きます。

gif.gif

ブラウザ表示できました!画面は「うまれた歳からの秒数を表示するページ」です。このHTML Previewですが、JavaScriptで付けた動きも表示してくれるみたいですね!(Texファイルなどにも対応しているPreview拡張を入れても、JavaScriptの動作を確認できませんでした。)Webページを作る時にかなり効く機能ではないでしょうか。

※僕、レポート類全部Texファイルで書いてGit管理したいと考えているんですが、Tex対応してる拡張の方を入れてもHTML Previewはちゃんと動きました。複数刀流が良さげですね。

参考

Visual Studio Codeでhtmlコーディングはリアルタイムプレビューがすごく良い
https://rui-log.com/vscode-html-cording/#VScodeMarkdown

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

【Nuxt.js】Vue Router基礎編:params, queryを使おう

前置き

とっても便利なparams, queryについてご紹介?
・同じコンポーネントを見せたいけど、
 カテゴリごとにURLだけを変えたい…
・一覧ページからソートして表示させたい

そんな時に便利です♪

params, queryについて
いくつかに分けて書きます✍️
router-linkが分かれば簡単です?
まだ不安な方のためにも
複数の書き方で記載しました?

・params, queryの違い
・使うメリット
・クエリパラメーターとは?
・router-link色々

params, queryの違い

まずはURLを見るのが
分かりやすいと思います?

localhost:3000/param/param?query=123

・パスパラメーター(param)
 ?より前の部分、省略できない
・クエリパラメーター(query)
 ?以降の部分、省略できる

?例えば

localhost:3000/project123

projectごとにURLを変更
表示ページは同じでコンポーネントで表示分け

file
pages/
--| _id/
-----| index.vue

?例えば

localhost:3000/events?today=true

events/index.vueの中で
today=trueでソートをかけて表示

file
pages/
--| events/
-----| index.vue

eventsは絶対省略できないですね。
pages/events/index.vueに
行けなくなってしまいます。

?today=trueは省略しても
ソートが外れるだけなので
ページはきちんと表示されます♪

メリット

# params, queryの違い で書いた通り!
もう少し身近な例でいうと…

例えば!

?イベントサイトで一覧を見たい

https://events

その中でも1ページ目だけ見たい

https://events?page=1

ということが、
できちゃいます!!!

以前paginationで使ったことがあるので
こちらも確認してみてください?
https://note.com/aliz/n/nd9f344e4686f

クエリパラメーターとは

サーバーに情報を送るための文字列のこと✍️
urlの最後に?から始まる文字列をつけて
サーバーにデータが送信されます!
複数のデータを送る時は?で繋げます。

localhost:3000/post?id=123

id=123というデータを
サーバーに送信していますね!

ではディレクトリ や
表示はどうなるかというと…

file
pages/
--| post/
-----| index.vue

サーバーにデータを送りたいだけなので
表示するページ自体はpages/post/index.vue

そこにクエリパラメーターを入れて
パラメーターを参照することで
自分のIDや登録名が見れたり?
フィルターをかけてソートできたり?✨
するわけです!!!

router-link色々

paramsやqueryを使う前に?
pathやnameを使った
router-link(nuxt-link)を理解しましょう!
?template内のlink部分だけ書きます?

【飛びたいURL】

localhost:3000/home

【ディレクトリ】 

file
pages/
--| home/
-----| index.vue

ではvue検証で
nameとpathを見てみましょう?

・nameはhome
・pathは/home

ということが分かりますね!
Frame 3.png

【pathを使った書き方】

◾️router-linkバージョン

index.vue
<router-link
    :to="{path: 'home'}"
>
    home
</router-link>

◾️$router.pushバージョン

index.vue
<button
    type="button"
    @click="$router.push('home')"
>
    home
</button>

【nameを使った書き方】

◾️router-linkバージョン

index.vue
<router-link
    :to="{name: 'home'}"
>
    home
</router-link>

◾️$router.pushバージョン

index.vue
<button
    type="button"
    @click="$router.push({ name: 'home'})"
>
    home
</button>

さあ、ここまではOKですか???

router-linkにparamsとqueryを追加

ではname, pathに
paramsとqueryを追加してみましょう!
基本的な書き方は公式のこちら。

https://router.vuejs.org/ja/guide/essentials/navigation.html

paramsの追加

【path + params を使った書き方】

pathはURLを書けば良いだけ?
こんがらがったら
# params, queryとは
に戻りましょう!

【飛びたいURL】

localhost:3000/post/profile

【ディレクトリ 】

file
pages/
--| post/
-----| profile.vue

◾️router-linkバージョン

index.vue
<router-link
    :to="{ path: '/post/profile'}"
>
    home
</router-link>

◾️$router.pushバージョン

index.vue
<button
    type="button"
    @click="$router.push({ path: 'post/profile'})"
>
    home
</button>

◾️動的ルーティング
user.idごとにページを変えたい(_id.vue)
その場合はuser.idを
fetchでstoreから取ってきたりします!
その辺は別記事にて書こうと思います?
一旦リンクの書き方のみ。

✅'' にurlを書いていましたが、
 動的ルーティングの場合は
 ``を使ってかいていきます✍️

https://ja.nuxtjs.org/guide/routing/

index.vue
<router-link
    :to="{ path: `/post/${user.id}`}"
>
    home
</router-link>

【name + params を使った書き方】

【飛びたいURL】

localhost:3000/post/123

【ディレクトリ 】

file
pages/
--| post/
-----| _id.vue

再びvue検証を見てみましょう?
mix.png

◾️router-linkバージョン

index.vue
<router-link
    :to="{ name: 'post-id', params:  {id: 123} }"
>
    home
</router-link>

◾️$router.pushバージョン

index.vue
<button
    type="button"
    @click="$router.push({ name: 'post-id', params: {id: 123} })"
>
    home
</button>

✍️書き方パターン
・pathはurlをそのまま書く
・nameは追加でparams指定

✍️実際の書き方
・name
 URLの/を-にする
 _id.vueはidにする
・path

index.vue
 { path: 'hoge/hoge' }
 または
 { path: `hoge/${変数}`}

 

queryの追加

【path + query を使った書き方】

【飛びたいURL】

localhost:3000/post?id=123

【ディレクトリ 】

file
pages/
--| post/
-----| index.vue

◾️router-linkバージョン

index.vue
<router-link
    :to="{ path: '/post?id=123'}"
>
    home
</router-link>

◾️$router.pushバージョン

index.vue
<button
    type="button"
    @click="$router.push({ path: 'post?id=123'})"
>
    home
</button>

【nama + query を使った書き方】

【飛びたいURL】

localhost:3000/post?userId=123

【ディレクトリ 】

index.vue
pages/
--| post/
-----| index.vue

◾️router-linkバージョン

index.vue
<router-link
    :to="{ name: 'post', query: {userId: 123} }"
>
    home
</router-link>

◾️$router.pushバージョン

index.vue
<button
    type="button"
    @click="$router.push({ name: 'post', query: {userId: 123} })"
>
    home
</button>

まとめ

すごく簡単にまとめると
この2点が分かれば基礎は⭕️です!

・nameとpathが
 ディレクトリ のどこにあたるか
・params, queryはURLのどこにあたるか

記事が公開したときにわかる様に、
note・Twitterフォローをお願いします?

https://twitter.com/aLizlab

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

GoogleDriveにアップロードした画像から、外部参照可能なURLを生成しimgタグ化するコードを書いてみた

概要

Codepenを最近よく使うようになって、画像使いたいなーでも有料会員になるほどでもないなーって思って、
コードペン上で画像を扱う方法をググってみたら、ゆんつ様の下記記事に遭遇!

▼こんぷれ / ゆんつ様
CodePenの無料会員で画像を使うには
※詳細はゆんつ様の記事をご参照ください!

要約すると、GoogleDriveやDropBoxにアップロードされた画像から共有リンクを生成し、
共有リンクの一部を書き換えて画像参照できるようにするといった内容です。

この記事はゆんつ様の記事を参考に、JavaScriptを使って、
GoogleDriveにアップロードした画像リンクを1回の作業で終わるようコードを書いてみたという話です。

作ろうと思ったきっかけ

私はGoogleDriveでアイコンや画像とかをまとめてるので、
GoogleDirveでの手順を参照して、さっそくやってみたわけですが

使ってみたい画像の共有を押して、
共有リンクコピーして、
imgタグのsrcに貼り付けて、
URL書き換えて…
…以下画像分繰り返し…

だあああああああめんどくさい!
想像以上にめんどくさい!

10分ほど気の遠くなるちまい作業を続けながら、考えました。
このめんどくさい作業を一回で終わらせたいと…

考えた結果
画像はアップロードした時点で固有IDが与えられるなら、
フォルダ内のアイコン、画像の分だけ繰り返し処理でURLを加工してimgタグ化してしまえば、
この作業しなくて済むんでは?
と、思いついたわけですね

出来上がったコード

ソース
// removeSize関数:url の末尾に指定されている画像サイズを取り除く処理
const removeSize = (url) => {
        if (url.match('=w300-k-iv165')) {
            return url.repalce('=w300-k-iv165', '');
        } 
        else if (url.match('=w300-k-iv1')) {
            return url.replace('=w300-k-iv1', '');
        }
        else if (url.match('=w300-k-iv5')) {
            return url.replace('=w300-k-iv5', '');
        }
        else if (url.match('=w300-k-iv2')) {
            return url.replace('=w300-k-iv2', '');
        }
        else if (url.match('=w300-k-iv3')) {
            return url.replace('=w300-k-iv3', '');
        }
        else if (url.match('=w200-h190-p-k-nu-iv1')) {
            return url.replace('=w200-h190-p-k-nu-iv1', '');
        }
        else if (url.match('=w200-h190-p-k-nu-iv165')) {
            return url.replace('=w200-h190-p-k-nu-iv165', '');
        }
}

// フォルダ内の画像を全て取得
let picture = document.getElementsByClassName('l-u-Ab-zb-Ua');

// 取得した画像の数だけ、加工処理を繰り返す
for (let i = 0; i < picture.length; i++ ) {

  // 画像の名前を取得して変数labelに代入
    const label = picture[i].closest('.jGNTYb').getAttribute('aria-label'); // ※1

  //  取得した画像のimgタグからsrcを取得し、取得したソースの一部を外部参照URLのフォーマットに置き換える処理  
  const url = picture[i].getAttribute('src').replace('https://lh3.google.com/u/0/d/', 'https://drive.google.com/uc?export=view&id=');

  // removeSizeでURL末尾のサイズ指定を取り除いたURLを取得し、imgタグのsrcにぶち込んだものをconsoleに出力
    console.log(`${label}\n<img src="${removeSize(url)}" alt="">`);
}

Image from Gyazo

使い方

1.基本的には、GoogleDriveで適当なフォルダを作成します。

2.フォルダにアイコンや画像をアップロードします。

3.Chromeデベロッパーツール(F12 / Ctrl + Shift + I)を起動して、consoleタブから、上記コードをペーストし実行します。

4.出力されたimgタグをテキストファイルなどにコピペします。

あとは、使いたい時に生成したimgタグをそのままコピペして貼るだけで、使えます。
もしも画像を追加した場合は、再生成する必要がありますが、手作業でURLを加工するよりが楽かなと思います!

注意

フォルダを作らずに、マイドライブ上で上記コードを実行すると、
変数labelのgetAttributeがnullだよ!と怒られます。

これは、フォルダ内とマイドライブ上で祖先要素のclassが異なるため、指定したclassを持つ祖先要素が見つからずに発生するエラーです。
マイドライブ上で使いたい!という方は、変数labelのclosest('.Ccie2c')とする事で、リンクを生成する事が出来ます。

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

Intersection Observer API 実装方法

この記事は、社内教育向けの説明資料として書いています。
文中のIntersection Observer APIに関する引用は、以下のサイト・記事から抜粋したり、参考にしています。

以下の記事も参考になります。

この記事では、よく使いそうな利用例を1つですが用意しました。

  • スクロールに応じて要素をフワッと表示する

概要

mozillaをみていただくのがよいと思います。

Intersection Observer API (交差監視 API) は、ターゲットとなる要素が、祖先要素もしくは文書の最上位のビューポートと交差する変更を非同期的に監視する方法を提供します。

また、ics.mediaも非常にわかりやすく解説してあります。

Intersection Observer APIは直訳すると交差監視APIという名前です。これは要素と要素の交差を検知するAPIになります。一見するとスクロールとの関連性がないようにも思えますが、「スクロールして特定位置にきたら」 というのは 「要素と交差したら」 ということに置き換えられるので、このAPIを活用できます。

従来のscrollを使った手法とは違い、ビューポートサイズの変更で交差する位置が変わっても、自動的に反応します。また、スクロールに関するイベントではないので、スクロールのたびに呼ばれることもなくパフォーマンス面でも有利です。

割と最近まで、スクロールのアップ・ダウンをしたときに、画面の指定位置に要素が入ったことを取得するは、Waypointsなどのプラグインを使うことが多かったですが、その代替として使っています。

また、Waypointsは画面内の何%の位置にきたら発火、みたいな指定の仕方をするのですが(他の指定方法もあったらすんません)Intersection Observer APIはそれもできるし、要素自体がどの程度root(option設定のroot。後述)に入ったら、という設定もできるため使い勝手がよいので気に入っています。

メリット

  • プラグインを読み込まなくてもよい
  • パフォーマンスはこちらのほうがよい(計測したわけではないです)

後述しますが、IE対応しようとするとpolyfillは読み込まなくてはいけません。

パフォーマンスに関しては、スクロールイベントなどではなく、閾値(後述のthreshold)の前後でしかコールバックが呼ばれないため、動作が重くなることは少なくなります。

以下はmozillaからの抜粋です。

これまで、要素間の交差を検出する実装をするには、 Element.getBoundingClientRect() のようなメソッドを呼び出すイベントハンドラーやループがあり、影響を受ける要素に対する情報を都度計算し集めることで構成されていました。このようなコードがメインスレッドで実行されると、いずれかはパフォーマンスの問題を引き起こす可能性があります。試しにサイトにテストとして読み込めば分かりますが、事態は完全に酷くなりえます。

Intersection Observer API を使用することで、監視したい要素が別の要素 (もしくはviewport) に入ってきたり出ていったりする時、まだ両要素が交差する量がある一定の量を満たす時、実行されるコールバック関数を登録するが出来ます。

使い方

mozillaの記事と同じになってしまいますが、一応記載しておきます。

optionsオブジェクトとIntersectionObserverコンストラクタの作成
let options = {
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px',
  threshold: 1.0
}

let observer = new IntersectionObserver(callback, options);

IntersectionObserverオブジェクト

第一引数:実行されるコールバック関数です
第二引数:コールバックが呼び出される状況を制御します

optionsオブジェクト

root
ターゲットが見えるかどうかを確認するためのビューポートとして使用される要素です。指定されなかった場合、もしくは null の場合はデフォルトでブラウザーのビューポートが使用されます。

rootMargin
root の周りのマージンです。CSS margin プロパティに似た値を持つことができます。例えば、"10px 20px 30px 40px" (top, right, bottom, left) のようなものです。この値はパーセント値にすることができます。 たとえ値が0であっても "0px 15px 10px"のように単位をつける必要がある、ということです。

交差を検知するルート要素からの距離とも言えます。上記のように10pxを指定した場合、交差判定をルート要素の周囲10px分拡大します。見える少し前にイベントを発火させたい場合などに有効です。また負の値も指定できるので、ある程度見えてからイベントを発火させる、といった使い方もできます。

threshold
閾値になります。0〜1の間を数値もしくは配列形式で入力します(デフォルトは0)。rootMarginがルート要素にもとづくオプションに対して、thresholdは交差する要素にもとづくオプションです。

監視される要素をターゲットにする
const target = document.querySelector('#listItem');
observer.observe(target);

監視させたいターゲット要素をobserveメソッドの引数に指定することで監視ができます。

コールバック関数の作成
let callback = (entries, observer) => {
  entries.forEach(entry => {
  // entry.target等
  });
};

上記では監視対象が1要素ですが、複数の要素を監視対象にできるため、mozzillaでも配列で記載されています。

IE対応

2020.3.17時点では、IEのみ非対応のためポリフィルを読み込みます。
https://github.com/w3c/IntersectionObserver/tree/master/polyfill
スクリーンショット 2020-03-17 10.30.12.png

また、forEachでエラーを出さないようにJavaScriptを書く必要があります。
それは本題とそれるので詳しく書きませんが、以下の利用例はその一例です。

利用例:スクロールに応じて要素をフワッと表示する

See the Pen IntersectionObserver-sample01 by shuCream (@shuCream) on CodePen.

ポイント

  • rootMargin: '-50% 0px'なので、viewportの中心にきたら発火します
  • アニメーションはGSAPを使用しています。
  • 一度発火させたものは発火させないようにしたいので、data属性の設定・取得でreturn;をさせています。
  • IEでforEachを使うためには、Array.from()メソッドを使って、配列風オブジェクトや反復可能オブジェクトから、Arrayインスタンスを生成します。なお、IE11で動作保証するにはポリフィルが必要になります。

参考: Array.from()

const options = {
   root: null,
   rootMargin: '-50% 0px', // viewportの中心にきたら発火
   threshold: 0
 };

 const active = entries => {
  Array.from(entries).forEach(entry => {
     if (entry.isIntersecting) {
       // 一度アニメしたら発火させない
       if (entry.target.getAttribute('data-animationend')) return;
       entry.target.setAttribute('data-animationend', true);

       gsap.to(entry.target, 1, { opacity: 1, y: 0, ease: 'power1.out' });
     }
   })
 };

冒頭で紹介したics.mediaさんの記事でも、Intersection Observer APIを使って表示中コンテンツに合わせて目次を色を変える実装という見出し部分で実装方法をご紹介してくれてますので、参考になると思います。

今後の対応案件でこのような実装をするときは、スクロールイベントやWaypointsでなく、Intersection Observer APIのほうがメインになってくると思うので、新人のコーダーさんにも理解しておいてもらいたくて記事にしました。

冒頭紹介したmozillaやics.mediaの記事は、しっかり読んでおいてもらいたいです。

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

なにが時代を超えて共通する技術か考えたい - 分割統治編

物事の普遍性への追求は人類のテーマです。
時代と共に変わりゆくソフトウェア分野の技術の中で普遍的に存在しているのか、共通して存在する中核的な技術はあるのだろうかということを考えたいです。

新しいもの、古くからあるもの

近年、マイクロサービス、クリーンアーキテクチャなどの○○○アーキテクチャ、Reactを始めとするの宣言的UIなどがそれぞれ分野違えど注目を集めてるように思います。また時代にとらわらずUNIX哲学に代表されるコマンドが活用されています。

決して、新しく登場した技術だから良いという考えているわけではなく、新しいものは、古い技術の問題点を克服に作られていることが多いため、優れているものが多いと考えています。

それは分割統治なのではないか?

ソフトウェア技術の分野も広いのでいろんな視点があるとは思うのですが、分割統治は普遍的な技術の候補になるのではないかと思います。
ここでの分割統治は、よく例に上がるマージソートのようなアルゴリズムだけではなく、解くべき問題を小さい問題に解を合成して最終的な解を得ることだとします。「一つのことをうまく行い、協調させて動かす」ことが複雑なことを成し遂げる秘訣だろうということです。UNIX哲学 - Wikipedia

ソフトウェアの技術は幅広いため、他の分割統治とは異なる視点での普遍的な技術は他で書かれることを期待しつつ、この記事では自分の観測範囲からこの分割統治が共通して使われていると考えられる技術を集めたいと思います。そして最後に少し哲学的なことも考えたいと思います。

非同期処理 - async/await

いまでは多くの言語で違えはあれどasync/awaitが導入されています。JavaScript, Python, C#, Dart, Rust ...。async/await以前は基本的にシングルスレッドであるJavaScriptでは非同期処理にコールバックが使われていて、コールバックの大量の入れ子によって発生するコールバック地獄が問題とされていました。以下のようにJavaScriptでPromiseを使っても最初の方の値を使おうとすると入れ子が発生します。(この例ではPromise.all()使って入れ子減らせますがこれは例をシンプルにするためです。)

f1().then(a =>
  f2().then(b =>
    f3().then(c =>
      console.log(a + b + c)
    )
  )
);

以下のようにasync/awaitを使うことで、入れ子が消えフラットになります。

(async () => {
  const a = await f1();
  const b = await f2();
  const c = await f3();
  console.log(a + b + c);
})();

分割統治の視点で見るとPromiseを返すf1(), f2()などをasync/awaitで合成できていると考えられます。つまりPromiseというデータ構造にすることで非同期な処理を切り出し、合成することが容易になったと言えるでしょう。

async/awaitとモナドの比較

この煩わしい入れ子をフラットにする技術は決して新しいアイデアではないのではないかです。そこで思いつくのが、モナドの合成で用いるHaskellのdo記法やScalaのfor式です。async/awaitはdo記法やfor式に似ているのではないということです。

以下はdo記法を使わないHaskellです。先ほどの.then()の入れ子ととてもよく似ていることが分かります。

main :: IO ()
main =
  f1 >>= (\a ->
    f2 >>= (\b ->
      f3 >>= (\c ->
        print (a + b + c)
      )
    )
  )

上記をdo記法で書くと以下のようにフラットになりasync/awaitを使った時にとても見た目が似ていることが分かります。

main :: IO ()
main = do
  a <- f1
  b <- f2
  c <- f3
  print (a + b + c)

Haskellではdo記法はコンパイラが最初のコードに機械的に変換します(糖衣構文)。上記のような入れ子をフラットにする技術は特に新しくなくdo記法が今まで可能にしてきたと思います。

async/awaitの良さは非同期計算の合成を容易にしたことはもちろん、非同期処理と同期計算との合成も容易にしたことのように思えます。JavaScriptのasync/awaitでは、通常のif文/for文/while文でawaitでき同期処理のconsole.log()もawaitと同じレベルに書けます。Haskellのdo記法の場合、同じ文脈のモナドに持ち上げる必要がでてきます。そういう点でasync/awaitは非同期に特化した構文を用意した分非同期処理と同期処理の合成も容易にしたと考えられます。Haskellは特定の計算に限定せずすべてのモナドに対してdo記法が使える点で一貫性があり覚えることが減りシンプルと言えます。

do記法を考えれば、async/awaitが解決した入れ子の問題はものすごく新しい技術というわけではなくとも、非同期・同期処理問わず分割統治しやすくなっています。

ストリーム, パイプ, コレクション

ここではデータを細かく分けて、そのデータを流して処理する時に使うものをざっくりとストリームデータ処理とします。身近なストリームデータ処理はシェルのパイプでしょう。コマンド | grep 好きな文字で好きな文字列を探したり、tar c . | 暗号化 > 保存先で.tarを暗号化しながら保存するかもしれません。Node.jsでは「ストリームを制するものはNode.jsを制す」といわれNode.jsでもストリームは大事な役割を担っています。

パイプ - Unixパイプ

分割統治の視点で見ると、パイプはコマンド同士を合成する技術とみれます。コマンドごとに得意分野があり、そのコマンドがC言語で書かれていてもGo言語で書かれていてもRustでも未来の言語になっても合成が可能です。またパイプでの合成以外の観点でも、パイプは一時データ(ファイル)を作らず時間的にも空間的(メモリ・ストレージ的)にも効率が良いです。それに加え無限の乱数バイト列もcat /dev/urandom | ...のようにを扱うことが可能です。

.map(), .filter()など

あらゆる関数型に影響を受けている言語の配列やリスト(言語によってはコレクションに対して)にあるメソッドや関数として.map()filter()reduce()などの演算(高階関数)が言語ごとに多少命名が異なりますが存在します。これらのメソッドまたは関数は、引数に受け取る関数を処理を変えることができます。どんな関数と組みわせるかで.map().filter()であらゆるシーケンシャルなデータに対して演算できる点で分割統治できています。

遅延処理になる、JavaのStreamやHaskellのリストやApache Sparkのデータ構造などでは、パイプと同じく一時データを作らず時間的空間的に効率もよくなる利点も持ち合わせます。

React/Vue/Flutter/SwiftUI - 宣言的UI

最近、Webアプリでの宣言的なUIのコンポーネント化技術が多く使われているように感じます。モバイルネイティブアプリでもFlutter, SwiftUIやJetpack Composeなどの宣言的なUIが注目されていると思います。この宣言的UIに関しても従来の命令的なものと比較して、ビュー同士を分割し合成することがよりしやすくなったと、分割統治の視点で考えられると思います。

React Hooksのただ関数に切り出しただけのカスタムフックでビューとロジックを分割し合成がより容易になることが示されて、ロジックの再利用やテストも容易になっていると思います。Vue 3で標準で利用できる予定のVue Composition APIはこのReact Hooksに影響を受けています。ビューとロジックの分割統治されたと見ることができると思います。

一貫した概念

ここでは一貫した概念は、Unix/Linuxの「すべてファイル」、Haskellの「すべてが式」アクターモデルの「すべてがアクター」のようなことを意味したいと思います。
例えば、Reactなどで用いられるJSXはHTMLが式になったリテラルとしてJavaScript上で扱えるようにしたと考えられるのではないでしょうか。HTMLが式・オブジェクト・値になることによっていままでオブジェクトに使えてきたあらゆることが可能になります。つまりHTMLを引数に渡したり、戻り値として返したり、配列に要素として詰め込んだり、オブジェクトに対してできていたことをすべてHTMLに対しても行えるようになるということです。式となったHTMLは式が使えるあらゆる箇所で使えることで、既存の関数や変数やクラスなどと一緒に(合成して)使うことがより容易になったと考えられます。ほぼHTMLを許すJSXのような拡張は行わなくとも、Reactに影響を受けたであろうFlutterやSwiftUIはUIを表すオブジェクトを.dartや。.swift上のコードに書くため、VueのテンプレートよりReactに似ていると言えます。

このアイデアも決して新しいわけではなく、ScalaはXMLリテラルが扱え、XMLを直にScalaのコードとして書けます。つまり、今まで別だと考えていたHTMLとJavaScriptを一貫した概念"式"と扱えるようにすることでより自由に扱え分割統治しやすくなると思います。

パッケージ, パッケージマネージャー

いまは言語とセットのようにライプラリを管理するためのパッケージマネージャーがあります。言語によって呼び方の違いはあれどパッケージの分割された単位の一つだと思います。それら組み合わせたり管理するためにパッケージマネージャーがあるというふうに捉えられると思います。

Denoのimport

Node.jsの開発者のRyanが開発するDenoはまた違ったライブラリの取り込み方・合成の仕方だと思います。以下のようにURLでインポートができて、現在のところパッケージマネージャーを使わない方法を使っている思います。実際に.../server.tsをリンクをブラウザで開くとすぐにソースを確認することもできます。

import { serve } from "https://deno.land/std@v0.5/http/server.ts";

一瞬 <script src=""> 時代のようにも見えるかもしれません。ですがDenoのimportと<script>とは異なり、暗黙にグローバルに変数がのさばらなく、明示的に名前空間が作れます。そしてHTMLではなくJavaScriptの中で完結してライブラリを取り込み・合成ができます。

Ammonite (Scala)

パッケージマネージャを使わずという点では以下のAmmonite(Scala)も似ています。

import $ivy.`org.scalaz::scalaz-core:7.2.27`, scalaz._, Scalaz._
(Option(1) |@| Option(2))(_ + _)
// => Some(3)

ライブラリを取り込む手法・合成は、DenoやAmmoniteのようにパッケージマネージャを使わない方法もあります。まだこれらは発展途上かもしれませんが、一つのファイルでライブラリの依存も完結するシンプルさとインストールしない手軽さやポータビリティが今後より評価されるようになるかもしれません。パッケージマネージャーあるなし含め合成し、パッケージ(言語によってはモジュール)を再利用するのも分割統治と言えます。

GitHub Actions - 継続的インテグレーション(CI)

ソフトウェア開発に不可欠になっているCIについてです。
GitHubを中心とするOSSではTravis CIやCircleCIが良く使われていると思います。GitHub自体も類似の機能を持つGitHub Actionsを提供していて、このGitHub Actionsが分割統治の視点で個人的に注目してます。

それが、小さい部品に分かれたアクションを集めて設定ファイルを書く点です。たとえば、- uses: actions/checkout@v2でチェクアウトや- uses: actions/setup-node@v1でNode.jsのセットアップなどのアクションを並べていきます。"actions/..."はGitHub公式ですが、例えば- uses: peaceiris/actions-gh-pages@v3でGitHub Pagesへのデプロイのようにサードパーティ製のアクションを使うこともできます。こういったアクションを作ることもできます。つまりCIで使いたい部品(アクション)を再利用したり切り出し可能です。Travis CI やCircleCI では調べた限りこういった機能はありませんでした。CircleCI Orbsという類似の仕組みがあることを@peaceiris 氏に教えていただきました。分割統治の視点でもGitHub Actionsは優れているのではないでしょうか。

その他色々

依存性の注入(DI)も分割統治でしょうし、分割統治の視点で見れば、アプリケーションごとを1つのDockerコンテナに分けて、それを合成するものがDocker ComposeであったりKubernetesだったりするのではないでしょうか。他のアプリケショーンと連携するためにREST API, GraphQLがあったり、他言語を直接使いたい場合は、発展途上かもしれませんがOracleのGraalVMを使うかもしれません。WebAssemblyが活発なので、複数言語間でいえばWASIの方が今後より発展するかもしれません。「細く分けて、組み合わせる」という分割統治はまだまだ色々なところで活躍しているでしょう。脳内で補完していただければと思います。

合成した時の副作用を減らす技術や型

小さく分けたものを組み合わせるにあたって、その周辺に様々な技術が現れてくるでしょう。例えば、副作用を持たない純粋関数やイミュータブル(不変)のデータなどです。関数を呼び出したり適用したりする時に、渡した引数を変更されたり、他の環境を変えたりなるべく驚きを減らすことは、組み合わせることが増えれば防ぎたくなるのは自然なことに思えます。これは"関数"をFaaSやコマンドなどに読み替えても同じく言えると思います。

型の必要性

近年、Python, PHP, Rubyといった軽量言語にも静的な型を導入される動きや、JavaScriptに対する静的型付けされたTypeScriptのような存在もあります。C言語のような低レイヤーの言語でのメモリサイズを決める型はマシンのためだったものが、堅牢なプログラム書く人のための型になっていると思います。分割統治の視点で見ると、実行時の型の誤りを極力排除して、確実に合成できることを保障しようとしているとも取れると思います。

CDプレイヤーにDVDをいれて故障が多かったのも"型"が違うものだったのに同じだったからで、3DSのソフトのように突起があれば防げたかもしれません。

現代のエディタ/IDEであれば書いている段階で赤い波線でまたに誤りがあるものの教えてくれます。もしくはそれに準じる静的な解析技術が健全な合成に寄与すると思います。

「問題を分割する方法は、解を合成する方法に依存する」

分割する方法、合成する方法が違えど、いろんな場面で分割統治という技術がが使われていることが分かりました。分割統治してプログラムを組み立てる時に役にたつと思う言葉を引用したいと思います。

「問題を分割する方法は、解を合成する方法に依存する」

Scala Matsuri 2018のがくぞ さんの「Why "Composability" Matters」からです。大元は「Why Functional Programming Matters」です。

これを見て、「問題の分割方法が合成方法に依存する」ということを何度か捉え直してみると、合成方法がイマイチなら解の分割しても上手くいかないことがあるとも解釈できるのではと思います。複雑で長いコードを目の前にすると分割することに目が行きがちになることもあり、その後の合成をどのような方法を考えることこそが大事だということに改めて気付かされます。

哲学的なこと: 最終的にどこへ向かうのか?

では、分割統治は「最終的にどこへ向かうのか?」「どこに収束するのか?」と考えるときに思うことです。ここから技術的なことからは離れて、飛躍して少しばかり哲学的な感じです。

  • UIは宣言的に記述されコンポーネントとして組みわせて再利用でき
  • 小さいコマンドをパイプで組み合わせて、
  • 非同期処理を表すPromiseというデータとしてロジックを分けawaitで合成し、
  • ...

インターネットもコンピュータやその他装置のつながりで、そのコンピュータの中の論理回路もANDゲートやORゲートのような論理ゲートの組み合わせで、その論理ゲートの中の...

社会も我々の人体も小さいものの組み合わせで複雑なものを作っているのではないかと思うのです。いろんな臓器で成り立っていて、それらもまた小さな細胞からなりたっていて... 中を開けたらまた小宇宙が広がっているような。
ちょうどRubyの作者Matz氏の主張と似ているかもしれません。

Rubyの外観はシンプルです。けれど、内側はとても複雑なのです。 それはちょうど私たちの身体と同じようなものです。

(https://www.ruby-lang.org/ja/about/)

それは"美"なのではないか?

最終的に向かう先は"美"なのではないかと思うのです。
イタリアの画家のカルロッティがこのように"美"を定義しています。

すべての部位が完璧に調和して、手を加える必要がまったくないこと

(映画 『NEXT -ネクスト-』より)

これはUnix哲学、パイプの発明者のマキルロイの要約にも通じるところがあるではないでしょうか。

一つのことを行い、またそれをうまくやるプログラムを書け。
協調して動くプログラムを書け。

ガンカーズの"小さいものは美しい"とも通じるのではないでしょうか。
UNIX哲学 - Wikipedia

物事行き詰めると言語化できない領域に行くことはよくあると思います。すべての細かく分かれたパーツがバランスを保っていて合成され、それ以上足す必要がないシンプルな状態”美”に向かっているというのは、いままでコマンドとパイプや、Promiseとasync/awaitなどを見ても調和しこれ以上足す必要がないくらいシンプルに見えます。そういう完全には厳格に言葉で表せないもの"美"に我々は向かっているのかもしれません。






おわりに: トレードオフ

最後は飛躍しましたが、色々な技術で分割統治がある程度共通して使われていることを例をふまえて集めました。現実的にいまある全てを分けて合成しようと思っても、パフォーマンス、セキュリティ、後方互換性、"大人の事情"...など分割統治することとのトレードオフがあると思います。

例えば、HTTP/3で使用されるQUICはTCPが担っていた通信とTLSが担っていた暗号化が別れずに組みあわっさたような新しいUDP上のプロトコルです。その分割されない理由は、パフォーマンス的にラウンドトリップの削減をしたり、ヘッダの暗号化でセキュリティ的なことやプロトコルの硬直化を防ぐ役割があるようです。(参考: QUICの中身が分からないから仕様読んでみた | κeenのHappy Hacκing Blog, 硬直化 - HTTP/3 explained

この例以外でもいろんなトレードオフがあると考えられますし、制作途中のまだ全体像が見えてない段階で分割すると上手く行きづらい個人的な経験則もあります。
この記事では分割統治にフォーカスを当てました。他の視点で共通して使われている技術の記事が出ることに期待にしつつ終わりにします。

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

ログインなしでコードを共有できるサイトつくた

成果物

https://code.itsumen.com/

リポジトリ

https://github.com/yuzuru2/code_site

開発環境

  • ubuntu 18.04
  • docker
  • docker-compose

使用ライブラリ周り

フロントエンド

  • parcel
  • bootstrap
  • highlight.js
  • react
  • nginx(静的ファイルを配信)

バックエンド

  • typescript
  • nodejs
  • pm2

データベース

  • mongodb

成果物を使うケース

  • ちょろっと書いたコードを誰かに見せたい時

UI

ホーム画面

無題.png

コード閲覧画面

無題.png

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

コード共有サイト作成 docker react node.js mongodb

成果物

https://code.itsumen.com/

リポジトリ

https://github.com/yuzuru2/code_site

開発環境

  • ubuntu 18.04
  • docker
  • docker-compose

使用ライブラリ周り

フロントエンド

  • parcel
  • bootstrap
  • highlight.js
  • react
  • nginx(静的ファイルを配信)

バックエンド

  • typescript
  • nodejs
  • pm2

データベース

  • mongodb

成果物を使うケース

  • ちょろっと書いたコードを誰かに見せたい時

UI

ホーム画面

無題.png

コード閲覧画面

無題.png

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

コード共有サイトつくた

成果物

https://code.itsumen.com/

リポジトリ

https://github.com/yuzuru2/code_site

開発環境

  • ubuntu 18.04
  • docker
  • docker-compose

使用ライブラリ周り

フロントエンド

  • parcel
  • bootstrap
  • highlight.js
  • react
  • nginx(静的ファイルを配信)

バックエンド

  • typescript
  • nodejs
  • pm2

データベース

  • mongodb

成果物を使うケース

  • ちょろっと書いたコードを誰かに見せたい時

UI

ホーム画面

無題.png

コード閲覧画面

無題.png

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

Prettier 2.0 の主要な変更

ウェブ開発でよく使われているコードフォーマッターである Prettier の 2.0 がリリースされました。実装が間に合わず遅れている間に TypeScript 3.8 のリリースがあったりといろいろあり、当初の予定より 2 ヶ月ほど遅れてしまいました。

主要な変更について紹介します。詳細が知りたい方は、リリースブログや PR や Issue を確認してください。

Node 10.13.0 未満のサポート終了

https://github.com/prettier/prettier/pull/6907
https://github.com/prettier/prettier/pull/6908
https://github.com/prettier/prettier/pull/7302

EOL が来ているバージョンのサポートを終了します。

デフォルトオプションの変更

いくつかのオプションのデフォルト値が変更されます。現在デフォルト値を使っていてこれによるコードの修正を回避したい場合、明示的に設定をする必要があります。

trailingComma(末尾カンマ)

https://github.com/prettier/prettier/pull/6963

1.x 2.x
none es5

1.x ではあらゆる末尾カンマを許容しませんでしたが、2.x では ECMAScript 5 でバリッドになるように末尾カンマをつけます。

// 1.x
const obj = {
  foo
};

// 2.x
const obj = {
  foo,
};

ただし、all ではなく es5 なので関数の引数リストの末尾カンマは許容しません。

arrowParens(アロー関数の引数が一つのときのカッコ)

https://github.com/prettier/prettier/pull/7430

1.x 2.x
avoid always

1.x では、アロー関数の引数が1つのときカッコをつけないようにフォーマットをしていましたが、2.x では常につけるようになります。

// 1.x
arg => arg;

// 2.x
(arg) => arg;

endOfLine(改行コード)

https://github.com/prettier/prettier/pull/7435

1.x 2.x
auto lf

1.x ではファイル内の他の改行コードに合わせ、混在している場合は最初に使われているものに統一していましたが、2.x からは lf に統一されます。

フォーマットの変更

よく使われそうな構文のフォーマットに変更があります。

function キーワードのあとの空白

https://github.com/prettier/prettier/pull/3903
https://github.com/prettier/prettier/pull/7516

関数式(関数宣言ではない)のfunctionキーワードのあとに空白が入るようになります。

// 1.x
(function() {});

// 2.x
(function () {});

ただし、ジェネレータは例外でfunction*のあとに空白を追加します。

// 1.x
const generatorFunc = function*() {};

// 2.x
const generatorFunc = function* () {};

新機能等

いくつかの新しい機能や改善があります。

babel-ts パーサーオプション

https://github.com/prettier/prettier/pull/6400

1.x では TypeScript のパースに typescript-estree のみを使っていましたが、 2.x からは Babel の TypeScript パーサーを使えるようになります。

ただし、parserオプションを指定せずに TypeScript のフォーマットを行った場合は typescript-estree を使ってパースします。Babel を使って TypeScript のパースを行う場合はオプションで明示する必要があります。

# typescript-estree を使ってパースする
prettier foo.ts

# Babel を使ってパースする
prettier --parser=babel-ts foo.ts

TypeScript 3.8 のサポート

https://github.com/prettier/prettier/pull/7631
https://github.com/prettier/prettier/pull/7764
https://github.com/prettier/prettier/pull/7804

TypeScript 3.8 から新しく追加された次の構文をサポートします。

CLI の改善

https://github.com/prettier/prettier/pull/7660

1.x では CLI にファイル名しか渡せませんでしたが、2.x からはディレクトリを渡せるようになります。

# 1.x
prettier "**/*.{js,jsx,ts,tsx,md,json,yaml,html}"

# 2.x
prettier .

非推奨機能の廃止

いくつかの非推奨機能が廃止になります。おそらくこれらの機能を使っている人は少ないので、ここではあまり触れません。詳しくは https://github.com/prettier/prettier/issues/4097 を読んでください。

多くのバグ修正

通常のマイナーバージョンアップと同様に、多くのバグ修正が含まれます。

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

高輪ゲートウェイでズンドコキヨシを作ってみよう!

2020年3月14日

半世紀ぶりに山手線に新駅開業!

おめでとうございます。

というわけで、本題に入ります。

何をするの?

今回は...

「Javascriptでズンドコキヨシみたいな高輪ゲートウェイを作成!」

です!

この時点では意味がわからないと思います。

では、実際の画像です。

image.png

こんな感じで、「高輪」「ゲート」「ウェイ」がランダムに配列から取り出されて、

高輪ゲートウェイが出たら成功!みたいなゲームです!

完全にズンドコキヨシですね!

実際にプレイしたい方はこちら

では、ソースコードです

ソースコード

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>高輪ゲートウェイ</title>
    <link rel="stylesheet" href="style.css">
    </style>
</head>
<body>
    <div class="takanawa">
        <p id="text"></p>
    </div>
    <script type="text/javascript" src="script.js"></script>
</body>
</html>

style.css
body{
    font-family:serif;
    background-color:#dddddd;
    color:#ffffff;
    margin:0;

}
.takanawa {
    position: relative;
    height: 40vh;
    background-color: #008000;
    text-align:center;
    font-size:20vh;
}
.takanawa p {
    width: 100%;
    text-align: center;
}
script.js
        const takanawa=["高輪","ゲート","ウェイ"] ///ズンドコ風配列
        for(let i=1;i<4;i++){
            let random = Math.floor( Math.random() * takanawa.length );///ランダム
            document.getElementById("text").innerHTML+=takanawa[random]///innerHTMLで挿入
        }

工夫した点

  • 実際のフォント(明朝体)に似せた
  • 色合いも似せた
  • ゲームとして成立させた
  • オリジナリティ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BMIが計算したい!!

let w =document.querySelector(".weight");
let h =document.querySelector(".height");
w = parseFloat(w);
h = parseFloat(h);
let result=document.querySelector(".result");
let btn =document.querySelector(".btn");
btn.addEventListener("click", function() {
    let r =w/(h*h);
    result.innerText = r;
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

知識ゼロから始めるTypeScript 〜クラス編〜

はじめに

基本編と関数編の続きです。

予習はこちらから:point_down:
知識ゼロから始めるTypeScript 〜基本編〜
知識ゼロから始めるTypeScript 〜関数編〜
知識ゼロから始めるTypeScript 〜クラス編〜 :point_left:今ココ

クラス編ではTypeScriptの便利機能がもりもりです。

どのような機能があるか見ていきましょう。

クラスの型定義

  • クラスのトップレベルで型宣言
  • constructor(関数)の引数にも型宣言
  • constructor(関数)の戻り値の型宣言は行わない(TypeScriptの言語仕様)
class Person {
  // クラスの型宣言
  name: string
  age: number
  // constructorの引数に型宣言
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  // クラスのメソッド(関数)に型宣言
  profile(): string {
    return `name: ${this.name} age: ${this.age}`
  }
}
let taro = new Person('Taro', 30)

console.log(taro.profile())  // name: Taro age: 30 

アクセス修飾子

TypeScriptではJavaScriptには無いアクセス修飾子が利用できる。

  • public:全体からアクセスできる
  • private:クラス内部からのみアクセスできる
  • protected:クラスを継承した小クラスからでもアクセスできる
class Person {
  // publicはアクセスの制約がない。省略できる。
  public name: string
  // クラス内部からのみアクセスできる
  private age: number
  // 継承した子クラスからもアクセスできる
  protected nationality: string

  constructor(name: string, age: number, nationality: string) {
    this.name = name
    this.age = age
    this.nationality = nationality
  }

  profile(): string {
    // ageはprivateなのでクラス内部のみアクセスできる
    return `name: ${this.name} age: ${this.age}`
  }
}

let taro = new Person('Taro', 30, 'Japan')

console.log(taro.name)  // Taro
console.log(taro.age)  // ageはprivateなのでコンパイルエラー
console.log(taro.profile())  // privateのageを含むメソッドなのでエラーになる

Personクラスを継承したAndroidクラスを作成してみる

class Android extends Person {
  constructor(name: string, age: number, nationality: string) {
    // 親クラスのconstructorメソッドを継承
    super(name, age, nationality)
  }
  profile(): string {
  // ageはprivateなのでアクセスできないのでエラーになる
  // nationalityはprotectedなのでアクセスできる
    return `name: ${this.name} age: ${this.age}, nationality: ${this.nationality} `
  }
}

constructorの便利な書き方

アクセス修飾子をconstructorメソッドの引数に書くことで、クラスの型宣言初期化が可能

この場合public修飾子は省略しない

以下sampe①sample②は同じ結果になる

sample①
class Person {
  constructor(public name: string, protected age: number) {}
}

const me = new Person('foo', 30)
console.log(me)  // Person { name: 'foo', age: 30 }
sample②
class Person {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}

const me = new Person('foo', 30)
console.log(me)  // Person { name: 'foo', age: 30 }

getterとsetter

アクセスを制御する時に便利。

  • getはアクセスだけできる
  • setは値を設定するときのみ実行される

privateに設定された変数はアクセス(書き換えも)できないが、アクセスだけしたい時(get)、値だけ書き換えたい時(set)に便利。

// * owner
//   * 所有者
//   * 初期化時に設定できる
//   * 途中で変更できない
//   * 参照できる

// * secretNumber
//   * 個人番号
//   * 初期化時に設定できる
//   * 途中で変更できる
//   * 参照できない

class MyNumberCard {
  // 慣習的にアンダースコアでリネームする
  private _owner: string
  private _secretNumber: number

  constructor(owner: string, secretNumber: number) {
    this._owner = owner
    this._secretNumber = secretNumber
  }

  get owner() {
    return this._owner
  }

  set secretNumber(secretNumber: number) {
    this._secretNumber = secretNumber
  }

  debugPrint() {
    return `secretNumber: ${this._secretNumber}`
  }
}

let card = new MyNumberCard('fooさん', 1234567890)

console.log(card.owner)  // fooさん
console.log(card.secretNumber)  // undefined
console.log(card.debugPrint())  // secretNumber: 1234567890

readonly修飾子

  • クラスのプロパティを読み取り専用にする時に利用する
  • TypeScriptを詳しく無い人向けにpublicは省略しない方が良いかも
class VisaCard {
  constructor(public readonly owner: string) {
    this.owner = owner
  }
}

let myVisaCard = new VisaCard('foo')

console.log(myVisaCard.owner)  // foo
myVisaCard.owner = 'buzz'  // コンパイルエラー

静的メンバの定義

  • newキーワードを使って毎回インスタンスを生成しなくてもアクセスできるようになる
  • 静的なメソッドも定義できる
class Me {
  static isProgrammer: boolean = true
  static firstName: string = 'Satoru'
  static lastName: string = 'Iguchi'

  static work() {
    // thisを付ける
    return `Hello ${this.firstName}`
  }
}

// クラス名.メンバ変数でアクセスできる
console.log(Me.isProgrammer)  // true
console.log(Me.work())  // Hello

クラスの継承

  • 既存のクラスを拡張して新たなクラスを作り上げる
  • 継承はextendsを使用する
  • 派生クラスのconstructorにはsuperの呼び出しが必要
class Animal {
  constructor(public name: string) {}

  run(): string {
    return 'I can run'
  }
}
// Animalクラスを継承
class Lion extends Animal {
  public speed: number
  constructor(name: string, speed: number) {
    super(name)
    this.speed = speed
  }
  run(): string {
    //super.run()で親のrunメソッドを呼び出す
    return `${super.run()} ${this.speed}km/h.`
  }
}

console.log(new Animal('Mickey').run()) // I can run
console.log(new Lion('Simba', 80).run()) // I can run 80km/h.

抽象クラスと抽象メソッド

  • 大規模なアプリケーション開発をする時に便利
  • 抽象メソッドは継承するクラスで実装しなければならないので、実装漏れを防ぐ。
  • 抽象化はabstractを使用する
  • 抽象メソッドは必ずオーバーライドをする必要がある
  • 抽象メソッドの宣言はシグネチャーと言う
// 抽象メソッドは抽象クラスの中でしか使えない
abstract class Animal {
  abstract cry(): string
}

class Lion extends Animal {
  cry() {
    return 'roar'
  }
}

// 抽象メソッドが定義されていないのでエラーになる
class Tiger extends Animal {}

インターフェース・リターンズ

  • 複数のクラスは継承できないが、インターフェースを使うと多重継承の様なことができる
  • 厳密に言うと継承の様なことができるのであくまで実装するという
  • interfaceで定義したものをimplementsで実装する
  • interfaceで定義したメソッドをimplementsで実装したクラスに定義しないとエラーになる
class Mahotsukai {}
class Souryo {}

// 2つのクラスは継承できない。
class Taro extends Mahotsukai, Souryo {} // エラー

// interfaceの定義
interface Kenja {
  ionazun(): void
}

interface Senshi {
  kougeki(): void
}

// implementsの定義
class Jiro implements Kenja, Senshi {
  // interfaceで定義したメソッドを定義しないとエラーになる
  ionazun(): void {
    console.log('ionazun')
  }
  kougeki(): void {
    console.log('kougeki')
  }
}

const jiro = new Jiro() // インスタンス生成

jiro.ionazun() // ionazun
jiro.kougeki() // kougeki

まとめ

TypeScriptを書き方気持ちが女神級に高まってきました

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

[Tips] Babylon.js でカメラの操作がスマホだと逆方向になってしまう対策

はじめに

Babylon.js では FreeCameraattachControl した際のカメラ操作が、
スマホで通常画面操作する際の向きとは逆になってしまっています。。:movie_camera: :arrow_down:

dest.gif

カメラの操作を逆方向にする

修正するには angularSensibility という値をネガティブにします :thumbsup:
camera.angularSensibility = -camera.angularSensibility;

念の為、Babylon.js の Playground にも動作確認出来るコードを置いておきました :airplane_small:
https://www.babylonjs-playground.com/#TX7GF8#1

上記の実行結果です :arrow_down:
dest1.gif

ちなみに、angularSensibility の値は調整することで、
カメラの回転速度を向上させたり低下させたりすることが出来るので、
操作感を調整したい際は値を上げ下げしてみてください :gear:
(angularSensibilityの値のデフォルトは 2000.0 です)

おわりに

カメラ操作の挙動に悩んでいる方の助けになれれば幸いです。。:pray:

参考リンク

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

.htaccessを使わず、S3から圧縮したJS,CSSを提供する

サーバーレスのアプリのため.htaccessを操作しづらい環境があったのでS3からこうやりました。
肝はアップロード時のcontent-encodingとcontent-typeの指定です。これがないとうまく読み込んでくれないです。

gzip --best -f public/js/app.js # app.js.gzが生成される -fつけないと2回目から上書きしてくれない
aws s3 cp public/js/app.js.gz s3://xxxx/ --content-encoding "gzip" --content-type "text/javascript"
<script src="https://xxxx.s3-ap-northeast-1.amazonaws.com/app.js.gz"></script>

参考
How to: Gzip compression of CSS and JS files on S3 with s3cmd
【Tips】S3 アップロード時に ContentType を指定する

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

NFCシールを活用して自動打刻ツールを(個人的に)作ってみた話

はじめに

今回はNFCシールを使用して会社の自動打刻システムを、完全に自分用で作ります!

要件定義

なぜつくるか?

現在、弊社の勤怠は、エクセルで管理されています。
実際の打刻フローとしては、

  1. 出社したら出社時刻をエクセル開いて手動で打刻
  2. 保存
  3. 退社するときに退社時刻をエクセル開いて手動で打刻
  4. 保存

。。。
毎日エクセルポチポチするの面倒すぎる!!!!!!!!!

作業自体も面倒なのに、何日か打刻を忘れるとまあ面倒臭いことになります。

せっかくIT企業にいるんだからいろいろスマートにやりたい...
ということで、今回の自動打刻システムの開発を決意しました。

どう作るか?

今回開発する自動打刻システムでは、NFCシールを活用していきます。

処理の流れとしては、

  1. NFCシールにスマホをかざして専用のWEBサイトを表示する
  2. WEBサイトから自動打刻システムにリクエストを投げる
  3. 打刻する

といった感じにしようかと思います。

NFCシールにスマホかざすのとリクエストを投げるところにWEBサイト表示をはさんでいるのは、
意図しない打刻を防ぐためです。

クライアント側で打刻される時刻の確認、出社なのか退社なのかの選択できた方が、
手順は増えますが確実かなあということでワンクッションはさみました。

また、現状では個人用なのでユーザーの識別は行いません。

どう使うか?

想定される使用フローは下記のとおりです。

  1. 出社したらデスクのどこかしらに貼ったNFCシールにスマホをかざす
  2. 表示されるWEBサイトで打刻時間を確認、出社ボタンを押す
  3. (退社時も同じ)

かなりスマート...!(な気がする)

使用技術

インフラはAWSの各サービスを利用します。

メイン処理の部分にはLambda、エクセルファイル(勤怠管理)はS3に保存します。

サーバーサイドにnode.js、WEBフロントエンドにはVue.jsを使用しつつ、HTTP通信はaxiosを使用します。

なぜNFCシールを使うか?

ただ使ってみたかった。

実は今回の開発、個人的にNFCシールを使ったアプリを作ってみたかったので、NFCシールありきで考えていました。

スマホかざすだけで打刻できるのステキじゃん...

なぜnode.jsか?

一番の理由は、Lambdaがnode.jsで書けるからです笑

他にも
java, ruby, pythonなどなど、いろんな言語で書くことができます。

フロントエンド開発に興味があり、日頃からJavaScriptを勉強しているので、
サーバーサイドもJavaScriptで書こう!ということでnode.jsを選びました。

なぜLambdaか?

Lambdaとは、AWSが提供するサーバレスアーキテクチャを構築するためのサービスです。

通常は、EC2インスタンスは常時存在し、アプリケーションも常時起動されているのですが、Lambdaはリクエストが送られてきたときのみインスタンスを生成→アプリケーションを実行→インスタンスを破棄という挙動をします。

今回作成するアプリは常時起動している必要もないので、コストを抑える意味でもLambdaが適しているのではないかと考えました。

なぜVue.jsか?

個人的に使い慣れているのでフロントはVue.jsで書きます。

と言っても、現在時刻を表示するのと、ボタンを二つ配置するだけなので全く難しいことはしません笑

強いて言えば、ローディングのアニメーションを作り込むくらいでしょうか...。

HTTP通信はaxiosを使用します。

設計

アーキテクチャ図

アーキテクチャの全体像としては、下記の通りです。

Image from Gyazo

構成は至ってシンプルで、
HTTPリクエストをAPI Gatewayで受け付け、Lambdaに投げます。

勤怠を管理しているエクセルはS3においておき、Lambdaからそのエクセルファイルに書き込みをしていく感じです。

処理が完了すると、処理結果をSuccessかFailでクライアントに通知します。

アプリケーションの実装

自動打刻システム(node.js)実装

コードの全貌は下記のGitHubリポジトリを御覧ください。

GitHub リポジトリ

エクセルファイルの操作には、「xlsx-populate」というライブラリを使用しました。

最初は「xlsx」というライブラリを使って実装していましたが、このライブラリだと処理をして、保存するとマクロや書式が無効化された状態になってしまうのでつかえず...。

個人的にはドキュメントも「xlsx-populate」のほうが読みやすかったです!

処理としてはファイルを読み込んで、シートを指定して、セルを指定して値を書き込み、保存しているだけです。

弊社の勤怠表は月ごとにシートが分かれているので、処理の頭でDateオブジェクトを生成して、得られた各値でシートや記入するセルを判定しています。

また、弊社は30分ごとに勤務時間として打刻できるので、打刻する時刻を30分単位に変換する関数を用意しています。

今後もっと本格的に運用していくことになったら、このあたりで拡張の余地がありますね。

実装で苦労したのは非同期処理とAWS S3からファイルを取得して、書き込んだものをアップロードし直す処理のところ。

const params = {
  Bucket: 'バケット名',
  Key: 'キー'
}

s3.getObject(params, (err, data) => {
}

上記のように記述すれば、指定されたバケットのオブジェクト(ファイル)を取得できて、data変数に格納されます。

また、アップロードするときは、

const params = {
  Bucket: 'バケット名',
  Key: 'キー',
  Body: 'アップロードしたいファイル'
}

s3.putObject(params, (err, data) => {
})

でアップロードできます!

ここがnode.jsの情報がなかなか転がってなくて苦労しました。

取得も書き込みも注意点としては、取ってきたり送信するためには、データ形式に気をつけなければなりません。

今回僕は、これらの処理の前後にエクセルファイルをバッファーに変換する処理をはさみ、変換したものをparams変数のBodyとしています。

クライアントサイド(vue.js)実装

クライアントサイド(WEB)はVue.jsで作りました。

最終的には静的サイトとしてビルドして、Netlifyでホスティングします。

こちらは特に難しいことはしていません。

UIはVuetifyを使ったので適当に作った割には整っています。

スクショですが、下記のようになりました。

Image from Gyazo

打刻すると、vue-loading-templateを使用したアニメーションが流れて、レスポンスが帰ってくるとアラートが表示されます。

Image from Gyazo

(若干左によってるのはスクショが下手だからです...笑)

インフラ環境構築

構築したもの


いよいよインフラの構築に入ります。

今回は、メインのAPIをLambdaで動かします。

勤怠表(エクセルファイル)はS3にアップロードしておき、Lambdaから読み取り、書き込みを行います。

HTTPリクエストの受け口として、APIGatewayを配置します。

ここに想定されるリクエストがとんできたら、それをトリガーにLambda関数が動く仕組みにしていきます。

また、クライアントサイド(WEBアプリ)はNetlifyという静的サイトのホスティングサービスを利用します。

Netlifyに関しては後日別記事で言及します。

ハマったポイント


私はインフラ超初心者なので、インフラ構築でかなりつまづきました...。

LambdaからS3のファイルをとってこれない

作成したLambdaに正しくロールを付与していなかったため、アクセス権限 is 何の状態が1時間くらい続きました...。


Lambda関数(メインAPI)が非同期処理になっていて肝心の処理を行う前にLambdaが終了してしまっていた

今回使用した「xlsx-populate」は非同期処理をすることが前提のライブラリです。

恥ずかしながら、node.jsだけでなく非同期処理の知識も乏しく、Lambdaはエラーなく終了するのに肝心の処理が実行できてない...。

という状態で約5日間潰しました。

エラー箇所の切り出しが下手だったなあと反省しています。

いろんな記事や書籍を読み漁りつつ、async awaitを駆使してなんとか解決しました。

CROS

実際にクライアントサイドからリクエストを投げる時にはまりました。

今までなーんとなくしか理解してなかったですが、これを機にしっかり学べました。

CROSに関しては別記事でまとめます。

実際につかってみた


実際の動画がこちら

明日から出社と退社が楽しみになりそう。

(ちょっと処理は遅いですが)個人的にほぼノンストレスに打刻できるようになったので満足です。

ただ、本当にきちんと勤怠をつけるためには小難しい会社のルールがあるみたいなので、そのうちきちんとしたものも作りたいです。

今のところはほぼ毎日きっちり定時に退社しているので細かい調整はそんなに必要なさそう...だと思ってます笑

おわりに

NFCさいこう!!!!!たのしい!!!!!

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

Trello風Webアプリケーションを作成してみた

作ったもの

最近、プログラミングから少し離れていたので、思い出すこともかねてTrello風のWebアプリケーションを作ってみた。

vue-trello - Google Chrome 2020_03_17 3_05_51.png

▼デモ( https://x-color.github.io/vue-trello
demo.gif

▼完成品のリポジトリ

使用技術

フロントエンド

フロントエンドはSPAとなっており、Vue.jsで実装している。

  • Vue.js: JavaScriptフレームワーク
  • Vuex: Vue.js用状態管理ライブラリ
  • Vue Router: SPA構築用のルーター
  • Vuetify: Vue.jsのマテリアルデザインコンポーネントフレームワーク
  • Vue.Draggable: ドラッグアンドドロップ処理用ライブラリ

バックエンド

バックエンドはAPIサーバーとなっており、Go言語で実装している。

  • Go言語
  • GORM: Go言語用ORMライブラリ
  • Echo: Go言語用Webフレームワーク
  • jwt-go: Go言語用JWTを扱うライブラリ

実装内容(カードを動かす処理)

今回のアプリケーションで実装したカードを動かす処理の概要を以下で紹介していく。

基本的な実装

今回はカードを動かす処理に、Vue.Draggableを用いているため、draggable タグで動かしたいものを囲むだけで実装可能。
以下の例は、fruits 配列をドラッグアンドドロップで自由に並び替える処理。

<template>
    <draggable v-model="fruits">
       <!-- ここの要素がドラッグアンドドロップ可能になる -->
       <div v-for="(v, i) in fruits" :key="i">{{ v }}</div>
    </draggable>
</template>

<script>
import draggable from 'vuedraggable'

export default {
    components: {
        draggable,
    },
    data() {
        return {
            fruits: [
                "apple",
                "banana",
                "cherry"
            ],
        }
    }
}
</script>

Vuexで管理しているデータを並び替える

今回の場合は、カードのデータや並び順をVuex内で管理しているため、Vuex内のデータを並び替える必要がある。
公式のREADMEに記載されている通り、computed内からVuexのstateを呼び出し、Setterを用いて更新することで対応可能。
シンプルな配列を並び替えたいときは、以下のようにするだけで並び変え可能。

<template>
    <draggable v-model="list">
       <div v-for="(v, i) in list" :key="i">{{ v }}</div>
    </draggable>
</template>

<script>
import draggable from 'vuedraggable'

export default {
    components: {
        draggable,
    },
    computed: {
        list: {
            get() {
                return this.$store.state.list
            },
            set(value) {
                this.$store.commit('updateList', value)
            },
        },
    },
}
</script>

今回作成したアプリでは、カードを並び替えた際に一部データの更新を行う必要があったので、以下のようにメソッドを呼び出し、データの更新処理を行ったあとにデータの移動を反映させる形にした。

computed: {
    lists: {
        get() {
            return this.getListsByBoardId(this.id); // list の配列を取得
        },
        set(value) {
            this.moveList(value); // list が移動した際に行う処理を実施
        },
    },
}

実際には、移動したデータを追跡し、順番を保持している変数値の更新とAPIサーバーとの通信などを実施している。

動かせるものを指定する

<draggable> で囲った要素は基本的にすべて、ドラッグ可能となってしまう。そのため、動かせないものを一緒にタグで囲わなければならない場合、動かしたいものを指定する必要がある。例えば今回の場合は、カードを追加するための「+」ボタンのカードを動かしたくなかった。

以下は失敗例のサンプル。<draggable>の中に動かしたいカードと動かしたくない「+」ボタンが入ってしまっている。そのため、このままだとボタンがドラッグ可能となってしまう。

<draggable v-model="lists">
    <v-col v-for="(list, i) in lists" :key="i" cols="auto">
        <card-list :id="list.id" />
    </v-col>

    <!-- カード追加ボタン -->
    <v-col cols="auto">
        <v-btn>
            <v-icon>mdi-plus</v-icon>
        </v-btn>
    </v-col>
</draggable>

これを改善したのが以下のサンプル。Vue.Draggableでは、draggable 属性を用いて、動かしたいものと動かしたくないものを対象のclass属性で判別することが可能。

<!-- dragable属性(draggable=".item")を付与 -->
<draggable v-model="lists" draggable=".item">
    <!-- ドラッグ可能にするためにclass属性(class="item")を付与 -->
    <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item">
        <card-list :id="list.id" />
    </v-col>

    <!-- itemクラスが付与されていないためドラッグ不可 -->
    <v-col cols="auto">
        <v-btn>
            <v-icon>mdi-plus</v-icon>
        </v-btn>
    </v-col>
</draggable>

上記の例では、draggable=".item"を用いて、itemクラスを付与されているもののみ移動可能としている。
これにより、「+」ボタンカードを除いたカードのみ移動可能とすることができる。

ドラッグ可能な箇所を指定する

先ほどのサンプルを再掲。
以下のコードだと、実はカード外部でドラッグ可能となってしまう。
ドラッグ対象が <v-col> となっているので、実際ドラッグしたい <card-list> 外部でもドラッグが可能となってしまい、UI的にカードではない部分でドラッグできてしまう。

<draggable v-model="lists" draggable=".item">
    <!-- ドラッグ対象は以下の要素となってしまう -->
    <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item">
        <!-- ドラッグしたいカード -->
        <card-list :id="list.id" />
    </v-col>

    <v-col cols="auto">
        <v-btn>
            <v-icon>mdi-plus</v-icon>
        </v-btn>
    </v-col>
</draggable>

改善した結果が以下のサンプルとなる。 handle 属性を用いて、ドラッグ判定を出す部分をclass属性で指定することが可能。

<!-- handle属性(handle=".handle")を付与 -->
<draggable v-model="lists" draggable=".item" handle=".handle">
    <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item">
        <!-- class属性(class="handle")を付与 -->
        <card-list :id="list.id" class="handle" />
    </v-col>

    <v-col cols="auto">
        <v-btn>
            <v-icon>mdi-plus</v-icon>
        </v-btn>
    </v-col>
</draggable>

上記の例では、handle=".handle"を用いて、handle クラスを付与されているものがドラッグ可能と判定している。これにより、<card-list> 内(カード)をドラッグした場合のみドラッグ可能とすることができる。

スムーズなドラッグアニメーションにする

デフォルトの移動時のアニメーションだと、動かしたというよりも瞬間移動した感じが出てしまう。そのため今回は、アニメーションを変更し、スムーズに動かした感じを出すこととした。

<!-- animation属性(:animation="300")を付与 -->
<draggable v-model="lists" draggable=".item" handle=".handle" :animation="300">
    <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item">
        <card-list :id="list.id" class="handle" />
    </v-col>

    <v-col cols="auto">
        <v-btn>
            <v-icon>mdi-plus</v-icon>
        </v-btn>
    </v-col>
</draggable>

animation属性を用いてアニメーションの時間を変更している。今回は時間を多めにとることにより、動いている感じを出している。アニメーションも属性を一つ追加すればよいだけなのでとても簡単にできる。

最後に

久々のプログラミングだったので、細かなところなどを結構忘れていて、実装に時間がかかってしまった。

また、初めてドラッグアンドドロップを実装したが、Vue.Draggableを用いることで簡単に実装することができた。ドラッグアンドドロップを実装したい場合はとてもおすすめ。

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

GoとVue.jsでTrello風Webアプリケーションを作成してみた

作ったもの

最近、プログラミングから少し離れていたので、思い出すこともかねてTrello風のWebアプリケーションを作ってみた。

vue-trello - Google Chrome 2020_03_17 3_05_51.png

▼デモ( https://x-color.github.io/vue-trello
demo.gif

▼完成品のリポジトリ

使用技術

フロントエンド

フロントエンドはSPAとなっており、Vue.jsで実装している。

  • Vue.js: JavaScriptフレームワーク
  • Vuex: Vue.js用状態管理ライブラリ
  • Vue Router: SPA構築用のルーター
  • Vuetify: Vue.jsのマテリアルデザインコンポーネントフレームワーク
  • Vue.Draggable: ドラッグアンドドロップ処理用ライブラリ

バックエンド

バックエンドはAPIサーバーとなっており、Go言語で実装している。

  • Go言語
  • GORM: Go言語用ORMライブラリ
  • Echo: Go言語用Webフレームワーク
  • jwt-go: Go言語用JWTを扱うライブラリ

実装内容(カードを動かす処理)

今回のアプリケーションで実装したカードを動かす処理の概要を以下で紹介していく。

基本的な実装

今回はカードを動かす処理に、Vue.Draggableを用いているため、draggable タグで動かしたいものを囲むだけで実装可能。
以下の例は、fruits 配列をドラッグアンドドロップで自由に並び替える処理。

<template>
    <draggable v-model="fruits">
       <!-- ここの要素がドラッグアンドドロップ可能になる -->
       <div v-for="(v, i) in fruits" :key="i">{{ v }}</div>
    </draggable>
</template>

<script>
import draggable from 'vuedraggable'

export default {
    components: {
        draggable,
    },
    data() {
        return {
            fruits: [
                "apple",
                "banana",
                "cherry"
            ],
        }
    }
}
</script>

Vuexで管理しているデータを並び替える

今回の場合は、カードのデータや並び順をVuex内で管理しているため、Vuex内のデータを並び替える必要がある。
公式のREADMEに記載されている通り、computed内からVuexのstateを呼び出し、Setterを用いて更新することで対応可能。
シンプルな配列を並び替えたいときは、以下のようにするだけで並び変え可能。

<template>
    <draggable v-model="list">
       <div v-for="(v, i) in list" :key="i">{{ v }}</div>
    </draggable>
</template>

<script>
import draggable from 'vuedraggable'

export default {
    components: {
        draggable,
    },
    computed: {
        list: {
            get() {
                return this.$store.state.list
            },
            set(value) {
                this.$store.commit('updateList', value)
            },
        },
    },
}
</script>

今回作成したアプリでは、カードを並び替えた際に一部データの更新を行う必要があったので、以下のようにメソッドを呼び出し、データの更新処理を行ったあとにデータの移動を反映させる形にした。

computed: {
    lists: {
        get() {
            return this.getListsByBoardId(this.id); // list の配列を取得
        },
        set(value) {
            this.moveList(value); // list が移動した際に行う処理を実施
        },
    },
}

実際には、移動したデータを追跡し、順番を保持している変数値の更新とAPIサーバーとの通信などを実施している。

動かせるものを指定する

<draggable> で囲った要素は基本的にすべて、ドラッグ可能となってしまう。そのため、動かせないものを一緒にタグで囲わなければならない場合、動かしたいものを指定する必要がある。例えば今回の場合は、カードを追加するための「+」ボタンのカードを動かしたくなかった。

以下は失敗例のサンプル。<draggable>の中に動かしたいカードと動かしたくない「+」ボタンが入ってしまっている。そのため、このままだとボタンがドラッグ可能となってしまう。

<draggable v-model="lists">
    <v-col v-for="(list, i) in lists" :key="i" cols="auto">
        <card-list :id="list.id" />
    </v-col>

    <!-- カード追加ボタン -->
    <v-col cols="auto">
        <v-btn>
            <v-icon>mdi-plus</v-icon>
        </v-btn>
    </v-col>
</draggable>

これを改善したのが以下のサンプル。Vue.Draggableでは、draggable 属性を用いて、動かしたいものと動かしたくないものを対象のclass属性で判別することが可能。

<!-- dragable属性(draggable=".item")を付与 -->
<draggable v-model="lists" draggable=".item">
    <!-- ドラッグ可能にするためにclass属性(class="item")を付与 -->
    <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item">
        <card-list :id="list.id" />
    </v-col>

    <!-- itemクラスが付与されていないためドラッグ不可 -->
    <v-col cols="auto">
        <v-btn>
            <v-icon>mdi-plus</v-icon>
        </v-btn>
    </v-col>
</draggable>

上記の例では、draggable=".item"を用いて、itemクラスを付与されているもののみ移動可能としている。
これにより、「+」ボタンカードを除いたカードのみ移動可能とすることができる。

ドラッグ可能な箇所を指定する

先ほどのサンプルを再掲。
以下のコードだと、実はカード外部でドラッグ可能となってしまう。
ドラッグ対象が <v-col> となっているので、実際ドラッグしたい <card-list> 外部でもドラッグが可能となってしまい、UI的にカードではない部分でドラッグできてしまう。

<draggable v-model="lists" draggable=".item">
    <!-- ドラッグ対象は以下の要素となってしまう -->
    <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item">
        <!-- ドラッグしたいカード -->
        <card-list :id="list.id" />
    </v-col>

    <v-col cols="auto">
        <v-btn>
            <v-icon>mdi-plus</v-icon>
        </v-btn>
    </v-col>
</draggable>

改善した結果が以下のサンプルとなる。 handle 属性を用いて、ドラッグ判定を出す部分をclass属性で指定することが可能。

<!-- handle属性(handle=".handle")を付与 -->
<draggable v-model="lists" draggable=".item" handle=".handle">
    <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item">
        <!-- class属性(class="handle")を付与 -->
        <card-list :id="list.id" class="handle" />
    </v-col>

    <v-col cols="auto">
        <v-btn>
            <v-icon>mdi-plus</v-icon>
        </v-btn>
    </v-col>
</draggable>

上記の例では、handle=".handle"を用いて、handle クラスを付与されているものがドラッグ可能と判定している。これにより、<card-list> 内(カード)をドラッグした場合のみドラッグ可能とすることができる。

スムーズなドラッグアニメーションにする

デフォルトの移動時のアニメーションだと、動かしたというよりも瞬間移動した感じが出てしまう。そのため今回は、アニメーションを変更し、スムーズに動かした感じを出すこととした。

<!-- animation属性(:animation="300")を付与 -->
<draggable v-model="lists" draggable=".item" handle=".handle" :animation="300">
    <v-col v-for="(list, i) in lists" :key="i" cols="auto" class="item">
        <card-list :id="list.id" class="handle" />
    </v-col>

    <v-col cols="auto">
        <v-btn>
            <v-icon>mdi-plus</v-icon>
        </v-btn>
    </v-col>
</draggable>

animation属性を用いてアニメーションの時間を変更している。今回は時間を多めにとることにより、動いている感じを出している。アニメーションも属性を一つ追加すればよいだけなのでとても簡単にできる。

最後に

久々のプログラミングだったので、細かなところなどを結構忘れていて、実装に時間がかかってしまった。

また、初めてドラッグアンドドロップを実装したが、Vue.Draggableを用いることで簡単に実装することができた。ドラッグアンドドロップを実装したい場合はとてもおすすめ。

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

「Scrapboxにコードを書いて実行する」を説明してみる

この記事は株式会社クロノス「~2020年春~勝手にやりますアドベントカレンダー」の12日目の記事です!

はじめに

今回の投稿はScrapboxのUserScriptテンプレートまとめに引き続き、Scrapboxネタです。(よっぽど好きだということです)

まずは以下のリンク先ページをご覧ください。
Scrapboxにコードを書いて実行する

なんとっ Scrapbox上に書いたJavaScriptのコードが実行されてるじゃないですか! すごい。UserScriptととはまた違ったことやってる!
自分もやりたいぃってことで、どうやって実現しているのか調べてみました。

Vue.jsタグにさせてもらいましたが、Vue.js自体は実演で使わせてもらっただけで、チュートリアルの域を出ていないです。ご注意ください。

知れること・知れないこと

知れること

  • Scrapbox上にコード記法で記載したJavaScriptのコードを実行して表示させる方法

知れないこと

  • Scrapbox上に書いたJavaScript以外(コンパイルが必要になるような言語)を実行させる方法
  • Scrapbox上に実行結果を埋め込む方法

仕組み

どうやって実現させているか紐解く鍵は、下のコードを実行のリンクの中身にあります。
遷移した後のURLを見るのでも構いませんが、
https://[アカウント名].github.io/[リポジトリ名]/?code=[ほにゃらら]/[ほにゃらら].jsとなっているはずです。

Tip1 : CDN

(他にもやりようはあるかとは思いますが)各種プログラムを実行するために、お馴染みのCDN(Contents Delivery Network)を利用します。

Tip2 : GitHub Pages

urlを見てもらったら分かるように、github.ioの静的ホスティングサービスであるGitHub Pagesを利用します。
(実際は静的ホスティングサービスであれば、何であっても構わないと思うのですが、今回は参考元のやり方のGitHub Pagesで説明させてもらいます)

Tip3 : ScrapboxのAPI

公式で大々的に説明があるAPIではありませんが、コード記法で書いた内容にアクセスできるAPIがあります。
形式 : https://scrapbox.io/api/code/[プロジェクト名]/[ページ名]/[付けたファイル名]
画面収録 2020-03-13 22.15.03.mov.gif
普段は上記のようにコード記法で書いたコードをコピーするために利用されます。
(APIのURLにアクセスすればブラウザ上にコードの中身が表示されます)

「Scrapboxにコードを書いて実行する」の仕組みを、一言で(ざっくり)言うなら
静的ホスティングしたサイトにScrapboxで書いたコードを返却するAPIを渡して、コード記法で書かれた該当のコードを書き込み、CDNを利用して実行する
となります。

実装方法〜〜ユースケースとともに〜〜

本家Scrapboxにコードを書いて実行するのように、動きのあるコードの方が、面白味があるとは思いますが、今回はVue.jsのチュートリアルのコードを実行してみます。

GitHub Pagesの準備(簡易説明)

  • Github上にリポジトリを作成する。
  • index.htmlファイルを作成する。
  • branch : gh-pagesを作成する
  • 後はrepositoryのURLにアクセスでindex.htmlの内容が表示される!

コードの全容

私が今回説明で使っているソースはこちらです。
フォルダ構成

├── index.html
└── index.js

Github上に置くファイルはGithub Pagesで準備したindex.htmlindex.jsだけ!!(簡単)
README.mdファイルは省略して記載しています。

index.js (ScrapboxのAPIのURLをGetパラメータから取得して、画面に読み込ませる)

ここが一番肝要なところですが、
本家様のコードをほぼほぼ流用しています。基本、本家様のコードを参照でお願いします。:bow:

本家様コード
ちなみにgh-pagesブランチとmasterブランチで内容が違っていたので、見比べて勉強させてもらいました。(jsだけでなくstyle読み込んだりなどなど)

今回Vue.jsのコードを動かしたい関係で、jQueryありきの部分については少し変更を加えています。
今回のindex.js

index.js
// jQueryお馴染みの「$」の部分
$(function(){

// 純粋なJavaScriptに変更
window.addEventListener('DOMContentLoaded', function(){

コードを見てもらったらわかりますが、JavaScriptでアクセスしたURLを解析して、
?code=の=以降の値(Getパラメータの値)を読み取って、その値をscriptタグとして追加する実装になっています。
(実行する際は?code=[ScrapboxのAPIのURL]としてアクセスしてください)

index.html (必要なCDNの読み込みや簡易のレイアウト記載)

今回はこんなかんじ。

index.html
<!DOCTYPE html>
<html>

<head>
    <title>Scrapboxでプログラミングの勉強をする</title>
    <!-- bulma -->
    <link href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css" rel="stylesheet">
    <link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>

<div id="app">
    <app-template></app-template>
</div>

<!-- vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="./index.js"></script>

</html>

基本CDNと先ほどのindex.jsを読み込んでいるだけ!
CDNはここで自分の利用したいものをカスタマイズしましょう。
今回はVue.jsと表示の調整でBulmaを書きました。

ここまでで下準備は完了!

Scrapboxにコードを書いて実行

では、Scrapboxでコードを書いて実際に実行してみましょう。
コードはVue.jsのチュートリアルを参考に書いています。
Githubに挙げているコードは変更しない方向で、<app-template></app-template>をカスタム要素として利用した書き方に調整しています。

コード記法は拡張子「js」でファイル名書くようにしてください。

  • Hello World
 const Messages = {
     template:
      `<div class="hero">` +
      '<div class="hero-body has-text-centered">{{ message }}</div>' +
      '</div>',
     data: function() {
       return {
         message: 'Hello World!!',
       }
     }
 }

 new Vue({
   el: '#app',
   components: {
     'app-template': Messages
   }
 })

実行の様子。上部は切り取っていますが、実行すると新規タブが開きます。
画面収録 2020-03-14 21.33.44.mov.gif

※押しているボタンについてはおまけで後述します。やってることはhttps://[アカウント名].github.io/[リポジトリ名]/?code=[ScrapboxのAPIのURL]にアクセスするだけです。

Scrapboxのコードを実行しているだけなので、Scrapboxのコードを書き換えると、当然結果も変わります。
画面収録 2020-03-14 21.55.59.mov.gif

他の例も用意したので、載せておきます。

  • v-for
const BlogPost = {
    template:
        `<div class="hero">` +
        '<div class="hero-body has-text-centered">' +
        '<div v-for="post in posts" v-bind:key="post.id">' +
        '{{post.title}}' +
        '</div>' +
        '</div>' +
        '</div>',
    data: function () {
        return {
            posts: [
                { id: 1, title: 'My journey with Vue' },
                { id: 2, title: 'Blogging with Vue' },
                { id: 3, title: 'Why Vue is so fun' }
            ]
        }
    }
}

new Vue({
    el: '#app',
    components: {
        'app-template': BlogPost
    }
})

画面収録 2020-03-14 22.26.39.mov.gif

  • ボタン
 Vue.component('app-template',{
    data: function(){
        return {
            count1: 0,
            count2: 0
        }
    },
    template:
        '<div class="hero">'
        + '<div class="hero-body has-text-centered">'
        + '<button v-on:click="count1++">You clicked me {{count1}} times.</button>'
        + '<br>'
        + '<button v-on:click="count2++">You clicked me {{count2}} times.</button>'
        + '</div>'
        + '</div>'
 })

 new Vue({ el: '#app' })

画面収録 2020-03-14 22.28.26.mov.gif

アクセスするたびに同じ画面だけど違う結果が出てくる
というのはなかなかに面白いものです。
上記の3つのコードを続け様で実行した場合↓
画面収録 2020-03-15 0.32.54.mov.gif

どういった時に使える?

本家にも書いてありますが「プログラミング体験」として抜群です。
自分で勉強するにしても人に教えるにしても、文面と実行できるコードを共存して管理できるというのは、利点が多いのではないでしょうか。(いろいろな使い方ができそうです)

注意

言わずもがな、自分のコードは自分のGitHub Pages(あるいは別の静的ホスティングサービス)にアクセス・実行するようお願いします。

といっても他人のページだとCDNのカスタマイズもできないですし、自分で用意した方がいろいろ可能性が広がって楽しいと思います。

おまけ

開いているページ(前回はカードと書いていた)上のコード記法にアクセスするAPIを抽出して、https://[アカウント名].github.io/[リポジトリ名]/?code=[ScrapboxのAPIのURL]を開くUserScript作っているので、参考で載せておきます。
(今までのgif画像でも使っていました)

ScrapboxのUserScriptの詳しい説明については前回記事ScrapboxのUserScriptテンプレートまとめScrapboxのヘルプ参照ください。

下準備

前回記事の番外テンプレート(1)を作成するために、画像を用意して1つページを作ります。
スクリーンショット 2020-03-15 1.20.05.png

UserScript

UserScriptとはこんな感じ。

script.js
const GITHUB_REPOSITORY = 'https://[githubユーザ名].github.io/[リポジトリ名]';
const SCRAPBOX_URL = 'https://scrapbox.io';
const $appRoot = $('#app-container');
$appRoot.on('click', 'img[class="icon"]', e => {
    const $icon = $(e.target).closest('img[class="icon"]');
    const iconName = $icon.attr('title');
    // 「programming」アイコンの場合に処理を実行
    if (iconName === 'programming') {
        let codeUrl;
        $('a').each(function (index, atag) {
            let hrefStr = String($(atag).attr('href'));
            // getパラメータの作成
            if (hrefStr.startsWith('/api/code') && hrefStr.endsWith('js')) {
                codeUrl = (codeUrl == null) ?
                    'code=' + SCRAPBOX_URL + hrefStr
                    : codeUrl + '&code=' + SCRAPBOX_URL + hrefStr;
            }
        })
        console.log('呼び出し先コードのURL :' + codeUrl);
        window.open(GITHUB_REPOSITORY + '?' + codeUrl);
        return false;
    }
});

これで、Scrapboxの[programming.icon]アイコン記法)と書かれた場所をクリックすれば、プログラムの実行(条件を満たした状態でGitHub Pagesにアクセス)できます!!

※ScrapboxのUserScriptは自分にしか効かない(Scrapboxのヘルプ参照)ので、Publicで公開しているプロジェクトにおいて、閲覧者に実行してもらいたい場合等は、リンクをScrapbox上に書くようにしてください。

おわりの余談

前回のブログを書いていた頃含め、1年以上Scrapboxを書いてきたわけですが、アドベントカレンダーやろう! ってなったときも、内容考えるのすごく楽でした。
Scrapboxは項目と項目をつなげることで新たな発想が生まれたり、思考がクリアになったりと、使うほどにいろんな効果があるような気がしています。(個人の感想)

ちなみに……
Scrapboxでアウトプットするというのも一つですが、私個人として「Scrapboxの内容をまとめてブログに書く」使い方はこれからもしていくと思います。(私にとってScrapboxはネタ帳の色合いが濃いです)

以上です!

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

エンジニアの貴方必見

https://www.reiwarss.com/OpenProject

Top tags
python
swift
javascript
go
C
C++
C#
Ruby
TypeScript
PHP

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

CSS設計・Sassディレクトリ管理の標準化(CROCSS)

はじめに

HTML+CSSコーディングにおいて、Sass管理ディレクトリを標準化する方法を紹介します。
CSS設計は、サイト種別やプロジェクト規模、分業の有無や人数によっても最適解が異なります。
この仕組みは、様々な設計を同じロジックで受け入れることによって、CSSコードの管理効率を画一的に最大化する狙いのものです。
コーポレート・ポータル・ブログ・EC・LP・管理画面…など、様々な種別のサイトのCSSを、同じ仕組みで設計して管理できるようになります。

前提事項など

  • SassなどのCSSプロプロセッサの使用を前提とします。
  • 本記事の一部は、後で見られるよう別記事として切り出しています。(?のマークのもの)
  • この記事は標準化ノウハウ公開の一環として書いています。
    仕組みの概要や前提事項などについては「UltimateCoding 概要・前提事項」のエントリをご確認ください。

セクション一覧

  1. 全体の把握
  2. 仕組みの概要
  3. CROCSSの標準分類
  4. 物理化と実例
  5. 実用にあたって
  6. 使用感や設計・実例
  7. その他・考え方など
  8. さいごに

1・ 全体の把握

まずは全体像の説明です。
今回の仕組み(CROCSS / クロックス)は、過去に公開したHCDCモデルHTML Partsを実務利用するための具体的なノウハウとなります。HCDCモデルとHTML Partsは、以下の図のように制作行動の "要所" において、設計の在り方を示すガイドとして働きます。

コーディング全体の流れと対応する手法
そして今回紹介するCROCSSは、これらの概念をSassファイルの管理ディレクトリに変換し、概念と実務をスムーズに連動させる役割を担います。

過去記事は先にお読み頂いても良いですし、この記事からのリンクで局所的にお読み頂いても問題ありません。
もっと詳しく全体を把握したい場合は、記事内の「よりよい把握のための図解」や「著名な設計手法10個との概念比較」をご覧ください。

過去の記事

直接関連するもの

論拠や背景

2・ 仕組みの概要

基本ロジック

この仕組みの基幹にあるのは「分類の乗算」です。
以下のように2つの分類を掛け合わせることで、Sassディレクトリの主たる構造をつくります。分類-1,2は、CROCSS標準のものがあります。この記事ではそれらを中心に説明します。
基本ロジック図解
また、このロジックは思考の幅を広げ、構造の幹をつくるためのものです。隅々にまで適用しなければならないような「強制力」はありません。設計の枝葉の部分においては管理効率を最優先してください。

物理化メソッド

つぎに、実際にSassディレクトリを構築するまでの流れを図説します。
以下は、前述の「基本ロジック」にCROCSSの標準分類をセットして全体の流れを把握しやすくしたものです。
分類-1には、制作者が自然と判断できる分類を使い、分類-2には、HCDCモデルのデータ分類を使います。
Sassファイルを作成するまでのステップ
そして、分類-1の中に分類-2を平等に設置することで概念上の「型枠」をつくり、その中から必要なものを物理化(ディレクトリ化)します。
サイト種別ごとのガイドや、ディレクトリ構造の実例、使用感は後のセクションにて記載します。

このアプローチにより、以下の3つの効果・特徴を得ます。
リストをクリック or タップで展開できます


1 ・ 拡張しやすさの確保
この物量を見ると、反射的に「こんなに沢山のディレクトリは制御しきれない」とか、「もっと少ない方がよいに決まっている」と感じるかもしれません。
しかし、人は物量や階層の深さによって混乱するのではありません。人が混乱するのは「情報の捻れ」や「曖昧さ」「不整合」などが発生している時です。
 
最終的に物理化するものは絞られます。最大枠を概念側に置いておく事で、役割が欲しくなった時にすぐに拡張できるようにしておきます。


2 ・ カスタマイズ性
2種類の分類は、中身を変えることで設計を大きく変更・拡張できます。
例えば図中の「分類-1」を、FLOCSSの第一階層に変更しても同じように乗算できます。
 
この場合、当然FLOCSSではなくなりますし、CROCSS標準でも無くなりますが、新たな設計を試せます。
また同様に、標準として定める全ての文言や役割は追加・変更・削除しても問題ありません。


3 ・ 雛形作成と設計切り替え
この仕組みは雛形化しやすくなっています。特定のサイト種別のために作ったディレクトリ構造でも、概念枠によっていつでも拡張・縮小して多種別対応できます。
 
毎回最初から同じような構成を作る必要はありません。雛形を作成し、ブラッシュアップしていけばそれだけで業務標準化・効率化になります。

対応範囲

記事作成時点での実務利用実績(実際に対応した種別)は以下の通りです。

コーポレートサイト / ポータルサイト / サービスサイト / システム管理画面 / ブログサイト / ECサイト(オンラインショップ) / ランディングページ(LP) / イントラサイト

上記以外の実績はありませんが、おおよそいずれかに似た属性となるため問題なく使えるでしょう。

3・ CROCSSの標準分類

CROCSS標準の分類について説明します。
標準となるのは以下の2つです。

  • 【分類-1】:スコープ分類 … 4つの対象範囲による分類
  • 【分類-2】:データ分類 … 4つのデータ属性による分類

【分類-1】 スコープ分類

分類の1つ目は、多くの制作者にとってすぐに判断できるものを使います。
以下の2つの事柄によって、4つの項目を定義します。

  1. 一般的な制作ステップ
  2. ページ内での所属区域

1. は、「下地を整えてから枠組みをつくり、中にコンテンツを入れていき、各ページの固有の上書きをおこなう」といった、初期制作時における一般的な制作フローです。
2. は、対象が「ページの主たる情報」なのか「サイトの枠組み」に使うものなのか。といった判定で、制作者であれば恐らく誰もがすぐに特定できます。

この2つを組み合わせると、下地(Base)、共通の枠組み(Frame)、コンテンツ(Contents)、ページ(Pages)という4種になります。物理化した時に、それぞれのディレクトリ内部に格納したものは、その分類の役割が果たす範囲(スコープ)でしか使いません。

スコープ分類とレイヤーの図

また、これら4つのスコープは、概念によるレイヤー構造を持ちます。これらの関係は「下にあるものは上で使える」「上にあるものは下を上書きできる」というシンプルなものです。
このルールによってCSS管理に法則と秩序を与え、まずは大きな区分けでの影響分離をおこないます。

1・スコープ分類の詳細説明

詳細については長くなるため別記事に記載しています。
※まずは全容を把握したい場合には、後からご覧頂いても問題ありません。

? スコープ分類について

2・ディレクトリ化

スコープ分類による、最終的なディレクトリの構成は以下のようになります。

  • base/  … サイトの下地・土台・基底
  • frame/  … サイト全域で共通利用するような枠組み部分
  • contents/  … ページの主たる情報や機能、入力操作のための領域
  • pages/  … 全てを包括する最も大きな概念

3・カスタマイズ

何かが足りない場合や、他の英単語の方が馴染みが良い場合は、自由に追加・変更してください。例えば、theme/skin/templates/などの追加、contents/object/にした方がプロジェクトに馴染む。といった事が考えられます。

【分類-2】 データ分類

分類の2つ目は、「HCDCモデル図」の4つのデータ分類を使用します。
HCDCモデル図
このうち、左半分(Block , Parts)は「HTML Parts」による粒度分類・定義をそのまま使います。右半分(Structure , Utility)はCROCSS側でガイドを定めます。

1・データ分類の詳細説明

詳細については長くなるため別記事に記載しています。
※まずは全容を把握したい場合には、後からご覧頂いても問題ありません。

? データ分類について

2・ディレクトリ

これらのデータ分類を全て展開すると、以下のような構造になります。

  • block/
    • ※下層は適宜設置
  • parts/ (HTML Partsにより粒度分類)
    • module/
    • component/
    • element/
  • structure/
    • ※下層は適宜設置
  • utility/
    • ※下層は適宜設置

この構造は、最終的に「分類-1」の4つのスコープ(Base・Frame・Contents・Pages)内部に平等に設置でき、実際に物理化して利用する時にはそれぞれのスコープにおいて統治します。

3・カスタマイズ

何かが足りない場合や、他の英単語の方が馴染みが良い場合は、自由に追加・変更してください。
例えばwidget/vender/などの追加。小規模なサイトであればparts/が丸ごと不要。といったケースも考えられます。逆に、今回不要だったものが、次のプロジェクトでは必要になる。という事もあるでしょう。

4・ 物理化と実例

CROCSS標準の分類2つが把握できれば、あとは物理化するだけです。
方法は概要に記載した通り、まずは「スコープ分類」と「データ分類」の2つの概念を掛け合わせて「物理化のための型枠」を用意します。
物理化のための
作成した「型枠」は概念的なものであるため、一旦資料に残しておきます。テキストファイルやMarkDownでの箇条書き、Excelによる資料などで充分です。
何か問題があれば分類を見直し、常に概念と物理がスムーズに繋がるよう再設計します。

5・ 実用にあたって

部品(Sassファイル)の保存方法

Sassファイルには部品と同じ名前を与えます。
大抵の場合はHTML要素のclass属性の値で与えた名前になりますが、id属性によるスタイリングを許可する場合はidの値も対象になります。ファイルを作成する時は、全てのディレクトリを通して同一名のファイルは作成しないよう注意します。
Sassファイルを作成したら、対象に関連する部品や効果は、そのファイルの内で影響が収まるようにスタイリングします。
※スタイリングのパターンBか、パターンCのどちらかになります。

プレフィックスやサフィックス

接頭辞(プレフィックス)や接尾辞(サフィックス)に関しては、制作レギュレーションや実務用のコーディングガイドなどで自由に定めて下さい。
Layoutに関しては「命名重複回避」を主たる理由として、l-などのプレフィックス付与することを推奨していますが、強制力を持つルールではありません。

ページの識別・特定方法の問題

部品主体のコーディングをおこなう場合は、常に「ページ識別」の問題がつきまといます。
ディレクトリ名やファイル名などを、識別子としてbodyのclassに記述すると判定しやすくなりますが、全て手動でテキスト入力するのは面倒です。
そこで、こういったclassを自動出力する「bodyclass.js」を同グループで作成しています。
現在のところjQuery依存ではありますが、必要に応じてご自由にお使い下さい。

6・ 使用感や設計・実例

コーディング時の使用感

実際にコーディングする時の使用感は以下のようなものになります。
リストをクリック or タップで展開できます


ファイル管理
「スコープ分類×データ分類」によるディレクトリ構造は、いわば「部品の片付け先」をあらかじめ用意するようなものです。いざという時に引き出しがあればすぐに収納できますが、なければ引き出しを作るところからはじめなくてはなりません。
 
少し考えれば置き場所が見つかる・すぐに増やせる。そして、Sassファイルの置き場所で設計内容をある程度伝達・把握できるようになります。


CSS設計
CSS設計を柔軟に切り替えて使えます。部品は、グローバルに再利用するかどうかで別途部品化(=新規Sassファイル化)するかが決まります。
 
命名や単語連結手法(いわゆる命名規則やBEMのような手法)は普段慣れたものを使えます。また、プロジェクトごとに最適なものに入れ替えて使用することも可能です。


マークアップとスタイリング
Structureをうまく使うと「弁当箱を間仕切りして具材を詰めるように」コーディングできます。
 
Block内部のホワイトスペースはStructure側で制御できるため、主要な情報の部品(Component等)に余計な余白や位置決め用のスタイルを与えることなく、それぞれの責任範囲を分離してコード管理できます。
また、レスポンシブの制御もキャンセルを多用しなくても済むようになるでしょう。


レギュレーション
業務を標準化するには、最終的にHTMLの書き方や、部品ごとのマークアップ方針・レギュレーションが必要になります。これについては、また別の機会に紹介します。


命名の雛形化
こういったSassディレクトリ構築の取り組みをおこなう中で、その現場やチームで使用する「概念の型枠」は最適化され、次第に独自の型が定まっていきます。
この時、よく使う部品に固定の命名を与えて雛形化しておくと、デザインの後入れ(Design Injection)によるコーディングがおこなえます。
 
このような考え方にご興味があれば、「DI-CODING」のページをご覧ください。(1枚物の簡単なページで、もう少しだけ詳しく説明しています)

サイト種別ごとの管理設計

サイトの種別ごとに、どのように管理できるのかの概要を記載します。
リストをクリック or タップで展開できます


1 ・ 情報発信系サイト
多くの情報発信系サイトにおいて一般的な使い方は、BaseでHTML要素のデフォルトスタイルを整え、ContentsとFrameにてそれぞれの専用部品を管理。条件的な上書きをPagesで一元管理する。といったものです。
 
コーポレート系やブランディング系など、一般的な情報発信系サイトであればこの方式で対応できます。
小さい規模のサイトであれば、Blockの粒度のみでサイトが完成するでしょう。


2 ・ システム管理画面系サイト
システム管理画面などにおいて、サイト全域で小さな部品を再利用する場合はbase/にて部品を管理します。
これにより、Frame領域とContents領域とで同じ部品を利用できます。
CSSフレームワークやテーマなどの「既製部品」を利用する場合も同様です。もし、こういったものを使う場合は、vender/などで丸ごと一元管理すればよいでしょう。
 
逆に、Contents部分のみに「既製部品」を使いたい場合(Frameでは一切使わないと明示したい場合)は、contents/vender/などで管理します。


3 ・ ECサイト
カタログタイプのECサイトの場合、多くの中規模部品を再利用する事になります。
例えば、カテゴリ別の商品一覧や、おすすめ商品、ランキングなど、同じものを異なるページで再利用することが多くなるでしょう。
この時、部分的な機能や表示の塊をmodule/粒度のものとして管理できます。


4 ・ ランディングページ
ランディングページ(LP)は、プロジェクトによってはFrameそのものが不要かもしれません。
この場合、全てをContentsとして作ることになるでしょう。将来的に顧客の要望によってページが追加されれば、最初は不要と思っていたFrameが必要になる(簡易なヘッダーとフッターを追加する)事もあり得ます。


5 ・ その他のサイト
その他のサイトは、上記のいずれかに近いか、折衷した管理になるでしょう。
スタイリングのアプローチは部品単位で切り替えられるため、より良い管理を模索できます。

ディレクトリ構成の実例

ディレクトリ構成の実例を紹介します。
以下は、コーポレート系のサイト制作を対象にしつつも、念のため他の種別にもすぐに対応・拡張できるように構成したディレクトリの例です。

ディレクトリ構成・第一階層

リストをクリック or タップで展開できます


base/ の下層展開
成果物全域に関わるCSSを管理します。
vender/ ディレクトリを追加する事で、独自作成のものと外部のCSSを影響分離しています。
base/ の下層展開 ディレクトリ図


contents/ の下層展開
Contents領域のみで使うCSSを管理します。
block/の内部は、共通利用するものを_commonに収め、サイトのページ名やカテゴリ名でディレクトリを設置すると分業がおこないやすくなります。
contents/ の下層展開 ディレクトリ図


frame/ の下層展開
Frame領域のみで使うCSSを管理します。
Frameのblock/の内部は、部位や機能によってディレクトリを設置しておくと、何かあったときに拡張しやすくなります。
frame/ の下層展開 ディレクトリ図


pages/ の下層展開
コーポレート系サイトを想定した場合、Pagesのサブディレクトリは必要ないと判断し、単一階層でSassファイルを管理しています。
pages/ の下層展開 ディレクトリ図

上記は実務利用しているものと近く、実際には「DI-CODING」によってSassファイルの命名雛形も含めてスターターキット化しています。作る手間より削除する手間の方が楽であるため、雛形化するときには少し広げて作っておいたほうが後々楽に使えます。
目視でファイルを追いかける場合、平置きの大量のファイルをアルファベット順で探すより、格納場所が分かっている適量のディレクトリを下った方が遥かに楽に探せます。

これらにより、コーポレートを中心としながらも、どんな種別の案件でも対応できるようにしています。

7・ その他・考え方など

よりよい把握のための図解

「1 - 全体の把握」の図を再掲載した上で、仕組みについてもう少し詳しく図解します。
コーディング全体の流れと対応する手法

上記の詳細が以下の図です。これにより概念がどのように使われ、物理化されるのかが把握できます。
一見すると複雑に見えるかもしれませんが、1つづつは単純です。
仕組みの図解/実務と概念の流れ
青文字と矢印が「制作における行動」で、オレンジの文字と矢印が「概念の流れ」です。
制作の流れとしては(1)~(4)を繰り返しおこない、概念はそれをサポートする形で関与しています。

著名な設計手法10個との概念比較

この仕組みの定義と、著名な10種類のCSS設計や思想との比較・対応表です。
こうしてまとめてみると、それぞれ言い方が違うだけで、やりたい事の本質は似ていたのでは?という事が見てとれます。特に、ComponentとUtilityのあたりは顕著です。
逆を言えば、この度の一連の仕組みはこれらの特徴を併せ持っていると言えます。

概念比較表

各公式サイトへのリンク

SMACSS / OOCSS / BEM / MCSS / RSCSS / ECSS / ITCSS / FLOCSS / PRECSS / Atomic Design

手法とレギュレーションの考え方

現場で制作者が準拠すべきものは、現場ごとに定められたルールやレギュレーション、ガイドライン(図の3)です。これがその制作現場における最高位の権限と強制力を持つと考えます。

概念や手法の実用ステップ図
(1)は、その環境における「特効薬」をつくるための参考資料・材料であり、制作者が「これを使う」「この仕組みに準拠する」と定めて初めて、効力を発揮します。これは、ひとりであってもチームであっても「業務の標準化」を目指すのであれば変わりません。

この仕組みも(1)に該当し、その他多くのCSS設計と同じく「誰かがその人の環境を最適化するために作ったもの」です。取扱業務も、商圏も、組織規模も異なる現場や環境において、全く同じ仕組みがそのままの状態でベストマッチすると捉える方が不自然です。

各所で自由に変更してもよい。と記載しているのは、こういった考え方のためです。そのまま使える方が効率的ではありますが、現場のルールを決定するのは制作者(設計者)です。
決して(1)を(3)と捉えないよう、また、"ねばならないの思考"にはまらないようご注意ください。

8・ さいごに

これまで「HTML+CSSコーディングの言語化」からはじまり、「HCDCモデル図」からなる一連の情報を公開してきました。今回で概念をどのように実務利用するのかをお伝えできたのではないかと思います。

この仕組みは、著名なCSS設計手法では業務が標準化できなかったために独自で構築したものです。
当時の環境は、取り扱うサイト種別が、企業サイトやブランディングサイト、EC、ポータル、LP、サービスサイト、ブログ、管理画面(システム開発も含む)…、という幅広い範囲であったにも関わらず、コーディングルールもなく、過去のサイトに手を入れる場合は一苦労でした。

このような多くの種別の制作方法を統一したくても、当時著名だった手法ではCSSは溢れかえり、何かに寄せればどれかの種別で設計破綻する。といった状況でした。
こういった環境の中で、全てに通用するコーディングの本質は無いものかと求め続け、案件で叩き上げたのが今回の仕組みになります。

合う、合わないは必ずありますし、本当に使いやすい「特効薬」は現場ごとに作ることになりますが、その時のレシピや材料・参考資料になれば幸いです。

今後の情報公開

あと2つの手法の公開が残っていますが、これらは「命名規則」に関する話になります。
HCDCモデル図を実務利用するための4つの標準化手法
しかし、実はもうこの段階で実用できます。
命名に関する標準化は、一連の仕組みとは疎結合の関係にあるためです。どのような単語と連結記法を使おうと、仕組みの本質が変容することはありません。
もし今現在でCSSが氾濫する。設計手法の解釈が人によって違って困っている。といった悩みをお抱えの場合は、是非お試し下さい。

※単語連結手法については、もしご興味があれば、先行してREMMのサイト(http://remm.work/)をご覧ください。BEMの亜流・改良版で、この仕組みの利点を生かしやすくなっています。

次の記事を公開次第、以下の関連記事にリンクを追加します。

関連記事

詳細記事

クレジット・その他

Ultimate Coding
概要・前提事項

この仕組みは、組織所属時に業務効率化のために構築したものであり、許可を得た上で設計者本人が個人活動として公開しています。商用の制作や開発には利用していただけますが、仕組みを販売したり媒体化するなどの、制作以外での商用化はご遠慮下さい。質問その他、何かあれば@croco_worksまでお声かけください。

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

2020年から始めるAzure Cosmos DB - JavaScript SDK (SQL API)を見てみる (Part.1)

th.jpeg

この記事について

本記事は、2020年3月6日 (米国時間) にて、Azure Cosmos DB に新しく Free Tier (無償利用枠) が登場したことに伴い、改めて Azure Cosmos DB を色々と触っていく試みの 3 回目です。
今回は、前回記事 にて作成した CRUD アプリ内で使用している Microsoft Azure Cosmos JavaScript SDK について見ていきたいと思います。

対象読者

  • Azure Cosmos DB について学習したい方
  • Node.js で Azure Cosmos DB への CRUD 操作を行いたい方
  • Microsoft Azure Cosmos JavaScript SDK の動作について理解したい方

Microsoft Azure Cosmos JavaScript SDK

実際に、Microsoft Docsの内容を元に、JavaScript SDK (SQL API) の中身を見ていきます。
今回は Azure Cosmos DB に接続する際に生成する、CosmosClient について確認します。

CosmosClient

TypeDoc の記載は、以下の通りです。

Provides a client-side logical representation of the Azure Cosmos DB database account.
This client is used to configure and execute requests in the Azure Cosmos DB database service.
Azure Cosmos DBデータベースアカウントのクライアント側の論理表現を提供します。
このクライアントは、Azure Cosmos DBデータベースサービスで要求を構成および実行するために使用されます。

CosmosClient.ts
const client: CosmosClient = new CosmosClient({ endpoint, key });

上にある通り、endpointkey を使用してインスタンスを作成しています。

  • endpoint: Azure Cosmos アカウント URI、
  • key: Azure Cosmos アカウントのプライマリキー or セカンダリキー

どのコンストラクタが動いているのかを確認すると

(alias) new CosmosClient(options: CosmosClientOptions): CosmosClient (+1 overload)
import CosmosClient

とあり、CosmosClient(options: CosmosClientOptions)が動いているようです。
中身を確認してみます。

Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/CosmosClient.ts
/**
 * Creates a new {@link CosmosClient} object. See {@link CosmosClientOptions} for more details on what options you can use.
 * @param options bag of options - require at least endpoint and auth to be configured
 */
constructor(options: CosmosClientOptions); // tslint:disable-line:unified-signatures

あれ、1行しかない?と思ったらすぐその下にconstructor(optionsOrConnectionString: string | CosmosClientOptions)があったので焦りました。。
このコンストラクタは長いので、部分ごとに見ていきます。

コンストラクタ処理 (1)

if (typeof optionsOrConnectionString === "string") {
  optionsOrConnectionString = parseConnectionString(optionsOrConnectionString);
}

これは単純に、コンストラクタの引数がstringCosmosClientOptionsかを判別しています。
引数が string の場合は、if 文の中で接続文字列を使って CosmosClientOptions を生成し、 optionsOrConnectionString に代入しています。
コンストラクタの引数が string と CosmosClientOptions と異なっているので、お決まりな感じの処理です。
コンストラクタの引数の型が違うからと言い、内容がほとんど重複するようなコンストラクタをしっかり分けて書く人をたまに見かけます。このコードは「そんな無駄なことしなくていいよ」と教えてくれるいい例ですね。

コンストラクタ処理 (2)

optionsOrConnectionString.connectionPolicy = Object.assign(
  {},
  defaultConnectionPolicy,
  optionsOrConnectionString.connectionPolicy
);

ここでは、CosmosClientOptions の connectionPolicyに値を設定しています。
その前に、CosmosClientOptions って何者だ?、という疑問があるので、先にこちらを見てみます。

Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/CosmosClientOptions.ts
export interface CosmosClientOptions {
  endpoint: string;
  key?: string;
  resourceTokens?: { [resourcePath: string]: string };
  tokenProvider?: TokenProvider;
  permissionFeed?: PermissionDefinition[];
  connectionPolicy?: ConnectionPolicy;
  consistencyLevel?: keyof typeof ConsistencyLevel;
  defaultHeaders?: CosmosHeaders;
  agent?: Agent;
  userAgentSuffix?: string;
  plugins?: PluginConfig[];
}

CosmosClientOptions には 11 個のプロパティが定義されています。どうも CosmosClientOptions の中に endpoint や key をはじめとした Azure Cosmos DB を利用するための各種設定値が格納されるようです。
ちなみに TypeScript では、他の言語ではメンバー変数やフィールドと呼ばれる、名前を持ち、指定された型のデータを保持するものをプロパティといいます。

この CosmosClientOptions のプロパティの 1 つに ConnectionPolicyがありますので、先ほどの処理はこのプロパティの値を設定している部分と理解しました。
ConnectionPolicy は何かというのを確認するために、さらに中身をみてみます。

Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/documents/ConnectionPolicy.ts
export interface ConnectionPolicy {
  connectionMode?: ConnectionMode;
  requestTimeout?: number;
  enableEndpointDiscovery?: boolean;
  preferredLocations?: string[];
  retryOptions?: RetryOptions;
  useMultipleWriteLocations?: boolean;

ConnectionPolicy の中には、connectionModerequestTimeoutなどの接続に関するポリシー設定があるようです。(これ以上、クラスを深くみると大変なので、一旦ここまでにします。)

一旦元に戻って、

(再掲)
optionsOrConnectionString.connectionPolicy = Object.assign(
  {},
  defaultConnectionPolicy,
  optionsOrConnectionString.connectionPolicy
);

をみると、Object.assignを使用しています。
Object.assign() が何かについては、こちら を参照してください。
つまり、CosmosClientOptions の ConnectionPolicy プロパティ内で未定義となっているものについて、デフォルト値を代入している感じです。

コンストラクタ処理 (3)

optionsOrConnectionString.defaultHeaders = optionsOrConnectionString.defaultHeaders || {};
optionsOrConnectionString.defaultHeaders[Constants.HttpHeaders.CacheControl] = "no-cache";
optionsOrConnectionString.defaultHeaders[Constants.HttpHeaders.Version] = Constants.CurrentVersion;
if (optionsOrConnectionString.consistencyLevel !== undefined) {
  optionsOrConnectionString.defaultHeaders[Constants.HttpHeaders.ConsistencyLevel] = optionsOrConnectionString.consistencyLevel;
}

optionsOrConnectionString.defaultHeaders[Constants.HttpHeaders.UserAgent] = getUserAgent(
  optionsOrConnectionString.userAgentSuffix
);

このoptionsOrConnectionString.defaultHeadersは、CosmosHeaders クラスです。
中身を見てみます。

Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/queryExecutionContext/CosmosHeaders.ts
export interface CosmosHeaders {
  [key: string]: string | boolean | number;
}

キー(key) と キーに紐づく値(value) を格納できるようにしています。いわゆる連想配列の部分です。
連想配列をインタフェースで定義するという使い方もあるんですね。勉強になります。

つまり、ここの処理は、HTTP リクエストの各種ヘッダー情報を CosmosHeaders クラス (連想配列) を使って設定しているという感じです。

コンストラクタ処理 (4)

const globalEndpointManager = new GlobalEndpointManager(
  optionsOrConnectionString,
  async (opts: RequestOptions) => this.getDatabaseAccount(opts)
);

新しいGlobalEndpointManagerというヤツが出てきました。
GlobalEndpointManage にある、どのコンストラクタが動いているのかを確認すると

(alias) new GlobalEndpointManager(options: CosmosClientOptions, readDatabaseAccount: (opts: RequestOptions) => Promise<ResourceResponse<DatabaseAccount>>): GlobalEndpointManager
import GlobalEndpointManager

が動いているようです。中身を確認します。

Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/globalEndpointManager.ts
export class GlobalEndpointManager {
  private defaultEndpoint: string;
  public enableEndpointDiscovery: boolean;
  private isRefreshing: boolean;
  private options: CosmosClientOptions;
  private preferredLocations: string[];

  constructor(
    options: CosmosClientOptions,
    private readDatabaseAccount: (opts: RequestOptions) => Promise<ResourceResponse<DatabaseAccount>>
  ) {
    this.options = options;
    this.defaultEndpoint = options.endpoint;
    this.enableEndpointDiscovery = options.connectionPolicy.enableEndpointDiscovery;
    this.isRefreshing = false;
    this.preferredLocations = this.options.connectionPolicy.preferredLocations;
  }
}

ナンダコレ、、、
となったので、TypeDoc を見てみます。

This internal class implements the logic for endpoint management for geo-replicated database accounts.
この内部クラスは、地理的に複製されたデータベースアカウントのエンドポイント管理のロジックを実装します。

ああ、そういうことか! とこの絵を思い出しました。
Azure Cosmos DB で、これ忘れたらダメなヤツやん。。

azure-cosmos-db.png

Microsoft Docs にある Azure Cosmos DB の概要 にも一番最初に

Azure Cosmos DB は、Microsoft によってグローバルに配布されるマルチモデル データベース サービスです。

とあります。
Cosmos DB では、Cosmos アカウントに複数リージョンを関連付けさせることができ、関連付けられたすべてのリージョンにデータがシームレスにレプリケートされるようになっています。これはそれに関連する設定まわりの処理と認識しました。

コンストラクタ処理 (5)

this.clientContext = new ClientContext(optionsOrConnectionString, globalEndpointManager);

クライアントコンテキストがやっと出てきました。
どのコンストラクタが動いているのかを確認すると、

(alias) new ClientContext(cosmosClientOptions: CosmosClientOptions, globalEndpointManager: GlobalEndpointManager): ClientContext
import ClientContext

が動いているようです。

Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/ClientContext.ts
export class ClientContext {
  private readonly sessionContainer: SessionContainer;
  private connectionPolicy: ConnectionPolicy;
  public partitionKeyDefinitionCache: { [containerUrl: string]: any };

  public constructor(
    private cosmosClientOptions: CosmosClientOptions,
    private globalEndpointManager: GlobalEndpointManager
  ) {
    this.connectionPolicy = cosmosClientOptions.connectionPolicy;
    this.sessionContainer = new SessionContainer();
    this.partitionKeyDefinitionCache = {};
  }
}

(クライアントコンテキストについては、説明するまでもないと思いますが) いわゆる、コンテキストの伝播 とか言われているモノです。
ざっくり言うと、クライアントに関連する情報を保持している場所と私は思っています。メソッドの引数にクライアント情報をいちいち与えずに、全部ここを見ましょうよ、的なやつです。(適当)
クライアントコンテキストについて、良い説明となる資料を見つけられなかったので、見つけたら追記したいです。

コンストラクタ処理 (6)

this.databases = new Databases(this, this.clientContext);
this.offers = new Offers(this, this.clientContext);

やっと最後の処理です。
Databasesは、新しいデータベースの作成、およびすべてのデータベースの読み取り/クエリの操作を行うクラスです。
Offersについては、聞きなれない単語だと思います。(筆者もちゃんと理解しているわけではありませんが) REST経由で、SQL APIを使用する際に登場するもののようです。
Offer resource というものがあるんですね。別途、学習を進めたいと思います。

さいごに

今回は、Azure Cosmos DB に JavaScript/Node.js で接続する際に最初に生成される、CosmosClient について、中身を確認してみました。
今回の内容は、前回、実際に CRUD アプリを作成 (前回記事) した時はたったの 1 行で終わってしまった内容です。

普段のアプリ開発では、あまり意識しない世界なのかもしれませんが、実際にコンストラクタの中で何が行われているのかを確認することは、Azure Cosmos DB の仕組みや使用するライブラリへの深い知見を得る ためには必要な事かな、と思いました。

次回は、Databaseクラスについて見ていこうと思います。

関連リンク

前回記事

参考情報

npm

GitHub

Microsoft Docs

developer.mozilla.org

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

Yahoo!広告APIによる広告パフォーマンス計測の自動化

はじめに

広告運用を行なっている方はあるあるだと思うのですが、パフォーマンスをチェックするために、日々各媒体の管理画面を見にいくのは骨が折れる作業です。
そこで今回はYahoo!広告APIとGoogle SpreadSheetsを使って、Yahoo検索広告とYahooディスプレイ広告のデータを自動的に取得する方法をご紹介します。

Yahoo!広告APIとは?

従来利用されていたYahoo!プロモーション広告APIがリニューアルされて、Yahoo!広告APIとして生まれ変わりました。
Yahoo!広告APIのリリースと、現行APIへの新規利用受付の終了について

Yahoo!広告APIを利用するためには、申し込み手順に従い、利用申請を行う必要があります。

アプリケーションの登録

  • Yahoo!広告 API管理ツールにアクセスして、広告運用を行なっているYahoo!ビジネスIDでログインします。
  • 登録アプリケーションの画面で登録ボタンを選択します。
    image.png

  • 登録画面で必要事項を入力して確認を選択します。リダイクレイトURIには、アクセス可能な任意なURL(例:https://qiita.com )を入力してください。
    image.png

  • 登録が完了したら下記のようにアプリケーションの情報が表示されるため、クライアントIDクライアントシークレットをメモします。
    yahoo_1.png

認可コードの取得

  • 下記のURLを作成します。
    • CLIENT_ID:冒頭でメモしたクライアントID
    • REDIRECT_URI:アプリケーションの登録で設定したURL
    • THIS_VALUE_SHOULD_BE_UNIQUE_PER_REQUEST:任意の文字列(※乱数でOK)
https://biz-oauth.yahoo.co.jp/oauth/v1/authorize?response_type=code
&client_id=CLIENT_ID
&redirect_uri=REDIRECT_URI
&scope=yahooads
&state=THIS_VALUE_SHOULD_BE_UNIQUE_PER_REQUEST
  • 作成したURLをブラウザで開くと、Yahoo!ビジネスIDのログインを求められるため、ログインします。
    yahoo_2.png

  • ログインに成功すると、リダイレクトURIに設定したページがブラウザで表示されます。URLバーに出力されているcode=AUTH_CODEのAUTH_CODEが認可コードになるので値をメモします。

アクセストークン・リフレッシュトークンの取得

  • ターミナルから下記のリクエストを実行します。
    • CLIENT_ID:冒頭でメモしたクライアントID
    • CLIENT_SECRET:冒頭でメモしたクライアントシークレット
    • REDIRECT_URI:アプリケーションの登録で設定したURL
    • AUTH_CODE:先ほど取得した認可コード
curl - X GET \
https://biz-oauth.yahoo.co.jp/oauth/v1/token?grant_type=authorization_coden
&client_id=CLIENT_ID
&client_secret=CLIENT_SECRET
&redirect_uri=REDIRECT_URI
&code=AUTH_CODE
  • 下記のようにアクセストークンとリフレッシュトークンが返却されるので、それぞれの値をメモします。
    • アクセストークンは1時間で失効するため、リフレッシュトークンを使って都度で新しいアクセストークン を発行する必要があります。
{"access_token":"ACCESS_TOKEN",
"expires_in":"3600",
"token_type":"Bearer",
"refresh_token":"REFRESH_TOKEN"}

レポートの登録

  • ターミナルから下記のリクエストを実行して、データダウンロード用のレポートを登録します。
  • operand内でレポートの出力項目、出力期間など詳細な条件の設定が可能です。詳しくはYahoo!広告APIリファレンスReportDefinitionService/addを参照してください。
    • Yahoo検索広告とYahooディスプレイ広告で、URLおよび設定可能な項目が異なります。
curl -X POST \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"operand":
  [{
    "accountId": ACCOUNT_ID,
    "fields": [REPORT_FIELDS],
    "reportCompressType": "NONE",
    "reportDateRangeType": "ALL_TIME",
    "reportDownloadEncode": "UTF-8",
    "reportDownloadFormat": "CSV",
    "reportIncludeDeleted": "TRUE",
    "reportLanguage": "JA",
    "reportName": REPORT_NAME,
    "reportType": REPORT_TYPE
  }]}' \
"https://ads-search.yahooapis.jp/api/v1/ReportDefinitionService/add"
  • jsonが返却されるため、その中に記載されたreportJobIdの値をメモします。
{
   "errors":null,
   "rid":"...",
   "rval":{
      "values":[
         {
            "errors":null,
            "operationSucceeded":true,
            "reportDefinition":{
               "accountId":ACCOUNT_ID,
               "fields":[...],
               "reportJobId":ここに表示された値をメモ,
               ...
            }
         }
      ]
   }
}

Googleスプレッドシートへの出力

  • Googleスプレッドシートのメニューから「ツール > スクリプト エディタ」を選択します。
  • 下記のコードを貼り付け、ここまでの手順で得られた設定値を入力します。
// Yahoo!広告の設定
const URL_TOKEN = 'https://biz-oauth.yahoo.co.jp/oauth/v1/token';
const URL_API = 'https://ads-search.yahooapis.jp/api/v1';
const CLIENT_ID = 'xxxxx';
const CLIENT_SECRET = 'xxxxx';
const REFRESH_TOKEN = 'xxxxx';
const ACCOUNT_ID = xxxxx;
const REPORT_JOB_ID = xxxxx;

// 出力先のシート名
var sheet_name = 'xxxxx';

function updateYahooReport() {
    var access_token = getAccessToken(REFRESH_TOKEN);
    var data = donwloadReport(URL_API, access_token, ACCOUNT_ID, REPORT_JOB_ID);
    updateCsv(sheet_name, data);
}

function updateCsv(sheet_name, data) {
  var values = Utilities.parseCsv(data);
  var this_sheet = SpreadsheetApp.getActive().getSheetByName(sheet_name);
  this_sheet.clearContents();
  this_sheet.getRange(1, 1, values.length, values[0].length).setValues(values);
}

function getAccessToken(refresh_token) {
    var payload = {
        grant_type: 'refresh_token',
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET,
        refresh_token: refresh_token
    };
    var params = {
        payload : payload
    };

    var response = UrlFetchApp.fetch(URL_TOKEN, params);
    var response_body = JSON.parse(response.getContentText());

    return response_body['access_token'];
}

function downloadReport(url, access_token, account_id, report_jod_id) {
    var headers = {
        'Authorization': 'Bearer ' + access_token
    };
    var payload = {
        accountId: account_id,
        reportJobId: report_jod_id
    };
    var params = {
        method : 'post',
        contentType: 'application/json',
        headers: headers,
        payload: JSON.stringify(payload)
    };

    var response = UrlFetchApp.fetch(url + '/ReportDefinitionService/download', params);
    return response;
}
  • updateYahooReport の関数を選択して実行すると、指定したシート名に結果が出力されます。 capture.png

定期的な出力

  • GASのスケジューラー機能を利用することで、定期的にデータを更新することができます。スケジューラー機能はこちらの記事を参考にして下さい。

さいごに

  • Yahoo!広告はFacebook広告やGoogle広告と異なり、Yahooは明示的にレポートを登録しないとデータが出力できない点が独特でした。
  • 媒体管理画面と同様、Yahoo検索広告とYahooディスプレイ広告でAPI仕様が異なるので注意が必要です。(いつの日か統合されるのだろうか、、、)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでjs読み込む時に「$ is not defined javascript」エラー

めちゃめちゃ基本的なところでつまづいたので備忘録。

laravelを使ってjs読み込みたいのに何故か最初のおまじない部分でずっとコンソールエラー。
$ is not defined javascript

hoge.js
$(function(){ //←処理入る前のここで既に怒られる
 //ここに処理
});



なんでえ???と思ってたらjsの呼び出し方間違えてました。
↓こう書いてたのを

hoge.blade.php
 <script src="/js/hoge.js"></script>

こうしないといけなかったみたい。

hoge.blade.php
<script src="{{ asset('/js/hoge.js') }}"></script>

参考にさせていただいた記事
https://qiita.com/sakuraya/items/411dbc2e1e633928340e

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