あげるものがねーっ

明日仕事とかどういうことなのっ!
日曜日でしょっ!
安息日でしょっ!

くそーっ!

大昔のサークルカットを上げて一時しのぎだっ!

幼女リディア

これは先っぽでてるから修正したやつを提出しましたけどね。

バレンタインだというのに、季節感もへったくれもないですね。

ActiveRecord_Associations_CollectionProxy ってなんだよ!

rails でだいぶ無茶しなきゃいけないことがあったので備忘録

* * *

 ECサイトのカスタマイズで見積書を出す機能を追加したのですが、合計金額算出時に、ActiveRecord の sum メソッドを使用していたため見積りの内容を「注文として」一度DBに保存しないといけないという事実が発覚。
(※ sum メソッドは、”SELECT count(メソッド引数) FROM テーブル名” というクエリを発行するため、保存していないと 0 が返ってくる)
 注文として保存してしまうとログに残ったり、見積書作成中に下手にページ遷移されると危険なので sum メソッドの挙動を変更することにしたのですが、sum メソッドはいろいろなところで使ってるためモンキーパッチを当てるわけにも行かない。
 かと言って同じメンバを持つダミークラスを作ろうとすると、今度はせっかくフレームワークに実装されている消費税計算・経費計算が使えなくなってしまう。
 それらまで移植するのはゼッタイに嫌だったので、見積り算出時のみ sum メソッドでクエリを発行しないようにするコードをガリガリと書いてみました。

 それがこちら

class DummyOrder < Order
  self.table_name = 'orders'

  def initialize
    super nil, {}

    # LineItem モデルのリレーションを上書きする
    association_instance_set :line_items,
                             DummyLineItem::DummyHasManyAssociation.new(self, self.class.reflections[:line_items])
  end
end

# 見積書作成時に使うダミーLineItem
class DummyLineItem < LineItem
  self.table_name = 'line_item'

  # LineItem の CollectionProxy を騙す
  class DummyHasManyAssociation < ActiveRecord::Associations::HasManyAssociation
    def reader(force_reload = false)
      if force_reload
        klass.uncached { reload }
      elsif stale_target?
        reload
      end

      @proxy ||= DummyCollectionProxy.new DummyLineItem, self
    end
  end

  # sum メソッドを DB の呼び出しから、自身の持つ配列から算出するよう修正
  class DummyCollectionProxy < LineItem::ActiveRecord_Associations_CollectionProxy
    def sum(args)
      case args.to_s
      when "price * quantity"
        reduce(0) { |a, e| a + e.price * e.quantity }
      else
        reduce(0) { |a, e| a + e.send(args.to_s).to_i }
      end
    end
  end
end

…カオス。

一応解説を書いておくとですね、
 OrderモデルとLineItemモデルが1対多関係にあり、Orderモデルが line_items メソッドを持っているのですが、initialize で association_instance_setを呼び出し、line_items メソッドで返される LineItem::ActiveRecord_Associations_CollectionProxy インスタンスを DummyLineItem::DummyHasManyAssociation インスタンスに置き換えます。
 ダミークラス「DummyHasManyAssociation」のreaderは、元の「ActiveRecord::Associations::HasManyAssociation」クラスのreaderをほぼ丸コピーなのですが、最後の1行は書き換えてます。

@proxy ||= DummyCollectionProxy.new DummyLineItem, self

コレのことです。

ちなみに、元々のコードは下記のとおりです。
(activerecord-4.1.6/lib/active_record/associations/collection_association.rb)

@proxy ||= CollectionProxy.create(klass, self)

 ざっくり追った感じ、Reflection オブジェクト(has_manyとか指定すると自動的に作成されるリレーション用のオブジェクト)から、対象のテーブルに紐付いたモデルオブジェクトのCollectionProxyインスタンスを作成して返す、という挙動をしているみたいですが、今回は特定条件下でしか使わない前提だったので今回作ったDummyCollectionProxyインスタンスを返すようにしてます。
 これをやらないとActiveRecord が自動生成したCollectionProxyが生成されてしまいます。
(self.class.reflections で呼び出している Reflection オブジェクトの中身を書き換えられればいいのですが、コレは実態のあるテーブルと密に紐付いており、変更が面倒臭かった今回のケースではそこまで手を入れる必要がなかったのでHasManyAssociationを置き換える方法を取りました)

 最後に、DummyCollectionProxy の sum メソッドをオーバーライドしてやればOKです。

 見積り作成時にはこのDummyOrder を生成してやればDBを使うことなく合計値が出せるようになりました。

…もっとスマートな方法があったら誰か教えてください。

作業環境改善の為…

本日、サークルカットは進まず。
ので、前回のサークルカットをあげておちゃにごし…

リディア、サークルカット

デスクがだいぶ広くなったので、作業はしやすくなりました。次は椅子ですな。

作業環境

いちおう

描いてるよ、サークルカット
diary06_01

Spree で、slug の自動作成を防止する方法

ruby on rails で作られたCMS、Spree の管理画面から商品を新規作成する際、Slug は商品名称(name)と品番(sku)から英数字だけをつなぎあわせて自動生成されますが、
日本語の品名だけだと、Slug が空になりバリデーションエラー(3文字以上入力してください)になってしまいます。

回避方法としては

  • 日本語のslugを許容させる
  • slug の入力項目を作る。

のどちらかです。
ただし、後者で対処する場合、入力項目を作っても商品名称(name)と品番(sku)で強制的に上書きされてしまい、入力した項目は反映されません。
それを回避するには、Spree::Producrt のDecoratorを作って下記コードを埋め込めば回避できます。

/app/models/spree/product_decorator.rb

Spree::Product.class_eval do
  ...
  def slug_candidates_with_slug
    [:slug]
  end
  alias_method_chain :slug_candidates, :slug
end

こんな感じのコードです。

これは、spree core の product モデルに、フレンドリURLを実現するために friendly_id が呼ばれているためです。

    friendly_id :slug_candidates, use: :slugged

ちなみに、slug_candidates メソッドの中身は下記のようになってます。

    # Try building a slug based on the following fields in increasing order of specificity.
    def slug_candidates
      [
          :name,
          [:name, :sku]
      ]
    end

これを decorator で上書き(正確には、メソッドに別の名前をつけて呼び出しを回避)したわけです。

はぁ、spree は日本語の資料が少ないので大変だ…

悪戯にgithub のアカウントをとってみた

まだ何もあげてないんだけどね。
ただ、「プログラマの履歴書」とか呼ばれてるらしく、IT業界で仕事しているのに持ってないのはこれいかに、見たいな状況になったのでとりあえず取るだけ取ったしだいなのですよ。

いずれあげられるものができたらいいな。
いいな。

* * *

diary05_01

サークルカットのラフ描いてたはずなんだけど、なんかライム描いてた。

JTが飲料から撤退

すげぇショック。
Roots が無くなっちゃうよぉ。
どこか引き取ってくれないからぁ。

* * *

画廊あけました。
まだ、全部サルベージは終わってないですが、7年ぐらい前のものまでは上げたから、一旦よしとしてます。

RSS / feedly
  • follow us in feedly
  • follow us in feedly
ソーシャル
広告