RubyでバイナリデータをBase64エンコードする場合、 require 'base64'をしてからBase64.encode64(binary)をするか、 以下のようにpackmテンプレートを使うことができます。

   1  [binary].pack('m') #=> Base64 encoded string

しかし、アップロードしたファイル用のテンポラリなファイル名 などに使用する場合、Base64でエンコードした文字列は ファイル名に使えない(可能性が高い)文字を含んでいるため、 そのような場合にはbase64url形式でエンコードします。

base64url って何?

base64url とは、base64 を基に RFC4648 で規定された変換方式で、url とファイル名に使用しても安全になるように設計されています。変更点は、base64 では + と / が使用されていますが、それを - と _ を使用するようにします。具体的には、base64では「ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/」の表で変換しますが、base64url では「ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_」を使用します。

ということで、通常のBase64エンコードした結果に対して、 +-に、/_に置き換えればいいだけなので、 以下のようにすればOKです。

   1  [binary].pack('m').tr('+/', '-_')

posted by Png genki on Thu 24 Jan 2008 at 06:02

annotate_models は、テーブルの情報をModelとFixtureのファイルに コメントとして書き込んでくれる非常に便利なプラグインです。

永く愛用しているプラグインの一つなのですが、 しばらくメンテナンスされていないようなので、 欲しいと思っていたindexに関する情報も出力するようにしてみました。

http://svn.s21g.com/public/rails/plugins/annotate_models_with_index/

こんな感じに、インデックス情報をカラムごとに付加します。

   1  # == Schema Information
   2  # Schema version: 48
   3  #
   4  # Table name: users
   5  #
   6  #  id           :integer(11)   not null, primary key
   7     (* snip *)
   8  #  mobile_email :string(255)   index_users_on_mobile_email(unique)

posted by Png genki on Thu 24 Jan 2008 at 01:09

あすなろとのダブルポストです。

仕事で使ってみたところ、とても簡単にそしてかっこいいグラフが表示できたので紹介します。配置ディレクトリがRoR前提になっていますが、適宜自分の環境に合うように変更してください。
enjoy! ;)

Open Flash Chartサイト

GNU General Public Licenseで公開されています。
http://teethgrinder.co.uk/open-flash-chart/index.php

目標

GoogleFinanceのようなかっちょいいグラフをFlashで表示(下記はキャプチャ画像)
chart
http://finance.google.com/finance?q=google

ダウンロード&解凍

ダウンロードして適当なところに解凍します。
http://teethgrinder.co.uk/open-flash-chart/download.php

ファイルのコピー

解凍したディレクトリ直下のopen-flash-chart.swfと、js/swfobject.jsの2つを下記にコピーします

  • public/open-flash-chart.swf
  • public/javascripts/swfobject.js

viewまたはhtmlを編集します

   1    <% stats[:posts] = [23,44,35,46,78,7] %>
   2  
   3    <%= javascript_include_tag 'swfobject' %>
   4    <div id="chart"></div>
   5    <script type="text/javascript">
   6      var so = new SWFObject("/open-flash-chart.swf", "ofc", "180", "100", "9", "#FFFFFF");
   7      so.addVariable("variables","true");
   8      so.addVariable("y_label_style", 'none');
   9      so.addVariable("x_label_style", "10,#799191,0,2"); //22メモリごとにラベル表示の意。
  10      so.addVariable("x_axis_steps","1"); //メモリ軸
  11      so.addVariable("y_ticks","5,10,2");
  12      so.addVariable("y_min","<%= stats[:posts].min %>"); //最小値
  13      so.addVariable("y_max","<%= stats[:posts].max %>"); //最大値
  14      so.addVariable("x_labels","Mon,Tue,Wed,Thurs,Fri,Sat,Sun"); //x軸ラベル
  15      so.addVariable("values", "<%= stats[:posts].join(',') %>"); //  16      so.addVariable("line_hollow", "3,#799191,This week,10,5"); //グラフの種類
  17      so.addVariable("bg_colour", "#F0F0F0");
  18      so.addVariable("x_axis_colour", "#FFFFFF");
  19      so.addVariable("x_grid_colour", "#FFFFFF");
  20      so.addVariable("y_axis_colour", "#FFFFFF");
  21      so.addVariable("y_grid_colour", "#FFFFFF");
  22      so.addVariable("tool_tip","#x_label#:#val#");
  23  
  24      so.addParam("allowScriptAccess", "sameDomain");
  25      so.write("chart");
  26    </script>

posted by Png satoko on Wed 23 Jan 2008 at 10:41

irbを使っているときに、オブジェクトにどんなメソッドが 定義されているのかをpublic_methodsなどで調べることが 出来ますが、たいていの場合大量のメソッドがリストアップされて わかりにくくなってしまいます。

今回は、そんなときに良くやる方法を紹介します。

   1  >> Time.public_methods.sort - Object.public_methods
   2  => ["_load", "at", "days_in_month", "gm", "httpdate", "iso8601", "local", "local_time", "mktime", "now", "parse", "rfc2822", "rfc822", "time_with_datetime_fallback", "times", "today", "utc", "utc_time", "xmlschema", "yaml_new", "zone_offset"]

public_methodsからObjectpublic_methodsを取り除いたものを 表示しています。Objectの代わりに適当な親クラスを指定することで、 クラス階層の特定の領域で定義されたメソッドを表示することも できますね。

posted by Png genki on Mon 21 Jan 2008 at 14:14

Now, we are working on ShootingStar-4.0.0

It will be working on Windows by using WaitForMultipleObject API. I loved it since more than 10 years ago and thought it as is one of a beautiful aspect of Windows.

It may be shipped on next month.

posted by Png genki on Mon 21 Jan 2008 at 06:46

Rails勉強会@東京#26

Genki Takiuchi

Recent Situation:

  • Founded a small company.
    • And it has already survived 3 months, Yay!
  • Shipped ShootingStar 3.2.6
  • Jogging and swimming.
  • Finally I bought e-mobile (D02HW)
  • And also being with Rails 2.0.2

Favorite Things:

  • C/C++ Template, JavaScript, Ruby.
  • Thinking algorithm, Rendering (CG) and 3D.
  • Vim7, Zsh, GNU screen, rails.vim, ZenTest.

Thank you!

posted by Png genki on Sun 20 Jan 2008 at 14:33

以前紹介した Hash#only にちょっとした修正。

lib/hash_ext.rb

   1  class Hash
   2    def only(*args)
   3      args = *args if args[0].is_a? Array
   4      args.inject({}){|hash, key| hash[key] = self[key] if include? key; hash}
   5    end
   6  
   7    def only!(*args)
   8      args = *args if args[0].is_a? Array
   9      args.inject({}){|hash, key| hash[key] = self[key]; hash}
  10    end

以前のHash#onlyは、指定したキーが存在しない場合でも

   1  {:foo => 1}.only(:bar) #=> {:bar => nil}

というように、nilを指すHashを返していましたが、 キーが存在しない場合に空のHashを返すようにしてみました。 従来の挙動をするメソッドは、Hash#only!と感嘆符付きの メソッドにしています。 修正後の挙動は以下の通り。

   1  {:foo => 1}.only(:bar) #=> {}
   2  {:foo => 1}.only!(:bar) #=> {:bar => nil}

posted by Png genki on Sat 19 Jan 2008 at 13:30

最近は、自分用にRailsの挙動を拡張するために書いた lib/xxxx_ext.rbが増えてきました。 こういったファイルはconfig/initializer/の直下に 置いても良いのですが、ON/OFFを切り替えやすいので、 config/initializer/libs.rbの中から、require "xxxx_ext" を呼ぶようにしています。

さて、今回はActiveRecordをさらに便利に使うために、 僕が行っている拡張を紹介します。

lib/active_record_ext.rb

   1  module ActiveRecord
   2    class Base
   3      def self.[](arg, *args)
   4        case arg
   5        when Range
   6          find arg.to_a
   7        when Hash
   8          find :all, arg
   9        when :first, :all
  10          find arg, *args
  11        when :last
  12          find(:all, *args).last
  13        when Symbol
  14          send "find_#{arg}", *args
  15        else
  16          find arg, *args
  17        end
  18      end
  19  
  20      def update_counters(counters)
  21        self.class.update_counters id, counters
  22      end
  23    end
  24  end

どうでしょう、お分かりだと思いますが、ARクラスに インデクサ (rb_aref) を定義しています。こんな感じに動作します。

   1  >> Site[1].uri
   2  => "http://www.google.com/"
   3  >> Site[1,2].map &:uri
   4  => ["http://www.google.com/", "http://www.wikipedia.org/"]
   5  >> Site[1..2].map &:uri
   6  => ["http://www.google.com/", "http://www.wikipedia.org/"]
   7  >> Site[:last].title
   8  => "Ruby/Tk チュートリアル"
   9  >> Site[:by_uri, "http://www.google.com/"].uri
  10  => "http://www.google.com/"

実際のアプリケーションコードの中ではあまり使っていないですが、 script/consoleの中で使うのには大変便利です。

最後に、update_countersは、インスタンスメソッドとして 欲しかったので追加しています。 Rails 2.0から仕様が変わった counter_cache については、 日比さんがこちらに記事 「既存のモデル/テーブルでcounter_cacheを使う:update_counters」 を書いているので参照してみてください。

posted by Png genki on Tue 15 Jan 2008 at 06:38

(追記) 経緯:attr_readonlyを追加しました。

はじめに、counter_cacheの使い方

例えば、Blogテーブルにarticles_countカラムを持つことで、blog.articles.sizeをキャッシュし、増減はrailsが自動でやってくれます。 こんな感じ。

   1  class Article < ActiveRecord::Base
   2    belongs_to :blog, :counter_cache => true

経緯

Rails2.0からカウンターキャッシュカラム(ex Blog#articles_count)がどうやらattr_readonlyになったようで、migration内でupdate_attributeで数が更新できなったので、困った。エラーもでなくて、データで確認すると更新できてない状態。

   1  NG for Rails2.0
   2   blog.update_attribute :articles_count, total 

本題、migrationでの注意

  • カラム名は複数形_count(ex. articles_count)がデフォルト
  • カラム名のカスタマイズは、:counter_cache => :my_custom_counter
  • カラムはdefault => 0
  • 既存のテーブルでcounterカラムを追加する場合はupdate_countersを使う

   1    def self.up
   2      add_column :blogs, :articles_count, :integer, :default => 0
   3  
   4      Blog.find(:all).each do |blog|
   5        Blog.update_counters(blog.id, :articles_count => blog.articles.count)
   6      end

reference

http://josh.the-owens.com/archives/2007/11/03/rails-edge-change-how-to-add-a-counter-cache-to-an-existing-db-table/

posted by Png satoko on Tue 15 Jan 2008 at 00:40 with 2 comments

あすなろとダブルポストです。+最後の感想ちょこっと変更しました。

まとめ

CtonrollerやActionを指定して処理を実行できます

after_contoroller_action
before_contoroller_action

ex. after_blog_save
→ BlogController#save終了後処理をする。

流れをわかるためのもがき

  • ぐぐってヒントを見つける。
    [Rails][cache] Sweeper
    http://d.hatena.ne.jp/meritdemerit/20070607/p2
    なんとなくはわかるけど、やっぱりよくわからないのでソースを見ることにする。

  • Rails Referenceで検索する。右上にファイルの場所があるので確認。
    vendor/rails/actionpack/lib/action_controller/caching.rb
    http://api.rubyonrails.org/classes/ActionController/Caching/Sweeping.html

  • ソースを読む:cache_sweeperキーワード

       1    def cache_sweeper(*sweepers)
       2      return unless perform_caching
       3      configuration = sweepers.extract_options!
       4      sweepers.each do |sweeper|
       5        ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
       6        sweeper_instance = Object.const_get(Inflector.classify(sweeper)).instance
       7        if sweeper_instance.is_a?(Sweeper) 
       8         around_filter(sweeper_instance, :only => configuration[:only])
       9        else
      10          after_filter(sweeper_instance, :only => configuration[:only])
      11        end
      12      end
      13    end
    

  • ソースを読む:after処理のエントリポイント

       1   def after(controller)
       2      callback(:after)
       3      # Clean up, so that the controller can be collected after this request
       4      self.controller = nil
       5    end
    

  • ソースを読む:after_controller_action処理の呼び出し

       1    def callback(timing)
       2      controller_callback_method_name = "#{ timing}_#{controller.controller_name.underscore}"
       3      action_callback_method_name     = "#{controller_callback_method_name}_#{controller.action_name}"
       4      send!(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
       5      send!(action_callback_method_name)     if respond_to?(action_callback_method_name, true)
       6    end
    

最後の、"send!(action...:自分にメソッドが定義されていたら送る"ってところが実際の呼び出し。
愛が足りないながんばろ。

posted by Png satoko on Mon 14 Jan 2008 at 06:32