redmine 読み (5)

ええと、projects コントローラ確認中なのか。昨日 render_menu という手続きを見てたんですが微妙に中途半端で終わってますね。中身をざっくり見たところでは

  • menu_items_for で引数から node を取り出してそれらについて
    • render_menu_node という手続きの戻りを link な配列に追加
  • content_tag を使って ul なリストにしている

のかどうなのか。掘削して確認。

menu_items_for 手続き

これも lib/redmine/menu_manager.rb で定義されている模様。その前にそもそも render_menu がどんな形で呼び出されているかというと以下。

        render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project)

手続き定義は以下です(一部のみ)。

      def render_menu(menu, project=nil)

menu には :project_menu または :application_menu が格納らしい。menu_items_for の肝心な所を以下に。

      def menu_items_for(menu, project=nil)
        items = []
        Redmine::MenuManager.items(menu).root.children.each do |node|
          if allowed_node?(node, User.current, project)

まず、Redmine::MenuManager て何か。と思ったらこれって lib/redmine/menu_manager.rb なのかな。先頭確認したら以下になってました。

module Redmine
  module MenuManager

つーことは、と言いつつ M-x occur で menu_manager.rb をアレしてみたら以下な記述に hit してます。

    class << self
      def map(menu_name)
        @items ||= {}
        mapper = Mapper.new(menu_name.to_sym, @items)
        if block_given?
          yield mapper
        else
          mapper
        end
      end

      def items(menu_name)
        @items[menu_name.to_sym] || MenuNode.new(:root, {})
      end
    end

うう、クラス定義のあたりでスデに微妙。明快な解は得ておらぬのですが、シングルトンなナニ、って思ってて良いのかどうか。つうか Mapper って何でしょ。
と思ったら ManuManager でクラスが定義されてますね。
Redmine::MenuManager.items(menu) てのは @items 属性なんですが、items メソドにもある通りそれは MenuNode クラスのオブジェクトになっている模様。これも menu_manager.rb で定義されていますね。最終的に Redmine::MenuManager.items(menu).root.children は配列なカンジなんですが、それにしても Mapper#push が複雑すぎる。
とはいえ push メソドの中の target_root は MenuNode なオブジェクトになるのか。ちょっとここもあまり深く掘らずにスルーの方向ってことで。そもそも redmine あまり使ったことがないのにソース確認とか無茶すぎる。

projects_controller.rb

先頭部分の記述をきちんと確認入れたい。例えば menu_item

class ProjectsController < ApplicationController
  menu_item :overview
  menu_item :roadmap, :only => :roadmap
  menu_item :settings, :only => :settings

これも lib/redmine/menu_manager.rb にて定義されている模様。ClassMethods という module ですね。以下なコメントが参考になるのかどうか。

# Examples:
# * menu_item :tickets # => sets the menu name to :tickets for the whole controller
# * menu_item :tickets, :only => :list # => sets the menu name to :tickets for the 'list' action only

Set the menu item name for a controller ってのはどーゆー意味なのかなぁ。
む、settings なタブを開いたらサブメニューみたいのがありますね。むむむ、と言いつつ app 配下を menu_item で grep したら以下な出力。

$ find app |xargs grep menu_item
app/helpers/application_helper.rb:        { :value => project_path(:id => p, :jump => current_menu_item) }
app/helpers/application_helper.rb:        b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
app/helpers/application_helper.rb:        b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
app/controllers/reports_controller.rb:  menu_item :issues
app/controllers/repositories_controller.rb:  menu_item :repository
app/controllers/repositories_controller.rb:  menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers]
app/controllers/journals_controller.rb:  menu_item :issues
app/controllers/settings_controller.rb:  menu_item :plugins, :only => :plugin
app/controllers/issues_controller.rb:  menu_item :new_issue, :only => [:new, :create]
app/controllers/auth_sources_controller.rb:  menu_item :ldap_authentication
app/controllers/files_controller.rb:  menu_item :files
app/controllers/versions_controller.rb:  menu_item :roadmap
app/controllers/projects_controller.rb:  menu_item :overview
app/controllers/projects_controller.rb:  menu_item :roadmap, :only => :roadmap
app/controllers/projects_controller.rb:  menu_item :settings, :only => :settings
app/controllers/projects_controller.rb:      redirect_to_project_menu_item(@project, params[:jump]) && return
app/controllers/messages_controller.rb:  menu_item :boards
app/controllers/timelog_controller.rb:  menu_item :issues
app/controllers/calendars_controller.rb:  menu_item :calendar
app/controllers/activities_controller.rb:  menu_item :activity
app/controllers/queries_controller.rb:  menu_item :issues
app/controllers/wikis_controller.rb:  menu_item :settings
app/controllers/admin_controller.rb:  menu_item :projects, :only => :projects
app/controllers/admin_controller.rb:  menu_item :plugins, :only => :plugins
app/controllers/admin_controller.rb:  menu_item :info, :only => :info
app/controllers/issue_categories_controller.rb:  menu_item :settings
app/controllers/gantts_controller.rb:  menu_item :gantt

うーん、よく分からんな。なんで wikis_controller で settings なのかとか。ちょっとここは保留ってことにして、ってきちんと確認しときたいトリガで始まってるのにこの終わりかたはちと情け無いなぁ。

もうすこし

列挙のみ。menu_item 呼び出しなコントローラは以下。

  • reports
  • repositories
  • journals
  • settings
  • issues
  • auth_sources
  • files
  • versions
  • projects
  • messages
  • timelog
  • calendars
  • activities
  • queries
  • wikis
  • issue_categories
  • gantts

これら、いずれも projects 配下なソレなのかどうか。

  • /projects/:id/issues/report : reports#issue_report
  • /projects/:project_id/repositories : repositories#create
  • /issues/changes(.:format) : journals#index
  • /projects/:id/settings : projects#settings
  • /projects/:project_id/issues/ : issues#index
  • /auth_sources : auth_sources#index
  • /projects/:project_id/files : files#index
  • /projects/:project_id/versions : versions#index
  • /projects : projects#index
  • /boards/:boards_id/topics/:id : messages#show
  • /projects/:project_id/issue/:issue_id/time_entries : timelog#index
  • /projects/:project_id/issues/calendar : calendar#show
  • /projects/:id/activity : activities#index
  • /queries : queries#index
  • /projects/:id/wiki : wikis#edit
  • /projects/:project_id/issue_categories : issue_categories#index
  • /projects/:project_id/issues/gantt : gantts#how

なんかそうではなさげなのも入ってるように見えますね。

もうすこし

基本的に menu_item メソドは menu_items という hash な属性に引数な id を格納してる、ってのはなんとなく (?) 理解できてるんですが、これを云々してるのが Mapper というクラスなのか。そしてそれを云々してるのが map というメソド。
がしかし、map 呼び出してるのって lib/redmine/plugin.rb なんですがこれって一体内でしょ。

    # Adds an item to the given +menu+.
    # The +id+ parameter (equals to the project id) is automatically added to the url.
    #   menu :project_menu, :plugin_example, { :controller => 'example', :action => 'say_hello' }, :caption => 'Sample'
    #
    # +name+ parameter can be: :top_menu, :account_menu, :application_menu or :project_menu
    #
    def menu(menu, item, url, options={})
      Redmine::MenuManager.map(menu).push(item, url, options)
    end
    alias :add_menu_item :menu

これがどこから呼び出されておるか分からんな。つうか、controller と view の上っつらをなでてるだけであんまりきちんと掘れていない感満点なんスけど、まだ最初だからいいよねとかorz
さすがにマルチで色々ヤり杉かなぁ。。

別件ですが

以下はどっかでヤッておきたいかも。

別にプラグインを作る用向きがある訳ではないんですが。

宿題

  • call_hook について確認
    • Redmine::Hook::Listener が云々、という記述あり
      • lib/redmine/hook.rb らしいです
  • auto_discovery_link_tag 確認
  • menu_item の謎解明