Skip to content

Commit

Permalink
introducing resource attributes module to build attributes once per r…
Browse files Browse the repository at this point in the history
…esource
  • Loading branch information
Fivell committed Mar 11, 2017
1 parent 6f72188 commit 1ef08af
Show file tree
Hide file tree
Showing 22 changed files with 154 additions and 61 deletions.
6 changes: 3 additions & 3 deletions features/index/format_as_csv.feature
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ Feature: Format as CSV
When I am on the index page for posts
And I follow "CSV"
And I should download a CSV file for "posts" containing:
| Id | Title | Body | Published date | Position | Starred | Created at | Updated at |
| \d+ | Hello World | | | | | (.*) | (.*) |
| Id | Title | Body | Published date | Position | Starred | Foo |Created at | Updated at |
| \d+ | Hello World | | | | | |(.*) | (.*) |

Scenario: Default with alias
Given a configuration of:
Expand All @@ -24,7 +24,7 @@ Feature: Format as CSV
When I am on the index page for my_articles
And I follow "CSV"
And I should download a CSV file for "my-articles" containing:
| Id | Title | Body | Published date | Position | Starred | Created at | Updated at |
| Id | Title | Body | Published date | Position | Starred | Foo | Created at | Updated at |

Scenario: With CSV format customization
Given a configuration of:
Expand Down
12 changes: 6 additions & 6 deletions features/index/index_as_table.feature
Original file line number Diff line number Diff line change
Expand Up @@ -250,14 +250,14 @@ Feature: Index as Table
"""
When I am on the index page for posts
Then I should see the "index_table_posts" table:
| [ ] | Id | Title | Body | Published Date | Position | Starred | Created At | Updated At | |
| [ ] | 2 | Bye bye world | Move your... | | | No | /.*/ | /.*/ | ViewEditDelete |
| [ ] | 1 | Hello World | From the body | | | No | /.*/ | /.*/ | ViewEditDelete |
| [ ] | Id | Title | Body | Published Date | Author | Position | Category | Starred | Foo | Created At | Updated At | |
| [ ] | 2 | Bye bye world | Move your... | | | | | No | | /.*/ | /.*/ | ViewEditDelete |
| [ ] | 1 | Hello World | From the body | | | | | No | | /.*/ | /.*/ | ViewEditDelete |
When I follow "Id"
Then I should see the "index_table_posts" table:
| [ ] | Id | Title | Body | Published Date | Position | Starred | Created At | Updated At | |
| [ ] | 1 | Hello World | From the body | | | No | /.*/ | /.*/ | ViewEditDelete |
| [ ] | 2 | Bye bye world | Move your... | | | No | /.*/ | /.*/ | ViewEditDelete |
| [ ] | Id | Title | Body | Published Date | Author | Position | Category | Starred | Foo | Created At | Updated At | |
| [ ] | 1 | Hello World | From the body | | | | | No | | /.*/ | /.*/ | ViewEditDelete |
| [ ] | 2 | Bye bye world | Move your... | | | | | No | | /.*/ | /.*/ | ViewEditDelete |

Scenario: Sorting by a virtual column
Given a post with the title "Hello World" exists
Expand Down
2 changes: 1 addition & 1 deletion features/new_page.feature
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Feature: New Page
Then I should see "Post was successfully created."
And I should see the attribute "Title" with "Hello World"
And I should see the attribute "Body" with "This is the body"
#And I should see the attribute "Category" with "Music"
And I should see the attribute "Category" with "Music"
And I should see the attribute "Author" with "John Doe"

Scenario: Generating a custom form
Expand Down
2 changes: 1 addition & 1 deletion features/step_definitions/attribute_steps.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Then /^I should see the attribute "([^"]*)" with "([^"]*)"$/ do |title, value|
elems = all ".attributes_table th:contains('#{title}') ~ td:contains('#{value}')"
expect(elems.first).to_not eq(nil), 'attribute missing'
expect(elems.first).to_not eq(nil), "attribute missing"
end

Then /^I should see the attribute "([^"]*)" with a nicely formatted datetime$/ do |title|
Expand Down
4 changes: 2 additions & 2 deletions lib/active_admin/csv_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class CSVBuilder
def self.default_for_resource(resource)
new resource: resource do
column :id
resource.content_columns.each { |c| column c.name.to_sym }
resource.content_columns.each { |c| column c }
end
end

Expand Down Expand Up @@ -105,7 +105,7 @@ def initialize(name, resource = nil, options = {}, block = nil)

def humanize_name(name, resource, humanize_name_option)
if humanize_name_option
name.is_a?(Symbol) && resource.present? ? resource.human_attribute_name(name) : name.to_s.humanize
name.is_a?(Symbol) && resource ? resource.resource_class.human_attribute_name(name) : name.to_s.humanize
else
name.to_s
end
Expand Down
13 changes: 2 additions & 11 deletions lib/active_admin/filters/resource_extension.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def filter_lookup
def default_filters
result = []
result.concat default_association_filters if namespace.include_default_association_filters
result.concat default_content_filters
result.concat content_columns
result.concat custom_ransack_filters
result
end
Expand All @@ -121,7 +121,7 @@ def custom_ransack_filters
[]
end
end

# Returns a default set of filters for the associations
def default_association_filters
if resource_class.respond_to?(:reflect_on_all_associations)
Expand All @@ -137,15 +137,6 @@ def default_association_filters
end
end

# Returns a default set of filters for the content columns
def default_content_filters
if resource_class.respond_to? :content_columns
resource_class.content_columns.map{ |c| c.name.to_sym }
else
[]
end
end

def add_filters_sidebar_section
self.sidebar_sections << filters_sidebar_section
end
Expand Down
6 changes: 3 additions & 3 deletions lib/active_admin/namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def register(resource_class, options = {}, &block)

# Register the resource
register_resource_controller(config)
parse_registration_block(config, resource_class, &block) if block_given?
parse_registration_block(config, &block) if block_given?
reset_menu!

# Dispatch a registration event
Expand Down Expand Up @@ -218,8 +218,8 @@ def register_resource_controller(config)
config.controller.active_admin_config = config
end

def parse_registration_block(config, resource_class, &block)
config.dsl = ResourceDSL.new(config, resource_class)
def parse_registration_block(config, &block)
config.dsl = ResourceDSL.new(config)
config.dsl.run_registration_block(&block)
end

Expand Down
20 changes: 19 additions & 1 deletion lib/active_admin/resource.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'active_admin/resource/action_items'
require 'active_admin/resource/attributes'
require 'active_admin/resource/controllers'
require 'active_admin/resource/menu'
require 'active_admin/resource/page_presenters'
Expand Down Expand Up @@ -87,6 +88,7 @@ def initialize(namespace, resource_class, options = {})
include Sidebars
include Routes
include Ordering
include Attributes

# The class this resource wraps. If you register the Post model, Resource#resource_class
# will point to the Post class
Expand Down Expand Up @@ -163,6 +165,22 @@ def find_resource(id)
(decorator_class && resource) ? decorator_class.new(resource) : resource
end

def resource_columns
resource_attributes.values
end

def resource_attributes
@resource_attributes ||= default_attributes
end

def association_columns
@association_columns ||= resource_attributes.select{ |key, value| key != value }.values
end

def content_columns
@content_columns ||= resource_attributes.select{ |key, value| key == value }.values
end

private

def method_for_find(id)
Expand All @@ -174,7 +192,7 @@ def method_for_find(id)
end

def default_csv_builder
@default_csv_builder ||= CSVBuilder.default_for_resource(resource_class)
@default_csv_builder ||= CSVBuilder.default_for_resource(self)
end

end # class Resource
Expand Down
44 changes: 44 additions & 0 deletions lib/active_admin/resource/attributes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module ActiveAdmin

class Resource
module Attributes

def default_attributes
resource_class.columns.each_with_object({}) do |c, attrs|
unless reject_col?(c)
name = c.name.to_sym
attrs[name] = (method_for_column(name) || name)
end
end
end

def method_for_column(c)
resource_class.respond_to?(:reflect_on_all_associations) && foreign_methods.has_key?(c) && foreign_methods[c].name.to_sym
end

def foreign_methods
@foreign_methods ||= resource_class.reflect_on_all_associations.
select{ |r| r.macro == :belongs_to }.
reject{ |r| r.chain.length > 2 && !r.options[:polymorphic] }.
index_by{ |r| r.foreign_key.to_sym }
end

def reject_col?(c)
primary_col?(c) || sti_col?(c) || counter_cache_col?(c)
end

def primary_col?(c)
c.name == resource_class.primary_key
end

def sti_col?(c)
c.name == resource_class.inheritance_column
end

def counter_cache_col?(c)
c.name.end_with?('_count')
end

end
end
end
6 changes: 1 addition & 5 deletions lib/active_admin/resource_dsl.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
module ActiveAdmin
# This is the class where all the register blocks are evaluated.
class ResourceDSL < DSL
def initialize(config, resource_class)
@resource = resource_class
super(config)
end

private

Expand Down Expand Up @@ -105,7 +101,7 @@ def form(options = {}, &block)
# end
#
def csv(options={}, &block)
options[:resource] = @resource
options[:resource] = config

config.csv_builder = CSVBuilder.new(options, &block)
end
Expand Down
7 changes: 0 additions & 7 deletions lib/active_admin/view_helpers/display_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ def format_attribute(resource, attr)
def find_value(resource, attr)
if attr.is_a? Proc
attr.call resource
elsif attr =~ /\A(.+)_id\z/ && reflection_for(resource, $1.to_sym)
resource.public_send $1
elsif resource.respond_to? attr
resource.public_send attr
elsif resource.respond_to? :[]
Expand All @@ -81,11 +79,6 @@ def pretty_format(object)
end
end

def reflection_for(resource, method)
klass = resource.class
klass.reflect_on_association method if klass.respond_to? :reflect_on_association
end

def boolean_attr?(resource, attr)
if resource.class.respond_to? :columns_hash
column = resource.class.columns_hash[attr.to_s] and column.type == :boolean
Expand Down
4 changes: 2 additions & 2 deletions lib/active_admin/views/index_as_table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ def default_table
proc do
selectable_column
id_column if resource_class.primary_key
resource_class.content_columns.each do |col|
column col.name.to_sym
active_admin_config.resource_columns.each do |attribute|
column attribute
end
actions
end
Expand Down
2 changes: 1 addition & 1 deletion lib/active_admin/views/pages/show.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def default_main_content(&block)
end

def default_attribute_table_rows
resource.class.columns.collect{|column| column.name.to_sym }
active_admin_config.resource_columns
end
end

Expand Down
11 changes: 7 additions & 4 deletions spec/unit/csv_builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
RSpec.describe ActiveAdmin::CSVBuilder do

describe '.default_for_resource using Post' do
let(:csv_builder) { ActiveAdmin::CSVBuilder.default_for_resource(Post).tap(&:exec_columns) }
let(:application){ ActiveAdmin::Application.new }
let(:namespace){ ActiveAdmin::Namespace.new(application, :admin) }
let(:resource){ ActiveAdmin::Resource.new(namespace, Post, {}) }
let(:csv_builder) { ActiveAdmin::CSVBuilder.default_for_resource(resource).tap(&:exec_columns) }

it 'returns a default csv_builder for Post' do
expect(csv_builder).to be_a(ActiveAdmin::CSVBuilder)
Expand All @@ -18,8 +21,8 @@

it "has Post's content_columns" do
csv_builder.columns[1..-1].each_with_index do |column, index|
expect(column.name).to eq Post.content_columns[index].name.humanize
expect(column.data).to eq Post.content_columns[index].name.to_sym
expect(column.name).to eq resource.content_columns[index].to_s.humanize
expect(column.data).to eq resource.content_columns[index]
end
end

Expand All @@ -32,7 +35,7 @@
end

it 'gets name from I18n' do
title_index = Post.content_columns.map(&:name).index('title') + 1 # First col is always id
title_index = resource.content_columns.index(:title) + 1 # First col is always id
expect(csv_builder.columns[title_index].name).to eq localized_name
end
end
Expand Down
7 changes: 3 additions & 4 deletions spec/unit/filters/resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

it "should return the defaults if no filters are set" do
expect(resource.filters.keys).to match_array([
:author, :body, :category, :created_at, :custom_created_at_searcher, :custom_title_searcher, :custom_searcher_numeric, :position, :published_date, :starred, :taggings, :title, :updated_at
:author, :body, :category, :created_at, :custom_created_at_searcher, :custom_title_searcher, :custom_searcher_numeric, :position, :published_date, :starred, :taggings, :title, :updated_at, :foo_id
])
end

Expand All @@ -35,7 +35,7 @@
it "should return the defaults without associations if default association filters are disabled on the namespace" do
resource.namespace.include_default_association_filters = false
expect(resource.filters.keys).to match_array([
:body, :created_at, :custom_created_at_searcher, :custom_title_searcher, :custom_searcher_numeric, :position, :published_date, :starred, :title, :updated_at
:body, :created_at, :custom_created_at_searcher, :custom_title_searcher, :custom_searcher_numeric, :position, :published_date, :starred, :title, :updated_at, :foo_id
])
end

Expand Down Expand Up @@ -104,8 +104,7 @@
resource.add_filter :count, as: :string

expect(resource.filters.keys).to match_array([
:author, :body, :category, :count, :created_at, :custom_created_at_searcher, :custom_title_searcher, :custom_searcher_numeric, :position, :published_date, :starred, :taggings, :title, :updated_at

:author, :body, :category, :count, :created_at, :custom_created_at_searcher, :custom_title_searcher, :custom_searcher_numeric, :position, :published_date, :starred, :taggings, :title, :updated_at, :foo_id
])
end

Expand Down
50 changes: 50 additions & 0 deletions spec/unit/resource/attributes_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require 'rails_helper'

module ActiveAdmin
RSpec.describe Resource, "Attributes" do
let(:application) { ActiveAdmin::Application.new }
let(:namespace) { ActiveAdmin::Namespace.new application, :admin }
let(:resource_config) { ActiveAdmin::Resource.new namespace, Post }

describe "#resource_attributes" do
subject do
resource_config.resource_attributes
end

it 'should return attributes hash' do
expect(subject).to eq( author_id: :author,
body: :body,
created_at: :created_at,
custom_category_id: :category,
foo_id: :foo_id,
position: :position,
published_date: :published_date,
starred: :starred,
title: :title,
updated_at: :updated_at)
end
end

describe "#association_columns" do
subject do
resource_config.association_columns
end

it 'should return associations' do
expect(subject).to eq([:author, :category])
end
end

describe "#content_columns" do
subject do
resource_config.content_columns
end

it 'should return columns without associations' do
expect(subject).to eq([:title, :body, :published_date, :position, :starred, :foo_id, :created_at, :updated_at])
end
end

end
end

0 comments on commit 1ef08af

Please sign in to comment.