- 投稿日:2021-06-15T23:19:48+09:00
Webpack require.contextで.jpgをバンドルしたら404
はじめに ExpressをserverlessにしてNetifyで公開したが、その際にrequire.contextで特定のディレクトリ以下のファイル全てをバンドルするというやり方をしていて躓いたのでその備忘録を残しておく。 ※NetifyでBuild・Deployした時にだけ起きたのが理解できていないため、何かわかる方がいましたらご教示頂けると幸いです。(ローカルでBuildする分にはdistに.jpgファイルはちゃんと展開されていました(distフォルダ以下に出力されていました))。 どういう状況での話か? 以下のソースのように、.jpgを画面上にある場面で表示させるため各.jpgバンドルさせたいが、コメントアウトした部分のように1つ1つをimportするのは現実的でないので、require.contextでバンドルさせていた(動的なバンドルになってしまうが。) index.js // 省略 // import all img // import './img/strong_positive.jpg'; // import './img/positive.jpg'; // import './img/neutral.jpg'; // import './img/negative.jpg'; // import './img/strong_negative.jpg'; // import './img/none.jpg'; const importAll = (r) => r.keys().forEach(r); importAll(require.context('../', true, /\.jpg$/)); // 省略 }); この時に、ローカルでBuildしている分には、.jpgがdistにちゃんと出力されたが、Netify上でBuildすると以下の図のように404になってしまった。 どうやって解決したか? CopyWebpackPluginを用いて、明示的にdist以下に.jpgをコピーするようにした。これにより以下の図のように404にはならなくなった。 ※この解決方法で正しいのかは不明・・・ webpack.configについては以下のような感じ。 webpack.prod.js const path = require('path') const CopyWebpackPlugin = require("copy-webpack-plugin") // 省略 module.exports = { entry: './src/client/index.js', mode: 'production', output: { path: path.join(__dirname, 'dist'), }, module: { rules: [{ // 省略 }, { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader', options: { name: '[name].[ext]', } }] }, plugins: [ // 省略 new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, 'src/client/img'), to: path.resolve(__dirname, "dist"), }, ] }), ] } ソースコード ソースコード全体は以下。Netify上でサイトも公開している。 参考文献 https://webpack.js.org/plugins/copy-webpack-plugin/ Webpackで静的なファイルをコピーする方法
- 投稿日:2021-06-15T20:43:46+09:00
[kintone] 親タスクレコードにある子タスクサブテーブルから今日のタスクを抽出する
はじめに 1案件=1レコード(案件概要, 案件リーダー, 案件開始/終了予定) 1タスク=1サブテーブル行(作業概要, 作業担当者, 作業開始/終了予定) こんな感じで、案件(親タスク)とそれに紐づく子タスクを管理されているパターンって結構あるんじゃないでしょうか。 更にこの構成で某ガントチャートプラグイン等を使用するとかなり便利になります。 某ガントチャートプラグインでは、下記のようなガントチャートビューを表示できます。 - レコード一覧画面・・・レコード(案件)単位でのガントチャート(=マスタースケジュール) - レコード詳細画面・・・案件に紐づくタスクのガントチャート 欲が出てくる 非常に便利ですが、欲が出てきてこんなことを確認したくなります。 (前提・・・リーダー、作業者ともに複数の案件を並行して進めていることが多い) 【リーダー】差し込みタスクが発生!今日誰が何やってるっけ →案件横断しつつ案件レベルではなく作業レベルでのチームメンバーのスケジュールを確認したい 【作業者】いろんな案件を並行中。明日の作業予定は? →自身の明日の作業レベルでのスケジュールを確認したい 以上のように、作業レベルでのスケジュールの確認をパッとできるようにしたくなります。 (今のままでは、レコード一覧であの見にくいテーブルを展開するか、レコード詳細画面に入って確認するしかありません) カスタマイズしてみる というわけで、カスタマイズでなんとかしてみたいと思います。 流れ的にガントチャートビューかと思われそうですが、諸事情でただただテーブル形式で表示します。 テーブルの描画にはDatatablesを使用します。(使ってみたかった) とりあえずカスタマイズビューに表示してみたいと思います。 やりたいことを整理すると、 一覧ビューで表示されているレコードの中から、条件を満たすサブテーブル行を抽出し、表示する となります。ついでなので以下の機能も入れておきます。 ・自分のタスクは黄色く強調表示する ・行をクリックすると案件レコードに遷移する 完成イメージ (サンプルデータを作るのが面倒だったのでほぼモザイクになってしまいました、すみません) DataTablesの機能で生成される右上の検索窓に担当者の名前を入れたりすれば、担当者で絞り込みができます。お手軽ですね。 コード (フィールド名は適宜置き換えてください) renderTask.js (function($) { 'use strict'; //予めカスタマイズビューを作っておき、viewIdを確認して入れます。 const viewId = 0000000 kintone.events.on('app.record.index.show', function(event) { //対象の一覧んビューでなければスキップします if(event.viewId !== viewId){ return event; } //各レコードから当日分のサブテーブルを抜き出す let filteredRows = [] //なんとなくluxonを使ってみたかっただけです const today = luxon.DateTime.now().toFormat("yyyy-MM-dd") //自分のタスクを強調表示するために使います const loginusercode = kintone.getLoginUser().code; //表示している案件レコードを回していきます event.records.forEach(record=>{ const rows = record.タスクテーブル?.value; rows.forEach(row => { //テーブル内の開始日/予定日の範囲内に当日が含まれている行を抽出します。 if(row.value.タスク開始予定日?.value <= today && row.value.タスク終了予定日?.value >= today){ const body = { //担当者欄はユーザー選択フィールドの想定で、複数設定されている場合は/区切りで表示します 担当者: row.value.作業担当者?.value.map(x=>x.name).join(" / "), //サブテーブルから値をとってきます 開始日: row.value.タスク開始予定日?.value, 終了日: row.value.タスク終了予定日?.value, タスク項目: row.value.タスク項目?.value, 作業名: row.value.作業名?.value, タスク概要: row.value.タスク概要?.value, //親タスクとなるレコードから値をとってきます 企業名: record.企業名?.value, 案件名: record.案件名?.value, //行をクリックした時に案件レコードに遷移できるよう仕込みます url:`https://${document.domain}/k/${kintone.app.getId()}/show#record=${record.$id.value}&`, //自身のタスクを強調表示する為にフラグを仕込みます own: row.value.task_resource.value.find(user=>user.code == loginusercode) ? true : false }; filteredRows.push(body); } }); }); //カスタマイズビューで以下のidを持つdiv等を用意しておきます。 let $div = $("#taskList"); //Datatablesのスタイル調整用のクラスを指定しておきます。 $div.attr("class", "compact hover row-border cell-border"); //というわけでDatatablesを使ってテーブルを描画します。 $div.DataTable({ data: filteredRows, columns: [ { data: '担当者', title: '担当者' }, { data: '開始日', title: '開始日' }, { data: '終了日', title: '終了日' }, { data: '企業名', title: '企業名' }, { data: '案件名', title: '案件名' }, { data: 'タスク項目', title: 'タスク項目' }, { data: '作業名', title: '作業名' }, { data: 'タスク概要', title: 'タスク概要' }, ], displayLength: 100, 'createdRow': function( row, data, dataIndex ) { //自分のタスクは黄色くします if(data.own){ $(row).css({"backgroundColor":"#ffeaa5"}); } //行をクリックした時に案件レコードを遷移させます $(row) // カーソールオーバー時の見た目を変化 .css("cursor", "pointer") .click(function(e) { window.open(data.url) }); }, }); return event; }); })(jQuery); 最後に とりあえず本日分だけ見れるようにぱぱっと作りましたが、 datepickerとか置いて範期間指定できるようにするのもいいですね。 エラー処理などは入れておりません、ご容赦ください。
- 投稿日:2021-06-15T20:18:05+09:00
2021年の外国のプログラミング学習Webサイト ベスト5
ソフトウェア開発は急速に発展する分野です。プログラミング技術も絶えず更新されています。この文章で、ベスト5のプログラミング学習サイトを集めました。プログラミングは絶えず勉強しなければなりません! 1.Codegym CodegymはJava中心のWebサイトです。理論的知識と実践的知識を組み合わせており、1,200を超える実践プロジェクトを見つけることができます。ゲーミフィケーションとストーリーテリングを通じて学習を楽しくします。もう1つのハイライトは、オンラインIDEを提供し、タスクをクリアすることで学習します。アクティブなJavaコミュニティがあり、お互いに交流できます。 アドレス:https://codegym.cc/(Googleアカウントでの直接ログインをサポート) 2. Treehouse Treehouseは、プログラミングの知識を簡素化するもう1つのプラットフォームです。APPの作成、WordPressブログの作成など、特定のプロジェクトに対処するために必要なスキルを学びます。 アドレス:https://teamtreehouse.com/ (アカウントを新規作成が必要) 3. Khan Academy Khan AcademyにはJavaまたはPythonのチュートリアルはありませんが、プラットフォームはコンピュータサイエンスの基本概念を理解するための良い出発点です。ウェブサイトの課程を完成したら、データ構造とアルゴリズムに関する知識を理解し、「プログラマー思考」を獲得します。 アドレス:https://www.khanacademy.org/computing/computer-science(Googleアカウントでの直接ログインをサポート) 4. Code4Startup 創業分野でより多くの実務経験を得たいなら、Code 4 Startupは開発スキルをうまく利用できるプラットフォームです。このプロジェクトにより、初級プログラマが実際のスタートアップ会社のためにコードを作成することができます。同時に、企業は人材募集の費用を節約できます。 アドレス:https://code4startup.com/(Google/Githubアカウントでの直接ログインをサポート) 5. Freecodecamp 非常に幅広いチュートリアルとコースを備えた最大の技術コミュニティの1つです。ここで実用的なプロジェクトベースの作業を見つけます。しっかりとしたプログラミング理論を身につけたら、Freecodecampを推薦します。 アドレス:https://www.freecodecamp.org/(Google/Githubアカウントでの直接ログインをサポート)
- 投稿日:2021-06-15T18:45:46+09:00
Google I/O 2021 フロントエンドまとめ(3/6): 〜Webプラットフォーム最新情報(3/3): その他〜
Google I/O 2021 フロントエンドまとめ(3/6): 〜Webプラットフォーム最新情報(3/3): その他〜 この記事は、Google I/O 2021で発表されたフロントエンド関連情報の中で個人的に気になったものをまとめたもので、 全6記事中3本目。 Webプラットフォームの話 ウェブ プラットフォームの最新情報 @Google I/O 2021 Google Mapの新機能WebGLOverlayViewで3Dマップの操作が出来るようになったよ。というお話 WebGLOverlayViewの継承クラスを作成して、その決められたイベントハンドラメソッド(onAdd(), onContextRestored(), onDraw)内でそれぞれ適切なセットアップ・描画処理を記述するとのこと。 デモではthree.jsを使ってグラフィックを設定/操作していた。 すげぇ楽しそう!! WebGPUの紹介 WebGLとは別の、より低レベルなグラフィックAPIであるWebGPUの紹介。 低レベルAPIなのでハイパフォーマンスで、Vulcan, Metal, Direct3D 12などから発想を得ていて、3Dグラフィック界隈での今っぽい流れを汲んだものらしい。 いまのところ試験運用版とのこと。 V8がSIMDをサポートしたからChromeのWebAssemblyがもっと高速になったよ。というお話 まず、SIMD(読み: しむど、しむでぃー; Single Instruction, Multiple Dataの略)というのは、一回の命令で複数のデータを並列一括処理出来る仕組みで、いわゆるベクトル演算。ベクトル演算なのでグラフィック処理や暗号処理、物理演算、機械学習などの演算が得意と言われている。 今回ChromeやNode.jsのJavaSript実行エンジンであるV8でSIMDがサポートされたので、いくつかの処理でより高速化できる可能性が広がったよというお話。 その一例としてGoogle Meetで、ブラウザ内にメディア処理パイプラインに導入してパフォーマンスを200%向上したり、TensorFlowをつかった背景ぼかし機能を実現したりしたよ。という話もあった。 V8のデバッグ体験向上としてDevToolsでWebAssemblyのブレークポイントと変数検査が出来るようになったよ。というお話 「これで、クライアントとサーバの両方で安全かつ快適に動作するWebAssemblyが作れるね!」みたいなことを言ってました。 content-visibilityCSSプロパティによるレンダリングコスト削減手法の話 content-visibilityはCSSのプロパティの一つで、このプロパティが指定されたコンテンツのレンダリングを省略させたり出来る。 content-visibility: auto;を指定すると、要素がビューポート内にいないときはレンダリングが省略されパフォーマンスコストが削減できる。 今回は「これが使えるようになりましたよ」というお知らせ。 Core Web Vitalsのお話 Core Web Vitals自体の話や、 Core Web Vitalsを改善したことによるページパフォーマンス改善・コンバージョン向上の話、 Core Web Vitalsを計測可能なツールの広がり、 Google検索のページランクの考慮にCore Web Vitalsが含まれるようになる みたいな最近よく聞くCore Web Vitalsの話。 つづく
- 投稿日:2021-06-15T18:44:54+09:00
【Kintone】複数のユーザーフィールドの値を1つのユーザフィールドに重複なく当てはめる
やりたいこと Kintoneレコードで複数存在するユーザフィールドの値を別のユーザフィールドにすべて入れたい ぱっとググって調べて出てこなかったので、ここに残します KintoneのJavaScriptカスタマイズを使用します 例で使用するフィールド フィールドコードが"ユーザー1"のユーザー選択フィールド フィールドコードが"ユーザー2"のユーザー選択フィールド フィールドコードが"ユーザー3"のユーザー選択フィールド ユーザー1とユーザー2の値を重複なくユーザー3に当てはめます 実装例 レコード編集画面で一律にユーザ3の値をリセットし、レコード保存時に再度値を当てはめたうえで、保存される仕組みです ユーザ3の値のリセットを保存前に実行するとうまく動作しなかったため、処理を分けています kintone.events.on([ 'app.record.create.show', 'app.record.edit.show', ], function(event){ let record = event.record; // ユーザ3を空にする→レコードの保存直前に再度値を当てはめる record['ユーザー3'].value = []; return event; }); kintone.events.on([ 'app.record.create.submit', 'app.record.edit.submit', ], function(event){ let record = event.record; // ユーザごとの値を変数にする let valueUser1 = record['ユーザー1'].value; let valueUser2 = record['ユーザー2'].value; let valueUser3 = record['ユーザー3'].value; // 処理のなかで使用する配列の変数を中身が空の状態で先に用意 let usersArray = []; let usersUniqueArray = []; // ユーザフィールドの値をusersArrayの変数に入れる // (この時点ではユーザの重複が発生する) valueUser1.forEach(user => { // usersArray.push({'code': user['code']}); usersArray.push(user); }); valueUser2.forEach(user => { // usersArray.push({'code': user['code']}); usersArray.push(user); }); // usersArrayから重複のあるユーザを重複が無いようにして、 // usersUniqueArrayの変数に入れる usersUniqueArray = usersArray.filter((user, index, self) => index === self.findIndex((t) => ( t['code'] === user['code'] && t['name'] === user['name'] )) ); // 重複の無いユーザデータを'ユーザー3'フィールドに当てはめる usersUniqueArray.forEach(user => { valueUser3.push(user); }); return event; }); 注意事項 ユーザ選択をJavaScriptカスタマイズで登録するときは、ユーザーのcodeのみで登録が可能ですが、 私が試したときはユーザのnameも一緒に与えてあげないとうまくいきませんでした ユーザのcodeのみで動作する場合は以下のコメントアウトされている部分を使用してください usersArray.push({'code': user['code']});
- 投稿日:2021-06-15T18:31:41+09:00
Google I/O 2021 フロントエンドまとめ(2/6): 〜Webプラットフォーム最新情報(2/3): PWAによるWebとOSの連携強化関連〜
Google I/O 2021 フロントエンドまとめ(2/6): 〜Webプラットフォーム最新情報(2/3): PWAによるWebとOSの連携強化関連〜 この記事は、Google I/O 2021で発表されたフロントエンド関連情報の中で個人的に気になったものをまとめたもので、 全6記事中2本目。 Webプラットフォームの話 ウェブ プラットフォームの最新情報 @Google I/O 2021 PWA技術の拡充によるWebとOSの連携が強化されてくよ。みたいなお話 「最先端のエクスペリエンスを実現するための機能をWebですべて確保することを目指しています」って話からの、 WebアプリとOS間での使い勝手のシームレスな統合の具体的な推進として PWAアイコンからショートカットでクイックアクションを呼び出せる機能の紹介、 バッジAPIでランチャ上のPWAアイコンにバッジを表示させることでリエンゲージメントを図ることができますよという話、 PWAアプリのインストールUIが改善されたという話 などがあった。 Multi-Screen Window Placement API(マルチスクリーンウィンドウ配置API)のお話 Multi-Screen Window Placement APIは、Webアプリが接続されてるディスプレイを検出してそれらの配置する位置を制御できたりするAPIとのことで、リモートでのプレゼンとかビデオ会議とかで重宝されるよね。(そうだね。)というお話。 File System Access APIのお話 これまでさんざん色々出てきたこれ系宜しくなんと!Webアプリケーションからローカルファイルが開ける!!すごい(棒読み) そしてFile System Access APIでは、アプリケーションをファイルタイプのハンドラーとして登録できるとのこと。(ExcelインストールしてないPCの.xlsxファイルのデフォルト起動先をGoogleスプレッドシートに出来る的な?すごい(本音)) ひとりごと ところで、この、ファイルタイプのハンドラーとして登録出来る件、Macとかはともかく、Windowsのこの辺の設定ってレジストリいじる系だったと記憶してるんだが、大丈夫なんだろうか? (このへんの設定ってWebブラウザとかの普通のアプリが修正できるとこだっけ?覚えてない。 それともWindows八分?でもそれだと標準にできなくない?) WebHIDによる周辺機器のWeb対応のお話 WebHIDを使えば、キーボード、マウス・トラックパッド、ゲームパッドなどのHIDデバイスを、ソフトウェアのインストールやドライバーへの関与なしにWebで使用できるようになる。というお話。 一例として、 Nintendo SwitchのJoy-ConがHIDデバイスですよ、使えますよ。とか Stadiaなどで妥協のない (ん?) ゲームプレイが可能ですよ (では、どれくらいのタイトル数が"プレイ可能"ですか?) 。とか Chromeでネットワークトラブルのときに表示される恐竜ちゃんのゲームがゲームパッドの操作に対応したよ などが紹介された。 つづく
- 投稿日:2021-06-15T18:01:26+09:00
Google I/O 2021 フロントエンドまとめ(1/6)〜 Webプラットフォームの話(1/3): プライバシー関連〜
Google I/O 2021 フロントエンドまとめ(1/6)〜 Webプラットフォーム最新情報(1/3): プライバシー関連〜 この記事は、Google I/O 2021で発表されたフロントエンド関連情報の中で個人的に気になったものをまとめたもので、 全6記事中1本目。 Webプラットフォームの話 ウェブ プラットフォームの最新情報 @Google I/O 2021 Attribution Reporting APIっていう新しいAPIを提案したよ。というお話 Google Chromeはリリース以来、サイトごとにプロセスを分離する「サンドボックス」という機能を特徴としてきたが、近年のプライバシー意識の高まりにより、サードパティーCookieを初めとしてユーザを追跡することが忌避されるようになってきた。 そこでユーザを追跡することなく広告の成果(コンバージョン)やそれに至るユーザの行動の貢献度(アトリビューション)を測定する方法として、サイト間でユーザ識別データ(いわゆるトラッキングID)を共有することなく、ブラウザがAPIへ暗号化・匿名化されたレポートを送信する仕組みを作ったよ、Chromeで使えるようになったよ、とのこと。 ちなみに、レポート作成で遅延とノイズが発生することでよりユーザを特定する試みが抑制されるとのことで、これを「差分プライバシー」と呼んでいるらしい。 「サードパーティCookieを廃止する準備を段階的に進めている」←なんだって?! 上述の「Attribution Reporting APIがChromeで使えるようになった」という話の中でしれっと「サードパーティCookieを廃止する準備を段階的に進めている」という衝撃発言があった。 要は Google「(Google自身の収益事業でもある)広告において『サードパーティCookie使わないとコンバージョンやアトリビューションが計測できないよね』問題があったけど、それを解決するAttribution Reporting APIってのを使えるようにしたんだから、サードパーティCookieとかいう危なっかしいものはもう必ず使わなくても良くなってきてるよね?ね?こっち使お?サードパーティCookieつかうのやめよ?てことでじゃあ、段階的に廃止してくね。」 てことでしょうか? Google「(よ〜し、これでうちらだけはこの先生きのこれる。もしみんながついてこなくても、それはそれでうちらだけが一人勝ちィ(๑•̀ㅂ•́)و✧)」的な? とりあえずGoogle ChromeでAttribution Reporting APIが使えるようになったという発表で、 Web標準なったわけでもないまだ「新しい提案」レベルであるため、これによって本当にサードパーティCookieが廃止廃止されると判断するのはまだ早計と考えられる。 しかし、現在Webブラウザの世界シェアにおいて"強い寡占状態"を既に獲得しているChromeであるからこそこれほど強気の発信ができているのも事実であり、今後の成り行きを中止する必要があるかもしれない。 私見 (長文&私見のため、折りたたみ。「そういうのダルい」人は読み飛ばしてください) サイト間および特定ベンダによるユーザの追跡はプライバシー保護の観点において健全ではないのは事実で、それを実現しているサードパーティCookieも(広告というWeb業界における重要な収益化手段に関らざるを得ない要素ということから一旦目を背ければ)プライバシー保護の観点において好ましくない技術であることは否定できないと思う。 もちろん、Web収益上の問題を孕まない手段でサードパーティCookieを廃止できるならそれに越したことはなく、そして確かにAttribution Reporting APIはその一つの出口足り得る設計には見受けられる気がした。 しかし一方で、その提案・推進のしかたにおけるGoogleのやり方には眉をひそめざるを得ず、強い寡占状態を人質にした強引にも見える提案()は、市場経済における「寡占企業によるプライスリーダシップ(価格誘導)」にも似た「技術標準誘導」にも見えなくもない。 もし仮にGoogle Chromeが現在の圧倒的なブラウザシェアを持っていなかったとしても、同様にこのような提案・推進をおこなっていただろうか? このような進め方は、Web界隈にとって健全だろうか? 「GoogleによるWeb標準の私物化」の危惧が現実味を帯び始めていはいないか? そういえば、かの有名な「Don't Be Evil」って、もうGoogleの行動規範から外されているそうですね。いまは「Do the Right Thing」ですか、"正しさ"とは誰にとっての正しさですか?その"正しさ"には"「ゴールの」正しさ"だけでなく"「手段の」正しさ"も含まれているのでしょうか? Webがこれからも健全で素敵な技術・市場でありますように。 つづく
- 投稿日:2021-06-15T17:45:59+09:00
JavaScriptのでifを使った判定処理
はじめに JavaScriptのifを学習をしていてRubyとは違った記述などがあったのでアウトプットしていきます。 まずは簡単に記述します。 //()の中に条件式 if(true) { console.log('this is true'); } else { console.log('this is false'); } // this is true tureだった場合はconsole.logで'this is true'か返ってきます。 ここまでは記述の仕方は違えどtrueの時はこれ!falseの時はこれ!rubyの時のニュアンスは一緒だと思いました。 '==='と'=='の意味 if(1 == '1') { console.log('this is true'); } else { console.log('this is false'); } // this is true 数字の1文字列の'1'があります。 こちらが等しい時はもちろんtrueが返ってきます。 しかし数字と文字列は別の値だからfalseです!と思ったのですが。。。 this is true ええええええ! 人間から見たら1は1だけど文字式だから違うって前に学習しましたよ〜〜! MDNにちゃんと記載されてました。 JavaScript には、厳密な比較と型変換の比較の両方があります。厳密な比較 (例: ===) は、オペランドが同じ型で、内容も一致している場合にのみ真になります。もっとよく使用される抽象的な比較 (例: ==) は、比較する前にオペランドを同じ型に変換します。抽象的な関係比較 (例: <=) では、比較前にまずオペランドがプリミティブ型に変換され、それから同じ型に変換されます。 ==は等価演算子と言います。 等価演算子は、2 つのオペランドが同じ型でないならばオペランドを変換して、それから厳密な比較を行います。両方のオペランドがオブジェクトならば、 JavaScript は内部参照を比較するので、オペランドがメモリ内の同じオブジェクトを参照するときに等しくなります。 データの型までは比較されないので1と'1'は等しいとされtrueとなりました。 if(1 === '1') { console.log('this is true'); } else { console.log('this is false'); } // this is false イコールを3つした場合はfalseになりました。 ===の場合は厳密等価演算子と言います。 厳密等価演算子は、型変換なしでオペランド同士が (上に示した通り) 厳密に等しければ真を返します。 データの型も一緒でなければなりません。 今回は数字と1と文字式の1なのでfalseという結果になりました。 1とtrueの場合 if(1 == true) { console.log('this is true'); } else { console.log('this is false'); } // this is true boolean型のtrueなのになぜtrueとなるのか。 実はtrueは数字に変換した場合は1になります。 実際に数字に変換してみます。 const num = Number(true); console.log(num); // 1 //falseの場合は0です 出力結果の通り1になります。 3つの場合は厳密等価演算子となり、型も一緒でなければならないのでfalseになります。
- 投稿日:2021-06-15T17:44:54+09:00
JavaScriptでJavaのorElseと同じ挙動を実装する
はじめに JavaScriptでメソッドチェーンを書いた際にundefinedを参照しエラーになることがあるので、JavaのOptionalのorElseの挙動を実装。 const hoge = undefined; // "errorType": "TypeError", // "errorMessage": "Cannot read property 'fuga' of undefined" // エラーになる const fuga = hoge.fuga.fuga; コード const hoge = undefined; // undefinedだった場合、空文字が入る。 const fuga = hoge?.fuga?.fuga || ''; || は前の式がfalse(undefined)だった場合、後ろの値が代入されます。 ?.という書き方はオプショナルチェイニングというものです。 オプショナルチェイニングoptional chaining演算子 ?. は、接続されたオブジェクトチェーンの深くに位置するプロパティの値を、チェーン内の各参照が正しいかどうかを明示的に確認せずに読み込むことを可能にします。 ?. 演算子の機能は . チェーン演算子と似ていますが、参照が nullish (null または undefined) の場合にエラーとなるのではなく、式が短絡され undefined が返されるところが異なります。 関数呼び出しで使用すると、与えられた関数が存在しない場合、 undefined を返します。 おわりに JavaScriptでメソッドチェーンで書いた際にundefined参照で落ちるということがなくなり、デフォルト値を返却できるようになります。 参考
- 投稿日:2021-06-15T17:21:45+09:00
【React】React クイズ!!
はじめに いきなりですがクイズです!! React.js初心者向けのクイズを3問考えました。 クイズは、Reactの関数コンポーネントを用いたstateとライフサイクルに関する問題です。 クイズの答えは、記事の最後に記載しています。 第1問: 以下の様なコードを実行した場合、どのような順番に実行されるでしょうか? A〜Cの回答で答えよ。 import React, {useState, useEffect} from 'react' const Button = () => { const [count, setCount] = useState(0) useEffect(()=> { // ① console.log("①") return ()=> { // ② console.log("②") } }, []) return ( // ③ <div> {count} </div> ) } export default Button 回答: A. ① → ② → ③ B. ③ → ① → ② C. ② → ③ → ① 第2問: 以下の様なコードがあった場合、countの値はいくつになるでしょうか? import React, {useState, useEffect} from 'react' const Button = () => { const [count, setCount] = useState(0) useEffect(()=> { setCount(count + 100) setCount(count + 1000) setCount(count + 10000) setCount(count + 10000) setCount(count + 100000) setCount(count + 1000000) return ()=> { console.log("clean up") } }, []) return( <></> ) } export default Button 回答: A. 1111100 B. 100 C. 1000000 D. 1121100 第3問: 以下の様なコードがあった場合、初回マウント時の正しい実行順序を答えなさい。 import React, {useState, useEffect} from 'react' const Button = () => { const [count1, setCount1] = useState(0) const [count2, setCount2] = useState(0) const [count3, setCount3] = useState(0) useEffect(()=> { // ① console.log("update count1") }, [count1]) useEffect(()=> { // ② console.log("update count3") }, [count3]) useEffect(()=> { // ③ console.log("update") return ()=> { console.log("clear") } }, []) useEffect(()=> { // ④ console.log("update count2") }, [count2]) return( <></> ) } export default Button 回答: A. ③ → ① → ④ → ② B. ① → ② → ③ → ④ C. ③ → ① → ② → ④ D. ④ → ③ → ② → ① - - - - - - - - - - - 問題の答え 第1問の答え: B. ( ③がclassにおけるrender()、①がcomponent Did Mount()、②がcomponent will unmount()に該当する。) 第2問の答え: C. 第3問の答え: B. コードは上から順番に実行されます。component Did Mount()後のアップデートでは、①→②→④の順に実行されます。
- 投稿日:2021-06-15T15:21:26+09:00
フィボナッチ数列を求めるワンライナー各言語まとめ
お遊び記事です.こちらの記事の姉妹版です.いやその,某ラノベ読んでたらとりあえず21番目までのフィボナッチ数列を求める魔術式ワンライナーが書きたくなりましてね,はい. 制約 フィボナッチ数を求める方法はいろいろありすぎるので,次の制約を設けます. 一般項は使わない. ライブラリやモジュールの読み込みは行わない. こうすると,自ずと関数型プログラミングに近くなるかもしれません.ますます魔(略 気のせいでした. 各言語での記述例 0番目から21番目の値を求める最も短いと思われるREPL実行記述(+確認した実装・バージョン)のみを載せています.より短い表現&他言語版歓迎. CommonLisp(SBCL2.0.0),Scheme(Gauche0.9.10) (do((a 0 b)(b 1(+ a b))(l '()`(,@l ,a)))((=(length l)22)l)) Python2(CPython2.7.17) reduce(lambda x,_:x+[x[-1]+x[-2]],'_'*20,[0,1]) Python3(CPython3.7.3) g=lambda*a:len(a)<22and g(*a,a[-1]+a[-2])or a;g(0,1) Python3.8(CPython3.9.5) [a:=0,b:=1]+[(b:=a+(a:=b))for _ in'_'*20] Ruby(2.5.5) a=-b=1;(0..21).map{b=a+a=b} JavaScript(Node.js10.24.0) [a=-1,b=1,...Array(20)].map(c=>b=a+(a=b)) J言語(9.0.2) (,{:+_2&{)^:20 i.2 Haskell(GHC8.4.4) let f@(_:g)=0:1:zipWith(+)f g in take 22 f PostScript(Ghostscript9.52) 0 == 0 1 20 {0 1 2 index -1 1 {pop exch 1 index add} for == pop pop} for Smalltalk(GNUSmalltalk3.2.5) | a b | a := -1. b := 1. (0 to: 21) collect: [:n | b := a + (a := b)] Perl(5.28.1) do{@_=(0,1);push@_,$_[-2]+$_[-1]for 0..19;@_} Clojure(1.10) (take 22((fn f[](lazy-cat[0 1](map +(f)(rest(f))))))) Julia(1.0.3) 1|>(a,b=0)->0:21 .|>_->a=(b+=a)-a Forth(Gforth0.7.3) :noname 1 0 22 0 do dup . tuck + loop ; execute R(3.5.2) c(a<-0,b<-1,replicate(20,b<<-a+(a<<-b))) 備考 記事に関する補足 reduceつおい.次々と反復構文に置き換えられてしまった…. J言語の例はねっとからのぱくりです.独自には記述できなかった….→そしてそのぱくりから更に短く(コメントより).J言語にはどうあがいても短さでは勝てなさそう. Perlの記述例については,デバッガモード起動(perl -de0)のxコマンドで表示することを想定しています. 更新履歴 2021-06-16:JavaScript,Perl,Smalltalk,Python2,Python3の記述例を変更(コメントより) 2021-06-16:Forth,Rの記述例を追加(Twitterより) 2021-06-16:確認した実装・バージョンとREPL実行の旨記載 2021-06-15:Ruby,J言語の記述例を変更(Twitter,コメントより) 2021-06-15:SchemeとCommon Lispの記述例を変更・統合(Twitterより) 2021-06-15:Haskell,PostScript,Smalltalk,Perl,Clojure,Julia,Python3.8の記述例を追加(Twitter,コメントより) 2021-06-15:初版公開(Scheme,Common Lisp,Python2,Python3,Ruby,JavaScript,J言語)
- 投稿日:2021-06-15T14:08:04+09:00
【ES2020】自作ライブラリの書き方・読み込み方
序論 自作ライブラリを作ると色々考えるべき問題がありますよね。 privateな変数 ライブラリの渡し方 → windowを汚染 → 変数名の衝突 ライブラリを分割をするならば、どの順番で読み込むか ライブラリを読み込み終えるまで、どのようにしてメインの処理を待つのか 以上の問題に対する回答をES2020で導き出せたので、紹介します。 本論 (async()=>{ await import('https://rpgen3.github.io/lib/lib/jquery-3.5.1.min.js'); const rpgen3 = await Promise.all([ 'baseN', 'css', 'hankaku', 'input', // ... ].map(v=>import(`https://rpgen3.github.io/mylib/export/${v}.mjs`))).then(v=>Object.assign({},...v)); // ここからメインの処理を書く })(); これはjQueryと私の自作ライブラリを読み込む処理です。それぞれ動的importした配列をPromise.all()に渡しています。すべてのPromiseがresolveされるとexportされたものの連想配列が配列に格納され、thenの引数に渡されます。例えば、mylib1~3を読み込んだときexportされたものが以下のように渡されます。 [ {myfunc1, myfunc2, myfunc3}, // mylib1 {myfunc4, myfunc5}, // mylib2 {myfunc6, myfunc7, myfunc8, myfunc9} // mylib3 ] この配列をスプレッド構文(...)で展開し、Object.assign関数で1つのオブジェクトに統合します。これを変数に代入することで自作ライブラリの読み込みが完了します。従来の書き方と比較して以下のメリットが考えられます。 自作ライブラリの関数群を格納するオブジェクトの名前は自由 → jQueryとprototype.jsの$のようなライブラリ同士の変数名の衝突を回避できる 自作ライブラリの読み込む順番を気にしなくても良い 自作ライブラリごとにscriptタグを用意しなくて良い → HTMLとJSの依存関係を最小限にできる 次に、exportするモジュールの記述を見ていきましょう。 import {save, load} from 'https://rpgen3.github.io/mylib/export/save.mjs'; const _undef = void 0; const _makeId = () => 'label-' + Math.random().toString(36).slice(2); const _input = (elm, p, {get, get2, set}) => { // ... }; export const addInputStr = (dl, p) => { // ... }; // ... モジュールをimportで読み込むとき、exportされた変数や関数以外はブロック文を書かなくとも全てスコープで隔離されています。そのため、private変数を用意するためにわざわざクロージャを書いてネストを深くする必要がありません。そして、exportされたものは動的importのawaitで受け取れます。つまり、windowオブジェクトを一切汚染しないでライブラリの関数が渡せます。また、モジュールが他のモジュールに依存している場合、import文で読み込めば良いです。 結論 Promise.allと、ES2020の動的importを使えば、非常にシンプルな書き方で自作ライブラリを表現できます。
- 投稿日:2021-06-15T13:20:11+09:00
Node.jsのMySQLでINSERTしたレコードを取得したい時
課題 Node.jsのMySQLライブラリで INSERTした後のレスポンスにレコードの情報が返却されるのかと思っていたら そうでは無かったので、調べました。 テーブル情報 テーブル名 table_name カラム情報 id:Auto increments age:Int 解決方法 const [result] = await mysql.execute( `INSERT INTO table_name age VALUES 26`,[]); const [rows] = await mysql.execute( `SELECT * FROM table_name WHERE id = ${result.insertId};`,[]); console.log(rows[0]); 解説 INSERTのSQLのレスポンス値にinsertIdという項目があり、 これがテーブルのidの値が返ってくるようだったので、 それを使用してSELECTしている。
- 投稿日:2021-06-15T13:12:13+09:00
InDesign スクリプト テーブルをXMLに変換
テーブルをXMLに変換するスクリプトは、これで良いのかな・・・? /* 更新 2021/06/15 */ // アプリ指定 #target "indesign"; // スクリプト名 var scriptName = "テーブルをXMLに変換"; //スクリプトの動作指定(一つのアンドゥ履歴にする、及び、アンドゥ名) app.doScript(function () { // XMLのエンコーディング var encoding = "Shift_JIS" // ダイアログ var dialogueFlg = confirm("選択したテキストフレームにある最初のテーブルをXMLに変換します。" + "\r\r" + "ヘッダー(最上段の行)は変換されません。" + "\r\r" + "要素名の最初には数字(0~9)や空白等が使えませんので注意して下さい。" + "\r\r" + "テーブルの例" + "\r" + "要素名" + "\t" + "要素の内容" + "\t" + "属性1" + "\t" + "属性1の内容" + "・・・等々","", scriptName); // Noの場合 if (dialogueFlg == false) { // 終了 exit(); } // タグ名のプレフィックスを入力 var tagNamePrefix = prompt("要素名のプレフィックスが必要な場合は入力して下さい。","",scriptName); // キャンセルされた時の処理 if(tagNamePrefix == null){ // スクリプトを終了 exit(); } // 対象のストーリー var targetStory; // 成型されたXML var moldXML = "<?xml version='1.0' encoding='" + encoding + "' standalone='yes'?>" + "\r" + "<Root>"; // 選択がある場合 if(app.activeDocument.selection.length > 0){ // 選択オブジェクトのストーリーを入れる targetStory = getStory(app.activeDocument.selection[0]); // テーブルがある場合 if(targetStory.tables.length > 0){ // ヘッダーを除いた行の数だけ繰り返す for(var i = 1; i < targetStory.tables[0].rows.length; i++){ // 行にセルが2つ以上ある場合 if(targetStory.tables[0].rows[i].cells.length >= 2){ // セルが空ではない場合 if(targetStory.tables[0].rows[i].cells[0].contents != ""){ // XML成型 moldXML += "\r" + "<" + tagNamePrefix + targetStory.tables[0].rows[i].cells[0].contents; // 三っ目のセルから初めて一つ飛ばしてセルの数だけ繰り返す for(var ii = 2; ii < targetStory.tables[0].rows[i].cells.length; ii += 2){ // セルに内容がない場合 if(targetStory.tables[0].rows[i].cells[ii].contents != ""){ // 次のセルが存在する場合 if(ii + 1 < targetStory.tables[0].rows[i].cells.length){ // XML成型 moldXML += " " + targetStory.tables[0].rows[i].cells[ii].contents + "='" + targetStory.tables[0].rows[i].cells[ii+1].contents + "'"; } } } // XML成型 moldXML += ">" + targetStory.tables[0].rows[i].cells[1].contents + "</" + tagNamePrefix + targetStory.tables[0].rows[i].cells[0].contents + ">" } } } } // 文字を追加 moldXML += "\r" + "</Root>"; // ダイアログX座標 var dialogWidthXCoordinates = 0; // ダイアログY座標 var dialogWidthYCoordinates = 0; // ダイアログ幅 var dialogWidth = (app.activeDocument.layoutWindows[0].bounds[3] - app.activeDocument.layoutWindows[0].bounds[1]) / 2 // ダイアログ高さ var dialogHeight = (app.activeDocument.layoutWindows[0].bounds[2] - app.activeDocument.layoutWindows[0].bounds[0]) / 2 // 段落スタイル選択用ダイアログ作成 var textWindow = new Window("dialog", scriptName, [dialogWidthXCoordinates, dialogWidthYCoordinates, dialogWidthXCoordinates + dialogWidth, dialogWidthYCoordinates + dialogHeight]); // エディットテキスト追加 textWindow.add("edittext",[ 20, 20, dialogWidth - 20, dialogHeight - 20],moldXML,{readonly:true,multiline:true}); // 中央に textWindow.center(); // 表示 textWindow.show(); } //スクリプトの動作指定の続き }, ScriptLanguage.JAVASCRIPT, [scriptName], UndoModes.ENTIRE_SCRIPT, scriptName); /* ストーリーを取得する関数、引数(オブジェクト)の宣言 */ function getStory(anyObject) { // parentStoryプロパティが存在する場合 if (anyObject.hasOwnProperty("parentStory") == true) { // エラー処理 try{ // parentStoryの値がある場合 if (anyObject.parentStory) { // コンストラクタ名を戻す return anyObject.parentStory; } // エラーの場合 }catch(e){ // 抜ける return; } // Applicationの場合 } else if (anyObject.constructor.name == "Application") { // 抜ける return; } // オブジェクトの階層を一つ上げて再帰関数 return getStoryConstructorName(anyObject.parent); } /* ストーリーを取得する関数の宣言終了 */
- 投稿日:2021-06-15T12:55:55+09:00
javascriptでbindを使ってprivate変数を実現する
bindを使うと、javascriptでprivate変数を実現できるという話です。 結論を先に書くと、こうなります。 function Counter(){ let privates = { count : 0, }; this.add = Counter.prototype.add.bind(this, privates); this.getCount = Counter.prototype.getCount.bind(this, privates); } Counter.prototype.add = function(privates, diff) { privates.count += diff; } Counter.prototype.getCount = function(privates) { return privates.count; } let c1 = new Counter(); c1.add(2); console.log(c1.getCount()); // 2を表示。 以下、解説です。 クロージャを使う方法(メモリ効率が悪い) javascriptでprivate変数を作りたいという要望が出た時、一般的には以下のような、クロージャを利用した実装方法がとられることが多いと思います。 // クロージャを利用したprivate変数 function Counter(){ let _count = 0; this.add = diff => _count += diff; this.getCount = () => _count; } この方法はシンプルで理解しやすいですが、Counterを生成するたびにaddやgetCountメソッドが生成される為、メモリ効率がよくありません。 let c1 = new Counter(); let c2 = new Counter(); let c3 = new Counter(); 上記のc1, c2, c3それぞれに、異なるメモリ空間のaddとgetCountが生成されて割り当てられています。 処理内容は完全に同じなのですから、メソッドの生成は1回にして、それを全インスタンスで使いまわせるようにしたいところです。 prototypeを使ったメソッド定義(privateにならない) メソッドがインスタンス毎に生成されないようにする為には、通常、以下のようにprototypeプロパティにメソッドを追加します。 この方法なら、addとgetCountは1回しか生成されず、全てのCounterインスタンスで共有される為、メモリ効率の問題は起こりません。 // prototypeプロパティを使ったfunctionクラス定義(動作しない) function Counter(){ let _count = 0; } Counter.prototype.add = function(diff) { return _count += diff; } Counter.prototype.getCount = function() { return _count; } もちろんこの書き方はエラーになります。なぜなら、_count変数が、addやgetCountから参照できない為です。 _count変数はCounterのコンストラクタ内で定義されたローカル変数ですから、その外で定義されているaddやgetCountからそれを参照することはできません。 _count変数をaddやgetCountから参照する為には、以下のように書きます。 // prototypeプロパティを使ったfunctionクラス定義(動作するがprivateになっていない) function Counter(){ this._count = 0; } Counter.prototype.add = function(diff) { return this._count += diff; } Counter.prototype.getCount = function() { return this._count; } _count変数はthisのメンバ変数になっているので、addやgetCountからも参照できるようになります。 しかしこれでは、当初の目的である、_count変数をprivateにするという点からすると失格です。 thisのメンバ変数だということは、外部からも参照可能であることを意味します。これでは意味がありません。 let c1 = new Counter(); c1.add(1); console.log(c1._count); // エラーにならず、0が出力される。 インスタンス変数ではなくクラス変数になっている例 稀に、以下のようなコードを書いて「private変数を実現できました!」と書いている人もいますが、正しくありません。 // private変数がインスタンス毎に生成されていない、つまりクラスメンバ変数になっている function Counter(){ } (function(){ let _count = 0; Counter.prototype.add = function(diff) { return _count += diff; } Counter.prototype.getCount = function() { return _count; } })(); addやgetCountからのみ参照できる_count変数をクロージャ内で定義しており、一見うまく動くように見えます。 しかし、これは正しく動きません。 let c1 = new Counter(); let c2 = new Counter(); c1.add(1); console.log(c2.getCount()); // 0ではなく1と表示される。つまりc1とc2が同じ_countを共有している。 上記のやり方は、「クラス変数」を定義するには有効ですが、「インスタンス変数」を定義したことにはなっていません。 これまでのまとめ 問題をまとめると、次のようなことです。 (1) private変数を作るにはコンストラクタ内のローカル変数として定義しなくてはならない。 (2) そのローカル変数をメソッドから参照するにはメソッドも同じくコンストラクタ内で定義しなくてはならないが、メモリ効率が悪い。 (3) メソッドのメモリ効率をよくしようとコンストラクタの外に出すと、private変数(ローカル変数)を参照できなくなる。 バインドを利用したprivate変数 これらの問題を解決する方法として、バインドを利用した以下のような中間的な方法がありますので、今回ご紹介したいと思います。 function Counter(){ let privates = { count : 0, }; this.add = Counter.prototype.add.bind(this, privates); this.getCount = Counter.prototype.getCount.bind(this, privates); } Counter.prototype.add = function(privates, diff) { privates.count += diff; } Counter.prototype.getCount = function(privates) { return privates.count; } let c1 = new Counter(); c1.add(2); console.log(c1.getCount()); // 2を表示。 考え方としては、private変数をまとめて「privates」という名前のローカルオブジェクト変数に格納し、それを、prototypeで定義したメソッドにバインドし、自身の新たなメソッドとして再定義します。 こうすることで、addやgetCountに、コンストラクタで生成したprivate変数を渡しつつ、外部からはそれらを参照できないようにします。 このやり方もインスタンス毎にメソッドを生成していますが、メソッドの大部分はprototypeの方に存在する唯一のものを利用しているので、そこまで無駄にはなりません。 ちなみに、prototypeプロパティを使ってはいますが、実際にはすぐに同名のメソッドで上書きしており、prototypeチェーンとしては機能していません。 なので、prototypeを使わず、以下のようにfnというプロパティを別途作ってそこに定義しても、同様の結果になります。 function Counter(){ let privates = { count : 0, }; this.add = Counter.fn.add.bind(this, privates); this.getCount = Counter.fn.getCount.bind(this, privates); } Counter.fn = { add: function(privates, diff) { privates.count += diff; }, getCount: function(privates) { return privates.count; } } prototypeチェーンを放棄していますので、prototypeチェーンを前提にした継承構造を作ったりする際には向きませんが、比較的シンプルに、理解しやすく、管理もしやすいやり方でprivate変数を実現する方法として、覚えておいても良いかもしれません。IE11を含め、ほぼすべてのブラウザで動作します。 ところで、今回のやり方はIE11でも動作するやり方ですが、もしこの書き方をclass構文で書きたい場合は、以下のように書けます。class構文はfunctionとprototypeを使ったクラス宣言の糖衣構文にすぎないので、同じようにかけるわけです。 class Counter { constructor() { let privates = { count : 0, } this.add = Counter.prototype.add.bind(this, privates); this.getCount = Counter.prototype.getCount.bind(this, privates); } add(privates, diff) { privates.count += diff; } getCount(privates) { return privates.count; } } ただ、この書き方はほぼすべてのブラウザで動作するものの、IE11ではclass構文がサポートされていない為、動作しません。 ですので、どうしてもIE11でclassを使ってprivateフィールドを実現したい、という場合には、Babelなどのプリプロセッサを使って上記の書き方をする、というのも手かもしれません。 本物のprivateフィールド構文(ES6) もしIE11を捨てても良いのであれば、class構文自体にまもなく正式導入される、本物のprivateフィールド構文を利用した方がいいかもしれません。 javascriptの最新仕様ES6では、もうすぐclassにprivateフィールドが導入される予定です。 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes/Private_class_fields これが実現すると、次のように書くことができるようになります。フィールドの先頭に # を付けるとprivateになります。 シンプルでわかりやすいですね。 privateフィールドについては様々な実装方法が検討されたようですが、現在のjavascriptの動作速度を落とさずに実装する為には、プロパティ名を見ればそれがprivateかどうかをjavascriptエンジンが判断できるというのが大事だった模様です。 class Counter { #pcount = 0; add(diff) { this.#pcount += diff; } get count() { return this.#pcount; } } そして実は、privateフィールドはもう、いくつかのブラウザ使うことができます(IE11はダメです。今後もサポートされることはないでしょう)。 Chromeでは2019/4/23から使えており、EdgeやOperaでも最新版で問題なく使えます。 SafariとFirefoxの対応が遅れていたようですが、Safariは2021/4/26に、Firefoxも2021/6/29にサポートされる見込みとのこと。 モバイル版のブラウザの対応も同様のようです。 レガシーブラウザ対応は相変わらず重要とは思いますが、数年後にはもう、javascriptのクラス定義は、classを使ったやり方に統一されていくのでしょう。
- 投稿日:2021-06-15T11:32:40+09:00
Avenues物語~より良い教育を目指して~
今回はBejamas社が取り組んだJamstackの事例についてご紹介したいと思います Avenuesにとっては、静的なサイトでのパフォーマンスの向上は、合わせて良い事となりました。 著者:アルカディウス・ゴレツキ 2020年2月6日 原文:https://bejamas.io/blog/case-study-avenues/ 興味深くやりがいのあるプロジェクトは私たちのスキルを高めてくれます。有意義で意欲的なプロジェクトは、私たちの心に豊かさをもたらします。そのため、より良い教育で世界を変え、未来を切り拓くことを目指す学校のプロジェクトに取り組めたことはとても光栄でした。 光栄であり、大きな責任です。 Avenuesの物語 Avenues: The World Schoolは、多くのキャンパスを持つ学校の一つで、世界に焦点を当てた変革的な学習体験を世界中の学生達に提供しています。ニューヨークに本社を置き、サンパウロと深圳にキャンパスを持ち、バーチャルキャンパスを世界中の人々に公開しているAvenuesは、より良い実践方法を開発し、現状に挑戦し、キャンパスだけではなく、世界中の教育に貢献することを使命としています。Avenuesのミッションをウェブ上で紹介するために協力したことは、困難ではありましたが、非常にやりがいのあるものになりました。 1-1 概要 以前のAvenuesのサイトはWordPressで構築されてから、6年以上が経っていました。長年の維持管理が複雑になってきたため、AvenuesのウェブサイトマネージャーであるDmitry Terner氏は、今後の再設計について明確なアイデアを持っていました。彼は、少なくともあと5年以上はテックコミュニティで主流になる、モダンなウェブ開発アーキテクチャでWordpressを置き換えたいと考えていました。 以前はパフォーマンスが脆弱であり、かつモバイルからの訪問者の割合が大きかったため、スムーズで高速なモバイルフレンドリーなウェブサイトにしたいと思っていました。また、コストを削減して開発を効率化したいとも考えていました。さらに、彼は多言語のリクエストをバックアップするシンプルでエレガントなCMSも求めていました。 1-2 使用したスタック 問題点やビジョンを考えると、Avenuesはすでにサーバーレスのインフラを持っていたため、ヘッドレスCMSの導入は最初から理にかなっていました。そこからJamstackを選択するのは自然な流れでした。 静的サイト生成ツールとしてのGatsby、コンテンツ管理ツールとしてのStoryblok、ホスティングとCI/CDソリューションとしてのNetlify Storyblok Avenuesの場合、標準的なヘッドレスCMSとシームレスなエディタ用の「ページビルダー」を組み合わせたCMSとしてStoryblokを選択しました。ライブプレビューオプションのおかげで、静的なウェブサイトのコンテンツをより便利に更新することができます。基本的に担当編集者は、これを使えば、新しいページを作ることができるだけでなく、古いページへの変更点がウェブサイト上でどのように見えるかを、再構築を待つことなく瞬時に確認することができます。 このスタックは信頼性の高いユーザー/エディタ中心のコンボであることが証明されたため、すでにいくつかのプロジェクトで使用されています。Storyblokは、編集者がウェブサイトのコンテンツを完全に制御できる素晴らしいCMSです。今までは、中国のサーバーでファイルを保持することが義務化されているという懸念がありましたが、GatsbyとBuddyのスムーズな統合には本当に驚かされました。しかし、いくつかの課題がありました。 Jamstackのメリットをストレートに受けることができました。コストは、拡張機能の実装の容易さや固有のセキュリティの向上と同様に、明らかにメリットがありました。 ドミトリー・ターナー/アベンニューズのサイトマネージャー 1-3 このプロジェクトでの最大の課題 本来なら大きな助けになるはずだったStyled-Componentsが私たちの抱えていた最大の課題の一つになりました。 スタイリングされたコンポーネントとは?Styled Components は CSS-in-JS の変形版で、クラス名が衝突せず、実際の CSS コードを書いてコンポーネントのスタイル設定をすることができます。 小さくて、似ているようで違うものをいくつも持つよりもカスタマイズ性の高いコンポーネントを作ることに注力していました。クライアントからの厳しいデザインガイドラインがあり、デザインの悪いコンポーネントはウェブサイト全体のパフォーマンスに悪影響を及ぼす可能性があったため、この作業は非常に困難を極めました。 1-4 結果 現在のセットアップは、はるかに合理化され、費用対効果が高く、汎用性が高いことが判明しました。まだこれを活用したことはありませんが、APIアクセスでコンテンツレイヤーとプレゼンテーションレイヤーを完全に分離できるようになったことは、Avenuesチームに新たな可能性をもたらしました。彼らは今、キャンパスのロビースクリーン用のアプリケーションを使って、これをどのように拡大できるかを検討しています。サーバーインフラの心配をしなくて済むようになったのは大きなメリットです。 Bejamasにとって、学習は適切な教育から始まる刺激的な旅であり、できれば一生続くことを願っています。人生を教育に捧げる人々と一緒に働くことができたことで、私たちの努力ははるかに価値のあるものになりました。 BejamasはJamstack製品の専門家として、自社では検討していなかったStoryblok CMSを推奨していました。そのような指導、親しみやすさ、専門知識は、私たちが求めていたものであったため、本当にこのプロジェクトが行えてよかったと思っています。 ドミトリー・ターナー/アベンニューズのサイトマネージャー Avenuesプロジェクトで、私たちは真に変化を起こし、未来に影響を与え、より速く、より良いインターネットのウェブサイトを一度に構築することができました。 今日の世界に変化をもたらしたと言える人は何人いますか? 最後まで読んで下さり、ありがとうございました Jamstackに関心がある方はこちらまでお問合せください! 株式会社ヒューマンサイエンス https://www.science.co.jp/
- 投稿日:2021-06-15T08:03:28+09:00
event driven
Event driven is style of how to operate program. Usually computer programming operation is start from top to bottom, But this style program is started by timing event is happened. For example "when the page shows up on browser", "When Clint click on botton or Link"
- 投稿日:2021-06-15T05:51:16+09:00
InDesign スクリプト カスタムテキスト変数をテキストに変換
カスタムテキスト変数をテキストに変換するスクリプトは、これで良いのかな・・・? /* 更新 2021/6/15 */ // アプリ指定 #target "indesign"; // スクリプト名 var scriptName = "カスタムテキスト変数をテキストに変換"; //スクリプトの動作指定(一つのアンドゥ履歴にする、及び、アンドゥ名) app.doScript(function () { // ダイアログ var dialogueFlg = confirm("カスタムテキスト変数のインスタンスをテキストに変換します。","", scriptName); // Noの場合 if (dialogueFlg == false) { // 終了 exit(); } // ダイアログ var dialogueFlg2 = confirm("カスタムテキスト変数を削除しますか?","", scriptName); // カスタムテキスト変数の名前 var customTextVariablesName = []; // 変換した数 var convertInstancesNumber = 0; // 削除した数 var removeNumber = 0; // テキスト変数の数だけ繰り返す for(var i = 0; i < app.activeDocument.textVariables.length; i++){ // カスタムテキスト変数の場合 if(app.activeDocument.textVariables[i].variableType == VariableTypes.CUSTOM_TEXT_TYPE){ // 数を増やす convertInstancesNumber += app.activeDocument.textVariables[i].associatedInstances.length; // テキストに変換 app.activeDocument.textVariables[i].convertToText(); // 名前を配列に追加 customTextVariablesName.push(app.activeDocument.textVariables[i].name); } } // trueの場合 if(dialogueFlg2 == true){ // カスタムテキスト変数の名前の数だけ繰り返す for(var i = 0; i < customTextVariablesName.length; i++){ // 削除 app.activeDocument.textVariables.itemByName(customTextVariablesName[i]).remove(); // 数を増やす removeNumber++; } } if(dialogueFlg2 == false){ // 結果を表示 alert("テキストに変換したカスタムテキスト変数のインスタンスの数 " + convertInstancesNumber, scriptName); // 以外の場合 }else{ // 結果を表示 alert("テキストに変換したカスタムテキスト変数のインスタンスの数 " + convertInstancesNumber + "\r" + "削除したカスタムテキスト変数の数 " + removeNumber, scriptName); } //スクリプトの動作指定の続き }, ScriptLanguage.JAVASCRIPT, [scriptName], UndoModes.ENTIRE_SCRIPT, scriptName);
- 投稿日:2021-06-15T04:13:04+09:00
InDesign スクリプト カスタムテキスト変数の追加と変更
カスタムテキスト変数の追加と変更をおこなうスクリプトは、これで良いのかな・・・? /* 更新 2021/06/16 */ // アプリ指定 #target "indesign"; // スクリプト名 var scriptName = "カスタムテキスト変数の追加と変更"; //スクリプトの動作指定(一つのアンドゥ履歴にする、及び、アンドゥ名) app.doScript(function () { // 分割文字(タブは\t) var splitCharacter = "\t" // 分割文字名 var splitCharacterName; // \tの場合 if(splitCharacter == "\t"){ // タブを入れる splitCharacterName = "タブ"; // 半角スペースの場合 }else if(splitCharacter == " "){ // 半角スペースを入れる splitCharacterName = "半角スペース"; // 全角スペースの場合 }else if(splitCharacter == " "){ // 全角スペースを入れる splitCharacterName = "全角スペース"; // 以外の場合 }else{ // 分割文字を入れる splitCharacterName = splitCharacter; } // ダイアログ var dialogueFlg = confirm(splitCharacterName + "で区切られた文章でカスタムテキスト変数の追加と内容が違う場合に変更をおこないます。" + "\r\r" + "テキストフレーム等を選択して実行して下さい。" + "\r\r" + "カスタムテキスト変数名" + splitCharacter + "内容"+ "\r" + "の順に記述して下さい。","", scriptName); // Noの場合 if (dialogueFlg == false) { // 終了 exit(); } // 対象のストーリー var targetStory; // 対象の段落 var targetParagraphs = [] // テキスト変数の名前 var variablesName; // テキスト変数の内容 var variablesContents; // 追加されたテキスト変数 var addedTextVariables; // 分割されたテキスト var splitText; // 追加されたテキスト変数の数 var addedTextVariablesNumber = 0; // 変更されたテキスト変数の数 var changeTextVariablesNumber = 0; // 選択がある場合 if(app.activeDocument.selection.length > 0){ // 選択オブジェクトのストーリーを入れる targetStory = getStory(app.activeDocument.selection[0]); // ストーリーを段落で分割 targetParagraphs = targetStory.contents.split("\r") // 段落の数だけ繰り返す for(var i = 0; i < targetParagraphs.length; i++){ // 段落の内容を特定の文字で分割する splitText = targetParagraphs[i].split(splitCharacter); // 配列の数が二つ以上ある場合 if(splitText.length >= 2){ // テキストが無い場合 if(splitText[0] == ""){ // 次の繰り返しへ continue; }else{ // 一つ目を入れる variablesName = splitText[0]; } // 二つ目を入れる variablesContents = splitText[1]; // テキスト変数が存在する場合 if(app.activeDocument.textVariables.itemByName(variablesName).isValid == true){ // カスタムテキスト変数の場合 if(app.activeDocument.textVariables.itemByName(variablesName).variableType == VariableTypes.CUSTOM_TEXT_TYPE){ // 内容が違う場合 if(app.activeDocument.textVariables.itemByName(variablesName).variableOptions.contents != variablesContents){ // テキスト変数を追加(特定の文字のチェック対策) addedTextVariables = app.activeDocument.textVariables.add(); // 変数のタイプの指定 addedTextVariables.variableType = VariableTypes.CUSTOM_TEXT_TYPE; // 内容 addedTextVariables.variableOptions.contents = variablesContents; // 内容が違う場合 if(app.activeDocument.textVariables.itemByName(variablesName).variableOptions.contents != addedTextVariables.variableOptions.contents){ // 内容を変更 app.activeDocument.textVariables.itemByName(variablesName).variableOptions.contents = variablesContents; // 数を増やす changeTextVariablesNumber++; } // 削除 addedTextVariables.remove(); } } // 以外の場合 }else{ // テキスト変数を追加 addedTextVariables = app.activeDocument.textVariables.add(); // エラー処理 try{ // 名前 addedTextVariables.name = variablesName; // エラーの場合 }catch(e){ // 削除 addedTextVariables.remove(); // 次の繰り返しへ continue; } // 変数のタイプの指定 addedTextVariables.variableType = VariableTypes.CUSTOM_TEXT_TYPE; // 内容 addedTextVariables.variableOptions.contents = variablesContents; // 数を増やす addedTextVariablesNumber++; } } } } // 結果を表示 alert("変換元の段落数 " + targetParagraphs.length + "\r\r" + "追加数 " + addedTextVariablesNumber + "\r" + "変更数 " + changeTextVariablesNumber, scriptName); //スクリプトの動作指定の続き }, ScriptLanguage.JAVASCRIPT, [scriptName], UndoModes.ENTIRE_SCRIPT, scriptName); /* ストーリーを取得する関数、引数(オブジェクト)の宣言 */ function getStory(anyObject) { // parentStoryプロパティが存在する場合 if (anyObject.hasOwnProperty("parentStory") == true) { // エラー処理 try{ // parentStoryの値がある場合 if (anyObject.parentStory) { // コンストラクタ名を戻す return anyObject.parentStory; } // エラーの場合 }catch(e){ // 抜ける return; } // Applicationの場合 } else if (anyObject.constructor.name == "Application") { // 抜ける return; } // オブジェクトの階層を一つ上げて再帰関数 return getStoryConstructorName(anyObject.parent); } /* ストーリーを取得する関数の宣言終了 */
- 投稿日:2021-06-15T02:45:03+09:00
Google Apps Scriptを書いているやつは、今すぐすべての関数名の末尾にアンダースコアを入れろ
2021/06/15 追記 「すべての関数名」と記事タイトルでは煽り気味にしているが、本当にすべてに設定すると機能しなくなる。この記事やドキュメントを読んだ上で取捨選択は必要である。 特定の条件を満たすとGoogle Apps Script(GAS)に書かれた関数は外部から呼び出せる。 具体的な手段は控える(ただし、上のドキュメントを読めばウェブに詳しい人は即座に実行できるだろう)が、これは HTML Service でサイトを公開していることによって引き起こされる。 HTML Service で公開された HTML からサーバー側の関数を呼び出す機能があるのだが、なんとグローバル関数は初期状態ですべて呼び出すことが可能となっている。これは大変危険な初期状態に見えるがドキュメントに明示されているので、ぶっちゃけ仕方がない。関数名の末尾にアンダースコアをつけるか、グローバルに直接置かないようにすることで対処しよう。下記がその例である。 function privateFunction_() { return 'sample value!!'; } (function () { function sampleFunction1() { return 'sample value!!'; } })(); var obj1 = { sampleFunction2: function () { return 'sample value!!'; } } class Member { sampleFunction3() { return 'sample value!!'; } } Google Apps Scriptで組まれたウェブサービスにアクセスできる人はだれでも実行でき、しかもその関数名一覧も取れるために、アプリケーションによってはかなり致命的な事態を招く可能性がある。急ぎ確認したほうが良い。 ただし、この対応を行った関数は関数単体で実行してデバッグする機能だったり、トリガーだったりに使うことはできない。すなわち、 doGet とかは onEdit とかは必ず外部から実行されうるのである。トリガー関数で想定シチュエーションかどうかを何らかの方法で入念に確認する必要がある。これは仕様としてどうなんだ…?と思わざるを得ないが…。 逆に外部から実行できる関数一覧は私が確認した限りではデバッグ可能な関数と一致している。その一覧を見て、外部から呼び出されてはまずい関数が入っていないか確認しよう。
- 投稿日:2021-06-15T02:07:15+09:00
Create React AppでReactの開発環境を構築
はじめに 自身のメモとして、本記事では、Create React Appを利用したReact+TypeScriptのビルド環境を構築手順を記載します. Create React Appとは Create React AppはFacebookにより提供されているReactの公式ツールで、コマンド一発でReactの開発環境を構築することが出来るというもので、現在では、Reactアプリ開発におけるデファクトスタンダードとなっています. Create React App 公式サイト Create React App GitHub Create React Appを利用した開発環境構築 事前確認 各種コマンドのバージョンを確認します. nodeのバージョン $ node -v v16.0.0 yarnのバージョン $ yarn -v 1.22.10 npxのバージョン $ npx -v 7.10.0 create-react-appのバージョン確認 $ npx create-react-app --version 4.0.3 新規プロジェクト作成 下記のCreate React Appのコマンドを実行し、TypeScriptを利用したReactのビルド環境を構築します. $ npx create-react-app sample --template typescript Creating a new React app in sample. Installing packages. This might take a couple of minutes. Installing react, react-dom, and react-scripts with cra-template-typescript... yarn add v1.22.10 [1/4] ? Resolving packages... [2/4] ? Fetching packages... [3/4] ? Linking dependencies... warning "react-scripts > @typescript-eslint/eslint-plugin > tsutils@3.20.0" has unmet peer dependency "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta". [4/4] ? Building fresh packages... success Saved lockfile. success Saved 7 new dependencies. info Direct dependencies ├─ cra-template-typescript@1.1.2 ├─ react-dom@17.0.2 ├─ react-scripts@4.0.3 └─ react@17.0.2 info All dependencies ├─ cra-template-typescript@1.1.2 ├─ immer@8.0.1 ├─ react-dev-utils@11.0.4 ├─ react-dom@17.0.2 ├─ react-scripts@4.0.3 ├─ react@17.0.2 └─ scheduler@0.20.2 ✨ Done in 21.77s. Initialized a git repository. Installing template dependencies using yarnpkg... yarn add v1.22.10 [1/4] ? Resolving packages... [2/4] ? Fetching packages... [3/4] ? Linking dependencies... warning " > @testing-library/user-event@12.8.3" has unmet peer dependency "@testing-library/dom@>=7.21.4". [4/4] ? Building fresh packages... success Saved lockfile. success Saved 23 new dependencies. info Direct dependencies ├─ @testing-library/jest-dom@5.14.1 ├─ @testing-library/react@11.2.7 ├─ @testing-library/user-event@12.8.3 ├─ @types/jest@26.0.23 ├─ @types/node@12.20.15 ├─ @types/react-dom@17.0.7 ├─ @types/react@17.0.11 ├─ react-dom@17.0.2 ├─ react@17.0.2 ├─ typescript@4.3.2 └─ web-vitals@1.1.2 info All dependencies ├─ @testing-library/dom@7.31.2 ├─ @testing-library/jest-dom@5.14.1 ├─ @testing-library/react@11.2.7 ├─ @testing-library/user-event@12.8.3 ├─ @types/aria-query@4.2.1 ├─ @types/jest@26.0.23 ├─ @types/node@12.20.15 ├─ @types/prop-types@15.7.3 ├─ @types/react-dom@17.0.7 ├─ @types/react@17.0.11 ├─ @types/scheduler@0.16.1 ├─ @types/testing-library__jest-dom@5.14.0 ├─ css.escape@1.5.1 ├─ css@3.0.0 ├─ csstype@3.0.8 ├─ lz-string@1.4.4 ├─ min-indent@1.0.1 ├─ react-dom@17.0.2 ├─ react@17.0.2 ├─ redent@3.0.0 ├─ strip-indent@3.0.0 ├─ typescript@4.3.2 └─ web-vitals@1.1.2 ✨ Done in 12.29s. We detected TypeScript in your project (src/App.test.tsx) and created a tsconfig.json file for you. Your tsconfig.json has been populated with default values. Removing template package using yarnpkg... yarn remove v1.22.10 [1/2] ? Removing module cra-template-typescript... [2/2] ? Regenerating lockfile and installing missing dependencies... warning " > @testing-library/user-event@12.8.3" has unmet peer dependency "@testing-library/dom@>=7.21.4". success Uninstalled packages. ✨ Done in 9.58s. Created git commit. Success! Created sample at sample Inside that directory, you can run several commands: yarn start Starts the development server. yarn build Bundles the app into static files for production. yarn test Starts the test runner. yarn eject Removes this tool and copies build dependencies, configuration files and scripts into the app directory. If you do this, you can’t go back! We suggest that you begin by typing: cd sample yarn start Happy hacking! 依存関係の解消 あらためてエラー/警告が出てないか確認します.参考までにyarn install —check-filesは「node_modules に既にインストールされたファイルが削除されていないことを確認」するコマンドです(参考). $ yarn install --check-files yarn install v1.22.10 [1/4] ? Resolving packages... [2/4] ? Fetching packages... [3/4] ? Linking dependencies... warning " > @testing-library/user-event@12.8.3" has unmet peer dependency "@testing-library/dom@>=7.21.4". [4/4] ? Building fresh packages... ✨ Done in 8.38s. unmet peer dependencyの警告が出ている.警告の内容としては@testing-library/user-event@12.8.3が正しく動作するのにバージョン7.21.4以上の@testing-library/domが必要という意味になる. warning " > @testing-library/user-event@12.8.3" has unmet peer dependency "@testing-library/dom@>=7.21.4". install可能なバージョンを確認 $ yarn info @testing-library/dom versions | tail -n 10 '7.31.1', '7.31.2', '8.0.0-alpha.1', '8.0.0-alpha.2', '8.0.0-alpha.3', '8.0.0-alpha.4', '8.0.0-alpha.5', '8.0.0-alpha.6' ] Done in 0.24s. 依存関係を解消するため、パッケージを追加.latestを指定して最新のstable版をインストール. $ yarn add -D @testing-library/dom@latest package.jsonに下記が追加された "devDependencies": { "@testing-library/dom": "^7.31.2" } エラー/警告が出てないか再度確認し、エラー/警告が出なくなっていることを確認 $ yarn install --check-files yarn install v1.22.10 [1/4] ? Resolving packages... [2/4] ? Fetching packages... [3/4] ? Linking dependencies... [4/4] ? Building fresh packages... ✨ Done in 8.90s. プロジェクトの起動 プロジェクトを実行 $ cd sample $ yarn start 正しく動作していることをブラウザで確認 参考 https://create-react-app.dev/docs/adding-typescript/ https://ja.reactjs.org/docs/create-a-new-react-app.html https://github.com/facebook/create-react-app https://create-react-app.dev/ https://chore-update--yarnpkg.netlify.app/ja/docs/cli/install https://classic.yarnpkg.com/en/docs/cli/install/#toc-yarn-install-check-files