protocalendar用のウィジェット

日付を簡単に入力したかったのでprotocalendar.js用のウィジェットを作ってみた。

こんだけ。今のところprotocalendarのオプションは何も設定できない。

# widgets.py
from django import newforms as forms

class CalendarWidget(forms.TextInput):
  def render(self, name, value, attrs=None):
    return super(CalendarWidget, self).render(name, value, attrs) + """
      <script type="text/javascript">
         InputCalendar.createOnLoaded('%s');
      </script>
    """ % attrs['id']

使い方はこう。

import time
import widgets
class YourForm(djangoforms.ModelForm):
  class Meta:
    model = models.YourModel
  date = forms.CharField(widget=widgets.CalendarWidget, initial=time.strftime('%m/%d/%Y'))

現在日付を初期値にする必要がないならimport timeとinitialオプションはいらない。

injectをwhileの代わりに使う

Railsのacts_as_treeで、あるノードの親を全て取得するのに

def parents
  ret = []
  current = self
  while current.parent
    ret << current.parent
    cur = current.parent
  end
  ret
end

とかやってたんですけど、どうもどんくさくて嫌なので無理やり回避。

class LazyArray                                                
  def initialize(first, &next_block)                           
    @current = first                                           
    @next_block = next_block                                   
  end                                                          
                                                               
  def inject(sum, &block)                                      
    block.call sum, @current                                   
    inject sum, &block if @current = @next_block.call(@current)
    sum                                                        
  end                                                          
end                                                            

って言うのを作って、修正後はこう。

def parents
  LazyArray.new(parent){|d| d.parent}.inject([]){|s, d| s << d}
end

LazyArrayという名前が誇大に過ぎると言う文句は、まぁその通りだと思います。

サブディレクトリ内のコントローラに同一ディレクトリ内のapplication.rhtmlを適用

admin/top_controller.rbにはviews/layouts/admin/application.rhtmlを自動的に適用して欲しかったので、とりあえず次のようにしてみた。

app/controller/application.rb

class ApplicationController < ActionController::Base
  class <<self
    def default_layout_with_subdirectory
      l = default_layout_without_subdirectory
      if l == 'application' and name.include? '::'
        subdir_layout = "#{File.dirname name.underscore}/application"
        if File.exist? "#{RAILS_ROOT}/app/views/layouts/#{subdir_layout}.rhtml"
          return subdir_layout
        end
      end
      l
    end
    alias_method_chain :default_layout, :subdirectory
  end

  # 以下略

(注)application.rxmlには対応してません。

配列からランダムに指定数分アイテムを抜き出す

def pick_some(num, ary)
  (1..num).inject([]){|s, e| s << ary.delete_at(rand(ary.size))}.compact
end

順序は崩れます。
inject使いたいのでtimes doじゃなくてむりやりRangeにした。

画像をクリッピング

たとえば100x100の画像に10x10のマージンを用意するにはこんな感じ。

draw.define_clip_path 'clip' do
  draw.path "M 10 10 L 10 90 L 90 90 L 10 90 z"
end
draw.push
draw.clip_path 'clip'
# draw使っていろいろ描画
draw.pop

パス指定のフォーマットはSVGのパスと同じらしい。
名前つきのパスを定義して、パス名でクリップ領域を指定しないとだめらしい。
あと、pushとpopがなぜか必要らしい。


なんかごちゃごちゃするのでMagick::Drawに次のようなメソッドを追加した。

class Magick::Draw
  def within_clip(svg_path, &block)
    clip_name = "clip#{Time.now.to_i}"
    define_clip_path(clip_name){path svg_path}
    push
    clip_path clip_name
    block.call
    pop
  end
end
draw.within_clip("M 10 10 L 10 90 L 90 90 L 10 90 z") do
  # 描画
end

問題なく動いてるっぽい。

配列をランダムに並び替え

ふと思いついてやってみたけど、どうなんだろこれ。

function randomSort(ary) {
  var condition = function(){
    return [-1, 0, 1][Math.floor(Math.random() * 3)]};
  return ary.sort(condition).reverse().sort(condition);
}

ちょっと見たところいい感じに散らばってると思うんだけど。