├── lib ├── activeadmin.rb ├── active_admin │ ├── comments │ │ ├── views │ │ │ └── active_admin_comment.rb │ │ ├── views.rb │ │ ├── namespace_helper.rb │ │ ├── resource_helper.rb │ │ ├── configuration.rb │ │ ├── comment.rb │ │ └── show_page_helper.rb │ ├── stylesheets │ │ └── active_admin │ │ │ └── mixins │ │ │ └── _utilities.scss │ ├── version.rb │ ├── engine.rb │ ├── arbre │ │ ├── core_extensions.rb │ │ ├── context.rb │ │ ├── attributes.rb │ │ ├── class_list.rb │ │ ├── collection.rb │ │ ├── text_node.rb │ │ ├── document.rb │ │ └── html5_elements.rb │ ├── sass │ │ ├── active_admin.scss │ │ └── css_loader.rb │ ├── views.rb │ ├── view_helpers │ │ ├── title_helper.rb │ │ ├── assigns_with_indifferent_access_helper.rb │ │ ├── view_factory_helper.rb │ │ ├── icon_helper.rb │ │ ├── sidebar_helper.rb │ │ ├── active_admin_application_helper.rb │ │ ├── form_helper.rb │ │ ├── method_or_proc_helper.rb │ │ ├── renderer_helper.rb │ │ ├── breadcrumb_helper.rb │ │ ├── display_helper.rb │ │ └── auto_link_helper.rb │ ├── controller_action.rb │ ├── page_config.rb │ ├── views │ │ ├── action_items.rb │ │ ├── dashboard_section_renderer.rb │ │ ├── components │ │ │ ├── panel.rb │ │ │ ├── sidebar_section.rb │ │ │ ├── blank_slate.rb │ │ │ ├── columns.rb │ │ │ ├── attributes_table.rb │ │ │ ├── status_tag.rb │ │ │ └── scopes.rb │ │ ├── index_as_block.rb │ │ ├── pages │ │ │ ├── new.rb │ │ │ ├── edit.rb │ │ │ ├── show.rb │ │ │ └── dashboard.rb │ │ ├── index_as_grid.rb │ │ ├── header_renderer.rb │ │ ├── tabs_renderer.rb │ │ └── index_as_blog.rb │ ├── scope.rb │ ├── component.rb │ ├── resource_controller │ │ ├── action_builder.rb │ │ ├── menu.rb │ │ ├── form.rb │ │ ├── sidebars.rb │ │ ├── callbacks.rb │ │ ├── scoping.rb │ │ ├── page_configurations.rb │ │ └── filters.rb │ ├── asset_registration.rb │ ├── event.rb │ ├── view_helpers.rb │ ├── dashboards │ │ ├── section.rb │ │ └── dashboard_controller.rb │ ├── arbre.rb │ ├── reloader.rb │ ├── menu.rb │ ├── resource │ │ ├── belongs_to.rb │ │ ├── scopes.rb │ │ ├── naming.rb │ │ └── menu.rb │ ├── action_items.rb │ ├── view_factory.rb │ ├── locales │ │ ├── da.yml │ │ ├── es.yml │ │ ├── pt.yml │ │ └── en.yml │ ├── helpers │ │ ├── optional_display.rb │ │ └── settings.rb │ ├── sidebar.rb │ ├── devise.rb │ ├── csv_builder.rb │ ├── iconic.rb │ ├── dashboards.rb │ └── menu_item.rb └── generators │ └── active_admin │ ├── assets │ ├── templates │ │ ├── 3.1 │ │ │ ├── active_admin.js │ │ │ └── active_admin.css.scss │ │ └── dashboards.rb │ └── assets_generator.rb │ ├── resource │ ├── templates │ │ └── admin.rb │ └── resource_generator.rb │ ├── install │ ├── templates │ │ ├── migrations │ │ │ ├── 1_create_admin_notes.rb │ │ │ └── 2_move_admin_notes_to_comments.rb │ │ └── dashboards.rb │ └── install_generator.rb │ └── devise │ └── devise_generator.rb ├── .travis.yml ├── app ├── views │ ├── active_admin │ │ ├── resource │ │ │ ├── edit.html.arb │ │ │ ├── index.html.arb │ │ │ ├── new.html.arb │ │ │ ├── show.html.arb │ │ │ └── index.csv.erb │ │ ├── dashboard │ │ │ └── index.html.arb │ │ └── devise │ │ │ ├── mailer │ │ │ ├── unlock_instructions.html.erb │ │ │ └── reset_password_instructions.html.erb │ │ │ ├── unlocks │ │ │ └── new.html.erb │ │ │ ├── passwords │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ │ │ ├── sessions │ │ │ └── new.html.erb │ │ │ └── shared │ │ │ └── _links.erb │ └── layouts │ │ └── active_admin_logged_out.html.erb └── assets │ ├── stylesheets │ └── active_admin │ │ ├── _mixins.css.scss │ │ ├── mixins │ │ ├── _all.css.scss │ │ ├── _variables.css.scss │ │ ├── _icons.css.scss │ │ ├── _shadows.css.scss │ │ ├── _sections.css.scss │ │ ├── _gradients.css.scss │ │ ├── _buttons.css.scss │ │ └── _rounded.css.scss │ │ ├── _flash_messages.css.scss │ │ └── _comments.css.scss │ ├── images │ └── active_admin │ │ ├── loading.gif │ │ ├── orderable.png │ │ ├── admin_notes_icon.png │ │ ├── nested_menu_arrow.gif │ │ ├── nested_menu_arrow_dark.gif │ │ └── datepicker │ │ ├── datepicker-nipple.png │ │ ├── datepicker-header-bg.png │ │ ├── datepicker-input-icon.png │ │ ├── datepicker-next-link-icon.png │ │ └── datepicker-prev-link-icon.png │ └── javascripts │ └── active_admin │ └── base.js ├── .document ├── features ├── step_definitions │ ├── flash_steps.rb │ ├── tab_steps.rb │ ├── menu_steps.rb │ ├── asset_steps.rb │ ├── action_item_steps.rb │ ├── comment_steps.rb │ ├── pagination_steps.rb │ ├── sidebar_steps.rb │ ├── dashboard_steps.rb │ ├── attribute_steps.rb │ ├── user_steps.rb │ ├── index_scope_steps.rb │ ├── factory_steps.rb │ ├── configuration_steps.rb │ └── format_steps.rb ├── users │ ├── logging_out.feature │ └── logging_in.feature ├── index │ ├── formats.feature │ ├── index_as_block.feature │ ├── pagination.feature │ ├── index_as_grid.feature │ ├── format_as_csv.feature │ ├── index_as_blog.feature │ ├── index_blank_slate.feature │ └── index_scopes.feature ├── first_boot.feature ├── menu.feature ├── dashboard.feature ├── comments │ └── viewing_index.feature ├── show │ ├── page_title.feature │ └── default_content.feature ├── global_navigation.feature ├── registering_resources.feature ├── registering_assets.feature ├── support │ ├── selectors.rb │ └── paths.rb ├── sti_resource.feature └── specifying_actions.feature ├── cucumber.yml ├── .gitignore ├── spec ├── support │ ├── detect_rails_version.rb │ ├── integration_example_group.rb │ ├── templates │ │ └── cucumber.rb │ ├── rails_template_with_data.rb │ └── rails_template.rb ├── unit │ ├── component_spec.rb │ ├── generators │ │ └── install_rails_3_1_spec.rb │ ├── arbre │ │ └── html │ │ │ ├── context_spec.rb │ │ │ ├── element_finder_methods_spec.rb │ │ │ ├── tag_attributes_spec.rb │ │ │ └── tag_spec.rb │ ├── view_factory_spec.rb │ ├── helpers │ │ └── settings_spec.rb │ ├── dashboard_controller_spec.rb │ ├── reloader_spec.rb │ ├── display_name_spec.rb │ ├── action_items_spec.rb │ ├── resource │ │ ├── scopes_spec.rb │ │ └── naming_spec.rb │ ├── components │ │ ├── panel_spec.rb │ │ ├── blank_slate_spec.rb │ │ ├── sidebar_section_spec.rb │ │ └── columns_spec.rb │ ├── controller_filters_spec.rb │ ├── resource_controller │ │ └── collection_spec.rb │ ├── scope_spec.rb │ ├── pretty_format_spec.rb │ ├── auto_link_spec.rb │ ├── asset_registration_spec.rb │ ├── rails_spec.rb │ ├── belongs_to_spec.rb │ ├── event_spec.rb │ ├── dashboard_section_spec.rb │ ├── menu_spec.rb │ ├── dashboards_spec.rb │ ├── comments_spec.rb │ └── csv_builder_spec.rb └── integration │ ├── stylesheets_spec.rb │ └── belongs_to_spec.rb ├── script ├── use_rails └── local ├── Gemfile ├── activeadmin.gemspec ├── LICENSE └── CHANGELOG.rdoc /lib/activeadmin.rb: -------------------------------------------------------------------------------- 1 | require 'active_admin' 2 | -------------------------------------------------------------------------------- /lib/active_admin/comments/views/active_admin_comment.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/active_admin/stylesheets/active_admin/mixins/_utilities.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | script: bundle exec rake 2 | rvm: 3 | - ree 4 | - 1.9.2 5 | -------------------------------------------------------------------------------- /app/views/active_admin/resource/edit.html.arb: -------------------------------------------------------------------------------- 1 | render renderer_for(:edit) 2 | -------------------------------------------------------------------------------- /app/views/active_admin/resource/index.html.arb: -------------------------------------------------------------------------------- 1 | render renderer_for(:index) 2 | -------------------------------------------------------------------------------- /app/views/active_admin/resource/new.html.arb: -------------------------------------------------------------------------------- 1 | render renderer_for(:new) 2 | -------------------------------------------------------------------------------- /app/views/active_admin/resource/show.html.arb: -------------------------------------------------------------------------------- 1 | render renderer_for(:show) 2 | -------------------------------------------------------------------------------- /lib/active_admin/version.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | VERSION = '0.2.2' 3 | end 4 | -------------------------------------------------------------------------------- /app/views/active_admin/dashboard/index.html.arb: -------------------------------------------------------------------------------- 1 | render view_factory.dashboard_page 2 | -------------------------------------------------------------------------------- /.document: -------------------------------------------------------------------------------- 1 | README.rdoc 2 | lib/**/*.rb 3 | bin/* 4 | features/**/*.feature 5 | LICENSE 6 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin/_mixins.css.scss: -------------------------------------------------------------------------------- 1 | @import "active_admin/mixins/all"; 2 | -------------------------------------------------------------------------------- /lib/generators/active_admin/assets/templates/3.1/active_admin.js: -------------------------------------------------------------------------------- 1 | //= require active_admin/base 2 | -------------------------------------------------------------------------------- /lib/active_admin/engine.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class Engine < Rails::Engine 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /lib/active_admin/arbre/core_extensions.rb: -------------------------------------------------------------------------------- 1 | class Object 2 | def to_html 3 | to_s 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/active_admin/resource/templates/admin.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register <%= class_name.singularize %> do 2 | 3 | end 4 | -------------------------------------------------------------------------------- /app/assets/images/active_admin/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcoury/active_admin/master/app/assets/images/active_admin/loading.gif -------------------------------------------------------------------------------- /app/assets/images/active_admin/orderable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcoury/active_admin/master/app/assets/images/active_admin/orderable.png -------------------------------------------------------------------------------- /features/step_definitions/flash_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^I should see a flash with "([^"]*)"$/ do |text| 2 | Then %{I should see "#{text}"} 3 | end 4 | -------------------------------------------------------------------------------- /lib/active_admin/sass/active_admin.scss: -------------------------------------------------------------------------------- 1 | // This file is included when using Rails 3.0 2 | @import "active_admin/mixins"; 3 | @import "active_admin/base"; 4 | -------------------------------------------------------------------------------- /app/assets/images/active_admin/admin_notes_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcoury/active_admin/master/app/assets/images/active_admin/admin_notes_icon.png -------------------------------------------------------------------------------- /app/assets/images/active_admin/nested_menu_arrow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcoury/active_admin/master/app/assets/images/active_admin/nested_menu_arrow.gif -------------------------------------------------------------------------------- /app/assets/images/active_admin/nested_menu_arrow_dark.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcoury/active_admin/master/app/assets/images/active_admin/nested_menu_arrow_dark.gif -------------------------------------------------------------------------------- /features/step_definitions/tab_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^the "([^"]*)" tab should be selected$/ do |name| 2 | Then %{I should see "#{name}" within "ul#tabs li.current"} 3 | end 4 | -------------------------------------------------------------------------------- /app/assets/images/active_admin/datepicker/datepicker-nipple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcoury/active_admin/master/app/assets/images/active_admin/datepicker/datepicker-nipple.png -------------------------------------------------------------------------------- /app/assets/images/active_admin/datepicker/datepicker-header-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcoury/active_admin/master/app/assets/images/active_admin/datepicker/datepicker-header-bg.png -------------------------------------------------------------------------------- /app/assets/images/active_admin/datepicker/datepicker-input-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcoury/active_admin/master/app/assets/images/active_admin/datepicker/datepicker-input-icon.png -------------------------------------------------------------------------------- /lib/active_admin/comments/views.rb: -------------------------------------------------------------------------------- 1 | require 'active_admin/views' 2 | require 'active_admin/comments/views/active_admin_comments' 3 | require 'active_admin/comments/views/active_admin_comment' 4 | -------------------------------------------------------------------------------- /app/assets/images/active_admin/datepicker/datepicker-next-link-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcoury/active_admin/master/app/assets/images/active_admin/datepicker/datepicker-next-link-icon.png -------------------------------------------------------------------------------- /app/assets/images/active_admin/datepicker/datepicker-prev-link-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcoury/active_admin/master/app/assets/images/active_admin/datepicker/datepicker-prev-link-icon.png -------------------------------------------------------------------------------- /lib/active_admin/views.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | 4 | # Loads all the classes in views/*.rb 5 | Dir[File.expand_path('../views', __FILE__) + "/**/*.rb"].sort.each{ |f| require f } 6 | 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/active_admin/view_helpers/title_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module ViewHelpers 3 | module TitleHelper 4 | 5 | def title(_title) 6 | @page_title = _title 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/active_admin/view_helpers/assigns_with_indifferent_access_helper.rb: -------------------------------------------------------------------------------- 1 | module AssignsWithIndifferentAccessHelper 2 | 3 | def assigns 4 | @assigns_with_indifferent_access_helper ||= HashWithIndifferentAccess.new(super) 5 | end 6 | 7 | end 8 | -------------------------------------------------------------------------------- /lib/generators/active_admin/assets/templates/3.1/active_admin.css.scss: -------------------------------------------------------------------------------- 1 | // Active Admin CSS Styles 2 | @import "active_admin/mixins"; 3 | @import "active_admin/base"; 4 | 5 | // To customize the Active Admin interfaces, add your 6 | // styles here: 7 | -------------------------------------------------------------------------------- /cucumber.yml: -------------------------------------------------------------------------------- 1 | default: --format 'progress' --require features/support/env.rb --require features/step_definitions features 2 | wip: --format 'progress' --require features/support/env.rb --require features/step_definitions features --tags @wip:3 --wip features -------------------------------------------------------------------------------- /lib/active_admin/view_helpers/view_factory_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module ViewHelpers 3 | module ViewFactoryHelper 4 | 5 | def view_factory 6 | active_admin_application.view_factory 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /features/step_definitions/menu_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^I should see a menu item for "([^"]*)"$/ do |name| 2 | page.should have_css('#tabs li a', :text => name) 3 | end 4 | 5 | Then /^I should not see a menu item for "([^"]*)"$/ do |name| 6 | page.should_not have_css('#tabs li a', :text => name) 7 | end 8 | -------------------------------------------------------------------------------- /lib/active_admin/view_helpers/icon_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module ViewHelpers 3 | module IconHelper 4 | 5 | # Render an icon from the Iconic icon set 6 | def icon(*args) 7 | ActiveAdmin::Iconic.icon(*args) 8 | end 9 | 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/active_admin/controller_action.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class ControllerAction 3 | attr_reader :name 4 | def initialize(name, options = {}) 5 | @name, @options = name, options 6 | end 7 | 8 | def http_verb 9 | @options[:method] ||= :get 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/active_admin/page_config.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class PageConfig 3 | 4 | attr_reader :block 5 | 6 | def initialize(options = {}, &block) 7 | @options, @block = options, block 8 | end 9 | 10 | def [](key) 11 | @options[key] 12 | end 13 | 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /features/step_definitions/asset_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^I should see the css file "([^"]*)"$/ do |path| 2 | page.should have_xpath("//link[contains(@href, /stylesheets/#{path})]") 3 | end 4 | 5 | Then /^I should see the js file "([^"]*)"$/ do |path| 6 | page.should have_xpath("//script[contains(@src, /javascripts/#{path})]") 7 | end 8 | -------------------------------------------------------------------------------- /lib/active_admin/view_helpers/sidebar_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module ViewHelpers 3 | module SidebarHelper 4 | 5 | def skip_sidebar! 6 | @skip_sidebar = true 7 | end 8 | 9 | def skip_sidebar? 10 | @skip_sidebar == true 11 | end 12 | 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /features/step_definitions/action_item_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^I should see an action item button "([^"]*)"$/ do |content| 2 | page.should have_css(".action_items a", :text => content) 3 | end 4 | 5 | Then /^I should not see an action item button "([^"]*)"$/ do |content| 6 | page.should_not have_css(".action_items", :text => content) 7 | end 8 | -------------------------------------------------------------------------------- /lib/active_admin/arbre/context.rb: -------------------------------------------------------------------------------- 1 | module Arbre 2 | class Context < Arbre::HTML::Element 3 | def indent_level 4 | # A context does not increment the indent_level 5 | super - 1 6 | end 7 | 8 | def length 9 | to_html.length 10 | end 11 | alias :bytesize :length 12 | 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /features/step_definitions/comment_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^I should see a comment by "([^"]*)"$/ do |name| 2 | Then %{I should see "#{name}" within ".active_admin_comment_author"} 3 | end 4 | 5 | When /^I add a comment "([^"]*)"$/ do |comment| 6 | When %{I fill in "active_admin_comment_body" with "#{comment}"} 7 | And %{I press "Add Comment"} 8 | end 9 | -------------------------------------------------------------------------------- /features/step_definitions/pagination_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^I should not see pagination$/ do 2 | page.should_not have_css(".pagination") 3 | end 4 | 5 | Then /^I should see pagination with (\d+) pages$/ do |count| 6 | Then %{I should see "#{count}" within ".pagination a"} 7 | Then %{I should not see "#{count.to_i + 1}" within ".pagination a"} 8 | end 9 | -------------------------------------------------------------------------------- /features/step_definitions/sidebar_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^I should see a sidebar titled "([^"]*)"$/ do |title| 2 | page.should have_css(".sidebar_section h3", :text => title) 3 | end 4 | 5 | Then /^I should not see a sidebar titled "([^"]*)"$/ do |title| 6 | page.all(:css, "##{title.gsub(" ", '').underscore}_sidebar_section").count.should == 0 7 | end 8 | -------------------------------------------------------------------------------- /lib/active_admin/view_helpers/active_admin_application_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module ViewHelpers 3 | module ActiveAdminApplicationHelper 4 | 5 | # Returns the current Active Admin application instance 6 | def active_admin_application 7 | ActiveAdmin.application 8 | end 9 | 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/active_admin/comments/namespace_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Comments 3 | 4 | module NamespaceHelper 5 | 6 | # Returns true of the namespace allows comments 7 | def comments? 8 | application.allow_comments_in && application.allow_comments_in.include?(name) 9 | end 10 | 11 | end 12 | 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/active_admin/view_helpers/form_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module ViewHelpers 3 | module FormHelper 4 | 5 | def active_admin_form_for(resource, options = {}, &block) 6 | options[:builder] ||= ActiveAdmin::FormBuilder 7 | semantic_form_for resource, options, &block 8 | end 9 | 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/views/active_admin/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Your account has been locked due to an excessive amount of unsuccessful sign in attempts.

4 | 5 |

Click the link below to unlock your account:

6 | 7 |

<%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>

8 | -------------------------------------------------------------------------------- /lib/active_admin/comments/resource_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Comments 3 | 4 | module ResourceHelper 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | attr_accessor :comments 9 | end 10 | 11 | def comments? 12 | namespace.comments? && comments != false 13 | end 14 | end 15 | 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /features/users/logging_out.feature: -------------------------------------------------------------------------------- 1 | Feature: User Logging out 2 | 3 | Logging out of the system as an admin user 4 | 5 | Scenario: Logging out successfully 6 | Given a configuration of: 7 | """ 8 | ActiveAdmin.register Post 9 | """ 10 | And I am logged in 11 | When I go to the dashboard 12 | And I follow "Logout" 13 | Then I should see "Login" 14 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin/mixins/_all.css.scss: -------------------------------------------------------------------------------- 1 | @import "active_admin/mixins/variables"; 2 | @import "active_admin/mixins/reset"; 3 | @import "active_admin/mixins/gradients"; 4 | @import "active_admin/mixins/shadows"; 5 | @import "active_admin/mixins/icons"; 6 | @import "active_admin/mixins/rounded"; 7 | @import "active_admin/mixins/buttons"; 8 | @import "active_admin/mixins/sections"; 9 | -------------------------------------------------------------------------------- /lib/active_admin/views/action_items.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | 4 | class ActionItems < ActiveAdmin::Component 5 | 6 | def build(action_items) 7 | action_items.each do |action_item| 8 | span :class => "action_item" do 9 | instance_eval(&action_item.block) 10 | end 11 | end 12 | end 13 | 14 | end 15 | 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin/_flash_messages.css.scss: -------------------------------------------------------------------------------- 1 | .flash { 2 | @include primary-gradient; 3 | @include shadow; 4 | @include text-shadow(#222); 5 | border: none; 6 | font-weight: bold; 7 | padding: 10px 10px 8px 10px; 8 | margin-bottom: 10px; 9 | color: #fff; 10 | 11 | &.flash_notice { @include gradient(#87a28b, #657b6a); } 12 | &.flash_error { @include gradient(#ca5b4f, #97443c); } 13 | } 14 | -------------------------------------------------------------------------------- /features/index/formats.feature: -------------------------------------------------------------------------------- 1 | Feature: Index Formats 2 | 3 | Scenario: View index with default formats 4 | Given an index configuration of: 5 | """ 6 | ActiveAdmin.register Post 7 | """ 8 | And 1 post exists 9 | When I am on the index page for posts 10 | Then I should see a link to download "CSV" 11 | And I should see a link to download "XML" 12 | And I should see a link to download "JSON" 13 | -------------------------------------------------------------------------------- /lib/active_admin/arbre/attributes.rb: -------------------------------------------------------------------------------- 1 | module Arbre 2 | module HTML 3 | 4 | class Attributes < Hash 5 | 6 | def to_html 7 | self.collect do |name, value| 8 | "#{html_escape(name)}=\"#{html_escape(value)}\"" 9 | end.join(" ") 10 | end 11 | 12 | protected 13 | 14 | def html_escape(s) 15 | ERB::Util.html_escape(s) 16 | end 17 | end 18 | 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## MAC OS 2 | .DS_Store 3 | 4 | ## TEXTMATE 5 | *.tmproj 6 | tmtags 7 | 8 | ## EMACS 9 | *~ 10 | \#* 11 | .\#* 12 | 13 | ## VIM 14 | *.swp 15 | 16 | ## PROJECT::GENERAL 17 | tags 18 | coverage 19 | rdoc 20 | doc 21 | .yardoc 22 | pkg 23 | 24 | ## PROJECT::SPECIFIC 25 | .bundle 26 | spec/rails 27 | *.sqlite3-journal 28 | Gemfile.lock 29 | Gemfile-*.lock 30 | capybara* 31 | viewcumber 32 | test-rails* 33 | public 34 | .rvmrc 35 | -------------------------------------------------------------------------------- /spec/support/detect_rails_version.rb: -------------------------------------------------------------------------------- 1 | # Detects the current version of Rails that is being used 2 | # 3 | # You can pass it in as an ENV variable or it will use 4 | # the current Gemfile.lock to find it 5 | def detect_rails_version 6 | return ENV['RAILS'] if ENV['RAILS'] 7 | return nil unless (File.exists?("Gemfile.lock") || File.symlink?("Gemfile.lock")) 8 | 9 | File.read("Gemfile.lock").match(/^\W*rails \(([a-z\d.]*)\)/) 10 | return $1 11 | end 12 | -------------------------------------------------------------------------------- /app/views/active_admin/devise/unlocks/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend unlock instructions

2 | 3 | <%= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |

<%= f.label :email %>
7 | <%= f.text_field :email %>

8 | 9 |

<%= f.submit "Resend unlock instructions" %>

10 | <% end %> 11 | 12 | <%= render :partial => "devise/shared/links" %> -------------------------------------------------------------------------------- /lib/active_admin/scope.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class Scope 3 | 4 | attr_reader :name, :scope_method, :id, :scope_block 5 | 6 | def initialize(name, method = nil, &block) 7 | @name = name.to_s.titleize 8 | @scope_method = method || name.to_sym 9 | @id = @name.gsub(' ', '').underscore 10 | if block_given? 11 | @scope_method = nil 12 | @scope_block = block 13 | end 14 | end 15 | 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/active_admin/comments/configuration.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Comments 3 | 4 | module Configuration 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | # Set the namespaces that can create and view comments 9 | # 10 | # config.allow_comments_in = [:admin, :root] 11 | # 12 | attr_accessor_with_default :allow_comments_in, [:admin] 13 | end 14 | 15 | end 16 | 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/active_admin/views/dashboard_section_renderer.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | class DashboardSection < ActiveAdmin::Views::Panel 4 | 5 | def build(section) 6 | @section = section 7 | super(title, :icon => @section.icon) 8 | instance_eval &@section.block 9 | end 10 | 11 | protected 12 | 13 | def title 14 | @section.name.to_s.titleize 15 | end 16 | 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /features/step_definitions/dashboard_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^I should see the default welcome message$/ do 2 | Then %{I should see "Welcome to Active Admin" within "#dashboard_default_message"} 3 | end 4 | 5 | Then /^I should not see the default welcome message$/ do 6 | Then %{I should not see "Welcome to Active Admin"} 7 | end 8 | 9 | Then /^I should see a dashboard widget "([^"]*)"$/ do |name| 10 | Then %{I should see "#{name}" within ".dashboard .panel h3"} 11 | end 12 | -------------------------------------------------------------------------------- /app/views/active_admin/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Forgot your password?

3 | 4 | <%= active_admin_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| 5 | f.inputs do 6 | f.input :email 7 | end 8 | f.buttons do 9 | f.commit_button "Reset My Password" 10 | end 11 | end %> 12 | 13 | <%= render :partial => "devise/shared/links" %> 14 |
15 | -------------------------------------------------------------------------------- /lib/active_admin/component.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class Component < Arbre::HTML::Div 3 | 4 | # By default components render a div 5 | def tag_name 6 | 'div' 7 | end 8 | 9 | def initialize(*) 10 | super 11 | add_class default_class_name 12 | end 13 | 14 | protected 15 | 16 | # By default, add a css class named after the ruby class 17 | def default_class_name 18 | self.class.name.demodulize.underscore 19 | end 20 | 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/views/active_admin/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Someone has requested a link to change your password, and you can do this through the link below.

4 | 5 |

<%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>

6 | 7 |

If you didn't request this, please ignore this email.

8 |

Your password won't change until you access the link above and create a new one.

9 | -------------------------------------------------------------------------------- /spec/unit/component_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class MockComponentClass < ActiveAdmin::Component; end 4 | 5 | describe ActiveAdmin::Component do 6 | 7 | let(:component_class){ MockComponentClass } 8 | let(:component){ component_class.new } 9 | 10 | it "should be a subclass of an html div" do 11 | ActiveAdmin::Component.ancestors.should include(Arbre::HTML::Div) 12 | end 13 | 14 | it "should render to a div, even as a subclass" do 15 | component.tag_name.should == 'div' 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /lib/active_admin/arbre/class_list.rb: -------------------------------------------------------------------------------- 1 | require 'set' 2 | 3 | module Arbre 4 | module HTML 5 | 6 | # Holds a set of classes 7 | class ClassList < Set 8 | 9 | def add(class_names) 10 | class_names.to_s.split(" ").each do |class_name| 11 | super(class_name) 12 | end 13 | self 14 | end 15 | alias :<< :add 16 | 17 | def to_s 18 | to_html 19 | end 20 | 21 | def to_html 22 | to_a.join(" ") 23 | end 24 | 25 | end 26 | 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/active_admin/resource_controller/action_builder.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class ResourceController < ::InheritedResources::Base 3 | 4 | module ActionBuilder 5 | extend ActiveSupport::Concern 6 | 7 | module ClassMethods 8 | 9 | def clear_member_actions! 10 | active_admin_config.clear_member_actions! 11 | end 12 | 13 | def clear_collection_actions! 14 | active_admin_config.clear_collection_actions! 15 | end 16 | end 17 | 18 | end 19 | 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/generators/active_admin/resource/resource_generator.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Generators 3 | class ResourceGenerator < Rails::Generators::NamedBase 4 | desc "Installs ActiveAdmin in a rails 3 application" 5 | 6 | def self.source_root 7 | @_active_admin_source_root ||= File.expand_path("../templates", __FILE__) 8 | end 9 | 10 | def generate_config_file 11 | template "admin.rb", "app/admin/#{file_path.gsub('/', '_').pluralize}.rb" 12 | end 13 | 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/unit/generators/install_rails_3_1_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | 4 | if Rails.version[0..2] == '3.1' 5 | describe "Installing in Rails 3.1" do 6 | 7 | it "should add active_admin.css to app/assets/stylesheets/" do 8 | File.exists?(Rails.root + "app/assets/stylesheets/active_admin.css.scss").should be_true 9 | end 10 | 11 | it "should add active_admin.js to app/assets/javascripts" do 12 | File.exists?(Rails.root + "app/assets/javascripts/active_admin.js").should be_true 13 | end 14 | 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin/mixins/_variables.css.scss: -------------------------------------------------------------------------------- 1 | // Variables used throughout Active Admin 2 | $horizontal-page-margin: 30px; 3 | $primary-color: #5E6469; 4 | $secondary-color: #f0f0f0; 5 | $text-color: #323537; 6 | $link-color: #38678b; 7 | $section-header-text-color: $primary-color; 8 | $cell-padding: 5px 10px 3px 10px; 9 | $cell-horizontal-padding: 12px; 10 | $current-menu-item-background: lighten($primary-color, 12%); 11 | $hover-menu-item-background: lighten($primary-color, 12%); 12 | $table-stripe-color: lighten($primary-color, 57%); 13 | -------------------------------------------------------------------------------- /spec/unit/arbre/html/context_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Arbre::Context do 4 | include Arbre::HTML 5 | let(:assigns){ {} } 6 | 7 | before do 8 | h1 # Add some HTML to the context 9 | end 10 | 11 | it "should return a bytesize" do 12 | current_dom_context.bytesize.should == 10 13 | end 14 | 15 | it "should return a length" do 16 | current_dom_context.length.should == 10 17 | end 18 | 19 | it "should not increment the indent_level" do 20 | current_dom_context.indent_level.should == -1 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /features/index/index_as_block.feature: -------------------------------------------------------------------------------- 1 | Feature: Index as Block 2 | 3 | Viewing the resource as a block which is renderered by the user 4 | 5 | Scenario: Viewing the index as a block 6 | Given a post with the title "Hello World from Block" exists 7 | And an index configuration of: 8 | """ 9 | ActiveAdmin.register Post do 10 | index :as => :block do |post| 11 | span(link_to(post.title, admin_post_path(post))) 12 | end 13 | end 14 | """ 15 | Then I should see "Hello World from Block" within ".index_as_block" 16 | -------------------------------------------------------------------------------- /lib/active_admin/arbre/collection.rb: -------------------------------------------------------------------------------- 1 | module Arbre 2 | module HTML 3 | 4 | # Stores a collection of Element objects 5 | class Collection < Array 6 | 7 | def +(other) 8 | self.class.new(super) 9 | end 10 | 11 | def -(other) 12 | self.class.new(super) 13 | end 14 | 15 | def &(other) 16 | self.class.new(super) 17 | end 18 | 19 | def to_html 20 | self.collect do |element| 21 | element.to_html 22 | end.join.html_safe 23 | end 24 | end 25 | 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/active_admin/sass/css_loader.rb: -------------------------------------------------------------------------------- 1 | require 'sass/importers' 2 | 3 | # This monkey patches the SASS filesystem importer to work with files 4 | # that are named *.css.scss. This allows us to be compatible with both 5 | # Rails 3.0.* and Rails 3.1 6 | # 7 | # This should only be loaded in Rails 3.0 apps. 8 | class Sass::Importers::Filesystem 9 | 10 | # We want to ensure that all *.css.scss files are loaded as scss files 11 | def extensions_with_css 12 | extensions_without_css.merge('css.scss' => :scss) 13 | end 14 | alias_method_chain :extensions, :css 15 | 16 | end 17 | -------------------------------------------------------------------------------- /features/first_boot.feature: -------------------------------------------------------------------------------- 1 | Feature: First Boot 2 | 3 | As a developer 4 | In order to ensure I have a great first experience 5 | I want Active Admin to just work on install 6 | 7 | Scenario: Visiting /admin and logging in with no configurations 8 | Given a configuration of: 9 | """ 10 | """ 11 | And an admin user "admin@example.com" exists 12 | When I go to the dashboard 13 | When I fill in "Email" with "admin@example.com" 14 | And I fill in "Password" with "password" 15 | And I press "Login" 16 | Then I should be on the the dashboard 17 | -------------------------------------------------------------------------------- /lib/generators/active_admin/install/templates/migrations/1_create_admin_notes.rb: -------------------------------------------------------------------------------- 1 | class CreateAdminNotes < ActiveRecord::Migration 2 | def self.up 3 | create_table :admin_notes do |t| 4 | t.references :resource, :polymorphic => true, :null => false 5 | t.references :admin_user, :polymorphic => true 6 | t.text :body 7 | t.timestamps 8 | end 9 | add_index :admin_notes, [:resource_type, :resource_id] 10 | add_index :admin_notes, [:admin_user_type, :admin_user_id] 11 | end 12 | 13 | def self.down 14 | drop_table :admin_notes 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin/mixins/_icons.css.scss: -------------------------------------------------------------------------------- 1 | span.icon { vertical-align: middle; display: inline-block; } 2 | span.icon svg { vertical-align: baseline; } 3 | 4 | @mixin icon-color ($color) { 5 | span.icon svg { 6 | path, polygon, rect, circle { fill: $color !important; } 7 | } 8 | } 9 | 10 | @mixin icon-size ($size) { 11 | span.icon { width: $size; height: $size; } 12 | span.icon svg { width: $size; height: $size; } 13 | } 14 | 15 | @mixin icon($color, $size) { 16 | @include icon-color($color); 17 | @include icon-size($size); 18 | } 19 | 20 | @include icon-size(0.8em); 21 | -------------------------------------------------------------------------------- /app/views/active_admin/resource/index.csv.erb: -------------------------------------------------------------------------------- 1 | <%- 2 | csv_lib = if RUBY_VERSION =~ /^1.8/ 3 | require 'fastercsv' 4 | FasterCSV 5 | else 6 | require 'csv' 7 | CSV 8 | end 9 | 10 | csv_output = csv_lib.generate do |csv| 11 | columns = active_admin_config.csv_builder.columns 12 | csv << columns.map(&:name) 13 | collection.each do |resource| 14 | csv << columns.map do |column| 15 | call_method_or_proc_on resource, column.data 16 | end 17 | end 18 | end 19 | %> 20 | <%= csv_output.html_safe %> 21 | -------------------------------------------------------------------------------- /features/index/pagination.feature: -------------------------------------------------------------------------------- 1 | Feature: Index Pagination 2 | 3 | Background: 4 | Given an index configuration of: 5 | """ 6 | ActiveAdmin.register Post 7 | """ 8 | Scenario: Viewing index when one page of resources exist 9 | Given 20 posts exist 10 | When I am on the index page for posts 11 | Then I should see "Displaying all 20 Posts" 12 | And I should not see pagination 13 | 14 | Scenario: Viewing index when multiple pages of resources exist 15 | Given 31 posts exist 16 | When I am on the index page for posts 17 | Then I should see pagination with 2 pages 18 | -------------------------------------------------------------------------------- /app/views/active_admin/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Change your password

2 | 3 | <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | <%= f.hidden_field :reset_password_token %> 6 | 7 |

<%= f.label :password %>
8 | <%= f.password_field :password %>

9 | 10 |

<%= f.label :password_confirmation %>
11 | <%= f.password_field :password_confirmation %>

12 | 13 |

<%= f.submit "Change my password" %>

14 | <% end %> 15 | 16 | <%= render :partial => "devise/shared/links" %> -------------------------------------------------------------------------------- /lib/active_admin/asset_registration.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module AssetRegistration 3 | 4 | # Stylesheets 5 | 6 | def register_stylesheet(name) 7 | stylesheets << name 8 | end 9 | 10 | def stylesheets 11 | @stylesheets ||= [] 12 | end 13 | 14 | def clear_stylesheets! 15 | @stylesheets = [] 16 | end 17 | 18 | 19 | # Javascripts 20 | 21 | def register_javascript(name) 22 | javascripts << name 23 | end 24 | 25 | def javascripts 26 | @javascripts ||= [] 27 | end 28 | 29 | def clear_javascripts! 30 | @javascripts = [] 31 | end 32 | 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/active_admin/comments/comment.rb: -------------------------------------------------------------------------------- 1 | require 'kaminari/models/active_record_extension' 2 | 3 | module ActiveAdmin 4 | 5 | # manually initialize kaminari for this model 6 | ::ActiveRecord::Base.send :include, Kaminari::ActiveRecordExtension 7 | 8 | class Comment < ActiveRecord::Base 9 | self.table_name = "active_admin_comments" 10 | 11 | belongs_to :resource, :polymorphic => true 12 | belongs_to :author, :polymorphic => true 13 | 14 | validates_presence_of :resource_id 15 | validates_presence_of :resource_type 16 | validates_presence_of :body 17 | validates_presence_of :namespace 18 | end 19 | 20 | end 21 | 22 | -------------------------------------------------------------------------------- /features/step_definitions/attribute_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^I should see the attribute "([^"]*)" with "([^"]*)"$/ do |title, value| 2 | page.should have_css('.attributes_table th', :text => title) 3 | page.should have_css('.attributes_table td', :text => value) 4 | end 5 | 6 | Then /^I should see the attribute "([^"]*)" with a nicely formatted datetime$/ do |title| 7 | th = page.find('.attributes_table th', :text => title) 8 | page.find(:xpath, th.path.gsub(/th$/, 'td')).text.should =~ /\w+ \d{1,2}, \d{4} \d{2}:\d{2}/ 9 | end 10 | 11 | Then /^I should not see the attribute "([^"]*)"$/ do |title| 12 | page.should_not have_css('.attributes_table th', :text => title) 13 | end 14 | -------------------------------------------------------------------------------- /lib/active_admin/event.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | 3 | class EventDispatcher 4 | def initialize 5 | @events = {} 6 | end 7 | 8 | def clear_all_subscribers! 9 | @events = {} 10 | end 11 | 12 | def subscribe(event, &block) 13 | @events[event] ||= [] 14 | @events[event] << block 15 | end 16 | 17 | def subscribers(event) 18 | @events[event] || [] 19 | end 20 | 21 | def dispatch(event, *args) 22 | subscribers(event).each do |subscriber| 23 | subscriber.call(*args) 24 | end 25 | end 26 | end 27 | 28 | # ActiveAdmin::Event is set to a dispatcher 29 | Event = EventDispatcher.new 30 | 31 | end 32 | -------------------------------------------------------------------------------- /spec/unit/view_factory_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | def it_should_have_view(key, value) 4 | it "should have #{value} for view key '#{key}'" do 5 | subject.send(key).should == value 6 | end 7 | end 8 | 9 | describe ActiveAdmin::ViewFactory do 10 | 11 | it_should_have_view :global_navigation, ActiveAdmin::Views::TabsRenderer 12 | it_should_have_view :action_items, ActiveAdmin::Views::ActionItems 13 | it_should_have_view :header, ActiveAdmin::Views::HeaderRenderer 14 | it_should_have_view :blank_slate, ActiveAdmin::Views::BlankSlate 15 | 16 | it_should_have_view :dashboard_page, ActiveAdmin::Views::Pages::Dashboard 17 | 18 | end 19 | -------------------------------------------------------------------------------- /lib/active_admin/views/components/panel.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | 4 | class Panel < ActiveAdmin::Component 5 | builder_method :panel 6 | 7 | def build(title, attributes = {}) 8 | icon_name = attributes.delete(:icon) 9 | icn = icon_name ? icon(icon_name) : "" 10 | super(attributes) 11 | add_class "panel" 12 | @title = h3(icn + title.to_s) 13 | @contents = div(:class => "panel_contents") 14 | end 15 | 16 | def add_child(child) 17 | if @contents 18 | @contents << child 19 | else 20 | super 21 | end 22 | end 23 | end 24 | 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/active_admin/view_helpers.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module ViewHelpers 3 | 4 | # Require all ruby files in the view helpers dir 5 | Dir[File.expand_path('../view_helpers', __FILE__) + "/*.rb"].each{|f| require f } 6 | 7 | include AssignsWithIndifferentAccessHelper 8 | include ActiveAdminApplicationHelper 9 | include RendererHelper 10 | include AutoLinkHelper 11 | include BreadcrumbHelper 12 | include DisplayHelper 13 | include IconHelper 14 | include MethodOrProcHelper 15 | include SidebarHelper 16 | include FormHelper 17 | include FilterFormHelper 18 | include TitleHelper 19 | include ViewFactoryHelper 20 | 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /features/menu.feature: -------------------------------------------------------------------------------- 1 | Feature: Menu 2 | 3 | Background: 4 | Given I am logged in 5 | 6 | Scenario: Hide the menu item 7 | Given a configuration of: 8 | """ 9 | ActiveAdmin.register Post do 10 | menu false 11 | end 12 | """ 13 | When I am on the dashboard 14 | Then I should not see a menu item for "Posts" 15 | 16 | @wip 17 | Scenario: Set the menu item label 18 | Given a configuration of: 19 | """ 20 | ActiveAdmin.register Post do 21 | menu :label => "Articles" 22 | end 23 | """ 24 | When I am on the dashboard 25 | Then I should see a menu item for "Articles" 26 | And I should not see a menu item for "Posts" 27 | -------------------------------------------------------------------------------- /lib/active_admin/views/index_as_block.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | 4 | # Simplest rendering possible. Calls the block for each element in the collection. 5 | # 6 | # Example: 7 | # 8 | # ActiveAdmin.register Post do 9 | # index :as => :block do |post| 10 | # # render the post partial (app/views/admin/posts/_post) 11 | # render 'post', :post => post 12 | # end 13 | # end 14 | class IndexAsBlock < ActiveAdmin::Component 15 | 16 | def build(page_config, collection) 17 | collection.each do |obj| 18 | instance_exec(obj, &page_config.block) 19 | end 20 | end 21 | 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin/mixins/_shadows.css.scss: -------------------------------------------------------------------------------- 1 | @mixin shadow($x: 0, $y: 1px, $blur: 2px, $color: #aaa) { 2 | box-shadow: $x $y $blur $color; 3 | -moz-box-shadow: $x $y $blur $color; 4 | -webkit-box-shadow: $x $y $blur $color; 5 | } 6 | 7 | @mixin no-shadow { 8 | box-shadow: none; 9 | -moz-box-shadow: none; 10 | -webkit-box-shadow: none; 11 | } 12 | 13 | @mixin inset-shadow($x: 0, $y: 1px, $blur: 2px, $color: #aaa) { 14 | box-shadow: inset $x $y $blur $color; 15 | -moz-box-shadow: inset $x $y $blur $color; 16 | -webkit-box-shadow: inset $x $y $blur $color; 17 | } 18 | 19 | @mixin text-shadow($color: #fff, $x: 0, $y: 1px, $blur: 0) { 20 | text-shadow: $color $x $y $blur; 21 | } 22 | -------------------------------------------------------------------------------- /lib/active_admin/arbre/text_node.rb: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | 3 | module Arbre 4 | module HTML 5 | 6 | class TextNode < Element 7 | 8 | builder_method :text_node 9 | 10 | # Builds a text node from a string 11 | def self.from_string(string) 12 | node = new 13 | node.build(string) 14 | node 15 | end 16 | 17 | def add_child(*args) 18 | raise "TextNodes do not have children" 19 | end 20 | 21 | def build(string) 22 | @content = string 23 | end 24 | 25 | def tag_name 26 | nil 27 | end 28 | 29 | def to_html 30 | ERB::Util.html_escape(@content.to_html) 31 | end 32 | end 33 | 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /features/step_definitions/user_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^I am logged out$/ do 2 | visit destroy_admin_user_session_path 3 | end 4 | 5 | Given /^I am logged in$/ do 6 | Given 'an admin user "admin@example.com" exists' 7 | visit destroy_admin_user_session_path 8 | visit new_admin_user_session_path 9 | fill_in "Email", :with => "admin@example.com" 10 | fill_in "Password", :with => "password" 11 | click_button "Login" 12 | end 13 | 14 | Given /^an admin user "([^"]*)" exists$/ do |admin_email| 15 | unless AdminUser.find_by_email(admin_email) 16 | AdminUser.create! :email => admin_email, 17 | :password => "password", 18 | :password_confirmation => "password" 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/views/active_admin/devise/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

<%= title "#{active_admin_application.site_title} Login" %>

3 | 4 | <% scope = Devise::Mapping.find_scope!(resource_name) %> 5 | <%= active_admin_form_for(resource, :as => resource_name, :url => send(:"#{scope}_session_path"), :html => { :id => "session_new" }) do |f| 6 | f.inputs do 7 | Devise.authentication_keys.each { |key| f.input key } 8 | f.input :password 9 | f.input :remember_me, :as => :boolean, :if => false #devise_mapping.rememberable? } 10 | end 11 | f.buttons do 12 | f.commit_button "Login" 13 | end 14 | end 15 | %> 16 | 17 | <%= render :partial => "devise/shared/links" %> 18 |
19 | -------------------------------------------------------------------------------- /spec/unit/helpers/settings_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'active_admin/helpers/settings' 3 | 4 | describe ActiveAdmin::Settings do 5 | 6 | # A Class with settings module included 7 | let(:klass) do 8 | Class.new do 9 | include ActiveAdmin::Settings 10 | def initialize 11 | initialize_defaults! 12 | end 13 | end 14 | end 15 | 16 | it "should add a new setting with a default" do 17 | klass.setting :my_setting, "Hello World" 18 | klass.default_settings[:my_setting].should == "Hello World" 19 | end 20 | 21 | it "should initialize the defaults" do 22 | klass.setting :my_setting, "Hello World" 23 | klass.new.my_setting.should == "Hello World" 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /lib/active_admin/views/pages/new.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | module Pages 4 | class New < Base 5 | 6 | def title 7 | I18n.t('active_admin.new_model', :model => active_admin_config.resource_name) 8 | end 9 | 10 | def main_content 11 | config = self.form_config.dup 12 | config.delete(:block) 13 | config.reverse_merge!({ 14 | :url => collection_path 15 | }) 16 | 17 | if form_config[:partial] 18 | render(form_config[:partial]) 19 | else 20 | active_admin_form_for(resource, config, &form_config[:block]) 21 | end 22 | end 23 | end 24 | end 25 | 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/active_admin/views/pages/edit.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | module Pages 4 | class Edit < Base 5 | 6 | def title 7 | I18n.t('active_admin.edit_model', :model => active_admin_config.resource_name) 8 | end 9 | 10 | def main_content 11 | config = self.form_config.dup 12 | config.delete(:block) 13 | config.reverse_merge!({ 14 | :url => resource_path(resource) 15 | }) 16 | 17 | if form_config[:partial] 18 | render form_config[:partial] 19 | else 20 | active_admin_form_for resource, config, &form_config[:block] 21 | end 22 | end 23 | 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/unit/dashboard_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | 4 | module Admin 5 | class DashboardController < ActiveAdmin::Dashboards::DashboardController 6 | end 7 | end 8 | class DashboardController < ActiveAdmin::Dashboards::DashboardController; end 9 | 10 | describe ActiveAdmin::Dashboards::DashboardController do 11 | 12 | describe "getting the namespace name" do 13 | subject{ controller.send :namespace } 14 | 15 | context "when admin namespace" do 16 | let(:controller){ Admin::DashboardController.new } 17 | it { should == :admin } 18 | end 19 | 20 | context "when root namespace" do 21 | let(:controller){ DashboardController.new } 22 | it { should == :root } 23 | end 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /features/dashboard.feature: -------------------------------------------------------------------------------- 1 | Feature: Dashboard 2 | 3 | Background: 4 | Given I am logged in 5 | 6 | 7 | Scenario: With no configuration 8 | Given a configuration of: 9 | """ 10 | """ 11 | When I go to the dashboard 12 | Then I should see the default welcome message 13 | 14 | Scenario: Displaying a dashboard widget 15 | Given a configuration of: 16 | """ 17 | ActiveAdmin::Dashboards.build do 18 | section 'Hello World' do 19 | para "Hello world from the content" 20 | end 21 | end 22 | """ 23 | When I go to the dashboard 24 | Then I should not see the default welcome message 25 | And I should see a dashboard widget "Hello World" 26 | And I should see "Hello world from the content" 27 | -------------------------------------------------------------------------------- /lib/active_admin/views/components/sidebar_section.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | 4 | class SidebarSection < Panel 5 | builder_method :sidebar_section 6 | 7 | # Takes a ActiveAdmin::Sidebar::Section instance 8 | def build(section) 9 | @section = section 10 | super(@section.title, :icon => @section.icon) 11 | self.id = @section.id 12 | build_sidebar_content 13 | end 14 | 15 | protected 16 | 17 | def build_sidebar_content 18 | if @section.block 19 | rvalue = instance_eval(&@section.block) 20 | self << rvalue if rvalue.is_a?(String) 21 | else 22 | text_node render(@section.partial_name) 23 | end 24 | end 25 | end 26 | 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/active_admin/comments/show_page_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Comments 3 | 4 | # Adds #active_admin_comments to the show page for use 5 | # and sets it up on the default main content 6 | module ShowPageHelper 7 | 8 | # Add admin comments to the main content if they are 9 | # turned on for the current resource 10 | def default_main_content 11 | super 12 | active_admin_comments if active_admin_config.comments? 13 | end 14 | 15 | # Display the comments for the resource. Same as calling 16 | # #active_admin_comments_for with the current resource 17 | def active_admin_comments(*args, &block) 18 | active_admin_comments_for(resource, *args, &block) 19 | end 20 | end 21 | 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/unit/reloader_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | # Ensure we have both constants to play with 4 | begin 5 | ActionDispatch::Reloader 6 | rescue 7 | module ActionDispatch; module Reloader; end; end 8 | end 9 | 10 | begin 11 | ActionDispatch::Callbacks 12 | rescue 13 | module ActionDispatch; module Callbacks; end; end 14 | end 15 | 16 | 17 | describe ActiveAdmin::Reloader do 18 | 19 | it "should use ActionDispatch::Reloader if rails 3.1" do 20 | reloader = ActiveAdmin::Reloader.new '3.1.0' 21 | reloader.reloader_class.should == ActionDispatch::Reloader 22 | end 23 | 24 | it "should use ActionDispatch::Callbacks if rails 3.0" do 25 | reloader = ActiveAdmin::Reloader.new '3.0.0' 26 | reloader.reloader_class.should == ActionDispatch::Callbacks 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /features/comments/viewing_index.feature: -------------------------------------------------------------------------------- 1 | Feature: Viewing Index of Comments 2 | 3 | Background: 4 | Given a post with the title "Hello World" written by "Jane Doe" exists 5 | Given a show configuration of: 6 | """ 7 | ActiveAdmin.register Post 8 | """ 9 | 10 | Scenario: Viewing all commments for a namespace 11 | When I add a comment "Hello from Comment" 12 | When I am on the index page for comments 13 | Then I should see a table header with "Body" 14 | And I should see a table header with "Resource" 15 | And I should see a table header with "Author" 16 | And I should see "Hello from Comment" 17 | And I should see a link to "Hello World" 18 | And I should see "admin@example.com" 19 | And I should not see an action item button "New Comment" 20 | -------------------------------------------------------------------------------- /lib/active_admin/arbre/document.rb: -------------------------------------------------------------------------------- 1 | module Arbre 2 | module HTML 3 | 4 | class Document < Tag 5 | 6 | def build(*args) 7 | super 8 | build_head 9 | build_body 10 | end 11 | 12 | def document 13 | self 14 | end 15 | 16 | def tag_name 17 | 'html' 18 | end 19 | 20 | def doctype 21 | ''.html_safe 22 | end 23 | 24 | def to_html 25 | doctype + super 26 | end 27 | 28 | protected 29 | 30 | def build_head 31 | @head = head do 32 | meta :"http-equiv" => "Content-type", :content => "text/html; charset=utf-8" 33 | end 34 | end 35 | 36 | def build_body 37 | @body = body 38 | end 39 | end 40 | 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/unit/display_name_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "display names" do 4 | 5 | include ActiveAdmin::ViewHelpers 6 | 7 | [:display_name, :full_name, :name, :username, :login, :title, :email, :to_s].each do |m| 8 | it "should return #{m} if defined" do 9 | r = Class.new do 10 | define_method m do 11 | m.to_s 12 | end 13 | end.new 14 | display_name(r).should == m.to_s 15 | end 16 | end 17 | 18 | it "should memeoize the result for the class" do 19 | c = Class.new do 20 | def name 21 | "My Name" 22 | end 23 | end 24 | display_name(c.new).should == "My Name" 25 | ActiveAdmin.application.should_not_receive(:display_name_methods) 26 | display_name(c.new).should == "My Name" 27 | end 28 | 29 | end 30 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin/mixins/_sections.css.scss: -------------------------------------------------------------------------------- 1 | @mixin section-header { 2 | @include secondary-gradient; 3 | @include shadow; 4 | @include text-shadow; 5 | border-bottom: 1px solid #ededed; 6 | padding: 5px 10px 3px 10px; 7 | font-size: 1.0em; 8 | font-weight: bold; 9 | line-height: 140%; 10 | margin-bottom: 0.5em; 11 | color: $section-header-text-color; 12 | @include icon($section-header-text-color, 1.0em); 13 | span.icon { margin-right: 5px; } 14 | } 15 | 16 | @mixin section-background { 17 | background: #f4f4f4; 18 | @include rounded(4px); 19 | @include inset-shadow(0,1px,4px, #ddd); 20 | } 21 | 22 | @mixin section { 23 | @include section-background; 24 | h3 { @include section-header; } 25 | margin-bottom: 20px; 26 | > div { padding: 3px 15px 15px 15px; } 27 | } 28 | -------------------------------------------------------------------------------- /lib/generators/active_admin/assets/assets_generator.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Generators 3 | class AssetsGenerator < Rails::Generators::Base 4 | 5 | def self.source_root 6 | @_active_admin_source_root ||= File.expand_path("../templates", __FILE__) 7 | end 8 | 9 | def install_assets 10 | if ActiveAdmin.use_asset_pipeline? 11 | template '3.1/active_admin.js', 'app/assets/javascripts/active_admin.js' 12 | template '3.1/active_admin.css.scss', 'app/assets/stylesheets/active_admin.css.scss' 13 | else 14 | template '3.0/active_admin.js', 'public/javascripts/active_admin.js' 15 | directory '../../../../../app/assets/images/active_admin', 'public/images/active_admin' 16 | end 17 | end 18 | 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/support/integration_example_group.rb: -------------------------------------------------------------------------------- 1 | require 'action_dispatch' 2 | require 'capybara/rails' 3 | require 'capybara/dsl' 4 | 5 | module RSpec 6 | module Rails 7 | module IntegrationExampleGroup 8 | extend ActiveSupport::Concern 9 | 10 | include ActionDispatch::Integration::Runner 11 | include RSpec::Rails::TestUnitAssertionAdapter 12 | include ActionDispatch::Assertions 13 | include Capybara::DSL 14 | include RSpec::Matchers 15 | 16 | module InstanceMethods 17 | def app 18 | ::Rails.application 19 | end 20 | 21 | def last_response 22 | page 23 | end 24 | end 25 | 26 | included do 27 | before do 28 | @router = ::Rails.application.routes 29 | end 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/active_admin/dashboards/section.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Dashboards 3 | class Section 4 | 5 | DEFAULT_PRIORITY = 10 6 | 7 | attr_accessor :name, :block 8 | attr_reader :namespace, :options 9 | 10 | def initialize(namespace, name, options = {}, &block) 11 | @namespace = namespace 12 | @name = name 13 | @options = options 14 | @block = block 15 | end 16 | 17 | def priority 18 | @options[:priority] || DEFAULT_PRIORITY 19 | end 20 | 21 | def icon 22 | @options[:icon] 23 | end 24 | 25 | # Sort by priority then by name 26 | def <=>(other) 27 | result = priority <=> other.priority 28 | result = name.to_s <=> other.name.to_s if result == 0 29 | result 30 | end 31 | 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/unit/action_items_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin::ActionItems do 4 | 5 | describe "rendering" do 6 | include Arbre::HTML 7 | let(:assigns){ {} } 8 | let(:action_item) do 9 | ActiveAdmin::ActionItems::ActionItem.new do 10 | h2 "Hello World" 11 | end 12 | end 13 | 14 | let(:rendered){ insert_tag ActiveAdmin::Views::ActionItems, [action_item]} 15 | 16 | it "should have a parent .action_items div" do 17 | rendered.tag_name.should == 'div' 18 | rendered.class_list.should include('action_items') 19 | end 20 | 21 | it "should render the contents of each action item" do 22 | rendered.children.size.should == 1 23 | rendered.content.strip.should == "\n

Hello World

\n
" 24 | end 25 | end 26 | 27 | end 28 | -------------------------------------------------------------------------------- /features/step_definitions/index_scope_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^I should see the scope "([^"]*)"$/ do |name| 2 | Then %{I should see "#{name}" within ".scopes"} 3 | end 4 | 5 | Then /^I should see the scope "([^"]*)" selected$/ do |name| 6 | Then %{I should see "#{name}" within ".scopes span.selected"} 7 | end 8 | 9 | Then /^I should see the scope "([^"]*)" not selected$/ do |name| 10 | Then %{I should see the scope "#{name}"} 11 | page.should_not have_css('.scopes span.selected', :text => name) 12 | end 13 | 14 | Then /^I should see the scope "([^"]*)" with the count (\d+)$/ do |name, count| 15 | Then %{I should see "#{count}" within ".scopes .#{name.downcase} .count"} 16 | end 17 | 18 | Then /^I should see (\d+) ([\w]*) in the table$/ do |count, resource_type| 19 | page.should have_css("table##{resource_type} tr > td:first", :count => count.to_i) 20 | end 21 | -------------------------------------------------------------------------------- /lib/active_admin/arbre.rb: -------------------------------------------------------------------------------- 1 | require "active_admin/arbre/html" 2 | require "active_admin/arbre/attributes" 3 | require "active_admin/arbre/core_extensions" 4 | require "active_admin/arbre/element" 5 | require "active_admin/arbre/context" 6 | require "active_admin/arbre/collection" 7 | require "active_admin/arbre/class_list" 8 | require "active_admin/arbre/tag" 9 | require "active_admin/arbre/document" 10 | require "active_admin/arbre/html5_elements" 11 | require "active_admin/arbre/text_node" 12 | 13 | # Arbre - The DOM Tree in Ruby 14 | # 15 | # Arbre is a ruby library for building HTML in pure Object Oriented Ruby 16 | module Arbre 17 | end 18 | 19 | require 'action_view' 20 | 21 | ActionView::Template.register_template_handler :arb, lambda { |template| 22 | "self.class.send :include, Arbre::HTML; @_helpers = self; begin; #{template.source}; end; current_dom_context" 23 | } 24 | -------------------------------------------------------------------------------- /spec/unit/resource/scopes_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module ActiveAdmin 4 | describe Resource, "Scopes" do 5 | 6 | before { load_defaults! } 7 | 8 | let(:application){ ActiveAdmin::Application.new } 9 | let(:namespace){ Namespace.new(application, :admin) } 10 | 11 | def config(options = {}) 12 | @config ||= Resource.new(namespace, Category, options) 13 | end 14 | 15 | describe "adding a scope" do 16 | 17 | it "should add a scope" do 18 | config.scope :published 19 | config.scopes.first.should be_a(ActiveAdmin::Scope) 20 | config.scopes.first.name.should == "Published" 21 | end 22 | 23 | it "should retrive a scope by its id" do 24 | config.scope :published 25 | config.get_scope_by_id(:published).name.should == "Published" 26 | end 27 | 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/active_admin/view_helpers/method_or_proc_helper.rb: -------------------------------------------------------------------------------- 1 | module MethodOrProcHelper 2 | 3 | # Many times throughout the views we want to either call a method on an object 4 | # or instance_exec a proc passing in the object as the first parameter. This 5 | # method takes care of this functionality. 6 | # 7 | # call_method_or_proc_on(@my_obj, :size) same as @my_obj.size 8 | # OR 9 | # proc = Proc.new{|s| s.size } 10 | # call_method_or_proc_on(@my_obj, proc) 11 | # 12 | def call_method_or_proc_on(obj, symbol_or_proc, options = {}) 13 | exec = options[:exec].nil? ? true : options[:exec] 14 | case symbol_or_proc 15 | when Symbol, String 16 | obj.send(symbol_or_proc.to_sym) 17 | when Proc 18 | if exec 19 | instance_exec(obj, &symbol_or_proc) 20 | else 21 | symbol_or_proc.call(obj) 22 | end 23 | end 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /lib/active_admin/reloader.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | # Deals with reloading Active Admin on each request in 3 | # development and once in production. 4 | class Reloader 5 | 6 | # @param [String] rails_version 7 | # The version of Rails we're using. We use this to switch between 8 | # the correcr Rails reloader class. 9 | def initialize(rails_version) 10 | @rails_version = rails_version.to_s 11 | end 12 | 13 | # Attach to Rails and perform the reload on each request. 14 | def attach! 15 | reloader_class.to_prepare do 16 | ActiveAdmin.application.unload! 17 | Rails.application.reload_routes! 18 | end 19 | end 20 | 21 | def reloader_class 22 | if @rails_version[0..2] == '3.1' 23 | ActionDispatch::Reloader 24 | else 25 | ActionDispatch::Callbacks 26 | end 27 | end 28 | 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin/mixins/_gradients.css.scss: -------------------------------------------------------------------------------- 1 | $secondary-gradient-start: #efefef; 2 | $secondary-gradient-stop: #dfe1e2; 3 | 4 | @mixin gradient($start, $end){ 5 | background: $start; 6 | background: -webkit-gradient(linear, left top, left bottom, from($start), to($end)); 7 | background: -moz-linear-gradient(-90deg, $start, $end); 8 | // IE 6 & 7 9 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#{$start}, endColorstr=#{$end}); 10 | // IE 8 11 | -ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#{$start}, endColorstr=#{$end}); 12 | } 13 | 14 | @mixin primary-gradient { 15 | @include gradient(lighten($primary-color, 5%), darken($primary-color, 7%)); 16 | border-bottom: 1px solid darken($primary-color, 11%); 17 | } 18 | 19 | @mixin secondary-gradient { 20 | @include gradient($secondary-gradient-start, $secondary-gradient-stop); 21 | } 22 | -------------------------------------------------------------------------------- /spec/unit/components/panel_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin::Views::Panel do 4 | include Arbre::HTML 5 | let(:assigns){ {} } 6 | 7 | let(:helpers) { action_view } 8 | 9 | let(:the_panel) do 10 | panel "My Title" do 11 | span("Hello World") 12 | end 13 | end 14 | 15 | it "should have a title h3" do 16 | the_panel.find_by_tag("h3").first.content.should == "My Title" 17 | end 18 | 19 | it "should have a contents div" do 20 | the_panel.find_by_tag("div").first.class_list.should include("panel_contents") 21 | end 22 | 23 | it "should add children to the contents div" do 24 | the_panel.find_by_tag("span").first.parent.should == the_panel.find_by_tag("div").first 25 | end 26 | 27 | it "should set the icon" do 28 | panel("Title", :icon => :arrow_down).find_by_tag("h3").first.content.should include("span class=\"icon") 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin/mixins/_buttons.css.scss: -------------------------------------------------------------------------------- 1 | @mixin default-button { 2 | @include shadow; 3 | @include gradient(lighten($primary-color, 15%), darken($primary-color, 12%)); 4 | @include rounded(200px); 5 | @include text-shadow(#000); 6 | text-decoration: none; 7 | margin-right: 3px; 8 | font-weight: bold; 9 | font-size: 1.0em; 10 | cursor: pointer; 11 | padding: .6em 1.4em .5em 1.3em; 12 | border: none; 13 | color: #efefef; 14 | &:hover { color: #fff; @include icon-color(#fff); @include shadow(0, 1px, 3px, #888) } 15 | &:active { @include inset-shadow(0, 1px, 2px, #000); } 16 | } 17 | 18 | @mixin light-button { 19 | @include default-button; 20 | @include gradient(#f9f9f9, #dddbdb); 21 | @include text-shadow; 22 | color: #777; 23 | &:hover { color: #444; @include icon-color(#444) } 24 | &:active { @include inset-shadow; } 25 | } 26 | 27 | @mixin dark-button { @include default-button; } 28 | -------------------------------------------------------------------------------- /lib/active_admin/resource_controller/menu.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class ResourceController < ::InheritedResources::Base 3 | module Menu 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | before_filter :set_current_tab 8 | helper_method :current_menu 9 | end 10 | 11 | protected 12 | 13 | def current_menu 14 | active_admin_config.namespace.menu 15 | end 16 | 17 | # Set's @current_tab to be name of the tab to mark as current 18 | # Get's called through a before filter 19 | def set_current_tab 20 | @current_tab = if active_admin_config.belongs_to? && parent? 21 | active_admin_config.belongs_to_config.target.menu_item_name 22 | else 23 | [active_admin_config.parent_menu_item_name, active_admin_config.menu_item_name].compact.join("/") 24 | end 25 | end 26 | 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/active_admin/view_helpers/renderer_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module ViewHelpers 3 | module RendererHelper 4 | 5 | # Adds the ability to render ActiveAdmin::Renderers using the 6 | # standard render method. 7 | # 8 | # Example: 9 | # 10 | # render MyRendererClass, "Arg1", "Arg2" 11 | # 12 | # which is the same as doing 13 | # 14 | # MyRendererClass.new(self).to_html("Arg1", "Arg2") 15 | def render(*args) 16 | if args[0].is_a?(Class) && args[0].ancestors.include?(ActiveAdmin::Renderer) 17 | renderer = args.shift 18 | renderer.new(self).to_html(*args) 19 | elsif args[0].is_a?(Class) && args[0].ancestors.include?(Arbre::HTML::Tag) 20 | tag_class = args.shift 21 | insert_tag tag_class, *args 22 | else 23 | super 24 | end 25 | end 26 | 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/active_admin/menu.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class Menu 3 | 4 | def initialize 5 | @items = [] 6 | yield(self) if block_given? 7 | end 8 | 9 | def add(*args, &block) 10 | @items << MenuItem.new(*args, &block) 11 | end 12 | 13 | def [](name) 14 | items.find{ |i| i.name == name } 15 | end 16 | 17 | def items 18 | @items.sort 19 | end 20 | 21 | def find_by_url(url) 22 | recursive_find_by_url(items, url) 23 | end 24 | 25 | private 26 | 27 | def recursive_find_by_url(collection, url) 28 | found = nil 29 | collection.each do |item| 30 | if item.url == url 31 | found = item 32 | break 33 | else 34 | found = recursive_find_by_url(item.children, url) 35 | break if found 36 | end 37 | end 38 | found 39 | end 40 | 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/active_admin/resource/belongs_to.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class Resource 3 | class BelongsTo 4 | 5 | class TargetNotFound < StandardError; end 6 | 7 | # The resource which initiated this relationship 8 | attr_reader :owner 9 | 10 | def initialize(owner_resource, target_name, options = {}) 11 | @owner, @target_name = owner_resource, target_name 12 | @options = options 13 | end 14 | 15 | # Returns the target resource class or raises an exception if it doesn't exist 16 | def target 17 | namespace.resources[@target_name.to_s.camelize] or 18 | raise TargetNotFound, "Could not find registered resource #{@target_name} in #{namespace.name} with #{namespace.resources.keys.inspect}" 19 | end 20 | 21 | def namespace 22 | @owner.namespace 23 | end 24 | 25 | def optional? 26 | @options[:optional] 27 | end 28 | 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/active_admin/resource/scopes.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class Resource 3 | module Scopes 4 | 5 | # Return an array of scopes for this resource 6 | def scopes 7 | @scopes ||= [] 8 | end 9 | 10 | # Returns a scope for this object by its identifier 11 | def get_scope_by_id(id) 12 | id = id.to_s 13 | scopes.find{|s| s.id == id } 14 | end 15 | 16 | def default_scope 17 | @default_scope 18 | end 19 | 20 | # Create a new scope object for this resource. 21 | # If you want to internationalize the scope name, you can add 22 | # to your i18n files a key like "active_admin.scopes.scope_method". 23 | def scope(*args, &block) 24 | options = args.extract_options! 25 | self.scopes << ActiveAdmin::Scope.new(*args, &block) 26 | if options[:default] 27 | @default_scope = scopes.last 28 | end 29 | end 30 | 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/active_admin/resource_controller/form.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class ResourceController < ::InheritedResources::Base 3 | module Form 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | helper_method :form_config 8 | end 9 | 10 | module ClassMethods 11 | 12 | def form_config=(config) 13 | @form_config = config 14 | end 15 | 16 | def form_config 17 | @form_config ||= default_form_config 18 | end 19 | 20 | def reset_form_config! 21 | @form_config = nil 22 | end 23 | 24 | def default_form_config 25 | config = {} 26 | config[:block] = lambda do |f| 27 | f.inputs 28 | f.buttons 29 | end 30 | config 31 | end 32 | end 33 | 34 | protected 35 | 36 | def form_config 37 | @form_config ||= self.class.form_config 38 | end 39 | 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/support/templates/cucumber.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('config/environments/test', Rails.root) 2 | 3 | # rails/railties/lib/rails/test_help.rb aborts if the environment is not 'test'. (Rails 3.0.0.beta3) 4 | # We can't run Cucumber/RSpec/Test_Unit tests in different environments then. 5 | # 6 | # For now, I patch StringInquirer so that Rails.env.test? returns true when Rails.env is 'test' or 'cucumber' 7 | # 8 | # https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4458-rails-should-allow-test-to-run-in-cucumber-environment 9 | module ActiveSupport 10 | class StringInquirer < String 11 | def method_missing(method_name, *arguments) 12 | if method_name.to_s[-1,1] == "?" 13 | test_string = method_name.to_s[0..-2] 14 | if test_string == 'test' 15 | self == 'test' or self == 'cucumber' 16 | else 17 | self == test_string 18 | end 19 | else 20 | super 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/unit/components/blank_slate_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin::Views::BlankSlate do 4 | include Arbre::HTML 5 | 6 | let(:assigns){ {} } 7 | 8 | let(:helpers) { action_view } 9 | 10 | describe "#blank_slate" do 11 | subject { blank_slate("Posts", "/posts/new") } 12 | 13 | its(:tag_name) { should eql 'div' } 14 | its(:class_list) { should include('blank_slate_container') } 15 | 16 | describe "content" do 17 | subject { blank_slate("Posts", "/posts/new").content } 18 | 19 | context "when url passed in" do 20 | it { should include 'There are no Posts yet. Create one' } 21 | end 22 | 23 | context "when no url passed in" do 24 | subject { blank_slate("Posts").content } 25 | 26 | it { should include 'There are no Posts yet.' } 27 | end 28 | end 29 | 30 | end 31 | end -------------------------------------------------------------------------------- /lib/active_admin/action_items.rb: -------------------------------------------------------------------------------- 1 | require 'active_admin/helpers/optional_display' 2 | 3 | module ActiveAdmin 4 | module ActionItems 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | self.class_inheritable_accessor :action_items 9 | self.action_items = [] 10 | end 11 | 12 | module ClassMethods 13 | def action_item(options = {}, &block) 14 | self.action_items << ActiveAdmin::ActionItems::ActionItem.new(options, &block) 15 | end 16 | 17 | def clear_action_items! 18 | self.action_items = [] 19 | end 20 | 21 | def action_items_for(action) 22 | action_items.select{|item| item.display_on?(action) } 23 | end 24 | end 25 | 26 | class ActionItem 27 | include ActiveAdmin::OptionalDisplay 28 | 29 | attr_accessor :block 30 | 31 | def initialize(options = {}, &block) 32 | @options, @block = options, block 33 | normalize_display_options! 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/active_admin/resource_controller/sidebars.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class ResourceController < ::InheritedResources::Base 3 | 4 | module Sidebars 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | self.class_inheritable_accessor :sidebar_sections 9 | self.sidebar_sections = [] 10 | end 11 | 12 | module ClassMethods 13 | def sidebar(name, options = {}, &block) 14 | self.sidebar_sections << ActiveAdmin::Sidebar::Section.new(name, options, &block) 15 | end 16 | 17 | def clear_sidebar_sections! 18 | self.sidebar_sections = [] 19 | end 20 | 21 | def sidebar_sections_for(action) 22 | sidebar_sections.select{|section| section.display_on?(action) } 23 | end 24 | end 25 | 26 | protected 27 | 28 | def skip_sidebar! 29 | @skip_sidebar = true 30 | end 31 | 32 | def skip_sidebar? 33 | @skip_sidebar == true 34 | end 35 | end 36 | 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /script/use_rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # Switches the development environment to use the given 4 | # version of rails. Caches the Gemfile.locks so that 5 | # switching it very fast. 6 | 7 | def cmd(command) 8 | puts command 9 | system command 10 | end 11 | 12 | version = ARGV[0] 13 | 14 | unless version 15 | puts "usage: ./script/#{__FILE__} VERSION" 16 | exit(1) 17 | end 18 | 19 | def file_or_symlink?(path) 20 | File.exist?(path) || File.symlink?(path) 21 | end 22 | 23 | gem_lock_dir = ".gemfile-locks" 24 | gem_lock_file = "#{gem_lock_dir}/Gemfile-#{version}.lock" 25 | 26 | # Ensure our lock dir is created 27 | cmd "mkdir #{gem_lock_dir}" unless File.exists?(gem_lock_dir) 28 | 29 | unless File.exists?(gem_lock_file) 30 | cmd "rm Gemfile.lock" if file_or_symlink?("Gemfile.lock") 31 | cmd "export RAILS=#{version} && bundle install" 32 | cmd "mv Gemfile.lock #{gem_lock_file}" 33 | end 34 | 35 | cmd("rm Gemfile.lock") if file_or_symlink?("Gemfile.lock") 36 | cmd("ln -s #{gem_lock_file} Gemfile.lock") 37 | -------------------------------------------------------------------------------- /app/views/active_admin/devise/shared/_links.erb: -------------------------------------------------------------------------------- 1 | <%- if controller_name != 'sessions' %> 2 | <% scope = Devise::Mapping.find_scope!(resource_name) %> 3 | <%= link_to "Sign in", send(:"new_#{scope}_session_path") }")br /> 4 | <% end -%> 5 | 6 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 7 | <%= link_to "Sign up", new_registration_path(resource_name) %>
8 | <% end -%> 9 | 10 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' %> 11 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
12 | <% end -%> 13 | 14 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 15 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
16 | <% end -%> 17 | 18 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 19 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
20 | <% end -%> 21 | -------------------------------------------------------------------------------- /spec/unit/components/sidebar_section_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin::Views::SidebarSection do 4 | include Arbre::HTML 5 | let(:assigns){ {} } 6 | 7 | let(:section) do 8 | ActiveAdmin::Sidebar::Section.new(:help) do 9 | span "Help Me" 10 | end 11 | end 12 | 13 | let(:html) do 14 | sidebar_section(section) 15 | end 16 | 17 | it "should have a title h3" do 18 | html.find_by_tag("h3").first.content.should == "Help" 19 | end 20 | 21 | it "should have the class of 'sidebar_section'" do 22 | html.class_list.should include("sidebar_section") 23 | end 24 | 25 | it "should have an id based on the title" do 26 | html.id.should == "help_sidebar_section" 27 | end 28 | 29 | it "should have a contents div" do 30 | html.find_by_tag("div").first.class_list.should include("panel_contents") 31 | end 32 | 33 | it "should add children to the contents div" do 34 | html.find_by_tag("span").first.parent.should == html.find_by_tag("div").first 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin/_comments.css.scss: -------------------------------------------------------------------------------- 1 | // -------------------------------------- Admin Notes 2 | .comments { 3 | 4 | .active_admin_comment { 5 | clear: both; 6 | margin-top: 10px; 7 | margin-bottom: 40px; 8 | max-width: 700px; 9 | 10 | .active_admin_comment_meta { 11 | width: 130px; 12 | float: left; 13 | font-size: 0.9em; 14 | color: lighten($primary-color, 10%); 15 | .active_admin_comment_author { 16 | font-size: 1.2em; 17 | font-weight: bold; 18 | margin: 0; 19 | color: $primary-color; 20 | } 21 | } 22 | .active_admin_comment_body { 23 | margin-left: 150px; 24 | } 25 | } 26 | form.active_admin_comment { 27 | margin: 0; 28 | padding: 0; 29 | margin-left: 150px; 30 | 31 | fieldset.inputs { 32 | margin: 0; 33 | padding: 0; 34 | background: none; 35 | @include no-shadow; 36 | } 37 | li { padding: 0; } 38 | fieldset.buttons { padding: 0; margin-top: 5px;} 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/active_admin/view_helpers/breadcrumb_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module ViewHelpers 3 | module BreadcrumbHelper 4 | 5 | # Returns an array of links to use in a breadcrumb 6 | def breadcrumb_links(path = nil) 7 | path ||= request.fullpath 8 | parts = path.gsub(/^\//, '').split('/') 9 | parts.pop unless %w{ create update }.include?(params[:action]) 10 | crumbs = [] 11 | parts.each_with_index do |part, index| 12 | name = "" 13 | if part =~ /^\d/ && parent = parts[index - 1] 14 | begin 15 | parent_class = parent.singularize.camelcase.constantize 16 | obj = parent_class.find(part.to_i) 17 | name = obj.display_name if obj.respond_to?(:display_name) 18 | rescue 19 | end 20 | end 21 | name = part.titlecase if name == "" 22 | crumbs << link_to(name, "/" + parts[0..index].join('/')) 23 | end 24 | crumbs 25 | end 26 | 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /features/show/page_title.feature: -------------------------------------------------------------------------------- 1 | Feature: Show - Page Title 2 | 3 | Modifying the page title on the show screen 4 | 5 | Background: 6 | Given a post with the title "Hello World" written by "Jane Doe" exists 7 | 8 | Scenario: Set a method to be called on the resource as the title 9 | Given a show configuration of: 10 | """ 11 | ActiveAdmin.register Post do 12 | show :title => :title 13 | end 14 | """ 15 | Then I should see the page title "Hello World" 16 | 17 | Scenario: Set a string as the title 18 | Given a show configuration of: 19 | """ 20 | ActiveAdmin.register Post do 21 | show :title => "Title From String" 22 | end 23 | """ 24 | Then I should see the page title "Title From String" 25 | 26 | Scenario: Set a proc as the title 27 | Given a show configuration of: 28 | """ 29 | ActiveAdmin.register Post do 30 | show :title => proc{|post| "Title: " + post.title } 31 | end 32 | """ 33 | Then I should see the page title "Title: Hello World" 34 | -------------------------------------------------------------------------------- /spec/unit/controller_filters_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin, "filters" do 4 | let(:application){ ActiveAdmin::Application.new } 5 | 6 | describe "before filters" do 7 | it "should add a new before filter to ActiveAdmin::ResourceController" do 8 | ActiveAdmin::ResourceController.should_receive(:before_filter).and_return(true) 9 | application.before_filter :my_filter, :only => :show 10 | end 11 | end 12 | 13 | describe "after filters" do 14 | it "should add a new after filter to ActiveAdmin::ResourceController" do 15 | ActiveAdmin::ResourceController.should_receive(:after_filter).and_return(true) 16 | application.after_filter :my_filter, :only => :show 17 | end 18 | end 19 | 20 | describe "around filters" do 21 | it "should add a new around filter to ActiveAdmin::ResourceController" do 22 | ActiveAdmin::ResourceController.should_receive(:around_filter).and_return(true) 23 | application.around_filter :my_filter, :only => :show 24 | end 25 | end 26 | 27 | end 28 | -------------------------------------------------------------------------------- /spec/unit/resource_controller/collection_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin::ResourceController::Collection do 4 | let(:params) do 5 | {} 6 | end 7 | 8 | let(:controller) do 9 | rc = Admin::PostsController.new 10 | rc.stub!(:params) do 11 | params 12 | end 13 | rc 14 | end 15 | 16 | describe ActiveAdmin::ResourceController::Collection::Search do 17 | let(:params){ {:q => {} }} 18 | it "should call the metasearch method" do 19 | chain = mock("ChainObj") 20 | chain.should_receive(:metasearch).with(params[:q]).once.and_return(Post.search) 21 | controller.send :search, chain 22 | end 23 | end 24 | 25 | describe ActiveAdmin::ResourceController::Collection::Sorting do 26 | let(:params){ {:order => "id_asc" }} 27 | it "should prepend the table name" do 28 | chain = mock("ChainObj") 29 | chain.should_receive(:order).with("\"posts\".id asc").once.and_return(Post.search) 30 | controller.send :sort_order, chain 31 | end 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /app/views/layouts/active_admin_logged_out.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= [@page_title, active_admin_application.site_title].join(" | ") %> 7 | 8 | <% ActiveAdmin.application.stylesheets.each do |path| %> 9 | <%= stylesheet_link_tag path %> 10 | <% end %> 11 | <% ActiveAdmin.application.javascripts.each do |path| %> 12 | <%= javascript_include_tag path %> 13 | <% end %> 14 | 15 | <%= csrf_meta_tag %> 16 | 17 | 18 |
19 | 20 |
21 | <% if flash.keys.any? %> 22 | <% flash.each do |type, message| %> 23 | <%= content_tag :div, message, :class => "flash flash_#{type}" %> 24 | <% end %> 25 | <% end %> 26 |
27 | <%= yield %> 28 |
29 |
30 | 33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gemspec 4 | 5 | require File.expand_path('../spec/support/detect_rails_version', __FILE__) 6 | 7 | case detect_rails_version 8 | when /3.0.(\d)*/ 9 | gem 'rails', "= 3.0.#{$1}" 10 | gem "meta_search", '~> 1.0.0' 11 | when /3.1.(.*)/ 12 | gem 'rails', "= 3.1.#{$1}" 13 | gem "meta_search", '>= 1.1.0.pre' 14 | gem "uglifier" 15 | gem 'sass-rails', "~> 3.1.0.rc" 16 | gem 'coffee-script' 17 | gem 'execjs' 18 | gem 'therubyracer' 19 | end 20 | 21 | group :development, :test do 22 | gem 'sqlite3-ruby', :require => 'sqlite3' 23 | gem 'rake', '0.8.7', :require => false 24 | gem 'haml', '~> 3.1.1', :require => false 25 | end 26 | 27 | group :test do 28 | gem 'rspec', '~> 2.6.0' 29 | gem 'rspec-rails', '~> 2.6.0' 30 | gem 'capybara', '1.0.0' 31 | gem 'cucumber', '0.10.6' 32 | gem 'cucumber-rails', '0.5.2' 33 | gem 'database_cleaner' 34 | gem 'shoulda', '2.11.2', :require => nil 35 | gem 'launchy' 36 | end 37 | -------------------------------------------------------------------------------- /lib/active_admin/view_factory.rb: -------------------------------------------------------------------------------- 1 | require 'active_admin/abstract_view_factory' 2 | 3 | module ActiveAdmin 4 | class ViewFactory < AbstractViewFactory 5 | 6 | # Register Helper Renderers 7 | register :global_navigation => ActiveAdmin::Views::TabsRenderer, 8 | :action_items => ActiveAdmin::Views::ActionItems, 9 | :header => ActiveAdmin::Views::HeaderRenderer, 10 | :dashboard_section => ActiveAdmin::Views::DashboardSection, 11 | :index_scopes => ActiveAdmin::Views::Scopes, 12 | :blank_slate => ActiveAdmin::Views::BlankSlate 13 | 14 | # Register All The Pages 15 | register :dashboard_page => ActiveAdmin::Views::Pages::Dashboard, 16 | :index_page => ActiveAdmin::Views::Pages::Index, 17 | :show_page => ActiveAdmin::Views::Pages::Show, 18 | :new_page => ActiveAdmin::Views::Pages::New, 19 | :edit_page => ActiveAdmin::Views::Pages::Edit 20 | 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /features/step_definitions/factory_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^a post with the title "([^"]*)" exists$/ do |title| 2 | Post.create! :title => title 3 | end 4 | 5 | Given /^a post with the title "([^"]*)" and body "([^"]*)" exists$/ do |title, body| 6 | Post.create! :title => title, :body => body 7 | end 8 | 9 | Given /^a post with the title "([^"]*)" written by "([^"]*)" exists$/ do |title, author_name| 10 | first, last = author_name.split(' ') 11 | author = User.find_or_create_by_first_name_and_last_name(first, last, :username => author_name.gsub(' ', '').underscore) 12 | Post.create! :title => title, :author => author 13 | end 14 | 15 | Given /^(\d+) posts? exists?/ do |count| 16 | (0...count.to_i).each do |i| 17 | Post.create! :title => "Hello World #{i}" 18 | end 19 | end 20 | 21 | Given /^a category named "([^"]*)" exists$/ do |name| 22 | Category.create! :name => name 23 | end 24 | 25 | Given /^a user named "([^"]*)" exists$/ do |name| 26 | first, last = name.split(" ") 27 | User.create! :first_name => first, :last_name => last, :username => name 28 | end 29 | -------------------------------------------------------------------------------- /lib/active_admin/locales/da.yml: -------------------------------------------------------------------------------- 1 | da: 2 | active_admin: 3 | dashboard_welcome: 4 | welcome: "Velkommen til Active Admin. Dette er standardoversigtssiden." 5 | call_to_action: "Rediger 'app/admin/dashboards.rb' for at tilføje nye elementer til oversigtssiden." 6 | view: "Vis" 7 | edit: "Rediger" 8 | delete: "Slet" 9 | delete_confirmation: "Er du sikker på at du ønsker at slette?" 10 | new_model: "Ny(t) %{model}" 11 | edit_model: "Rediger %{model}" 12 | delete_model: "Slet %{model}" 13 | details: "%{model} detaljer" 14 | cancel: "Fortryd" 15 | empty: "Tom" 16 | previous: "Forrige" 17 | next: "Næste" 18 | download: "Download:" 19 | has_many_new: "Tilføj ny(t) %{model}" 20 | has_many_delete: "Slet" 21 | filter: "Filtrer" 22 | clear_filters: "Ryd filtre" 23 | search_field: "Søg %{field}" 24 | equal_to: "lig" 25 | greater_than: "større end" 26 | less_than: "mindre end" 27 | main_content: "Implementer venligst %{model}#main_content for at vise noget indhold." 28 | logout: "Log ud" 29 | -------------------------------------------------------------------------------- /features/global_navigation.feature: -------------------------------------------------------------------------------- 1 | Feature: Global Navigation 2 | 3 | 4 | Background: 5 | Given a configuration of: 6 | """ 7 | ActiveAdmin.register Post 8 | """ 9 | Given I am logged in 10 | And 10 posts exist 11 | 12 | Scenario: Viewing the current section in the global navigation 13 | Given I am on the index page for posts 14 | Then the "Posts" tab should be selected 15 | 16 | Scenario: Viewing the current section in the global navigation when on new page 17 | Given I am on the index page for posts 18 | And I follow "New Post" 19 | Then the "Posts" tab should be selected 20 | 21 | Scenario: Viewing the current section in the global navigation when on show page 22 | Given I am on the index page for posts 23 | And I follow "View" 24 | Then the "Posts" tab should be selected 25 | 26 | Scenario: Viewing the current section in the global navigation when on edit page 27 | Given I am on the index page for posts 28 | And I follow "View" 29 | And I follow "Edit Post" 30 | Then the "Posts" tab should be selected 31 | -------------------------------------------------------------------------------- /features/registering_resources.feature: -------------------------------------------------------------------------------- 1 | Feature: Registering Resources 2 | 3 | Registering resources within Active Admin 4 | 5 | Background: 6 | Given I am logged in 7 | And a post with the title "Hello World" exists 8 | 9 | Scenario: Registering a resource with the defaults 10 | Given a configuration of: 11 | """ 12 | ActiveAdmin.register Post 13 | """ 14 | When I go to the dashboard 15 | Then I should see "Posts" 16 | When I follow "Posts" 17 | Then I should see "Hello World" 18 | When I follow "View" 19 | Then I should see "Hello World" 20 | And I should be in the resource section for Post 21 | 22 | Scenario: Registering a resource with another name 23 | Given a configuration of: 24 | """ 25 | ActiveAdmin.register Post, :as => "My Post" 26 | """ 27 | When I go to the dashboard 28 | Then I should see "My Posts" 29 | When I follow "My Posts" 30 | Then I should see "Hello World" 31 | When I follow "View" 32 | Then I should see "Hello World" 33 | And I should be in the resource section for My Post 34 | -------------------------------------------------------------------------------- /lib/active_admin/views/components/blank_slate.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | # Build a Blank Slate 4 | class BlankSlate < ActiveAdmin::Component 5 | builder_method :blank_slate 6 | 7 | def default_class_name 8 | 'blank_slate_container' 9 | end 10 | 11 | def build(name, url = nil) 12 | if url 13 | super(span(blank_slate_content_with_link(name, url), :class => "blank_slate")) 14 | else 15 | super(span(blank_slate_content_without_link(name), :class => "blank_slate")) 16 | end 17 | end 18 | 19 | private 20 | 21 | def blank_slate_content_with_link(name, url) 22 | I18n.t('active_admin.blank_slate.content', :resource_name => name).html_safe + 23 | " " + 24 | link_to(I18n.t('active_admin.blank_slate.link').html_safe, url) 25 | end 26 | 27 | def blank_slate_content_without_link(name) 28 | I18n.t('active_admin.blank_slate.content', :resource_name => name).html_safe 29 | end 30 | 31 | end 32 | end 33 | end -------------------------------------------------------------------------------- /lib/active_admin/dashboards/dashboard_controller.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Dashboards 3 | class DashboardController < ResourceController 4 | 5 | before_filter :skip_sidebar! 6 | 7 | actions :index 8 | 9 | clear_action_items! 10 | 11 | def index 12 | @dashboard_sections = find_sections 13 | render 'active_admin/dashboard/index.html.arb' 14 | end 15 | 16 | protected 17 | 18 | def set_current_tab 19 | @current_tab = "Dashboard" 20 | end 21 | 22 | def find_sections 23 | ActiveAdmin::Dashboards.sections_for_namespace(namespace) 24 | end 25 | 26 | def namespace 27 | class_name = self.class.name 28 | if class_name.include?('::') 29 | self.class.name.split('::').first.underscore.to_sym 30 | else 31 | :root 32 | end 33 | end 34 | 35 | # Return the current menu for the view. This is a helper method 36 | def current_menu 37 | ActiveAdmin.application.namespaces[namespace].menu 38 | end 39 | 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /features/step_definitions/configuration_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^a configuration of:$/ do |configuration_content| 2 | eval configuration_content 3 | Rails.application.reload_routes! 4 | ActiveAdmin.application.namespaces.values.each{|n| n.load_menu! } 5 | end 6 | 7 | Given /^an index configuration of:$/ do |configuration_content| 8 | eval configuration_content 9 | Rails.application.reload_routes! 10 | ActiveAdmin.application.namespaces.values.each{|n| n.load_menu! } 11 | 12 | And 'I am logged in' 13 | When "I am on the index page for posts" 14 | end 15 | 16 | Given /^a show configuration of:$/ do |configuration_content| 17 | eval configuration_content 18 | Rails.application.reload_routes! 19 | ActiveAdmin.application.namespaces.values.each{|n| n.load_menu! } 20 | 21 | And 'I am logged in' 22 | When "I am on the index page for posts" 23 | And 'I follow "View"' 24 | end 25 | 26 | Given /^"([^"]*)" contains:$/ do |filename, contents| 27 | require 'fileutils' 28 | filepath = Rails.root + filename 29 | FileUtils.mkdir_p File.dirname(filepath) 30 | File.open(filepath, 'w+'){|f| f << contents } 31 | end 32 | -------------------------------------------------------------------------------- /spec/unit/scope_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin::Scope do 4 | 5 | describe "creating a scope" do 6 | subject{ scope } 7 | 8 | context "when just a scope method" do 9 | let(:scope) { ActiveAdmin::Scope.new :published } 10 | its(:name) { should == "Published"} 11 | its(:id) { should == "published"} 12 | its(:scope_method) { should == :published } 13 | end 14 | 15 | context "when a name and scope method" do 16 | let(:scope) { ActiveAdmin::Scope.new "My Scope", :scope_method } 17 | its(:name) { should == "My Scope"} 18 | its(:id) { should == "my_scope"} 19 | its(:scope_method) { should == :scope_method } 20 | end 21 | 22 | context "when a name and scope block" do 23 | let(:scope) { ActiveAdmin::Scope.new("My Scope"){|s| s } } 24 | its(:name) { should == "My Scope"} 25 | its(:id) { should == "my_scope"} 26 | its(:scope_method) { should == nil } 27 | its(:scope_block) { should be_a(Proc)} 28 | end 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /lib/active_admin/helpers/optional_display.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | 3 | # Shareable module to give a #display_on?(action) method 4 | # which returns true or false depending on an options hash. 5 | # 6 | # The options hash accepts: 7 | # 8 | # :only => :index 9 | # :only => [:index, :show] 10 | # :except => :index 11 | # :except => [:index, :show] 12 | # 13 | # call #normalize_display_options! after @options has been set 14 | # to ensure that the display options are setup correctly 15 | 16 | module OptionalDisplay 17 | def display_on?(action) 18 | return @options[:only].include?(action.to_sym) if @options[:only] 19 | return !@options[:except].include?(action.to_sym) if @options[:except] 20 | true 21 | end 22 | 23 | private 24 | 25 | def normalize_display_options! 26 | if @options[:only] 27 | @options[:only] = @options[:only].is_a?(Array) ? @options[:only] : [@options[:only]] 28 | end 29 | if @options[:except] 30 | @options[:except] = @options[:except].is_a?(Array) ? @options[:except] : [@options[:except]] 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /features/registering_assets.feature: -------------------------------------------------------------------------------- 1 | Feature: Registering Assets 2 | 3 | Registering CSS and JS files 4 | 5 | Background: 6 | Given a configuration of: 7 | """ 8 | ActiveAdmin.register Post 9 | """ 10 | And I am logged in 11 | 12 | 13 | Scenario: Viewing default asset files 14 | When I am on the index page for posts 15 | Then I should see the css file "admin/active_admin.css" 16 | Then I should see the js file "active_admin_vendor.js" 17 | Then I should see the js file "active_admin.js" 18 | 19 | Scenario: Registering a CSS file 20 | Given a configuration of: 21 | """ 22 | ActiveAdmin.application.register_stylesheet "some-random-css.css" 23 | ActiveAdmin.register Post 24 | """ 25 | When I am on the index page for posts 26 | Then I should see the css file "some-random-css.css" 27 | 28 | Scenario: Registering a JS file 29 | Given a configuration of: 30 | """ 31 | ActiveAdmin.application.register_javascript "some-random-js.js" 32 | ActiveAdmin.register Post 33 | """ 34 | When I am on the index page for posts 35 | Then I should see the js file "some-random-js.js" 36 | -------------------------------------------------------------------------------- /spec/support/rails_template_with_data.rb: -------------------------------------------------------------------------------- 1 | # Use the default 2 | apply File.expand_path("../rails_template.rb", __FILE__) 3 | 4 | # Register Active Admin controllers 5 | %w{ Post User Category }.each do |type| 6 | generate :'active_admin:resource', type 7 | end 8 | 9 | # Setup some default data 10 | append_file "db/seeds.rb", <<-EOF 11 | users = ["Jimi Hendrix", "Jimmy Page", "Yngwie Malmsteen", "Eric Clapton", "Kirk Hammett"].collect do |name| 12 | first, last = name.split(" ") 13 | User.create! :first_name => first, 14 | :last_name => last, 15 | :username => [first,last].join('-').downcase 16 | end 17 | 18 | categories = ["Rock", "Pop Rock", "Alt-Country", "Blues", "Dub-Step"].collect do |name| 19 | Category.create! :name => name 20 | end 21 | 22 | 1_000.times do |i| 23 | user = users[i % users.size] 24 | cat = categories[i % categories.size] 25 | Post.create :title => "Blog Post \#{i}", 26 | :body => "Blog post \#{i} is written by \#{user.username} about \#{cat.name}", 27 | :category => cat, 28 | :author => user 29 | end 30 | EOF 31 | 32 | rake 'db:seed' 33 | -------------------------------------------------------------------------------- /lib/active_admin/helpers/settings.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/concern' 2 | 3 | module ActiveAdmin 4 | 5 | # Adds a class method to a class to create settings with default values. 6 | # 7 | # Example: 8 | # 9 | # class Configuration 10 | # include ActiveAdmin::Settings 11 | # 12 | # setting :site_title, "Default Site Title" 13 | # 14 | # def initialize 15 | # # You must call this method to initialize the defaults 16 | # initialize_defaults! 17 | # end 18 | # 19 | module Settings 20 | extend ActiveSupport::Concern 21 | 22 | module InstanceMethods 23 | 24 | def default_settings 25 | self.class.default_settings 26 | end 27 | 28 | def initialize_defaults! 29 | default_settings.each do |key, value| 30 | send("#{key}=".to_sym, value) 31 | end 32 | end 33 | 34 | end 35 | 36 | module ClassMethods 37 | 38 | def setting(name, default) 39 | default_settings[name] = default 40 | attr_accessor(name) 41 | end 42 | 43 | def default_settings 44 | @default_settings ||= {} 45 | end 46 | 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/active_admin/sidebar.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Sidebar 3 | 4 | class Section 5 | include ActiveAdmin::OptionalDisplay 6 | 7 | attr_accessor :name, :options, :block 8 | 9 | def initialize(name, options = {}, &block) 10 | @name, @options, @block = name, options, block 11 | normalize_display_options! 12 | end 13 | 14 | # The id gets used for the div in the view 15 | def id 16 | name.to_s.downcase.underscore + '_sidebar_section' 17 | end 18 | 19 | def icon? 20 | options[:icon] 21 | end 22 | 23 | def icon 24 | options[:icon] if icon? 25 | end 26 | 27 | # The title gets displayed within the section in the view 28 | def title 29 | begin 30 | I18n.t!("active_admin.sidebars.#{name.to_s}") 31 | rescue I18n::MissingTranslationData 32 | name.to_s.titlecase 33 | end 34 | end 35 | 36 | # If a block is not passed in, the name of the partial to render 37 | def partial_name 38 | options[:partial] || "#{name.to_s.downcase.gsub(' ', '_')}_sidebar" 39 | end 40 | end 41 | 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/active_admin/resource/naming.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class Resource 3 | module Naming 4 | 5 | # An underscored safe representation internally for this resource 6 | def underscored_resource_name 7 | @underscored_resource_name ||= if @options[:as] 8 | @options[:as].gsub(' ', '').underscore.singularize 9 | else 10 | resource.name.gsub('::','').underscore 11 | end 12 | end 13 | 14 | # A camelized safe representation for this resource 15 | def camelized_resource_name 16 | underscored_resource_name.camelize 17 | end 18 | 19 | # Returns the name to call this resource. 20 | # By default will use resource.model_name.human 21 | def resource_name 22 | @resource_name ||= if @options[:as] || !resource.respond_to?(:model_name) 23 | underscored_resource_name.titleize 24 | else 25 | resource.model_name.human.titleize 26 | end 27 | end 28 | 29 | # Returns the plural version of this resource 30 | def plural_resource_name 31 | @plural_resource_name ||= resource_name.pluralize 32 | end 33 | 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/active_admin/devise.rb: -------------------------------------------------------------------------------- 1 | require 'devise' 2 | 3 | module ActiveAdmin 4 | module Devise 5 | 6 | def self.config 7 | { 8 | :path => ActiveAdmin.application.default_namespace, 9 | :controllers => ActiveAdmin::Devise.controllers, 10 | :path_names => { :sign_in => 'login', :sign_out => "logout" } 11 | } 12 | end 13 | 14 | def self.controllers 15 | { 16 | :sessions => "active_admin/devise/sessions", 17 | :passwords => "active_admin/devise/passwords" 18 | } 19 | end 20 | 21 | module Controller 22 | extend ::ActiveSupport::Concern 23 | included do 24 | layout 'active_admin_logged_out' 25 | helper ::ActiveAdmin::ViewHelpers 26 | end 27 | 28 | # Redirect to the default namespace on logout 29 | def root_path 30 | "/#{ActiveAdmin.application.default_namespace}" 31 | end 32 | end 33 | 34 | class SessionsController < ::Devise::SessionsController 35 | include ::ActiveAdmin::Devise::Controller 36 | end 37 | 38 | class PasswordsController < ::Devise::PasswordsController 39 | include ::ActiveAdmin::Devise::Controller 40 | end 41 | 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/active_admin/resource_controller/callbacks.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class ResourceController < ::InheritedResources::Base 3 | 4 | module Callbacks 5 | extend ActiveSupport::Concern 6 | include ::ActiveAdmin::Callbacks 7 | 8 | included do 9 | define_active_admin_callbacks :build, :create, :update, :save, :destroy 10 | end 11 | 12 | protected 13 | 14 | def build_resource 15 | object = super 16 | run_build_callbacks object 17 | object 18 | end 19 | 20 | def create_resource(object) 21 | run_create_callbacks object do 22 | save_resource(object) 23 | end 24 | end 25 | 26 | def save_resource(object) 27 | run_save_callbacks object do 28 | object.save 29 | end 30 | end 31 | 32 | def update_resource(object, attributes) 33 | object.attributes = attributes 34 | run_update_callbacks object do 35 | save_resource(object) 36 | end 37 | end 38 | 39 | def destroy_resource(object) 40 | run_destroy_callbacks object do 41 | object.destroy 42 | end 43 | end 44 | end 45 | 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/active_admin/views/index_as_grid.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | class IndexAsGrid < ActiveAdmin::Component 4 | 5 | def build(page_config, collection) 6 | @page_config = page_config 7 | @collection = collection 8 | build_table 9 | end 10 | 11 | def number_of_columns 12 | @page_config[:columns] || default_number_of_columns 13 | end 14 | 15 | protected 16 | 17 | def build_table 18 | table :class => "index_grid" do 19 | collection.in_groups_of(number_of_columns).each do |group| 20 | build_row(group) 21 | end 22 | end 23 | end 24 | 25 | def build_row(group) 26 | tr do 27 | group.each do |item| 28 | item ? build_item(item) : build_empty_cell 29 | end 30 | end 31 | end 32 | 33 | def build_item(item) 34 | td :for => item do 35 | instance_exec(item, &@page_config.block) 36 | end 37 | end 38 | 39 | def build_empty_cell 40 | td ' '.html_safe 41 | end 42 | 43 | def default_number_of_columns 44 | 3 45 | end 46 | 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /activeadmin.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "active_admin/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = %q{activeadmin} 7 | s.version = ActiveAdmin::VERSION 8 | s.platform = Gem::Platform::RUBY 9 | s.homepage = %q{http://activeadmin.info} 10 | s.authors = ["Greg Bell"] 11 | s.email = ["gregdbell@gmail.com"] 12 | s.description = %q{The administration framework for Ruby on Rails.} 13 | s.summary = %q{The administration framework for Ruby on Rails.} 14 | 15 | s.files = `git ls-files`.split("\n").sort 16 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 17 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 18 | s.require_paths = ["lib"] 19 | 20 | s.add_dependency("rails", ">= 3.0.0") 21 | s.add_dependency("meta_search", ">= 0.9.2") 22 | s.add_dependency("devise", ">= 1.1.2") 23 | s.add_dependency("formtastic", ">= 1.1.0") 24 | s.add_dependency("inherited_resources", ">= 0") 25 | s.add_dependency("kaminari", ">= 0.12.4") 26 | s.add_dependency("sass", ">= 3.1.0") 27 | s.add_dependency("fastercsv", ">= 0") 28 | end 29 | -------------------------------------------------------------------------------- /spec/unit/pretty_format_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "#pretty_format" do 4 | include ActiveAdmin::ViewHelpers::DisplayHelper 5 | 6 | context "when a String is passed in" do 7 | it "should return the String passed in" do 8 | pretty_format("hello").should == "hello" 9 | end 10 | end 11 | 12 | context "when a Date or a Time is passed in" do 13 | it "should return a localized Date or Time with long format" do 14 | t = Time.now 15 | self.should_receive(:localize).with(t, {:format => :long}) { "Just Now!" } 16 | pretty_format(t).should == "Just Now!" 17 | end 18 | end 19 | 20 | context "when an ActiveRecord object is passed in" do 21 | it "should delegate to auto_link" do 22 | post = Post.new 23 | self.should_receive(:auto_link).with(post) { "model name" } 24 | pretty_format(post).should == "model name" 25 | end 26 | end 27 | 28 | context "when something else is passed in" do 29 | it "should delegate to display_name" do 30 | something = Class.new.new 31 | self.should_receive(:display_name).with(something) { "I'm not famous" } 32 | pretty_format(something).should == "I'm not famous" 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /features/users/logging_in.feature: -------------------------------------------------------------------------------- 1 | Feature: User Logging In 2 | 3 | Logging in to the system as an admin user 4 | 5 | Background: 6 | Given a configuration of: 7 | """ 8 | ActiveAdmin.register Post 9 | """ 10 | And I am logged out 11 | And an admin user "admin@example.com" exists 12 | When I go to the dashboard 13 | 14 | Scenario: Logging in Successfully 15 | When I fill in "Email" with "admin@example.com" 16 | And I fill in "Password" with "password" 17 | And I press "Login" 18 | Then I should be on the the dashboard 19 | And I should see "Logout" 20 | And I should see "admin@example.com" 21 | 22 | Scenario: Attempting to log in with an incorrent email address 23 | When I fill in "Email" with "not-an-admin@example.com" 24 | And I fill in "Password" with "not-my-password" 25 | And I press "Login" 26 | Then I should see "Login" 27 | And I should see "Invalid email or password." 28 | 29 | Scenario: Attempting to log in with an incorrect password 30 | When I fill in "Email" with "admin@example.com" 31 | And I fill in "Password" with "not-my-password" 32 | And I press "Login" 33 | Then I should see "Login" 34 | And I should see "Invalid email or password." 35 | -------------------------------------------------------------------------------- /lib/active_admin/views/header_renderer.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | 4 | # Renderer for the header of the application. Includes the page 5 | # title, global navigation and utility navigation. 6 | class HeaderRenderer < ::ActiveAdmin::Renderer 7 | 8 | def to_html 9 | title + global_navigation + utility_navigation 10 | end 11 | 12 | protected 13 | 14 | def title 15 | content_tag 'h1', active_admin_application.site_title, :id => 'site_title' 16 | end 17 | 18 | # Renders the global navigation returned by 19 | # ActiveAdmin::ResourceController#current_menu 20 | # 21 | # It uses the ActiveAdmin.tabs_renderer option 22 | def global_navigation 23 | render view_factory.global_navigation, current_menu 24 | end 25 | 26 | def utility_navigation 27 | content_tag 'p', :id => "utility_nav" do 28 | if current_active_admin_user? 29 | content_tag(:span, display_name(current_active_admin_user), :class => "current_user") + 30 | link_to(I18n.t('active_admin.logout'), "/#{active_admin_application.default_namespace}/logout") 31 | end 32 | end 33 | end 34 | end 35 | 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/unit/auto_link_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class AutoLinkMockResource 4 | attr_accessor :namespace 5 | def initialize(namespace) 6 | @namespace = namespace 7 | end 8 | end 9 | 10 | describe "auto linking resources" do 11 | include ActiveAdmin::ViewHelpers::ActiveAdminApplicationHelper 12 | include ActiveAdmin::ViewHelpers::AutoLinkHelper 13 | include ActiveAdmin::ViewHelpers::DisplayHelper 14 | 15 | let(:active_admin_config) { AutoLinkMockResource.new(namespace) } 16 | let(:namespace){ ActiveAdmin::Namespace.new(ActiveAdmin::Application.new, :admin) } 17 | let(:post){ Post.create! :title => "Hello World" } 18 | 19 | def admin_post_path(post) 20 | "/admin/posts/#{post.id}" 21 | end 22 | 23 | context "when the resource is not registered" do 24 | it "should return the display name of the object" do 25 | auto_link(post).should == "Hello World" 26 | end 27 | end 28 | 29 | context "when the resource is registered" do 30 | before do 31 | namespace.register Post 32 | end 33 | it "should return a link with the display name of the object" do 34 | self.should_receive(:link_to).with("Hello World", admin_post_path(post)) 35 | auto_link(post) 36 | end 37 | end 38 | 39 | end 40 | -------------------------------------------------------------------------------- /lib/active_admin/view_helpers/display_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module ViewHelpers 3 | module DisplayHelper 4 | 5 | def display_name_method_for(resource) 6 | @@display_name_methods_cache ||= {} 7 | @@display_name_methods_cache[resource.class] ||= 8 | active_admin_application.display_name_methods.find{|method| resource.respond_to? method } 9 | end 10 | 11 | # Tries to display an object with as friendly of output 12 | # as possible. 13 | def display_name(resource) 14 | resource.send(display_name_method_for(resource)) 15 | end 16 | 17 | # Return a pretty string for any object 18 | # Date Time are formatted via #localize with :format => :long 19 | # ActiveRecord objects are formatted via #auto_link 20 | # We attempt to #display_name of any other objects 21 | def pretty_format(object) 22 | case object 23 | when String 24 | object 25 | when Arbre::HTML::Element 26 | object 27 | when Date, Time 28 | localize(object, :format => :long) 29 | when ActiveRecord::Base 30 | auto_link(object) 31 | else 32 | display_name(object) 33 | end 34 | end 35 | 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /features/support/selectors.rb: -------------------------------------------------------------------------------- 1 | module HtmlSelectorsHelpers 2 | # Maps a name to a selector. Used primarily by the 3 | # 4 | # When /^(.+) within (.+)$/ do |step, scope| 5 | # 6 | # step definitions in web_steps.rb 7 | # 8 | def selector_for(locator) 9 | case locator 10 | 11 | when "the page" 12 | "html > body" 13 | 14 | # Add more mappings here. 15 | # Here is an example that pulls values out of the Regexp: 16 | # 17 | # when /^the (notice|error|info) flash$/ 18 | # ".flash.#{$1}" 19 | 20 | # You can also return an array to use a different selector 21 | # type, like: 22 | # 23 | # when /the header/ 24 | # [:xpath, "//header"] 25 | 26 | when "index grid" 27 | [:css, "table.index_grid"] 28 | 29 | when /^the "([^"]*)" sidebar$/ 30 | [:css, "##{$1.gsub(" ", '').underscore}_sidebar_section"] 31 | 32 | # This allows you to provide a quoted selector as the scope 33 | # for "within" steps as was previously the default for the 34 | # web steps: 35 | when /^"(.+)"$/ 36 | $1 37 | 38 | else 39 | raise "Can't find mapping from \"#{locator}\" to a selector.\n" + 40 | "Now, go and add a mapping in #{__FILE__}" 41 | end 42 | end 43 | end 44 | 45 | World(HtmlSelectorsHelpers) 46 | -------------------------------------------------------------------------------- /lib/active_admin/csv_builder.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | # CSVBuilder stores CSV configuration 3 | # 4 | # Usage example: 5 | # 6 | # csv_builder = CSVBuilder.new 7 | # csv_builder.column :id 8 | # csv_builder.column("Name") { |resource| resource.full_name } 9 | # 10 | class CSVBuilder 11 | 12 | # Return a default CSVBuilder for a resource 13 | # The CSVBuilder's columns would be Id followed by this 14 | # resource's content columns 15 | def self.default_for_resource(resource) 16 | new.tap do |csv_builder| 17 | csv_builder.column(:id) 18 | resource.content_columns.each do |content_column| 19 | csv_builder.column(content_column.name.to_sym) 20 | end 21 | end 22 | end 23 | 24 | attr_reader :columns 25 | 26 | def initialize(&block) 27 | @columns = [] 28 | instance_eval &block if block_given? 29 | end 30 | 31 | # Add a column 32 | def column(name, &block) 33 | @columns << Column.new(name, block) 34 | end 35 | 36 | class Column 37 | attr_reader :name, :data 38 | 39 | def initialize(name, block = nil) 40 | @name = name.is_a?(Symbol) ? name.to_s.titleize : name 41 | @data = block || name.to_sym 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/integration/stylesheets_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Stylesheets" do 4 | if Rails.version[0..2] == '3.1' 5 | require "sprockets" 6 | context "when Rails 3.1.x" do 7 | let(:css) do 8 | assets = Rails.application.assets 9 | assets.find_asset("active_admin.css") 10 | end 11 | it "should successfully render the scss stylesheets using sprockets" do 12 | css.should_not be_nil 13 | end 14 | it "should not have any syntax errors" do 15 | css.to_s.should_not include("Syntax error:") 16 | end 17 | end 18 | end 19 | 20 | if Rails.version[0..2] == '3.0' 21 | context "when Rails 3.0.x" do 22 | let(:stylesheet_path) do 23 | Rails.root + 'public/stylesheets/active_admin.css' 24 | end 25 | 26 | before do 27 | "rm #{stylesheet_path}" if File.exists?(stylesheet_path) 28 | Sass::Plugin.force_update_stylesheets 29 | end 30 | 31 | it "should render the scss stylesheets using SASS" do 32 | File.exists?(stylesheet_path).should be_true 33 | end 34 | 35 | it "should not have any syntax errors" do 36 | css = File.read(stylesheet_path) 37 | css.should_not include("Syntax error:") 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/unit/asset_registration_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module MockRegistration 4 | extend ActiveAdmin::AssetRegistration 5 | end 6 | 7 | describe ActiveAdmin::AssetRegistration do 8 | 9 | before do 10 | MockRegistration.clear_stylesheets! 11 | MockRegistration.clear_javascripts! 12 | end 13 | 14 | it "should register a stylesheet file" do 15 | MockRegistration.register_stylesheet "active_admin.css" 16 | MockRegistration.stylesheets.should == ["active_admin.css"] 17 | end 18 | 19 | it "should clear all existing stylesheets" do 20 | MockRegistration.register_stylesheet "active_admin.css" 21 | MockRegistration.stylesheets.should == ["active_admin.css"] 22 | MockRegistration.clear_stylesheets! 23 | MockRegistration.stylesheets.should == [] 24 | end 25 | 26 | it "should register a javascript file" do 27 | MockRegistration.register_javascript "active_admin.js" 28 | MockRegistration.javascripts.should == ["active_admin.js"] 29 | end 30 | 31 | it "should clear all existing javascripts" do 32 | MockRegistration.register_javascript "active_admin.js" 33 | MockRegistration.javascripts.should == ["active_admin.js"] 34 | MockRegistration.clear_javascripts! 35 | MockRegistration.javascripts.should == [] 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /features/step_definitions/format_steps.rb: -------------------------------------------------------------------------------- 1 | Then "I should see nicely formatted datetimes" do 2 | page.body.should =~ /\w+ \d{1,2}, \d{4} \d{2}:\d{2}/ 3 | end 4 | 5 | Then /^I should see a link to download "([^"]*)"$/ do |format_type| 6 | page.should have_css("#index_footer a", :text => format_type) 7 | end 8 | 9 | # Check first rows of the displayed CSV. 10 | Then /^I should download a CSV file for "([^"]*)" containing:$/ do |resource_name, table| 11 | page.response_headers['Content-Type'].should == 'text/csv; charset=utf-8' 12 | csv_filename = "#{resource_name}-#{Time.now.strftime("%Y-%m-%d")}.csv" 13 | page.response_headers['Content-Disposition'].should == %{attachment; filename="#{csv_filename}"} 14 | body = page.driver.response.body 15 | 16 | begin 17 | csv = CSV.parse(body) 18 | table.raw.each_with_index do |expected_row, row_index| 19 | expected_row.each_with_index do |expected_cell, col_index| 20 | cell = csv.try(:[], row_index).try(:[], col_index) 21 | if expected_cell.blank? 22 | cell.should be_nil 23 | else 24 | (cell || '').should match(/#{expected_cell}/) 25 | end 26 | end 27 | end 28 | rescue 29 | puts "Expecting:" 30 | p table.raw 31 | puts "to match:" 32 | p csv 33 | raise $! 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/active_admin/resource_controller/scoping.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class ResourceController < ::InheritedResources::Base 3 | 4 | # This module deals with scoping entire controllers to a relation 5 | module Scoping 6 | extend ActiveSupport::Concern 7 | 8 | protected 9 | 10 | # Override the default InheritedResource #begin_of_association_chain to allow 11 | # the scope to be defined in the active admin configuration. 12 | # 13 | # If scope_to is a proc, we eval it, otherwise we call the method on the controller. 14 | def begin_of_association_chain 15 | return nil unless active_admin_config.scope_to 16 | case active_admin_config.scope_to 17 | when Proc 18 | instance_eval &active_admin_config.scope_to 19 | when Symbol 20 | send active_admin_config.scope_to 21 | else 22 | raise ArgumentError, "#scope_to accepts a symbol or a block" 23 | end 24 | end 25 | 26 | # Overriding from InheritedResources::BaseHelpers 27 | # 28 | # Returns the method for the association chain when using 29 | # the scope_to option 30 | def method_for_association_chain 31 | active_admin_config.scope_to_association_method || super 32 | end 33 | 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/integration/belongs_to_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe_with_capybara "Belongs To" do 4 | 5 | let(:user){ User.create(:first_name => "John", :last_name => "Doe", :username => "johndoe") } 6 | let(:post){ user.posts.create :title => "Hello World", :body => "woot!"} 7 | 8 | before do 9 | # Make sure both are created 10 | user 11 | post 12 | end 13 | 14 | describe "the index page" do 15 | before do 16 | visit admin_user_posts_path(user) 17 | end 18 | 19 | describe "the main content" do 20 | it "should display the default table" do 21 | page.should have_content(post.title) 22 | end 23 | end 24 | 25 | describe "the breadcrumb" do 26 | it "should have a link to the parent's index" do 27 | page.body.should have_tag("a", "Users", :attributes => { :href => "/admin/users" }) 28 | end 29 | it "should have a link to the parent" do 30 | page.body.should have_tag("a", user.id.to_s, :attributes => { :href => "/admin/users/#{user.id}" }) 31 | end 32 | end 33 | 34 | describe "the view links" do 35 | it "should take you to the sub resource" do 36 | click_link "View" 37 | current_path.should == "/admin/users/#{user.id}/posts/#{post.id}" 38 | end 39 | end 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /spec/unit/rails_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin do 4 | 5 | describe "use_asset_pipeline?" do 6 | 7 | before(:each) do 8 | @orig_rails_version = Rails::VERSION::MINOR 9 | end 10 | 11 | after(:each) do 12 | silence_warnings { Rails::VERSION::MINOR = @orig_rails_version } 13 | end 14 | 15 | it "should be false when using rails 3.0.x" do 16 | silence_warnings { Rails::VERSION::MINOR = 0 } 17 | ActiveAdmin.use_asset_pipeline?.should be_false 18 | end 19 | 20 | context "when rails 3.1.x" do 21 | before(:each) do 22 | @orig_rails_app = Rails.application.dup 23 | silence_warnings { Rails::VERSION::MINOR = 1 } 24 | end 25 | 26 | after(:each) do 27 | Rails.application = @orig_rails_app 28 | end 29 | 30 | it "should be false without asset pipeline enabled" do 31 | assets = mock(:enabled => false) 32 | Rails.application.config.stub!(:assets => assets) 33 | ActiveAdmin.use_asset_pipeline?.should be_false 34 | end 35 | 36 | it "should be true with asset pipeline enabled" do 37 | assets = mock(:enabled => true) 38 | Rails.application.config.stub!(:assets => assets) 39 | ActiveAdmin.use_asset_pipeline?.should be_true 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/active_admin/views/components/columns.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | 4 | class Columns < ActiveAdmin::Component 5 | builder_method :columns 6 | 7 | def column(*args, &block) 8 | insert_tag Column, *args, &block 9 | end 10 | 11 | # Override add child to set widths 12 | def add_child(*) 13 | super 14 | calculate_columns! 15 | end 16 | 17 | protected 18 | 19 | def margin_size 20 | 2 21 | end 22 | 23 | def calculate_columns! 24 | # Calculate our columns sizes and margins 25 | count = children.size 26 | margins_width = margin_size * (count - 1) 27 | column_width = (100.00 - margins_width) / count 28 | 29 | # Convert to an integer if its not a float 30 | column_width = column_width.to_i == column_width ? column_width.to_i : column_width 31 | 32 | children.each_with_index do |col, i| 33 | col.set_attribute :style, "width: #{column_width}%;" 34 | col.attr(:style) << " margin-right: #{margin_size}%;" unless i == (count - 1) 35 | end 36 | end 37 | 38 | def to_html 39 | super.to_s + "
".html_safe 40 | end 41 | 42 | end 43 | 44 | class Column < ActiveAdmin::Component 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Greg Bell, VersaPay Corporation 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | Iconic Icons are designed by P.J. Onori and are shared under 23 | the Creative Commons Attribution-Share Alike 3.0 license: 24 | http://creativecommons.org/licenses/by-sa/3.0/us 25 | http://somerandomdude.com/projects/iconic/ 26 | -------------------------------------------------------------------------------- /lib/active_admin/locales/es.yml: -------------------------------------------------------------------------------- 1 | es: 2 | active_admin: 3 | dashboard_welcome: 4 | welcome: "Bienvenido a Active Admin. Esta es la página de panel predeterminada." 5 | call_to_action: "Para agregar secciones edita 'app/admin/dashboards.rb'" 6 | view: "Ver" 7 | edit: "Editar" 8 | delete: "Eliminar" 9 | delete_confirmation: "¿Está seguro que quiere eliminar esto?" 10 | new_model: "Nuevo %{model}" 11 | edit_model: "Editar %{model}" 12 | delete_model: "Eliminar %{model}" 13 | details: "Detalles de %{model}" 14 | cancel: "Cancelar" 15 | empty: "Vacio" 16 | previous: "Anterior" 17 | next: "Siguiente" 18 | download: "Descargar:" 19 | has_many_new: "Agregar nuevo %{model}" 20 | has_many_delete: "Eliminar" 21 | filter: "Filtrar" 22 | clear_filters: "Quitar Filtros" 23 | search_field: "Buscar %{field}" 24 | equal_to: "Igual a" 25 | greater_than: "Mayor que" 26 | less_than: "Menor que" 27 | main_content: "Por favor implemente %{model}#main_content para mostrar contenido." 28 | logout: "Salir" 29 | sidebars: 30 | filters: "Filtros" 31 | pagination: 32 | empty: "Ningún %{model} encontrado" 33 | one: "Mostrando 1 %{model}" 34 | one_page: "Mostrando todos los %{n} %{model}" 35 | multiple: "Mostrando %{model} %{from} - %{to} de un total de %{total}" 36 | any: "Todos" 37 | -------------------------------------------------------------------------------- /lib/generators/active_admin/assets/templates/dashboards.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin::Dashboards.build do 2 | 3 | # Define your dashboard sections here. Each block will be 4 | # rendered on the dashboard in the context of the view. So just 5 | # return the content which you would like to display. 6 | 7 | # == Simple Dashboard Section 8 | # Here is an example of a simple dashboard section 9 | # 10 | # section "Recent Posts" do 11 | # ul do 12 | # Post.recent(5).collect do |post| 13 | # li link_to(post.title, admin_post_path(post)) 14 | # end 15 | # end 16 | # end 17 | 18 | # == Render Partial Section 19 | # The block is rendererd within the context of the view, so you can 20 | # easily render a partial rather than build content in ruby. 21 | # 22 | # section "Recent Posts" do 23 | # render 'recent_posts' # => this will render /app/views/admin/dashboard/_recent_posts.html.erb 24 | # end 25 | 26 | # == Section Ordering 27 | # The dashboard sections are ordered by a given priority from top left to 28 | # bottom right. The default priority is 10. By giving a section numerically lower 29 | # priority it will be sorted higher. For example: 30 | # 31 | # section "Recent Posts", :priority => 10 32 | # section "Recent User", :priority => 1 33 | # 34 | # Will render the "Recent Users" then the "Recent Posts" sections on the dashboard. 35 | 36 | end 37 | -------------------------------------------------------------------------------- /script/local: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require File.expand_path('../../spec/support/detect_rails_version', __FILE__) 4 | 5 | unless ARGV[0] 6 | puts <<-EOF 7 | Usage: ./script/#{__FILE__} COMMAND [ARGS] 8 | 9 | The command will be run in the context of the local rails 10 | app stored in test-rails-app. 11 | 12 | Examples: 13 | 14 | ./script/local server 15 | ./script/local c 16 | ./script/local rake db:migrate 17 | EOF 18 | exit(1) 19 | end 20 | 21 | # Set up some variables 22 | rails_version = detect_rails_version || '3.0.0' 23 | 24 | test_app_dir = ".test-rails-apps" 25 | test_app_path = "#{test_app_dir}/test-rails-app-#{rails_version}" 26 | 27 | # Ensure .test-rails-apps is created 28 | system "mkdir #{test_app_dir}" unless File.exists?(test_app_dir) 29 | 30 | # Create the sample rails app if it doesn't already exist 31 | unless File.exists? test_app_path 32 | system "RAILS='#{rails_version}' bundle exec rails new #{test_app_path} -m spec/support/rails_template_with_data.rb" 33 | end 34 | 35 | # Link this rails app 36 | system "rm test-rails-app" 37 | system "ln -s #{test_app_path} test-rails-app" 38 | 39 | # If it's a rails command, auto add the rails script 40 | RAILS_COMMANDS = %w{generate console server dbconsole g c s runner} 41 | args = RAILS_COMMANDS.include?(ARGV[0]) ? ["rails", ARGV].flatten : ARGV 42 | 43 | # Run the command 44 | exec "cd test-rails-app && GEMFILE=../Gemfile bundle exec #{args.join(" ")}" 45 | -------------------------------------------------------------------------------- /lib/active_admin/locales/pt.yml: -------------------------------------------------------------------------------- 1 | pt: 2 | active_admin: 3 | dashboard_welcome: 4 | welcome: "Bem vindo ao Active Admin. Esta é a página de painéis padrão." 5 | call_to_action: "Para adicionar seções ao painel, verifique 'app/admin/dashboards.rb'" 6 | view: "Visualizar" 7 | edit: "Editar" 8 | delete: "Remover" 9 | delete_confirmation: "Você tem certeza que deseja remover este item?" 10 | new_model: "Novo(a) %{model}" 11 | edit_model: "Editar %{model}" 12 | delete_model: "Remover %{model}" 13 | details: "Detalhes do(a) %{model}" 14 | cancel: "Cancelar" 15 | empty: "Vazio" 16 | previous: "Anterior" 17 | next: "Próximo" 18 | download: "Baixar:" 19 | has_many_new: "Adicionar Novo(a) %{model}" 20 | has_many_delete: "Remover" 21 | filter: "Filtrar" 22 | clear_filters: "Limpar Filtros" 23 | search_field: "Pesquisar %{field}" 24 | equal_to: "Igual A" 25 | greater_than: "Maior Que" 26 | less_than: "Menor Que" 27 | main_content: "Por favor implemente %{model}#main_content para exibir conteúdo." 28 | logout: "Sair" 29 | sidebars: 30 | filters: "Filtros" 31 | pagination: 32 | empty: "Nenhum(a) %{model} encontrado(a)" 33 | one: "Exibindo 1 %{model}" 34 | one_page: "Exibindo todos os(a) %{n} %{model}" 35 | multiple: "Exibindo %{model} %{from} - %{to} de um total de %{total}" 36 | 37 | -------------------------------------------------------------------------------- /lib/active_admin/resource/menu.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class Resource 3 | module Menu 4 | 5 | # Set the menu options. To not add this resource to the menu, just 6 | # call #menu(false) 7 | def menu(options = {}) 8 | options = options == false ? { :display => false } : options 9 | @menu_options = options 10 | end 11 | 12 | # The options to use for the menu 13 | def menu_options 14 | @menu_options ||= {} 15 | end 16 | 17 | # Returns the name to put this resource under in the menu 18 | def parent_menu_item_name 19 | menu_options[:parent] 20 | end 21 | 22 | # Returns the name to be displayed in the menu for this resource 23 | def menu_item_name 24 | menu_options[:label] || plural_resource_name 25 | end 26 | 27 | # Returns the items priority for altering the default sort order 28 | def menu_item_priority 29 | menu_options[:priority] || 10 30 | end 31 | 32 | # Returns a proc for deciding whether to display the menu item or not in the view 33 | def menu_item_display_if 34 | menu_options[:if] || proc { true } 35 | end 36 | 37 | # Should this resource be added to the menu system? 38 | def include_in_menu? 39 | return false if menu_options[:display] == false 40 | !(belongs_to? && !belongs_to_config.optional?) 41 | end 42 | 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/generators/active_admin/install/templates/migrations/2_move_admin_notes_to_comments.rb: -------------------------------------------------------------------------------- 1 | class MoveAdminNotesToComments < ActiveRecord::Migration 2 | def self.up 3 | remove_index :admin_notes, [:admin_user_type, :admin_user_id] 4 | rename_table :admin_notes, :active_admin_comments 5 | rename_column :active_admin_comments, :admin_user_type, :author_type 6 | rename_column :active_admin_comments, :admin_user_id, :author_id 7 | add_column :active_admin_comments, :namespace, :string 8 | add_index :active_admin_comments, [:namespace] 9 | add_index :active_admin_comments, [:author_type, :author_id] 10 | 11 | # Update all the existing comments to the default namespace 12 | say "Updating any existing comments to the #{ActiveAdmin.application.default_namespace} namespace." 13 | execute "UPDATE active_admin_comments SET namespace='#{ActiveAdmin.application.default_namespace}'" 14 | end 15 | 16 | def self.down 17 | remove_index :active_admin_comments, :column => [:author_type, :author_id] 18 | remove_index :active_admin_comments, :column => [:namespace] 19 | remove_column :active_admin_comments, :namespace 20 | rename_column :active_admin_comments, :author_id, :admin_user_id 21 | rename_column :active_admin_comments, :author_type, :admin_user_type 22 | rename_table :active_admin_comments, :admin_notes 23 | add_index :admin_notes, [:admin_user_type, :admin_user_id] 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/active_admin/iconic.rb: -------------------------------------------------------------------------------- 1 | require 'active_admin/iconic/icons' 2 | 3 | module ActiveAdmin 4 | module Iconic 5 | 6 | # Default color to use for icons 7 | @@default_color = "#5E6469" 8 | mattr_accessor :default_color 9 | 10 | # Default width to use for icons 11 | @@default_width = 15 12 | mattr_accessor :default_width 13 | 14 | # Default height to use for icons 15 | @@default_height = 15 16 | mattr_accessor :default_height 17 | 18 | # Render an icon: 19 | # Iconic.icon :loop 20 | def self.icon(name, options = {}) 21 | options = { 22 | :color => default_color, 23 | :width => default_width, 24 | :height => default_height, 25 | :id => "" 26 | }.merge(options) 27 | 28 | 29 | options[:style] = "fill:#{options[:color]};" 30 | options[:fill] = options.delete(:color) 31 | 32 | # Convert to strings representations of pixels 33 | [:width, :height].each do |key| 34 | options[key] = "#{options[key]}px" unless options[key].is_a?(String) 35 | end 36 | 37 | template = ICONS[name.to_sym] 38 | 39 | if template 40 | svg = template.dup 41 | options.each do |key, value| 42 | svg.gsub!("{#{key}}", value) 43 | end 44 | "#{svg}".html_safe 45 | else 46 | raise "Could not find the icon named #{name}" 47 | end 48 | end 49 | 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/generators/active_admin/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Generators 3 | class InstallGenerator < Rails::Generators::Base 4 | desc "Installs Active Admin and generats the necessary migrations" 5 | 6 | hook_for :users, :default => "devise", :desc => "Admin user generator to run. Skip with --skip-users" 7 | 8 | include Rails::Generators::Migration 9 | 10 | def self.source_root 11 | @_active_admin_source_root ||= File.expand_path("../templates", __FILE__) 12 | end 13 | 14 | def self.next_migration_number(dirname) 15 | Time.now.strftime("%Y%m%d%H%M%S") 16 | end 17 | 18 | def copy_initializer 19 | template 'active_admin.rb.erb', 'config/initializers/active_admin.rb' 20 | end 21 | 22 | def setup_directory 23 | empty_directory "app/admin" 24 | template 'dashboards.rb', 'app/admin/dashboards.rb' 25 | end 26 | 27 | def setup_routes 28 | route "ActiveAdmin.routes(self)" 29 | end 30 | 31 | def create_assets 32 | generate "active_admin:assets" 33 | end 34 | 35 | def create_migrations 36 | Dir["#{self.class.source_root}/migrations/*.rb"].sort.each do |filepath| 37 | name = File.basename(filepath) 38 | migration_template "migrations/#{name}", "db/migrate/#{name.gsub(/^\d+_/,'')}" 39 | sleep 1 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/generators/active_admin/install/templates/dashboards.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin::Dashboards.build do 2 | 3 | # Define your dashboard sections here. Each block will be 4 | # rendered on the dashboard in the context of the view. So just 5 | # return the content which you would like to display. 6 | 7 | # == Simple Dashboard Section 8 | # Here is an example of a simple dashboard section 9 | # 10 | # section "Recent Posts" do 11 | # ul do 12 | # Post.recent(5).collect do |post| 13 | # li link_to(post.title, admin_post_path(post)) 14 | # end 15 | # end 16 | # end 17 | 18 | # == Render Partial Section 19 | # The block is rendered within the context of the view, so you can 20 | # easily render a partial rather than build content in ruby. 21 | # 22 | # section "Recent Posts" do 23 | # div do 24 | # render 'recent_posts' # => this will render /app/views/admin/dashboard/_recent_posts.html.erb 25 | # end 26 | # end 27 | 28 | # == Section Ordering 29 | # The dashboard sections are ordered by a given priority from top left to 30 | # bottom right. The default priority is 10. By giving a section numerically lower 31 | # priority it will be sorted higher. For example: 32 | # 33 | # section "Recent Posts", :priority => 10 34 | # section "Recent User", :priority => 1 35 | # 36 | # Will render the "Recent Users" then the "Recent Posts" sections on the dashboard. 37 | 38 | end 39 | -------------------------------------------------------------------------------- /lib/active_admin/locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | active_admin: 3 | dashboard_welcome: 4 | welcome: "Welcome to Active Admin. This is the default dashboard page." 5 | call_to_action: "To add dashboard sections, checkout 'app/admin/dashboards.rb'" 6 | view: "View" 7 | edit: "Edit" 8 | delete: "Delete" 9 | delete_confirmation: "Are you sure you want to delete this?" 10 | new_model: "New %{model}" 11 | edit_model: "Edit %{model}" 12 | delete_model: "Delete %{model}" 13 | details: "%{model} Details" 14 | cancel: "Cancel" 15 | empty: "Empty" 16 | previous: "Previous" 17 | next: "Next" 18 | download: "Download:" 19 | has_many_new: "Add New %{model}" 20 | has_many_delete: "Delete" 21 | filter: "Filter" 22 | clear_filters: "Clear Filters" 23 | search_field: "Search %{field}" 24 | equal_to: "Equal To" 25 | greater_than: "Greater Than" 26 | less_than: "Less Than" 27 | main_content: "Please implement %{model}#main_content to display content." 28 | logout: "Logout" 29 | sidebars: 30 | filters: "Filters" 31 | pagination: 32 | empty: "No %{model} found" 33 | one: "Displaying 1 %{model}" 34 | one_page: "Displaying all %{n} %{model}" 35 | multiple: "Displaying %{model} %{from} - %{to} of %{total} in total" 36 | any: "Any" 37 | blank_slate: 38 | content: "There are no %{resource_name} yet." 39 | link: "Create one" -------------------------------------------------------------------------------- /spec/unit/belongs_to_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module ActiveAdmin 4 | class Resource 5 | describe BelongsTo do 6 | 7 | let(:application){ ActiveAdmin::Application.new } 8 | let(:namespace){ Namespace.new(application, :admin) } 9 | let(:post){ namespace.register(Post) } 10 | let(:belongs_to){ BelongsTo.new(post, :user) } 11 | 12 | it "should have an owner" do 13 | belongs_to.owner.should == post 14 | end 15 | 16 | it "should have a namespace" do 17 | belongs_to.namespace.should == namespace 18 | end 19 | 20 | describe "finding the target" do 21 | context "when the resource has been registered" do 22 | let(:user){ namespace.register(User) } 23 | before { user } # Ensure user is registered 24 | 25 | it "should return the target resource" do 26 | belongs_to.target.should == user 27 | end 28 | end 29 | 30 | context "when the resource has not been registered" do 31 | it "should raise a ActiveAdmin::BelongsTo::TargetNotFound" do 32 | lambda { 33 | belongs_to.target 34 | }.should raise_error(ActiveAdmin::Resource::BelongsTo::TargetNotFound) 35 | end 36 | end 37 | end 38 | 39 | it "should be optional" do 40 | belongs_to = BelongsTo.new post, :user, :optional => true 41 | belongs_to.should be_optional 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/active_admin/resource_controller/page_configurations.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class ResourceController < ::InheritedResources::Base 3 | 4 | module PageConfigurations 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | helper_method :index_config 9 | helper_method :show_config 10 | end 11 | 12 | module ClassMethods 13 | 14 | def set_page_config(page, options, &block) 15 | active_admin_config.page_configs[page] = ActiveAdmin::PageConfig.new(options, &block) 16 | end 17 | 18 | def get_page_config(page) 19 | active_admin_config.page_configs[page] 20 | end 21 | 22 | def reset_page_config!(page) 23 | active_admin_config.page_configs[page] = nil 24 | end 25 | 26 | # Define the getting and re-setter for each configurable page 27 | [:index, :show].each do |page| 28 | # eg: index_config 29 | define_method :"#{page}_config" do 30 | get_page_config(page) 31 | end 32 | 33 | # eg: reset_index_config! 34 | define_method :"reset_#{page}_config!" do 35 | reset_page_config! page 36 | end 37 | end 38 | 39 | end 40 | 41 | protected 42 | 43 | def index_config 44 | @index_config ||= self.class.index_config 45 | end 46 | 47 | def show_config 48 | @show_config ||= self.class.show_config 49 | end 50 | 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/active_admin/dashboards.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Dashboards 3 | 4 | autoload :DashboardController, 'active_admin/dashboards/dashboard_controller' 5 | autoload :Section, 'active_admin/dashboards/section' 6 | 7 | @@sections = {} 8 | mattr_accessor :sections 9 | 10 | class << self 11 | 12 | # Eval an entire block in the context of this module to build 13 | # dashboards quicker. 14 | # 15 | # Example: 16 | # 17 | # ActiveAdmin::Dashboards.build do 18 | # section "Recent Post" do 19 | # # return a list of posts 20 | # end 21 | # end 22 | # 23 | def build(&block) 24 | module_eval(&block) 25 | end 26 | 27 | # Add a new dashboard section to a namespace. If no namespace is given 28 | # it will be added to the default namespace. 29 | def add_section(name, options = {}, &block) 30 | namespace = options.delete(:namespace) || ActiveAdmin.application.default_namespace || :root 31 | self.sections[namespace] ||= [] 32 | self.sections[namespace] << Section.new(namespace, name, options, &block) 33 | self.sections[namespace].sort! 34 | end 35 | alias_method :section, :add_section 36 | 37 | def sections_for_namespace(namespace) 38 | @@sections[namespace] || [] 39 | end 40 | 41 | def clear_all_sections! 42 | @@sections = {} 43 | end 44 | 45 | end 46 | 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /features/support/paths.rb: -------------------------------------------------------------------------------- 1 | module NavigationHelpers 2 | # Maps a name to a path. Used by the 3 | # 4 | # When /^I go to (.+)$/ do |page_name| 5 | # 6 | # step definition in web_steps.rb 7 | # 8 | def path_to(page_name) 9 | case page_name 10 | 11 | when /the home\s?page/ 12 | '/' 13 | when /the dashboard/ 14 | "/admin" 15 | when /the new post page/ 16 | "/admin/posts/new" 17 | 18 | # the index page for posts in the root namespace 19 | # the index page for posts in the user_admin namespace 20 | when /^the index page for (.*) in the (.*) namespace$/ 21 | if $2 != 'root' 22 | send(:"#{$2}_#{$1}_path") 23 | else 24 | send(:"#{$1}_path") 25 | end 26 | 27 | # same as above, except defaults to admin namespace 28 | when /^the index page for (.*)$/ 29 | send(:"admin_#{$1}_path") 30 | 31 | # Add more mappings here. 32 | # Here is an example that pulls values out of the Regexp: 33 | # 34 | # when /^(.*)'s profile page$/i 35 | # user_profile_path(User.find_by_login($1)) 36 | 37 | else 38 | begin 39 | page_name =~ /the (.*) page/ 40 | path_components = $1.split(/\s+/) 41 | self.send(path_components.push('path').join('_').to_sym) 42 | rescue Object => e 43 | raise "Can't find mapping from \"#{page_name}\" to a path.\n" + 44 | "Now, go and add a mapping in #{__FILE__}" 45 | end 46 | end 47 | end 48 | end 49 | 50 | World(NavigationHelpers) 51 | -------------------------------------------------------------------------------- /spec/unit/event_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'active_admin/event' 3 | 4 | describe ActiveAdmin::EventDispatcher do 5 | 6 | let(:test_event){ 'active_admin.test_event' } 7 | let(:dispatcher){ ActiveAdmin::EventDispatcher.new } 8 | 9 | it "should add a subscriber for an event" do 10 | dispatcher.subscribers(test_event).size.should == 0 11 | dispatcher.subscribe(test_event){ true } 12 | dispatcher.subscribers(test_event).size.should == 1 13 | end 14 | 15 | it "should call the dispatch block with no arguments" do 16 | dispatcher.subscribe(test_event){ raise StandardError, "From Event Handler" } 17 | lambda { 18 | dispatcher.dispatch(test_event) 19 | }.should raise_error(StandardError, "From Event Handler") 20 | end 21 | 22 | it "should call the dispatch block with one argument" do 23 | arg = nil 24 | dispatcher.subscribe(test_event){|passed_in| arg = passed_in } 25 | dispatcher.dispatch(test_event, "My Arg") 26 | arg.should == "My Arg" 27 | end 28 | 29 | it "should clear all subscribers" do 30 | dispatcher.subscribe(test_event){ false } 31 | dispatcher.subscribe(test_event + "_2"){ false } 32 | dispatcher.clear_all_subscribers! 33 | dispatcher.subscribers(test_event).size.should == 0 34 | dispatcher.subscribers(test_event + "_2").size.should == 0 35 | end 36 | 37 | it "should have a dispatcher available from ActiveAdmin::Event" do 38 | ActiveAdmin::Event.should be_an_instance_of(ActiveAdmin::EventDispatcher) 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /spec/unit/dashboard_section_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin::Dashboards::Section do 4 | 5 | def section(options = {}) 6 | name = options.delete(:name) || "Recent Posts" 7 | ActiveAdmin::Dashboards::Section.new(:admin, name, options){ } 8 | end 9 | 10 | describe "accessors" do 11 | it "should have a namespace" do 12 | section.namespace.should == :admin 13 | end 14 | 15 | it "should have a block" do 16 | section.block.class.should == Proc 17 | end 18 | 19 | it "should have a name" do 20 | section.name.should == 'Recent Posts' 21 | end 22 | end 23 | 24 | describe "priority" do 25 | context "when not set" do 26 | subject{ section.priority } 27 | it { should == ActiveAdmin::Dashboards::Section::DEFAULT_PRIORITY } 28 | end 29 | 30 | context "when set" do 31 | subject{ section(:priority => 1).priority } 32 | it { should == 1 } 33 | end 34 | end 35 | 36 | describe "icon" do 37 | it "should set the icon" do 38 | s = section(:icon => :my_icon) 39 | s.icon.should == :my_icon 40 | end 41 | it "should be nil by default" do 42 | section.icon.should be_nil 43 | end 44 | end 45 | 46 | describe "sorting sections" do 47 | it "should sort by priority then alpha" do 48 | s1 = section :name => "Woot" 49 | s2 = section :name => :Alpha 50 | s3 = section :name => "Zulu", :priority => 1 51 | s4 = section :name => "Beta", :priority => 100 52 | [s1,s2,s3,s4].sort.should == [s3, s2, s1, s4] 53 | end 54 | end 55 | 56 | end 57 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin/mixins/_rounded.css.scss: -------------------------------------------------------------------------------- 1 | @mixin rounded($radius: 3px) { 2 | -webkit-border-radius: $radius; 3 | -moz-border-radius: $radius; 4 | border-radius: $radius; 5 | } 6 | 7 | @mixin rounded-all($top-left:3px, $top-right:3px, $bottom-right:3px, $bottom-left:3px) { 8 | border-top-right-radius: $top-right; 9 | -moz-border-radius-topright: $top-right; 10 | -webkit-border-top-right-radius: $top-right; 11 | 12 | border-top-left-radius: $top-left; 13 | -moz-border-radius-topleft: $top-left; 14 | -webkit-border-top-left-radius: $top-left; 15 | 16 | border-bottom-right-radius: $bottom-right; 17 | -moz-border-radius-bottomright: $bottom-right; 18 | -webkit-border-bottom-right-radius: $bottom-right; 19 | 20 | border-bottom-left-radius: $bottom-left; 21 | -moz-border-radius-bottomleft: $bottom-left; 22 | -webkit-border-bottom-left-radius: $bottom-left; 23 | } 24 | 25 | @mixin rounded-top($radius: 3px) { 26 | @include rounded(0); 27 | border-top-right-radius: $radius; 28 | border-top-left-radius: $radius; 29 | -moz-border-radius-topright: $radius; 30 | -moz-border-radius-topleft: $radius; 31 | -webkit-border-top-right-radius: $radius; 32 | -webkit-border-top-left-radius: $radius; 33 | } 34 | 35 | @mixin rounded-bottom($radius: 3px) { 36 | @include rounded(0); 37 | border-bottom-right-radius: $radius; 38 | border-bottom-left-radius: $radius; 39 | -moz-border-radius-bottomright: $radius; 40 | -moz-border-radius-bottomleft: $radius; 41 | -webkit-border-bottom-right-radius: $radius; 42 | -webkit-border-bottom-left-radius: $radius; 43 | } 44 | -------------------------------------------------------------------------------- /lib/active_admin/view_helpers/auto_link_helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module ViewHelpers 3 | module AutoLinkHelper 4 | 5 | # Automatically links objects to their resource controllers. If 6 | # the resource has not been registered, a string representation of 7 | # the object is returned. 8 | # 9 | # The default content in the link is returned from ActiveAdmin::ViewHelpers::DisplayHelper#display_name 10 | # 11 | # You can pass in the content to display 12 | # eg: auto_link(@post, "My Link Content") 13 | # 14 | def auto_link(resource, link_content = nil) 15 | content = link_content || display_name(resource) 16 | if registration = active_admin_resource_for(resource.class) 17 | begin 18 | content = link_to(content, send(registration.route_instance_path, resource)) 19 | rescue 20 | end 21 | end 22 | content 23 | end 24 | 25 | # Returns the ActiveAdmin::Resource instance for a class 26 | def active_admin_resource_for(klass) 27 | active_admin_namespace.resource_for(klass) 28 | end 29 | 30 | # Returns the current Active Admin namespace 31 | def active_admin_namespace 32 | if respond_to?(:active_admin_config) && active_admin_config 33 | active_admin_config.namespace 34 | else 35 | # Return a default namespace if none exists 36 | active_admin_application.find_or_create_namespace(active_admin_application.default_namespace) 37 | end 38 | end 39 | 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /features/index/index_as_grid.feature: -------------------------------------------------------------------------------- 1 | Feature: Index as Grid 2 | 3 | Viewing resources as a grid on the index page 4 | 5 | Scenario: Viewing index as a grid with a simple block configuration 6 | Given 9 posts exist 7 | And an index configuration of: 8 | """ 9 | ActiveAdmin.register Post do 10 | index :as => :grid do |post| 11 | h2 auto_link(post) 12 | end 13 | end 14 | """ 15 | Then the table ".index_grid" should have 3 rows 16 | And the table ".index_grid" should have 3 columns 17 | And there should be 9 "a" tags within index grid 18 | 19 | Scenario: Viewing index as a grid and set the number of columns 20 | Given 9 posts exist 21 | And an index configuration of: 22 | """ 23 | ActiveAdmin.register Post do 24 | index :as => :grid, :columns => 1 do |post| 25 | h2 auto_link(post) 26 | end 27 | end 28 | """ 29 | Then the table ".index_grid" should have 9 rows 30 | And the table ".index_grid" should have 1 columns 31 | And there should be 9 "a" tags within "table.index_grid" 32 | 33 | Scenario: Viewing index as a grid with an odd number of items 34 | Given 9 posts exist 35 | And an index configuration of: 36 | """ 37 | ActiveAdmin.register Post do 38 | index :as => :grid, :columns => 2 do |post| 39 | h2 auto_link(post) 40 | end 41 | end 42 | """ 43 | Then the table ".index_grid" should have 5 rows 44 | And the table ".index_grid" should have 2 columns 45 | And there should be 9 "a" tags within "table.index_grid" 46 | -------------------------------------------------------------------------------- /spec/unit/arbre/html/element_finder_methods_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Arbre::HTML::Element, "Finder Methods" do 4 | include Arbre::HTML 5 | let(:assigns){ {} } 6 | 7 | describe "finding elements by tag name" do 8 | 9 | it "should return 0 when no elements exist" do 10 | div.get_elements_by_tag_name("li").size.should == 0 11 | end 12 | 13 | it "should return a child element" do 14 | html = div do 15 | ul 16 | li 17 | ul 18 | end 19 | elements = html.get_elements_by_tag_name("li") 20 | elements.size.should == 1 21 | elements[0].should be_instance_of(Arbre::HTML::Li) 22 | end 23 | 24 | it "should return multple child elements" do 25 | html = div do 26 | ul 27 | li 28 | ul 29 | li 30 | end 31 | elements = html.get_elements_by_tag_name("li") 32 | elements.size.should == 2 33 | elements[0].should be_instance_of(Arbre::HTML::Li) 34 | elements[1].should be_instance_of(Arbre::HTML::Li) 35 | end 36 | 37 | it "should return children's child elements" do 38 | html = div do 39 | ul 40 | li do 41 | li 42 | end 43 | end 44 | elements = html.get_elements_by_tag_name("li") 45 | elements.size.should == 2 46 | elements[0].should be_instance_of(Arbre::HTML::Li) 47 | elements[1].should be_instance_of(Arbre::HTML::Li) 48 | elements[1].parent.should == elements[0] 49 | end 50 | end 51 | 52 | describe "finding an element by id" 53 | describe "finding an element by a class name" 54 | end 55 | -------------------------------------------------------------------------------- /features/index/format_as_csv.feature: -------------------------------------------------------------------------------- 1 | Feature: Format as CSV 2 | 3 | Background: 4 | Given I am logged in 5 | 6 | Scenario: Default 7 | Given a configuration of: 8 | """ 9 | ActiveAdmin.register Post 10 | """ 11 | And a post with the title "Hello World" exists 12 | When I am on the index page for posts 13 | And I follow "CSV" 14 | And I should download a CSV file for "posts" containing: 15 | | Id | Title | Body | Published At | Created At | Updated At | 16 | | \d+ | Hello World | | | (.*) | (.*) | 17 | 18 | Scenario: Default with alias 19 | Given a configuration of: 20 | """ 21 | ActiveAdmin.register Post, :as => "MyArticle" 22 | """ 23 | And 1 post exists 24 | When I am on the index page for my_articles 25 | And I follow "CSV" 26 | And I should download a CSV file for "my-articles" containing: 27 | | Id | Title | Body | Published At | Created At | Updated At | 28 | 29 | Scenario: With CSV format customization 30 | Given a configuration of: 31 | """ 32 | ActiveAdmin.register Post do 33 | csv do 34 | column :title 35 | column("Last update") { |post| post.updated_at } 36 | column("Copyright") { "Greg Bell" } 37 | end 38 | end 39 | """ 40 | And a post with the title "Hello, World" exists 41 | When I am on the index page for posts 42 | And I follow "CSV" 43 | And I should download a CSV file for "posts" containing: 44 | | Title | Last update | Copyright | 45 | | Hello, World | (.*) | Greg Bell | 46 | 47 | -------------------------------------------------------------------------------- /lib/active_admin/views/pages/show.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | module Pages 4 | class Show < Base 5 | 6 | def config 7 | active_admin_config.page_configs[:show] || ::ActiveAdmin::PageConfig.new 8 | end 9 | 10 | def title 11 | case config[:title] 12 | when Symbol, Proc 13 | call_method_or_proc_on(resource, config[:title]) 14 | when String 15 | config[:title] 16 | else 17 | default_title 18 | end 19 | end 20 | 21 | def main_content 22 | if config.block 23 | # Eval the show config from the controller 24 | instance_exec resource, &config.block 25 | else 26 | default_main_content 27 | end 28 | end 29 | 30 | def attributes_table(*args, &block) 31 | panel(I18n.t('active_admin.details', :model => active_admin_config.resource_name)) do 32 | attributes_table_for resource, *args, &block 33 | end 34 | end 35 | 36 | protected 37 | 38 | def default_title 39 | "#{active_admin_config.resource_name} ##{resource.id}" 40 | end 41 | 42 | module DefaultMainContent 43 | def default_main_content 44 | attributes_table *default_attribute_table_rows 45 | end 46 | 47 | def default_attribute_table_rows 48 | resource.class.columns.collect{|column| column.name.to_sym } 49 | end 50 | end 51 | 52 | include DefaultMainContent 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /features/show/default_content.feature: -------------------------------------------------------------------------------- 1 | Feature: Show - Default Content 2 | 3 | Viewing the show page for a resource 4 | 5 | Background: 6 | Given a post with the title "Hello World" written by "Jane Doe" exists 7 | 8 | Scenario: Viewing the default show page 9 | Given a show configuration of: 10 | """ 11 | ActiveAdmin.register Post 12 | """ 13 | Then I should see the attribute "Title" with "Hello World" 14 | And I should see the attribute "Body" with "Empty" 15 | And I should see the attribute "Created At" with a nicely formatted datetime 16 | And I should see the attribute "Author" with "jane_doe" 17 | And I should see an action item button "Delete Post" 18 | And I should see an action item button "Edit Post" 19 | 20 | Scenario: Attributes should link when linked resource is registered 21 | Given a show configuration of: 22 | """ 23 | ActiveAdmin.register User 24 | ActiveAdmin.register Post 25 | """ 26 | Then I should see the attribute "Author" with "jane_doe" 27 | And I should see a link to "jane_doe" 28 | 29 | Scenario: Customizing the attributes table with a set of attributes 30 | Given a show configuration of: 31 | """ 32 | ActiveAdmin.register Post do 33 | 34 | show do 35 | attributes_table :title, :body, :created_at, :updated_at 36 | end 37 | 38 | end 39 | """ 40 | Then I should see the attribute "Title" with "Hello World" 41 | And I should see the attribute "Body" with "Empty" 42 | And I should see the attribute "Created At" with a nicely formatted datetime 43 | And I should not see the attribute "Author" 44 | -------------------------------------------------------------------------------- /spec/unit/menu_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin::Menu do 4 | 5 | context "with no items" do 6 | it "should be empty" do 7 | ActiveAdmin::Menu.new.items.should == [] 8 | end 9 | 10 | it "should accept new items" do 11 | menu = ActiveAdmin::Menu.new 12 | menu.add "Dashboard", "/admin" 13 | menu.items.first.should be_an_instance_of(ActiveAdmin::MenuItem) 14 | menu.items.first.name.should == "Dashboard" 15 | end 16 | 17 | it "should default new items to the priority of 10" do 18 | menu = ActiveAdmin::Menu.new 19 | menu.add "Dashboard", "/admin" 20 | menu.items.first.priority.should == 10 21 | end 22 | end 23 | 24 | context "with many item" do 25 | let(:menu) do 26 | ActiveAdmin::Menu.new do |m| 27 | m.add "Dashboard", "/admin" 28 | m.add "Blog", "/admin/blog" 29 | m.add "Users", "/admin/users" 30 | m.add "Settings", "/admin/settings" do |s| 31 | s.add "Admin Settings", "/admin/settings/admin" do |as| 32 | s.add "User Settings", "/admin/settings/users" 33 | end 34 | end 35 | end 36 | end 37 | 38 | it "should give access to the menu item as an array" do 39 | menu['Dashboard'].name.should == 'Dashboard' 40 | end 41 | 42 | it "should find the item by a url on the top level" do 43 | menu.find_by_url("/admin").name.should == "Dashboard" 44 | end 45 | 46 | it "should find the item deep in the tree" do 47 | menu.find_by_url("/admin/settings/users").name.should == "User Settings" 48 | end 49 | 50 | end 51 | 52 | end 53 | 54 | -------------------------------------------------------------------------------- /lib/active_admin/resource_controller/filters.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class ResourceController < ::InheritedResources::Base 3 | 4 | module Filters 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | helper_method :filters_config 9 | end 10 | 11 | module ClassMethods 12 | def filter(attribute, options = {}) 13 | return false if attribute.nil? 14 | @filters ||= [] 15 | @filters << options.merge(:attribute => attribute) 16 | end 17 | 18 | def filters_config 19 | @filters && @filters.any? ? @filters : default_filters_config 20 | end 21 | 22 | def reset_filters! 23 | @filters = [] 24 | end 25 | 26 | # Returns a sane set of filters by default for the object 27 | def default_filters_config 28 | default_association_filters + default_content_filters 29 | end 30 | 31 | # Returns a default set of filters for the associations 32 | def default_association_filters 33 | if resource_class.respond_to?(:reflections) 34 | resource_class.reflections.collect{|name, r| { :attribute => name }} 35 | else 36 | [] 37 | end 38 | end 39 | 40 | # Returns a default set of filters for the content columns 41 | def default_content_filters 42 | if resource_class.respond_to?(:content_columns) 43 | resource_class.content_columns.collect{|c| { :attribute => c.name.to_sym } } 44 | else 45 | [] 46 | end 47 | end 48 | end 49 | 50 | protected 51 | 52 | def filters_config 53 | self.class.filters_config 54 | end 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/unit/arbre/html/tag_attributes_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Arbre::HTML::Tag, "Attributes" do 4 | include Arbre::HTML 5 | let(:assigns){ {} } 6 | 7 | let(:tag){ Arbre::HTML::Tag.new } 8 | 9 | describe "attributes" do 10 | before { tag.build :id => "my_id" } 11 | 12 | it "should have an attributes hash" do 13 | tag.attributes.should == {:id => "my_id"} 14 | end 15 | 16 | it "should render the attributes to html" do 17 | tag.to_html.should == <<-HTML 18 | 19 | HTML 20 | end 21 | 22 | it "should get an attribute value" do 23 | tag.attr(:id).should == "my_id" 24 | end 25 | 26 | describe "#has_attribute?" do 27 | context "when the attribute exists" do 28 | it "should return true" do 29 | tag.has_attribute?(:id).should == true 30 | end 31 | end 32 | 33 | context "when the attribute does not exist" do 34 | it "should return false" do 35 | tag.has_attribute?(:class).should == false 36 | end 37 | end 38 | end 39 | 40 | it "should remove an attribute" do 41 | tag.attributes.should == {:id => "my_id"} 42 | tag.remove_attribute(:id).should == "my_id" 43 | tag.attributes.should == {} 44 | end 45 | end 46 | 47 | describe "rendering attributes" do 48 | it "should html safe the attribute values" do 49 | tag.set_attribute(:class, "\">bad things!") 50 | tag.to_html.should == <<-HTML 51 | 52 | HTML 53 | end 54 | it "should should escape the attribute names" do 55 | tag.set_attribute(">bad", "things") 56 | tag.to_html.should == <<-HTML 57 | 58 | HTML 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /features/sti_resource.feature: -------------------------------------------------------------------------------- 1 | Feature: STI Resource 2 | 3 | Ensure that standard CRUD works with STI models 4 | 5 | Background: 6 | Given I am logged in 7 | And a configuration of: 8 | """ 9 | ActiveAdmin.register Publisher 10 | ActiveAdmin.register User 11 | """ 12 | 13 | Scenario: Create, update and delete a child STI resource 14 | Given I am on the index page for publishers 15 | When I follow "New Publisher" 16 | And I fill in "First name" with "Terry" 17 | And I fill in "Last name" with "Fox" 18 | And I fill in "Username" with "terry_fox" 19 | And I press "Create Publisher" 20 | Then I should see "Publisher was successfully created" 21 | And I should see "Terry" 22 | 23 | When I follow "Edit Publisher" 24 | And I fill in "First name" with "Joe" 25 | And I press "Update Publisher" 26 | Then I should see "Publisher was successfully updated" 27 | And I should see "Joe" 28 | 29 | When I follow "Delete Publisher" 30 | Then I should see "Publisher was successfully destroyed" 31 | 32 | Scenario: Create, update and delete a parent STI resource 33 | Given I am on the index page for users 34 | When I follow "New User" 35 | And I fill in "First name" with "Terry" 36 | And I fill in "Last name" with "Fox" 37 | And I fill in "Username" with "terry_fox" 38 | And I press "Create User" 39 | Then I should see "User was successfully created" 40 | And I should see "Terry" 41 | 42 | When I follow "Edit User" 43 | And I fill in "First name" with "Joe" 44 | And I press "Update User" 45 | Then I should see "User was successfully updated" 46 | And I should see "Joe" 47 | 48 | When I follow "Delete User" 49 | Then I should see "User was successfully destroyed" 50 | -------------------------------------------------------------------------------- /features/index/index_as_blog.feature: -------------------------------------------------------------------------------- 1 | Feature: Index as Blog 2 | 3 | Viewing resources as a blog on the index page 4 | 5 | Scenario: Viewing the blog with a resource 6 | Given a post with the title "Hello World" exists 7 | And an index configuration of: 8 | """ 9 | ActiveAdmin.register Post do 10 | index :as => :blog 11 | end 12 | """ 13 | And I am logged in 14 | When I am on the index page for posts 15 | Then I should see "Hello World" within "h3" 16 | And I should see a link to "Hello World" 17 | 18 | Scenario: Viewing the blog with a resource as a simple configuration 19 | Given a post with the title "Hello World" and body "My great post body" exists 20 | And an index configuration of: 21 | """ 22 | ActiveAdmin.register Post do 23 | index :as => :blog do 24 | title :title 25 | body :body 26 | end 27 | end 28 | """ 29 | Then I should see "Hello World" within "h3" 30 | And I should see a link to "Hello World" 31 | And I should see "My great post body" within ".post" 32 | 33 | Scenario: Viewing the blog with a resource as a block configuration 34 | Given a post with the title "Hello World" and body "My great post body" exists 35 | And an index configuration of: 36 | """ 37 | ActiveAdmin.register Post do 38 | index :as => :blog do 39 | title do |post| 40 | post.title + " From Block" 41 | end 42 | body do |post| 43 | post.body + " From Block" 44 | end 45 | end 46 | end 47 | """ 48 | Then I should see "Hello World From Block" within "h3" 49 | And I should see a link to "Hello World From Block" 50 | And I should see "My great post body From Block" within ".post" 51 | -------------------------------------------------------------------------------- /lib/active_admin/views/components/attributes_table.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | 4 | class AttributesTable < ActiveAdmin::Component 5 | builder_method :attributes_table_for 6 | 7 | attr_reader :resource 8 | 9 | def build(record, *attrs) 10 | @record = record 11 | super(:for => @record) 12 | @table = table 13 | rows(*attrs) 14 | end 15 | 16 | def rows(*attrs) 17 | attrs.each {|attr| row(attr) } 18 | end 19 | 20 | def row(attr, &block) 21 | @table << tr do 22 | th do 23 | header_content_for(attr) 24 | end 25 | td do 26 | content_for(block || attr) 27 | end 28 | end 29 | end 30 | 31 | protected 32 | 33 | def default_id_for_prefix 34 | 'attributes_table' 35 | end 36 | 37 | def header_content_for(attr) 38 | @record.class.respond_to?(:human_attribute_name) ? @record.class.human_attribute_name(attr).titleize : attr.to_s.titleize 39 | end 40 | 41 | def empty_value 42 | span I18n.t('active_admin.empty'), :class => "empty" 43 | end 44 | 45 | def content_for(attr_or_proc) 46 | value = case attr_or_proc 47 | when Proc 48 | attr_or_proc.call 49 | else 50 | content_for_attribute(attr_or_proc) 51 | end 52 | value = pretty_format(value) 53 | value == "" || value == nil ? empty_value : value 54 | end 55 | 56 | def content_for_attribute(attr) 57 | if attr.to_s =~ /^([\w]+)_id$/ && @record.respond_to?($1.to_sym) 58 | content_for_attribute($1) 59 | else 60 | @record.send(attr.to_sym) 61 | end 62 | end 63 | end 64 | 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/active_admin/views/components/status_tag.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | # Build a StatusTag 4 | class StatusTag < ActiveAdmin::Component 5 | builder_method :status_tag 6 | 7 | def tag_name 8 | 'span' 9 | end 10 | 11 | def default_class_name 12 | 'status' 13 | end 14 | 15 | # @method status_tag(status, type = nil, options = {}) 16 | # 17 | # @param [String] status the status to display. One of the span classes will be an underscored version of the status. 18 | # @param [Symbol] type type of status. Will become a class of the span. ActiveAdmin provide style for :ok, :warning and :error. 19 | # @param [Hash] options such as :class, :id etc 20 | # 21 | # @return [ActiveAdmin::Views::StatusTag] 22 | # 23 | # Examples: 24 | # status_tag('In Progress') 25 | # # => In Progress 26 | # 27 | # status_tag('active', :ok) 28 | # # => Active 29 | # 30 | # status_tag('active', :ok, :class => 'important', :id => 'status_123') 31 | # # => Active 32 | # 33 | def build(*args) 34 | options = args.extract_options! 35 | status = args[0] 36 | type = args[1] 37 | classes = options.delete(:class) 38 | 39 | status = status.titleize if status 40 | 41 | super(status, options) 42 | 43 | add_class(status_to_class(status)) if status 44 | add_class(type.to_s) if type 45 | add_class(classes) if classes 46 | end 47 | 48 | protected 49 | 50 | def status_to_class(status) 51 | status.titleize.gsub(/\s/, '').underscore 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/active_admin/arbre/html5_elements.rb: -------------------------------------------------------------------------------- 1 | module Arbre 2 | module HTML 3 | 4 | AUTO_BUILD_ELEMENTS = [ :a, :abbr, :address, :area, :article, :aside, :audio, :b, :base, 5 | :bdo, :blockquote, :body, :br, :button, :canvas, :caption, :cite, 6 | :code, :col, :colgroup, :command, :datalist, :dd, :del, :details, 7 | :dfn, :div, :dl, :dt, :em, :embed, :fieldset, :figcaption, :figure, 8 | :footer, :form, :h1, :h2, :h3, :h4, :h5, :h6, :head, :header, :hgroup, 9 | :hr, :html, :i, :iframe, :img, :input, :ins, :keygen, :kbd, :label, 10 | :legend, :li, :link, :map, :mark, :menu, :meta, :meter, :nav, :noscript, 11 | :object, :ol, :optgroup, :option, :output, :pre, :progress, :q, 12 | :s, :samp, :script, :section, :select, :small, :source, :span, 13 | :strong, :style, :sub, :summary, :sup, :table, :tbody, :td, 14 | :textarea, :tfoot, :th, :thead, :time, :title, :tr, :ul, :var, :video ] 15 | 16 | HTML5_ELEMENTS = [ :p ] + AUTO_BUILD_ELEMENTS 17 | 18 | AUTO_BUILD_ELEMENTS.each do |name| 19 | class_eval <<-EOF 20 | class #{name.to_s.capitalize} < Tag 21 | builder_method :#{name} 22 | end 23 | EOF 24 | end 25 | 26 | class P < Tag 27 | builder_method :para 28 | end 29 | 30 | class Table < Tag 31 | def initialize(*) 32 | super 33 | set_table_tag_defaults 34 | end 35 | 36 | protected 37 | 38 | # Set some good defaults for tables 39 | def set_table_tag_defaults 40 | set_attribute :border, 0 41 | set_attribute :cellspacing, 0 42 | set_attribute :cellpadding, 0 43 | end 44 | end 45 | 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /features/index/index_blank_slate.feature: -------------------------------------------------------------------------------- 1 | Feature: Index Blank Slate 2 | 3 | Viewing an index page with no resources yet 4 | 5 | Scenario: Viewing the default table with no resources 6 | Given an index configuration of: 7 | """ 8 | ActiveAdmin.register Post 9 | """ 10 | Then I should not see a sortable table header 11 | And I should see "There are no Posts yet. Create one" 12 | And I should not see pagination 13 | When I follow "Create one" 14 | Then I should be on the new post page 15 | 16 | Scenario: Viewing the default table with no resources and no 'new' action 17 | Given an index configuration of: 18 | """ 19 | ActiveAdmin.register Post do 20 | actions :index, :show 21 | end 22 | """ 23 | And I should see "There are no Posts yet." 24 | And I should not see "Create one" 25 | 26 | Scenario: Viewing a index using a grid with no resources 27 | Given an index configuration of: 28 | """ 29 | ActiveAdmin.register Post do 30 | index :as => :grid do |post| 31 | h2 auto_link(post) 32 | end 33 | end 34 | """ 35 | And I should see "There are no Posts yet. Create one" 36 | 37 | Scenario: Viewing a index using blocks with no resources 38 | Given an index configuration of: 39 | """ 40 | ActiveAdmin.register Post do 41 | index :as => :block do |post| 42 | span(link_to(post.title, admin_post_path(post))) 43 | end 44 | end 45 | """ 46 | And I should see "There are no Posts yet. Create one" 47 | 48 | Scenario: Viewing a blog with no resources 49 | Given an index configuration of: 50 | """ 51 | ActiveAdmin.register Post do 52 | index :as => :blog 53 | end 54 | """ 55 | And I should see "There are no Posts yet. Create one" -------------------------------------------------------------------------------- /app/assets/javascripts/active_admin/base.js: -------------------------------------------------------------------------------- 1 | //= require "active_admin/vendor" 2 | 3 | /* Active Admin JS */ 4 | 5 | $(function(){ 6 | $(".datepicker").datepicker({dateFormat: 'yy-mm-dd'}); 7 | 8 | $(".clear_filters_btn").click(function(){ 9 | window.location.search = ""; 10 | return false; 11 | }); 12 | 13 | // AJAX Comments 14 | $('form#admin_note_new').submit(function() { 15 | 16 | if ($(this).find('#admin_note_body').val() != "") { 17 | $(this).fadeOut('slow', function() { 18 | $('.loading_indicator').fadeIn(); 19 | $.ajax({ 20 | url: $(this).attr('action'), 21 | type: 'POST', 22 | dataType: 'json', 23 | data: $(this).serialize(), 24 | success: function(data, textStatus, xhr) { 25 | $('.loading_indicator').fadeOut('slow', function(){ 26 | 27 | // Hide the empty message 28 | $('.admin_notes_list li.empty').fadeOut().remove(); 29 | 30 | // Add the note 31 | $('.admin_notes_list').append(data['note']); 32 | 33 | // Update the number of notes 34 | $('.admin_notes h3 span.admin_notes_count').html("(" + data['number_of_notes'] + ")"); 35 | 36 | // Reset the form 37 | $('form#new_active_admin_admin_note').find('#active_admin_admin_note_body').val(""); 38 | 39 | // Show the form 40 | $('form#new_active_admin_admin_note').fadeIn('slow'); 41 | }) 42 | }, 43 | error: function(xhr, textStatus, errorThrown) { 44 | //called when there is an error 45 | } 46 | }); 47 | }); 48 | 49 | }; 50 | 51 | return false; 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /spec/support/rails_template.rb: -------------------------------------------------------------------------------- 1 | # Rails template to build the sample app for specs 2 | 3 | # Create a cucumber database and environment 4 | copy_file File.expand_path('../templates/cucumber.rb', __FILE__), "config/environments/cucumber.rb" 5 | gsub_file 'config/database.yml', /^test:.*\n/, "test: &test\n" 6 | gsub_file 'config/database.yml', /\z/, "\ncucumber:\n <<: *test\n database: db/cucumber.sqlite3" 7 | 8 | # Generate some test models 9 | generate :model, "post title:string body:text published_at:datetime author_id:integer category_id:integer" 10 | inject_into_file 'app/models/post.rb', " belongs_to :author, :class_name => 'User'\n belongs_to :category\n accepts_nested_attributes_for :author\n", :after => "class Post < ActiveRecord::Base\n" 11 | generate :model, "user type:string first_name:string last_name:string username:string" 12 | inject_into_file 'app/models/user.rb', " has_many :posts, :foreign_key => 'author_id'\n", :after => "class User < ActiveRecord::Base\n" 13 | generate :model, "publisher --migration=false --parent=User" 14 | generate :model, 'category name:string description:text' 15 | inject_into_file 'app/models/category.rb', " has_many :posts\n", :after => "class Category < ActiveRecord::Base\n" 16 | 17 | # Add our local Active Admin to the load path 18 | inject_into_file "config/environment.rb", "\n$LOAD_PATH.unshift('#{File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib'))}')\nrequire \"active_admin\"\n", :after => "require File.expand_path('../application', __FILE__)" 19 | 20 | run "rm Gemfile" 21 | run "rm -r test" 22 | run "rm -r spec" 23 | 24 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 25 | generate :'active_admin:install' 26 | 27 | # Setup a root path for devise 28 | route "root :to => 'admin/dashboard#index'" 29 | 30 | rake "db:migrate" 31 | rake "db:test:prepare" 32 | run "/usr/bin/env RAILS_ENV=cucumber rake db:migrate" 33 | -------------------------------------------------------------------------------- /lib/active_admin/views/pages/dashboard.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | module Pages 4 | class Dashboard < Base 5 | 6 | def main_content 7 | if assigns[:dashboard_sections] && assigns[:dashboard_sections].any? 8 | render_sections(assigns[:dashboard_sections]) 9 | else 10 | default_welcome_section 11 | end 12 | end 13 | 14 | protected 15 | 16 | # Dashboards don't have a sidebar 17 | def build_sidebar; end 18 | 19 | def title 20 | "Dashboard" 21 | end 22 | 23 | def render_sections(sections) 24 | table :class => "dashboard" do 25 | sections.in_groups_of(3, false).each do |row| 26 | tr do 27 | row.each do |section| 28 | td do 29 | render_section(section) 30 | end 31 | end 32 | end 33 | end 34 | end 35 | end 36 | 37 | # Renders each section using their renderer 38 | def render_section(section) 39 | insert_tag section_renderer(section), section 40 | end 41 | 42 | def section_renderer(section) 43 | if section.options[:as] 44 | view_factory["dashboard_section_as_#{section.options[:as]}"] 45 | else 46 | view_factory.dashboard_section 47 | end 48 | end 49 | 50 | def default_welcome_section 51 | div :class => "blank_slate_container", :id => "dashboard_default_message" do 52 | span :class => "blank_slate" do 53 | span I18n.t('active_admin.dashboard_welcome.welcome') 54 | small I18n.t('active_admin.dashboard_welcome.call_to_action') 55 | end 56 | end 57 | end 58 | 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/unit/arbre/html/tag_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Arbre::HTML::Tag do 4 | include Arbre::HTML 5 | let(:assigns){ {} } 6 | 7 | let(:tag){ Arbre::HTML::Tag.new } 8 | 9 | describe "building a new tag" do 10 | before { tag.build "Hello World", :id => "my_id" } 11 | 12 | it "should set the contents to a string" do 13 | tag.content.should == "Hello World" 14 | end 15 | 16 | it "should set the hash of options to the attributes" do 17 | tag.attributes.should == { :id => "my_id" } 18 | end 19 | end 20 | 21 | describe "creating a tag 'for' an object" do 22 | let(:model_name){ mock(:singular => "resource_class")} 23 | let(:resource_class){ mock(:model_name => model_name) } 24 | let(:resource){ mock(:class => resource_class, :to_key => ['5'])} 25 | 26 | before do 27 | tag.build :for => resource 28 | end 29 | it "should set the id to the type and id" do 30 | tag.id.should == "resource_class_5" 31 | end 32 | it "should add a class name" do 33 | tag.class_list.should include("resource_class") 34 | end 35 | end 36 | 37 | describe "css class names" do 38 | it "should add a class" do 39 | tag.add_class "hello_world" 40 | tag.class_names.should == "hello_world" 41 | end 42 | 43 | it "should remove_class" do 44 | tag.add_class "hello_world" 45 | tag.class_names.should == "hello_world" 46 | tag.remove_class "hello_world" 47 | tag.class_names.should == "" 48 | end 49 | 50 | it "should not add a class if it already exists" do 51 | tag.add_class "hello_world" 52 | tag.add_class "hello_world" 53 | tag.class_names.should == "hello_world" 54 | end 55 | 56 | it "should seperate classes with space" do 57 | tag.add_class "hello world" 58 | tag.class_list.size.should == 2 59 | end 60 | end 61 | 62 | 63 | end 64 | -------------------------------------------------------------------------------- /spec/unit/dashboards_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin::Dashboards do 4 | 5 | after(:each) do 6 | ActiveAdmin::Dashboards.clear_all_sections! 7 | end 8 | 9 | describe "adding sections" do 10 | before do 11 | ActiveAdmin::Dashboards.clear_all_sections! 12 | ActiveAdmin::Dashboards.add_section('Recent Posts') 13 | end 14 | it "should add a new section namespaced" do 15 | ActiveAdmin::Dashboards.sections[:admin].first.should be_an_instance_of(ActiveAdmin::Dashboards::Section) 16 | end 17 | end 18 | 19 | describe "adding sections using the build syntax" do 20 | before do 21 | ActiveAdmin::Dashboards.clear_all_sections! 22 | ActiveAdmin::Dashboards.build do 23 | section "Recent Posts" do 24 | end 25 | end 26 | end 27 | 28 | it "should add a new section" do 29 | ActiveAdmin::Dashboards.sections[:admin].first.should be_an_instance_of(ActiveAdmin::Dashboards::Section) 30 | end 31 | end 32 | 33 | describe "clearing all sections" do 34 | before do 35 | ActiveAdmin::Dashboards.add_section('Recent Posts') 36 | end 37 | it "should clear all sections" do 38 | ActiveAdmin::Dashboards.clear_all_sections! 39 | ActiveAdmin::Dashboards.sections.keys.should be_empty 40 | end 41 | end 42 | 43 | describe "finding namespaced sections" do 44 | context "when the namespace exists" do 45 | before do 46 | ActiveAdmin::Dashboards.add_section('Recent Posts') 47 | end 48 | it "should return an array of sections" do 49 | ActiveAdmin::Dashboards.sections_for_namespace(:admin).should_not be_empty 50 | end 51 | end 52 | 53 | context "when the namespace does not exists" do 54 | it "should return an empty array" do 55 | ActiveAdmin::Dashboards.sections_for_namespace(:not_a_namespace).should be_empty 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/active_admin/views/tabs_renderer.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | 4 | # Renders out a horizontal list of tabs. 5 | class TabsRenderer < Renderer 6 | 7 | # Pass in an ActiveAdmin::Menu and it will display the first level 8 | # of navigation as a horizontal list of tabs 9 | def to_html(menu, options = {}) 10 | @options = default_options.merge(options) 11 | render_menu(menu) 12 | end 13 | 14 | protected 15 | 16 | def render_menu(menu) 17 | content_tag :ul, :id => @options[:id] do 18 | menu.items.collect do |item| 19 | render_item(item) 20 | end.join.html_safe 21 | end 22 | end 23 | 24 | def render_item(item) 25 | if !call_method_or_proc_on(self, item.display_if_block) 26 | return 27 | elsif (!item.url or item.url == '#') and item.children.any? and (item.children.detect {|child| call_method_or_proc_on(self, child.display_if_block)}).nil? 28 | return 29 | end 30 | 31 | content_tag :li, :id => item.dom_id, :class => [("current" if current?(item)), ("has_nested" unless item.children.blank?)].compact.join(" ") do 32 | unless item.children.blank? 33 | link_to(item.name, item.url || "#") + render_nested_menu(item) 34 | else 35 | link_to item.name, item.url 36 | end 37 | end 38 | end 39 | 40 | def render_nested_menu(item) 41 | content_tag :ul do 42 | item.children.collect {|child| render_item(child)}.join.html_safe 43 | end 44 | end 45 | 46 | # Returns true if the menu item name is @current_tab 47 | def current?(menu_item) 48 | @current_tab.split("/").include?(menu_item.name) unless @current_tab.blank? 49 | end 50 | 51 | def default_options 52 | { :id => "tabs" } 53 | end 54 | 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/unit/resource/naming_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module ActiveAdmin 4 | describe Resource, "Naming" do 5 | 6 | before { load_defaults! } 7 | 8 | let(:application){ ActiveAdmin::Application.new } 9 | let(:namespace){ Namespace.new(application, :admin) } 10 | 11 | def config(options = {}) 12 | @config ||= Resource.new(namespace, Category, options) 13 | end 14 | 15 | 16 | describe "underscored resource name" do 17 | context "when class" do 18 | it "should be the underscored singular resource name" do 19 | config.underscored_resource_name.should == "category" 20 | end 21 | end 22 | context "when a class in a module" do 23 | it "should underscore the module and the class" do 24 | module ::Mock; class Resource; end; end 25 | Resource.new(namespace, Mock::Resource).underscored_resource_name.should == "mock_resource" 26 | end 27 | end 28 | context "when you pass the 'as' option" do 29 | it "should underscore the passed through string and singulralize" do 30 | config(:as => "Blog Categories").underscored_resource_name.should == "blog_category" 31 | end 32 | end 33 | end 34 | 35 | describe "camelized resource name" do 36 | it "should return a camelized version of the underscored resource name" do 37 | config(:as => "Blog Categories").camelized_resource_name.should == "BlogCategory" 38 | end 39 | end 40 | 41 | describe "resource name" do 42 | it "should return a pretty name" do 43 | config.resource_name.should == "Category" 44 | end 45 | it "should return the plural version" do 46 | config.plural_resource_name.should == "Categories" 47 | end 48 | context "when the :as option is given" do 49 | it "should return the custom name" do 50 | config(:as => "My Category").resource_name.should == "My Category" 51 | end 52 | end 53 | end 54 | 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /features/specifying_actions.feature: -------------------------------------------------------------------------------- 1 | Feature: Specifying Actions 2 | 3 | Specifying which actions to allow on my resource 4 | 5 | Scenario: Only creating the index action 6 | Given a configuration of: 7 | """ 8 | ActiveAdmin.register Post do 9 | actions :index 10 | end 11 | """ 12 | And I am logged in 13 | And a post with the title "Hello World" exists 14 | When I am on the index page for posts 15 | Then an "AbstractController::ActionNotFound" exception should be raised when I follow "View" 16 | 17 | Scenario: Specify a custom collection action with template 18 | Given a configuration of: 19 | """ 20 | ActiveAdmin.register Post do 21 | action_item(:only => :index) do 22 | link_to('Import Posts', import_admin_posts_path) 23 | end 24 | 25 | collection_action :import 26 | end 27 | """ 28 | Given "app/views/admin/posts/import.html.erb" contains: 29 | """ 30 |

We are currently working on this feature...

31 | """ 32 | And I am logged in 33 | When I am on the index page for posts 34 | And I follow "Import" 35 | Then I should see "We are currently working on this feature" 36 | 37 | Scenario: Specify a custom member action with template 38 | Given a configuration of: 39 | """ 40 | ActiveAdmin.register Post do 41 | action_item(:only => :show) do 42 | link_to('Review', review_admin_post_path) 43 | end 44 | 45 | member_action :review do 46 | @post = Post.find(params[:id]) 47 | end 48 | end 49 | """ 50 | Given "app/views/admin/posts/review.html.erb" contains: 51 | """ 52 |

Review: <%= @post.title %>

53 | """ 54 | And I am logged in 55 | And a post with the title "Hello World" exists 56 | When I am on the index page for posts 57 | And I follow "View" 58 | And I follow "Review" 59 | Then I should see "Review: Hello World" 60 | -------------------------------------------------------------------------------- /features/index/index_scopes.feature: -------------------------------------------------------------------------------- 1 | Feature: Index Scoping 2 | 3 | Viewing resources and scoping them 4 | 5 | Scenario: Viewing resources with one scope and no default 6 | Given 10 posts exist 7 | And an index configuration of: 8 | """ 9 | ActiveAdmin.register Post do 10 | scope :all 11 | end 12 | """ 13 | Then I should see the scope "All" not selected 14 | And I should see the scope "All" with the count 10 15 | And I should see 10 posts in the table 16 | 17 | Scenario: Viewing resources with one scope as the default 18 | Given 10 posts exist 19 | And an index configuration of: 20 | """ 21 | ActiveAdmin.register Post do 22 | scope :all, :default => true 23 | end 24 | """ 25 | Then I should see the scope "All" selected 26 | And I should see the scope "All" with the count 10 27 | And I should see 10 posts in the table 28 | 29 | Scenario: Viewing resources with mulitple scopes as blocks 30 | Given 10 posts exist 31 | And an index configuration of: 32 | """ 33 | ActiveAdmin.register Post do 34 | scope 'Today', :default => true do |posts| 35 | posts.where(["created_at > ? AND created_at < ?", ::Time.zone.now.beginning_of_day, ::Time.zone.now.end_of_day]) 36 | end 37 | scope 'Tomorrow' do |posts| 38 | posts.where(["created_at > ? AND created_at < ?", ::Time.zone.now.beginning_of_day + 1.day, ::Time.zone.now.end_of_day + 1.day]) 39 | end 40 | end 41 | """ 42 | Then I should see the scope "Today" selected 43 | And I should see the scope "Tomorrow" not selected 44 | And I should see the scope "Today" with the count 10 45 | And I should see the scope "Tomorrow" with the count 0 46 | And I should see 10 posts in the table 47 | And I should see a link to "Tomorrow" 48 | 49 | When I follow "Tomorrow" 50 | Then I should see the scope "Tomorrow" selected 51 | And I should see the scope "Today" not selected 52 | And I should see a link to "Today" 53 | -------------------------------------------------------------------------------- /lib/active_admin/views/components/scopes.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | 4 | # Renders a collection of ActiveAdmin::Scope objects as a 5 | # simple list with a seperator 6 | class Scopes < ActiveAdmin::Component 7 | builder_method :scopes_renderer 8 | 9 | def build(scopes) 10 | scopes.each do |scope| 11 | build_scope(scope) 12 | end 13 | end 14 | 15 | protected 16 | 17 | def build_scope(scope) 18 | span :class => classes_for_scope(scope) do 19 | begin 20 | scope_name = I18n.t!("active_admin.scopes.#{scope.scope_method}") 21 | rescue I18n::MissingTranslationData 22 | scope_name = scope.name 23 | end 24 | 25 | if current_scope?(scope) 26 | em(scope_name) 27 | else 28 | a(scope_name, :href => url_for(params.merge(:scope => scope.id, :page => 1))) 29 | end 30 | text_node(" ") 31 | scope_count(scope) 32 | text_node(" ") 33 | end 34 | end 35 | 36 | def classes_for_scope(scope) 37 | classes = ["scope", scope.id] 38 | classes << "selected" if current_scope?(scope) 39 | classes.join(" ") 40 | end 41 | 42 | def current_scope?(scope) 43 | if params[:scope] 44 | params[:scope] == scope.id 45 | else 46 | active_admin_config.default_scope == scope 47 | end 48 | end 49 | 50 | def scope_count(scope) 51 | span :class => 'count' do 52 | "(" + get_scope_count(scope).to_s + ")" 53 | end 54 | end 55 | 56 | def get_scope_count(scope) 57 | if scope.scope_method 58 | scoping_class.send(scope.scope_method).count 59 | else 60 | instance_exec(scoping_class, &scope.scope_block).count 61 | end 62 | end 63 | 64 | def scoping_class 65 | assigns["before_scope_collection"] || active_admin_config.resource 66 | end 67 | 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/generators/active_admin/devise/devise_generator.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Generators 3 | class DeviseGenerator < Rails::Generators::NamedBase 4 | desc "Creates an admin user and uses Devise for authentication" 5 | 6 | argument :name, :type => :string, :default => "AdminUser" 7 | 8 | class_option :registerable, :type => :boolean, :default => false, 9 | :desc => "Should the generated resource be registerable?" 10 | 11 | def install_devise 12 | require 'devise' 13 | if File.exists?(File.join(destination_root, "config", "initializers", "devise.rb")) 14 | log :generate, "No need to install devise, already done." 15 | else 16 | log :generate, "devise:install" 17 | invoke "devise:install" 18 | end 19 | end 20 | 21 | def create_admin_user 22 | invoke "devise", [name] 23 | end 24 | 25 | def remove_registerable_from_model 26 | unless options[:registerable] 27 | model_file = File.join(destination_root, "app", "models", "#{file_path}.rb") 28 | gsub_file model_file, /\:registerable([.]*,)?/, "" 29 | end 30 | end 31 | 32 | def set_namespace_for_path 33 | routes_file = File.join(destination_root, "config", "routes.rb") 34 | gsub_file routes_file, /devise_for :#{table_name}/, "devise_for :#{table_name}, ActiveAdmin::Devise.config" 35 | end 36 | 37 | def add_default_user_to_migration 38 | # Don't assume that we have a migration! 39 | devise_migrations = Dir["db/migrate/*_devise_create_#{table_name}.rb"] 40 | if devise_migrations.size > 0 41 | inject_into_file Dir["db/migrate/*_devise_create_#{table_name}.rb"].first, 42 | "# Create a default user\n #{class_name}.create!(:email => 'admin@example.com', :password => 'password', :password_confirmation => 'password')\n\n ", 43 | :before => "add_index :#{table_name}, :email" 44 | end 45 | end 46 | 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/unit/comments_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Comments" do 4 | let(:application){ ActiveAdmin::Application.new } 5 | 6 | describe "Configuration" do 7 | it "should have an array of namespaces which allow comments" do 8 | application.allow_comments_in.should be_an_instance_of(Array) 9 | end 10 | 11 | it "should allow comments in the default namespace by default" do 12 | application.allow_comments_in.should include(application.default_namespace) 13 | end 14 | end 15 | 16 | describe ActiveAdmin::Comment do 17 | describe "Associations and Validations" do 18 | it { should belong_to :resource } 19 | it { should belong_to :author } 20 | 21 | it { should validate_presence_of :resource_id } 22 | it { should validate_presence_of :resource_type } 23 | it { should validate_presence_of :body } 24 | it { should validate_presence_of :namespace } 25 | end 26 | end 27 | 28 | describe ActiveAdmin::Comments::NamespaceHelper do 29 | describe "#comments?" do 30 | it "should have comments if the namespace is in the settings" do 31 | ns = ActiveAdmin::Namespace.new(application, :admin) 32 | ns.comments?.should be_true 33 | end 34 | it "should not have comments if the namespace is not in the settings" do 35 | ns = ActiveAdmin::Namespace.new(application, :not_in_comments) 36 | ns.comments?.should be_false 37 | end 38 | end 39 | end 40 | 41 | describe ActiveAdmin::Comments::ResourceHelper do 42 | it "should add an attr_accessor :comments to ActiveAdmin::Resource" do 43 | ns = ActiveAdmin::Namespace.new(application, :admin) 44 | resource = ActiveAdmin::Resource.new(ns, Post) 45 | resource.comments.should be_nil 46 | resource.comments = true 47 | resource.comments.should be_true 48 | end 49 | 50 | it "should not have comment if set to false by in allow_comments_in" do 51 | ns = ActiveAdmin::Namespace.new(application, application.default_namespace) 52 | resource = ActiveAdmin::Resource.new(ns, Post) 53 | resource.comments = false 54 | resource.comments?.should be_false 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /CHANGELOG.rdoc: -------------------------------------------------------------------------------- 1 | == 0.2.2 (2011-05-26) 2 | 3 | 68 Commits by 13 Contributors 4 | 5 | === Features & Enhancements 6 | 7 | * Arbre includes self closing tags (#100) 8 | * Controller class & action added to body as CSS classes (#99) 9 | * HAML is not required by default (#92) 10 | * Devise login now respects Devise.authentication_keys (#69) 11 | * Active Admin no longer uses ActiveRecord::Base#search (#28) 12 | * Resource's can now override the label in the menu (#48) 13 | * Subdirectories are now loaded in the Active Admin load path 14 | 15 | === Bug Fixes 16 | 17 | * Sort order now includes table name (#38) 18 | * Fixed table_for 'odd', 'even' row classes (#96) 19 | * Fixed Devise installation if AdminUser already exists (#95) 20 | * Fixed issues when ActiveAdmin.default_namespaces is false (#32) 21 | * Added styles for missing HTML 5 inputs (#31) 22 | * Fixed issue if adding empty Active Admin Comment (#21) 23 | * Fixed layout issues in FF 4 (#22) 24 | * Use Sass::Plugin.options[:css_location] instead of Rails.root (#55) 25 | 26 | === Test Suite 27 | 28 | * Update RSpec to latest & fix specs (Thanks Ben Marini & Jeremt Ruppel!) (#100) 29 | * Added tests for STI models (#52) 30 | 31 | === Contributors 32 | 33 | * Ben Marini 34 | * Bookis Smuin 35 | * Caley Woods 36 | * Doug Puchalski 37 | * Federico Romero 38 | * Greg Bell 39 | * Ian MacLeod 40 | * Jeremy Ruppel 41 | * Jordan Sitkin 42 | * Juha Suuraho 43 | * Mathieu Martin 44 | * Paul Annesley 45 | * Philippe Creux 46 | 47 | == 0.2.1 (2011-05-12) 48 | 49 | === Bug Fixes 50 | * Fixed issue with dashboard rendering a sidebar 51 | 52 | == 0.2.0 (2011-05-12) 53 | 54 | 0.2.0 is essentially an entire re-write of Active Admin. Here are some 55 | of the highlights. 250 commits. Enough said. 56 | 57 | === Features & Enhancements 58 | 59 | * Full visual redesign 60 | * Integrated Devise for authentication 61 | * Brand new view and component layer called Arbre (Project coming soon) 62 | * Added ActiveAdmin::Comments 63 | 64 | === Bug Fixes 65 | 66 | * Too many to list! Been in production for close to a year 67 | 68 | == 0.1.1 (2010-09-15) 69 | 70 | === Bug Fixes 71 | 72 | * Fixed issues running on Ruby 1.9.2 73 | 74 | == 0.1.0 75 | 76 | * Initial release 77 | -------------------------------------------------------------------------------- /spec/unit/components/columns_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin::Views::Columns do 4 | include Arbre::HTML 5 | let(:assigns){ {} } 6 | 7 | describe "Rendering one column" do 8 | let(:cols) do 9 | columns do 10 | column { span "Hello World" } 11 | end 12 | end 13 | 14 | it "should have the class .columns" do 15 | cols.class_list.should include("columns") 16 | end 17 | 18 | it "should have one column" do 19 | cols.children.size.should == 1 20 | cols.children.first.class_list.should include("column") 21 | end 22 | 23 | it "should have one column with the width 100%" do 24 | cols.children.first.attr(:style).should include("width: 100%") 25 | end 26 | end 27 | 28 | describe "Rendering two columns" do 29 | let(:cols) do 30 | columns do 31 | column { span "Hello World" } 32 | column { span "Hello World" } 33 | end 34 | end 35 | 36 | it "should have two columns" do 37 | cols.children.size.should == 2 38 | end 39 | 40 | it "should have a first column with width 49% and margin 2%" do 41 | cols.children.first.attr(:style).should == "width: 49%; margin-right: 2%;" 42 | end 43 | 44 | it "should have a second column with width 49% and no right margin" do 45 | cols.children.last.attr(:style).should == "width: 49%;" 46 | end 47 | end 48 | 49 | describe "Rendering four columns" do 50 | let(:cols) do 51 | columns do 52 | column { span "Hello World" } 53 | column { span "Hello World" } 54 | column { span "Hello World" } 55 | column { span "Hello World" } 56 | end 57 | end 58 | 59 | it "should have four columns" do 60 | cols.children.size.should == 4 61 | end 62 | 63 | 64 | (0..2).to_a.each do |index| 65 | it "should have column #{index + 1} with width 49% and margin 2%" do 66 | cols.children[index].attr(:style).should == "width: 23.5%; margin-right: 2%;" 67 | end 68 | end 69 | 70 | it "should have column 4 with width 49% and no margin" do 71 | cols.children[3].attr(:style).should == "width: 23.5%;" 72 | end 73 | end 74 | 75 | end 76 | -------------------------------------------------------------------------------- /lib/active_admin/menu_item.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class MenuItem 3 | 4 | # Use this to get to the routes 5 | include Rails.application.routes.url_helpers 6 | 7 | attr_accessor :name, :url, :priority, :parent, :display_if_block 8 | 9 | def initialize(name, url, priority = 10, options = {}) 10 | @name, @url, @priority = name, url, priority 11 | @children = [] 12 | @cached_url = {} # Stores the cached url in a hash to allow us to change it and still cache it 13 | 14 | @display_if_block = options.delete(:if) 15 | 16 | yield(self) if block_given? # Builder style syntax 17 | end 18 | 19 | def add(name, url, priority=10, options = {}, &block) 20 | item = MenuItem.new(name, url, priority, options, &block) 21 | item.parent = self 22 | @children << item 23 | end 24 | 25 | def children 26 | @children.sort 27 | end 28 | 29 | def parent? 30 | !parent.nil? 31 | end 32 | 33 | def dom_id 34 | name.downcase.gsub( " ", '_' ).gsub( /[^a-z0-9_]/, '' ) 35 | end 36 | 37 | def url 38 | case @url 39 | when Symbol 40 | generated = send(@url) # Call the named route 41 | else 42 | generated = @url 43 | end 44 | @cached_url[@url] ||= generated 45 | end 46 | 47 | # Returns an array of the ancestory of this menu item 48 | # The first item is the immediate parent fo the item 49 | def ancestors 50 | return [] unless parent? 51 | [parent, parent.ancestors].flatten 52 | end 53 | 54 | # Returns the child item with the name passed in 55 | # @blog_menu["Create New"] => <#MenuItem @name="Create New" > 56 | def [](name) 57 | @children.find{ |i| i.name == name } 58 | end 59 | 60 | def <=>(other) 61 | result = priority <=> other.priority 62 | result = name <=> other.name if result == 0 63 | result 64 | end 65 | 66 | # Returns the display if block. If the block was not explicitly defined 67 | # a default block always returning true will be returned. 68 | def display_if_block 69 | @display_if_block || lambda { |_| true } 70 | end 71 | 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /spec/unit/csv_builder_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveAdmin::CSVBuilder do 4 | 5 | describe '.default_for_resource using Post' do 6 | let(:csv_builder) { ActiveAdmin::CSVBuilder.default_for_resource(Post) } 7 | 8 | it "should return a default csv_builder for Post" do 9 | csv_builder.should be_a(ActiveAdmin::CSVBuilder) 10 | end 11 | 12 | specify "the first column should be Id" do 13 | csv_builder.columns.first.name.should == 'Id' 14 | csv_builder.columns.first.data.should == :id 15 | end 16 | 17 | specify "the following columns should be content_column" do 18 | csv_builder.columns[1..-1].each_with_index do |column, index| 19 | column.name.should == Post.content_columns[index].name.titleize 20 | column.data.should == Post.content_columns[index].name.to_sym 21 | end 22 | end 23 | end 24 | 25 | context 'when empty' do 26 | let(:builder){ ActiveAdmin::CSVBuilder.new } 27 | 28 | it "should have no columns" do 29 | builder.columns.should == [] 30 | end 31 | end 32 | 33 | context "with a symbol column (:title)" do 34 | let(:builder) do 35 | ActiveAdmin::CSVBuilder.new do 36 | column :title 37 | end 38 | end 39 | 40 | it "should have one colum" do 41 | builder.columns.size.should == 1 42 | end 43 | 44 | describe "the column" do 45 | let(:column){ builder.columns.first } 46 | 47 | it "should have a name of 'Title'" do 48 | column.name.should == "Title" 49 | end 50 | 51 | it "should have the data :title" do 52 | column.data.should == :title 53 | end 54 | end 55 | end 56 | 57 | context "with a block and title" do 58 | let(:builder) do 59 | ActiveAdmin::CSVBuilder.new do 60 | column "My title" do 61 | # nothing 62 | end 63 | end 64 | end 65 | 66 | it "should have one colum" do 67 | builder.columns.size.should == 1 68 | end 69 | 70 | describe "the column" do 71 | let(:column){ builder.columns.first } 72 | 73 | it "should have a name of 'My title'" do 74 | column.name.should == "My title" 75 | end 76 | 77 | it "should have the data :title" do 78 | column.data.should be_an_instance_of(Proc) 79 | end 80 | end 81 | end 82 | 83 | end 84 | -------------------------------------------------------------------------------- /lib/active_admin/views/index_as_blog.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module Views 3 | class IndexAsBlog < ActiveAdmin::Component 4 | 5 | def build(page_config, collection) 6 | @page_config = page_config 7 | @collection = collection 8 | 9 | # Call the block passed in. This will set the 10 | # title and body methods 11 | instance_eval &page_config.block if page_config.block 12 | 13 | build_posts 14 | end 15 | 16 | # Setter method for the configuration of the title 17 | # 18 | # index :as => :blog do 19 | # title :a_method_to_call #=> Calls #a_method_to_call on the resource 20 | # 21 | # # OR 22 | # 23 | # title do |post| 24 | # post.a_method_to_call 25 | # end 26 | # end 27 | def title(method = nil, &block) 28 | if block_given? || method 29 | @title = block_given? ? block : method 30 | end 31 | @title 32 | end 33 | 34 | # Setter method for the configuration of the body 35 | # 36 | # index :as => :blog do 37 | # title :my_title 38 | # 39 | # body :a_method_to_call #=> Calls #a_method_to_call on the resource 40 | # 41 | # # OR 42 | # 43 | # title do |post| 44 | # post.a_method_to_call 45 | # end 46 | # end 47 | def body(method = nil, &block) 48 | if block_given? || method 49 | @body = block_given? ? block : method 50 | end 51 | @body 52 | end 53 | 54 | private 55 | 56 | def build_posts 57 | @collection.each do |post| 58 | build_post(post) 59 | end 60 | end 61 | 62 | def build_post(post) 63 | div :for => post do 64 | build_title(post) 65 | build_body(post) 66 | end 67 | end 68 | 69 | def build_title(post) 70 | if @title 71 | h3 do 72 | link_to(call_method_or_proc_on(post, @title), resource_path(post)) 73 | end 74 | else 75 | h3 do 76 | auto_link(post) 77 | end 78 | end 79 | end 80 | 81 | def build_body(post) 82 | if @body 83 | div(call_method_or_proc_on(post, @body), :class => 'content') 84 | end 85 | end 86 | 87 | end # Posts 88 | end 89 | end 90 | --------------------------------------------------------------------------------