redmine 読み (6)

そもそもの目的としては Rails の理解なので menu_item という redmine ローカルな手続きでまごまごしてるのは微妙。ちょっとハンドルを切って、いっちゃんてっぺんに出力されてる top-menu な id の div 要素のあたりを確認。
app/views/layouts/base.html.erb によれば以下のあたりなのかな。

<div id="top-menu">
    <div id="account">
        <%= render_menu :account_menu -%>
    </div>
    <%= content_tag('div', "#{l(:label_logged_as)} #{link_to_user(User.current, :format => :username)}".html_safe, :id => 'loggedas') if User.current.logged? %>
    <%= render_menu :top_menu if User.current.logged? || !Setting.login_required? -%>
</div>

むむ。何故か右から、って形になってるのも気になるな。
つうかここでも render_menu が出てきましたね。基本的に menu が ul な箇条書きになってて CSS あたりで見栄えを調整してるのでこんな括り方ができるのか。
特に account な div の中身はログインしてるかどうかで出力が異るはずなんですが、render_menu 一発なあたりが凄いですね。むむむと言いつつ grep してみたら以下な結果。

$ find app lib |xargs grep account_menu
app/views/layouts/base.html.erb:        <%= render_menu :account_menu -%>
lib/redmine/plugin.rb:    # +name+ parameter can be: :top_menu, :account_menu, :application_menu or :project_menu
lib/redmine.rb:Redmine::MenuManager.map :account_menu do |menu|

ええと、lib/redmine.rb で :top_menu とか :account_menu とか :project_menu とかの map の初期化な記述がありますね。つうか、lib/redmine.rb てどの契機で呼び出されるんだろ。
色々探してみるに lib/redmine/plugin.rb らしい。

$ find lib app  |xargs grep autoload
lib/redmine/plugin.rb:      # Adds the app/{controllers,helpers,models} directories of the plugin to the autoload path
lib/redmine/plugin.rb:        ActiveSupport::Dependencies.autoload_paths += [dir]
lib/redmine/plugin.rb:            ActiveSupport::Dependencies.autoload_paths += [lib]

上記命令は一体? と思いつつググッてみたら config.autoload_path が云々って記述は結構ありつつ以下を発見。

がしかし、そもそもこの lib/redmine/plugin.rb を云々してるのは誰だろ。探してみるに require してるのは

$ find lib app  |xargs grep redmine/plugin
lib/redmine.rb:require 'redmine/plugin'

なんスけど微妙にループしてる感あり。うーん。でも redmine/plugin を require してるのは上記 lib/redmine.rb のみですね。うんうん唸りつつ load_path で grep してみると config 配下で以下な出力。

./config/application.rb:    config.autoload_paths += %W(#{config.root}/lib)
./config/application.rb:    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
./config/initializers/00-core_plugins.rb:      ActiveSupport::Dependencies.autoload_paths += [lib]

lib/*.rb は autoload 対象ってことで良いかな。

何してたのか忘れたorz

lib/redmine.rb 確認中だったのか。account_menu が面白いですね。

Redmine::MenuManager.map :account_menu do |menu|
  menu.push :login, :signin_path, :if => Proc.new { !User.current.logged? }
  menu.push :register, :register_path, :if => Proc.new { !User.current.logged? && Setting.self_registration? }
  menu.push :my_account, { :controller => 'my', :action => 'account' }, :if => Proc.new { User.current.logged? }
  menu.push :logout, :signout_path, :if => Proc.new { User.current.logged? }
end

面白いというかむしろ凄い。これで sign in/sign out な状態によって出力を変えることができるんだからすばらですね。変換してるのは render_menu_node に見えます。

          caption, url, selected = extract_node_details(node, project)
          return content_tag('li',
                               render_single_menu_node(node, caption, url, selected))

extract_node_details メソドはどこで定義なのかな。menu_manager.rb ですね。てか push でどう登録されるのか、を確認したい。Redmine::MenuManager.map は以下な定義で

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

ブロック渡しになってるので mapper が menu として渡されるんですね。ブロックの中では push メソドが呼び出されているのか。で、基本的には MenuItem のオブジェクトにされて云々、な模様。
caption 云々が微妙に気になってて extract_node_details メソドで

        caption = item.caption(project)

みたいな取り出しかたしてます。これも謎。と思ったら MenuItem#caption がありました。おそらくは以下を通過なのかどうか。

          if @caption.nil?
            l_or_humanize(name, :prefix => 'label_')

なんでここまで拘っているかというと

  menu.push :login, :signin_path, :if => Proc.new { !User.current.logged? }

なソレの表示が "Sign in" なんスよね。demo のソレと手元のソースの違い、なのかどうなのか。ここも瑣末なのでスルーします。