ページ

2015年9月29日火曜日

GASまとめ4 - PropertiesService -


今回はデータをkey-value形式で管理してくれるPropertiesServiceについて。
スクリプト内の設定情報などを管理してくれる便利なクラスです。

データの共有範囲として、
  • 全てのユーザーで現在のドキュメント間(add-onで公開しても共有)
  • 全てのユーザーで現在のスクリプト間
  • 現在のユーザーで現在のスクリプト間
の3つある。それぞれ専用のメソッドが用意されており
  • getDocumentProperties()
  • getScriptProperties()
  • getUserProperties()
の3つある。それぞれPropertiesクラスが返ってくる。
また、別々のスクリプト間で共有はできない。

まあ、複数のドキュメント間では共有できないという事ですね

サンプル

早速サンプルを試してみたいと思います。
使用するのはgetDocumentProperties()
function setup() {
  var dp = PropertiesService.getDocumentProperties();
  dp.setProperty('AAA', 'HOGE');
  dp.setProperty('BBB', 'FUGA');
}

function dump() {
  var dp = PropertiesService.getDocumentProperties();
  var keyA = dp.getProperty('AAA');
  var keyB = dp.getProperty('BBB');
  var keyC = dp.getProperty('CCC');

  Logger.log('AAA : ' + keyA);
  Logger.log('BBB : ' + keyB);
  Logger.log('CCC : ' + keyC);
}
単純ですが、setup()でデータをそれぞれのキーで保存し、
dump()でデータを取り出しログ出力しています。
↓※実行する時には認証を求められます。


実行結果としては、
AAA : HOGE
BBB : FUGA
CCC : null
になります。
ちなみに存在しないキーを設定すると'null'が返ってきます。
Propertiesクラス

Propertiesクラスで用意されているメソッドです。
  • deleteAllProperties()
    現在のProperties設定値を全削除
  • deleteProperty(key)
    現在のProperties設定値のkeyで設定されている内容を削除
  • getKeys()
    現在のProperties設定値のkey配列String[]を取得
  • getProperties()
    現在のPropertiesのコピーを取得
  • getProperty(key)
    現在のPropertiesから引数で指定されたkeyの設定値を取得
  • setProperties(properties)
    現在のPropertiesに引数のPropertiesを設定
  • setProperties(properties, deleteAllOthers)
    引数のPropertiesを現在のPropertiesに設定し、
    引数のdeleteAllOthersがtrueの場合、設定された内容以外の設定値を削除
  • setProperty(key, value)
    現在のPropertiesに引数のkeyをキーにvalueを設定する

2015年9月26日土曜日

iOSのUIWebView + AngularJSで画面遷移


以前にUIWebView+AngularJSで軽く動作させてみましたが、
今回は画面遷移をやってみようと思います。

構成としては
  • トップメニュー画面
  • サブ画面
  • メイン画面
で、トップメニュー画面とサブ画面がUIWebView上のHTMLで表示、
メイン画面を通常のViewで実装してみたいと思います。
・イメージ図

独自URLスキームの作成


実装するアプリ専用のURLスキームを作成し、そのURLスキームで
リクエストが来た場合のみ処理を行うようにしたいと思います。
URLスキームとは
URLスキームはhttp://hogehogeのhttpを差します


前回WebViewが新たなフレームをロードする時にハンドリングする
メソッドをオーバーライドしました
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
      return true
    }
今回はここでNSURLRequestからURLを取得し、作成する独自スキームであれば 画面遷移を行うようにしたいと思います。

作成するURLスキームはsampleとして進めていきます
sampleをURLスキームとした例は以下のようになります
  sample://hogehoge/fuga/index.html

独自URLスキームのハンドリング


func webViewを独自URLスキームを判定する為に以下のような内容で編集します
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {

    let url = request.URL!.absoluteString
    if url.hasPrefix("sample://") {
        // 何かしらの処理
        return false
    }
    return true
}
request.URL!.absoluteStringでリクエストURLのフルパスを取得しています
次の行のif url.hasPrefix("sample://")で独自URLスキームかどうかの判定をしています

画面遷移(トップメニュー画面→サブ画面)


トップメニュー画面からサブ画面の画面遷移を実装してみたいと思います。
別のHTMLをロードしてほしいリクエストURLをsample://html/XXXXとします
XXXにはロードしたいhtml名を設定します。
例) menu.htmlからsub.htmlを表示させたい時
sample://html/sub
URLはサンプル用です
html名を与えたらhtmlをロードしてくれるメソッドを作成します
private func locationChanged(res :String) {
    let url = NSBundle.mainBundle().pathForResource(res, ofType: "html");
    let reqURL = NSURL(string: url!)
    let req = NSURLRequest(URL: reqURL!)
    webView.loadRequest(req)
}
↑では引数のresの名前+拡張子がhtmlのリソースパスを取得し、
リソースパスをリクエストURLに変換しUIWebViewにロードさせています
作成したメソッドを使ってfunc webViewを以下のように修正します
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {

    let url = request.URL!.absoluteString
    if url.hasPrefix("sample://") {
        if url == "sample://html/sub { ・・
          locationChanged("sub")
        }
        return false
    }
    return true
}
リクエストURLがsample://html/subの場合locationChanged
引数"sub"を渡して呼び出しています
  • menu.html
<!doctype html>
<html ng-app="myApp">
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
    </head>
    <body>
        <div>
            <label>Name:</label>
            <input type="text" ng-model="yourName" placeholder="Enter a name here">
            <hr>
            <h1>Hello {{yourName}}!</h1>
        </div>
        <div>
            <form ng-controller="myController">
                <lable>Link:</label>
                <button ng-click="onClick()">send</button>
            </form>
        </div>
    </body>
    <script>
        angular.module('myApp', [])
            .controller('myController', ['$scope', function($scope) {
                // setting click listener
                $scope.onClick = function() {
                    location.href = "sample://html/sub";・・
                };
            }]);
    </script>
</html>
前回作成したindex.htmlでのボタン押したリンク先をサブ画面に変更しています
  • sub.html
<!doctype html>
<html>
    <head>
        <title>Sub Screen</title>
    </head>
    <body>
        <h2>Sub Screen</h2>
    </body>
</html>
サブ画面はいたって単純でSub Screenと表示するだけです
↓画面遷移の様子
optimaize

画面遷移(トップメニュー画面→メイン画面)


トップメニュー画面からメイン画面への遷移ですが、
こちらも特定のリクエストURLの場合にハンドリングするのは変わらず、
ハンドリングしたら別UIViewControllerへ遷移させます
別UIViewControllerへ遷移する為のメソッドを作成します
// Screen transition MainViewController.
private func startMainView() {
    let mainViewController = self.storyboard!
        .instantiateViewControllerWithIdentifier("mainview")
    self.presentViewController(mainViewController, animated: false, completion: nil)
}
↑ではストーリーボード上の遷移先のStoryboard IDmainviewを取得し
presentViewControllerで画面遷移させています
Storyboard IDのつけ方
storyboardid

今度はsample://html/subのリクエストURLが来たら
startMainViewを呼ぶようにfunc webViewを変更します
// handling event
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {

    let url = request.URL!.absoluteString
    if url.hasPrefix("sample://") {
        if url == "sample://html/sub" {
            locationChanged("sub")
        } else if url == "sample://html/main" {・・
            startMainView()
        }
        return false
    }
    return true
}
また、menu.htmlもメイン画面へのリンクボタンを追加します
<!doctype html>
<html ng-app="myApp">
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
    </head>
    <body>
        <div>
            <label>Name:</label>
            <input type="text" ng-model="yourName" placeholder="Enter a name here">
            <hr>
            <h1>Hello {{yourName}}!</h1>
        </div>
        <div>
            <form ng-controller="myController">
                <lable>Link: Sub</label>
                <button ng-click="onClickSub()">send</button>
                <lable>Link: MainView</label>
                <button ng-click="onClickMain()">go</button>
            </form>
        </div>
    </body>
    <script>
        angular.module('myApp', [])
            .controller('myController', ['$scope', function($scope) {
                // setting click listener
                $scope.onClickSub = function() {
                    location.href = "sample://html/sub";
                };

                // setting click listener
                $scope.onClickMain = function() {・・
                    location.href = "sample://html/main";
                };
            }]);
    </script>
</html>
↓実行結果
optimaize

2015年9月25日金曜日

道の駅を巡ってみる 4 - 下関方面 -


前回道の駅スタンプラリーの九州版に挑戦していると書いたんですが、
今回は九州・・ではなく山口県下関市の「道の駅きくがわ」に行ってきました。
スタンプラリーは今回おやすみ。



下関市の菊川町にあって、九州方面からだと長門市や萩市方面に行く途中で寄れます。




イメージキャラクターの「カモンちゃん」
ご利益があるのか、頭にお金が載ってました(笑)




名物の菊川そうめんをつかった「焼きそうめん」をいただきました。
麺がしっかりしてて普通のそうめんと比べたらちょっと太めです。
とてもおいしかったです^^。



・リンク
http://michinoekikikugawa.co.jp

2015年9月22日火曜日

VMware Fusion 8.0購入!! Windows10動作レビュー



iMacを購入して、テンションが上がっていたのか、
「VMware Fusion 8.0 Windows10とOS X El Capitanに対応!!」
というの謳い文句に負けて購入してしまいました。。



※ロゴのアイコンがYosemiteっぽくなってる!!。

仮想マシンとしてインストールしているWindows10は以前MacBookAirの
VMware Fusion6.0にインストールしていたWindows10 Insider Preview版です。


仮想マシンの構成


↓HDDの構成はこんな感じ





HDDのサイズが約70GBとSSD256GBのiMacにはちょっと圧迫されてる感があります(泣)
メモリは4Gに設定してます。



使ってみて


実際Windows10を起動して使ってみた感想ですが、、





今の所さくさく動いてます。
たまに通知が右下に表示される時に音が鳴って、音量でかいとびっくりしますが(笑)

Visual Studioでの作業も問題なく作業できてます。




Microsoft Edgeや他ブラウザ 上でApple Magic Mouseの上下スクロールはスワイプ操作で
いけましたが、左右のスワイプで戻る、進むを操作できるようにしていても
仮想マシンのWindows10上のブラウザでは操作できませんでした。

またWindows8の時もそうでしたが、Commans + Tabのショートカットキーは
OS Xのタスク切り替えが反応します。Windows10のタスクビュー切り替えはできませんでした。



その他


あと、VMware Fusion関係ないんですが、Windows10で地味に便利になったのが、

コマンドプロンプトでCtrl + C / Ctrl + Vのコピー&ペーストが使えるようになりました♪
範囲選択は今まで通りですが、いちいち「編集」> 「コピー」とかしなくてよくなります。

ただデフォルトでは使えないので少し設定が必要です。

1. コマンドプロンプトを起動し、プロパティを選択



2.  「従来のコンソールを使う」のチェックボックスを外し、「Ctrlキーショートカットを有効にする」にチェックを入れる。


3. コマンドプロンプトを再起動する事でCtrl + C/Ctrl + Vが使えるようになります。


まとめ

以前MacBookAirでVMware Fusion 6.0にWindows10をインストールして使っていた時は
結構再起動を繰り返していましたが、正式な対応版なのでばっちり安定しています。
引き続き使っていこうと思います

2015年9月19日土曜日

Elixir/Phoenixで遊ぶ 9 - プロジェクト管理ツールMix -


ちょっと今回は基礎編ではなくElixirのプロジェクト管理ツールであるMixについて
Rubyでいう所のgemに相当するものになります

主なコマンド
コマンド詳細
mix new新規プロジェクトの作成
mix run指定されたファイル又はプロジェクト実行
mix testプロジェクトのテスト実行
mix deps.getmix.exsで定義された依存パッケージの取得
iex -S mixiexを起動し、プロジェクトを実行

新規プロジェクト作成
Mixを使ってプロジェクトを作成すると以下のようになります。
$ mix new sample
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/sample.ex
* creating test
* creating test/test_helper.exs
* creating test/sample_test.exs

Your mix project was created successfully.
You can use mix to compile it, test it, and more:

    cd sample
    mix test

Run mix help for more commands.
プロジェクト作成したらlib/sample.exにコードを書いていきます。


mix.exs
プロジェクトの設定ファイル。 ↓プロジェクト作成直後のmix.exs
defmodule Sample.Mixfile do
  use Mix.Project

  def project do
    [app: :sample,
     version: "0.0.1",
     elixir: "~> 1.0",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps]
  end

  # Configuration for the OTP application
  #
  # Type `mix help compile.app` for more information
  def application do
    [applications: [:logger]]
  end

  # Dependencies can be Hex packages:
  #
  #   {:mydep, "~> 0.3.0"}
  #
  # Or git/path repositories:
  #
  #   {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
  #
  # Type `mix help deps` for more examples and options
  defp deps do
    []
  end
end
  • def project do 〜 end
       プロジェクトのバージョン設定やElixirのバージョンなどを設定する。
  • def application do 〜 end
       アプリケーションコンパイル時に必要なモジュールを定義する。
  • defp deps do 〜 end
       プロジェクトが依存するパッケージを定義する。


mixで実行
先ほどサンプルで作ったlib/sample.exを以下に編集
defmodule Sample do

def list([t|h]) do
  IO.puts(t)
  list(h)
end

def list([]), do: nil
end
コンパイル
$ mix compile
iexから実行してみる
$ iex -S mix
rlang/OTP 18 [erts-7.0] [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.0.5) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Sample.list([1, 2, 3, 4, 5])
1
2
3
4
5
nil
iex(2)>
lib/sample.exで定義したlist/1を実行し、リストの内容を表示しています。


テストの実行
作成したプロジェクトにtestディレクトリが作成されており、 中に、sample_test.exstest_helper.exsが作成されています。 テストはsample_test.exsに書きます
sample_test.exsを以下に編集
defmodule SampleTest do
  use ExUnit.Case

  test "list" do
    assert Sample.list([]) == nil
  end
end
空のリストの引数に対してnilを返すかのテスト
テストを実行してみる
mix testでテスト実行、mix test --traceでテストの 詳細を表示してテストを実行します
  • mix test
$ mix test
Compiled lib/sample.ex
Generated sample app
.

Finished in 0.08 seconds (0.08s on load, 0.00s on tests)
1 tests, 0 failures

Randomized with seed 212541
  • mix test --trace
$ mix test --trace
Compiled lib/sample.ex
Generated sample app
.

SampleTest
  * list (0.00ms)


Finished in 0.06 seconds (0.06s on load, 0.00s on tests)
1 tests, 0 failures

Randomized with seed 904189
  • テスト失敗時
assert Sample.list([]) == nilassert Sample.list([]) == 0 に変更
$ mix test --trace

SampleTest
  * list (9.2ms)

  1) test list (SampleTest)
     test/sample_test.exs:4
     Assertion with == failed
     code: Sample.list([]) == 0
     lhs:  nil
     rhs:  0
     stacktrace:
       test/sample_test.exs:5



Finished in 0.08 seconds (0.07s on load, 0.01s on tests)
1 tests, 1 failures

Randomized with seed 363937


mix.exsにライブラリを指定
例として、JSONのライブラリであるposionを使ってみる。

  • mix.exsを以下に編集
defmodule Sample.Mixfile do
  〜 省略 〜
  defp deps do
    [{:poison, "~> 1.5"}]
  end
end
  • プロジェクトの依存関係を更新
$ mix deps.get
  • ライブラリを使う lib/sample.exを以下に編集
defmodule Sample do

  @derive [Poison.Encoder]
  defstruct [:name, :age]

def list([t|h]) do
  IO.puts(t)
  list(h)
end

def list([]), do: nil

end
  • 実行してみる
$ iex -S mix
iex(1)> Poison.encode!(%Sample{name: "Hoge", age: 20})        
"{\"name\":\"Hoge\",\"age\":20}"
iex(2)> Poison.decode!(~s({"name": "Fuga", "age": 30}), as: Sample)        
%Sample{age: 30, name: "Fuga"}
iex(3)>
↑では、エンコードとデコードを行っています


まとめ
ruby同様に依存関係の管理や、テストなど使いやすいと感じました。
個人的には.gitignoreREADME.mdも作ってくれるので便利