From 58f7ba93b63d71d6fe931483e7bd213d472b3611 Mon Sep 17 00:00:00 2001 From: Benjamin ter Kuile Date: Sun, 25 Nov 2012 09:52:46 +0100 Subject: [PATCH] migrate to new menu handling system --- .../layouts/cmtool/application.html.slim | 29 +-- config/initializers/cmtool_menu.rb | 27 +++ config/locales/cmtool.en.yml | 8 +- lib/cmtool.rb | 1 + lib/cmtool/menu.rb | 185 ++++++++++++++++++ spec/cmtool_menu_spec.rb | 64 ++++++ spec/dummy/config/application.rb | 1 - 7 files changed, 299 insertions(+), 16 deletions(-) create mode 100644 config/initializers/cmtool_menu.rb create mode 100644 lib/cmtool/menu.rb create mode 100644 spec/cmtool_menu_spec.rb diff --git a/app/views/layouts/cmtool/application.html.slim b/app/views/layouts/cmtool/application.html.slim index 7e36496..1292664 100644 --- a/app/views/layouts/cmtool/application.html.slim +++ b/app/views/layouts/cmtool/application.html.slim @@ -34,19 +34,20 @@ html lang="en" .container.nav-collapse - if user_signed_in? ul.nav - li[ class=(controller_name == 'pages' ? 'ui-state-active' : 'ui-state-default') ]=link_to Page.model_name.human_plural, cmtool.pages_path - li[ class=(controller_name == 'news' ? 'ui-state-active' : 'ui-state-default') ]=link_to Cmtool::News.model_name.human_plural, cmtool.news_index_path - li[ class=(controller_name == 'keywords' ? 'ui-state-active' : 'ui-state-default') ]=link_to Cmtool::Keyword.model_name.human_plural, cmtool.keywords_path - li[ class=(controller_name == 'faqs' ? 'ui-state-active' : 'ui-state-default') ]=link_to Cmtool::Faq.model_name.human_plural, cmtool.faqs_path - li[ class=(controller_name == 'quotes' ? 'ui-state-active' : 'ui-state-default') ]=link_to Cmtool::Quote.model_name.human_plural, cmtool.quotes_path - li[ class=(%w[contact_forms demo_requests call_requests newsletter_subscriptions].include?(controller_name) ? ['ui-state-active', 'parent'] : ['ui-state-default', 'parent']) ] - span.title= t('cmtool.menu.forms.title') - ul.child-menu - li[ class=(controller_name == 'contact_forms' ? 'ui-state-active' : 'ui-state-default') ]=link_to Cmtool::ContactForm.model_name.human_plural, cmtool.contact_forms_path - li[ class=(controller_name == 'newsletter_subscriptions' ? 'ui-state-active' : 'ui-state-default') ]=link_to Cmtool::NewsletterSubscription.model_name.human_plural, cmtool.newsletter_subscriptions_path - li[ class=(controller_name == 'users' ? 'ui-state-active' : 'ui-state-default') ]=link_to User.model_name.human_plural, cmtool.users_path - li[ class=(controller_name == 'images' ? 'ui-state-active' : 'ui-state-default') ]=link_to Cmtool::Image.model_name.human_plural, cmtool.images_path - li[ class=(controller_name == 'directories' ? 'ui-state-active' : 'ui-state-default') ]=link_to Cmtool::Directory.model_name.human_plural, cmtool.directories_path + - Cmtool::Menu.items.each do |menu_item| + - if menu_item.group? + li.dropdown class=(menu_item.controller_names.include?(controller_name) ? 'active' : '') + a.dropdown-toggle href="#" data-toggle="dropdown" + span = menu_item.title + b.caret + ul.child-menu.dropdown-menu + - menu_item.items.compact.each do |child_item| + - if child_item.divider? + li.divider + - else + li[ class=(controller_name == child_item.controller_name ? 'active' : '') ] = link_to child_item.title, child_item.path + - elsif false && menu_item.resource_link? + li[ class=(menu_item.controller_name == controller_name ? 'active' : '')]= link_to menu_item.title, menu_item.path .btn-group.pull-right a.btn.dropdown-toggle[data-toggle="dropdown" href="#"] i.icon-user @@ -78,6 +79,6 @@ html lang="en" = yield :sidebar footer - p © KPN 2012 + p © Cmtool 2012 = yield :footer #hidden-html.hide= yield :hidden_html diff --git a/config/initializers/cmtool_menu.rb b/config/initializers/cmtool_menu.rb new file mode 100644 index 0000000..e7acca1 --- /dev/null +++ b/config/initializers/cmtool_menu.rb @@ -0,0 +1,27 @@ +Cmtool::Menu.register do + group label: :site do + title t('cmtool.menu.site.title') + resource_link Page, scope: Cmtool + resource_link Cmtool::Keyword + end + group label: :publications do + title t('cmtool.menu.publications.title') + resource_link Cmtool::News + resource_link Cmtool::Faq + resource_link Cmtool::Quote + end + group label: :forms do + title t('cmtool.menu.forms.title') + resource_link Cmtool::ContactForm + resource_link Cmtool::NewsletterSubscription + end + group label: :files do + title t('cmtool.menu.files.title') + resource_link Cmtool::Image + resource_link Cmtool::Directory + end + + resource_link User, label: :users, scope: Cmtool +end +Cmtool::Menu.register do +end diff --git a/config/locales/cmtool.en.yml b/config/locales/cmtool.en.yml index abfe719..7c5c814 100644 --- a/config/locales/cmtool.en.yml +++ b/config/locales/cmtool.en.yml @@ -7,8 +7,14 @@ en: are_you_sure_with_name: 'Are you sure you want to delete %{model} %{name}?' empty_result: No %{models} found menu: + site: + title: Site forms: - title: Form + title: Forms + publications: + title: Publications + files: + title: Files action: index: title: List %{models} diff --git a/lib/cmtool.rb b/lib/cmtool.rb index 2c5a90d..8cc517e 100644 --- a/lib/cmtool.rb +++ b/lib/cmtool.rb @@ -2,6 +2,7 @@ require "cmtool/engine" require 'cmtool/includes/user' require 'cmtool/includes/page' require 'cmtool/includes/pages_controller' +require 'cmtool/menu' module Cmtool end diff --git a/lib/cmtool/menu.rb b/lib/cmtool/menu.rb new file mode 100644 index 0000000..6cdbe90 --- /dev/null +++ b/lib/cmtool/menu.rb @@ -0,0 +1,185 @@ +module Cmtool + module Menu + def self.register(&block) + @register ||= Register.new + @register.instance_eval(&block) + @registrations ||= [] + @registrations << block + end + + def self.items + @register.items + end + + def self.method_missing(*args, &blk) + @register.send(*args, &blk) + end + + # Reset the complete menu + def self.reset! + @register = Register.new + @registrations = [] + end + + # Getter for the registrations + def self.registrations + @registrations ||= [] + end + + ## + # Base class for elements in the menu. All elements like Groups/ResourceLinks should + # inherit from this class + ## + class ElementBase + def initialize(*args) + @options = args.extract_options! + end + def controller_names + [] + end + def controller_name + '' + end + def options + @options ||= {} + end + def method_missing(*args) + return true if self.class.name.sub(/.*::/, '').underscore.concat("?") == args.first.to_s + return false if args.first.to_s.last == '?' + @register.send(*args) + end + end + + ## + # Just a simple class that identifies itself as a divider + ## + class Divider < ElementBase + end + + ## + # A group element, works as a new kind of menu, but then within another one + ## + class Group < ElementBase + def initialize(options = {}, &block) + block ||= Proc.new{} + @register = Register.new + @options = options + @block = block + @register.instance_eval &block + end + def title + @register.title.respond_to?(:call) ? @register.title.call : @register.title + end + + def controller_names + resource_links.map(&:controller_name) + end + def group? + true + end + end + + ## + # Resource link, indicates a link to standard resource. The first argument should + ## + class ResourceLink < ElementBase + attr_reader :resource + def initialize(resource, options = {}) + @resource, @options = resource, options + end + + def resource_link? + true + end + + def controller_name + @resource.name.underscore.split('/').last.pluralize + end + def title + @resource.model_name.human_plural + end + def path + engine.routes.url_helpers.url_for(controller: nested_controller_name, action: 'index', only_path: true) + end + + def engine + base = @options[:scope].presence + base ||= @resource.name.split('::').first if @resource.name.index('::') + return Rails.application unless base + return base if base.is_a?(Rails::Engine) + if e = "#{base}::Engine".safe_constantize + return e + end + return Rails.application + end + def nested_controller_name + if @options[:scope].present? + "#{@options[:scope]}::#{@resource.name}".underscore.pluralize + else + @resource.name.underscore.pluralize + end + end + end + + ## + # Register class on which the register blocks are executed. Should hold all possible DSL logic + ## + class Register + attr_reader :items + def initialize + @items = [] + end + def t(*args) + -> { I18n.t(*args) } + end + def group(options = {}, &block) + @items << Group.new(options, &block) + end + def title(*args) + return @title if args.empty? + @title = args.first + end + def resource_link(resource, options = {}) + @items << ResourceLink.new(resource, options) + end + def resource_links + @items.select{|i| i.is_a?(ResourceLink)} + end + def method_missing(*args) + @items.send(*args).to_a + end + + def before(label, &block) + @before = [] + @before << items.shift while items.first && items.first.options[:label] != label + @after = @items + @items = [] + instance_eval(&block) + @items = @before + @items + @after + end + + def after(label, &block) + @before = [] + @before << items.shift while items.first && items.first.options[:label] != label + @before << items.shift if items.any? + @after = @items + @items = [] + instance_eval(&block) + @items = @before + @items + @after + end + + def append_to(label, &block) + item = items.find{|i| i.options[:label] == label} + if item + register = item.instance_variable_get('@register') + if register + register.instance_variable_get('@items') << Cmtool::Menu::Divider.new + register.instance_eval(&block) + end + else + instance_eval(&block) + end + end + end + end +end diff --git a/spec/cmtool_menu_spec.rb b/spec/cmtool_menu_spec.rb new file mode 100644 index 0000000..a2396fd --- /dev/null +++ b/spec/cmtool_menu_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +describe Cmtool::Menu do + before :all do + @base_block = Cmtool::Menu.registrations.first + end + before :each do + Cmtool::Menu.reset! + Cmtool::Menu.register &@base_block + end + describe Cmtool::Menu::ElementBase do + it "should return false on specific element inquiry" do + base_element = Cmtool::Menu::ElementBase.new + element_names = Cmtool::Menu::ElementBase.subclasses.map{|e| e.name.sub(/.*::/, '').underscore } + for element_name in element_names + base_element.send("#{element_name}?").should be_false + end + end + end + describe Cmtool::Menu::Group do + it "should identify itself as such" do + Cmtool::Menu::Group.new.group?.should be_true + end + it "should not identify as resource link" do + Cmtool::Menu::Group.new.resource_link?.should be_false + end + end + describe Cmtool::Menu::ResourceLink do + it "should identify itself as such" do + Cmtool::Menu::ResourceLink.new(User).resource_link?.should be_true + end + end + describe :items do + it "should return an array" do + Cmtool::Menu.items.should be_a Array + end + it "should return an array of 5" do + Cmtool::Menu.items.size.should == 5 + end + + end + + describe 'New register block' do + before :each do + Cmtool::Menu.register do + before :users do + resource_link Page, scope: Cmtool + end + after :publications do + resource_link Cmtool::Image + end + + append_to :publications do + resource_link Cmtool::Directory + end + end + end + it "should have pages as second last items argument" do + second_last = Cmtool::Menu.items[-2] + second_last.should be_a Cmtool::Menu::ResourceLink + second_last.resource.should == Page + end + end +end diff --git a/spec/dummy/config/application.rb b/spec/dummy/config/application.rb index bf96737..5996db3 100644 --- a/spec/dummy/config/application.rb +++ b/spec/dummy/config/application.rb @@ -61,4 +61,3 @@ module Dummy config.assets.version = '1.0' end end -