Skip to content

Instantly share code, notes, and snippets.

@alpaca-tc
alpaca-tc / use_index.rb
Created May 23, 2021 14:07
ActiveRecordでUSE INDEXを使うための拡張
module WithIndex
extend ActiveSupport::Concern
class IndexNotFound < StandardError; end
class_methods do
# インデックスを指定する
#
# @example
# User.where(...).use_index([:group_id, :created_at]).load
# frozen_string_literal: true
RSpec.describe 'ActiveRecord associations' do
let(:connection) { ActiveRecord::Base.connection }
let(:table_names) { connection.tables }
let(:models) { table_names.map { table_to_model(_1) } }
# テスト対象から除外する関連を定義
let(:ignored_reflections) do
# { table_names => [association_name, ...] }
@alpaca-tc
alpaca-tc / stable_sort_by.rb
Created May 24, 2021 00:47
安定ソートのsort_by
module Enumerable
def stable_sort_by
sort_by.with_index { |item, index| [yield(item), index] }
end
end
@alpaca-tc
alpaca-tc / lightweight_active_model_attributes.rb
Created May 23, 2021 14:04
軽量なActiveModel::Attributes相当のもの。どうしても大量のオブジェクトを扱いたいときに使う。
# ActiveModel::Attributesは不要な抽象化が多くてメモリを大量に消費するため、軽量なActiveModel::Attributes相当のmoduleを用意する
# このmoduleは、ActiveModel::Attributesと比べて多くの機能(dirty trackingなど)がないため、readonlyなオブジェクトに利用するとよい
module LightweightActiveModelAttributes
extend ActiveSupport::Concern
include ActiveModel::AttributeMethods
included do
class_attribute :_attribute_types, :_default_attributes, instance_accessor: false
self._attribute_types = {}
@alpaca-tc
alpaca-tc / force_attribute.rb
Last active May 23, 2021 14:02
特定の条件下でのカラムの値を宣言的に定義する
# app/models/concerns/forced_attribute.rb
module ForcedAttribute
extend ActiveSupport::Concern
CONDITIONAL_OPTIONS = %i[if unless on].freeze
class ForcedAttributeCallbacks
# @param attributes [Hash<symbol, any>]
def initialize(attributes = {})
@attributes = attributes.freeze
@alpaca-tc
alpaca-tc / slim_partial_view_spec.rb
Created May 23, 2021 13:46
partial viewでインスタンス変数を使っていないことをテスト(slim版)
require 'parser/current'
RSpec.describe 'View spec' do
describe 'partial view' do
# partial viewの一覧
let(:partial_views) do
path = Rails.application.paths['app/views'].first
Dir.glob("#{path}/**/_*.html.slim")
end
@alpaca-tc
alpaca-tc / phone_number.rb
Created May 23, 2021 13:42
渡された値から数値以外を削除する型(主に電話番号用)
# Usage
# attribute :phone_number, :number_string
class NumberString < ActiveModel::Type::String
# 与えられた値を数値のみの文字列、またはnilに変換する
#
# @param value [Object]
#
# @return [String, NilClass]
def cast(value)
super(value)&.remove(/\D/)
@alpaca-tc
alpaca-tc / wrapped_type.rb
Created May 23, 2021 13:40
渡された値でクラスを初期化する型
# Usage
# attribute :wrapped_type, klass: UserModel, array: true
class WrappedType < ActiveModel::Type::Value
KEYWORD_PARAMETERS = %i[keyreq key].freeze
private_constant(:KEYWORD_PARAMETERS)
# @param [Class, String] klass 変換したいクラス
# @param [Boolean] array 配列で変換するかどうか
def initialize(klass:, array: false)
super()
@alpaca-tc
alpaca-tc / association_owner_validator.rb
Created May 23, 2021 13:22
関連の持ち主が同一人物であることをテスト
# 使い方
# validates_with(
# AssociationOwnerValidator,
# associations: {
# sub_item: :user
# },
# owner: -> { profile_image.user },
# if: %i[sub_item profile_image]
# )
class AssociationOwnerValidator < ActiveModel::Validator
@alpaca-tc
alpaca-tc / ridgepole_unsigned_primary_id.rb
Created May 23, 2021 13:14
ridgepoleで全ての主キー・外部キーをunsigned: trueで定義する
# Schemafileで下記のように読み込む
# require_relative './lib/ridgepole_unsigned_primary_id'
# `create_table`, `t.references` で、常に `unsigned: true` を有効にするための拡張
# 個別に指定することもできるが、可読性が悪くなるのでデフォルトとする
module UnsingedReferences
def references(*args)
options = args.extract_options!
options[:unsigned] = true
args.push(options)