月別アーカイブ 9月 2019

takahashi 著者:takahashi

redisを触ってみる Pub/Sub編

前々回からご紹介しているredisですが、KVS型のDBとしての機能のほかに、もう一つredisが備えている機能があります。

メッセージ配信の仕組みである”Pub/Sub”をredisクライアント間でできる機能です。

Pub/Subとは、メッセージを受信したいユーザーが特定のチャネルを購読(Subscribe)し、配信側はそのチャンネルに対してメッセージを一斉配信(Publish)することで購読側はメッセージを受け取るという仕組みです。

Pub/Subをするために、必ずしもredisを使用する必要はありませんが、自分で実装するとそこそこ大変なのでredisのような既存の仕組みで利用できるのはとてもありがたいです。

今回はredisのPub/Sub機能を実際に試してみたいと思います。

まず、redis-cliクライアントを2つ立ち上げておきます。

片側のredis-cliで、下記のようなコマンドを実行します。

subscribe チャネル名

subscribeしたクライアントは、待機状態になります。

なお、subscribeしている間は他のredisコマンドは実行できなくなるので注意してください。

この状態で、今度はもう片側のredis-cliで下記のようにコマンドを実行します。

publish チャネル名 メッセージ

すると、先程subscribeした側のredis-cliにpublishされたメッセージがリアルタイムで受信されます。

前々回にご紹介した、redisをリモートから接続できるようにしておけば、ネットワーク経由でも送ることができます。

複数クライアントもばっちりです。

redis単体でここまでできるので、あとはプログラムとredisをつないでWebsocketあたりを使えば、簡単にリアルタイムチャットなども実装できそうですね。

・前の記事
redisを触ってみる 基本操作編

  • この記事いいね! (0)
takahashi 著者:takahashi

redisを触ってみる 基本操作編

前回は、redisのインストール・起動方法についてご紹介しました。

今回は、redisを実際に操作して、値を保存する方法をご紹介したいと思います。

値の保存・呼び出し

redisはKVS(Key-Value Store)型のDBなので、データ構造としては、あるキーに対してある値が紐づく、といった概念になります。

redisで値の保存(RDBでいうinsertに相当)を行うには、先日説明したredis-cliを起動し、下記のようにコマンドを入力します。

set キー名 値

こうすると、キー名で指定された名前のキーが作成され、値で指定された値がキーと紐づけられ、redisに保存されます。

キーに保存した値を確認(RDBでいうsekectに相当)するときは

get キー名

とするだけで取得できます。

データ構造の概念が簡単だけあって、コマンドも超簡単で扱いやすいですね。

キー一覧を表示する

先程までの手順で、キーを作成して中に値を登録し、キー内に登録した値を表示するところまではできました。

ではキー名を忘れてしまった場合などに、現時点で作成済みのキーの一覧を見たい、という場合もあるかと思います。

そういう場合は下記のコマンドを実行すると作成したキーの一覧が表示されます。

keys *

ほかにも、変数の型を変更することで一つのkeyに複数の値を挿入したり、値の保存期間を指定したり、といった機能があるそうです。

SQLと比較すると、やはり簡素すぎて代替えにはならないなーという印象ですが、その分非常に扱いやすいうえ動作も軽く、速いので、一時的に値をストックしておく使い方であれば十二分に便利に使えそうです。

導入も簡単ですし、手軽に触れますので、まだredisを触ってないけど興味が出てきたかも…という方はぜひ触ってみてください。

・次の記事
redisを触ってみる Pub/Sub編

・前の記事
redisを触ってみる 初期設定編

  • この記事いいね! (0)
著者:杉浦

【PHP】全く異なる複数クラスを疑似継承する黒魔術

 PHPの継承は常に単一です。これによって知るべき親クラスの情報を単純化してコードの複雑度を上げにくくしてあります。多段継承こそあれ複数経路は作れません。

class Hoge extends Foo // Foo一つを継承することはできる
class Fuga extends Foo,Bar // FooとBarを同時に継承するのはだめ

 やりかたはDI(Dependeny Injection)とマジックメソッドの応用です。

class Foo {
    public function echoFoo() {
        echo 'foo';
    }
    public function selfIntroduction() {
        echo "I'm foo.";
    }
}

class Bar {
    public function echoBar() {
        echo 'bar';
    }
    public function selfIntroduction() {
        echo "I'm bar.";
    }
}

class FooBar {
    public $foo;
    public $bar;
    
    public function __construct($foo, $bar)
    {
        // if コンストラクタでこけるのをいとわないか引数を気にしないなら:
        //     DIせず、ここで$this->foo = new Foo()の様に直接インスタンス化することもできる
        $this->foo = $foo;
        $this->bar = $bar;
    }

   public function __call($name, $arguments)
    {
        method_exists($this->foo, $name)
         ? $this->foo->$name($arguments)
         : $this->bar->$name($arguments);
    }
}

$f = new Foo();
$b = new Bar();
$fb = new FooBar($f, $b);
$fb->echoFoo();// foo
$fb->echoBar();// bar
$fb->selfIntroduction();// I'm foo.

 実行結果はコメントの通りになります。環境によってはセキュリティ問題でWARNINGが出ます。
 このようにするとFooBarインスタンスで未定義のメソッドが呼ばれたとき、DIしたfooのメソッドが存在するか確認し、fooにあればfooから実行、fooにもなければbarのメソッドを探し実行します。同様のやり方でstaticメソッド、プロパティも継承もどきができます。
 この方法を使えば簡単に全てのクラスの役割を持った神クラスを作れますが、もちろんそんな状態は作るべきでありません。複雑さに際限がなくなりコードを追うのも動作を理解するのも大変になります。

  • この記事いいね! (1)
asaba 著者:asaba

【android・bitmp】bitmapからblobを作成する方法

bitmapとして生成したものは直接データベースには保存できません。

送るにはまずbyte配列に変換してblobに対応させる必要が

あります。blobとは、バイナリデータを格納するための型の一つで、

おおよその画像はデータベースに保存される際はblobに格納されます。

 

一般的には、ByteArrayOutputStreamを使って変換することで

データベースに格納できるようになります。


//bitmap imgには画像が入っていると仮定する
<pre>Bitmap img;</pre>
<pre>ByteArrayOutputStream baos = new ByteArrayOutputStream();
img.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] arr = baos.toByteArray();
System.out.println(arr);
values.put("image", arr);</pre>

 

変換が成功すれば後はputで自由に送ることができます。

 

追記:こちらはbitmapからUriを作成する方法です。

new Fileのところは適宜変えても大丈夫そう。

<pre>File path = new File(MediaStore.Images.Media.insertImage(this.getContentResolver(), img, "Title", null));
Uri uri = Uri.parse(String.valueOf(path));</pre>
  • この記事いいね! (0)
村上 著者:村上

【Cordova】iframeで指定した外部サイトが表示されないときの対処法【iOS】

Cordova で iframe を使った際に遭遇した不具合についてまとめ。
外部サイトを iframe で表示しようとしたのですが、iOS でだけ表示ができなかったので、その対処法についてです。
なお、Android では問題なく表示できていました。

参考にしたサイトはこちらから。

ionic3 – iframe内に外部サイトが表示できない – memorandum-plus
https://memorandum-plus.com/2018/12/21/ionic3 – iframe内に外部サイトが表示できない(ios不具合)/

なお、外部サイトを開く方法としては、「cordova-plugin-inappbrowser」プラグインを使うという手もあったのですが、レイアウト的な問題で、iframe を使いました。

 

さて、こちらの対処法ですが、config.xml ファイルに下記の一文を追加するだけでした。

<allow-navigation href="*" />

こちらを使うと、WebView 上で使用できる URL を制御することができます。
* を指定すると、全ての URL を許可します。

上記を追加したら、あとは再ビルドして実行してください。
私の環境では、問題なく表示されました。

 

以上、Cordova の iOS アプリで、iframe で指定した外部サイトが表示されないときの対処法でした。
参考になれば幸いです。

  • この記事いいね! (0)
著者:杉浦

【Vue】Vuexことはじめ

Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。
Vuex とは何か? | Vuex

 Vuexは上記の様にVue.jsで作成したプログラム中の状態管理を担うライブラリです。乱暴な言い方をするとVuexは管理しやすいグローバル変数を提供してくれます。Vuexのやることはコードと合わせてを見るとわかりやすいです。

// Vuexのストア(状態管理定義)の記述
export const HogeStore:Module<any, any> = {
    // 管理する状態。グローバル変数の本体的なモノ。
    // これを好き勝手弄らせないが、どこからでも参照できるようにする。
    state: { 
        foo: [],
        bar: false,
    },
    // 管理する状態を直接弄ることのできる関数群。ここにある記述以外ではstateを変更できない。
    // store等を介して、commit()関数で呼び出してのみ実行される。
    // 下記の様に思いっきり抽象化すると扱いやすい。
    mutations: {
        setFoo(state, newFoo){// state.fooを変更する
            state.foo = newFoo;
        }
    },
    // 具体的なcommitの内容を記述する。
    // コンポーネント中でstore.dispatch(アクション名)とやると直接commitするより安全安心。
    actions: {
        setFoo(context, newFoo){// state.fooを変更する.mutationsと名前がかぶってもOK
            context.commit('setFoo', newFoo);
        },
        pushFoo(context, newFoo){// state.fooに追記的な
            context.commit('setFoo', newFoo);
        },
        async resetFoo(context){// state.fooをAPI等のリポジトリから取得して改めてセット
            const newFoo = await FooRepository.get();
            context.commit('setFoo', newFoo);
        }
    }
}
// コンポーネント側の記述
// 参照の際には算術プロパティを利用すると便利
computed: {
    foo: {
      get() {
        return store.state.HogeStore.foo;
      },
      set(newFoo) {
        store.dispatch('setFoo', newFoo);
      },
    },
},
methods: {
    async reset() {
        await store.dispatch('resetFoo');// dispatch('アクション名')でactions中のメソッドを呼び出す
    }
}

 公式を読むともっと多彩なことができますが、とりあえず上記のだけでも十分に機能を発揮します。setterが厳格でgetterが自由なグローバル変数な印象を受けました。getterは追記できるため同じリソースを多角的に読みたい際も複雑さがそれほど増しません。
 Vuexを用いるタイミングですが、コンポーネント間のemitが長大になる時でしょう。例えば、あるコンポーネント群(メニューとか検索ボックス)はリソースを取得、あるコンポーネント群(表とかグラフとか)はリソースを表示、あるコンポーネント群(編集モーダルとか)はリソースを加工、とかやりだすと値の変更を4つ5つ越しのコンポーネントに伝播させる必要が出たり、その伝播のルートが3つ股4つ股になったりと煩雑になります。Vuex抜きにこれを整理しようとすると各コンポーネント群の重心であるメインコンポーネント(コンポーネントを呼び出すコンポーネント。App.vueとかPages/Fuga.vueとか)の中に処理が集中しだします。こうなるとメインコンポーネントのやっていることがまさしくVuexのやるべきことになります。
 Vuexはコンポーネント間の依存が直列や一方向の様な整然とした場合は不要ですが、そうでない時は値のやりとり最低限になりすっきりします。また純粋なTypeScriptで記述ができるため動作を明確にしやすいです。先述の例の様にVueアプリが複雑な場合、どこかしらでVuexの役割を既に担おうとしている部分があります。ソースコードが複雑化し、もうどうにもならない時はいっそ後付けでVuexを追加する良いです。

  • この記事いいね! (1)
asaba 著者:asaba

【android】onSaveInstanceStateでメンバ変数を復元する

activityは、画像の回転やブラウザの移動に伴い、破棄されて新しいactivityに

生成されるライフサイクルを持っています。

仮に遷移前にメンバ変数を持っていた場合、移動後は基本destroyされ

またonCreateされるため、持っていた値が無かったことになり

全部消えてしまいます。

 

値を保存して新しいactivity生成時にまた同じ値を使いたいという時は、

onSaveInstanceStateを使って保存する方法があります。

保存対象は主にメンバ変数で、edittextやtextViewといったview系の値はviewが独自で

復元してくれるのでそちらは考慮する必要はないです。

ではどうやって保存するかというと、まずonSaveInstanceStateメソッドをこのように

書いていきます。

 


@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("str", mName);
savedInstanceState.putInt("int", mAge);
}

 

保存する値は、Bundleというクラスが持っているput○○というメソッドに

持たせて保存します。

この他にも、浮動所数点以下の値を保存できるputFloatや画像を保存する

putParcelableがあります。

 

後はどのタイミングで呼ばれるかですが、呼ばれる順番はonparseのすぐ後になります。

onparse->onSaveInstanceState->onDestroy->onCreate(新しいactivity作成)

と覚えていれば問題ないと思います。

 

次に保存した値をどうやって復元するかですが、onSaveInstanceStateで保存した値は

onCreate内でgetStringやgetIntしてメンバ変数に入れるだけで再利用することができます。

 

</pre>
//onCreate内

if (null != savedInstanceState) {
mName = savedInstanceState.getBoolean("str");
mAge = savedInstanceState.getInt("int");
}
<pre>

 

若しくはonRestoreInstanceStateを使って復元するか

 

</pre>
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mName = savedInstanceState.getBoolean("str");
mAge = savedInstanceState.getInt("int");
}
<pre>

 

注意点ですが、画像などのデータ量が大きいものを保存しようとすると

android.os.TransactionTooLargeException: data parcel size 32445533 bytesと

エラーを受けるので、どうしても再利用したい場合は必ずデータ量を小さくしたり

Uriのまま保存することをおすすめします。

  • この記事いいね! (0)
村上 著者:村上

【Cordova】「Refused to load gap://ready because it does not appear in the frame-src directive of the Content Security Policy」エラーの対処法

Cordova で開発中のアプリを iOS で実行したところ、「Refused to load gap://ready because it does not appear in the frame-src directive of the Content Security Policy.」というエラーが発生しました。
Google翻訳にかけてみたところ、「コンテンツセキュリティポリシーの frame-src ディレクティブに表示されないため、gap:// ready の読み込みを拒否しました。」とのことでした。
HTML の Meta タグで指定している Content Security Policy に関する記述のところかとあたりをつけつつ、念のため検索。

こちらの記事がヒットしました。

iOSでRefused to load gap://ready because it appears in neither the child-src directive nor the default-src directive of the Content Security Policy. エラー
https://ufirst.jp/memo/2016/09/16/ios%E3%81%A7refused-to-load-gapready-because-it-appears-in-neither-the-child-src-directive-nor-the-default-src-directive-of-the-content-security-policy-エラー/

 

上記の記事によると、やはり Content Security Policy に関する記述に抜けがあったようで、frame-src の項目に gap://ready file: を追加し、再度実行したところ、今度は問題なくページが表示されました!
なお、記事内には default-src に追加とありましたが、私の環境では、frame-src の項目があったため、こちらに追加しました。
というか、frame-src がある状態で、default-srcgap://ready file: を追加してもこの設定は frame-src には反映されないので、今回のエラーは解決できません。

なので、gap://ready file: の記述は frame-src がある場合は frame-src に、frame-src がない場合は default-src に記述するようにしてください。

 

以上、Cordova アプリを iOS で起動した際に「Refused to load gap://ready because it does not appear in the frame-src directive of the Content Security Policy」エラーが発生するときの対処法でした。

  • この記事いいね! (0)
takahashi 著者:takahashi

redisを触ってみる 初期設定編

以前にも記事で取り上げたredisですが、いろいろきっかけがありまして実際に触ってみることにしました。

redisはNoSQLなデータベースサーバーなので、redisのサーバープロセスを立ち上げる必要があります。

ということで、まずはOSにredisをインストールします。

RedHat系(CentOSなど) の場合は

sudo yum install redis #インストール
sudo systemctl start redis #起動
sudo systemctl enable redis #自動起動に登録

debian系(Ubuntuなど)の場合は

sudo apt update #パッケージリスト更新
sudo apt install redis #インストール
sudo systemctl start redis-server #起動
sudo systemctl enable redis-server #自動起動に登録

mac OSでbrewコマンドが利用できる場合は

brew install redis #インストール
redis-server #フォアグラウンドで起動

とします。

インストールが完了すると、redisサーバー本体のredis-serverとredisを操作するクライアントであるredis-cliが利用できるようになります。

同じPC内でredisのサーバーとクライアント両方を動かす場合は、redis-serverが起動しているのを確認したうえで

redis-cil

とするだけでredis操作用のプロンプトに入ることができます。

ところで、redis-cliからリモートのredis-serverに接続しようとすると

こんな感じで、はじかれてしまいます。

redisはデフォルトでlocalhostからの接続のみ受け付けているため、外部からの接続を受け付けるようにするにはLANからの接続を受け付けるように設定する必要があります。

Ubuntuの場合は

/etc/redis/redis.conf

に設定があるので、vimなどでこのファイルを編集します。

編集するのは、上の画像にあるように “bind”と書かれている行です。

デフォルトでは 127.0.0.1 つまり、localhost(サーバー自身)からの接続のみしか受け付けない設定になっています。

外部からアクセスできるようにするには、redisが稼働するサーバーマシンのLANポート(NIC)に割り振られているIPアドレスを指定します。

例えば、 サーバーマシンに192.168.11.100というIPアドレスが降られている場合は

bind 127.0.0.1 192.168.11.100 #2019/9/19 訂正 127.0.0.1をとってしまうとlocalhostからの接続ができなくなります。

とすればこのIPアドレスが振られたNIC経由でのredisへの接続を受け付けるようになります。

もし複数のNICが搭載され、複数のIPが振られている状態で両方のネットワークから接続を受け付けたい場合は

bind 192.168.11.100 192.168.1.100

のようにスペース区切りにすると複数指定できます。

また、すべてのNIC経由での接続を受け付けたい場合は

bind 0.0.0.0

とします。

設定が終わったら、Linux系の場合は

sudo systemctl restart redis-server (あるいはredis)

とします。

mac OSなどでフォアグラウンドで起動している場合はいったんredisを停止させてから起動しなおします。

この状態で再度 redis-cli コマンドでリモートにつなげると

こんな感じでリモートからでも接続できるようになります。

・次の記事
redisを触ってみる 基本操作編

  • この記事いいね! (0)
著者:ym

Windows サンドボックス

先日ようやく Windows Update May 2019 Update をしたので、「Windows サンドボックス (sandbox)」という機能を使ってみました。

この機能は Windows OS上に、危ない環境をテストする為のOSで、このOSで実行する限りは、親の OS には影響が出ない環境です。

サンドボックスのウィンドウを閉じると、データは初期化されてしまいます。

インストールテストなど、環境を壊しかねない機能を事前にテストする際に良さそうです。私のHDDなPCでもどうにか、素早く(30秒程度)で起動してくれました。

再起動や、サインアウトすると、セッション切断されて初期化されるが、日本語にならないのは、どうにかならないのだろうか。

  • この記事いいね! (0)