このエントリーをはてなブックマークに追加
ラベル Rails の投稿を表示しています。 すべての投稿を表示
ラベル Rails の投稿を表示しています。 すべての投稿を表示

2017年2月9日木曜日

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


どうも、はじめです。
前回はUPDATEについて書いてみました。
はじめの一歩 -Rails ActiveRecord編- UPDATE
今回はUPDATEの際readonlyやvalidationで更新ができなかった場合に関して書いていこうと思います。


はじめに


今回も前回と同じテーブルを使用しようと思います。
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


ではまず【readonly】で更新できなかった場合の対処法を書いてみます。

readonly


下のように更新をしてみます。
[id: 2, user_id: 1, item_id: 2]
↓
[id: 2, user_id: 1, item_id: 3]
手元でなかなかreadonlyになる状況を再現できなかったため
以下の方法でreadonlyにしました。
user_item = UserItem.readonly(true)

この状態で変更しようとしても更新できません。
user_item.find(2).update(item_id: 3)
# ActiveRecord::ReadOnlyRecord: UserItem is marked as readonly

これだとreadonlyでrollbackされてしまいます。

モデルからインスタンスを取得する際にreadonlyをfalseにしてあげれば更新が可能になります。
user_item = UserItem.readonly(false)
user_item.find(2).update(item_id: 3)


次に【validation】でエラーになった場合です。

validation


ユーザーの登録の際、user_nameを入力必須にしていたとします。
入力必須のバリデーションを定義するにはUserモデルに対し以下のように定義します。

# app/models/user.rb
class User < ApplicationRecord
 validates :user_name, presence: true
end

この状態でユーザー名を空で登録しようとするとエラーになります。
user = User.new
user.save

次のように記述することで一時的にバリデーションを無効にすることができます。
user = User.new
user.save(:validate => false)


最後に


validationを一時的に無効にすることは可能ですが、
今回例としてあげたのケースの場合、
DBでuser_nameにnot null制約がかかっているとバリデーションを無視することができても、
DBへの登録時にエラーになってしまいますので気をつけましょう。

次回はupdate_attributeとupdate_attributesを使ってみようと思います。

2017年2月2日木曜日

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


どうも、はじめです。
前回はJOINについて書いてみました。
はじめの一歩 -Rails ActiveRecord編- SELECT4
今回はUPDATEに関して書いていこうと思います。


はじめに


今回も前回と同じテーブルを使用しようと思います。
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


UPDATEを行うには主に以下の二つの方法があります。
・saveメソッドを使用する。
・updateメソッドを使用する。

でははじめに【saveメソッド】を使用した方法から書いていきます。


save


user_itemsを以下のように変更したいと思います。
[id: 3, user_id: 2, item_id: 3]
↓
[id: 3, user_id: 3, item_id: 3]

1.変更したいモデルインスタンスを取得
user_item = UserItem.find(3)

2.変更したい値を代入
user_item.user_id = 3

3.代入が完了したら登録
user_item.save

以上の手順でupdateが完了します。
2で複数のカラムを変更することも可能です。


次に【updateメソッド】を使用した方法を書いてみます。

update


updateでも同様にuser_itemsを以下のように変更したいと思います。
[id: 3, user_id: 2, item_id: 3]
↓
[id: 3, user_id: 3, item_id: 3]

updateメソッドを使用する際も2つの方法があります。
一つはsaveメソッドと同じように変更したいモデルインスタンスを取得し、
updateという流れになります。

user_item = UserItem.find(3)
user_item.update(user_id: 3)

もう一つは一度変数にインスタンスを取得するという工程を飛ばす方法です。

UserItem.find(3).update(user_id: 3)

どれも結果は同じになります。


まとめ


updateをする際変更したいレコードがreadonlyになっていた場合や、
validation等の関係で更新が失敗する場合があります。
その場合の対処法等を次回書いてみようと思います。

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月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月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月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月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月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

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月27日火曜日

Railsのバリデーション

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

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月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月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月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月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と同様に
翌日や前日等の日時を表現することができます。

次回

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