flowtypeに入門してみる

Flowとは

flowは、JavaScriptのコードに対して静的な型チェックを行います。
プログラムのバグを早い段階で発見できます。

インストール

プロジェクトのフォルダーに移動して「npm init」を実行します。

npm init

「package.json」ファイルが作成されました。

flow本体とコマンドラインインターフェースをインストールします。

npm install --save flowtype flow-bin

package.jsonの「scripts」に「”flow”: “flow”」を追加します。

{
    "name": "flowtest",
    "version": "1.0.0",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "flow": "flow"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
        "flow-bin": "^0.32.0",
        "flowtype": "^1.0.0"
    },
    "description": ""
}

「flow init」を実行します。

rpm run-script flow init

「.flowconfig」ファイルが作成されました。

型をチェックする

「flow check」で型をチェックします。

rpm run-script flow check

実行結果は次のようになりました。

Found 0 errors

チェックするソースコードがないので、エラーはありませんでした。

「index.js」ファイルを作成する

「index.js」ファイルを作成します。 

// @flow
function add(num1, num2) {
    return num1 + num2;
}
var x = add(3, '0');
console.log(x);

型チェックします。

rpm run-script flow check

実行結果は次のようになりました。

Found 0 errors

エラーにはなりませんでした。

関数に型を指定する

関数の引数と戻り値の型を指定します。

// @flow
// 引数と戻り値の型を指定する
function add(num1: number, num2: number): number {
    return num1 + num2;
}
var x = add(3, '0');
console.log(x);

型チェックします。

rpm run-script flow check

実行結果は次のようになりました。

index.js:6
  6: var x = add(3, '0');
             ^^^^^^^^^^^ function call
  6: var x = add(3, '0');
                    ^^^ string. This type is incompatible with
  3: function add(num1: number, num2: number): number {
                                      ^^^^^^ number


Found 1 error

エラーを検出しました。
number型の引数にstringを渡していることがエラーになっています。

エラーになっている箇所を修正します。
stringではなく、numberを渡します。

// @flow

function add(num1: number, num2: number): number {
    return num1 + num2;
}
var x = add(3, 2); // 引数を修正する
console.log(x);

型チェックします。

rpm run-script flow check

実行結果は次のようになりました。

Found 0 errors

ソースコードから型を削除する

ソースコードから型を削除するには、「flow-remove-types」を使用します。

「flow-remove-types」をインストールします。

npm install --save flow-remove-types

index.jsから型を削除したoutput.jsを作成します。

node node_modules\flow-remove-types\flow-remove-types index.js > output.js

output.jsが作成されました。

//      

function add(num1        , num2        )         {
    return num1 + num2;
}
var x = add(3, 2);
console.log(x);

Flow Comments形式

ソースコードに型を記述する形式に、Flow Comments形式があります。

型をJavaScriptのコメントとして記述するため、ソースコードはそのままJavaScriptとして実行できます。

// @flow

function add(num1/*: number */, num2/*: number */)/*: number */ {
    return num1 + num2;
}

最後に

既存のソースコードを壊すことなく、型チェックの仕組みを追加できるのはいいですね。

webpackでエラー「SyntaxError: missing ) after argument list」

webpackでエラー「SyntaxError: missing ) after argument list」

webpackを実行するとエラーになる。

>node node_modules\.bin\webpack app.js bundle.js
c:\Users\~\node_modules\.bin\webpack:2
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
          ^^^^^^^

SyntaxError: missing ) after argument list
    at exports.runInThisContext (vm.js:53:16)
    at Module._compile (module.js:373:25)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Function.Module.runMain (module.js:441:10)
    at startup (node.js:139:18)
    at node.js:974:3

webpackのパスを「node_modules.bin\webpack」から「node_modules\webpack\bin\webpack」に変更すると、正しく実行できるようになった。

>node node_modules\webpack\bin\webpack app.js bundle.js
Hash: b5d1e561b44e32934914
Version: webpack 1.13.2
Time: 59ms
    Asset     Size  Chunks             Chunk Names
bundle.js  1.49 kB       0  [emitted]  main
   [0] ./app.js 98 bytes {0} [built]

Google Feed APIの代替手段と設定方法

Google Feed APIがもうすぐ終了します。

このサイトでは、Google Feed APIを使用してRSSから更新情報を取得し、表示しているページがありました。 Google Feed APIの代わりとなるサービスを調べ、乗り換えました。

乗り換え先として選んだサービスと、その設定方法を紹介します。

乗り換え先の条件

乗り換え先は次の条件を満たすものを探しました。

  • 無料で使用できること。
  • ブラウザだけで動作すること。サーバーサイドのプログラムは不要であること。
  • 一昔前のブラウザでも動作すること。最新技術に依存していないこと。

Yahoo!Japanの爆速YQL

選んだのが「Yahoo!Japanの爆速YQL」というサービスです。 上記の条件をすべて満たしており、使い方も簡単でした。

爆速YQLについては、次のページをご覧ください。

爆速YQLの使い方

基本となるひな形は次のようになります。

<html>
<body>

<script
  src="http://i.yimg.jp/images/yjdn/js/bakusoku-yql-v1-min.js"
  data-url="[RSSのURL]"
  data-p-[パラメータ名]="[値]">
  [HTMLのテンプレート]
</script>

</body>
</html>

scriptタグは、更新情報を表示したい場所に置きます。

URLを設定する

[RSSのURL]の部分には、実際のRSSのURLを設定します。

RSSのURLにパラメータが含まれている場合は、scriptタグの属性に「data-p-[パラメータ名]=”[値]”」を追加します。
パラメータがない場合は「data-p-[パラメータ名]」は不要です。

たとえば「山本隆の開発日誌」のRSSは、URLが「http://www.gesource.jp/weblog/?feed=rss2」です。
この場合、パラメータ名が「feed」、値が「rss2」となり、scriptタグは次のようになります。

<script
  src="http://i.yimg.jp/images/yjdn/js/bakusoku-yql-v1-min.js"
  data-url="http://www.gesource.jp/weblog/"
  data-p-feed="rss2">
  [HTMLのテンプレート]
</script>

デバッグモードで受信したデータを表示する

URLを正しく設定できたか確認します。

HTMLのテンプレートが空の時は、デバッグモードで動作し、取得されたデータが表示されます。

<script
  src="http://i.yimg.jp/images/yjdn/js/bakusoku-yql-v1-min.js"
  data-url="http://www.gesource.jp/weblog/"
  data-p-feed="rss2"></script>

scriptタグの位置に取得したデータが表示されると成功です。

{
"query": {
    "count": 1,
    …
  }
}

HTMLのテンプレートを設定する

次に、HTMLのテンプレートを設定します。

HTMLのテンプレートには「mustache」というライブラリが使用されているようです。
ですが、mustacheライブラリの詳細を知る必要はありません。
テンプレートで使用する機能は簡単なものです。

デバッグモードで確認したデータで表示したいデータを確認します。
「query.results.rss.channel.item」が記事の情報になっていました。
この部分を繰り返し表示します。

<script
  src="http://i.yimg.jp/images/yjdn/js/bakusoku-yql-v1-min.js"
  data-url="http://www.gesource.jp/weblog/"
  data-p-feed="rss2">
  <ul>
  {{#query.results.rss.channel.item}}
    <li>
      {{pubDate}}<br>
      <a href="{{link}}">{{title}}</a>
    </li>
  {{/query.results.rss.channel.item}}
  </ul>
</script>

テンプレートの中のHTMLタグはそのまま表示されます。
「{{」と「}}で囲んだ部分がjavaScriptで処理されるコードになります。

  {{#query.results.rss.channel.item}}
  …
  {{/query.results.rss.channel.item}}

と書くと、「query.results.rss.channel.item」のデータが繰り返し表示されます。

繰り返しの部分の中で「{{title}}」と書くと、「query.results.rss.channel.item」の「title」の値が表示されます。

「{{#~}}」「{{/~}}」で繰り返し、「{{~}}」で値の表示、これだけわかっていれば使えます。

テンプレートについて詳しく知りたい方は、次のページをご覧ください。

日付の書式を変更する

日付(pubDate)の値が「Thu, 15 Sep 2016 23:47:18 +0000」という形式になっています。
この形式を「2016年9月16日」と表示するように修正します。

scriptタグの属性に「data-filter=”filterRSS”」を追加します。
「data-filter=”関数名”」属性を追加すると、JavaScriptの関数が呼ばれようになります。
関数で受け取ったデータを修正できます。

<script
  src="http://i.yimg.jp/images/yjdn/js/bakusoku-yql-v1-min.js"
  data-url="http://www.gesource.jp/weblog/"
  data-p-feed="rss2"
  data-filter="filterRSS">
  <ul>
  {{#query.results.rss.channel.item}}
    <li>
      {{pubDate}}<br>
      <a href="{{link}}">{{title}}</a>
    </li>
  {{/query.results.rss.channel.item}}
  </ul>
</script>

「filterRSS」関数を作成します。

<script>
function filterRSS(data) {
    for (var i = 0; i < data.query.results.rss.channel.item.length; ++i) {
        var date = new Date(data.query.results.rss.channel.item[i].pubDate);
        data.query.results.rss.channel.item[i].pubDate = date.getFullYear() + '年' + (date.getMonth() + 1) + '月' + date.getDate() + '日';
    }
    return data;
}
</script>

JavaScriptの「var date = new Date(文字列)」で日付の文字列からオブジェクトを作成し、puDateの値を上書きしています。

テンプレートの機能を活用すれば、もっと簡単にできるかもしれませんが、今回はこのような方法で実現しました。

最後に

サイトのトップページでは、この方法でブログの更新情報を表示しています。

参考になれば幸いです。

Delphi 10.1 BerlinのFireMonkeyアプリケーションでスタイルをリソースから読み込んで適用する

Delphi 10.1 BerlinのFireMonkeyアプリケーションでスタイルをリソースから読み込んで適用するサンプルアプリケーションです。

style001 style002 style003

FireMonkey Premium Styles Pack for RAD Studio 10.1 BerlinEmbarcaderoの登録ユーザーのページからダウンロードして、プロジェクトの下の「style」に配置します。

StyleSample
└style
  ├Android
  ├iOS
  ├MacOS
  │├Copper.style
  │├…
  │└Vapor.style
  └Win
    ├Copper.style
    ├CopperDark.style
    ├CoralCrystal.style
    ├CoralDark.style
    ├Diamond.style
    ├EmeraldCrystal.style
    ├EmeraldDark.style
    ├Jet.style
    ├Radiant.style
    ├Sterling.style
    └Vapor.style

プラットフォームごとにリソースファイルを作成します。
リソース識別子はスタイル名、リソースタイプはRCDATAにします。

Resource.win.rc

CopperStyle RCDATA "style\\Win\\Copper.style"
CopperDarkStyle RCDATA "style\\Win\\CopperDark.style"
CoralCrystalStyle RCDATA "style\\Win\\CoralCrystal.style"
…

Resource.mac.rc

CopperStyle RCDATA "style\\MacOS\\Copper.style"
CopperDarkStyle RCDATA "style\\MacOS\\CopperDark.style"
CoralCrystalStyle RCDATA "style\\MacOS\\CoralCrystal.style"
…

brcc32.exeを使って、rcファイルからresファイルを作成します。

"c:\program files (x86)\embarcadero\studio\18.0\bin\brcc32.exe" Resource.win.rc -foResource.win.res
"c:\program files (x86)\embarcadero\studio\18.0\bin\brcc32.exe" Resource.mac.rc -foResource.mac.res

条件付きコンパイルで、プラットフォーム用のリソースファイルを指定します。

{$IFDEF MACOS}
{$R Resource.mac.res}
{$ENDIF}
{$IFDEF MSWINDOWS}
{$R Resource.win.res}
{$ENDIF}

プラットフォームごとに分けない場合は、rcファイルをプロジェクトに追加します。
プロジェクトに追加したrcファイルは、プロジェクトをビルドしたときにresファイルが作成されます。
また「{$R ~}」を追加する必要もありません。

スタイルのリソース名を定数で定義します。

private const
  StyleNames: array [0 .. 10] of string = ('CopperStyle', 'CopperDarkStyle',
    'CoralCrystalStyle', 'CoralDarkStyle', 'DiamondStyle',
    'EmeraldCrystalStyle', 'EmeraldDarkStyle', 'JetStyle', 'RadiantStyle',
    'SterlingStyle', 'VaporStyle');

リソースからスタイルの名前を取得します。

function TForm1.LoadStyleNames: TArray<string>;
var
  StyleName: string;
  Style: TFmxObject;
  StyleDescription: TStyleDescription;
  List: TList<string>;
begin
  List := TList<string>.Create;

  for StyleName in StyleNames do
  begin
    Style := TStyleStreaming.LoadFromResource(
      HInstance,
      StyleName,
      RT_RCDATA);
    StyleDescription := TStyleManager.FindStyleDescriptor(Style);
    List.Add(StyleDescription.Title);

    StyleDescription.Free;
    Style.Free;
  end;

  Result := List.ToArray;
end;

スタイルによっては、StyleDescriptionが設定されていないものがあります。
その場合は「TStyleManager.FindStyleDescriptor」はnilを返します。

取得したスタイル名をリストボックスに登録します。

procedure TForm1.LoadListBoxItem;
begin
  ListBoxStyleNames.Items.AddStrings(LoadStyleNames);
end;

FormのOnCreateイベントで上の処理を呼び出します。

procedure TForm1.FormCreate(Sender: TObject);
begin
  LoadListBoxItem;
end;

リストボックスの項目が選択されたら、選択されたスタイルをフォームに適用します。

procedure TForm1.ListBoxStyleNamesChange(Sender: TObject);
begin
  if ListBoxStyleNames.Selected <> nil then
    SetStyle(ListBoxStyleNames.Selected.Index);
end;

procedure TForm1.SetStyle(const AStyleIndex: Integer);
var
  Control: TFmxObject;
begin
  Control := TStyleStreaming.LoadFromResource(
    HInstance,
    StyleNames[AStyleIndex],
    RT_RCDATA);
  TStyleManager.SetStyle(Control);
end;

ボタンを押したら、デフォルトのスタイルを適用します。

procedure TForm1.ButtonDefaultClick(Sender: TObject);
begin
  TStyleManager.SetStyle(nil);
end;

サンプルプロジェクトはこちらからダウンロードできます。

スタイルのファイルはサイズが大きいので入れていません。
Embarcadero登録ユーザーのページからダウンロードしてください。

Delphi 10.1 BerlinでTGridを継承したクラスを作成するには

Delphi 10.1 BerlinでTGridを継承したクラスを作成するには

Delphi 10.1 BerlinでTGridを継承した次のようなクラスを作成しました。

type
  TMyGrid = class(TGrid)
  end;

ところが実行すると、次のようなメッセージが表示されます。

grid

クラス TMyGrid に対して TStyledPresentationProxy の下位クラスが登録されていません。 おそらく uses セクションに FMX.Grid.Style モジュールを追加する必要があります。

TMyGridと同じユニットに次のコードを追加することで解決しました。

uses
  FMX.Presentation.Factory,
  FMX.Presentation.Style;

initialization

TPresentationProxyFactory.Current.Register(
  TMyGrid,
  TControlType.Styled,
  TStyledPresentationProxy<TStyledGrid>);

finalization

TPresentationProxyFactory.Current.Unregister(
  TMyGrid,
  TControlType.Styled,
  TStyledPresentationProxy<TStyledGrid>);

Visual Studio CodeでHTMLを整形するには

Visual Studio CodeでHTMLを整形するには「Shift+Alt+F」キーを押します。

範囲を選択している場合は選択範囲を、選択していない場合はコード全体を整形します。

次のようなコードに対して、

FormatHTML01

「Shift+Alt+F」キーを押すと次のようになります。

FormatHTML02

Delphi 10.1 BerlinのFireMonekyアプリケーションで実行中のOSを(Windows/OSX/Android/iOS)を取得する

Delphi 10.1 BerlinのFireMonekyアプリケーションで実行中のOSを(Windows/OSX/Android/iOS)を取得する

実行中のプラットフォームはTOSVersionレコードのPlatformフィールドで取得できます。

戻り値は次のような定数になっています。

TPlatform = (pfWindows, pfMacOS, pfiOS, pfAndroid, pfWinRT, pfLinux);

次のコードは、アプリケーションを実行しているOSを表示します。

uses
  FMX.DialogService;

procedure TForm1.Button1Click(Sender: TObject);
const
  OSNames: array [0 .. 3] of String = ('Windows', 'OSX', 'iOS', 'Android');
var
  OSName: string;
begin
  OSName := OSNames[Integer(TOSVersion.Platform)];
  FMX.DialogService.TDialogService.ShowMessage(OSName);
end;

android

ios

osx

windows

PNGファイルをWindows/OS X/Android/iPhone/iPad用アイコンのサイズに変更するWebアプリ

PNGファイルをWindows/OS X/Android/iPhone/iPad用アイコンのサイズに変更するWebアプリを作りました。
ひとまず動作するようになったので公開します。

アイコンのサイズはDelphi/C++Builder用になっていますが、他の開発環境でも使えると思います。

すべての処理をJavaScriptで行っています。
サーバーサイドでは処理は一切行っていません。

最新版のChromeとFirefoxで動作します。
IEは動作対象外です。

使い方は画面を見るとわかると思いますが、簡単に説明します。

  1. PNG画像を選択、またはドロップしてアップロードします。
  2. プラットフォームを選択します。
  3. 「Create image files」ボタンを押します。
  4. 「Download image files」ボタンを押します。
  5. ダウンロードしたZIPファイルを展開します。

アプリをインストールしなくても手軽に使えて便利だと思います。

Delphi 10.1 Berlinのスタイルを使い方の備忘録

Delphi 10.1 Berlinのスタイルを使い方の備忘録

登録されているスタイルを取得する

uses
  FMX.Styles;

  // 登録されているスタイルを列挙します。
  TStyleManager.EnumStyleResources(
    procedure(const AResourceName: string; const APlatform: TOSPlatform)
    begin
      ListBoxStyleNames.Items.Add(AResourceName)
    end);

設定されているスタイルオブジェクトを取得する

var
  StyleResource: TFmxObject;

StyleResource := TStyleManager.ActiveStyle(Self);

スタイル名からスタイルオブジェクトを取得する

var
  StyleResource: TFmxObject;

StyleResource := TStyleManager.GetStyleResource(ResourceName);

スタイルオブジェクトからスタイルの説明を取得する

  // スタイル説明を取得します
  StyleDescription := TStyleManager.FindStyleDescriptor(StyleResource);
  Memo1.Lines.Add('スタイルの作者=' + StyleDescription.Author);
  Memo1.Lines.Add('スタイルの作成者のeメールアドレス=' + StyleDescription.AuthorEMail);
  Memo1.Lines.Add('スタイルの作成者のWebサイトのURL =' + StyleDescription.AuthorURL);
  Memo1.Lines.Add('スタイルがモバイルプラットフォーム上で使用できるか=' +
    BoolToStr(StyleDescription.MobilePlatform, True));
  Memo1.Lines.Add('スタイルのタイトル=' + StyleDescription.Title);
  Memo1.Lines.Add('スタイルのバージョン=' + StyleDescription.Version);

デフォルトのスタイルを設定する

TStyleManager.SetStyle(nil);

スタイルをファイルから読み込んで適用する

AFileName := 'C:\~\Air.Style'
TStyleManager.SetStyleFromFile(AFileName);

サンプルアプリケーション

登録されているスタイルをリストボックスに一覧表示します。
リストボックスのスタイルを選択すると、スタイルの情報をメモに表示します。

ソースコードはこちら→StyleSample

ES2015 (ES6)の関数の引数にデフォルト値を指定する

ES2015 (ES6)では、関数の引数にデフォルト値を指定できます。

function func(a=100, b=200) {
    console.log(a);
    console.log(b);
}

func(30);

実行結果は次のようになります。

30
200

デフォルト値には、関数の返値も使用できます。

function func(a=new Date()) {
    console.log(a);
}