Skip to content

Commit

Permalink
Completely refactored the menu system to work with i18n
Browse files Browse the repository at this point in the history
Menu's are now generated on demand using the MenuBuilder instead of
being created as the resources are registered. MenuItem can now store
labels as procs, which allows us to properly i18n all the menu labels
at runtime.

ActiveAdmin::Resource's are now in charge of building their own MenuItem
objects.

The controller now stores the instance of the currently selected tab in
@current_tag instead of just a string label.
  • Loading branch information
gregbell committed Feb 25, 2012
1 parent b7b7401 commit 7930e2f
Show file tree
Hide file tree
Showing 25 changed files with 385 additions and 320 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ group :development, :test do
gem 'haml', '~> 3.1.1', :require => false gem 'haml', '~> 3.1.1', :require => false
gem 'yard' gem 'yard'
gem 'rdiscount' # For yard gem 'rdiscount' # For yard
gem 'rails-i18n' # Gives us default i18n for many languages
end end


group :test do group :test do
Expand Down
16 changes: 16 additions & 0 deletions features/i18n.feature
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -17,3 +17,19 @@ Feature: Internationalization
Then I should see "Edit Bookstore" Then I should see "Edit Bookstore"
When I press "Update Bookstore" When I press "Update Bookstore"
Then I should see a flash with "Bookstore was successfully updated." Then I should see a flash with "Bookstore was successfully updated."

Scenario: Switching language at runtime
Given I am logged in
And a configuration of:
"""
ActiveAdmin.register Store
"""
When I set my locale to "fr"
And I go to the dashboard
Then I should see "Store"
And I should see "Tableau de Bord"

When I set my locale to "en"
And I go to the dashboard
Then I should see "Bookstore"
And I should see "Dashboard"
3 changes: 1 addition & 2 deletions features/step_definitions/configuration_steps.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ def load_active_admin_configuration(configuration_content)
eval(configuration_content) eval(configuration_content)
ActiveAdmin::Event.dispatch ActiveAdmin::Application::LoadEvent, ActiveAdmin.application ActiveAdmin::Event.dispatch ActiveAdmin::Application::LoadEvent, ActiveAdmin.application
Rails.application.reload_routes! Rails.application.reload_routes!
ActiveAdmin.application.namespaces.values.each{|n| n.load_menu! } ActiveAdmin.application.namespaces.values.each{|n| n.reset_menu! }
end end


end end
Expand Down Expand Up @@ -46,7 +46,6 @@ def self.rollback!


Given /^a configuration of:$/ do |configuration_content| Given /^a configuration of:$/ do |configuration_content|
load_active_admin_configuration(configuration_content) load_active_admin_configuration(configuration_content)
ActiveAdmin.application.namespaces.values.each{|n| n.load_menu! }
end end


Given /^an index configuration of:$/ do |configuration_content| Given /^an index configuration of:$/ do |configuration_content|
Expand Down
3 changes: 3 additions & 0 deletions features/step_definitions/i18n_steps.rb
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,3 @@
When /^I set my locale to "([^"]*)"$/ do |lang|
I18n.locale = lang
end
3 changes: 0 additions & 3 deletions lib/active_admin/application.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -163,9 +163,6 @@ def load!
# If no configurations, let's make sure you can still login # If no configurations, let's make sure you can still login
load_default_namespace if namespaces.values.empty? load_default_namespace if namespaces.values.empty?


# Load Menus
namespaces.values.each{|namespace| namespace.load_menu! }

# Dispatch an ActiveAdmin::Application::LoadEvent with the Application # Dispatch an ActiveAdmin::Application::LoadEvent with the Application
ActiveAdmin::Event.dispatch LoadEvent, self ActiveAdmin::Event.dispatch LoadEvent, self


Expand Down
6 changes: 3 additions & 3 deletions lib/active_admin/base_controller/menu.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ def current_menu
# Get's called through a before filter # Get's called through a before filter
def set_current_tab def set_current_tab
@current_tab = if active_admin_config.belongs_to? && parent? @current_tab = if active_admin_config.belongs_to? && parent?
active_admin_config.belongs_to_config.target.menu_item_name active_admin_config.belongs_to_config.target.menu_item
else else
[active_admin_config.parent_menu_item_name, active_admin_config.menu_item_name].compact.join("/") active_admin_config.menu_item
end end
end end


end end
end end
Expand Down
72 changes: 42 additions & 30 deletions lib/active_admin/menu.rb
Original file line number Original file line Diff line number Diff line change
@@ -1,42 +1,54 @@
module ActiveAdmin module ActiveAdmin

class Menu class Menu


attr_accessor :children

def initialize def initialize
@items = [] @children = Menu::ItemCollection.new

yield(self) if block_given? yield(self) if block_given?
end

def add(*args, &block)
@items << MenuItem.new(*args, &block)
end end


def [](name) # Add a new MenuItem to the menu
items.find{ |i| i.name == name } #
# Example:
# menu = Menu.new
# dash = MenuItem.new :label => "Dashboard"
# menu.add dash
#
# Accepts as many menu items as you wish to add:
#
# menu = Menu.new
# dash = MenuItem.new :label => "Dashboard"
# admin = MenuItem.new :label => "Admin"
# menu.add dash, admin
#
# @param [MenuItem] menu_items Add as many menu items as you pass in
def add(*menu_items)
menu_items.each do |menu_item|
menu_item.parent = nil
@children << menu_item
end
end end

def items def [](id)
@items.sort @children.find_by_id(id)
end end

def find_by_url(url) def items
recursive_find_by_url(items, url) @children.sort
end end


private class ItemCollection < Array


def recursive_find_by_url(collection, url) def find_by_id(id)
found = nil id = MenuItem.generate_item_id(id)
collection.each do |item| find{ |i| i.id == id }
if item.url == url
found = item
break
else
found = recursive_find_by_url(item.children, url)
break if found
end
end end
found
end end

end end

end end
75 changes: 75 additions & 0 deletions lib/active_admin/menu_builder.rb
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,75 @@
module ActiveAdmin

class MenuBuilder

def self.build_for_namespace(namespace)
new(namespace).menu
end

attr_reader :menu

def initialize(namespace)
@namespace = namespace
end

def menu
@menu ||= build_menu
end

private

def namespace
@namespace
end

def build_menu
menu = Menu.new

add_dashboard_to_menu(menu)

namespace.resources.each do |resource|
register_with_menu(menu, resource) if resource.include_in_menu?
end

menu
end

def add_dashboard_to_menu(menu)
dashboard_path = namespace.root? ? :dashboard_path : "#{@namespace.name}_dashboard_path".to_sym

item = MenuItem.new :id => "dashboard",
:label => proc{ I18n.t("active_admin.dashboard") },
:url => dashboard_path,
:priority => 1
menu.add item
end

# Does all the work of registernig a config with the menu system
def register_with_menu(menu, resource)
# The menu we're going to add this resource to
add_to = menu

# Adding as a child
if resource.parent_menu_item_name
# Create the parent if it doesn't exist
unless menu[resource.parent_menu_item_name]
item = MenuItem.new(:label => resource.parent_menu_item_name, :url => "#", :id => resource.parent_menu_item_name)
add_to.add(item)
end

add_to = menu[resource.parent_menu_item_name]
end

if add_to[resource.menu_item.id]
existing = add_to[resource.menu_item.id]
add_to.children.delete(existing)
add_to.add(resource.menu_item)
resource.menu_item.add(*existing.children)
else
add_to.add resource.menu_item
end
end

end

end
69 changes: 55 additions & 14 deletions lib/active_admin/menu_item.rb
Original file line number Original file line Diff line number Diff line change
@@ -1,22 +1,63 @@
module ActiveAdmin module ActiveAdmin

class MenuItem class MenuItem


attr_accessor :name, :url, :priority, :parent, :display_if_block attr_accessor :id, :label, :url, :priority, :parent, :display_if_block, :children


def initialize(name, url, priority = 10, options = {}) # Build a new menu item
@name, @url, @priority = name, url, priority #
@children = [] # @param [Hash] options The options for the menu
@cached_url = {} # Stores the cached url in a hash to allow us to change it and still cache it #
# @option options [String, Proc] :label
# The label to display for this menu item. It can either be a String or a
# Proc. If the option is Proc, it is called each time the label is requested.
#
# @option options [String] :id
# A custom id to reference this menu item with. If empty an id is automatically
# generated for you.
#
# @option options [String, Symbol] :url
# A string or symbol representing the url for this item. If it's a symbol, the
# view will automatically call the method for you.
#
# @option options [Integer] :priority
# MenuItems are sorted by priority then by label. The lower the priority, the
# earlier in the menu the item will be displayed.
# Default: 10
#
# @option options [Proc] :if
# A block for the view to call to decide if this menu item should be displayed.
# The block should return true of false
def initialize(options = {})
@label = options[:label]
@id = MenuItem.generate_item_id(options[:id] || label)
@url = options[:url]
@priority = options[:priority] || 10
@children = Menu::ItemCollection.new


@display_if_block = options.delete(:if) @display_if_block = options[:if]


yield(self) if block_given? # Builder style syntax yield(self) if block_given? # Builder style syntax
end end


def add(name, url, priority=10, options = {}, &block) def self.generate_item_id(id)
item = MenuItem.new(name, url, priority, options, &block) id.to_s.downcase.gsub(" ", "_")
item.parent = self end
@children << item
def label
case @label
when Proc
@label.call
else
@label.to_s
end
end

def add(*menu_items)
menu_items.each do |menu_item|
menu_item.parent = self
@children << menu_item
end
end end


def children def children
Expand All @@ -28,7 +69,7 @@ def parent?
end end


def dom_id def dom_id
name.downcase.gsub( " ", '_' ).gsub( /[^a-z0-9_]/, '' ) id.gsub( " ", '_' ).gsub( /[^a-z0-9_]/, '' )
end end


# Returns an array of the ancestory of this menu item # Returns an array of the ancestory of this menu item
Expand All @@ -40,13 +81,13 @@ def ancestors


# Returns the child item with the name passed in # Returns the child item with the name passed in
# @blog_menu["Create New"] => <#MenuItem @name="Create New" > # @blog_menu["Create New"] => <#MenuItem @name="Create New" >
def [](name) def [](id)
@children.find{ |i| i.name == name } @children.find_by_id(id)
end end


def <=>(other) def <=>(other)
result = priority <=> other.priority result = priority <=> other.priority
result = name <=> other.name if result == 0 result = label <=> other.label if result == 0
result result
end end


Expand Down
Loading

0 comments on commit 7930e2f

Please sign in to comment.