【ど忘れ御免】Ruby(とかRails)でアレってどうやるんだっけ？

いやぁ、脳の劣化には敵いませんな。最近ではすぐにタイトルの状況に陥って嫌になります。
自分への戒めと備忘録を兼ねて、ど忘れする度にここに追記していきます。

## ファイルへ文字列を出力するワンライナー

```
File.write('hoge.txt', 'aheahe')
```

引数の順番すぐ忘れます。

## 今のファイルから絶対パスを取得

**base.rb**というファイルに書かれたスクリプト内で、同じフォルダ内の**hoge.txt**の絶対パスを取得するには以下のようにします。

```
File.expand_path('../hoge.txt', __FILE__)
```

## 親クラス内のメソッドから子クラス内で定義された定数を参照
以下の様なクラスがあった場合

```
class Parent
  NAME = 'parent'
  def name
    NAME
  end
end

class Child < Parent
  NAME = 'child'
end
```
rubyでは`Child.new.name`を実行すると、期待に反して**parent**が返ります。
何故こうなるかは例によって忘れましたが、期待通りに**child**が欲しければ、**Parent#name**を以下の様に修正します。

```
def name
  self.class.const_get(:NAME)
end
```

いやぁ、脳の劣化には敵いませんな。最近ではすぐにタイトルの状況に陥って嫌になります。
自分への戒めと備忘録を兼ねて、ど忘れする度にここに追記していきます。

## テーブルのTRUNCATEを行う

```rb
ActiveRecord::Base.connection.execute('TRUNCATE TABLE `users`')
```

## ActiveRecordのクエリキャッシュを使わずクエリを発行する

`ActiveRecord::QueryCache.uncached`メソッドを使うと出来ます。
このメソッドは`ActiveRecord::Base`を継承したクラスなら普通に使えるので以下のようにできます。

```rb
User.uncached do 
  User.where(active: true).count
end
```

## 現在のDB名を取得する

```
Rails.configuration.database_configuration[Rails.env]["database"]
```
DB名に限らず他のDB周りの設定を取得できますね。

## 複数のseedファイルを作り、個別に実行できるようにする
通常は**db/seeds.rb**に全テーブル用の処理を全部書くのですが、これを複数のseedファイルに分けて記述すると便利なことが有ります。
例えば、マスタ用データはmaster.rbに書いて、デモ用のデータはdemo.rbに書くなど。
そうしておくと、デモデータは残したままマスタだけ初期化するなんてことも可能で柔軟にデータのリセットが出来ます。
これを実現するには以下のようにします。

**lib/tasks/multi_seed.rake**を以下の内容で作成。

```
namespace :db do
  namespace :seed do
    Dir[File.join(Rails.root, 'db', 'seeds', '*.rb')].each do |filename|
      task_name = File.basename(filename, '.rb').intern
      task task_name => :environment do
        load(filename) if File.exist?(filename)
      end
    end
  end
end
```

**db/seeds**ディレクトリを作成し、その中に**master.rb**や**demo.rb**などを作成。

以下の様にして特定のseedを実行。(master.rbを実行する例)

```
rake db:seed:master
```

## 日本語localeファイルの有りか

https://raw.githubusercontent.com/svenfuchs/rails-i18n/master/rails/locale/ja.yml

## MySQLでutf8mb4を使う

**config/database.yml**を修正し、以下のように**encoding**,**charset**,**collation**を設定。

```yaml
default: &default
  adapter: mysql2
  host: localhost
  username: hogehoge
  password: fugafuga
  encoding: utf8mb4
  charset: utf8mb4
  collation: utf8mb4_general_ci
  pool: 5
  timeout: 5000
  database: pepepepe
```

**config/initializers/ar_innodb_raw_format.rb**を以下の内容で作成。

```rb
ActiveSupport.on_load :active_record do
  module ActiveRecord::ConnectionAdapters

    class AbstractMysqlAdapter
      def create_table_with_innodb_row_format(table_name, options = {})
        table_options = options.merge(:options => 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC')
        create_table_without_innodb_row_format(table_name, table_options) do |td|
          yield td if block_given?
        end
      end
      alias_method_chain :create_table, :innodb_row_format
    end

  end
end
```

## TurboLinks無効化

* **Gemfile**から`gem 'turbolinks'`を除去
* **app/views/layouts/application.html.erb**から`'data-turbolinks-track' => true`を除去
* **app/assets/javascripts/application.js**から`//= require turbolinks`を除去

## SMTPでメール送信する
mailcatcher使うときに設定します。

**config/environments/development.rb**

```rb
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = { address: 'localhost', port: 1025 }
```

## モデルのバリデーションエラーだけをrescueで捕捉したい
こんな感じですな。

```
def create
  Comment.create(params[:comment])
rescue ActiveRecord::RecordInvalid
  render :new
end
```

## StrongParameterの使い方

コントローラ内で以下の様なメソッドを作ります。(例:ユーザーのパラメータをフィルタリングしたい場合)

```rb
  private
  def user_params
    params.require(:user).permit(
      :name,
      :address, 
      {favorite_store_ids: []}, 
      {photos_attributes: [:file, :file_cache, :filename]}
    )
  end
```

## URLの後ろに#hogehogeみたいなハッシュ(フラグメント)をつける

```rb
hoge_path(@user, anchor: "hogehoge")
```

## RESTfullなリクエストのIDに`id`以外のカラムを使う

例えば、`/users/1`みたいなURLじゃなくて、`/users/yamada`みたいな感じのURLにしたい場合は、Userモデルに`to_param`メソッドを実装する。

```rb
def to_param
  name
end
```
↑これは`name`カラムの値をIDとして使用する例。

## メール送信時のURLヘルパーのオプションを動的に変更する

メールテンプレートView内でURLヘルパーを使うときのオプションは、**environments/development.rb**などに

```
config.action_mailer.default_url_options = { host: 'example.com:3000', protocol: 'https' }
```
という形で設定します。
マルチテナント環境などでは、これを動的に変更したくなることが良く有ります。
その場合、適当な場所で以下のようにすると実現できます。

```
ActionMailer::Base.default_url_options[:host] = 'example.com'
ActionMailer::Base.default_url_options[:protocol] = 'https'
ActionMailer::Base.default_url_options[:port] = 3000
```

## モデルの属性getterメソッドの挙動を変更する
とあるモデルにnameをという属性があるとして、それがDBに格納されている値をそのまま取得するのではなく、加工したい場合には以下の様にします。

```
class User < ActiveRecord::Base
  def name 
    read_attribute(:name).upcase
  end
end
```

これで、`User.first.name`とかすると`upcase`されたnameが取れます。

## MySQLのTIME型カラムの値を文字列で取り出したい
(少なくとも)Rails4まではMySQLのTIME型カラムの扱いはあまり得意じゃないようです。
例えば、**users**テーブルに**lunch_time**という**TIME**型のカラムが有るとします。

**users**テーブル

| カラム名 | 型 | 値の例 |
| --- | --- | --- |
| id | INT | 1 |
| lunch_time | TIME | 10:00:00 |

そして、このテーブルにActiveRecordでレコードを以下のように保存するとします。

```rb
User.create(lunch_time: '10:00:00')
```

Rails4ではTIME型カラムに格納された値をActiveRecordはRubyのTime型として表現します。

```
User.first.lunch_time
=> 2000-01-01 10:00:00 UTC
```
見ての通り、勝手に2000-01-01なんて日付を付与してくれてますね。しかもタイムゾーンはUTC扱いです。
場合によってはこれはこれで便利なことも有るんですが、ここではシンプルに"10:00"という文字列で取得したいと思います。
方法としては上で紹介した、ゲッターメソッドの上書きテクを使います。

```
class User < ActiveRecord::Base
  def lunch_time 
    read_attribute(:lunch_time).try(:to_s, :time)
  end
end
```