Author Archive

    はてなブックマーク - [rails] config.cache_classes = trueでも特定のクラスだけアクセスの度にloadする
    このエントリーをはてなブックマークに追加

    Ruby技術者認定試験に合格してほっとして倒れそうな山本ですこんにちは。

    (Rails2.xの話になります)
    さて、Railsでは起動時に環境の指定ができますが、productionモードの時はある程度のクラスがキャッシュ化されて読み込みが早くなります。
    通常であれば、config/environments/production.rbに以下の記述があると思います。

    config.cache_classes = true

    これにより、クラスは起動時の一回のみの読み込みとなり高速化が図られてます。

    話はちょっと変わりますが、modelクラスでバリデーションを定義するとき、メッセージを個別に指定したい場合は以下のように書くと思います。

    validates_format_of :hoge, :with => /moge/,  :message => "mogeと書いてください。"

    この時、以下のように

    validates_format_of :hoge, :with => /moge/,  :message => "(#{Time.now}に発生)mogeと書いてください。"

    にして、クラスが読まれたときの時間を表示するといった場合、
    developmentモードでは、その時の時間が表示されるのですが、productionモードでは初回の起動時の時間が表示されたまま、変わることはありません。
    これはproductionモードだとmodelクラスが一度しか読まれないからです。

    そもそも、productionモードでは何が起こっているのか

    ひとえにproductionモードと言っても、ウラでは何が起きているのか。
    その情報を見つけることができなかったため、ソースを追ってみました。

    config.cache_classesは、どこで何らかの処理をされているのか。
    config/environments~以下の各クラスで定義されている、
    config.cache_classesは、どこで判定がされ処理されているのでしょうか、ってことで探してみると以下の場所にありました。

    # Rails2.3.5 rails-2.3.5/lib/initializer.rb
    def initialize_dependency_mechanism
      ActiveSupport::Dependencies.mechanism = configuration.cache_classes ? :require : :load
    end

    ここで、クラスを読み込むために、requireを使うかloadを使うかのか判定を行っています。
    requireは一度しか読み込みませんが、loadは何回でも読みます。

    特定のクラスだけ読み込む術はないものか

    “指定したクラスだけは、load読み込みにする” なんてのができればいいのでしょうが、一括で起動時にrequireしてしまっているので、どうしようもないのかなとか思いつつ、いろいろやってみました。
    ActiveSupportのDependenciesクラスが色々と鍵を握ってるみたいです。

    まず、controllerにbefore_filterを仕込みその中で、以下のように書いてみました。

    ActiveSupport::Dependencies.load_file("Modelクラスへのパス")

    いかにもなメソッドです。loadし直してくれるのではと、やってみましたが表示時間は変わらず結果は×。

    次に、

    ActiveSupport::Dependencies.load_with_new_constant_marking("Modelクラスへのパス")

    これもいかにもな名前です。が、あえなく×。

    で、

    modelクラスに "unloadable" と宣言する

    ついに見つけました! これを書けば起動時にこのクラスが読まれることはない! はず!

    と意気揚々と試しましたが、×。

    こうなったらと、手当たり次第作戦。
    すると、以下のメソッドをbefore_filterに仕込むと、アクセスの度に、表示する時間が変わりました。

    ActiveSupport::Dependencies.remove_unloadable_constants!

    ついにやったと思いつつ、ソースを見てみます。

    def remove_unloadable_constants!
      autoloaded_constants.each { |const| remove_constant const }
      autoloaded_constants.clear
      explicitly_unloadable_constants.each { |const| remove_constant const }
    end

    どうやら、起動時に読み込んだクラスをリムーブしてるみたいですね。
    これ自体を読んだのであれば、全てが消えてしまう感じでこわいので、ここで使われているremove_constantメソッドだけを使って、該当のクラスを読み込み直しました。

    ActiveSupport::Dependencies.remove_constant "Modelのクラス名"
    load "Modelクラスへのパス"

    すると、晴れてアクセスする度に表示する時間が変わりました。
    #:nodoc:となっていますのでこのメソッドに解説がないのですが、このメソッドは以下のとおり。

    def remove_constant(const)
      return false unless qualified_const_defined? const
     
      const = $1 if /\A::(.*)\Z/ =~ const.to_s
      names = const.to_s.split('::')
      if names.size == 1 # It's under Object
        parent = Object
      else
        parent = (names[0..-2] * '::').constantize
      end
     
      log "removing constant #{const}"
      parent.instance_eval { remove_const names.last }
      return true
    end

    引数で渡したクラス名をチェックしたのち、
    remove_constという記述があり、クラスの定数を削除しているようです。

    一応、他のページ表示や処理にも問題ないようです。

    最適解か

    バリデーションのメッセージを動的に変えたい、という動機からここまでの調査に至りましたが、もっと簡単な方法があるのかもしれません。
    そもそも、どうしてもと言うならcontroller側でエラーメッセージを定義してもいいでしょうし。

    いかんともしがたい理由でmodelをアクセスの度にloadしたい方はご参考に~。

      はてなブックマーク - Titanium Mobile 社内勉強会の裏側 ~Ustream配信で使った機器
      このエントリーをはてなブックマークに追加

      Advent Calendarの途中ですが、ここでブレイクを。

      すみません、山本です。
      個人的に、年内15エントリーを目標に掲げておるものですから、割り込み失礼します。

      技術ネタが多いなか、今日はコラム的なものを。

      皆さん、Ustreamはご存知かと思いますが、見るのではなく配信をされたことはあるでしょうか。
      カメラとマイクとネット環境さえあれば、年中いつでもどこでも全世界に映像を配信できるサービスですが、実際に配信された方は少ないかと思います。
      そもそも配信するコンテンツがないから、というケースが多いかもしれませんが、いざ配信しようとしたときに、何を用意すればいいのかわからないという方もいらっしゃると思います。

      そこで今回は、11/25にZENPRE上でTitaniumMobile勉強会を配信した際に使用した機器などをご紹介します。

      カメラ


      Logicool QVP-30SV
      (発売当初のオンラインストア価格は2,480円)

      Logicool製の、お手頃価格なカメラですね。
      2007年発売というのもありまして、動画の画素数は30万画素と低めです。
      これを使って撮った映像が以下です。

      多少の粗さが目立ちますね。
      それでも、人の表情は認識できるレベルです。

      ちなみに、不測の事態に備えて、この模様はMacBookProのカメラでも撮っていました。
      その映像が以下です。

      MBPに搭載されているiSight130万画素で、Logicoolのカメラよりも多いですが、あまり変わりないですね。

      マイク

      このコンテンツを配信するにあたって重要なのは、映像よりも音声でした。
      資料はZENPREのスライド上に表示されるので見える必要はないのですが、音声に関しては発表者が何を言っているのかはっきりと認識できるレベルが必要でした。

      単一指向性か無指向性か

      まず、無指向性を試しました。
      これは会議によく使われるタイプで、より大勢の音声が拾えるよう設計されたマイクですね。
      これでテストしたところ、やはり発表者の声が小さくなってしまいました。
      そのコンテンツが発表者重視なのか、会場の雰囲気重視なのかで選択肢は変わると思いますが、今回は発表者メインですので、単一指向性にしました。


      audio-technica AT9904
      メーカー価格4,410円

      テレビでよく見かけるピンマイクですね。
      発表者の音声がちゃんと拾えてます。

      ちなみにMacBookProで撮った方も、音声の聞き取り度は高いですね。

      まとめ

      何か書いていて、「MacBookProさえあればUstreamを簡単に配信できる」というタイトルに変更しようかと思うくらい、
      MBPは便利ですね。
      でも、持っていない方でも、1万円以内で機材を揃えることができますし、今ならHD対応のウェブカメラも安価で購入できます。
      もっと探せば、同程度の機材であれば5,000円以内に収まると思います。

      今日もどこかで勉強会やセミナーが行われていることでしょうが、
      多くの人に見てもらうため、認知度向上のためにも、オンラインで配信される機会が増えればと思います。
      いち地方都市で開催されるような東京に比べたら集客数は減る勉強会でも、オンライン配信すれば擬似的な参加者は格段に上がるはずですから。
      また、逆に東京でしか行われない勉強会も、物理的に行けない人のために積極的にオンライン配信が行われればと思います。
      配信すること自体は、特別難しいことではないですから。

      で、画面が粗いので資料の識字ができないって時に、ZENPREを使っていただけたらと思います。

      { 2010.12.4 }

      rails 日付TIPS

        はてなブックマーク - rails 日付TIPS
        このエントリーをはてなブックマークに追加

        昨日まで続いた師走の嵐から一変、ど晴天の休日の今日、皆様いかがお過ごしでしょうか。

        昨日エントリーを書きました安元の結婚式である本日も、Advent Calendarのバトンを引き継いでおります。
        休日なので、軽めのtipsを書かせていただきます。

        皆さんは日付を和暦で表示する時、どのような処理をしているでしょうか。
        日付なんてDBに入ってる値を、多少整形する形で済めばいいのですが、案件によっては和暦表示にせざるを得ないことは、往々にしてあるかと思います。
        私がメインで扱っているruby(rails)では、和暦表示用のプラグインがあるので、それを導入すれば済むのですが、
        それを知らずに自前で作っていた時は以下のようにメソッドを作ってました。

        def get_wm_y(ymd)
            return "" if ymd.blank? 
            case ymd.year
              when 0..1925
                return "#{ymd.year}年"
             when 1868..1911
                 return '明治' + "#{ymd.year - 1867}年"
              when 1912..1925
                 return '大正' + "#{ymd.year - 1911}年"
              when 1926..1988 
                 return '昭和' + "#{ymd.year - 1925}年"
              else 
                return '平成' + "#{ymd.year - 1988}年"
            end
          end

        とても泥臭い感じはしますが、和暦計算の法則に則ったかたちになります。
        ひとつ難点は、「元年」表示ができないことですが、これも条件分岐で処理すればいいでしょう。

        日付に関してもう一つ。
        これは日本特有かもしれませんが、「年度」という概念があります。
        2010年1月は、2009年度扱いにしないといけないわけですが、これは以下のようにしてます。

        def get_nend_y(ymd)
            return "" if ymd.blank?
            case ymd.month
            when 1..3
              (ymd.year - 1).to_s
            else
              ymd.year.to_s
            end
          end

        これはすごくシンプルですかね。

        大したことは書いてませんが、ちょっとしたtipsとしてお役に立てればと思います。
        あと、こうしたほうがいいというのがあれば、ぜひご意見お寄せください。

        では、めでたき結婚式に行って参ります。