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

2016年12月31日土曜日

Swift filter, map, reduceメソッド

 こんにちは、Hiroです。久しぶりにiOSアプリをやることになり四苦八苦しています。
 さて、本日はRuby on Railsでは馴染み深いメソッドのSwift版を紹介したいと思います。


配列(Array)のfilter, map, reduce


配列に関して便利メソッドを3種類紹介します。先ずは、以下の例で操作対象となる配列を宣言しておきます。
// Array
var numbers: [Int] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

filter

クロージャで指定した条件を満たす、新しい配列を返却します。
例として、2の倍数を取得する処理を記述します。
let evenNumbers: [Int] = numbers.filter { (value: Int) -> Bool in
    return value % 2 == 0
}
print(evenNumbers)
// => [0, 2, 4, 6, 8]
上記のようにクロージャを渡すのですが、第一引数に「$0」でアクセスでき、下記のように簡略にすることもできます。
let evenNumbers: [Int] = numbers.filter { $0 % 2 == 0 }
print(evenNumbers)
// => [0, 2, 4, 6, 8]

map

配列の1つ1つにクロージャで指定した処理を行った配列の結果を返却します。
例として、1つ1つの要素を2倍にする処理を記述します。
let doubleNumbers: [Int] = numbers.map { (value: Int) -> Int in
    return value * 2
}
print(doubleNumbers)
// => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
簡略式も下記に記載します。
let doubleNumbers: [Int] = numbers.map { $0 * 2 }
print(doubleNumbers)
// => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

reduce

要素のたたみ込みをするためのメソッドです。要素をまとめて一つにする場合などで利用できます。 例として、要素の合計値を取得する処理を記述します。
let totalNumber = numbers.reduce(0) { (total: Int, value: Int) -> Int in
    total + value
}
print(totalNumber)
// => 45
簡略式も下記に記載します。
let totalNumber = numbers.reduce(0) { $0 + $1 }
print(totalNumber)
// => 45


辞書(Dictionary)のfilter, map, reduce


辞書に関しても上記の「filter」「map」「reduce」のメソッドは使えますので、簡略式ですが、こちらも記載しておきます。先ずは、例で操作対象となる辞書を宣言しておきます。
// Dictionary
var dictNumbers = ["zero":0, "one": 1, "two": 2, "three": 3 , "four": 4, "five": 5, "six": 6, "seven": 7, "eight": 8, "nine": 9]

filter

Dictionaryのため、順序保証がないのと、返却値はタプルの配列になっています。
let evenDictNumbers = dictNumbers.filter { $0.1 % 2 == 0 }
print(evenDictNumbers)
// => [("four", 4), ("zero", 0), ("two", 2)] 

map

こちらもタプルとしていますが、辞書で返却したい場合が普通のケースですので、そん場合、次で紹介する「reduce」のやり方がよいでしょう。
let doubleTupleNumbers = dictNumbers.map { ($0.0, $0.1 * 2) }
print(doubleTupleNumbers)
// => [("three", 6), ("one", 2), ("nine", 18), ("seven", 14), ("four", 8), ("five", 10), ("zero", 0), ("six", 12), ("eight", 16), ("two", 4)]

reduce

let doubleDictNumbers = dictNumbers.reduce([String: Int]()) { var varDict = $0; varDict[$1.0] = $1.1 * 2; return varDict}
print(doubleDictNumbers)
// => ["three": 6, "one": 2, "nine": 18, "seven": 14, "four": 8, "five": 10, "zero": 0, "six": 12, "eight": 16, "two": 4]
上記で、注意点としては、一度「$0」を「varDict」変数に代入しているところです。これは、「$0」がイミュータブルな変数だからです。
そのため、下記ではエラーとなります。
let doubleDictNumbers = dictNumbers.reduce([String: Int]()) { $0[$1.0] = $1.1 * 2; return $0}
// =>Cannot assign through subscript: '$0' is immutable


いかがでしたでしょうか。Ruby on Railsではおなじみのメソッドですが、少しSwiftならではの制約があります。ただ、とても可読性が高く、便利なメソッドなのでどんどん利用してみようと思います。

2016年12月30日金曜日

ObjectMapperでJSONのネスト配列に対応する方法

こんにちは、onukiです。

私はSwiftでJSONを取り扱う際に、
ObjectMapperを使用してるのですが
今回は読み込むJSONにネスト配列がある場合の使い方を書いていこうと思います。

ObjectMapperとは

ObjectMapperとはJSONデータをモデルに変換する処理を
簡単に扱えるライブラリです。

使い方

今回、読み込むJSONは以下のようなネスト配列が含まれているものを使用します。
{
    "id": 1,
    "name": "user1",
    "address": "hoge",
    "photos": [
    {
    "id": 1,
    "name": "photo1"
    },
    {
    "id": 2,
    "name": "photo2"
    }
    ]
}

ObjectMapperはMappableプロトコルをモデルクラスに実装し使用するのですが
今回はネスト配列に対応するために2つ作成します。
struct User: Mappable {
    var id = 0
    var name = ""
    var address = ""
    var photos: [Photo] = []
    
    init?(map: Map){}
    mutating func mapping(map: Map) {
        id <- map["id"]
        name <- map["name"]
        address <- map["address"]
        photos <- map["photos"]
    }
}
struct Photo: Mappable {
    var id = 0
    var name = ""
    
    init?(map: Map){}
    mutating func mapping(map: Map) {
        id <- map["id"]
        name <- map["name"]
    }
}

JSONからモデルに変換するのには以下のように取得します。
let user: User = Mapper().map(JSONString: jsonString)!
print(user.name) // => "user1"

そして、userのphotos配列から0番目のデータを取得したい際にはこのように取得します。
let photo: Photo = user.photos[0]

これでObjectMapperでJSONのネスト配列を読み込み、扱うことが出来ました。

2016年12月29日木曜日

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


どうも、はじめです。

前回はActiveRecordについて書いてみました。
はじめの一歩 -Rails ActiveRecord編- 序章
今回はActiveRecordのデータの保存(create,new)について
描いてみようと思います。


データを保存したい場合


ActiveRecordにてDBにデータを保存する場合は
createまたは、newを使用します。
それぞれの記述方法は以下のようになります。

create
User.create(name: '山田', mail: 'xxx@xxx.xxx')
new
user = User.new(name: '山田', mail: 'xxx@xxx.xxx')
user.save()

#  または
user = User.new()
user.name = '山田'
user.mail = 'xxx@xxx.xxx'
user.save()
上記のように記述することでusersテーブルに対し、
name: 山田
mail: xxx@xxx.xxx
というレコードがinsertされます。


例外を発生させたい場合


create,new共に「!」をつけるだけでinsertに失敗した際に例外を発生させてくれます。

create

User.create!(...)

new

user = User.new(...)
・
・
user.save!


createとnewの違い


違いとしてあげられるものはDBにinsertをするタイミングです。
それぞれ以下のタイミングでDBへinsertされます。
create -> createメソッドの実行時
new -> saveメソッドの実行時

ちなみに、
以下のようにsaveメソッドを複数回記述しても
insertされるのは最初の一度のみとなります。
user = User.new(...)
・
・
user.save # ←ここでのみinsertされる
user.save # 何もinsertされない
user.save # 何もinsertされない
試してみたところ2回目以降のinsertは何もinsertされませんが、
返り値はtrueが返却されました。


最後に


createを実行した際は裏でnewとsaveを順番に行なっているだけなので、
結果やっていることは変わりません。
なので、インスタンスを生成するタイミングで登録に必要な値が全て揃っている場合はcreate。
それ以外の場合。
例として、
・任意のタイミングでsaveしたい。
・インスタンスの生成後に何かの処理をしたい。
・特定の条件でのみ登録するカラムが存在する。
等の理由がある場合はnewを使用するといった形になるのではないかと思いました。


次回


次回は更新(update)について書いていきたいと思います。

2016年12月28日水曜日

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


こんにちは、h_ono_222です。
今回のドトール会はで「サイバーセキュリティ入門: 私たちを取り巻く光と闇」の1. インターネットの仕組みについて発表しました。

はじめに、本書の構成について簡単に説明すると、本書では六つのテーマについて書かれています。
テーマはそれぞれ、

  1. インターネットの仕組み(プロトコル、ルーティング)
  2. 暗号の世界に飛び込もう(AES、RSAなどの暗号化方式)
  3. インターネットとセキュリティ(VPN、電子証明書)
  4. インターネットにおけるサイバー攻撃(DOSを始めとした攻撃手法)
  5. ハードウェアとソフトウェア(ハードウェアによる暗号化とその解析)
  6. 私達を取り巻くセキュリティ(法律)

となっています。
本書は全体を通して、専門用語をわかりやすい例えで説明しており、また、インターネットの基礎的な知識も得ることができることから、
コンピュータに興味がある高校生などにもオススメの一冊となっております。

1. インターネットの仕組みでは、まず電話とインターネットの違いから説明し、
インターネットが耐障害性の強いネットワークであることを示しています。

そして、通信を行うための約束事(プロトコル)に関して、セオリー通りOSI参照モデルの各層について、その役割を説明しています。

この部分を読んでおくと、インターネットの基礎的な部分が理解できると思います。

次回は4と5について発表する予定です。

2016年12月27日火曜日

Railsのバリデーション

この記事は移転しました。
http://re-engines.com/2017/12/08/rails-validation/

2016年12月26日月曜日

InnoDBでauto_incrementの値が戻る?

Taroです、最近寒くて朝が辛いです。
本日は実際に困って調べた話にしたいと思います。
タイトルですが、InnoDBだとDBを再起動した際にauto_incrementが最適化されてしまうとのこと。
すでに多くの知見があるようですが、今回は実際に試してみました。
5.5.9のリリースにて、最適化にてリセットされる問題は解消されたようですが、今回は試しにやってみたいと思います。
Changes in MySQL 5.5.9 (2011-02-07, General Availability)

準備


MySQLのバージョンは5.7.14になります。
$ mysql --version
mysql  Ver 14.14 Distrib 5.7.14, for osx10.10 (x86_64) using  EditLine wrapper
とりあえず、テスト用のDBを作成します。
mysql> create database test;
ストレージエンジンの一覧を見てみます。
現在はInnoDBがデフォルトですね。(というより、こんなにたくさんあるんですね。。。)
mysql> show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| InnoDB             | DEFAULT | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine                                          | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
| ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
| FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
今回はInnoDBとよく比較されるMyISAMでも試したいと思います。
InnoDBとMyISAMの簡単な特徴は下記になります。
  • InnoDBはレコード単位でロックされるが、MyISAMはテーブル単位。
  • トランザクション機能がMyISAMにはない。
  • InnoDBはMyISAMに比べてデータサイズが大きくなる。
  • InnoDBは更新系、MyISAMは参照系が得意。

それでは実際にそれぞれのエンジンのテーブルを作成します。
mysql> create table innodb_test (id int primary key auto_increment) engine=InnoDB;
mysql> create table myisam_test (id int primary key auto_increment) engine=MyISAM;
実際に各テーブルのストレージエンジンはinformation_schemaに移動すれば見ることができます。
mysql> use information_schema;
mysql> select table_name, engine from tables where table_schema = "test";
+-------------+--------+
| table_name  | engine |
+-------------+--------+
| innodb_test | InnoDB |
| myisam_test | MyISAM |
+-------------+--------+

レコード作成


それぞれのテーブルにレコードを作成します。
mysql> insert into innodb_test (id) values (0), (0), (0), (0), (0);
mysql> insert into myisam_test (id) values (0), (0), (0), (0), (0);
mysql> select * from innodb_test;
mysql> select * from myisam_test;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
とりあえず、2つレコードを削除しておきます。
mysql> delete from innodb_test where id in (4, 5);
mysql> delete from myisam_test where id in (4, 5);

テーブル最適化


それでは最適化してみます。
まずはInnoDBから。
mysql> optimize table innodb_test;
+------------------+----------+----------+-------------------------------------------------------------------+
| Table            | Op       | Msg_type | Msg_text                                                          |
+------------------+----------+----------+-------------------------------------------------------------------+
| test.innodb_test | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| test.innodb_test | optimize | status   | OK                                                                |
+------------------+----------+----------+-------------------------------------------------------------------+
およ、何かメッセージがでています。
どうやらInnoDBではoptimizeコマンドはALTER TABLEとして実行されるそうです。(その通知メッセージのようです。)
14.7.2.4 OPTIMIZE TABLE Syntax

続きましてMyISAMです。
mysql> optimize table myisam_test;
+------------------+----------+----------+----------+
| Table            | Op       | Msg_type | Msg_text |
+------------------+----------+----------+----------+
| test.myisam_test | optimize | status   | OK       |
+------------------+----------+----------+----------+
それでは、再度インサートします。
mysql> insert into innodb_test (id) values (0), (0);
mysql> insert into myisam_test (id) values (0), (0);
mysql> select * from innodb_test;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  6 |
|  7 |
+----+
mysql> select * from myisam_test;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  6 |
|  7 |
+----+
最適化ではやはり戻らないですね。
次は再起動を試してみます。

MySQL再起動


それでは再起動後にインサートしてみます。(再起動前はauto_incrementの値が13でした。)
mysql> select * from innodb_test;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+

mysql> select * from myisam_test;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
| 13 |
| 14 |
+----+
InnoDBの方はauto incrementが戻っていました。
実際、テーブル情報を見ると、戻っていました。
mysql> show table status like 'innodb_test' \G;
*************************** 1. row ***************************
           Name: innodb_test
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 3
 Avg_row_length: 5461
    Data_length: 16384
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: 4
    Create_time: 2016-12-15 23:38:15
    Update_time: NULL
     Check_time: NULL
      Collation: utf8_general_ci
       Checksum: NULL
 Create_options: 
        Comment: 

mysql> show table status like 'myisam_test' \G;
*************************** 1. row ***************************
           Name: myisam_test
         Engine: MyISAM
        Version: 10
     Row_format: Fixed
           Rows: 3
 Avg_row_length: 7
    Data_length: 56
Max_data_length: 1970324836974591
   Index_length: 2048
      Data_free: 35
 Auto_increment: 13
    Create_time: 2016-12-15 19:30:36
    Update_time: 2016-12-15 23:52:41
     Check_time: 2016-12-15 23:38:22
      Collation: utf8_general_ci
       Checksum: NULL
 Create_options: 
        Comment: 


最適化では確認できませんでしたが、再起動ではInnoDBの方はauto_incrementの値が戻っておりました。
InnoDBではauto incrementのカラムをメモリ上のみに持ち、再起動のタイミングで以下で値を取得しているようです。
SELECT MAX(ai_col) FROM table_name FOR UPDATE;

参考資料
15.8.6 AUTO_INCREMENT Handling in InnoDB
【MySQL】AUTO_INCREMENTの値が戻る@InnoDBエンジンのテーブル
Changes in MySQL 5.5.9 (2011-02-07, General Availability)
MySQLの「InnoDB」と「MyISAM」についての易しめな違い
14.7.2.4 OPTIMIZE TABLE Syntax

2016年12月25日日曜日

はじめの一歩 -Rails ActiveRecord編- 序章


どうも、はじめです。

前回はおまけとしてapplication.rbでのタイムゾーンの設定の仕方を紹介しました 。
「はじめの一歩 -Rails 時間・タイムゾーン編- おまけ」
今回から「ActiveRecord編」に入っていこうと思います。


ActiveRecordとは


「Ruby on Rails」にて用意されているORMになります。
AcriveRecordを使用することでSQLを記述しなくても
わずかなアクセスコードでBDへのデータの保存や取得を行うことができます。


AcriveRecordのルール


AcriveRecordを使用する上でのルールとして以下の命名規則があります。
・モデル名、クラス名には単数形のキャメルケース(語頭を大文字)を使用する
・テーブル名、カラム名には複数形で語の区切り文字として「_(アンダーバー)」を使用する。
・テーブルの外部キーは「テーブルの単数形_id」とする。
・主キーには「id」を使用する。

例としてユーザーテーブルとユーザー投稿テーブルを使用する際には以下のようになります。
usersテーブル           -> Userモデル
user_postsテーブル -> UserPostモデル
という形でテーブル名、モデル名が決まります。

両テーブルの主キーは「id」
user_postsテーブルが持つ外部キーは「user_id」となります。

他にも幾つか用意されているカラム名が存在しています。
 ・created_at - レコード作成時の日時が自動的に登録されます。
 ・updated_at - レコードが更新された時の日時が自動的に登録されます。
 ・lock_version - モデルにoptimistic lockingを追加します。
 ・type - モデルでSingle Table Inheritanceを使用する場合に指定します
 ・関連付け名_type - ポリモーフィック関連付けの種類を保持します。
 ・テーブル名_count - 関連付けにおいて、所属しているオブジェクトの数をキャッシュするのに使用されます。


ActiveRecordのモデルを作成


例としてUserモデルの作成を行います。
# app/models/user.rb
class User < ActiveRecord::Base
end

AcriveRecordの命名規則に反した名前になってしまった場合
以下のようにオーバーライドすることも可能です。
# app/models/user.rb
class User < ActiveRecord::Base
    self.table_name = ‘USER’
end
主キーとなるカラム名が「id」以外の場合は以下のように指定をすることも可能です。
# app/models/user.rb
class User < ActiveRecord::Base
    self.primary_key = ‘user_id'
end

このようにテーブルに対して紐づくモデルの作成が完了すれば
テーブルからのデータの呼び出し、登録等の操作が可能になります。


次回


モデルの作成が完了したので、次回は登録系について書いていこうと思います。

2016年12月24日土曜日

xcode8のシステムログを消す

こんにちは、onukiです。

今回はxcode8でアプリの実行時に大量にシステムログが吐かれるので
それを解除する方法を書きます。

手順は、xcodeのEdit Scheme...を開き、Argumentsのタグを選択の
Environment VariablesにNameを「OS_ACTIVITY_MODE」、
Valueを「disable」で追加します。











これで、システムログは出なくなります。
ただしこの設定を行うと実機で「NSLog」が出力されなくなるのでご注意ください。
※「print」は出力されます

2016年12月23日金曜日

はじめの一歩 -Rails 時間・タイムゾーン編- おまけ

どうも、はじめです。

前回は月や年の最初や最後の取得をやってみました。
「はじめの一歩 -Rails 時間・タイムゾーン編- その5」
今回はapplication.rbでのタイムゾーンの設定方法を紹介したいと思います。

はじめに

application.rbでタイムゾーンを指定をしなかった場合はデフォルトでUTCが指定されます。
任意のタイムゾーンを設定するには以下のように指定をします。
# config/application.rb
config.time_zone = 'Tokyo'
ここで指定している’Tokyo’という文字列ですが何でも良いという訳ではありません。 RailsのActiveSupportにて予め用意されているものの中から任意の文字列を指定する事により、
該当するタイムゾーンを指定してくれるようになっています。

それではActiveSupportが用意している文字列を幾つか紹介したいと思います。

Asia/Tokyo

このタイムゾーンを指定したい場合は以下のように指定をします。
config.time_zone = 'Tokyo'
他にも’Osaka’や’Sapporo'と指定しても同様に”Asia/Tokyo”のタイムゾーンが指定されます。

America/New_York

アメリカのニューヨークのタイムゾーンを指定したい場合はこのように指定をします。
config.time_zone = 'Eastern Time (US & Canada)'

Asia/Seoul

こちらは韓国のソウルのタイムゾーンです。
config.time_zone = 'Seoul'

Asia/Taipei

こちらは中国の台北のタイムゾーンです。
config.time_zone = 'Taipei'

ちなみに、
Tokyoのように同じタイムゾーンを示す文字列が複数存在するものは他にもあります。

Pacific/Midway

こちらはアメリカのミッドウェイ島というところで、国際日付変更線も表しています。
以下の文字列で指定が可能です。
config.time_zone = 'International Date Line West'
config.time_zone = 'Midway Island'

America/Mexico_City

こちらはアメリカのメキシコのタイムゾーンです。
以下の文字列で指定が可能です。
config.time_zone = 'Guadalajara'
config.time_zone = 'Mexico City'

Europe/London

こちらはロンドンのタイムゾーンで、以下の文字列での指定が可能です。
config.time_zone = 'Edinburgh'
config.time_zone = 'London'

他にも幾つかあるので興味のある方は調べてみて下さい。

最後に、

ここで紹介した'Asia/Taipei’や'America/New_York’は
「はじめの一歩 -Rails 時間・タイムゾーン編- その4」
で紹介した、任意のタイムゾーンに変換して表示をする
time.in_time_zone('America/Chicago')
でも使用することができます。

次回

次は「時間・タイムゾーン編」のまとめをやりたいと思います。

2016年12月22日木曜日

Swift 型推論とコンパイル時間

 こんにちは、Hiroです。今回はサクッとブログを書きます。

型推論とは


Swiftは静的型付け言語ですので、初めて聞くと「?」となると思いますが、Swiftでは、変数の宣言時に型を明示しなくとも代入されている値から、「コンパイル時」に型を推測してくれる機能があります。
そのため、下記の変数value1、value2はどちらの宣言であっても、Int型として問題なく動作します。
var value1: Int = 10
var value2 = 10
print("value1: \(value1), value2: \(value2)")
// => value1: 10, value2: 10


型推論の注意点


このように、どちらでも問題ないのであれば、型推論だけでいいじゃないかと思いますが、上記で記載しているとおり、「コンパイル時」に型を推測するため、あまりにも型推論が多いとコンパイルに時間がかかってきてしまいます。小規模なアプリでは、問題ないのですが、大規模なコードになるとコンパイルに時間がとられてしまい、開発効率を落としかねないので、注意したいところですね。

このように、簡易的に使えるものであっても、理解をした上で利用することが大切だと思いました。

2016年12月21日水曜日

Rails UPDATE or INSERT

 こんにちは、Hiroです。
 以前のブログで、既にレコードが存在していればUPDATEをし、存在していなければINSERTをする方法として、MySQLの「on duplicate key update」を紹介しました。(以前のブログもぜひ、あわせてお読みください。)
 ただ、以前のブログでも触れているように「on duplicate key update」には「auto_increment」に関わる注意点があります。
 そこで、今回は「on duplicate key update」を使わずに、Railsのプログラムで同様な処理を実現させようと思います。


テーブルの説明


テーブルは以前と同じテーブルを使いますが、こちらで再掲しておきます。
idカラムがPRIMARY KEYで「auto_increment」として定義しています。mst_spot_idがスポットを特定するためのidで、UNIQUEインデックスとなっています。
また、更新対象となるカラムは、like_countでデフォルト値が0としています。
mysql> desc spot_like_counts;
+----------------------------+-----------+------+-----+---------+----------------+
| Field                                | Type      | Null   | Key | Default | Extra             |
+----------------------------+-----------+------+-----+---------+----------------+
| id                                    | int(11)   | NO    | PRI  | NULL    | auto_increment |
| mst_spot_id                 | int(11)   | NO    | UNI | NULL    |                        |
| like_count                     | int(11)   | NO    |         | 0          |                       |
| created_at                    | datetime | NO   |         | NULL   |                       |
| updated_at                   | datetime | NO   |         | NULL   |                       |
+----------------------------+----------+-------+-----+---------+----------------+


RailsプログラムでのUPDATE or INSERT


処理としては、likeをする処理です。
実際には、1ユーザで同じ場所に複数回likeできないように、履歴なども管理する必要がありますが、説明を単純化するためにそのあたりは省略しています。
ポイントは、下記に記載してきます。
  def add_like(mst_spot_id)
    begin
      spot_like_count = SpotLikeCount.find_or_create_by(mst_spot_id: mst_spot_id)
    rescue ActiveRecord::RecordNotUnique => ex
      # SELECTとINSERTの間で万が一別のトランザクションでINSERTが走った場合を考慮
      spot_like_count = SpotLikeCount.find_by(mst_spot_id: mst_spot_id)
    end
    check_in_spot_like_count.increment!(:like_count)
  end

find_or_create_byとActiveRecord::RecordNotUnique

find_or_create_byは、まずSELECTでレコードを取得しようとし、存在しなければINSERTを発行します。
SQLとしては、下記が発行されます。
SELECT  `spot_like_counts`.* FROM `spot_like_counts` WHERE `spot_like_counts`.`mst_spot_id` = 1 LIMIT 1
INSERT INTO `spot_like_counts` (`mst_spot_id`, `created_at`, `updated_at`) VALUES (1, '2016-12-20 08:48:15', '2016-12-20 08:48:15')
上記のとおり、SQLが2本分かれて実行されることから、仮にSELECT文とINSERT文の間に別のアクセスでINSERTが実行された場合、mst_spot_idはUNIQUEインデックスとなっていることから、INSERTに失敗して、例外が発生します。
そこで、rescueで「ActiveRecord::RecordNotUnique」を捕捉し、再度SELECT文で更新対象となるレコードを取得するようにします。

increment!

increment!(:like_count)でlike_countを1つインクリメントするUPDATE文を実行します。
発行されるSQLは下記になります。
UPDATE `spot_like_counts` SET `like_count` = COALESCE(`like_count`, 0) + 1 WHERE `spot_like_counts`.`id` = 1
これで、既にレコードが存在していても、していなくてもlike_countを1つインクリメントすることができます。

本日のブログはいかがでしたでしょうか。他にも方法はあると思いますので、ぜひもっとよい方法があれば、コメントをいただければと思います。
また、末尾になりますが、ぜひ以前のブログもご参照ください。

2016年12月20日火曜日

Railsで画像をアップロードする(carrierwave)

こんにちは、h_ono_222です。
今回はRailsで画像アップロードする処理を簡単にまとめます。

今回やること

  1. レコード生成時にファイルを指定してアップロードする
  2. 画像のファイル名を変更する
  3. アップロード時に画像を一定サイズ内に収める
  4. アップロード時にサムネイル画像を作成する
  5. 画像の保存先、一時保存先を変更する

下準備

まず、ImageMagickをインストールします(ImageMagickは画像のリサイズとサムネイル作成で使います)
brew install imagemagick

次に必要なGemを追加してbundle installします。
gem 'carrierwave'
gem 'rmagick'

1. レコード生成時にファイルを指定してアップロードする

まず、アップローダを作成します。
./bin/rails g uploader photo_uploader

次に画像用のカラム名とアップローダを関連付けます。
class user < ActiveRecord::Base
  mount_uploader :profile_image, PhotoUploader
end

画像追加フォームを追加します(レコードの保存に失敗した場合、profile_image_cacheにアップロードした画像情報が格納されます)
<%= form_for @uesr do |f| %>
  <%= f.file_field :profile_image %>
  <%= f.hidden_field :profile_image_cache %>
<% end %>

2. 画像のファイル名を変更する

PhotoUploaderに下記のメソッドを追加します。
def filename
  "#{Time.now.to_i}.jpg" if original_name
end

ファイル名を変更する際の注意点としては、submitしてからレコードが保存されてアップロードした画像がリネームされるまでに上記のfilenameメソッドが複数回呼ばれるため、
ファイル名にランダム文字列を含める場合レコードに保存されたファイル名と実際のファイル名が変わってしまいます。
そのため、一度インスタンス変数に格納するなど工夫する必要があります。

3. アップロード時に画像を一定サイズ内に収める

PhotoUploaderに下記の設定を追加します。
process :resize_to_limit => [300, 300]

この設定では、アスペクト比を保ったまた縮小されます。

4. アップロード時にサムネイル画像を作成する

PhotoUploaderに下記の設定を追加します。
version :thumb do
  process :resize_to_limit => [100, 100]
end

サムネイル画像へのアクセスは下記の通りです。
@user.profile_image.thumb.url

5. 画像の保存先、一時保存先を変更す

画像の保存先の変更は下記の通りです。
def store_dir
  "images/#{model.id}/profile"
end

サムネイルの保存先の変更は下記の通りです。
version :thumb do
  process :resize_to_limit => [100, 100]
  def store_dir
    "images/#{model.id}/profile"
  end
end

画像の一時保存先の変更は下記の通りです。
def cache_dir
  'images/tmp'
end

サムネイルも保存先の変更と同様にブロックの中に記述します。
version :thumb do
  process :resize_to_limit => [100, 100]
  def store_dir
    "images/#{model.id}/profile"
  end
  def cache_dir
    'images/tmp'
  end
end

2016年12月19日月曜日

「新・所得倍増論」は胡散臭い本ではなかった


先日本屋にて、「なんだこの胡散臭い本は」と思い購入しました(大変失礼ではありますが。。。)、デービッド・アトキンソン さんの「新・所得倍増論」について書きたいと思います。
日本人にとってはとても耳が痛い指摘が多く、刺激的な内容です。
(「新・所得倍増論: こうすれば実現する好況スパイラル」という本もありますが、それとは別です。)

デービッド・アトキンソン氏について


大変失礼ながら、デービッド・アトキンソンさんのことを知らなかったため、簡単に調べてみました。

  • イギリス出身、オックスフォード大学で「日本学」を学ぶ
  • アクセンチュアの前身であるアンダーセン・コンサルティングやソロモン・ブラザーズ、ゴールドマン・サックスにて金融アナリストとしてご活躍。
  • 現在は、日本の文化財専門家としてコンサルティング業務に携わっている。

生産性の低い国、日本


主な焦点でもある生産性。
こちらの算出式は、GDP / 人口になります。
ざっくり考えれば、国民一人が稼いでいるお金です。
確かによく、日本人は効率が悪い、長時間労働している割には成果が出ていない、と指摘しているものもチラホラあります。
また、この算出式を書き換えれば、GDP = 人口 × 生産性になるため、人口の増減でGDPも変わります。
日本は1億人以上の国民がおり、世界でも上位です。
そりゃあ、世界のGDPランキングでも上位にくるよね、とのことです。

変わらない、変わろうとしない日本人


「ルールだから」、「法律で決まっているから」という言葉を私も使用したことがあります。
筆者としては、これを言い訳に変わることを拒否している、と指摘しています。
私も以前は閉鎖的な業界で働いておりましたが、ペーパーレス化が進む昨今でも、大量の書類に囲まれ、「規則だから」と納得させられたことを覚えております。
確かに、同じような生活は楽です。

なぜ変われないのか?


ここから下は私の考えになります。
考える上で、先日ブログにも書かせていただきました、実行の4つの規律が役立ちました。
ほとんどの方は変わらなきゃ、と思っております。(あくまで仕事に限った話で。私生活まで含めると複雑なので。)
ただ、日常の嵐のような業務に追われ、「組織として考える時間がない」のではないでしょうか?
一人で頑張ったところで、お金もなければ成果も出ず、モチベーションも下がっていきます。
また、アトキンソン氏も「実行の4つの規律」でも同じことが書かれておりましたが、「組織の経営層が変わらない限り、変わらない」とのこと。
ボトムアップが重要だと言われる昨今ですが、それを本気で推し進めるトップの姿勢が重要ということですね

おわりとして


日常の嵐が変われない原因とすれば、「嵐の対応にかかる時間を減らす」「日常業務とは分けて、改善業務の時間を作る」などが考えられます。
どちらにしても 実行の4つの規律で提唱されている方法が有効そうなので、試してみようと思います。
ただ、私自身気をつけようと思っているのは、アトキンソン氏によれば、「世代が変わっても、今までの流れを踏襲しようとする」と指しています(そりゃ楽ですからね)。
今回考えたことを忘れず、かつ実行できるようにブログとして残しておきます。
「新・所得倍増論」は胡散臭い本ではなく、示唆に富んだ本なのでオススメです。

2016年12月18日日曜日

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

どうも、はじめです。

前回は幾つかのタイムゾーンの紹介と、任意のタイムゾーンでの日時の取得について書いてみました。
「はじめの一歩 -Rails 時間・タイムゾーン編- その4」
今回は月や週などの最初と最後を取得する方法を書いていきます。

はじめに、

前提として今回はTimeWithZoneクラスを使用して、
タイムゾーンはJSTでの実行例を載せていきます。

それでは、年月週などそれぞれの最初と最後を取得する方法を書いていきます。

Time.zone.now.beginning_of_year
# => Fri, 01 Jan 2016 00:00:00 JST +09:00
Time.zone.now.end_of_year
=> Sat, 31 Dec 2016 23:59:59 JST +09:00

Time.zone.now.beginning_of_month
# => Thu, 01 Dec 2016 00:00:00 JST +09:00
Time.zone.now.end_of_month
# => Sat, 31 Dec 2016 23:59:59 JST +09:00

Time.zone.now.beginning_of_week
# => Mon, 12 Dec 2016 00:00:00 JST +09:00
Time.zone.now.end_of_week
# => Sun, 18 Dec 2016 23:59:59 JST +09:00
週の最初は月曜日、最後は日曜日と言うかたちで取得されます。

Time.zone.now.beginning_of_day
# => Sun, 18 Dec 2016 00:00:00 JST +09:00
Time.zone.now.end_of_day
# => Sun, 18 Dec 2016 23:59:59 JST +09:00
ちなみに、
Time.zone.now.beginning_of_day
は以下のようにも書くことができます。
Time.zone.now.midnight
# => Sun, 18 Dec 2016 00:00:00 JST +09:00

現在時刻を14時代だとした場合、
以下のように14時の0分0秒、59分59秒を取得してくれます。
Time.zone.now.beginning_of_hour
# => Sun, 18 Dec 2016 14:00:00 JST +09:00
Time.zone.now.end_of_hour
# => Sun, 18 Dec 2016 14:59:59 JST +09:00

時と同様に現在時刻を14時27分とした場合、
以下のように取得できます。
Time.zone.now.beginning_of_minute
# => Sun, 18 Dec 2016 14:27:00 JST +09:00
Time.zone.now.end_of_minute
# => Sun, 18 Dec 2016 14:27:59 JST +09:00

ちなみに、
以下のように書くことで、任意の曜日の最初を取得することもできます。
Time.zone.now.beginning_of_week(:tuesday)
# => Tue, 13 Dec 2016 00:00:00 JST +09:00
end_of_weekに曜日を指定すると以下のようになります。
Time.zone.now.end_of_week(:tuesday)
# => Mon, 19 Dec 2016 23:59:59 JST +09:00
来週の指定した曜日の最後ではなく、
その日の前日の最後を取得するようなので注意が必要です。


最後に、

曜日を表示する際は日曜日を0として取得をしますが、
beginning_of_weekやend_of_weekで取得する日時は
日曜日ではなく月曜日を最初として取得するので意外でした。
end_of_weekで曜日を指定した場合も含め週を扱う際は、
一度確認をしてから使用すると良いと思います。

次回

次はおまけでapplication.rbにて
タイムゾーンを設定する方法を書きたいと思います。

2016年12月17日土曜日

MySQL「auto_increment」と「on duplicate key update」

 こんにちは、Hiroです。
 早速ですが、既にレコードが存在していればUPDATEをし、存在していなければINSERTをするという処理は、開発をしていると時々必要になってきますよね。
 MySQLの場合、そのような時に「on duplicate key update」を使うと便利ですが、ちょっとした注意点がありますので、今回はそのことをブログにしました。


「on duplicate key update」とは


MySQLの公式リファレンスより引用します。
ON DUPLICATE KEY UPDATE を指定したとき、UNIQUEインデックスまたはPRIMARY KEYに重複した値を発生させる行が
挿入された場合は、MySQLによって古い行のUPDATEが実行されます。
また、MySQLの固有の構文であり、PostgreSQLやOracleではまた別の構文で実現させる必要があります。


注意点!「auto_increment」の増加


上記の記述だけを読むと重複した行の場合は、UPDATEが実行されるとあるので、PRIMARY KEYが「auto_increment」であっても、UPDATE時は増加しないと思えますが、直感に反して、「on duplicate key update」を使った場合は、UPDATE(レコード数は増加しない)でも、auto_increment値が増加してしまします。
実行結果をもとに説明していきます。


まず、テーブルの説明つについて。
あるスポットのいいね数を管理(カウントアップ/ダウン)するためのテーブルを例にしたいと思います。idカラムがPRIMARY KEYで「auto_increment」として定義しています。mst_spot_idがスポットを特定するためのidで、UNIQUEインデックスとなっています。
mysql> desc spot_like_counts;
+----------------------------+-----------+------+-----+---------+----------------+
| Field                                | Type      | Null   | Key | Default | Extra             |
+----------------------------+-----------+------+-----+---------+----------------+
| id                                    | int(11)   | NO    | PRI  | NULL    | auto_increment |
| mst_spot_id                 | int(11)   | NO    | UNI | NULL    |                        |
| like_count                     | int(11)   | NO    |         | 0          |                       |
| created_at                    | datetime | NO   |         | NULL   |                       |
| updated_at                   | datetime | NO   |         | NULL   |                       |
+----------------------------+----------+-------+-----+---------+----------------+
何もINSERTされていな空の状態では、当然「auto_increment」値は「1」となっています。
mysql> SHOW TABLE STATUS LIKE 'spot_like_counts'\G
*************************** 1. row ***************************
                    Name: spot_like_counts
                   Engine: InnoDB
                 Version: 10
         Row_format: Dynamic
                     Rows: 0
 Avg_row_length: 0
         Data_length: 16384
Max_data_length: 0
        Index_length: 0
             Data_free: 0
  Auto_increment: 1                     ← ここです。
         Create_time: 2016-12-16 12:03:21
        Update_time: NULL
          Check_time: NULL
                Collation: utf8mb4_general_ci
             Checksum: NULL
    Create_options: 
              Comment: 
1 row in set (0.00 sec)
もう一度「INSERT...ON DUPLICATE KEY UPDATE」をするとmst_spot_idがUNIQUEインデックスとなっているため、既存のレコードが更新され、like_countが「2」となります。
mysql> INSERT INTO spot_like_counts(mst_spot_id, like_count, created_at, updated_at) values(1, 1, NOW(), NOW()) ON DUPLICATE KEY UPDATE like_count=like_count+1;
Query OK, 2 rows affected (0.02 sec)

mysql> SELECT * FROM spot_like_counts;
+----+----------------------------+-------------+---------------------+---------------------+
| id   | mst_spot_id                 | like_count | created_at          | updated_at          |
+----+----------------------------+-------------+---------------------+---------------------+
|  1   |                                     1 |                2 | 2016-12-16 12:08:03 | 2016-12-16 12:08:03 |
+----+----------------------------+-------------+---------------------+---------------------+
1 row in set (0.00 sec)
ここで、再度「auto_increment」値を確認するとレコードがINSERTされておらず、UPDATEされているにもかかわらず、 「auto_increment」値は1増加して、「3」となっています。
mysql> SHOW TABLE STATUS LIKE 'spot_like_counts'\G
*************************** 1. row ***************************
                    Name: spot_like_counts
                  Engine: InnoDB
                 Version: 10
         Row_format: Dynamic
                     Rows: 1
  Avg_row_length: 16384
         Data_length: 16384
 Max_data_length: 0
        Index_length: 0
             Data_free: 0
   Auto_increment: 3                     ← ここです。
         Create_time: 2016-12-16 12:03:21
        Update_time: 2016-12-16 12:21:50
          Check_time: NULL
               Collation: utf8mb4_general_ci
             Checksum: NULL
    Create_options: 
              Comment: 
1 row in set (0.00 sec)
ここで、mst_spot_idを1ではなく、2にしてINSERTを行うとidは「2」を飛ばして、「3」としてレコードがINSERTされます。
mysql> INSERT INTO spot_like_counts(mst_spot_id, like_count, created_at, updated_at) values(2, 1, NOW(), NOW()) ON DUPLICATE KEY UPDATE like_count=like_count+1;
Query OK, 1 row affected (0.02 sec)

mysql> SELECT * FROM spot_like_counts;
+----+----------------------------+--------------+---------------------+---------------------+
| id   | mst_spot_id                 | like_count  | created_at          | updated_at          |
+----+----------------------------+--------------+---------------------+---------------------+
|  1   |                                    1 |                 2 | 2016-12-16 12:08:03 | 2016-12-16 12:08:03 |
|  3   |                                    2 |                 1 | 2016-12-16 12:22:45 | 2016-12-16 12:22:45 |
+----+----------------------------+--------------+---------------------+---------------------+
2 rows in set (0.00 sec)

 SHOW TABLE STATUS LIKE 'spot_like_counts'\G
*************************** 1. row ***************************
                    Name: spot_like_counts
                  Engine: InnoDB
                 Version: 10
         Row_format: Dynamic
                     Rows: 2
  Avg_row_length: 8192
         Data_length: 16384
 Max_data_length: 0
        Index_length: 16384
             Data_free: 0
   Auto_increment: 4                     ← ここです。
         Create_time: 2016-12-16 12:03:21
        Update_time: 2016-12-16 12:22:45
          Check_time: NULL
               Collation: utf8mb4_general_ci
             Checksum: NULL
    Create_options: 
              Comment: 
1 row in set (0.00 sec)


まとめ


「on duplicate key update」を使うと「既にレコードが存在していればUPDATEをし、存在していなければINSERTをするという処理」が簡単に実現することができます。ただし、注意点としてはUPDATEであっても「auto_increment」値が増加し続けるということです。そのため、UPDATEが大量に発生するテーブルに対して利用すると「auto_increment」値が増加し続けてしまい、最大値まで達してしまう可能性があるということを認識して利用すべきでしょう。

次回は、Ruby on Railsで「on duplicate key update」を利用せずに、同じような機能を実現する方法を紹介したいと思います。

2016年12月16日金曜日

Alamofireのバージョン4以降でCookie付きの通信を行う

こんにちは、onukiです。

今回はAlamofireでCookie付きの通信を行う方法です。
Alamofireのバージョン4以降だと簡単にできるのでサクッと紹介します。

Alamofireのrequestメソッドにheadersが渡せるようになったので、
そこにCookie情報を渡せばそれだけでOKです。
例えば、headersは[String: String]形式なので、
["Cookie": "hoge"]の様な形で渡す感じです。
let cookie: [String: String] = ["Cookie": "hoge"]
Alamofire.request("https://~",
    method: .get,
    parameters: nil,
    encoding: JSONEncoding.default,
    headers: cookie).responseJSON {
        //処理
}

2016年12月15日木曜日

はじめの一歩 -Rails 時間・タイムゾーン編- その4

どうも、はじめです。

前回はさまざまな日時の取得方法について書いてみました。
「はじめの一歩 -Rails 時間・タイムゾーン編- その3」
今回はいくつかのタイムゾーンの紹介や
任意のタイムゾーンでの出力方法について書いてみます。

はじめに、

タイムゾーンの基準となる「UTC(協定世界時)」というものがあり、
タイムゾーンを表示した際に表示される「+0900」とは、
この「UTC」からの時差になります。

それではいくつかタイムゾーンを紹介していきます。

UTC (+0000)

こちらは先程も書いたとおり「協定世界時」と言って
世界の時間の基準となるものです。
スペインやポルトガルの時間を表示したい際にも用いられるそうです。
また、タイムゾーンを指定しなかった場合に
デフォルトで指定されているのも「UTC」になります。

JTC (+0900)

こちらは日本のタイムゾーンになります。
または、Asia/Tokyoと指定する事もできます。

KST (+0900)

こちらは韓国の標準時になります。
または、Asia/Seoulと指定する事もできます。

CST (+0600)

こちらはアメリカ・カナダ(中部標準時)になります。
または、America/Chicagoと指定する事もできます。

ちなみに、

中国の場合は文字列(UTCやJSTのようなもの)はなく、
+0800と指定するのが良さそうです。


任意のタイムゾーンでの取得

次に任意のタイムゾーンで時間を取得したい場合ですが、
文字列を指定する場合とUTCとの時差(+0900)を指定する場合があります。

文字列での指定はこのように行います。
Time.parse('2016-12-15 19:28:18 CST')
# => 2016-12-15 19:28:18 -0600

parseの場合はUTCからの時差で指定することもできます。
Time.parse('2016-12-15 19:28:18 -0600')
# => 2016-12-15 19:28:18 -0600

一時的にタイムゾーンを変更して取得を行いたい場合は、
以下のようにすることも可能です。
SYSTEM_TZ = ENV[‘TZ’]
ENV[’TZ’] => ‘CST’
Time.local('2016,12,15,19,28,18')
# => 2016-12-15 19:28:18 +0600
EVN[‘TZ’] = SYSTEM_TZ


任意のタイムゾーンでの表示

時刻の取得後に任意のタイムゾーンで表示をしたい場合は、
以下のように指定をすることで変更が可能となります。
time = Time.zone.now
# => Thu, 15 Dec 2016 19:28:18 JST +09:00
time.in_time_zone('UTC')
# => Thu, 15 Dec 2016 10:28:18 UTC +00:00

またはこのように指定することもできます。
time.in_time_zone('America/Chicago')
# => Thu, 15 Dec 2016 04:28:18 CST -06:00


最後に

UTCやJSTなどの文字列でタイムゾーンを指定する場合、
想定しているものとは違っていたり、
parseで指定したとしても反映されない場合等がありました。
明確に分かっているものならともかく確証がないものの場合は、
[Asia/Tkyo]のような文字列を使用するか[+0900]と時差指定の方が確実ではないかと思いました。

次回

月の始めや終わり、週の始めや終わりの取得方法を書いていきたいと思います。

2016年12月14日水曜日

2016年12月13日火曜日

RailsでLEFT OUTER JOINを頑張っていた結果、アソシエーションで解決した話

こんにちは、Taroです。
今回の趣旨は、無駄に難しく考える必要はないということです。(勝手に悩み、ドツボにはまっておりました。)
例えば、下記のようなテーブルがあったとします。
例としてビールメーカーで作成しましたが、現実のものとは全く関係ありません。(妻夫木くんストラップも本当にあるかは知りません。)
companies(メーカー管理)
id company_name
1 キリン
2 アサヒ
3 サントリー
4 サッポロ
products(各メーカーの商品管理)
id company_id product_name price
1 1 ラガービール 100
2 1 一番搾り 300
3 2 スーパードライ 200
4 3 プレミアムモルツ 300
5 3 モルツ 200
6 3 プレミアムモルツブラック 500
7 4 サッポロビール 200
campaigns(各メーカーの実施しているキャンペーン管理)
id company_id campaign_name started_at ended_at
1 1 缶ビール1本無料!! 2013-12-01 00:00:00 2014-01-01 00:00:00
2 1 缶ビール1本無料!!(復活) 2016-12-01 00:00:00 2017-01-01 00:00:00
3 4 妻夫木君ストラップ 2016-12-10 00:00:00 2016-12-21 00:00:00

モデルは簡単に作成します。
class Company < ApplicationRecord
  has_many :products
  has_many :campaigns
end

class Product < ApplicationRecord
  belongs_to :company
end

class Campaign < ApplicationRecord
  belongs_to :company
end
ここで、メーカーの一覧を取得する際に、紐づく製品、キャンペーンも取得したいと思います。
ただ、条件がありまして、productsからはpriceが500円以下(今回の例だと全てですが。。。)、campaignsからは今日開催しているものです。
製品はまだしも、キャンペーンはメーカーによっては開催していない場合もあります。
N+1問題を考慮しincludesで事前ロードしつつ、紐づくテーブルに条件を設定するため、下記の命令で実行しました。
Company.includes([:products,campaigns]).
where('products.price <= 500').
where('campaigns.started_at <= ?', Time.zone.now).
where('campaigns.ended_at <= ?', Time.zone.now).
references([:products, :campaigns])
発行されるSQLと結果はこちらになります。
SELECT "companies"."id" AS t0_r0,
  "companies"."company_name" AS t0_r1,
  "companies"."created_at" AS t0_r2,
  "companies"."updated_at" AS t0_r3,
  "products"."id" AS t1_r0,
  "products"."company_id" AS t1_r1,
  "products"."product_name" AS t1_r2,
  "products"."price" AS t1_r3,
  "products"."created_at" AS t1_r4,
  "products"."updated_at" AS t1_r5,
  "campaigns"."id" AS t2_r0,
  "campaigns"."company_id" AS t2_r1,
  "campaigns"."campaign_name" AS t2_r2,
  "campaigns"."started_at" AS t2_r3,
  "campaigns"."ended_at" AS t2_r4,
  "campaigns"."created_at" AS t2_r5,
  "campaigns"."updated_at" AS t2_r6
FROM "companies"
LEFT OUTER JOIN "products" ON "products"."company_id" = "companies"."id"
LEFT OUTER JOIN "campaigns" ON "campaigns"."company_id" = "companies"."id"
WHERE (products.price <= 500)
  AND (campaigns.started_at <= '2016-12-12 11:22:12.523389')
  AND (campaigns.ended_at <= '2016-12-12 11:22:12.534654');

#<ActiveRecord::Relation [#<Company id: 1, company_name: "キリン", created_at: "2016-12-12 00:00:00", updated_at: "2016-12-12 00:00:00">]>
おかしい。。。キリンしか取得できない。。。
発行したSQLを確認すると、JOINした後に条件指定しております。
ON句の中に入れられないかと思い、路頭に迷っているところで、下記のようにモデルを書き換えました。
class Company < ApplicationRecord
  has_many :products
  has_many :products_price_under_500, -> { where('products.price <= 500') }, class_name: 'Product'
  has_many :campaigns
  has_many :campaigns_published, -> { where('campaigns.started_at <= ?', Time.zone.now).where('campaigns.ended_at <= ?', Time.zone.now) }, class_name: 'Campaign'
end
# ProductとCampaignは変わらないので省略。
アソシエーションの際に条件指定して取得するという、非常に基本的なところを忘れておりました。
一応、条件指定したくないケースもあると思うので、デフォルトとは別でつくりました。
これで書くと、先ほどのクエリも、短くなります。
Company.includes([:products_price_under_500,:campaigns_published])
SELECT "companies".* FROM "companies";
SELECT "products".* FROM "products" WHERE (products.price <= 500) AND "products"."company_id" IN (1, 2, 3, 4);
SELECT "campaigns".* FROM "campaigns" WHERE (campaigns.started_at <= '2016-12-12 11:30:03.468617')
    AND (campaigns.ended_at <= '2016-12-12 11:30:03.469799') AND "campaigns"."company_id" IN (1, 2, 3, 4);

#<ActiveRecord::Relation [#<Company id: 1, company_name: "キリン", created_at: "2016-12-12 00:00:00", updated_at: "2016-12-12 00:00:00">, 
#<Company id: 2, company_name: "アサヒ", created_at:-12-12 00:00:00", updated_at: "2016-12-12 00:00:00">, 
#<Company id: 3, company_name: "サントリー", created_at: "2016-12-12 00:00:00", updated_at: "2016-12-12 00:00:00">, 
#<Company id: 4, compane: "サッポロ", created_at: "2016-12-12 00:00:00", updated_at: "2016-12-12 00:00:00">]>
発行されるSQLは3本ですが、N+1問題を回避しつつ、条件指定できているかと思います。
もし、同じようなことをやられている方がいらっしゃいましたら、ぜひ参考にしてください。(反面教師的な意味でも。)

2016年12月12日月曜日

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

 こんにちは、Hiroです。本日は、Ruby on Rails上であればちょっと便利なメソッドをObjectクラスに追加する方法を紹介します。
 皆さんもObjectを数値(Integer)に変換する「to_i」や文字列(String)に変換する「to_s」などは使ったことがあると思います。(下記のように使います。)
[1] pry(main)> "1".to_i
=> 1
[2] pry(main)> 1.to_s
=> "1"

 今回は、あまり使用頻度は高くはないのですが、Objectを論理値(Boolean)に変換するためのメソッド「to_b」というのを実装してみようと思います。


1. to_bメソッドの使い方

まずは、いきなり使い方からですが、下記に例を記載します。APIでJSONが送信されてくるような時に、params[:spot_request]に文字列で"true"または、"false"が入っていて、"true"なのか"false"なのかで処理を分ける場合のものです。
def create
  if params[:spot_request].to_b
      # TODO: 申請処理をする
  else
      # TODO: 通常の処理をする
  end
end


2. to_bメソッドの実装内容

to_bメソッドですが、今回は文字列・数値・シンボルなどから変換できるようにしたいと思います。下記のような形で、「lib/extensions/object/to_b.rb」として保存します。(trueとしたいものが他にあれば、追加してみるのもよいと思います。)
class Object
  def to_b
    target_value = self.class == String ? self.downcase : self
    case target_value
    when 'yes', 'true', 'ok', '1', :yes, :true, :ok, 1, true
      true
    else
      false
    end
  end
end


3. to_bメソッドがアプリケーション内で利用できるように読み込ませる

to_bメソッドをcontrollerやmodel内で利用できるようにto_b.rbを読み込ませる必要があります。「config/initializers」配下に新しいファイルを作っても構いませんが、今回は「config/application.rb」のファイルの末尾に下記を追加します。
Dir[Rails.root.join('lib/extensions/**/*.rb')].sort.each do |file|
  require file
end


4. to_bメソッドを実際に使ってみる

これで、to_bメソッドが使えるようになりましたので、簡単に実行結果をのせておきます。
[1] pry(main)> "true".to_b
=> true
[2] pry(main)> "false".to_b
=> false
[3] pry(main)> 1.to_b
=> true
[4] pry(main)> 0.to_b
=> false
[5] pry(main)> :true.to_b
=> true
[6] pry(main)> :false.to_b
=> false

 いかがでしたでしょうか。今回は単純なメソッドをObjectクラスに追加してみましたが、同じような方法で既存のクラスにメソッドを追加したりすることができますので、ぜひ色々と試してみてください。

2016年12月11日日曜日

はじめの一歩 -Rails 時間・タイムゾーン編- その3

どうもはじめです。

前回は時間に関するクラスである
「Time,Date,DateTime,TimeWithZone」の違い
について書いてみました。

はじめの一歩 -Rails 時間・タイムゾーン編- その2
今回は様々な日時の取得方法を見ていきます。
例としてTimeクラスとTimeWithZoneクラスを使用していきます。


Time

前回までも使用していますが、まずは現在時刻の取得から見ていきます。
Time.now
# => 2016-12-11 13:19:53 +0900

次に任意の時間を設定します。
Time.gmではUTCで時刻を設定し、
環境変数で設定しているタイムゾーンで時刻を設定したい場合は
Time.localを使用します。
Time.gm('2016,12,11,13,19,53')
# => 2016-12-11 13:19:53 UTC
Time.local('2016,12,11,13,19,53')
# => 2016-12-11 13:19:53 +0900

Timeクラスでは任意の秒数を加算減算して、
指定時刻からの数秒後、数秒前を取得することができます。
1日前、1日後を取得する場合
time = Time.now
# => 2016-12-11 13:19:53 +0900
time - 24 * 60 * 60
# => 2016-12-10 13:19:53 +0900
time + 24 * 60 * 60
# => 2016-12-12 13:19:53 +0900

文字列への変換は以下のように行います。
time = Time.now
time.to_s
# => "2016-12-11 13:19:53 +0900”

文字列からの変換を行うには
require 'time'
を記述する必要があります。
require 'time'
Time.parse('2016-12-11 13:19:53 JST')
# => 2016-12-11 13:19:53 +0900

unixtimeへの変換とunixtimeからの変換は以下のように行います。
time = Time.now
time.to_i
# => 1481429993
Time.at(1481429993)
# => 2016-12-11 13:19:53 +0900

Timeクラスには2038年問題がありましたが、
現在はすでに解消されているそうです。


TimeWithZone

現在時刻の取得は以下のように行います。
Time.zone.now
# => Sun, 11 Dec 2016 17:23:24 JST +09:00
Time.current
# => Sun, 11 Dec 2016 17:23:24 JST +09:00
Time.zone.nowまたはTime.currentで取得する時刻は
application.rbに設定したタイムゾーンを使用します。

任意の時間設定は以下のように行います。
Time.zone.parse('Sun, 11 Dec 2016 17:23:24 JST +09:00')
# => Sun, 11 Dec 2016 17:23:24 JST +09:00

次にさまざまの時刻を取得する方法です。
昨日明日
time = Time.zone.now
time.yesterday
# => Sat, 10 Dec 2016 17:23:24 JST +09:00
time.tomorrow
# => Mon, 12 Dec 2016 17:23:24 JST +09:00
先月来月
time = Time.zone.now
time.prev_month
# => Fri, 11 Nov 2016 17:23:24 JST +09:00
time.next_month
# => Wed, 11 Jan 2017 17:23:24 JST +09:00
去年来年
time = Time.zone.now
time.prev_year
# => Fri, 11 Dec 2015 17:23:24 JST +09:00
time.next_year
# => Mon, 11 Dec 2017 17:23:24 JST +09:00
その他にも以下のように表現することができます。
例として2日前と3年後を表示してみます。
time.ago(2.days)
# => Fri, 09 Dec 2016 17:23:24 JST +09:00
time.since(3.years)
# => Wed, 11 Dec 2019 17:23:24 JST +09:00

文字列への変換は以下のように行います。
time = Time.zone.now
time.to_s
# => "2016-12-11 17:23:24 +0900”

unixtimeへの変換とunixtimeからの変換
time = Time.zone.now
time.ti_s
# => 1481444604
Time.zone.parse(1481444604)
# => 2016-12-11 17:23:24 +0900

まとめ

TimeWithZoneではyesterdayやtomorrowなど 直感的に記述することができますが、
Timeクラスでも
require ‘active_support/all’
と記述することでTimeWithZoneと同様に
翌日や前日等の日時を表現することができます。

次回

次回はいくつかのタイムゾーンの紹介や
任意のタイムゾーンでの出力等ついて書いてみようと思います。

2016年12月10日土曜日

JQueryの.onメソッドについて

こんにちは、Taroです。
既に多くの方がご存知のことかと思いますが、JQuery 1.7以降、イベント定義では.onメソッドを使用することが主流なようです。
恥ずかしながら、私は昨日まで、下記のように書いておりました。
そのため、本日はこの.onメソッドについて調べてみたいと思います。
使用するJQueryのバージョンは1.12.4になります。
<div id="click" style="background-color: #cc0000; width: 50px; height: 50px;"></div>
<script>
  $(function() {
    $('#click').click(function() {
      $(this).css('background-color', '#00ff00');
    });
  });
</script>
それではこちらを.onメソッドで書き換えるとどうなるのでしょうか?
<script>
  $(function() {
    $('#click').on('click', function() {
      $(this).css('background-color', '#00ff00');
    });
  });
</script>
あまり変わらなく、逆にclickの方が直感的かも。。。
ただ、この.onメソッドの偉いところは、複数のイベントに対して定義ができることです。
$(function() {
  var change_color_flg = true;
  $('#click').on('click mouseenter', function() {
    if (change_color_flg) {
      $(this).css('background-color', '#00ff00');
      change_color_flg = false;
    } else {
      $(this).css('background-color', '#0000ff');
      change_color_flg = true;
    }
  });
});
これで、クリックしたときとマウスオーバーしたとき両方で色を変えることができます。 また、各イベントに対してハンドラを定義することもできます。 その際は、マップ(連想配列)で渡すようです。

$('#click').on({
  // クリックしたとき
  'click': function() {
    $(this).css('background-color', '#ff7fbf');
  },
  // マウスオーバーしたとき
  'mouseenter': function() {
      $(this).css('background-color', '#7fffff');
  },
  // マウスアウトしたとき
  'mouseleave': function() {
    $(this).css('background-color', '#bfff7f');
  }
});
これは見やすくなりました。
他にも便利な使い方がないかと、JQueryの日本語ドキュメントをながめてみました。
http://js.studio-kingdom.com/jquery/events/on

実は.onメソッドには4つの引数を与えられるようです。

  • 第一引数:イベント
  • 第二引数:セレクタ
  • 第三引数:ハンドラに渡したいdata
  • 第四引数:ハンドラ

第二引数と第三引数は省略可能です。
そのため、上述したように、イベントとハンドラだけでも動きます。
ただ、折角なので、使用してみたいと思います。

あとから追加した要素にイベントを定義する


親要素(#click)を起点として、子要素を監視させます。(以前はdelegete()で管理していたやつです。)
普通は後から追加した要素にイベントハンドラは実行されません。

<div id="click">
  <button>初めからある</button>
</div>
<script>
$('#click').on('click', 'button', function() {
  $(this).after("<button>新しいボタンです</button>");
});
</script>

イベントハンドラにデータを渡す



$('#click').on('click', {value: 'Hello'}, function(event) {
  alert(event.data.value);
});

おわりに


非常に簡単ではありますが、JQueryの.onメソッドについてまとめてみました。
今後もJavaScriptやJQueryについて学習したことを記述していきたいと思います。

2016年12月9日金曜日

「心の時代」にモノを売る方法


 こんにちは、Hiroです。今回は、ドトール会で発表した本に関してのブログになります。この本は、2012年11月に出版された本です。
 ちょうどその前後に、CRM(Customer Relationship Management)のWebサービスを開発をしていて、この本以外にも「小阪裕司」さんの本はよく読み返し、開発の参考にしたものです。

 さて、この本なのですが、消費社会の変化の中でどのようにモノを売るのか、そのためにはどのような能力を磨くべきなのかが事例をもとに書かれています。各章での概要を記載しますので、ぜひ参考にして興味があれば読んでみてください。

1. 変わりゆく消費社会とたったひとつの欲求

 この章では、時代の変化とともに、消費社会が変化している歴史について書かれています。
  1. 耐乏生活期
  2. モダン消費期(高度経済成長期)
  3. ポストモダン消費期
  4. ネオポストモダン消費期(縮む消費と選ぶ消費)
 モダン消費期は、「生産と分配の経済」であり、共通の需要の概念であありまsた。一方で、現在はモノが満たされ、需要が共通的ではなく、1回ごとの試みによって(お客様が喜ばれるかどうかが)模索される消費の時代となっています。例で食の話がでてきているのですが、「胃袋から舌へ、そして頭へ」という言葉がわかりやすいのではないかと思います。空腹を満たすための食から、味を楽しむ食となり、そしてワインに代表されるような頭で楽しむ食へと移り変わっていると小阪氏は説明されています。

2. なぜ、この「現実」が見えなくなるのか

 2章では、1章のように消費活動が大きく変化しているにもかかわらず、この現実が見えにくくなっている理由を説明しています。大きく3点に絞られて説明されています。
  1. 人は見ようとするものしか見えない(メンタルモデルとしての思い込み)
  2. 複雑すぎて見えない(局所的な解しか理解できない)
  3. 変化が大きすぎて見えない(受け入れがたい状況で防御的になる)
 この内容は、この消費社会が見えないということだけに関わらず、人が陥りやすい状況を説明していて、自分も気をつけなければならないと気づかされる内容でした。

3. 売れない三重の問題とビジネスの未来

 3章では、なぜ売れないのかを3つに絞って説明しています。
  1. そもそも伝えるべき価値を伝えるべき相手に伝えていない
  2. 心の豊かさと毎日の精神的充足感の面から捉え直していない
  3. モノをコトとして見られていない

4. 新しいビジネス6つの整理要件

 4章では、新旧でのビジネス要件を対比させながら新しいビジネスでの要件を説明しています。
  1. 旧:価格訴求         - 新:価値創造
  2. 旧:商品力(感性価値)    - 新:商品力(コストパフォーマンス)
  3. 旧:顧客コミニュニティの育成 - 新:人口と立地
  4. 旧:新基準の品揃え      - 新:豊富な品揃え
  5. 旧:もてなしとしつらえ    - 新:ローコストオペレーション
  6. 旧:人材育成(感性と実践知) - 新:標準化とマニュアル化

まとめ

 この本を読んで、所有物の移転から体験の取引へという言葉が一番印象的でした。マイカーやマイホームがほしいという所有したいという欲求をおして、モノを販売するではなく、体験つまりコトを提案して、結果としてモノが売れるという考えです。もちろん、全てが上記のような消費社会となっているのではなく、現在は旧来の消費社会と新しい消費社会が併存している社会であると小阪氏も言われています。
 今後のビジネスを考えていく上で、一般的にはなりつつある考えかもしれませんが、改めてこの本を一読してみると新しい発見があると思います。

2016年12月8日木曜日

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

どうも、はじめです。

前回は指定したフォーマットで時刻を表示するstrftimeについて書いてみました。
「はじめの一歩 -Rails 時間・タイムゾーン編-」
今回は日付や時間に関するクラスである
こちらの4つについて書いていきます。
「Time、Date、DateTime、TimeWithZone」

はじめに、

現在時刻の取得をした後、strftime()で「年月日、時刻、曜日、タイムゾーン」を表示して
それぞれの違いを見ていきます。


Time

こちらはRubyで用意されている時刻を表すクラスです。
現在時刻の取得、表示はこのように行います。
time = Time.now
# => 2016-12-07 21:28:39 +0900
time.strftime('%Y * %m * %d * %w * %U * %X * %Z')
# => "2016 * 12 * 07 * 3 * 49 * 21:28:39 * JST"
年月日、時分秒、曜日、タイムゾーンなどの情報を表示することができました。


Date

こちらはRubyで用意されている日付を表すクラスです。
使用する際は
require ‘date’
と記述をする必要があります。
現在時刻の取得、表示はこのように行います。
require ‘date'
date = Date.today
date.strftime('%Y * %m * %d * %w * %U * %X * %Z')
# => "2016 * 12 * 07 * 3 * 49 * 00:00:00 * +00:00"
Timeとは違って年月日、曜日のみ表示されました。
その他の時分秒、タイムゾーンの情報は全て0で表示されています。


DateTime

こちらは日付と時刻を扱えるDateのサブクラスです。
DateTimeもDateクラスと同様に使用する際には
require ‘date’
と記述する必要があります。
現在時刻の取得、表示はこのように行います。
requrire ‘date’
datetime = DateTime.now
datetime.strftime('%Y * %m * %d * %w * %U * %X * %Z')
# => "2016 * 12 * 07 * 3 * 49 * 21:28:39 * +09:00”
Timeクラスと同じように年月日、時分秒、曜日の情報が表示されましたが、
タイムゾーンに関しては表示が違います。
Timeクラスでは”JST"と文字列で表示されていますが、
DateTimeクラスの場合は”+09:00”とUTC(協定世界時)からの時差が表示されています。


TimeWithZone

こちらはRailsで使用することができるActiveSupport::TimeWithZoneクラスです。
素のRubyでは使用することはできません。
TimeWithZoneクラスでの現在時刻の取得、表示は以下のようになります。
timewithzone = Time.zone.now
timewithzone.strftime('%Y * %m * %d * %w * %U * %X * %Z’)
# => "2016 * 12 * 07 * 3 * 49 * 21:28:39 * JST”
こちらはTimeクラスと全く同じ表示になりました。

まとめ

Time、DateTime、TimeWithZoneの3つに関しては、
大きな違いはないように見えます。

調べてみたところTime、DateTimeの違いとしては以下の点があげられるようです。

Timeの場合、
・require無しで使える
・タイムゾーンの指定なしでインスタンスの生成を行った時、システムまたは環境変数で設定しているタイムゾーンが使用される
(DateTimeの場合はUTCが使用される)
・うるう秒を使える
・サマータイムを使える

ということからDateTimeよりTimeを使用したほうが良いとのことです。

Timeに対してTimeWithZoneはシステムや環境変数ではなく、
application.rbに設定したタイムゾーンが使用されるようです。
また、"Time.current"でタイムゾーンを指定して時刻の取得を行うこともできます。


つまり。。。
Time、DateTime、TimeWithZoneの全てが使える環境であれば、
タイムゾーンを柔軟に扱えるTimeWithZoneを使用し、
そうでない環境である場合はTimeを使用する形になるのではないかと思います。


次回

前回から今回にかけて現在時刻を取得して比較をしていましたが、
次回は翌日や翌年、時刻の指定やUnixTimeの変換等について
書いてみようと思います。

2016年12月7日水曜日

Swift3でMapKitの吹き出し(Callout)タップを取得したい!

こんにちは、onukiです。

今回、iOSのMapKitで吹き出しのタップを取得しようと思ったのですが、
これが案外簡単にいかなかったというか無理矢理やりました。(笑)

MKMapViewDelegateに以下のメソッドが用意されています
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
    //処理
}
ただ、こいつは基本的にMKAnnotationViewの
「leftCalloutAccessoryView」か「rightCalloutAccessoryView」にUIButton等のUIControlが取得できるものを配置し、
そして、そのボタンを押すなどした時に、このメソッドが呼ばれます。
しかし、今回やりたいことはGoogleMapみたいに吹き出しのどこでもタップできるようにしたいという事。
(というか吹き出しにボタンを配置したくない。。。)

というわけで結論を早々に述べるとMKAnnotationViewに直接UITapGestureRecognizerを設定します。
MapKitはピンを刺すときにMKAnnotationViewを使用していますがピン部分だけではなく
吹き出し(Callout)部分もSubViewではなくピンと一緒にMKAnnotationViewとして扱われています。
なので以下のようにすると吹き出し部分のタップも取得できます。
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hoge.tapGesture(gestureRecognizer:)))
    view.addGestureRecognizer(tapGesture)
}
これで吹き出し部分のタップが取得できます!

ですが、このままだとピン画像(MKAnnotationView.image)の部分もタップできてしまうので、
タップ処理側で以下のような条件を付けます。
func tapGesture(gestureRecognizer: UITapGestureRecognizer){
    let view = gestureRecognizer.view
    let tapPoint = gestureRecognizer.location(in: view)
    //ピン部分のタップだったらリターン
    if tapPoint.x >= 0 && tapPoint.y >= 0 {
        return
    }
    //処理
}

なぜこれでいけるかというと、MKAnnotationView自体のフレームはピン画像部分のみで
吹き出し部分はMKAnnotationViewのフレーム外に描画されています。

以上、だいぶ無理矢理タップ判定しましたが
これでGoogleMapと同じく吹き出しのどこをタップされても取得することができます。
(もっとスマートなやり方ご存知の方、ご教授いただけると幸いです)

2016年12月6日火曜日

幸せになる勇気

「幸せになる勇気」の画像検索結果

今回は「嫌われる勇気」の続編となる「幸せになる勇気」
前作でアドラー心理学を学んだ青年が実際に実行してみた結果を哲人に報告するところから始まります。

本書は5部で構成されている

第一部:悪いあの人、かわいそうな私

第二部:なぜ「賞罰」を否定するのか

第三部:競争原理から協力原理へ

第四部:与えよ、さらば与えられん

第五部:愛する人生を選べ


本書の中で個人的に関心が高かったのは
「問題行動を5つの段階に分けて考える」
いかなる目的を持って問題行動にでるのかを5段階に分けて考えることで、エスカレートする前に対策を講じることができるというもの。
どの環境にいても問題行動を起こす人は存在する。
この章を読んでいるときに、問題行動を起こす人がどの段階にいるのか、いたのか、それを答えあわせしてる感覚となった。

【Git】マージコミットを消したい

こんにちは、Taroです。
今回はGitのケーススタディ的なものを書きたいと思います。
反面教師として利用してください。

例えば、下図のようなことってないでしょうか?


同じ開発ブランチで、既にリモートブランチが進んでいると、「プルしなさい」と怒られてしまいます。
ここで「git pull --rebase」を使用すれば、マージコミットもできず、綺麗にリモートを取り込めます。
「git pull」と「git pull --rebase」の違いはこちらのサイトで紹介されております。

ただ、頭が弱い私はrebaseオプションを付け忘れ、ふつうにpullしてしまいました。
そうすると、下図のようなブランチになります。

このマージコミットが非常に邪魔です。
コミットを巻き戻すときには「reset」や「revert」が使用されるかと思います。
「revert」だと「消したというログができてしまうため、「reset」を使用します。
「reset」にも大きく2種類あり、コミットと一緒に変更したファイルまで消してしまうhardオプションと、変更したファイルは残しておくsoftオプションがあります。
ケースバイケースで使い分けるとして、今回はsoftオプションでコミットだけ消し、rebaseしたいと思います。

# マージコミットをなかったことに
$ git reset --soft
# 自分のコミットをなかったことに
$ git reset --soft
# 1回stashで逃がす
$ git stash save "テンポラリ"
# rebaseして綺麗な状態に
$ git rebase origin/ブランチ名
# stashした差分を取り込む
$ git stash pop stash@{0}
「reset」を2回やって巻き戻していますが、1回でも良いかと思います。
これで、Bさんはローカルブランチを修復できましたとさ。
めでたしめでたし。
(このようなケースでもっとスマートな方法があれば、ぜひ教えてください!!)





2016年12月5日月曜日

新型MacBook Pro(Late 2016)使用レビュー

こんにちは。h_ono_222です。
先日、新型MacBook Pro (15-inch, Late 2016) 15 inchモデルが届きましたので、3日間使ってみた感想を軽くまとめようと思います。

1. 仕様

購入したモデルのスペックは下記の通りです。

ディスプレイ15.4インチ(2880 × 1800)
プロセッサ2.7GHz Core i7(4コア)
グラフィックRadeon Pro 455(2GB GDD)
メモリ16GB
ストレージ512GB
重量1.83kg

2. サイズ比較

下の写真はMacBook Pro (13-inch, Mid 2012)モデルとの比較です。


1枚目の写真の通り、2012年の13インチ版(非Retina)とのサイズの差はわずかで、開封した時は間違って13インチ版が送られてきたのかと勘違いしました。

また、厚みも非常に薄く重量的にもなんとか持ち運びできます。
(ちなみに前モデルの重量は13インチ版で1.58kg、15インチ版で2.04kg)

3. Touch Bar

スリープから復帰した際など、Touch Barの右端をタッチするだけでパスワード入力なしにロックを解除できます。


下の画像はSafari、Xcode(実行中)、Finder(未選択)、Finder(表示切り替え)画面でのTouch Barの表示です。


基本的には、画面内のボタンと同じアイコンが表示されています。
まだ使い始めて間もないので、Touch Bar?フーン…という感じ(音量操作などは2タップしなければならないので若干面倒)なので、今後のアップデートに期待です。

4. 気になる点

現状殆ど不満はありませんが、一点気になることはキーの打鍵感です。
ストロークが浅く、パコパコとした感覚で、音も大きい?です。

5. おまけ

下記に13インチ版と15インチ版の簡易な比較を載せました。少しでも購入を検討されている方の参考になれば幸いです。

13インチ版15インチ版
ディスプレイ13.3inch15.4inch
プロセッサ2コア(低電圧版)4コア
グラフィックスCPU統合Radeon Pro(+CPU統合)
重量1.37kg1.83kg

13インチ版と15インチ版とで大きく異なるのはディスプレイ、プロセッサ、グラフィックだと思います。
ディスプレイは言わずもがななので割愛します。プロセッサは13インチモデルは低電圧版となります。
ですので、13インチ版のカスタマイズ(特にCPU)をするくらいなら、思い切って15インチ版を購入することをお勧めします。

ちなみに、15インチ版の最下位モデルのCPUの共通L3キャッシュは6MBで15インチ版の他のモデルと比べて2MB少なくなっています(体感速度は変わらないかもしれませんが一応)

また、最新の第7世代であるKaby Lakeではなくその前のSkylake(13インチ版はBroadwell)です。
現状、不具合もあるようですし、待てる人はマイナーチェンジ版を待つのもいいと思います。

6. 参考

2016年12月4日日曜日

はじめの一歩 -Rails 時間・タイムゾーン編-

どうも、はじめです。
本日から「はじめの一歩 -時間・タイムゾーン編」ということで、
何度かに分けてRailsの時間・タイムゾーンについての記事を連載していきます。

今回の内容は「strftime」についてです。

はじめに

strftimeとは、

Rubyの組み込みオブジェクトとして用意されており、
時刻を指定したフォーマットに沿って文字列へ変換してくれるものになります。


time = Time.now # Sun Dec 03 16:06:18 +0900 2016
time.strftime('%Y年%m月%d日 %H:%M:%S')
# => 2016年12月04日 16:06:18

それではフォーマットについて見ていきたいと思います。
今回は例としてTime型での実行結果をまとめてみました。

フォーマット


フォーマット説明実行結果
%Y西暦を表す数 2016
%y西暦の下2桁16
%m月を表す数字(01-12)12
%B月の名称(January,February,March,April,May,June,July,August,September,October,November,December)December
%b月の省略名(Jan,Feb,Mar,Aprl,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)Dec
%d日(01-31)04
%x日付 ('%m/%d/%y')と同じ意味になります12/04/16
%j年中の通算日(001-366)339
曜日
%A曜日の名称(Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday)Sunday
%a曜日の略称(Sun,Mon,Tue,Wed,Thu,Fri,Sat)Sun
%w曜日を表す数。日曜日が0(0-6)0
%U週を表す数。最初の日曜日が第1週の始まり(00-53)49
%W週を表す数。最初の月曜日が第1週の始まり(00-53)48
%H24時間制の時(00-23)16
%I12時間制の時(01-12)04
%M分(00-59)06
%S週秒(00-60)(60はうるう秒)18
その他
%c日付と時間"Sun Dec 4 16:06:18 2016"
%p午前または午後(AM,PM)PM
%X時刻"16:06:18"
%ZタイムゾーンJST
%%パーセント%

下記はオプション一覧です。


オプション
フォーマット説明
^大文字で出力
#小文字は大文字に、大文字は小文字に変換
-左寄せ
_空白埋め
00埋め
数値指定した表示桁数での表示

オプションを幾つか組み合わせて実行した結果がこちらになります。
桁数指定をしている状態で左寄せを行うと通常と同じ表示になるようです。

time.strftime('%^10A') # => "    SUNDAY"
time.strftime('%0#6Z') # => "000jst"
time.strftime('%Y/%-m/%-d %X') # => "2017/1/4 16:06:18"
ちなみに、、、
曜日を日本語で表示したいときは以下のように書きます。
Time.now.strftime("#{%w(日 月 火 水 木 金 土)[Time.now.wday]}")
# => 日

まとめ


年月日、曜日、週に関してはTime型、Date型、DateTime型、TimeWithZone型のどれであっても
表示される内容は同じでした。
ですが、時刻やその他のフォーマットに関してはデータ型によって出力内容が異なってきます。
実際に使用する際には、一度想定通りに表示されているかを確認してから使用する用にしましょう。
(今回の時刻、その他の項目に表示されている実行結果はTimeWithZone型となっております。)

次回


今回の最後に出てきましたデータ型について、
時刻だけでも幾つかのデータ型がありますのでそちらをやってみようと思います。

2016年12月3日土曜日

2016年12月2日金曜日

「実行の4つの規律」について考えてみた

こんにちは、Taroです。
私の中で、昨年のブックオブイヤーは「7つの習慣」でした。
(あの当時で今更感はありましたが。。。)
その「7つの習慣」を組織で実行するテーマの「4つの規律」という本が出たようなので、今回題材にしました。

そもそも4つの規律って?

  1. 最重要目標にフォーカスする
  2. 先行指標に基づいて行動する
  3. 行動を促すスコアボードをつける
  4. アカウンタビリティのリズムを生み出す
分かるような分からないような。。。
本に記載されている内容を一部引用しながら、例としてあげると、
  • あるプランニング会社が、今年度の売上を1億円から2億円にあげることを決めた。(最重要目標
  • これを受けて、その目標を達成するためには、高いプランを選択してれる顧客を20 %増やす必要があった。(遅行指標
  • 20 %増やすために、1人1人の目標として「高いプランを勧める営業を毎日2件行うこと」とに決めた。(先行指標
  • 各人が見える場所に、目標と実績を分かりやすい図で掲示した。(スコアボード
  • 毎週必ず1回、この件に関するMTGを行い、進捗と先行指標の修正を行った。(アカウンタビリティ
遅行指標とは、先行指標を行った上での結果と書かれています。
例えば、車が故障する頻度を下げる、という目標は私たちでは関与できません。ただ、車を整備に出すことは私たちの意思で決めることができます。

じゃあ4つの規律でどんな組織を作りたいの?


本にはいろいろ記載がありましたが、私が考えるに、ボトムアップ型の組織だと思っています。
ある程度目標が明確になれば、主体的に考え、意見なども言うことができるようになるかと思います。
本の中で印象的であったのは、レガッタとラフティングの話です。
高度経済成長期のような変化が(今に比べて)乏しいときは、リーダーに合わせて漕ぐことが重要です。
ただ、現代のような、何が起こるかよく分からないような時代では、リーダーはいるにしろ、個々人で判断する機会が増えます。
組織的な動きは当然として、各々でも考えることができる組織を作りたいのかな、と思っています。

技術屋にとっての4つの規律

一概に技術屋といっても様々な職種が存在します。
今回は「製品、サービスを完成させ、納品すること」が業務上の目標である場合を想定します。

  • 最重要目標:自部署、自チームの売り上げアップ(期間と増加幅は省略)
  • 遅行指標:ある製品を納品すること
  • 先行指標:製品の工程を週単位で分割できるところまで落とし込み、その工程が終了したかどうかを指標とする
製品を納品することは、間違いなく売上に貢献できることだと思います。
とはいえ、これは結果なので、コミットできる先行指標が必要です。
これはあくまで私の考えですが、製品の工程を分割して、指標とすれば良いのかな、と思います。
1週間で2つの工程を完了させることを各々のコミットメントとし、スコアボードを作ります。
幸い、そのようなタスク管理系のアプリケーションはたくさんありますし、エクセルで簡単な図を作っても良いかもしれません。
そして、週1回のMTGで進捗報告、および案外手間がかかりそうな工程に関して共有、軌道修正を実施していきます。
あくまで私が考えた一例ですが、漫然といついつまでに完成させる。。。と考えるよりもメリハリがつくのではないかな、と思います。

4つの規律よりも7つの習慣?

4つの規律はどちらかというと管理職、マネジメント職の方向けの本という印象です。
組織の中にいかにルールを作るか、という感じですが、個人向けというと7つの習慣の方がしっくりくるかもしれません。

本の中でも、ビジネスパーソンは木である。根っこに人格(7つの習慣)があって、その上に組織の規律(4つの規律)という幹があり、スキルという葉っぱを茂らせているようです。
良い機会なので、7つの習慣も再読し、歯をみがくかのごとく、良い習慣を根付かせていきたいと思います。

2016年11月28日月曜日

RailsでERBからJavaScriptに変数の値を渡す方法

このエントリーはWeb開発初級者向けです。

JavaScriptでViewを動的に更新したい場合、Controllerで定義したインスタンス変数の値などをJavaScript側に渡したい場合があると思います。

数値や文字列の場合はhiddenフィールドなどに出力していますが、Hashの場合はそのまま出力してもJavaScript側で取得すると文字列になり、そのままでは使えません。

Hashを受け渡したい場合は、事前にHashを.to_jsonでJSON化し、JavaScript側でparseJSONしても良いのですが、
下記のようにカスタムデータ属性を使うと、そのままHashとしてアクセスできます。

変数
@test = { 'hoge' => 'huga', 'hogehoge' => 'hugahuga' }

ERB
<div id="hoge" data-hoge-id="<%= @test.to_json %>"></div>

JavaScript
var test = $('#hoge').data('hoge-id');

実行結果
test
Object {hoge: "huga", hogehoge: "hugahuga"}

「嫌われる勇気」=「他人はどうでもいい」ではない


今回の書籍は、2013年頃にかなり話題になっていたらしい(私は全く知りませんでした…)、「嫌われる勇気」です。
通して読んでみて感じたのは「とても有益な内容だけど、拾い読みやつまみ食いは危ない」でした。
少しでも本書に興味をお持ちの方は、途中を読み飛ばさず最後まで通読することをおすすめします。

というのも、文中にアドラー心理学のキーワードがいくつか出てきますが、分かりやすいスローガンや指針ではありません。
その言葉の意味するところを理解しないまま行動に移すと、むしろ痛い目を見るのでは…と思います。

例えば、本のタイトルである「嫌われる勇気」ですが、「他人なんてどうでもいいから嫌われても構わない」という論旨では全くありません
対人関係を築く上で大切なのは「他者貢献」と「他者信頼」、そして「自己受容」だと説いています。
他にも「承認欲求は不要」と強くバッサリ切り捨てていたり、他者を評価してはいけないと言いつつ、
上で述べたように「他者貢献」が重要というのがアドラー心理学の肝です。

本書の文章は「青年」と「哲人」の会話形式で進み、青年が哲人にアドラー心理学の教えを請う形になっていて
青年は文中で何度も、哲人の言葉に矛盾があると噛み付いています。
この本をネタにした当日のドトール会でも、あれこれと肯定・否定の意見が出てきて、議論の題材としては良い本でした。
アドラー自身、「それまで生きてきた年数の半分」が、アドラー心理学の理解に必要だと言っていますし、
今後も折に触れて開けば、その都度発見がありそうな本です。

2016年11月27日日曜日

PHPer (PHPプログラマー)から Rubyist (Rubyプログラマー)へ

初めて投稿させていただきます。
よろしくお願いします。

今回とあることをきっかけに初めてRubyを勉強することになり、
その上本格的にRuby on Railsを触れる機会を頂くことができました。

これまで実戦で使っていた言語は基本的にPHPが中心だった私からすれば、
驚きだらけだったので下記のブログを参考にPHPとRubyとで比較をしてみました。

Rubyで使える配列関連のメソッドを学習した

まず先ほどのブログと同様に前提として以下のようなテーブルがあるとし、
[books]という連想配列にデータを取得したとして比較をしていきます。

id name price description
1 Rubyの本 1000 Rubyの本です。
2 JavaScriptの本 1000 JavaScriptの本です。
3 PHPの本 1000 PHPの本です。
4 HTMLの本 2000 HTMLの本です。
5 CSSの本 2000 CSSの本です。

1. IDのみを取得して配列にしたい


where句でin演算子を使用したいときなどによく使うと思います。


PHP

$ids = array();
foreach ($books as $book) {
 $ids[] = $book['id'];
}
# => [1, 2, 3, 4, 5]


Ruby

ids = books.map { | book | book.id }
# [1, 2, 3, 4, 5]
私は普段PHPではこのように書いていましたが、
 Rubyで書くとすごく単純にかけてしまうものだなと思いました。 

調べてみたところPHPでもこのような書き方があるそうです。

$ids = array_column($books, 'id');

このarray_columnですが、
第一引数に値を取り出したい多次元配列を、
第二引数に取り出したいカラムのkeyや名前を設定していますが、
第三引数値を設定すると、その値をkeyとして連想配列を生成してくれます。


$id_to_names = array_column($books, 'name', 'id');
# => [1 => "Rubyの本", 2 => "JavaScriptの本", 3 => "PHPの本"...]

こんな便利なものがあったなんて。。。
知らなかった。
  



2.特定の値でのグループ分け


参考にした記事ですごいと思ったのでPHPとの比較をしてみました。

PHP


  function group_by_price(array $books, $keyName) {
    $keys = array_column($books, $keyName);
    $books_group_by_price = array();
    foreach ($books as $book) {
      $books_group_by_price[$book[$keyName]][] = $book;
    }
      return $books_group_by_price;
  }
  $books_group_by_price = group_by_price($books, 'price');

Ruby

books_group_by_price = Books.all.group_by{ |book| book.p }
どちらも同様に下記のような出力結果になります。

  # [1000] =>
  #  [0] => ["id" => "1", "name" => "Rubyの本", "price" => "1000", "description" => "Rubyの本です。"],
  #  [1] => ["id" => "2", "name" => "JavaScriptの本", "price" => "1000", "description" => "JavaScriptの本です。"],
  #  [2] => ["id" => "3", "name" => "PHPの本", "price" => "1000", "description" => "PHPの本です。"]
  # [2000]=>
  #  [0] => ["id" => "4", "name" => "HTMLの本", "price" => "2000", "description" => "HTMLの本です。"],
  #  [1] => ["id" => "5", "name" => "CSSの本",  "price" => "2000", "description" => "CSSの本です。"]


こちらはもう全然変わってきますね。 PHPで書く場合ほかの書き方もあるのかもしれませんが、 Rubyではこのように1行で端的に書けてしまうんです。

3. その他個人的にここが良いと思ったことをいくつか


・変数名 先ほどまでも例として挙げているソースを見ていただいてもわかる通り、
PHPでは変数名の前に「 $ 」を付けますが、Rubyでは何も記載する必要はありません。
その他にも、PHPでは1行の最後には必ず「 ; (セミコロン)」を付けなければエラーとなりますが Rubyでは省略可能です

 このようにRubyでは省略可能なものが多く存在するという印象を強く受けました。
「省略可能 = 文字数が少なくなる = すっきりする = コードが見やすくなる」
といったメリットがあります。

連想配列やハッシュの書き方でもかなり変わってくると思います。



  ・文字列結合
どんなアプリやサイトを作っていてもかなりの確率で文字列結合をする場面はあると思います。
PHPにもRubyにもsprintfはありますがその他での記述方法を用いた場合かなり差が出るなと驚きました。
"「Rubyの本」の価格は1000円です。"と表示する例を挙げてみます。

PHP

echo('「'.$book['name'].'」の価格は'.$book['price'].'円です。');

Ruby

puts "「#{books.name}」の価格は#{books.price}円です。"

この例だけではあまりわからないかもしれませんが、
もう少し入り乱れてくるとPHPよりもRubyの方が見やすいのではないかと思いました。

全体的に見てもRubyで書くと比較的シンプルに書けるといった印象を受けました。
私自身PHPに関してもRubyに関しても未熟で知らないことばかりなので、
こんな書き方もあるぞ!とかこういった面ではPHPのほうが優れている!とかがあれば
コメントしていただければと思います。

 では、最後まで見て頂きありがとうございました。
 メントしていただければと思います。

2016年11月25日金曜日

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

 こんにちは、Hiroです。Amazon RDSでのテーブルに保存されている「一般クエリログ」と「スロークエリログ」の削除方法について、簡単にまとめたいと思います。

 まず、RDSのデフォルトのDBパラメータでは、一般クエリログ、スロークエリログともにファイルにもテーブルにも保存されないようになっています。
 そこで、詳しくはAmazonのガイドページを見ていただければと思いますが、クエリログ関連の設定内容を転記させていただきます。
  • slow_query_log: スロークエリログを作成するには、1 に設定します。デフォルトは 0 です。
  • general_log: 一般ログを作成するには、1 に設定します。デフォルトは 0 です。
  • long_query_time: ファストクエリがスロークエリログに記録されないようにするために、ログに記録されるクエリの最短実行時間の値を秒単位で指定します。デフォルトは 10 秒で、最小値は 0 です。log_output = FILE の場合は、マイクロ秒の精度になるように、浮動小数点値を指定できます。log_output = TABLE の場合は、秒の精度になるように、整数値を指定する必要があります。実行時間が long_query_time の値を超えたクエリのみがログに記録されます。たとえば、long_query_time を 0.1 に設定すると、実行時間が 100 ミリ秒未満のすべてのクエリはログに記録されなくなります。
  • log_queries_not_using_indexes: インデックスを使用しないすべてのクエリをスロークエリログに記録するには、1 に設定します。デフォルトは 0 です。インデックスを使用しないクエリは、その実行時間が long_query_time パラメータの値未満であってもログに記録されます。
  • log_output optionlog_output パラメータに指定できるオプションは、次のとおりです。
    • TABLE(デフォルト)– 一般クエリを mysql.general_log テーブルに、スロークエリを mysql.slow_log テーブルに書き込みます。
    • FILE – 一般クエリログとスロークエリログの両方をファイルシステムに書き込みます。ログファイルは 1 時間ごとにローテーションされます。
    • NONE – ログ記録を無効にします。
今回は、下記の設定した場合のケースでお話します。
  • slow_query_log: 1
  • general_log: 1
  • long_query_time: 1
  • log_queries_not_using_indexes: 0
  • log_output: TABLE
上記の設定をすると設定内容に記載の通り一般クエリログは「mysql.general_log」に、スロークエリログは「mysql.slow_log」に保存され、ある一定サイズや空き容量をもとにローテーションされます。
 このテーブルなのですが、RDSのユーザ権限ではDELETEすることができないため、Amazonから2つのストアドプロシージャが提供されています。
CALL mysql.rds_rotate_slow_log;
CALL mysql.rds_rotate_general_log;  

 1回の実行ではバックアップにログがローテーションされ、2 回目の実行で完全に削除されることになります。

Amazonの公式ガイドページ