このエントリーをはてなブックマークに追加

2017年1月31日火曜日

Gemのバージョンをプロジェクトごとに分ける方法

こんにちは。h_ono_222です。

複数のアプリケーションを同時に開発している場合、同じGemでもそれぞれのアプリケーションごとにバージョンを分けたい場合があると思います。
今回はその方法を紹介します。


1. 空のプロジェクトを作成する

$ mkdir test_app && cd test_app
$ bundle init
Writing new Gemfile to /Users/user_name/test_app/Gemfile


2. Gemfileを修正する

# gem "rails"のコメントアウトを外します。


3. インストール先を指定してbundle install

$ bundle install --path vendor/bundle
Fetching gem metadata from https://rubygems.org/ 
Fetching version metadata from https://rubygems.org/ 
Fetching dependency metadata from https://rubygems.org/ 
Resolving dependencies....
# 〜中略〜
Bundle complete! 1 Gemfile dependency, 38 gems now installed.
Bundled gems are installed into ./vendor/bundle.


4. アプリケーションを作成する

$ bundle exec rails new .
       exist  
      create  README.md
      create  Rakefile
      create  config.ru
      create  .gitignore
    conflict  Gemfile
Overwrite /Users/DevWork/Documents/test_app/Gemfile? (enter "h" for help) [Ynaqdh] Y # Yを入力してEnter
force  Gemfile
# 〜中略〜
Bundle complete! 15 Gemfile dependencies, 62 gems now installed.
Bundled gems are installed into ./vendor/bundle.
         run  bundle exec spring binstub --all
* bin/rake: spring inserted
* bin/rails: spring inserted

以上です。

2017年1月30日月曜日

配牌からあがれる可能性を予測する!(その4 勾配を求める際に詰まったこと)

こんにちは、Taroです。
前回の記事の記事の続きになります。
配牌からあがれる可能性を予測する!(その3 学習の実装)
本日は学習を実装する上で、すぐに理解できなかった勾配算出に関わる部分をまとめたいと思います。

前回の訂正


損失値を算出して、グラフ化しておりましたが、1回ごとの平均値として算出しておりませんでした。
そのため、実際にどれくらいの誤差が生じているか分かりづらかったため、平均値で図をプロットするよう修正しました。
差分はこちらです。
https://github.com/naoki85/python_mahjong/commit

勾配法について


ある重み、バイアスパラメーターのときの、損失関数の勾配(傾き)を求め、傾きが小さくなるようパラメーターを変化させる手法とのことです。
機械学習はこの最適パラメーターを求めるのが1つの命題となっています。
ただ、パラメーターを決めると一口で言っても無限に近い数値の海から探さなければいけません。
ここで本の中で使っていた例えは、思わずへ〜、と頷いてしまいました。
文中ででてくるSGDは、確率的勾配降下法という手法の略です。

ここに風変わりな冒険家がいます。彼は広大な乾燥地帯を旅しながら、日々深い谷底を求めて旅を続けています。

~~中略~~

しかも、彼は、厳しい”制約”を2つ自分に課しています。ひとつは地図を見ないこと、もうひとつは目隠しをすることです。

~~中略~~

この困難な状況で重要となってくるのが、地面の「傾斜」です。

~~中略~~

そこで、今いる場所で一番傾斜がきつい方向に進もうというのが、SGDの戦略です。

「ゼロから作るDeep Learning」第6章より引用


勾配を算出する関数について


さて、実際に勾配を算出する段になりまして、少し理解が追いつきませんでした。
例えば、損失関数に対する微分は下記のようになります。(本の中の式)
# xはテストデータ、tは教師データです。
# lossは損失関数です。
def f(W):
    return net.loss(x, t)
ここで関数fはxとtから求める損失関数、f(W)でWに関する関数になります。
(Pythonではlambda式で一行で書けるようですが、あえてこちらで記載します。)
fをWに関して微分して勾配を求めます。

ここで、高校数学程度の知識しかない私はこう思いました。
あれ、fをWに関して微分しても、Wが要素になってないから0なんじゃないか、と。
とりあえずこの疑問は置いておいて、本を読み進めていくと、どう考えても勾配を0として計算しているようには見えませんでした。
Wをどこかで関わらせているのかと思い、勾配を求める関数をちゃんと読んでみました。
# 実際に呼んでいるコード
# net.Wはネットワーククラスが持つ重みの値(クラスプロパティ)
dW = numerical_gradient(f, net.W)

def numerical_gradient(self, f, x):
    h = 1e-4
    grad = np.zeros_like(x)
    
    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index
        tmp_val = x[idx]
        # ここでプロパティの値を上書きしている!
        x[idx] = float(tmp_val) + h
        fxh1 = f(x)
        
        x[idx] = tmp_val - h
        fxh2 = f(x)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        
         # ここで元に戻している!
        x[idx] = tmp_val
        it.iternext()
        
    return grad
つまり、重みパラメーターをクラスに持たせることで、微分式内で値を暫定的に書き換えて処理をさせています。
恥ずかしながら、ここで引っかかり、いまいち以降の処理を理解できておりませんでした。
もし、こちらの本で勉強していて、同じような場所で詰まっているという方がいらっしゃれば、参考にしていただければ、と思います。

2017年1月28日土曜日

iOS SwiftでS3への画像アップロード

 こんにちは、Hiroです。今回はSwiftでAWSのS3へ画像をアップロードする方法について、ブログを書きたいと思います。
 AWS Cognitoでの認証が本来は望ましいのですが、今回は簡単にIAMユーザを作成し、ACCESS KEYとSECRET ACCESS KEYを利用した方法で紹介したいと思います。アプリをリリースする場合は、AWS Cognitoを利用するか、IAMユーザの場合は、アクセス権限を十分に絞ってください。


AWS上での事前準備


AWS上での準備については、省略しますが下記の2つを準備してください。IAMユーザについては、作成したS3のバケットへアップロード(PutObject)できる権限を付与しておく必要があります。
  • S3にバケットを作成
  • IAMユーザの作成(上記のバケットへの「s3:PutObject」権限あり)

CocoaPodsでAWS SDKをインストールをする


Podfileに下記を記載して、インストールします。
  use_frameworks!

  pod 'AWSS3'
$ pod install


認証の設定をする


冒頭で記載したとおり、今回はIAMユーザでの認証を行います。accessKeyとsecretKeyにはAWSで作成したIAMユーザのものを設定してください。
下記の処理は、S3で画像などをアップロードする前に1度だけ実行するようにします。
import AWSCore
...
...
    func configureService() {
        let credentialsProvider = AWSStaticCredentialsProvider(accessKey: "your access key", secretKey: "your secret key")
        let serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.apNortheast1, credentialsProvider: credentialsProvider)
        AWSServiceManager.default().defaultServiceConfiguration = serviceConfiguration
    }


UIImageを保存して、URLを取得する


S3へ投稿するファイルは、一時的に端末内に保存して、そのURLをS3のSDKに指定する必要があります。
下記のメソッドで、S3へのアップロードする画像が保存されているURLを取得できます。
    private func generateImageUrl(_ uploadImage: UIImage) -> URL {
        let imageURL = URL(fileURLWithPath: NSTemporaryDirectory().appendingFormat("upload.jpg"))
        if let jpegData = UIImageJPEGRepresentation(uploadImage, 80) {
            try! jpegData.write(to: imageURL, options: [.atomicWrite])
        }
        return imageURL
    }


S3へアップロードする


下記のメソッドでS3へアップロードができます。下記のメソッドを呼ぶ前には、上記で記載している認証の設定を事前に実行する必要があります。
また、「uploadRequest?.bucket」にはAWS上で作成したバケット名を指定し、「uploadRequest?.key」にはS3上に保存したいファイル名を指定します。 なお、ファイル名は「test/hello.jpg」とするとAWSコンソールなどから見た際にtestフォルダ内にhello.jpgファイルが保存されているように見えるので、ファイルをまとめて管理したい場合には、「test/」などのプレフィックスをつけるとよいでしょう。
import AWSS3
...
...
    public func uploadImage(_ uploadImage: UIImage) {
        let transferManager = AWSS3TransferManager.default()
        let uploadRequest = AWSS3TransferManagerUploadRequest()
        uploadRequest?.bucket = "your S3 bucket name"
        uploadRequest?.key = "your file name on S3"
        uploadRequest?.body = generateImageUrl(uploadImage)
        transferManager?.upload(uploadRequest).continue({ (task: AWSTask) -> Any? in
            if task.error != nil || task.exception != nil {
                // エラー
            }
            return nil
        })
    }


2017年1月27日金曜日

Swift3でDateからその月の月末のDateを取得

こんにちはonukiです。

今回はSwift3でDateの月末が何日か取得したかったので
備忘録がてら、私が行った方法を紹介したいと思います。

実装

var date = Date()
let calendar = NSCalendar(identifier: NSCalendar.Identifier.gregorian)!

// 年月日時分秒のNSComponents
var comp = calendar.components([.year, .month, .day, .hour, .minute, .second], from: date as Date)

// 月初の0時0分0秒に設定
comp.day = 1
comp.hour = 0
comp.minute = 0
comp.second = 0
// ここでcalendar.date(from: comp)!すれば月初のDateが取得できます

// その月が何日あるかを計算します
let range = calendar.range(of: .day, in: .month, for: date as Date)
let lastDay = range.length

// ここで月末の日に変えます
comp.day = lastDay

// Dateを作成
let retDate = calendar.date(from: comp)!

上記では現在日時からDateを作成し、その月の月末にしてるので
例えば2017年1月に実行した場合下記のフォーマットで整形しprintすると「20170131」になります。
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
let dateStr: String = formatter.string(from: retDate as Date)
print(dateStr)

2017年1月26日木曜日

はじめの一歩 -Rails ActiveRecord編- SELECT4


どうも、はじめです。
前回はJOINについて書いてみました。
はじめの一歩 -Rails ActiveRecord編- SELECT3
今回も引き続きJOINに関する内容ですが、JOINでも3つ以上のテーブルの結合について書いていこうと思います。


はじめに


3つ以上のテーブルの結合なので、結合できるテーブルを3つ用意します。

usersテーブル
[id: 1, user_name: 'Aさん'],
[id: 2, user_name: 'Bさん'],
[Id: 3, user_name: 'Cさん']

itemsテーブル
[id: 1, item_name: 'Rubyの本'],
[id: 2, item_name: 'Railsの本'],
[id: 3, item_name: 'PHPの本']

user_itemsテーブル(userが持っているitemを管理するテーブル)
[id: 1, user_id: 1, item_id: 1],
[id: 2, user_id: 1, item_id: 2],
[id: 3, user_id: 2, item_id: 3]

関連を説明すると以下のようになります。
user : user_item => 1 : n
item : user_item => 1 : n

それでは前回使用したincludesを使用して書いていこうと思います。


取得


・Aさんが持っているアイテムを全て取得したい場合
user_items = User.includes(user_items: :item).find(1)

上記のように記述することで以下のようなSQLが実行されます。
SELECT `users`.* FROM `users` WHERE `users`.`id` = 1;
SELECT `user_items`.* FROM `user_items` WHERE `user_items`.`id` in (1,2);
SELECT `items`.* FROM `items` WHERE `items`.`id` in (1,2);

joinsの場合も同様の書き方となります。


表示


アイテム名を表示したい場合は以下のようにします。
user_items.user_items.each do |user_item|
  p user_item.item.item_name
end
# => 'Rubyの本'
# => 'Railsの本'


さらにテーブルが増えた場合


itemsにcategoryテーブルを関連付けして色々なパターンで取得をしてみたいと思います。
以下のような関連性でitemsに対し、categoriesテーブルを作成します。
items : category => n : 1

itemsテーブル
[id: 1, item_name: 'Rubyの本', category_id: 1],
[id: 2, item_name: 'Railsの本', category_id: 1],
[id: 3, item_name: 'PHPの本', category_id: 2]

categoriesテーブル
[id: 1, category_name: 'Ruby'],
[id: 2, category_name: 'PHP']

パターン1
・Aさんが持っているアイテムのカテゴリーまで取得したい場合
User.includes(user_items: {item: :category}).find(1)

パターン2
・"Rubyの本"のカテゴリーと持っているユーザーを取得したい場合
Item.includes(:category, {user_items: :user}).find(1)


まとめ


以下のような関連性だった場合
a:b:c => 1:n:1
c:d =>n:1

隣接しているテーブルを複数結合したい場合はのように「,」でつなげることができます。
B.includes(:a, :c)
二つ隣のテーブルの情報を取得する場合は「:」を向かい合わせに記述をします。
A.includes(b: :c)
さらに深いテーブルの情報を取得する場合は{}を使用します
A.includes(b: {c: :d})

前回も記述をしたように
3つ以上のテーブルを結合する場合でもテーブル名は
複数系、単数系を意識しなければいけませんので注意してください。

以上でSELECT系(JOIN系)を終了し、
次回からはUPDATEに入ろうと思います。

2017年1月25日水曜日

360度カメラ買いました

どうもー。デザイナーのnottyです。
今年から、NowDevelopingに参加しました。どうぞお見知り置きを 〜
さて、話は変わりまして、
最近360度カメラを購入しました。※この記事は360度カメラの模索中のメモです
THETA SC という機種です。

購入した経緯
正直、何も考えずに買いました。
だから使い道を探るため毎日持ち歩き、撮影しています。

自分の顔やいろんな人の顔が映ってしまうので、公式からですが、
こんな感じの写真が撮れます。

撮った後、写真を見るのが楽しくなるプロダクトです。
技術的に相性が良いのはVRに当たるのかな?

360度カメラのコンテンツはまだまだ面白いのが生まれると感じています。
みたいなWebVRフレームワークが登場したり(2015年で古いですが...)
VRの空間?に絵を描いたり(これ半端ないす)

2017年となった今では情報がたくさんあって、デバイスの性能も良くなったので、利用する人、モノ作りする人は、VRへの参入障壁が低くなったと思います。

今年はVRコンテンツが増えるのか

正直、まだまだVRが普及するとは思えませんが、360度カメラはWebでコンテンツを見るときに全体の雰囲気や様子を掴めるので使いどころによっては、効果があると思います。(旅行先,不動産,水族館,フェス,山頂,クラブetx...)

世の中にあったコンテンツを提供できるように、先回りして技術を知っておく事によって、タイミング良く出せると思います。


今年は研究も兼ねて360×◯◯のようなコンテンツに挑戦します。

_以上


2017年1月24日火曜日

macOS SierraにDocker for Macをインストールしてみる

こんにちは、h_ono_222です。
今回はmacOS SierraにDocker for Macをインストールしてみました。

要求スペック

・2010年以降に製造されたMac
・OS X El Capitan 10.11以降のOS(10.10.3 Yosemiteは限定的にサポート)
・4GB以上のRAM
・version 4.3.30より前のVirtualBoxがインストールされていないこと

1. Docker for Macのインストール

下記のサイトにアクセスし、Get Docker for Mac[stable]をクリックしてDocker.dmgをダウンロードします。
https://docs.docker.com/docker-for-mac/

Docker.dmgをダブルクリックし、表示されたFinderのDockerアイコンをApplicationsにドラッグ&ドロップします。

2. Dockerを起動する

Dockerをダブルクリックするとポップアップが表示されるのでOKを選択し、自分のMacにログインする際のパスワードを入力します。

Dockerが起動し、メニューバーにDockerのアイコンが表示されます。

Terminalを起動し、Docker commandを入力してみます。
Users-MacBook-Pro:Desktop User$ docker version
Client:
 Version:      1.13.0
 API version:  1.25
 Go version:   go1.7.3
 Git commit:   49bf474
 Built:        Wed Jan 18 16:20:26 2017
 OS/Arch:      darwin/amd64

Server:
 Version:      1.13.0
 API version:  1.25 (minimum version 1.12)
 Go version:   go1.7.3
 Git commit:   49bf474
 Built:        Wed Jan 18 16:20:26 2017
 OS/Arch:      linux/amd64
 Experimental: true

3. サーバを起動する

Users-MacBook-Pro:Desktop user$ docker run -d -p 80:80 --name webserver nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
5040bd298390: Pull complete 
d7a91cdb22f0: Pull complete 
9cac4850e5df: Pull complete 
Digest: sha256:33ff28a2763feccc1e1071a97960b7fef714d6e17e2d0ff573b74825d0049303
Status: Downloaded newer image for nginx:latest
61a7880cc6d001376f222cb3214c457c3d625720a5111a72dba14415a0d2d40f

無事にnginxが起動しました。

今回は以上です。

2017年1月23日月曜日

配牌からあがれる可能性を予測する!(その3 学習の実装)



前回の訂正


CSVファイルからの読み込み時、String型になってしまうので、Int型にキャストしなければなりませんでした。
差分はこちらになりますが、その中でリスト内包記述というfor文を1行で書くように修正しております。
# 修正前
tmp_hand = []
for tile in range(0, 12):
tmp_hand.append(row[tile])

# 修正後
tmp_hand = [int(tile) for tile in row]

学習の実装開始


今回より学習機能をつけていきたいと思います。
基本的には、「ゼロから作るディープラーニング」の手順で論理を進めています。
(誤差を求め、その誤差の微分値からパラメータを求める。)
ソースコードはこちらになります。
https://github.com/naoki85/python_mahjong

matplotlib について


その前に、グラフを描画する便利ライブラリであるmatplotlibの準備をしておきます。
まだそこまで複雑なグラフはいらないので、基本的な書き方で大丈夫そうです。
今回はX軸はループの回数、Yは誤差(教師データとの誤差とします。)
# xの値を1ずつ定義
x = range(0, 100)
# ループごとに誤差を格納しておく
y = loss_array
plt.xlabel("x")
plt.ylabel("loss")
plt.plot(x, y)
plt.show()
ためしに作ってみた図はこちらです。


ループ回数を重ねるごとに、誤差がちいさくなっていることが分かります。
ただし、こちらの正当性はまだ確認できていないので、実際に次回は学習後の重み、バイアスを使用してテストしてみます。

学習結果の保存について(pickleモジュール)


学習結果はpickle形式で保存しました。
pickleは「漬物」という意味で、オブジェクトをそのまま保存してくれます。(ピクルスのことか、と覚えました。)
はじめはCSV形式にしようと思ったのですが、いちいち分解して保存するのも、取り出すときに再度辞書型にするのも面倒です。
例えば、下記の結果を保存するとします。
{'W': [1, 2, 3], 'b': [4, 5]}
これを特に前処理なしで下記のように保存(漬物)にしてしまいます。(基本的なファイルの読み書きと同じ感じです。)
with open(pickle_filepath, 'wb') as f:
    writer = pickle.dump(results, f)
取り出すときも下記のようにかけば、なんとびっくりそのまま取り出せます。
with open(pickle_filepath, 'rb') as f:
    params = pickle.load(f)

{'W': [1, 2, 3], 'b': [4, 5]}
そのため、重み、バイアスの値は教師データとは異なりpickleで管理することとしました。

おわりに


まだ私自身、微分して最適な重みを求める「勾配法」について、 腑に落ちていないところがあるので、
そちらを理解して、実装を踏まえて書きたいと思います。

2017年1月21日土曜日

配牌からあがれる可能性を予測する!(その2 教師データの作成)

こんにちは、Taroです。
前回の記事の記事の続きになります。
配牌からあがれる可能性を予測する!(その1 推論処理)
次回は学習を実装する、と記載しましたが、その前に教師データを取得する方法を実装したいと思います。
今回はDeep LearningというよりはPythonの勉強といった感じです。

教師データの選定


麻雀のようなギャンブルにおいて、教師になるデータは何なのか?
考え出すとキリがなさそうなので、ひとまずプロの対局からデータを作成しました。
プロであれば、(もちろん技術や個性、状況で異なってくるとは思いますが)あがりやすい配牌は必ず仕上げてくれるはずです。
そのため、Youtubeでプロの対局を見続けました(ある種これが苦行かもしれません笑)。
余談ですが、個人的に好きなプロ雀士は土田浩翔プロです。

CSVファイルの作成


教師データはCSVファイルにまとめます。
今回必要なデータは、 配牌結果 なので、下記のようにまとめました。
tile_1 tile_2 tile_3 tile_4 tile_5 tile_6 tile_7 tile_8 tile_9 tile_10 tile_11 tile_12 tile_13 win loss
11 13 17 21 25 27 29 31 31 31 35 43 47 1 0
このデータを読み込んでいきたいと思います。

CSVファイルの読み込み


Pythonのcsvモジュールを利用して簡単に実装します。
(というよりこれくらいしか分からない。。。)
   def load_trainig_data(self):
        u"""
        CSVファイルから教師データを読み込み、データを返します。
        @return array
        [0]で教師データの配牌、[1]で結果を返します
        """
        with open('csv/training_data.csv', 'r', newline='') as csvfile:
            reader = csv.reader(csvfile)
            header = next(reader)
            hand = []
            results = []
            for row in reader:
                tmp_hand = []
                for tile in range(0, 12):
                    tmp_hand.append(row[tile])

                results.append([row[13], row[14]])
                hand.append(tmp_hand)

        return hand, results
このメソッドをMyHandクラスに追加しました。
from my_hand import *
my_hand = MyHand()
input_data, results = my_hand.load_trainig_data()
print(input_data)
[['47', '26', '17', '21', '47', '12', '42', '43', '34', '15', '45', '37'], ['42', '42', '41', '17', '39', '21', '39', '15', '37', '11', '11', '21'], ['47', '35', '22', '22', '23', '26', '12', '13', '17', '18', '39', '38'], ['22', '24', '24', '25', '28', '31', '32', '33', '13', '13', '14', '16'], ['41', '47', '31', '12', '29', '18', '45', '34', '19', '22', '18', '39'], ['46', '15', '15', '16', '16', '47', '45', '11', '43', '32', '34', '27'], ['41', '41', '43', '44', '44', '17', '14', '13', '13', '23', '23', '33'], ['35', '38', '39', '39', '23', '25', '26', '27', '28', '16', '42', '42'], ['45', '47', '39', '44', '43', '21', '36', '27', '13', '32', '39', '16'], ['21', '28', '32', '35', '21', '23', '35', '12', '18', '37', '45', '14'], ['12', '14', '15', '17', '41', '44', '34', '35', '22', '23', '23', '26'], ['24', '24', '29', '31', '32', '36', '37', '38', '39', '13', '17', '47']]
print(results)
[['0', '1'], ['1', '0'], ['0', '1'], ['0', '1'], ['0', '1'], ['0', '1'], ['1', '0'], ['0', '0'], ['1', '0'], ['0', '0'], ['0', '0'], ['0', '0']]
これだとちょっと分かりづらいので、1つ目の要素だけ抜き出します。
print(input_data[0])
['47', '26', '17', '21', '47', '12', '42', '43', '34', '15', '45', '37']
print(results)
['0', '1']
これで、教師データが読み込めました。
(これで正しいのかは置いておきます笑)

Jupyter Notebookで書いてみたよ!


Jupyterという単語は見たことがあったのですが、ずっとライブラリの1つだと思っていました笑。
実はエディタのようで、データサイエンティストの人などが、実際に実行したプログラムを確認しながら文章を書いたりするもののようです。
Anacondaをインストールすると一緒についてきます。
詳しいことは諸先輩方が記述しておりますので、割愛しますが、本記事も下図のように書きました。
プロジェクト上に配置しておけば、自作のコードも読み込んで実行してくれます。
Python以外の言語もサポートしているようなので、コードと実行結果をブログにのせたいときは使用を検討しても良いのではないでしょうか?

2017.01.21 追記


何も考えずに、Jupyterが生成してくれたソースコードを貼り付けたら、Google bloggerのCSSなどを上書きしてしまったようで、不都合が生じました。
急いで書き直したので、結局普通になってしまいました。。。笑
ブログなどに貼り付けるときは事前に検討が必要です。

おわりに


今回のソースコードは下記になります。
Jupyter Notebookで作成したファイルは.ipynb拡張子で保存されています。
https://github.com/naoki85/python_mahjong
次回は、今度こそ学習ロジックの実装をしたいと思います!

2017年1月20日金曜日

Swift3でTTTAttributedLabelの文字列からリンク表示をやってみた

こんにちはonukiです。

今回はTTTAttributedLabelを使用し、
文字列からリンクを表示、リンクタップ制御を
Swiftでの実装方法を書いてこうと思います。

TTTAttributedLabelとは

リッチなテキストを表示するためのUILabelの機能を拡張するOSSです。
その機能の一つに文字列からリンクを検出してくれる機能があるので
今回は主にその部分の実装についてになります。

準備

インストールにはCocoaPodsを使用します。
pod 'TTTAttributedLabel'
それと、TTTAttributedLabelはObjective-Cで書かれているので
Bridging-Header.hにimportしてください。
#import <TTTAttributedLabel/TTTAttributedLabel.h>

実装

まずはTTTAttributedLabelをimportします
import TTTAttributedLabel
StoryboardにUILabelを適当に用意し
ClassにTTTAttributedLabelを指定してください。
それをIBOutlet接続します。
@IBOutlet weak var linkLabel: TTTAttributedLabel!
テキストのリンクをタップできるように設定します。
linkLabel.enabledTextCheckingTypes = NSTextCheckingResult.CheckingType.link.rawValue
リンクを含むテキストをセットします。
linkLabel.text = "URLにタップできます。\nhttps://www.google.co.jp/"
そうするとこのような表示になります。

このままだとタップが検知できないので
TTTAttributedLabelDelegateを設定します。
class ViewController: UIViewController, TTTAttributedLabelDelegate {
TTTAttributedLabelのDelegateを設定します。
linkLabel.delegate = self
テキスト内のリンクがタップされた場合、以下のメソッドが呼ばれます。
func attributedLabel(_ label: TTTAttributedLabel!, didSelectLinkWith url: URL!) {
    //処理
}
これで、文字列からリンクを表示、リンクタップ制御ができたと思います。

2017年1月19日木曜日

はじめの一歩 -Rails ActiveRecord編- SELECT3


どうも、はじめです。
前回はActiveRecordのSELECTについて書いてみました。
はじめの一歩 -Rails ActiveRecord編- SELECT2
今回はActiveRecordのデータの取得(SELECT)の中でもJOIN系に関して書いていこうと思います。


はじめに


booksテーブルに以下のデータが登録済みであることを前提とします。
[id: 1, title: ‘Rubyの本’, publisher_id: 1],
[id: 2, title: ‘Railsの本’, publisher_id: 1],
[Id: 3, title: ‘PHPの本’, publisher_id: 2]

今回はJoin系のメソッドについて書いていくので、
booksに対し多対1で紐づくpublishersテーブルを以下のように用意します。
[id: 1, publisher_name: '出版社1'],
[id: 2, publisher_name: '出版社2']


joins


joinsはinner joinでの結合を行います。
Books.joins(:publisher)
# SELECT `books`.* FROM `books` INNER JOIN `publishers` ON `publishers`.`id` = `books`.`publisher_id`

出版社名を表示したい場合は以下のように表示をすることができます。
例としてbooksテーブルのID:1のレコードで取得してみます。
book = Book.joins(:publisher).find(1)
p book.publisher.publisher_name
# '出版社1'

※joinsを使用するとreadonlyとなるため更新ができないと
いろいろなところで目にしましたが、手元で確認をしたところreadonlyはfalseとなっていました。
ちなみにRailsのバージョンは5.0.0.1です。
バージョンによるものか環境によるものかはわかりませんが、
実際に使用する場合は一度確認をしてみるべきだと思います。

joinsを使用した状態でwhereを使用したい場合は以下のように書くことができます。
# Bookの情報で検索を行いたい場合
Book.joins(:publisher).where(title: ‘Railsの本’)

# Publisherの情報で検索を行いたい場合(以下の二つは同じ内容が実行されます)
Book.joins(:publisher).where(publishers: {publisher_name: '出版社1'})
Book.joins(:publisher).where("publishers.publisher_name = '出版社1'")
mergeを使用するとActiveRecord::Relationを使用して検索することもできます。
Book.joins(:publisher).merge(Publisher.where(publisher_name: '出版社1'))
※後で記述するincludesでは使用できませんでした。
使用できない理由はこの後書かせていただきます。


includes


includesは結合先のテーブルと結合元のテーブルに対し
別々にクエリを実行してデータを取得します。
Book.includes(:publisher)
# SELECT `books`.* FROM `books`
# SELECT `publishers`.* FROM `publishers` WHERE `publishers`.`id` IN (1, 2)

表示の仕方はjoinsと同じです。
book = Book.joins(:publisher).find(1)
p book.publisher.publisher_name
# '出版社1'

それぞれに対して別々にクエリを実行していることから、
指定したデータがBookに存在していない場合はPublisherに対するクエリは実行されません。
Book.joins(:publisher).find(4)
# SELECT `books`.* FROM `books` WHEHE `books`.`id` = 4

includesを使用した状態でwhereを使用したい場合は、
どのカラムで検索するかによって実行されるSQLの内容が変わってくるので注意が必要です。


実際に違いを見てみます。
検索条件のパターンとして以下の2つをあげます。
1.Book内の情報で検索をする
2.Publish内の情報で検索をする
実際に試してみます。

1.Book内の情報で検索をする
Book.includes(:publisher)where(title: ‘Railsの本’)
# SELECT `books`.* FROM `books` WHERE `books`.`title` = "Railsの本"
# SELECT `publishers`.* FROM `publishers` WHERE `publishers`.`id` = 1
こちらは条件指定をしない場合と変わりません。

2.Publish内の情報で検索をする
Book.includes(:publisher)where(publishers: {publisher_name: '出版社1'})
# SELECT `books`.`id, `books`.`title`, `books`.`publisher_id`, `publishers`.`id`, `publishers`.`publisher_name`
# FROM `books` LEFT OUTER JOIN `publishers` ON ``publishers`.`id` = `books`.`publisher_id`
# WHERE `publishers`.`publisher_name` = "出版社1"
このように結合先のテーブル情報で検索を行うと"LEFT OUTER JOIN"を使った結合が行われます。

includesでmergeを使用した場合以下のようなSQLが実行されます。
Book.includes(:publisher).merge(Publisher.where(publisher_name: '出版社1'))
# SELECT `books`.* FROM `books` WHERE `publishers`.`publisher_name` = "出版社1"
クエリが各テーブル毎に発行されているためこのようなクエリが生成されるのだと思います。


最後に


今回のJOIN系に関して個人的に大変だと思ったのはテーブル名の記述です。
joinsやincludesで指定するテーブル名ですが、
自分から見て結合するテーブルがどのような関連性を持っているかによって
記述するテーブル名が変わってきます。

・自分から見て結合先のテーブルが一つしか存在しない場合(Bookから見たPublishのような関係)
単数系のテーブル名で指定する。
・自分から見て結合するテーブルのデータが複数存在する場合(Publishから見たBookのような関係)
複数形のテーブル名で指定する。

また、結合先のテーブル情報でwhereを使用する場合は
複数形のテーブル名で指定をします。

今回書ききれなかったこともあるので、次回もJOIN系の続きを書きます。

2017年1月18日水曜日

ダイナミックアイデンティティ


こんちはーーー!デザイナーのnottyです。普段はWeb,アプリのUIなどをデザインしているのですが、
今日はブランディングについて、書きたいと思います。
今日ご紹介するのは

ダイナミックアイデンティティ



ここ最近、特に注目&導入されているロゴの概念です。
昔は、ロゴマークは競合他社の違いや企業の理念や思いを示す静的(変わらない)なロゴマークが多かったですが、
ここ最近は動的なロゴ、制約の中でコンテンツ,環境や時代に合わせて変化するロゴ
「ダイナミックアイデンティティ(以下:DI)」という手法が生まれています。

箱型(テンプレート)のロゴに中身はコンテンツによってデザインを変えたり、
あるいはロゴタイプは固定で、周りのアピアランスはプログラムによって自動生成して、
その時だけのロゴを作成したり、すでに身の回りにDIが登場してます。

DIは今後、浸透するのか?

企業や組織、プロダクトが成長していく中で、時代に合わせて変化していく(有機体)であった方が、
企業,時代,環境などの変化を受け入れることが可能なダイナミック・アイデンティティが必要だと感じます。
徐々には浸透してきていますが、この概念が浸透するのはまだまだ時間がかかりそうな印象を持ちます。。。

が、流行りや技術,サービスの移り変わりが激しい業界や人工知能が発達して、
ロゴの自動生成の精度があがると、一気に浸透するのでは?と思います。
ただ、信用が大事な権威のある会社や、商標を取得する場合などまだまだ障壁があると思います。
DIが全て良いわけではないので、コンテンツによって出し分けれると良いですね。


ダイナミックな挑戦

デザイナーも、DIを伝えるのは非常に難しいと感じてます。
遊び心で作ったのでは?とかロゴとして機能してないなど、厳しい意見もありますが、
DIという概念もきちんと伝えるのもデザイナーのミッションなので、今後意識して挑戦したいと思います。
最後はつぶやきみたいになってしまったが、新しい概念に挑戦して、
時代合わせて成長し続けるのが強いデザイナーだと思います。

なので、ガンバローーーーーーー!!!!!


2017年1月17日火曜日

サイバーセキュリティ入門 後編


こんにちは、h_ono_222です。
今回のドトール会のテーマは「サイバーセキュリティ入門: 私たちを取り巻く光と闇」の「4. インターネットにおけるサイバー攻撃」についてです。

4. インターネットにおけるサイバー攻撃

ここでは、有名な攻撃手法とその手口について書かれています。
具体的には、
  • DoS(DDoS)攻撃
  • 標的型攻撃
  • クロスサイトスクリプティング(XSS)
  • SQLインジェクション
  • DNSキャッシュポイズニング
  • クロスサイトリクエストフォージェリ(CSRF)
など(個人的な観点に基づいて抜粋しました)です。
上記の攻撃手法について簡単に説明します。

(1) DoS(DDoS)攻撃
DOSはDenial of Serviceの略語です。
攻撃目標(Webサイトなど)に対してリクエストを大量に送りつけ、サーバのメモリやCPUを浪費させて処理能力を低下させたり、
ネットワークを過負荷にすることにより攻撃目標のサービスを妨害します。

DDoSはDistributed DoSの略語で、分散DoSとも呼ばれます。
ボットネットワーク(ボットに感染した複数のホストにより構成されたネットワーク)から攻撃目標に対して一斉にリクエストを送りつける攻撃です。


(2)標的型攻撃
攻撃目標をある程度絞って行う攻撃で、一般的にはマルウェアやSPAMなどの迷惑メールを介して行われます。
標的型攻撃でのメールは攻撃目標が関心をもっている、あるいは、関係あると思わせるような内容が記載されていることが多々あります。
しかし、標的型攻撃では攻撃者がある程度攻撃対象の情報を持っていなければならず、送られたメールから攻撃者を特定できる場合もあるため、近年では減少傾向にあります。


(3)クロスサイトスクリプティング(XSS)
ウェブサイトに対する攻撃手法の代表とも言われるほどメジャーな攻撃方法です。
攻撃者は文字列を入力させるフォーム(例えばコメント欄など)に対してJavaScriptコードを入力します。
これにより、サニタイジング(入力された文字列をエスケープし無害な文字列に変換すること)がしっかりされていないサイトでは、任意のJavaScriptコードを実行できてしまいます。


(4)SQLインジェクション
フォームに対して不正な文字列を入力することで、攻撃対象のデータベースを制御する攻撃です。
例えば、ログインフォームに下記のような入力を行います。
user_name = hogehoge
password  = 'OR'A' = 'A
これにより発行されるSQLは下記のようになります。
SELECT * FROM users WHERE user_name = hogehoge AND password = ''OR 'A' = 'A'
ORの後ろの'A' = 'A'は真となるためログインに成功してしまいます。


(6)DNSキャッシュポイゾニング
DNSは大本であるルートサーバ、対象ドメインを管理する権威DNSサーバ(プライマリサーバ)、
キャッシュサーバ(セカンダリサーバ)から構成されています。

ユーザはまずキャッシュサーバに問い合わせを行います。
問い合わせたドメインがキャッシュにない場合、キャッシュサーバはルートサーバに問い合わせを行い、その後、委任されたDNSサーバを辿って最終的な権威DNSサーバに問い合わせを行います。

攻撃者はキャッシュサーバが問い合わせを行っている間に、キャッシュサーバに対して偽のDNSレスポンスを連続的に送ります。
キャッシュサーバは送られてきた偽のレスポンスを正規のレスポンスとして受け入れます。
その結果、そのドメインにアクセスしたユーザは偽のアドレスに誘導されてしまいます。


(7)クロスサイトリクエスをフォージェリ(CSRF)
攻撃者はまず、脆弱性を持つウェブサイトを見つけ出し、不正なプログラムを仕込むなどの改ざんを行います。
次に、匿名掲示板やメールなどを通じて改ざんしたサイトに誘導します。
改ざんしたサイトにアクセスが有ると、仕込んだ不正なプログラムをユーザ(ブラウザなど)に送り込み支配します。
攻撃者の命令がインターネットを通じて発行されると、支配したブラウザなどを通し別のサイトなどへ不正なリクエストを送ります。

本来被害者であるはずのユーザが加害者になってしまうところがこの攻撃の脅威です。


本日は以上です。

2017年1月16日月曜日

配牌からあがれる可能性を予測する!(その1 推論処理)


2017.01.21 更新
その2を公開しました!
https://nowdeveloping.blogspot.jp/2017/01/blog-post_21.html

こんにちは、Taroです。
日増しに寒くなってきましたね。
今年もセンター試験は大寒波とかニュースでやっていましたが、毎年そうなのでタイミングが悪いんですかね?

ゼロから作るDeep Learningを読んでいます


最近、趣味かつ独学ではありますが、Deep Learningの勉強をしています。
ゼロから作るDeep Learning」は初心者でも分かりやすい本だと思います。
(知識がない私でも頑張ってなんとかついていっています笑)

しかし、(私自身に知識がないためだとは思いますが)いまいちピンときません。
ピンとこないというのは、意味が分からないということではなく、「実際にこれをどうやって使うんだろう」というところです。

あれこれ悩んだ結果、簡単なネットワークを自分で作ってみようと思いつきました。
せっかくなら自分の興味のある麻雀をベースに、
配牌を入力して、あがれるかあがれないか予測する
モデルを作ってみたいと思います。

ソースコードも公開していきますが、Deep LearningどころかPythonも初心者なので、ぜひご指導をお願いしますm(_ _)m
麻雀は知らないけど、Deep Learningとか興味ある、という方でも読んでいただけるようにしていきます。

とりあえず設計


配牌(はじめに配られる手札のようなもの)から、アガリの確率を求めるために必要なファクターは何か、真剣に考えてみました(それはもう、プログラムを書いているとき以上に)。
また、あまりに複雑すぎるファクターだと私が実装できない可能性があり、かつ本筋を見失いかねないので、以下の3つの要素としました。
  1. 配牌時に存在する順子の数
  2. 配牌時に存在する対子の数
  3. 配牌時に存在する暗刻の数
麻雀が分からない方は、トランプで7、8、9と階段で持っているか、7、7、7と同じ数字を複数枚持っているか、どちらがアガリやすいかという風に考えてください。

これら3つの要素を算出し、それぞれに対して重み(W)を掛けて、最終的な総和からアガれるかアガれないか予測します。
ちょっと私の説明が下手なきらいもあるので、図にしたいと思います。



データの扱い

例えば、下記のような配牌があります(一番右の牌は無視してください笑)。
このときの入力値はリスト型で、
input_data = [14, 15, 19, 23, 25, 25, 32, 37, 39, 39, 43, 43, 47]
と渡します。
順子の数は0、対子の数が3つ、暗刻の数が0です。
そのため、
X1 = 0
X2 = 3
X3 = 0
とします。
ここで、それぞれに対して重みWを掛けます。
重みは、それぞれのファクターがどれだけ寄与しているか示す値です。
この重みを今後学習させて、最適な値を探していきます。
今回は適当な値を渡しておきます。

そして、最後にそれらを足し合わせて結論を導きます。
結論を導く際に、バイアスで閾値を設定しますが、こちらも今後の学習で値を決めていくので、今回は適当な値を入れます。
最終的には下記のように出力させたいと思います。
result = [0.50407631, 0.49592369]
第一要素がアガれる確率、第二要素がアガれない確率と考えます。
今回は適当な値を入れたので、フィフティフィフティみたいな中途半端なことになっておりますが、今後の学習で立派な予想屋になってもらいます!

次回は、学習を実装していきたいと思います。
できれば完成までお付き合いください!

最後に、ソースコードはこちらになりますので、よろしければご覧ください。
https://github.com/naoki85/python_mahjong
2017.01.21 更新
その2を公開しました!
https://nowdeveloping.blogspot.jp/2017/01/blog-post_21.html

2017年1月14日土曜日

Rails5 alias_method_chain is deprecated.

 こんにちは、Hiroです。
 直近のプロジェクトでは、Rails5を使っているのですが、利用しているgem関連で下記のような警告が大量に出力されるので、少し調べてみました。
(「./bin/rails c」や「./bin/bundle exec rspec」を実行すると大量に出力されます。)
DEPRECATION WARNING: alias_method_chain is deprecated.
これは、Rails5からはActiveSupportの「alias_method_chain」がdeprecatedになり、代わりに「Module#prepend」を使うように促しています。


alias_method_chainとは


まず、deprecatedになった「alias_method_chain」ですが、下記のように利用します。
class HelloUser
  def hello
    puts 'Hello'
  end

  def hello_with_world
    hello_without_world
    puts 'World'
  end
  alias_method_chain :hello, :world
end
上記のコードは、「alias_method」を使った場合は、下記と同等になります。
class HelloUser
  def hello
    puts 'Hello'
  end

  def hello_with_world
    hello_without_world
    puts 'World'
  end
  alias_method :hello_without_world, :hello
  alias_method :hello, :hello_with_world
end
helloメソッドがhello_with_worldメソッドに置き換わり、元々のhelloメソッドはhello_without_worldメソッドとして使えるようになります。
実行結果を下記に記載しますが、置き換わっているのがわかると思います。
[1] pry(main)> hello_user = HelloUser.new
[2] pry(main)> hello_user.hello
Hello
World
[3] pry(main)> hello_user.hello_without_world
Hello


Module#prependとは


早速、「Module#prepend」の使い方ですが下記のように使います。
class HelloUser
  def hello
    puts 'Hello'
  end
end

module HelloWithWorld
  def hello
    super
    puts 'World'
  end
end
HelloUser.prepend(HelloWithWorld)
prependされたHelloWithWorldモジュールは継承関係上で、HelloUserクラスより手前に位置することになり、helloメソッドを オーバーライドしたような形になります。
そのため、HelloWithWorldモジュールのメソッド内でsuperを呼び出すことで元々のメソッドを呼び出すことができます。
実行結果とancestorsメソッドで継承関係を表示しておきます。HelloWithWorldがHelloUserの前にあるのがわかると思います。
[1] pry(main)> hello_user = HelloUser.new
[2] pry(main)> hello_user.hello
Hello
World
[3] pry(main)> HelloUser.ancestors
=> [HelloWithWorld,
 HelloUser,
 ActiveSupport::ToJsonWithActiveSupportEncoder,
 Object,
 PP::ObjectMixin,
 RequireAll,
 ActiveSupport::Dependencies::Loadable,
 JSON::Ext::Generator::GeneratorMethods::Object,
 ActiveSupport::Tryable,
 Kernel,
 BasicObject]
モンキーパッチなどを書くときに利用することになると思います。(モンキーパッチなんて書かずに、githubにプルリクする方がよいですが)


とりあえず、DEPRECATION WARNINGの出力をなくしたい


Rails5にしたものの、全てのgemに対して、この警告を消すための書き換えの対応をしていくのは大変。
ただ、この出力が目障りという方は、「config/application.rb」に下記を書けば、消すことができますが、自己責任でお願いしますね。
require_relative 'boot'

require 'rails/all'

# TODO: DEPRECATION WARNING: alias_method_chain is deprecated
ActiveSupport::Deprecation.silenced = true

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
...
...


deprecatedなメソッドは、今後のアップデートで利用できなくなることもありますので、しっかりと理解した上で利用し、可能であれば、置き換えていくようにしましょう。

2017年1月13日金曜日

Swift3でMapKitの複数のピンを一括で表示・削除する方法

こんにちは、onukiです。

今回はMapKitでピンを一括で表示・削除する方法について、
例えばカテゴリーごとにピンを表示したい、
カテゴリー切り替え時にピンをごっそり入れ替えたいみたいな使い方がしたかったのですが
以外と調べても出てこなかったので備忘録がてら書いておきます。

やり方

大体、ピンの表示方法を調べると以下のような書き方が出てきます。
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(37.331652997806785, -122.03072304117417)
self.mapView.addAnnotation(annotation)
これだと入れ替えが面倒なので今回は以下のようにします。
まず、グローバル変数にMKAnnotationの配列を用意します。
var annotationArray: [MKAnnotation] = []
マップにMKAnnotationを追加します。
for coordinate in coordinateArray! {
    let annotation = MKPointAnnotation()
    annotation.coordinate = CLLocationCoordinate2DMake(coordinate.latitude, coordinate.longitude)
    annotationArray.append(annotation)
}
self.mapView.addAnnotations(annotationArray)
設定したピンを削除したい時は、こうすると先ほど設定したピンを全削除できます。
self.mapView.removeAnnotations(annotationArray)

これで複数のMKAnnotation配列を用意してやり、
用途に応じてremoveとaddをしてやれば
複数のピンの入れ替えが出来るようになります。

2017年1月11日水曜日

Google Developers LaunchPad Build Tokyo 2016に参加してきました。

昔のイベントレポートだが、メモに貼り付けているだけだと忘れるので備忘録的にレポートを書きます。
2016.10.7に私は
Google Developers LaunchPad Build Tokyo 2016に参加してきました。

イベント内容

Google Developers LaunchPad Build は、アプリデベロッパー・デザイナーの方に向けたイベントです。今回のイベントでは、Material Design を中心としたデザインが、アプリやサービスにおいていかに重要になるかについてのセッションを行います。
GoogleがMaterial Designについて、他社の活用事例や守るべきルールなどを発表するイベントです。

プログラム一部(私が特に良かったと感じたセッション)

  • アプリデザインで守るべき 25 のルール
  • Material Design Fireside Chat(マテリアルデザインを導入している企業のセッション) マネーフォワード,Fril,C CHANNEL,Famm等の聞いたことのある企業の方がセッションに参加していました。
当日のタイムテーブル

アプリデザインで守るべき 25 のルール

アプリ内のコンバージョンを増やすため、そのためのユーザー体験をどう改善していくかなどの守るべきルールを紹介するプログラムです。
25個すべて書くのは、ボリューミーになってしますので、個人的にピックアップ(驚きや新たな発見)したのをご紹介します。
  • 日本のユーザー半数近く、ストアのレビューを参考にしてインストール。
  • 平均36個のアプリが入っていて、1ヶ月に2個インストールし、1個アンインストールしている。
  • アプリが高評価であるほど高収益 – 星2→3になると9倍 星3→4になると4倍
  • 迷子にならないナビゲーション
    • インストールして何をユーザーが期待するかを考え、機能を適切に見せてあげることが大事。
    • アプリを立ちげ5分以内に「いいな!」と思わなければユーザーは立ち去る
  • 丁寧な操作説明
    まれにユーザーによって意味を間違える。「クレジットカードをスキャンしてください」というアナウンスに対して、カメラ撮影してほしいところ、端末の上にクレジットカードを置いて待機していた(スキャンできると勘違い)人が103人中3人。
  • 納得感のあるユーザビリティ
    • アイコンだけのボタンは良くない。テキストラベルを補充しよう。
    • 「☆」アイコンも人によって「お気に入り」「ランキング」などなど全ての人が同じ解釈をするとは限らないから説明大事。
ユーザーの視点に関して、細かく説明されているので非常に参考になりました。ユーザー目線と思って作ったモノが実際にはユーザーにとって障壁が高かったり、文化の違い等で思ったように解釈されてない部分にハッとさせられました。
全てご紹介できなかったですが動画で見れます。
Material Design Fireside Chat
マテリアルデザインを導入している企業のセッションです。導入した経緯、導入してどうなったかなど、普段聞けない話が聞けました。
共通していた話題をピックアップして紹介します。

マテリアルデザイン導入のきっかけ

  • 知ってて当たり前だったので自然と導入
  • リニューアルのタイミングで導入。ちょうど発表されたところだった。
存在は知っていて、タイミングが来た時に導入したという印象。
エンジニアもデザイナーもマテリアルデザインについて話し合う文化?自然と話題にでて、導入に至ったそうです。主観ですがトレンドを追ったり、意識の高いメンバーがいると自然と導入する流れがあるのかな?と思いました。

マテリアルデザイン導入してどうなったか?
  • 保守が圧倒的に楽になった
  • 課金率が上がった
保守(改修)のタスクが楽になって、工数が削減できたと声が上がっていました。
ある程度要素が決まっているページに関しては、デザイナーがカンプを起こさないで、エンジニアだけで完結できるようになったそうです。
他にも、同じユーザー体験を提供できタブレットや様々な端末に対応できて課金率が上がったそうです。
マテリアルデザインによって、開発とユーザーにもメリットをもたらしてくれたという話が聞けました。

まとめ

マテリアルデザインのメリットや守るべきルール、他にもテクニカルの部分もありましたが、今回のイベントを通じて、マテリアルデザインに関する知見が深まりました。信頼できる数値や導入してよかったメリットなども聞けたので、これを元にマテリアルデザインに挑戦(導入)してみては良いと思います。
こういったデザインの考え方やメリットなどは開発陣が積極的に声をあげて広めていくのが大事だと思いました。
ぼやき
iOSとAndroidを同じデザインでやるのは、短期的にはデザイナーのみの工数は削減できるが、中・長期的にみたときに、保守改修や機能追加などする際には、工数が膨れ上がってしまいます。
プラットフォームにあったデザインをキチン提供できるといいな。
どこにいっても通用するデザイナーなれるように頑張ろう!!

2017年1月10日火曜日

はじめの一歩 -Rails ActiveRecord編- SELECT2


どうも、はじめです。

前回はActiveRecordのSELECTについて書いてみました。
はじめの一歩 -Rails ActiveRecord編- SELECT
今回も引き続きActiveRecordのデータの取得(SELECT)に関して書いていこうと思います。


はじめに、


前回と同様に以下のデータが登録済みであることを前提とします。
[id: 1, name: ‘名前1’, mail: ‘111@xxx.xxx’],
[id: 2, name: ‘名前2’, mail: ‘222@xxx.xxx’],
[Id: 3, name: ‘名前3’, mail: ‘333@xxx.xxx’]

前回記述したallやwhereを使用してデータの取得をした場合、
ActiveRecord::Relationというクラスのオブジェクトが返却されます。
取得したレコードが1件だけであったとしても使用する際は以下のように記述する必要があります。
user = User.where(id: 1)
p user[0][:name]
# => '名前1'
上記のように1件のみの取得と確定している場合でも[0]をつけなくてはなりません。
今回は1件のみを取得する方法を書いていきたいと思います。


find


findはIDの直指定でレコードを取得することができます。
実行文と実行結果は以下のようになります。
user = User.find(2)
p user.mail
# => ‘222@xxx.xxx’
findでもし存在しないIDを指定した場合は例外が発生します。

ちなみにIDの複数指定も可能です。
複数のIDを指定する際は以下のように配列で複数のIDを指定します。
user = User.find([2,3])
p user[0][:name]
# => '名前2'
p user[1][:name]
# => '名前3'
こちらは以下のようにwhereで取得した場合と同じ結果になります。
user = User.where(id: [2,3])


find_by


findの場合はIDしか指定できませんでしたが、
他のカラムでの検索で1件のみ取得したい場合はfind_byを使用します。
user = User.find_by(name: '名前1', mail: ‘111@xxx.xxx’)
p user.id
# => 1
find_byでの検索はSQLの末尾に[limit 1]を追加して検索をしてくれるので、
検索結果が複数あった場合でもヒットした中の一番最初のレコード1件のみを取得してくれます。
上記のように複数条件を指定することもできますが、すべてAND条件での検索となります。

find_byでIDを指定することもできますが、
findとの違いとしましては、検索結果が0件だった場合に例外ではなくnilを返します。


first


firstはを実行した場合は[order by `id` asc limit 1]が実行されます。
引数で数値を渡してlimitの数を指定することもできます。
user = User.first
user.id
# => 1
user = User.first(2)
user[1][:name]
# => '名前2'


最後に、


今後もしばらくデータの取得に関して書いていこうと思っております。
次回はjoin系を書いてみようと思っております。

carrierwave-awsを使ってS3に画像をアプロードする

こんにちは、h_ono_222です。
今回はcarrirewave-awsを使ってS3に画像をアップロードする方法を紹介します。

目次

  1. carrierwave-awsとは
  2. gemをインストールする
  3. initializerの設定

1. carrierwave-awsとは

carrierwave-awsはfogに似たgemで、Amazon S3へのアップロード機能を提供します。

carrierwaveでS3などのストレージ画像をアップロードするにはfogがよく使われると思いますが、
AWSへのアクセスをaws-sdkに統一するために、今回carrierwave-awsを利用しました。

GitHubのリポジトリはこちら

2. gemをインストールする

下記のgemをインストールします。
# Gemfile
gem 'carrierwave'
gem 'carrierwave-aws'

3. initializerの設定

carrierwaveの設定を行います。()
# Gemfile
CarrierWave.configure do |config|
  if Rails.env.production?
    config.storage    = :aws
    config.aws_bucket = 'data.hoge.upload'
    config.aws_acl    = 'public-read'

    # The maximum period for authenticated_urls is only 7 days.
    config.aws_authenticated_url_expiration = 60 * 60 * 24 * 7

    # Set custom options such agit s cache control to leverage browser caching
    config.aws_attributes = {
        expires: 1.week.from_now.httpdate,
        cache_control: 'max-age=604800'
    }

    # aws credential
    config.aws_credentials = {
        # 今回はIAM ロールを使用するため記載しない
        # access_key_id:     ENV.fetch('AWS_ACCESS_KEY_ID'),
        # secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY'),
        region:            'ap-northeast-1' # Required
    }
  else
    # テスト時はローカルにファイルを保存する
    config.storage    = :file
  end
end
設定は以上です。
アップローダの方で特に意識することなく、S3へファイルをアップロードできました。

2017年1月9日月曜日

同じレコードがないときだけインサートする!

あるアイテムを持っていない人だけ、別のアイテムをあげたい!
もしくはその逆で、あるアイテムを持っている人に追加でアイテムをあげたい!

そういうことってないでしょうか?
先日、僕がそのような状況になり、四苦八苦しておりました。
本日は復習をかねて、調べた内容を記述していきたいと思います。

内容


本日は主に下記の基本的なところを書きたいと思います。
  • EXISTS / NOT EXISTS
  • INSERT ... SELECT...
  • 上記2つを合わせて

準備


まずはテスト用のテーブル、レコードを作成しておきます。
実はMySQLのリファレンスを読んだのですが、いまいちピンとこなかったので、実際に似たテーブルを作成して試してみました。
CREATE TABLE cities(id int(11) NOT NULL AUTO_INCREMENT, name varchar(32) NOT NULL DEFAULT '0', PRIMARY KEY (id)) DEFAULT CHARSET=utf8;
CREATE TABLE stores(id int(11) NOT NULL AUTO_INCREMENT, name varchar(32) NOT NULL DEFAULT '0', PRIMARY KEY (id)) DEFAULT CHARSET=utf8;
CREATE TABLE cities_stores(id int(11) NOT NULL AUTO_INCREMENT, city_id int(11) NOT NULL DEFAULT 0, store_id int(11) NOT NULL DEFAULT 0, PRIMARY KEY (id)) DEFAULT CHARSET=utf8;
INSERT INTO cities (id, name) VALUES (1, '渋谷'),(2, '新宿'),(3, '池袋');
INSERT INTO stores (id, name) VALUES (1, 'セブンイレブン'),(2, 'ファミリーマート'),(3, 'ローソン'),(4, 'ミニストップ'),(5, 'サークルK'), (6, 'スーパー');
INSERT INTO cities_stores (city_id, store_id) VALUES (1, 1), (1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 1), (3, 3), (3, 5);
cities(街)、stores(店)、cities_stores(街と店の紐付け)を作成し、それぞれの街にいくつかの店があるとします。
具体的には下記の組み合わせです。
渋谷 セブン、ファミマ、ローソン
新宿 ローソン、ミニストップ、サークルK
池袋 セブン、ローソン、サークルK
どこにもない スーパー

EXISTS / NOT EXISTS


まずは、EXIST句、NOT EXISTS句です。
リファレンスはこちらになります。
これは、その条件のものが存在していればTRUEを、存在していなければFALSEを返し、TRUEのものだけ取得してくれます。
(NOT EXIST句は逆です。)
下記に例を示しますが、storesの中で街にないものは「スーパー」だけです。
# 1. cities_storesに存在する場合
SELECT DISTINCT name FROM stores
  WHERE EXISTS (SELECT * FROM cities_stores WHERE cities_stores.store_id = stores.id);
セブン、ファミマ、ローソン、ミニストップ、サークルK
# 2. cities_storesに存在しない場合
SELECT DISTINCT name FROM stores
  WHERE NOT EXISTS (SELECT * FROM cities_stores WHERE cities_stores.store_id = stores.id);
スーパー

INSERT ... SELECT...


次に、INSERT ... SELECT...構文です。
リファレンスはこちらになります。
これは、SELECTで引っ張ってきた値を使用してINSERT文を作成します。
具体的には下記です。
良い例が思いつかなかったため、storesから全件取得し、それをcitiesに突っ込みます。
INSERT INTO cities (name) SELECT stores.name FROM stores;
ご想像の通り、これでまるっとcitiesのレコードが増えます。
SELECT * FROM cities;
id name
1 渋谷
2 新宿
3 池袋
4 セブンイレブン
5 ファミリーマート
6 ローソン
7 ミニストップ
8 サークルK
9 スーパー

同じレコードがないときだけインサートする!


ここから本題になります。
想定としては、ある街に全ての店が進出してきた!ということにします。(意味が分かりませんが笑)
当然、既に存在している店もあるので、その店はインサートしないようにしたいです。
NOT EXISTS句とINSERT ... SELECT ...構文を併用して下記のように書きます。
(例として、渋谷にセブンイレブンとファミリーマートがきたということにします。セブンは既に存在しています。)
# セブンの場合、もう渋谷にあるのでインサートされない
INSERT INTO cities_stores (city_id, store_id)
  SELECT target_city_id, target_store_id FROM dual
  WHERE NOT EXISTS(SELECT * FROM cities_stores WHERE city_id = 1 AND store_id = 1);
# ファミマの場合、まだ渋谷にないのでインサートされる
INSERT INTO cities_stores (city_id, store_id)
  SELECT target_city_id, target_store_id FROM dual
  WHERE NOT EXISTS(SELECT * FROM cities_stores WHERE city_id = 1 AND store_id = 2);
dualはテーブルを参照する必要のない場合に使用するダミーテーブルで、WHERE句を指定したい場合などに入れる必要があるそうです。
SELECT Syntax
私自身は特に省略しても違和感はありませんが、Oracle データベースなどを扱っていた方々は 記述するのが当たり前のようです。

とりあえずSQL文はできましたが、これだとレコード数が少ない場合は良いのですが、
レコード数が多くなると面倒なので、ストアドプロシージャを使用してループ文を作りたいと思います。
ストアドプロシージャは機会があればいろいろ試して、ブログにしたいと思いますが、 ←よく分かっていないだけ
参考資料を下記に載せておきます。
CREATE PROCEDURE and CREATE FUNCTION Syntax
はじめてのMySQL ストアドプロシージャ・ストアドファンクション
DELIMITER //
CREATE PROCEDURE RegisterAllStoresWithCity(IN target_city_id INT)
BEGIN
  DECLARE target_store_id INT;
  DECLARE max_id_stores INT;

  SET target_store_id = 1;
  SET max_id_stores = (SELECT MAX(id) FROM stores);

  WHILE target_store_id <= max_id_stores DO
    INSERT INTO cities_stores (city_id, store_id)
      SELECT target_city_id, target_store_id FROM dual
      WHERE NOT EXISTS(SELECT * FROM cities_stores WHERE city_id = target_city_id AND store_id = target_store_id);

    SET target_store_id = target_store_id + 1;
end WHILE; END // DELIMITER ;
これでRegisterAllStoresWithCity()が登録されたので、下記で呼び出し、インサートしてみます。
CALL RegisterAllStoresWithCity(1);
これで、渋谷に全ての店が出店できました!
SELECT cs.id, c.name, s.name FROM cities_stores cs INNER JOIN cities c ON cs.city_id = c.id INNER JOIN stores s ON cs.store_id = s.id;
id cities.name stores.name
1 渋谷 セブンイレブン
2 渋谷 ファミリーマート
3 渋谷 ローソン
25 渋谷 ミニストップ
26 渋谷 サークルK
27 渋谷 スーパー
余談ですが、登録したRegisterAllStoresWithCityをリセットするには下記コマンドになるそうです。
DROP PROCEDURE IF EXISTS RegisterAllStoresWithCity;

2017年1月7日土曜日

はじめの一歩 -Rails ActiveRecord編- SELECT


どうも、はじめです。

あけましておめでとうございます。
今年もよろしくお願いいたします。

さて前回はActiveRecordのINSERTについて書いてみました。
はじめの一歩 -Rails ActiveRecord編- INSERT
今回はActiveRecordのデータの更新(update)について書こうと思っていましたが、
先にデータの取得(select)について書いてみようと思います。


データの取得をしたい場合


モデルに登録されているすべてのデータを取得したい場合はallを、
検索をしたい場合はwhereを使用します。

Userモデルに以下でデータを登録したとします。
[id: 1, name: ‘名前1’, mail: ‘111@xxx.xxx’],
[id: 2, name: ‘名前2’, mail: ‘222@xxx.xxx’],
[Id: 3, name: ‘名前3’, mail: ‘333@xxx.xxx’]

all
User.all
# [Id: 1, name: ‘名前1’, mail: ‘111@xxx.xxx’],
# [Id: 2, name: ‘名前2’, mail: ‘222@xxx.xxx’],
# [Id: 3, name: ‘名前3’, mail: ‘333@xxx.xxx’]

where
User.where(id: 2)
# [Id: 2, name: ‘名前2’, mail: ‘222@xxx.xxx’]
allの場合は指定したモデルに登録されているデータをすべて取得することができます。
whereの場合はwhere()内に「カラム名: 検索したい値」を指定することで、
指定した値で検索した結果を取得することができます。


複数条件で検索をしたい場合


複数の条件でデータの取得を行いたい場合はwhereにて条件を複数指定します。
Idが2と3のレコードを取得したい場合は以下のようにidに対し配列で条件を指定します。

User.where(id: [2, 3])
# [Id: 2, name: ‘名前2’, mail: ‘222@xxx.xxx’],
# [Id: 3, name: ‘名前3’, mail: ‘333@xxx.xxx’]

複数カラムを指定したい場合は以下のように指定します。
User.where(id: 2, name: ‘名前2’)
# [Id: 2, name: ‘名前2’, mail: ‘222@xxx.xxx’]
このように指定した場合はAND条件で検索されるので注意が必要です。


最後に、


OR条件での検索や、AND条件でも他の記述方法があったりと
他にもデータの抽出には種類があるため、
次回も引き続きSELECT関連を書いていこうと思います。

2017年1月6日金曜日

iOS10のWKWebViewとUIWebViewでhttpでの通信を許容する方法

こんにちは、onukiです。

対応が必須になったATS(NSAppTransportSecurity)ですが
こちらの対応を行うとhttpでの通信は行えなくなります。
ただし、iOS10のWKWebViewとUIWebViewに限りInfo.plistにキーを追加するだけで
httpでの通信を許容してくれるので今回はそちらの紹介をします。

ATS(NSAppTransportSecurity)とは


Cocoa keys
簡単に言ってしまうとiOS9以上で以下の通信にhttpの通信が使えなくなります
  • WKWebView
  • UIWebView
  • NSURLSession
  • NSURLConnection

httpでの通信を許容するには


iOS10よりATSに関して新たなキー「NSAllowsArbitraryLoadsInWebContent」が追加されました。
このキーはWebViewを利用してウェブコンテンツを表示している場合、
その内容に関してはhttpでの通信を許容するというものです。

設定方法


Info.plistの「App Transport Security Settings」に
「NSAllowsArbitraryLoadsInWebContent」を追加し値を「YES」にする


注意


タイトルからiOS10としてきましたが、
「NSAllowsArbitraryLoadsInWebContent」は現状、iOS10のみ有効になります。
このキーを設定していてもiOS9ではWebViewでのhttp通信は弾かれてしまいます。


2017年1月5日木曜日

Dockerと私。第1回

明けましておめでとうございます。
旧年中は色々とお世話になり誠にありがとうございました。
本年も何卒よろしくお願い致します。

最近このブログのテック度合いが半端ないなと感じておりますが、
重ねるかの様に「Docker」について話をしたく思います。

以前のドトール会で「Docker」の話をさせて頂いたのですが、
その時はDocker engineのインストールや、イメージPull、DockerRunさせて
コンテナの起動といった一連の流れを実践しながらやらせて頂きました。
ただ、上手く伝え切れなかった部分が多々あったというか、
結構な漏れがあったと思われましたので、
仮想環境の話やそもそも的なところからやり直そうと思った次第でございます。

  • コンテナ型・ハイパーバイザー型・ホスト型といった仮想化について
  • Dockerって?
  • Dockerの特徴(利点・欠点)

■コンテナ型・ハイパーバイザー型・ホストOS型といった仮想化について

この3つの違いは絵的なものを見てもピンと来ないかもしれませんので、
下記にて説明をします。

・コンテナ型仮想化(Docker、LXC、OpenVZ)

    ホストOSからは各コンテナがプロセスとして認識される。
    (コンテナ内では自コンテナのプロセスしか見えないが、ホスト側から各コンテナで動作する全プロセスが見える)
    他の仮想化と同様に、コンテナ毎にCPUやメモリなどのリソース制限ができる。

・ホスト型仮想化(VMware Player、VMware Fusion、VirtualBox)

    ホストOS上で仮想化ソフトを実行し、その上で仮想マシン、ゲストOSが稼働する。
    3種の仮想化の中では一番ホストマシンへの処理負荷が高い。
    ホストOS上で動作するアプリケーションと共存できるため、PC上など個人環境での利用が多い。   

・ハイパーバイザ型仮想化(ESXi、Hyper-V、Xen、KVM)

    ホストOSの代わりに仮想マシンを制御するプログラムのハイパーバイザが動作する。
    ホストサーバ全体を仮想化で利用する。
    ホストOSを経由しないため、ホストOS型よりレスポンスはよくなる。

ホスト型なんかはVirtualBoxを使ったりする方にはイメージし易いかもしれません。


■Dockerって?

 

Dockerはソフトウェアコンテナ内のアプリケーションのデプロイメントを自動化するオープンソースソフトウェア。
Linuxカーネルにおける「libcontainer」と呼ばれるLinuxコンテナ技術・namespace、aufs、cgroup等を利用してコンテナ型の仮想化を行う。
VMwareやKVM・Xenなどの完全仮想化を行うハイパーバイザー型と比べ
コンテナ型であるDockerは、ディスク使用量が少なく、
インスタンスの作成や起動は速く、性能劣化がほとんどないという利点を持っている。
Dockerfileと呼ばれる設定ファイルからコンテナイメージファイルの作成が可能という利便性を持つ一方で、
コンテナOSとしてはホストOSと同じLinuxカーネルしか動作しない。
(Docker Toolbox を使うことでMacやWindowsでの動作が可能)


■Dockerの特徴(利点・欠点)

Dockerの利点

ホスト側のカーネルを使うためオーバーヘッドが小さくて高速。
メモリなどのリソース消費を抑える事が出来る。
リソース消費が少ないので多くのコンテナを立ち上げる事が可能。
新たにカーネルを立ち上げる必要は無く、ハードウェアの初期化などの操作が不要であるためコンテナの起動が速い。

上記利点については、
以前携わっていた仕事で使っていたOSレベル仮想化ソフトウェアLXCも同様なのですが、
ただDockerが違うのは、コンテナを起動する際、
各コンテナ毎にDisk上にデータを持つのではなく、
イメージから複数のコンテナを作るという方式でコンテナを立ち上げたりします。
コンテナのイメージ(テンプレート的なもの)からコンテナを作れるので、
同一環境や構成を簡単に作る事が可能なのです。
開発からデプロイといった一連の作業が速く進むといった具合です。
   
LXCだとディスクをLVM等で管理してあげないと駄目だったり、
カーネルの持つケーパビリティの機能を使って
コンテナの特権を制御とか必要となったりするので、
ことソフトウエア開発に特化する場合においては
Dockerの便利さが際立っているのがわかると思います。

Dockerの欠点

Docker上のコンテナはホストのカーネルを利用する為、
カーネルを共有できるCentOSやUbuntuといったLinuxでは動作し共存も出来るのですが、
根本的にカーネルの異なるOS、例としてWindowsServerはホストとなるLinux上で動作は不可能です。

恐らく日々オープンソース云々とか言ってられる方にとっては、
Hyper-Vを使ってとかあんまり機会がないかもしれませんのでこの辺は多くを語らずに。

そんなMicrosoftさんですが、Microsoft Azureにてコンテナーベースのアプリケーションをデプロイおよび管理出来るそうですね。

そんな事は置いておいて、次回はDockerの利用方法について語ろうかと思います。

2017年1月4日水曜日

ネットワークインジケータとAlamofireNetworkActivityIndicator

 こんにちは、Hiroです。
 iOSアプリ開発において、API通信での通信中は、ステータスバーにネットワークインジケータを表示させると思いますが、API通信部分でAlamofireを利用している場合、AlamofireNetworkActivityIndicatorを使うと表示・非表示を勝手に制御してくれます。とても便利なので、今回はこちらをご紹介したいと思います。
注)AlamofireNetworkActivityIndicatorは、2016年12月24日時点で、最新バージョンの2.1.0を対象にしています。


CocoaPodsでインストール


まずは、Alamofireを利用中ということを前提としています。
Podfileに下記を記載して、インストールします。
  use_frameworks!

  pod 'Alamofire'
  pod 'AlamofireNetworkActivityIndicator'
$ pod install


swiftコード上での設定


CocoaPodsでのインストールが完了したら、AppDelegate.swiftでAlamofireNetworkActivityIndicatorの設定をします。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    NetworkActivityIndicatorManager.shared.isEnabled = true
    NetworkActivityIndicatorManager.shared.startDelay = 0
    NetworkActivityIndicatorManager.shared.completionDelay = 0.2
    return true
}
以上で、Alamofire.requestで通信を開始するとステータスバーにネットワークインジケータが表示され、通信が終了すると非表示になります。


AlamofireNetworkActivityIndicator設定値の説明

NetworkActivityIndicatorManager.sharedManager.isEnabled

この設定を「true」にするだけで、基本的には設定完了となります。
残りの「startDelay」と「completionDelay」は、下記で説明しますが、基本的にはオプションでデフォルト値があります。

NetworkActivityIndicatorManager.sharedManager.startDelay

通信が開始されて、この設定値の秒数後にインジケータが表示されます。
デフォルト値は、1.0となっており、通信が早く完了すると表示されないこともあるため、0を設定することの方が多いです。

NetworkActivityIndicatorManager.sharedManager.completionDelay

通信が完了して、この設定値の秒数後にインジケータが非表示になります。
デフォルト値は、0.2となっています。


Alamofireを使わない場合(WebViewなど)


Alamofireを使わない場合は、本内容の設定をしてもステータスバーにネットワークインジケータが表示されることはりません。
その場合、自前で通信開始時と終了時に下記の設定を記述する必要があります。
通信開始時にネットワークインジケータの表示:
UIApplication.shared.isNetworkActivityIndicatorVisible = true
通信終了時にネットワークインジケータの非表示:
UIApplication.shared.isNetworkActivityIndicatorVisible = false

2017年1月3日火曜日

macOS Sierraでcapybara-webkitを使うためにやったこと

こんにちは、h_ono_222です。
今回はmacOS Sierra環境でcapybara-webkitを使うために行ったことをまとめます。

手順

  1. Qt 5.5のインストール
  2. default_pre.prfを書き換える
  3. gemをインストール

1. Qt 5.5のインストール

こちらのウェブサイトから「qt-opensource-mac-x64-clang-5.5.1.dmg」をダウンロードしてインストール(デフォルトの設定で良い)します。

2. default_pre.prfを書き換える

デフォルトの設定でインストールした場合、下記の場所にあります。
# default_pre.prf
~/Qt5.5.1/5.5/clang_64/mkspecs/features/mac/default_pre.prf

このファイルの15行目を下記のように変更します。
# before
isEmpty($$list($$system("/usr/bin/xcrun -find xcrun 2>/dev/null"))): \

# after
isEmpty($$list($$system("/usr/bin/xcrun -find xcodebuild 2>/dev/null"))): \

3. gemをインストール

最後に、capybara-webkitをインストールします。
# pathを指定してgem install
PATH=~/Qt5.5.1/5.5/clang_64/bin:$PATH gem install capybara-webkit

2017年1月2日月曜日

私たちのFactoryGirlとRspecの使用ルール その1

こんにちは、Taroです。
新年明けましておめでとうございます。

最近、私たちはRailsのテストにRspecを使用しております。
(テストレコードの作成にはFactoryGirlを使用しています。)
今回は私たちのグループ内における基本的な使用ルールについて記載したいと思います。

その1と題しておりますが、その2があるかは未定です。
使っている中で、ルールを更新した場合は、その2、その3としてブログを書いていきたいと思います。

そもそもなぜ基本ルールなどを作成しようとしたのか?
RspecにしろFactoryGirlにしろ、それに沿った使い方をしていれば問題ないのではないか?
当初は私たちもそう考えておりました。
しかし、あまりに優秀すぎるが故、以下の悩みが生じました。
  • 1. 学習コストが高い
    便利な用法が数多く存在するが、それ故に統一感がなくなる可能性がある。
  • 2. 思わぬところでエラーが発生する可能性がある
    特にFactoryGirlですが、Aさんが手の込んだテストモデルを作成し、Bさんがそれをまた改修した場合、Aさんが担当したテストに影響が出る可能性があります。
  • 3. テスト実行に時間がかかるようになる
    せっかくだからと、コールバックメソッドを呼び続けると、全体で見たときのテスト実行に時間をとられることになってしまいます。

このような問題への一つの解として、基本的なルールを設けることとしました。

Rspec編


1. describe、context、itの階層で使用する


Rspecには他にもspecifyやexampleも存在しますが、基本は上記の3つの階層でテストを分類します。
  • describe・・・テストしたい対象(ex. Item#add、items/index)
  • context・・・テストの条件(ex. ログインに成功したとき or ログインに失敗したとき)
  • it・・・リクエストパラメータの違いなどで生じる結果(ex. リクエストパラメータが◯○のときは△△)
また、どうしてもcontextとitの間に階層がほしい場合はspecifyの使用を許可しています。
(ただ、現状specifyを利用するといったテストはないので、そこまで必要ではないのかもしれません。)

describe 'Item#add' do
    context '同一の商品名がない場合' do
        it 'successが返る' do
           expect(Item.add('pokemon')).to eq('success')
        end
    end
    context '同一の商品名がある場合' do
         it 'falseが返る' do
           expect(Item.add('pokemon')).to eq('false')
         end
    end
end

2. 10個以上のテストレコードを作成したい場合は、トランザクションを張る


これはテスト実行速度を少しでもあげるための策です。
ApplicationRecord.transaction do
# テストレコードの作成 end

FactoryGirl編


FactoryGirlの方が特にパフォーマンスなどに影響を与えるため、試行錯誤しております。

1. デフォルトのモデルは初期値を与えるだけで、個別に値設定したい場合は、createメソッドの引数で指定する


デフォルトのモデルはきれいな状態で残しておきます。

2. traitにてパーツを量産しない


ただし、以下の条件の場合は作成できます。
これは、パーツの名前と継承したモデル名で、どんなモデルなのか判断しやすいためです。

2-1. enumで定義されているカラム

例えば、itemsテーブルにカテゴリを定義するcategoryカラムがあったとします。
1〜4までのカテゴリがあり、それぞれにenumでカテゴリ名が定義されているとします。このような場合は、traitとそれを継承したモデルを作成します。
trait :game do
    category 1
end
factory :item_game, traits: [:game], parent: :item

2-2. 過去、未来、現在で固定する

trait :past do
    started_at Time.now.yesterday.beginning_of_day
    ended_at Time.now.yesterday.end_of_day
end
factory :item_past, traits: [:past], parent: :item

2-3. 関連データを生成する場合

関連データを作成してくれることは非常に便利ですが、テストが遅くなる原因となります。
また、別に関連データ自体必要ない人もいるため、こちらもtraitでパーツにして必要な人のみ使用します。
# Item belongs_to Companyとします
trait create_company do
    company
end
factory :item_create_company, traits: [:create_company], parent: :item

現状まとめますと、以上になります。
今後もRSpec、FactoryGirlは使用していこうと思いますので、ルールを更新したら、その2、その3というかたちで記事を書いていきたいと思います。
また、このブログを読んだ方で、何かご意見などございましたらコメントをお待ちしております!

2017年1月1日日曜日

明けましておめでとうございます。2016年の人気ブログ ランキング

 明けまして、おめでとうございます。Hiroです。2016年は色々な方にお世話になり、学びの多い年となりました。本年もどうぞよろしくお願いします。
 さて、新年の最初の投稿は、2016年の当ブログ記事内でPV数の高かった上位5つのブログを発表します。ぜひ、再度お読みいただければと思います。

1位:さらば「rails migrate」、よろしく「ridgepole」


2位:Amazon RDS 一般クエリログとスロークエリログの削除


3位:MySQL 5.7 オプティマイザの改善(UNION ALL)


4位:ObjectクラスにBooleanへの変換メソッド「to_b」を追加する


5位:はじめの一歩 -Rails 時間・タイムゾーン編- その2


 今後も読者の方にとって、役立つ情報をブログにしていきたいと思っています。