ひーろのアウトプットブログ

プログラミングの学習記録と開発日記です

範囲オブジェクト

範囲を表す時は..または...を使用する。
対象とする範囲が異なるため使い分ける。

n以上m以下

n以上m以下を指定する場合は..を使用する。

irb(main):001:0> numbers = 1..5
=> 1..5

irb(main):002:0> numbers.include?(1)
=> true

irb(main):003:0> numbers.include?(5)
=> true

n以上m未満

n以上m未満を指定する場合は...を使用する。

irb(main):001:0> numbers = 1...5
=> 1...5

irb(main):002:0> numbers.include?(1)
=> true

irb(main):003:0> numbers.include?(5)
=> false

配列の削除について

A = [1, 2, 3, 4, 1, 2]という配列があった時に、deletedelete_atを使用した場合の挙動の違いは下記のようになる。

delete

irb(main):001:0> A = [1, 2, 3, 4, 1, 2]
=> [1, 2, 3, 4, 1, 2]

irb(main):002:0> A.delete(2)
=> 2

irb(main):003:0> A
=> [1, 3, 4, 1]

deleteは配列に存在するnの要素を削除する。

delete_at

irb(main):001:0> A = [1, 2, 3, 4, 1, 2]
=> [1, 2, 3, 4, 1, 2]

irb(main):002:0> A.delete_at(2)
=> 3

irb(main):003:0> A
=> [1, 2, 4, 1, 2]

delete_atは配列のn番目の要素を削除する。

where句で「_」を検索する方法

ユーザーの検索でSQL文を使い、_(アンダースコア)から始まるユーザーネームを検索しようとした際に、少し困ったので書いておきます。

なんで困ったの?

まずスコープを作って検索の共通化をしました。

class User < ApplicationRecord
  scope :search_user, ->(name) { where('name like?', "#{name}%") }
end

こんな感じのスコープで、任意の場所で検索をかけます。
@users.search_user('_')と記述したところ、結果は全検索となりました。
欲しい結果はアンダースコアから始まるユーザーです。
原因を考えていたところ、そういえばSQLにはワイルドカードなんてのがあったなと思い出し調べてみました。

ワイルドカード

LIKE句で使えるワイルドカードは2種類あります。

%……0文字以上の任意の文字列
_…任意の1文字

なるほど。ワイルドカードで検索をしてしまったようです。
@users.search_user('_')の意味するところは、任意の1文字を何もない文字に指定したので、結果的に全検索になっていました。

解決策

アンダースコアから始まる名前を検索するにはエスケープ文字を使ってあげれば良いです。
ワイルドカードである「_」の前に、バックスラッシュを書いてあげれば正しく検索できます。

@users.search_user('\_')

これで欲しい結果を得ることができました。

参考

http://www.sql-reference.com/select/like.html https://rooter.jp/programming/ruby/rails_use_where_or_sanitize_sql_methods_to_avoid_sql_injection/
https://itsakura.com/sql-select-like

blongs_toの外部キーがnilでもDBへ保存できるようになるオプション

著者(author)と書籍(book)の1対多の関係があった場合、モデルは次のようになります。

class Author < ApplicationRecord
  has_many :books
end
class Book < ApplicationRecord
  belongs_to :author
end

Bookは次のカラムを持っています。

name:string
author_id:integer

belongs_to

Bookを登録する場合、author_idがnilだとバリデーションで弾かれます。
これはbelongs_toオプションを設定することで、自動的に外部キーがpresence: trueとなるからです。
デフォルトで設定してくれるのは助かりますが、困ることもあります。
例えば、書籍の著者が分からない、なんてケースもあるかと思います。

optional: true

そういう時はoptional: trueオプションを付けることで、外部キーがnilでも保存ができるようになります。

class Book < ApplicationRecord
  belongs_to :author, optional: true
end

これでauthor_idがnilでも保存ができるようになりました。

impressionistを使ってPV数を計測する

制作中のアプリにPV数をカウントする機能が欲しかったので調べてみたところ、impressionistというgemが良さそうだったので導入してみました。

前提

ruby: 2.6.6
rails: 6.0.6.5

導入

いつものようにGemfileへgemを記述しinstallします。

gem 'impressionist'
$ bundle install

これで導入できた…と思ったんですが、後になってエラーが起こりました。
どうやらimpressionistの最新版にはバグがあるようなので、安定版を使います。
参考に記載のある記事を元に次のように記述します。

gem 'impressionist',
    git: 'git://github.com/charlotte-ruby/impressionist.git',
    ref: '46a582ff8cd3496da64f174b30b91f9d97e86643'
$ bundle install

これでOKです。

次にテーブルを作成します。

$ rails g impressionist

このコマンドでマイグレーションファイルが作られます。
一応中身を確認してみると、

  def self.up
    create_table :impressions, :force => true do |t|
      t.string :impressionable_type
      t.integer :impressionable_id
      t.integer :user_id
      t.string :controller_name
      t.string :action_name
      t.string :view_name
      t.string :request_hash
      t.string :ip_address
      t.string :session_hash
      t.text :message
      t.text :referrer
      t.text :params
      t.timestamps
    end
    add_index :impressions, [:impressionable_type, :message, :impressionable_id], :name => "impressionable_type_message_index", :unique => false, :length => {:message => 255 }
    add_index :impressions, [:impressionable_type, :impressionable_id, :request_hash], :name => "poly_request_index", :unique => false
    add_index :impressions, [:impressionable_type, :impressionable_id, :ip_address], :name => "poly_ip_index", :unique => false
    add_index :impressions, [:impressionable_type, :impressionable_id, :session_hash], :name => "poly_session_index", :unique => false
    add_index :impressions, [:controller_name,:action_name,:request_hash], :name => "controlleraction_request_index", :unique => false
    add_index :impressions, [:controller_name,:action_name,:ip_address], :name => "controlleraction_ip_index", :unique => false
    add_index :impressions, [:controller_name,:action_name,:session_hash], :name => "controlleraction_session_index", :unique => false
    add_index :impressions, [:impressionable_type, :impressionable_id, :params], :name => "poly_params_request_index", :unique => false, :length => {:params => 255 }
    add_index :impressions, :user_id
  end

  def self.down
    drop_table :impressions
  end

この様になってるかと思います。
そのままmigrateします。

$ rails db:migrate

PV数を計測したいページのテーブルにカラムを追加します。
今回はUsersのshowページに付けることにしてみます。
まずはmigrationファイルを作成します。

$ railg g migration AddImpressionsCountToUsers impressions_count:integer

作成したmigrationファイルにdefaultの値を追記します。

class AddImpressionsCountToUsers < ActiveRecord::Migration[5.2]
  def change
  # default: 0 初期値を0とする
    add_column :users, :impressions_count, :integer, default: 0
  end
end

再びmigrateします。

rails db:migrate

一度サーバーを再起動してimpressionistのメソッドを使えるようにしておきます。

コントローラの設定

Userの詳細ページを閲覧した際にPVを計測したいので、showアクションに記述していきます。
同一の閲覧者がページをリロードし3回見た時PVのカウントを3とすると正確性が落ちるので、session_hashを使用して1回だけカウントされるようにします。

def show
  impressionist(@user, nil, unique: [:session_hash])
  @user = User.find(params[:id])
end

モデルの設定

counter_cacheオプションを付けて記述します。

class User < ActiveRecord::Base
  is_impressionable counter_cache: true
end

ビューで表示する

これでPV数計測の準備が整いました。 実際にshowページで見てみましょう。

= @user.impressions_count

先ほど追加したカラムを使い表示すると、「1」という数字が出てくるはずです。
これは自分がアクセスした数が表示されていて、PV数の計測が成功していることがわかります。
以上で実装ができました。アプリを実際にデプロイしたら、ユニークユーザーがアクセスしてくれる度にPV数が1ずつ増えていきます。

参考

https://github.com/charlotte-ruby/impressionist https://remonote.jp/rails-impressionist-ranking https://qiita.com/yimajo/items/995584ede90be1a873ce

https://www.autovice.jp/articles/108?tag_id=1 *6/24追記

high_voltageを使用して簡単に静的ページを作成する

静的なページを作成する時にそれぞれcontrollerを作るか検討していたら、high_voltageというgemを見つけました。
controllerやroutesを設定しなくともapp/view/pages以下のファイルを表示できるようになるというものです。

前提

ruby: 2.6.6
rails: 6.0.3.5

導入

まずはgemをinstallします。

gem 'high_voltage'
bundle install

次に、pagesディレクトリを作成します。

mkdir app/view/pages

これでhigh_voltageを使う準備ができました。
簡単ですね。

使用方法

試しに利用規約のページを作ってみます。 app/view/pagesterms.html.slimというファイルを作成します。

touch app/view/pages/terms.html.slim

サーバーを立ち上げ、http://localhost:3000/pages/termsへアクセスすると、空のページが表示されます。
先ほどのファイルに記述をして再度アクセスしたら表示が確認出来るかと思います。

パス

ページのパスは、page_path('*')となります。
今回はtermsなのでpage_path('terms')と指定してあげればリンクが作れます。

= link_to '利用規約', page_path('terms')

こんな感じで使うことができます。

URLのpagesを表示しないようにする

high_voltageで生成したページにはpagesというURLが含まれてしまいますが、設定で消すことができます。
config/initializershigh_voltage.rbという設定ファイルを作成します。 そこへ次のように記述をします。

HighVoltage.configure do |config|
  config.route_drawer = HighVoltage::RouteDrawers::Root
end

一度サーバーを再起動したら、URLが変更されています。
http://localhost:3000/pages/termsは使えなくなり、http://localhost:3000/termsにアクセスすることで表示されます。
パスは変わらずpage_path('terms')を使います。

参考

https://github.com/thoughtbot/high_voltage