Skip to content

Commit

Permalink
Make the component configuration additive
Browse files Browse the repository at this point in the history
Hopefully this makes it easier to understand how to customize the
components.

This is done by having all the component configuration generated onto
the template CatalogController rather than having it included in a mixin
automatically.

For backwards compatibility people will have to mix in:
```
include Blacklight::DefaultComponentConfiguration
```

or add to CatalogController:

```
  configure_blacklight do |config|
    config.add_results_document_tool(:bookmark, partial: 'bookmark_control', if: :render_bookmarks_control?)

    config.add_results_collection_tool(:sort_widget)
    config.add_results_collection_tool(:per_page_widget)
    config.add_results_collection_tool(:view_type_group)

    config.add_show_tools_partial(:bookmark, partial: 'bookmark_control', if: :render_bookmarks_control?)
    config.add_show_tools_partial(:email, callback: :email_action, validator: :validate_email_params)
    config.add_show_tools_partial(:sms, if: :render_sms_action?, callback: :sms_action, validator: :validate_sms_params)
    config.add_show_tools_partial(:citation)

    config.add_nav_action(:bookmark, partial: 'blacklight/nav/bookmark', if: :render_bookmarks_control?)
    config.add_nav_action(:search_history, partial: 'blacklight/nav/search_history')
  end

```
  • Loading branch information
jcoyne committed Aug 31, 2017
1 parent 97871e6 commit 4f8faa4
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 152 deletions.
57 changes: 57 additions & 0 deletions app/builders/blacklight/action_builder.rb
@@ -0,0 +1,57 @@
# frozen_string_literal: true

module Blacklight
# Dynamically creates methods on the given controller (typically CatalogController)
# for handling configured show tools
class ActionBuilder
def initialize(klass, name, opts)
@klass = klass
@name = name
@opts = opts
end

attr_reader :klass, :name, :opts

# Define a simple action handler for the tool as long as the method
# doesn't already exist or the `:define_method` option is not `false`
def build
return if skip?
callback = opts.fetch(:callback, nil).inspect
validator = opts.fetch(:validator, nil).inspect
klass.class_eval <<EORUBY, __FILE__, __LINE__ + 1
def #{name}
@response, @documents = action_documents
if request.post? && #{callback} &&
(#{validator}.blank? || send(#{validator}))
send(#{callback}, @documents)
flash[:success] ||= I18n.t("blacklight.#{name}.success", default: nil)
respond_to do |format|
format.html do
return render "#{name}_success" if request.xhr?
redirect_to action_success_redirect_path
end
end
else
respond_to do |format|
format.html do
return render layout: false if request.xhr?
# Otherwise draw the full page
end
end
end
end
EORUBY
end
# rubocop:enable Metrics/LineLength

private

def skip?
klass.method_defined?(name) || opts[:define_method] == false
end
end
end
5 changes: 4 additions & 1 deletion app/controllers/concerns/blacklight/catalog.rb
Expand Up @@ -3,7 +3,6 @@ module Blacklight::Catalog
extend ActiveSupport::Concern

include Blacklight::Base
include Blacklight::DefaultComponentConfiguration
include Blacklight::Facet

# The following code is executed when someone includes blacklight::catalog in their
Expand Down Expand Up @@ -147,6 +146,10 @@ def facet_limit_for(facet_field)
# non-routable methods ->
#

def render_sms_action?(_config, _options)
sms_mappings.present?
end

def search_service
search_service_class.new(blacklight_config, search_state.to_h)
end
Expand Down
Expand Up @@ -4,6 +4,9 @@ module DefaultComponentConfiguration
extend ActiveSupport::Concern

included do
Deprecation.warn(self, "Blacklight::DefaultComponentConfiguration is deprecated and will be removed in the next release." \
"this means you must call add_results_document_tool, add_results_collection_tool, " \
"add_show_tools_partial and add_nav_action manually in your config")
add_results_document_tool(:bookmark, partial: 'bookmark_control', if: :render_bookmarks_control?)

add_results_collection_tool(:sort_widget)
Expand All @@ -19,10 +22,6 @@ module DefaultComponentConfiguration
add_nav_action(:search_history, partial: 'blacklight/nav/search_history')
end

def render_sms_action?(_config, _options)
sms_mappings.present?
end

module ClassMethods
# YARD will include inline disabling as docs, cannot do multiline inside @!macro. AND this must be separate from doc block.
# rubocop:disable Metrics/LineLength
Expand All @@ -40,53 +39,26 @@ module ClassMethods
# @option opts [Symbol] :callback method for further processing of documents, receives Array of documents
def add_show_tools_partial(name, opts = {})
blacklight_config.add_show_tools_partial(name, opts)
create_action_handler(name, opts)
end

# Define a simple action handler for the tool as long as the method
# doesn't already exist or the `:define_method` option is not `false`
def create_action_handler(name, opts)
return if method_defined?(name) || opts[:define_method] == false

define_method name do
@response, @documents = action_documents

if request.post? && opts[:callback] &&
(opts[:validator].blank? || send(opts[:validator]))

send(opts[:callback], @documents)

flash[:success] ||= I18n.t("blacklight.#{name}.success", default: nil)

respond_to do |format|
format.html do
return render "#{name}_success" if request.xhr?
redirect_to action_success_redirect_path
end
end
else
respond_to do |format|
format.html do
return render layout: false if request.xhr?
# Otherwise draw the full page
end
end
end
end
ActionBuilder.new(self, name, opts).build
end
# rubocop:enable Metrics/LineLength

deprecation_deprecate add_show_tools_partial: 'use blacklight_config.add_show_tools_partial instead'

# Add a tool to be displayed for each document in the search results.
# @!macro partial_if_unless
delegate :add_results_document_tool, to: :blacklight_config
deprecation_deprecate add_results_document_tool: 'use blacklight_config.add_results_document_tool instead'

# Add a tool to be displayed for the list of search results themselves.
# @!macro partial_if_unless
delegate :add_results_collection_tool, to: :blacklight_config
deprecation_deprecate add_results_collection_tool: 'use blacklight_config.add_results_collection_tool instead'

# Add a partial to the header navbar.
# @!macro partial_if_unless
delegate :add_nav_action, to: :blacklight_config
deprecation_deprecate add_nav_action: 'use blacklight_config.add_nav_action instead'
end
end
end
6 changes: 3 additions & 3 deletions app/models/concerns/blacklight/configurable.rb
Expand Up @@ -14,7 +14,7 @@ def blacklight_config

module ClassMethods
def copy_blacklight_config_from(other_class)
self.blacklight_config = other_class.blacklight_config.inheritable_copy
self.blacklight_config = other_class.blacklight_config.inheritable_copy(self)
end

# lazy load a deep_copy of superclass if present, else
Expand All @@ -24,7 +24,7 @@ def copy_blacklight_config_from(other_class)
# we lazy load to 'inherit' how we want.
def blacklight_config
@blacklight_config ||= if superclass.respond_to?(:blacklight_config)
superclass.blacklight_config.deep_copy
superclass.blacklight_config.build(self)
else
default_configuration
end
Expand All @@ -39,7 +39,7 @@ def configure_blacklight(*args, &block)
##
# The default configuration object
def default_configuration
Blacklight::Configurable.default_configuration.inheritable_copy
Blacklight::Configurable.default_configuration.inheritable_copy(self)
end
end

Expand Down
10 changes: 9 additions & 1 deletion lib/blacklight/configuration.rb
Expand Up @@ -281,7 +281,14 @@ def deep_copy
end
end
end
alias_method :inheritable_copy, :deep_copy

# builds a copy for the provided controller class
def build(klass)
deep_copy.tap do |conf|
conf.klass = klass
end
end
alias_method :inheritable_copy, :build

# Get a view configuration for the given view type
# including default values from the index configuration
Expand All @@ -304,6 +311,7 @@ def view_config(view_type)
def add_show_tools_partial(name, opts = {})
opts[:partial] ||= 'document_action'
add_action(show.document_actions, name, opts)
klass && ActionBuilder.new(klass, name, opts).build
end
# rubocop:enable Metrics/LineLength

Expand Down
14 changes: 14 additions & 0 deletions lib/generators/blacklight/templates/catalog_controller.rb
Expand Up @@ -30,6 +30,20 @@ class <%= controller_name.classify %>Controller < ApplicationController
config.index.display_type_field = 'format'
#config.index.thumbnail_field = 'thumbnail_path_ss'
config.add_results_document_tool(:bookmark, partial: 'bookmark_control', if: :render_bookmarks_control?)
config.add_results_collection_tool(:sort_widget)
config.add_results_collection_tool(:per_page_widget)
config.add_results_collection_tool(:view_type_group)
config.add_show_tools_partial(:bookmark, partial: 'bookmark_control', if: :render_bookmarks_control?)
config.add_show_tools_partial(:email, callback: :email_action, validator: :validate_email_params)
config.add_show_tools_partial(:sms, if: :render_sms_action?, callback: :sms_action, validator: :validate_sms_params)
config.add_show_tools_partial(:citation)
config.add_nav_action(:bookmark, partial: 'blacklight/nav/bookmark', if: :render_bookmarks_control?)
config.add_nav_action(:search_history, partial: 'blacklight/nav/search_history')
# solr field configuration for document/show views
#config.show.title_field = 'title_display'
#config.show.display_type_field = 'format'
Expand Down
@@ -1,6 +1,10 @@
# frozen_string_literal: true

RSpec.describe Blacklight::DefaultComponentConfiguration do
before do
allow(Deprecation).to receive(:warn)
end

subject do
Class.new do
include Blacklight::Configurable
Expand All @@ -13,18 +17,18 @@ def some_existing_action
end

describe ".add_show_tools_partial" do
it "should define an action method" do
subject.add_show_tools_partial :xyz
it "defines an action method" do
subject.blacklight_config.add_show_tools_partial :xyz
expect(subject.new).to respond_to :xyz
end

it "should not replace an existing method" do
subject.add_show_tools_partial :some_existing_action
it "does not replace an existing method" do
subject.blacklight_config.add_show_tools_partial :some_existing_action
expect(subject.new.some_existing_action).to eq 1
end

it "should allow the configuration to opt out of creating a method" do
subject.add_show_tools_partial :some_missing_action, define_method: false
it "allows the configuration to opt out of creating a method" do
subject.blacklight_config.add_show_tools_partial :some_missing_action, define_method: false
expect(subject.new).not_to respond_to :some_missing_action
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/controllers/catalog_controller_spec.rb
Expand Up @@ -639,7 +639,7 @@ def export_as_mock

describe "#add_show_tools_partial" do
before do
described_class.add_show_tools_partial(:like, callback: :perform_like, validator: :validate_like_params)
described_class.blacklight_config.add_show_tools_partial(:like, callback: :perform_like, validator: :validate_like_params)
allow(controller).to receive(:solr_document_url).and_return('catalog/1')
allow(controller).to receive(:action_documents).and_return(1)
Rails.application.routes.draw do
Expand Down
29 changes: 14 additions & 15 deletions spec/models/blacklight/configurable_spec.rb
Expand Up @@ -17,19 +17,19 @@ class Child < Parent
end
end
end
it "inherits the configuration when subclassed" do
it "inherits the configuration when subclassed" do
expect(TestCaseInheritence::Child.blacklight_config.list).to include(1,2,3)
end
it "inherited version should be a deep copy, not original" do

it "inherited version should be a deep copy, not original" do
expect(TestCaseInheritence::Child.blacklight_config).to_not be(TestCaseInheritence::Parent.blacklight_config)

TestCaseInheritence::Child.blacklight_config.list << "child_only"


expect(TestCaseInheritence::Child.blacklight_config.list).to include("child_only")
expect(TestCaseInheritence::Child.blacklight_config.list).to include("child_only")
expect(TestCaseInheritence::Parent.blacklight_config.list).to_not include("child_only")
end
end
end

describe "default configuration" do
Expand All @@ -56,15 +56,15 @@ class Child < Parent
a.send(:include, Blacklight::Configurable)
expect(a.blacklight_config.a).to eq 1
end

it "has configure_blacklight convenience method" do
klass = Class.new
klass.send(:include, Blacklight::Configurable)

klass.configure_blacklight do |config|
config.my_key = 'value'
end
end

expect(klass.blacklight_config.my_key).to eq 'value'
end

Expand All @@ -75,11 +75,11 @@ class Child < Parent

instance = klass.new
instance.blacklight_config = Blacklight::Configuration.new

expect(instance.blacklight_config).to_not eq klass.blacklight_config
expect(instance.blacklight_config.foo).to be_nil
end

it "allows instance to set it's own config seperate from class" do
# this is built into class_attribute; we spec it both to document it,
# and to ensure we preserve this feature if we change implementation
Expand All @@ -89,7 +89,7 @@ class Child < Parent
klass.blacklight_config.foo = "bar"
klass.blacklight_config.bar = []
klass.blacklight_config.bar << "asd"

instance = klass.new
instance.blacklight_config.bar << "123"
expect(instance.blacklight_config).to_not eq klass.blacklight_config
Expand All @@ -111,7 +111,6 @@ class Child < Parent
expect(klass.blacklight_config.foo).to eq "bar"
expect(klass2.blacklight_config.foo).to eq "asdf"
end

end
end

0 comments on commit 4f8faa4

Please sign in to comment.