logo

Redmineプラグイン開発の注意事項

今期より社内でRedmineを使い始め、社内のシステムをRedmineに連携しようと試行錯誤中です。

色々とあった気がしますが、特にハマった注意事項

1) unloadable
Model / Controller に unloadable を記述しないと、キャッシュをOffにしていると正常なオブジェクトが取得できずに色々なエラーが発生する。(undefined などなど)

class PluginTestController < ApplicationController
  unloadable

2) Controller の変数で、@action_name を利用すると、redmineの変数とバッティングして正常に取得できない。

class PluginTestController < ApplicationController
  def test
    @action_name = 'create'	# NG
    @action_name_ = 'create'	# OK
  end

引き続き試行錯誤中。。。

protocalendar.js 用の rails ヘルパメソッド(のひな形)を公開しました

今回、本当はモデルの特定アトリビュートだけにバリデーションを実行するプラグイン「valid_attributes」を公開しようかと思っていたのですが、

個人的なサイトの方で公開してしまったので、今回は protocalendar.js の ruby on rails 用ヘルパメソッド(のひな形)を公開したいと思います。

オプション関係など完璧ではないのですが、独自ヘルパメソッドを作るベースになればと思います。

action_view_ext.rb

module ActionView
  module Helpers
    module FormHelper
      def date_field(object_name, method, options = {})
        result = text_field_for_date(object_name, method, options)
        styleId = options[:id] ? options[:id] : object_name.to_s + '_' + method.to_s
        contents = "InputCalendar.createOnLoaded('#{styleId}', {";
        lang = options[:lang] ? "lang:#{options[:lang]} " : "lang:'ja'"
        contents << lang
        contents << ", startYear:#{options[:startYear]} " if options[:startYear]
        contents << ", endYear:#{options[:endYear]} " if options[:endYear]
        contents << "});";
        return (result + javascript_tag(contents))
      end

      protected

      def text_field_for_date(object_name, method, options = {})
        tag = InstanceTag.new(object_name, method, self, nil, options.delete(:object))
        result = tag.value(tag.object)
        unless result.nil?
          time = result.strftime("%Y/%m/%d")
          tag.object.write_attribute(method, time) unless tag.nil?
        end
        return tag.to_input_field_tag("text", options)
      end
    end
  end
end

デフォルトで lang:’ja’ を使っているため、日付フォーマットが yyyy/mm/dd になるように、

FormHelper を拡張して、text_field_for_date で小細工をしています。

cakephp 用のヘルパメソッドも近々公開します。

Railsの解読(1)

Railsで開発するのはもう一年たちました。 しかし、Railsの仕組みはよく分からないのです。最近LighttpdのFastCgiでRailsを動かすときに、迷いました。

週末家でいらいらして、Railsのソースコードを読んで見ました。

みんな知っているように、RailsはActiveRecord, ActiveSupport, ActionControllerとActionViewによって構成されています。ActiveRecordはO/R Mappingで、ActiveSupportはrubyを拡張するライブラリです。 Railsの中心にあるのはやはりActionControllerです。

一つのRequstを送る際に、Railsはどういう流れで処理するのかに注目しつつ、ソースコードを解読しました。

Mongrelにしても、FastCgiにしても、最初はCgiRequestを作って、Dispatcher.dispatchを呼び出します。 Dispatcher.dispatchは主に下の二行です。

controller = ActionController::Routing::Routes.recognize(request)
controller.process(request, response).out(output)


Routingはroutes.rbにしたがって、urlを解析して、controllerのクラスを返します。 

def self.process(request, response)
  new.process(request, response)
end

def process(request, response, method = :perform_action, *arguments) #:nodoc:
  initialize_template_class(response)  #ActionViewのクラスの取る
  assign_shortcuts(request, response)  #params, session, requestなどのショートカットを設定する
  initialize_current_url
  assign_names  #@action_nameの書き込み
  forget_variables_added_to_assigns

  log_processing
  send(method, *arguments)  #!!perform_actionの実行

  assign_default_content_type_and_charset
  response
ensure
  process_cleanup
end

def perform_action
  if self.class.action_methods.include?(action_name)
    #Actionの呼び出し
    send(action_name)
     #Actionの中でrenderとredirect_toが実行されなかったら、デフォルトのテンプレートを表示する
     render unless performed?
  elsif respond_to? :method_missing
    #Actionがない場合、method_missingを呼び出す
    send(:method_missing, action_name)
     render unless performed?
  elsif template_exists? && template_public?
    render
  else
    raise UnknownAction, "No action responded to #{action_name}", caller
  end
end

これでRailsの流れをちょっと捕まえました。

しかし、そんなに単純ではありません。 様々な処理が見えていません! FilterでもBenchmarkでも、Sessionでも。 いったいどこにあるのか?
答えはここです。(benchmark.rb)

module Benchmarking #:nodoc:
  def self.included(base)
    base.extend(ClassMethods)
    base.class_eval do
      alias_method_chain :perform_action, :benchmark
      alias_method_chain :render, :benchmark
    end
  end
  #省略
end

alias_method_chain :perform_action, :benchmarkをやると、下の二行と同じです。

alias_method :perform_action_without_benchmark, :perform_action
alias_method :perform_action, :perform_action_with_benchmark

これでperform_actionはテンプレートメソッドみたいに、名前変えずにbenchmarkの動作が組み込まれました。むしろ、rubyでのAOPとみなされるでしょう。
さらに、ちょっと考えてみると、perform_actionに対して、alias_method_chainは何回適応しても、うまくいけます。すっごいです!

「ModelやControllerはいつロードされましたのか?」ともう一つの疑問を抱いてきました。
答えはdependencies.rbです。DependenciesはObjectのconst_missingとrequireをオーバーライドしています。それで毎度Controllerなどをリフレクションでインスタンス化する際に(const_missingが呼び出される)、Railsのネーミングルールによって特定のフォルダーから読み込んで、ロードするのです。

これでRailsの仕組みをおおざっぱに理解できるでしょうか。 何か間違ったら、皆さんぜひ指摘してください。

Railsの解読(1)

Railsで開発するのはもう一年たちました。 しかし、Railsの仕組みはよく分からないのです。最近LighttpdのFastCgiでRailsを動かすときに、迷いました。

週末家でいらいらして、Railsのソースコードを読んで見ました。

(続きを読む)