Rodhos Soft

備忘録を兼ねた技術的なメモです。Rofhos SoftではiOSアプリ開発を中心としてAndroid, Webサービス等の開発を承っております。まずはご相談下さい。

flexbox その2

flexboxを改めてやる。以下のサイトの記事を読んだ。

coliss.com

flexアイテムとflexコンテナ

flexコンテナは多数のflexアイテムを内包する。flexコンテナにしたい要素に display: flex;を設定する。
すると子要素は自動的にflexアイテムになる。

並べる方向

flex-direction: row; で横方向に並べる。 row-reverseなら右から左になる。columnで上から下。column-reverseで下から上。

1行に収めたい

flex-wrap: nowrap; で1行に収める。flexアイテムの幅は一行に収まるように縮まる。

flex-wrap: wrap;だと複数行

まとめて設定

flex-flow: row nowrap;

右づめで表示したい。

justify-content: flex-end; でdirectionがrowなら右づめになる。flex-startなら左づめ、centerなら中央、space-betweenなら等間隔に間をあけて表示される。space-aroundなら端っこにも空間があいた等間隔。

横にならべておいてアイテムの高さはコンテナいっぱいにひろげたい。

横に並べている(row)のときの縦をクロス軸と呼ぶ。
align-items: stretch; を設定すれば良い。
縦方向は上詰めにしたい場合はalign-items: flex-start;。下詰めにしたいときはflex-end。縦方向に中央に配置したい場合はcenter。baselaineに沿わせるならbaseline。

横にならべておいて、複数行の行間を調整したい。

align-content: stretch; コンテナの高さに基づいて均等配置。flex-startなら上からつまる。flex-endなら下からつまる。centerは中央。上端から下端で等間隔ならspace-between。上端と下端で端っこにも空間があいた等間隔ならspace-around。

アイテムの並び順

アイテムに対して order: 2; 等と設定。

アイテムの幅の比率

アイテムに対して flex-grow: 1; 等と設定。デフォルトは0
他のアイテムに対して縮まるかはflex-shrink: 1;等と設定。デフォルトは1

アイテムの幅の設定

アイテムに対してflex-basis: 350px; で350に、flex-basis: auto;なら自動計算。

glow, shrink, basisはまとめてflex:で指定できる。

デフォルト値なら flex: 0 1 auto;

アイテムを指定して並べ方を指定

align-self

URLヘルパ

URLを作る。

$this->Url->build(['action' => 'home', $article->id])

/kagero3/articles/view/1

ができる。

エレメントとHTMLヘルパ等と組み合わせて次のようなものが作れた。

<?= $this->Html->tag( "a",
    $this->Element( "agora_card", 
                    [
                        "title" => $article->title,
                        "body" => $article->body
                    ]
                )
    , ["href" => 
        $this->Url->build(['action' => 'home', $article->id])
        ]
    ) 
?>
<a url="/kagero3/articles/view/1"><div style="width:200px; height:200px; background-color:white;">
    <div>
        <p>
            タイトル        </p>
    </div>
    <div>
        <p>
            内容        </p>
    </div>
</div></a>

ちょっと面白くなってきた。

HTMLヘルパー その2

以下を参考に色々さわっている。
qiita.com
CakePHP 1.3 - Htmlヘルパー


スクリプトタグ

<?= $this->Html->script('myscripts'); ?>
<script src="/kagero3/js/myscripts.js"></script>

CSS

<?= $this->Html->css( 'mycss'); ?>
<link rel="stylesheet" href="/kagero3/css/mycss.css"/>

画像リンク

<?= $this->Html->image( 'cake.power.gif',
                        ["alt" => "cake.power",
                         "url" => [ "controller" => "Articles",
                                    "action" => "home",
                                    "1"]
                        ]
                    ); ?>
<a href="/kagero3/articles/home/1"><img src="/kagero3/img/cake.power.gif" alt="cake.power"/></a>

urlを指定しないと単純にimageタグができる。

<?= $this->Html->image( 'cake.power.gif',
                        ["alt" => "cake.power"]
                    ); ?>
<img src="/kagero3/img/cake.power.gif" alt="cake.power"/>

タグ

タグ自体を指定して生成することもできる。

<?= $this->Html->tag( 'div',
                        'hello',
                        ['class' => 'classA']
                    ); ?>
<div class="classA">hello</div>


よって、タグの入れ子も表現できる。

<?= $this->Html->tag('div',$this->Html->tag( 'p','hello'),['class' => 'classA']); ?>
<div class="classA"><p>hello</p></div>

HTMLヘルパー

qiita.com

単純にリンク

<?= $this->Html->link("hello","http://rodhos.info/") ?>

は次のように変換される。

<a href="http://rodhos.info/">hello</a>

つまり、第1引数が表示する文字で、第2引数がURL。
第2引数は以下のように連想配列で指定することもできる。

<?= $this->Html->link(__('View'), ['action' => 'view', $article->id]) ?>
<a href="/kagero3/articles/view/1">View</a>

もう一つ例として

<?= $this->Html->link("hello",[ "controller" => "Articles", 
                                "action" => "home",
                                "1"]) ?>

は次のように変換される。

<a href="/kagero3/articles/home/1">hello</a>

第3引数

第3引数を指定するとaタグに属性をつけられる。

<?= $this->Html->link(  "hello",
                        "http://rodhos.info/",
                        ["class" => "button"]) ?>

は次のようになる。

<a href="http://rodhos.info/" class="button">hello</a>

テーブルのところ

CakePHPのbakeで作られたテーブル

    <table cellpadding="0" cellspacing="0">
        <thead>
            <tr>
                <th scope="col"><?= $this->Paginator->sort('id') ?></th>
                <th scope="col"><?= $this->Paginator->sort('title') ?></th>
                <th scope="col"><?= $this->Paginator->sort('category_id') ?></th>
                <th scope="col"><?= $this->Paginator->sort('avatar_id') ?></th>
                <th scope="col"><?= $this->Paginator->sort('created') ?></th>
                <th scope="col"><?= $this->Paginator->sort('modified') ?></th>
                <th scope="col" class="actions"><?= __('Actions') ?></th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($articles as $article): ?>
            <tr>
                <td><?= $this->Number->format($article->id) ?></td>
                <td><?= h($article->title) ?></td>
                <td><?= $article->has('category') ? $this->Html->link($article->category->name, ['controller' => 'Categories', 'action' => 'view', $article->category->id]) : '' ?></td>
                <td><?= $article->has('avatar') ? $this->Html->link($article->avatar->name, ['controller' => 'Avatars', 'action' => 'view', $article->avatar->id]) : '' ?></td>
                <td><?= h($article->created) ?></td>
                <td><?= h($article->modified) ?></td>
                <td class="actions">
                    <?= $this->Html->link(__('View'), ['action' => 'view', $article->id]) ?>
                    <?= $this->Html->link(__('Edit'), ['action' => 'edit', $article->id]) ?>
                    <?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $article->id], ['confirm' => __('Are you sure you want to delete # {0}?', $article->id)]) ?>
                </td>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>

Objective-C++でクラス

JSONを使った受け渡しを想定して書いてみた。非効率かも。

#import <Foundation/Foundation.h>

@class Engine;

typedef NSDictionary JSON;

@protocol EngineProtocol <NSObject>
- (void)engine:(nonnull Engine *)engine message:(nonnull JSON *)message;
@end

@interface Engine : NSObject
+ (nonnull instancetype)engine:(nonnull id <EngineProtocol>)delegate;
- (void)tellMessage:(nonnull JSON *)message;
@end

#include <stdio.h>
#include <iostream>

class EngineCpp;




class EngineCpp {
public:
    EngineCpp();
    ~EngineCpp();
    void hello();
    typedef std::shared_ptr<EngineCpp> Ptr;
    std::string message;
    std::string proc(std::string src);
};

EngineCpp::EngineCpp() {
    std::cout << "EngineCpp new" << std::endl;
}

EngineCpp::~EngineCpp() {
    std::cout << "EngineCpp delete" << std::endl;
}

void EngineCpp::hello() {
    std::cout << "EngineCpp hello" << std::endl;
}

std::string EngineCpp::proc(std::string src) {
    return "{\"Hello\":" + src + "}";
}

@interface NSJSONSerialization(String)
+ (NSString *)jsonString:(NSDictionary *)dic;
+ (NSDictionary *)jsonString_r:(std::string &)str;
@end

@implementation NSJSONSerialization(String)
+ (NSString *)jsonString:(NSDictionary *)dic {
    NSData *d = [NSJSONSerialization dataWithJSONObject:dic
                                                options:NSJSONWritingPrettyPrinted
                                                  error:nil];
    NSString *sd = [[NSString alloc] initWithData:d
                                         encoding:NSUTF8StringEncoding];
    return sd;
}

+ (NSDictionary *)jsonString_r:(std::string &)str {
    NSData *data = [NSData dataWithBytes:str.c_str() length:str.length()];
    
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data
                                                        options:NSJSONReadingMutableContainers                                                          error:nil];
    return dic;
}

@end




@interface Engine()
@property (nonatomic) id <EngineProtocol> delegate;
@property (nonatomic, assign) EngineCpp::Ptr engineCpp;
@end


@implementation Engine {
}

- (void)dealloc {
    NSLog(@"Engine dealloc");
}

+ (instancetype)engine:(id<EngineProtocol>)delegate {
    Engine *engine = [Engine new];
    engine.delegate = delegate;
    engine.engineCpp = std::make_shared<EngineCpp>();

    return engine;
}


- (void)tellMessage:(JSON *)message {

    std::string str = [NSJSONSerialization jsonString:message].UTF8String;
    
    std::string result = self.engineCpp->proc(str);
    
    NSDictionary *dic = [NSJSONSerialization jsonString_r:result];
    
    [self.delegate engine:self message:dic];
}

@end

リーダー選出アルゴリズム

リーダー選出のアルゴリズム

後続(successor)がダウンしている場合、飛ばして次のプロセスにElectionメッセージを送信する。
このメッセージには自分のプロセス番号をつける。
後続者は次々と自分の後続者にElectionメッセージを自分のプロセス番号を追加してまわしていく。
一周してElectionメッセージがもどってきたら(メッセージに自分のプロセスIDが入っているかどうかで判断する)、メッセージのタイプをCoordinatorに変更、これを回覧する。
すべてのプロセス番号がそろっているのでCoordinatorが誰かがわかる。
結果、2周する。

EventManager

登録方法は2種類。
EventListenerInterfaceを実装してonで自身を登録するか、onでキーとともに無名関数を登録しておくか。

Eventを作ってdispatchすることも可能。

登録したものの削除はoffでできる。

使いすぎて密結合に注意。

イベントシステム - 3.x

UIWindow

自作アラートのような使い方ができる。
UIWindow.rootViewControllerを設定
UIWindow.windowLevelはノーマルかアラートの+幾つかを設定するとより前に出てくる。
UIWindow.makeAndVisibleで表示される。
参照を破棄してmakeAndVisibleで消える。

一文字づつ区切る

NSStringEnumerationByComposedCharacterSequencesを用いる。

        NSString *string= @"ハローワールド🗾";
        
        NSMutableArray *list = [NSMutableArray array];
        
        [string enumerateSubstringsInRange:NSMakeRange(0,string.length)
                                                   options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
                                                       [list addObject:substring];
                                                   }];
        for (NSString *sub in list) {
            NSLog(@"%@", sub);
            for (int i=0;i<sub.length;i++) {
                NSLog(@"%04x", [sub characterAtIndex:i]);
            }
                  
        }

Unicode block

Unicode block - Wikipedia

Basic Latin

UTF8では1バイトで表現される。
Basic Latin (Unicode block) - Wikipedia

Latin-1 Supplement

U+0080–U+00FF

Halfwidth and fullwidth forms

Halfwidth and fullwidth forms - Wikipedia
U+FF00–FFEF 

  1. U+FF01–FF5EはASCIIの全角
  2. U+FF65–FFDCはカタカナ、ハングルの半角

UTF、Unicode

Unicode

ユニコードスカラ値(非負。16進数で書く。prefixにU+) 例 U+143FC
基本多言語面(BMP)は4桁。補助多言語面は5,6桁使用。
ユニコードスカラ値のシーケンスが文字列。スカラの値のある場所が符号ポイント。符号ポイントのつらなりをブロックと呼ぶ。

文字列符号化方式

  1. 文字符号化スキーム(CES)
  2. 文字符号化方式(CEF) → UTF-8UTF-16UTF-32

UTF8

  1. 可変長(1-4バイト)の8ビット符号単位
  2. ASCIIと同じ部分は1バイト(ASCIIに対して上位互換)
  3. その他の部分を2-6バイトで符号化
  4. 文字の境界が明確

UTF-16

  1. BMP文字を16ビット符号単位
  2. 他をサロゲートペアを使って16ビット符号単位二つで表す。
  3. ファイルの先頭にバイト順マーク (BOM) が付与。(U+FEFF) → FF FEならリトリエンディアン、FE FFならビッグエンディアン

UTF-32

  1. Unicodeのすべての符号位置を単一長の符号単位として32ビットで表現する
  2. ファイルのサイズが大きくなる
  3. BOM FF FE 00 00ならリトルエンディアン00 00 FE FFならビッグエンディアン

Hello World 4回目

アプリ作る。cakephp 3.5

composer create-project --prefer-dist cakephp/app MyApp2017

中に入る

cd MyApp2017/

sqlite3のdb作成

sqlite3 MyApp2017.db
sqlite> .tables
sqlite> .q

コンフィグでデータベースをsqlite3に。atomは好きなエディタで。

atom ./config/app.php
    'Datasources' => [
        'default' => [
            'className' => 'Cake\Database\Connection',
            'driver' => 'Cake\Database\Driver\Sqlite',
            'persistent' => false,
            'database' => 'MyApp2017.db',
            'encoding' => 'utf8',
            'timezone' => 'UTC',
            'flags' => [],
            'cacheMetadata' => true,
            'log' => false,

ここでサーバを立ち上げて確認する。

./bin/cake server

http://localhost:8765/にアクセス。databaseにつながっていることを確認。

ここでDBのテーブルを考える。
ログインしたユーザが書き込める掲示板という発想。

ユーザーテーブルを作ってみる。

rodhos.hatenadiary.jp

./bin/cake bake migration CreateUsers name:string description:text created modified


/config/Migrationsフォルダに20170911144034_CreateUsers.phpファイルができる。

    public function change()
    {
        $table = $this->table('users');
        $table->addColumn('name', 'string', [
            'default' => null,
            'limit' => 255,
            'null' => false,
        ]);
        $table->addColumn('description', 'text', [
            'default' => null,
            'null' => false,
        ]);
        $table->addColumn('created', 'datetime', [
            'default' => null,
            'null' => false,
        ]);
        $table->addColumn('modified', 'datetime', [
            'default' => null,
            'null' => false,
        ]);
        $table->create();
    }

実際に作るには

./bin/cake migrations migrate

確認する。

sqlite3 MyApp2017.db
.tables
.schema

たしかにusersができている。idというautoincrementな主キーは自動で追加されるようだ。

次にUsersのモデル(テーブルとエンティティ)を作る。

./bin/cake bake model Users

これで

src/Model/Table/UsersTable.php

src/Model/Entity/User.php

ができた。

class UsersTable extends Table
{

    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->setTable('users');
        $this->setDisplayField('name');
        $this->setPrimaryKey('id');

        $this->addBehavior('Timestamp');
    }

    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator)
    {
        $validator
            ->integer('id')
            ->allowEmpty('id', 'create');

        $validator
            ->scalar('name')
            ->allowEmpty('name');

        $validator
            ->scalar('description')
            ->allowEmpty('description');

        return $validator;
    }
}
class User extends Entity
{

    /**
     * Fields that can be mass assigned using newEntity() or patchEntity().
     *
     * Note that when '*' is set to true, this allows all unspecified fields to
     * be mass assigned. For security purposes, it is advised to set '*' to false
     * (or remove it), and explicitly make individual fields accessible as needed.
     *
     * @var array
     */
    protected $_accessible = [
        '*' => true,
        'id' => false
    ];
}

調子に乗ってユーザーコントローラを作る。

./bin/cake bake controller Users

これで
/src/Controller/UsersController.php ができた。

<?php
namespace App\Controller;

use App\Controller\AppController;

/**
 * Users Controller
 *
 * @property \App\Model\Table\UsersTable $Users
 *
 * @method \App\Model\Entity\User[] paginate($object = null, array $settings = [])
 */
class UsersController extends AppController
{

    /**
     * Index method
     *
     * @return \Cake\Http\Response|void
     */
    public function index()
    {
        $users = $this->paginate($this->Users);

        $this->set(compact('users'));
        $this->set('_serialize', ['users']);
    }

    /**
     * View method
     *
     * @param string|null $id User id.
     * @return \Cake\Http\Response|void
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
     */
    public function view($id = null)
    {
        $user = $this->Users->get($id, [
            'contain' => []
        ]);

        $this->set('user', $user);
        $this->set('_serialize', ['user']);
    }

    /**
     * Add method
     *
     * @return \Cake\Http\Response|null Redirects on successful add, renders view otherwise.
     */
    public function add()
    {
        $user = $this->Users->newEntity();
        if ($this->request->is('post')) {
            $user = $this->Users->patchEntity($user, $this->request->getData());
            if ($this->Users->save($user)) {
                $this->Flash->success(__('The user has been saved.'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The user could not be saved. Please, try again.'));
        }
        $this->set(compact('user'));
        $this->set('_serialize', ['user']);
    }

    /**
     * Edit method
     *
     * @param string|null $id User id.
     * @return \Cake\Http\Response|null Redirects on successful edit, renders view otherwise.
     * @throws \Cake\Network\Exception\NotFoundException When record not found.
     */
    public function edit($id = null)
    {
        $user = $this->Users->get($id, [
            'contain' => []
        ]);
        if ($this->request->is(['patch', 'post', 'put'])) {
            $user = $this->Users->patchEntity($user, $this->request->getData());
            if ($this->Users->save($user)) {
                $this->Flash->success(__('The user has been saved.'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The user could not be saved. Please, try again.'));
        }
        $this->set(compact('user'));
        $this->set('_serialize', ['user']);
    }

    /**
     * Delete method
     *
     * @param string|null $id User id.
     * @return \Cake\Http\Response|null Redirects to index.
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
     */
    public function delete($id = null)
    {
        $this->request->allowMethod(['post', 'delete']);
        $user = $this->Users->get($id);
        if ($this->Users->delete($user)) {
            $this->Flash->success(__('The user has been deleted.'));
        } else {
            $this->Flash->error(__('The user could not be deleted. Please, try again.'));
        }

        return $this->redirect(['action' => 'index']);
    }
}


最後にtemplateをbakeする。

./bin/cake bake template Users

src/Template/Users/index.ctp

src/Template/Users/view.ctp

src/Template/Users/add.ctp

src/Template/Users/edit.ctp

が生成された。

このあたりでもう一度サーバーを立ち上げみてみる。

http://localhost:8765/users にアクセスするとユーザー一覧が出てくる。

続く。