europeana-blacklight 0.2.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d75b280e4194a85ffb379a19407b6de1084ce7b0
4
- data.tar.gz: d571e1f7bb56cadeaad835963913ae5321c0d63e
3
+ metadata.gz: 78442f9dbd68b04249c1bb5d6e0820ef4f75d925
4
+ data.tar.gz: bfcb4792d1cc6412a5551b76f67706f4b8aa7a6f
5
5
  SHA512:
6
- metadata.gz: f6e5d347b85b1252a3770eac11503c291a3ab574d363901581db896bef5706320bfafa8a38856ecd812beea8b5f592854c3d8a049d0015877ec998450d444c71
7
- data.tar.gz: 55929793d0f7f863744bf1a115d9bf29929ffc64cc7a15da2ef597bff619681462996add301bd0af9c3b5a70d2e80881f3c790f1a9b274365e073a56c974b4b9
6
+ metadata.gz: 0e0cde651009ecd7b881c0f34edc745d2b431a9f4664cf168c9f436521f664b4f08e127d1e5d4efcfa3721bac065a36ea05dd313ad31dd58fd6d0079b9aca9e8
7
+ data.tar.gz: c88b697f78b65c586ac09b377e4b166b9c028de0284fc8c22ca7600c202434a069890b70e3cedcbdb57b5a9373285641beb06812860a32d7dea649febc07ab5c
data/.ruby-style.yml CHANGED
@@ -6,6 +6,7 @@ AllCops:
6
6
  Exclude:
7
7
  - "vendor/**/*"
8
8
  - "db/**/*"
9
+ - "lib/generators/europeana/blacklight/templates/**/*"
9
10
  RunRailsCops: true
10
11
  DisplayCopNames: false
11
12
  StyleGuideCopsOnly: false
@@ -497,7 +498,7 @@ Metrics/LineLength:
497
498
  Description: Limit lines to 80 characters.
498
499
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#80-character-limits
499
500
  Enabled: true
500
- Max: 80
501
+ Max: 140
501
502
  AllowURI: true
502
503
  URISchemes:
503
504
  - http
data/QUICKSTART.md ADDED
@@ -0,0 +1,42 @@
1
+ # Europeana Blacklight Quick Start Guide
2
+
3
+ ## Install Rails 4
4
+ ```
5
+ gem install rails
6
+ rails -v
7
+ # Rails 4.2.4
8
+ ```
9
+
10
+ ## Create a new Rails application
11
+ ```
12
+ rails new culture_vulture
13
+ cd culture_vulture
14
+ ```
15
+
16
+ ## Bundle europeana-blacklight
17
+ Add to the Gemfile:
18
+ ```ruby
19
+ gem 'europeana-blacklight',
20
+ github: 'europeana/europeana-blacklight',
21
+ require: 'europeana/blacklight'
22
+ ```
23
+
24
+ ## Get a Europeana API key
25
+ From http://labs.europeana.eu/api/
26
+
27
+ ## Install Blacklight with the Europeana API adapter
28
+ ```
29
+ bundle install
30
+ bundle exec rails generate blacklight:install --devise
31
+ bundle exec rails generate europeana:blacklight:install YOUR_API_KEY
32
+ bundle exec rake db:migrate
33
+ ```
34
+
35
+ ## Customise the config
36
+ Review the generated `CatalogController` and adjust the default configuration
37
+ to your preferences.
38
+
39
+ ## Start the application
40
+ ```
41
+ bundle exec rails server
42
+ ```
data/README.md CHANGED
@@ -1,62 +1,40 @@
1
1
  # Europeana::Blacklight
2
2
 
3
- [![Build Status](https://travis-ci.org/europeana/europeana-blacklight.svg?branch=master)](https://travis-ci.org/europeana/europeana-blacklight) [![Coverage Status](https://coveralls.io/repos/europeana/europeana-blacklight/badge.svg?branch=master&service=github)](https://coveralls.io/github/europeana/europeana-blacklight?branch=master) [![security](https://hakiri.io/github/europeana/europeana-blacklight/master.svg)](https://hakiri.io/github/europeana/europeana-blacklight/master)
3
+ [![Build Status](https://travis-ci.org/europeana/europeana-blacklight.svg?branch=master)](https://travis-ci.org/europeana/europeana-blacklight) [![Coverage Status](https://coveralls.io/repos/europeana/europeana-blacklight/badge.svg?branch=master&service=github)](https://coveralls.io/github/europeana/europeana-blacklight?branch=master) [![security](https://hakiri.io/github/europeana/europeana-blacklight/master.svg)](https://hakiri.io/github/europeana/europeana-blacklight/master) [![Dependency Status](https://gemnasium.com/europeana/europeana-blacklight.svg)](https://gemnasium.com/europeana/europeana-blacklight)
4
4
 
5
5
  Ruby gem providing an adapter to use the
6
6
  [Europeana REST API](http://labs.europeana.eu/api/introduction/) as a data
7
7
  source for [Blacklight](http://projectblacklight.org/).
8
8
 
9
- ## Installation
9
+ ## Usage
10
10
 
11
- Add this line to your application's Gemfile:
11
+ See the [Quick Start Guide](QUICKSTART.md).
12
12
 
13
- ```ruby
14
- gem 'europeana-blacklight',
15
- github: 'europeana/europeana-blacklight',
16
- require: 'europeana/blacklight'
17
- ```
13
+ ## Features
18
14
 
19
- And then execute:
15
+ ### Supported Blacklight features
20
16
 
21
- $ bundle
17
+ * Search
18
+ * View record
19
+ * Pagination of search results
20
+ * Field facets
21
+ * [Query facets](#query-facets)
22
+ * Facet limits
23
+ * Fielded search
24
+ * Bookmarks
25
+ * Range queries
22
26
 
23
- ## Usage
27
+ ### Unsupported Blacklight features
24
28
 
25
- 1. Get a Europeana API key from http://labs.europeana.eu/api/
26
- 2. Set the API key in `config/blacklight.yml`:
27
-
28
- ```yml
29
- production:
30
- europeana_api_key: YOUR_API_KEY
31
- ```
32
-
33
- 3. Configure Blacklight to use the Europeana API adapter:
34
-
35
- ```ruby
36
- class CatalogController < ApplicationController
37
- self.search_params_logic = Europeana::Blacklight::SearchBuilder.default_processor_chain
38
-
39
- configure_blacklight do |config|
40
- config.repository_class = Europeana::Blacklight::ApiRepository
41
- config.search_builder_class = Europeana::Blacklight::SearchBuilder
42
- config.response_model = Europeana::Blacklight::Response
43
- config.document_model = Europeana::Blacklight::Document
44
- config.document_presenter_class = Europeana::Blacklight::DocumentPresenter
45
- config.facet_paginator_class = Europeana::Blacklight::FacetPaginator
46
- end
47
- end
48
- ```
49
-
50
- 4. Caching (optional)
51
-
52
- To enable caching of API responses:
53
-
54
- ```ruby
55
- configure_blacklight do |config|
56
- config.europeana_api_cache = Rails.cache # or any {ActiveSupport::Cache} instance
57
- config.europeana_api_cache_expires_in = 60.minutes # defaults to 24.hours
58
- end
59
- ```
29
+ * Result sorting :(
30
+ * "Did you mean" spellcheck
31
+ * MLT Solr-style (but see custom features)
32
+
33
+ ### Custom features
34
+
35
+ * Nested EDM field names
36
+ * MLT by record ID in :mlt URL parameter
37
+ * Query facets with arbitrary API parameters
60
38
 
61
39
  ## Query facets
62
40
 
@@ -65,13 +43,14 @@ permit specification of multiple parameters to be passed to the API:
65
43
 
66
44
  ```ruby
67
45
  configure_blacklight do |config|
68
- config.add_facet_field 'Cities (reusable content)', query: [
69
- { label: 'Paris', fq: { qf: 'paris', reusability: 'open' } },
70
- { label: 'Berlin', fq: { qf: 'berlin', reusability: 'open' } }
71
- ]
46
+ config.add_facet_field 'Cities (reusable content)', query: {
47
+ paris: { label: 'Paris', fq: { qf: 'paris', reusability: 'open' } },
48
+ berlin: { label: 'Berlin', fq: { qf: 'berlin', reusability: 'open' } }
49
+ }
72
50
  end
73
51
  ```
74
52
 
75
53
  *Warning:* query facets are achieved by sending additional queries to the
76
- API. If you configure 10 query facets, this will result in an additional
77
- 10 queries being sent to the API.
54
+ API. If you configure 2 query facets each with 10 facet values, this will result
55
+ in an additional 20 queries being sent to the API.
56
+
@@ -0,0 +1,73 @@
1
+ module Europeana
2
+ module Blacklight
3
+ module Catalog
4
+ extend ActiveSupport::Concern
5
+
6
+ include ::Blacklight::Catalog
7
+ include Europeana::Blacklight::SearchHelper
8
+
9
+ included do
10
+ self.search_params_logic = Europeana::Blacklight::SearchBuilder.default_processor_chain
11
+
12
+ configure_blacklight do |config|
13
+ # Adapter classes
14
+ config.repository_class = Europeana::Blacklight::ApiRepository
15
+ config.search_builder_class = Europeana::Blacklight::SearchBuilder
16
+ config.response_model = Europeana::Blacklight::Response
17
+ config.document_model = Europeana::Blacklight::Document
18
+ config.document_presenter_class = Europeana::Blacklight::DocumentPresenter
19
+ # config.facet_paginator_class = Europeana::Blacklight::FacetPaginator
20
+
21
+ # Prevent BL's "did you mean" spellcheck feature kicking in
22
+ config.spell_max = -1
23
+ end
24
+ end
25
+
26
+ # Empty search returns all records
27
+ def has_search_parameters?
28
+ super || params.key?(:q) || params.key?(:mlt)
29
+ end
30
+
31
+ def search_results(user_params, _search_params_logic)
32
+ super.tap do |results|
33
+ if has_search_parameters?
34
+ results.first[:facet_queries] = europeana_api_query_facet_counts(user_params)
35
+ end
36
+ end
37
+ end
38
+
39
+ protected
40
+
41
+ def blacklight_query_facets
42
+ blacklight_config.facet_fields.select do |_, facet|
43
+ facet.query &&
44
+ (facet.include_in_request ||
45
+ (facet.include_in_request.nil? &&
46
+ blacklight_config.add_facet_fields_to_solr_request))
47
+ end
48
+ end
49
+
50
+ def europeana_api_query_facet_counts(user_params)
51
+ qf_counts = []
52
+
53
+ blacklight_query_facets.each_pair do |_facet_name, query_facet|
54
+ query_facet.query.each_pair do |_field_name, query_field|
55
+ count = europeana_api_query_facet_count(query_field[:fq], user_params)
56
+ qf_counts.push([query_field[:fq], count])
57
+ end
58
+ end
59
+
60
+ qf_counts.sort_by(&:last).reverse.each_with_object({}) do |qf, hash|
61
+ hash[qf.first] = qf.last
62
+ end
63
+ end
64
+
65
+ def europeana_api_query_facet_count(query_field_fq, user_params)
66
+ query = search_builder_class.new(search_params_logic, self).
67
+ with(user_params).with_overlay_params(query_field_fq || {}).query.
68
+ merge(rows: 0, start: 1, profile: 'minimal')
69
+ repository.search(query).total
70
+ end
71
+ end
72
+ end
73
+ end
@@ -20,12 +20,15 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.required_ruby_version = '>= 2.0.0'
22
22
 
23
+ spec.add_dependency 'activesupport', '>= 4.0', '< 5.0'
23
24
  spec.add_dependency 'blacklight', '>= 5.12.0', '< 6.0.0'
24
- spec.add_dependency 'europeana-api', '~> 0.3.4'
25
+ spec.add_dependency 'europeana-api', '~> 0.4.0'
25
26
  spec.add_dependency 'iso-639', '~> 0.2.5'
26
27
  spec.add_dependency 'kaminari', '~> 0.16'
27
28
 
28
29
  spec.add_development_dependency 'bundler', '~> 1.8'
29
30
  spec.add_development_dependency 'rake', '~> 10.0'
30
31
  spec.add_development_dependency 'rspec', '~> 3.2'
32
+ spec.add_development_dependency 'shoulda-matchers', '~> 2.8'
33
+ spec.add_development_dependency 'webmock', '~> 1.21'
31
34
  end
@@ -1,6 +1,9 @@
1
- require 'europeana/blacklight/version'
2
- require 'europeana/api'
1
+ require 'active_support/core_ext/hash'
2
+ require 'active_support/core_ext/module/delegation'
3
3
  require 'blacklight'
4
+ require 'europeana/api'
5
+ require 'europeana/blacklight/engine' if defined?(Rails)
6
+ require 'europeana/blacklight/version'
4
7
 
5
8
  module Europeana
6
9
  ##
@@ -9,7 +12,10 @@ module Europeana
9
12
  autoload :ApiRepository, 'europeana/blacklight/api_repository'
10
13
  autoload :Document, 'europeana/blacklight/document'
11
14
  autoload :DocumentPresenter, 'europeana/blacklight/document_presenter'
15
+ # autoload :FacetPaginator, 'europeana/blacklight/facet_paginator'
12
16
  autoload :Response, 'europeana/blacklight/response'
17
+ autoload :Routes, 'europeana/blacklight/routes'
13
18
  autoload :SearchBuilder, 'europeana/blacklight/search_builder'
19
+ autoload :SearchHelper, 'europeana/blacklight/search_helper'
14
20
  end
15
21
  end
@@ -13,11 +13,7 @@ module Europeana
13
13
  # @return (see blacklight_config.response_model)
14
14
  def find(id, params = {})
15
15
  id = "/#{id}" unless id[0] == '/'
16
- cache_key = "Europeana:API:Record:#{id}"
17
- cache_key << ':' + params.to_query unless params.blank?
18
- res = cached(cache_key) do
19
- connection.record(id, auth_params(params))
20
- end
16
+ res = connection.record(id, params)
21
17
 
22
18
  blacklight_config.response_model.new(
23
19
  res, params, document_model: blacklight_config.document_model,
@@ -26,10 +22,7 @@ module Europeana
26
22
  end
27
23
 
28
24
  def search(params = {})
29
- cache_key = "Europeana:API:Search:" + params.to_query
30
- res = cached(cache_key) do
31
- connection.search(auth_params(params))
32
- end
25
+ res = connection.search(params)
33
26
 
34
27
  blacklight_config.response_model.new(
35
28
  res, params, document_model: blacklight_config.document_model,
@@ -37,46 +30,6 @@ module Europeana
37
30
  )
38
31
  end
39
32
 
40
- ##
41
- # Fetches the hierarchy data for a Europeana record
42
- #
43
- # If the hierarchy data for the requested record is cached, that will be
44
- # returned, otherwise it will be obtained from the Europeana REST API.
45
- #
46
- # @param id [String] Europeana record ID, with leading slash
47
- # @return [Hash] Record's hierarchy data, or false if it has none
48
- def fetch_document_hierarchy(id)
49
- cached("Europeana:API:Record:hierarchy:#{id}") do
50
- begin
51
- europeana_api_document_hierarchy(id)
52
- rescue Europeana::API::Errors::RequestError => error
53
- unless error.message == 'This record has no hierarchical structure!'
54
- raise
55
- end
56
- false
57
- end
58
- end
59
- end
60
-
61
- ##
62
- # Requests hierarchy data for a Europeana record from the REST API
63
- #
64
- # The return value will contain a combination of the responses from the
65
- # ancestor-self-siblings and children API endpoints.
66
- #
67
- # @param id [String] Europeana record ID, with leading slash
68
- # @return [Hash] Record's hierarchy data
69
- def europeana_api_document_hierarchy(id)
70
- record = Europeana::API::Record.new(id, auth_params)
71
- hierarchy = record.hierarchy('ancestor-self-siblings')
72
-
73
- if hierarchy['self']['hasChildren']
74
- hierarchy = record.hierarchy('ancestor-self-siblings', :children)
75
- end
76
-
77
- hierarchy
78
- end
79
-
80
33
  ##
81
34
  # Queries the API for items similar to a given document
82
35
  def more_like_this(doc, field = nil, params = {})
@@ -88,19 +41,17 @@ module Europeana
88
41
  end
89
42
 
90
43
  def build_connection
91
- Europeana::API
44
+ Europeana::API.tap do |api|
45
+ api.api_key = blacklight_config.connection_config[:europeana_api_key]
46
+ api.cache_store = cache_store
47
+ api.cache_expires_in = cache_expires_in
48
+ end
92
49
  end
93
50
 
94
51
  protected
95
52
 
96
- def auth_params(params = {})
97
- {
98
- wskey: blacklight_config.connection_config[:europeana_api_key]
99
- }.merge(params)
100
- end
101
-
102
- def cache
103
- @cache ||= begin
53
+ def cache_store
54
+ @cache_store ||= begin
104
55
  blacklight_config.europeana_api_cache || ActiveSupport::Cache::NullStore.new
105
56
  end
106
57
  end
@@ -110,12 +61,6 @@ module Europeana
110
61
  blacklight_config.europeana_api_cache_expires_in || 24.hours
111
62
  end
112
63
  end
113
-
114
- def cached(key)
115
- cache.fetch(key, expires_in: cache_expires_in) do
116
- yield
117
- end
118
- end
119
64
  end
120
65
  end
121
66
  end
@@ -12,11 +12,11 @@ module Europeana
12
12
 
13
13
  include ActiveModel::Conversion
14
14
  include ::Blacklight::Document
15
+ include ::Blacklight::Document::ActiveModelShim
15
16
  include MoreLikeThis
16
17
  include Relations
17
18
 
18
19
  attr_writer :provider_id, :record_id
19
- attr_accessor :hierarchy
20
20
 
21
21
  class << self
22
22
  # @todo Are three-letter language codes valid in EDM?
@@ -24,7 +24,12 @@ module Europeana
24
24
  # output; remove when fixed at source
25
25
  def lang_map?(obj)
26
26
  return false unless obj.is_a?(Hash)
27
- obj.keys.all? { |k| (k == 'def') || (k == '') || (!ISO_639.find(k.split('-').first).nil?) }
27
+ obj.keys.map(&:to_s).all? { |key| known_lang_map_key?(key) }
28
+ end
29
+
30
+ def known_lang_map_key?(key)
31
+ key = key.dup.downcase
32
+ ['def', '', 'sh'].include?(key) || (!ISO_639.find(key.split('-').first).nil?)
28
33
  end
29
34
 
30
35
  def localize_lang_map(lang_map)
@@ -49,6 +54,8 @@ module Europeana
49
54
  end
50
55
  end
51
56
 
57
+ delegate :lang_map?, :localize_lang_map, to: :class
58
+
52
59
  def initialize(source_doc = {}, response = nil)
53
60
  fields, @relations = extract_relations(source_doc)
54
61
  super(fields, response)
@@ -86,11 +93,11 @@ module Europeana
86
93
  true
87
94
  end
88
95
 
89
- def private?(exhibit)
90
- !public?(exhibit)
96
+ def private?(_exhibit)
97
+ false
91
98
  end
92
99
 
93
- def public?(exhibit)
100
+ def public?(_exhibit)
94
101
  true
95
102
  end
96
103
 
@@ -102,25 +109,13 @@ module Europeana
102
109
  @record_id ||= id.to_s.split('/')[2]
103
110
  end
104
111
 
105
- def as_json(options = nil)
106
- json = super
107
- unless @hierarchy.nil?
108
- json.merge!('hierarchy' => @hierarchy.as_json(options))
109
- end
110
- json.tap do |j|
112
+ def as_json(options = {})
113
+ super.tap do |json|
111
114
  relations.each do |k, v|
112
- j[k] = v.as_json
115
+ json[k] = v.as_json
113
116
  end
114
117
  end
115
118
  end
116
-
117
- def lang_map?(obj)
118
- self.class.lang_map?(obj)
119
- end
120
-
121
- def localize_lang_map(lang_map)
122
- self.class.localize_lang_map(lang_map)
123
- end
124
119
  end
125
120
  end
126
121
  end
@@ -68,6 +68,31 @@ module Europeana
68
68
  end
69
69
  end
70
70
 
71
+ def has?(k, *values)
72
+ if !key?(k)
73
+ false
74
+ elsif values.empty?
75
+ fetch(k, nil).present?
76
+ else
77
+ Array(values).any? do |expected|
78
+ Array(fetch(k, nil)).any? do |actual|
79
+ case expected
80
+ when Regexp
81
+ actual =~ expected
82
+ else
83
+ actual == expected
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def key?(k)
91
+ [nested_field_container(k)].flatten.any? do |container|
92
+ container._source.key?(nested_field_key(k))
93
+ end
94
+ end
95
+
71
96
  def has_relation?(name)
72
97
  relations.key?(name.to_s)
73
98
  end
@@ -3,7 +3,7 @@ module Europeana
3
3
  ##
4
4
  # Blacklight document presenter for Europeana documents
5
5
  class DocumentPresenter < ::Blacklight::DocumentPresenter
6
- include ActionView::Helpers::AssetTagHelper
6
+ include ActionView::Helpers::AssetTagHelper # ?
7
7
 
8
8
  def render_document_show_field_value(field, options = {})
9
9
  render_nested_field_value(field, :show, options)
@@ -0,0 +1,11 @@
1
+ module Europeana
2
+ module Blacklight
3
+ class Engine < Rails::Engine
4
+ engine_name 'europeana_blacklight'
5
+
6
+ initializer 'europeana_blacklight.routes' do |_app|
7
+ ::Blacklight::Routes.send(:include, Europeana::Blacklight::Routes)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -2,7 +2,7 @@ module Europeana
2
2
  module Blacklight
3
3
  ##
4
4
  # Facet paginator for Europeana API
5
- class FacetPaginator < Blacklight::FacetPaginator
5
+ class FacetPaginator < ::Blacklight::FacetPaginator
6
6
  end
7
7
  end
8
8
  end
@@ -10,6 +10,7 @@ module Europeana
10
10
  module Pagination
11
11
  include Kaminari::PageScopeMethods
12
12
  include Kaminari::ConfigurationMethods::ClassMethods
13
+ extend ActiveSupport::Concern
13
14
 
14
15
  def limit_value
15
16
  rows
@@ -28,6 +29,15 @@ module Europeana
28
29
  docs.first.model_name
29
30
  end
30
31
 
32
+ def max_pages
33
+ (defined?(@_max_pages) && @_max_pages) || (1000 / limit_value)
34
+ end
35
+
36
+ def total_pages
37
+ total = super
38
+ total > max_pages ? max_pages : total
39
+ end
40
+
31
41
  def next_page
32
42
  current_page + 1 unless last_page?
33
43
  end
@@ -0,0 +1,26 @@
1
+ module Europeana
2
+ module Blacklight
3
+ ##
4
+ # URL routing for Blacklight
5
+ module Routes
6
+ extend ActiveSupport::Concern
7
+
8
+ included do |klass|
9
+ klass.default_route_sets -= [:solr_document]
10
+ unless klass.default_route_sets.include?(:europeana_document)
11
+ klass.default_route_sets += [:europeana_document]
12
+ end
13
+ end
14
+
15
+ def europeana_document(primary_resource)
16
+ add_routes do |options|
17
+ args = { only: [:show] }
18
+ args[:constraints] = options[:constraints] if options[:constraints]
19
+
20
+ post 'record/*id/track', args.merge(to: "#{primary_resource}#track", as: 'track_document')
21
+ get 'record/*id', args.merge(to: "#{primary_resource}#show", as: 'document')
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -5,21 +5,26 @@ module Europeana
5
5
  class SearchBuilder < ::Blacklight::SearchBuilder
6
6
  require 'europeana/blacklight/search_builder/facet_pagination'
7
7
  require 'europeana/blacklight/search_builder/more_like_this'
8
+ require 'europeana/blacklight/search_builder/overlay_params'
8
9
  require 'europeana/blacklight/search_builder/ranges'
9
10
 
11
+ attr_accessor :default_processor_chain
10
12
  self.default_processor_chain = [
11
13
  :default_api_parameters, :add_profile_to_api,
12
- :add_query_to_api, :add_qf_to_api, :add_facet_qf_to_api,
13
- :add_reusability_to_api, :add_facetting_to_api, :add_paging_to_api,
14
+ :add_query_to_api, :add_qf_to_api, :add_facet_qf_to_api, :add_query_facet_to_api,
15
+ :add_standalone_facets_to_api, :add_facetting_to_api, :add_paging_to_api,
14
16
  :add_sorting_to_api
15
17
  ]
16
18
 
17
19
  include FacetPagination
18
20
  include MoreLikeThis
19
21
  include Ranges
22
+ include OverlayParams
20
23
 
21
24
  delegate :to_query, to: :to_hash
22
25
 
26
+ STANDALONE_FACETS = %w(COLOURPALETTE MEDIA REUSABILITY)
27
+
23
28
  ##
24
29
  # Start with general defaults from BL config. Need to use custom
25
30
  # merge to dup values, to avoid later mutating the original by mistake.
@@ -87,23 +92,47 @@ module Europeana
87
92
  def add_facet_qf_to_api(api_parameters)
88
93
  return unless blacklight_params[:f]
89
94
 
90
- salient_facets = blacklight_params[:f].reject do |k, _v|
91
- k == 'REUSABILITY'
95
+ salient_facets = blacklight_params[:f].select do |k, _v|
96
+ !STANDALONE_FACETS.include?(k) && api_request_facet_fields.keys.include?(k)
92
97
  end
93
98
 
94
99
  salient_facets.each_pair do |facet_field, value_list|
95
100
  Array(value_list).reject(&:blank?).each do |value|
96
101
  api_parameters[:qf] ||= []
97
- api_parameters[:qf] << "#{facet_field}:\"#{value}\""
102
+ if Europeana::API::Search::Fields::MEDIA.include?(facet_field)
103
+ api_parameters[:qf] << "#{facet_field}:#{value}"
104
+ else
105
+ api_parameters[:qf] << "#{facet_field}:\"#{value}\""
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ ##
112
+ # Filter results by a query facet
113
+ def add_query_facet_to_api(_api_parameters)
114
+ return unless blacklight_params[:f]
115
+
116
+ salient_facets = blacklight_params[:f].select do |k, _v|
117
+ facet = blacklight_config.facet_fields[k]
118
+ facet.query && (facet.include_in_request || (facet.include_in_request.nil? && blacklight_config.add_facet_fields_to_solr_request))
119
+ end
120
+
121
+ salient_facets.each_pair do |facet_field, value_list|
122
+ Array(value_list).reject(&:blank?).each do |value|
123
+ with_overlay_params(blacklight_config.facet_fields[facet_field].query[value][:fq])
98
124
  end
99
125
  end
100
126
  end
101
127
 
102
- # bizarrely, reusability is a distinct API param, even though it
103
- # is returned with the facets in a search response
104
- def add_reusability_to_api(api_parameters)
105
- if blacklight_params[:f] && blacklight_params[:f]['REUSABILITY']
106
- api_parameters[:reusability] = blacklight_params[:f]['REUSABILITY'].join(',')
128
+ ##
129
+ # Some facets need to be filtered as distinct API params, even though
130
+ # they are returned with the facets in a search response
131
+ def add_standalone_facets_to_api(api_parameters)
132
+ STANDALONE_FACETS.each do |field|
133
+ if blacklight_params[:f] && blacklight_params[:f][field]
134
+ api_parameters[field.downcase.to_sym] = blacklight_params[:f][field].join(',')
135
+ end
107
136
  end
108
137
  end
109
138
 
@@ -115,7 +144,9 @@ module Europeana
115
144
  # @see http://labs.europeana.eu/api/search/#individual-facets
116
145
  # @see http://labs.europeana.eu/api/search/#offset-and-limit-of-facets
117
146
  def add_facetting_to_api(api_parameters)
118
- api_parameters[:facet] = api_request_facet_fields.keys.join(',')
147
+ api_parameters[:facet] = api_request_facet_fields.keys.map do |field|
148
+ Europeana::API::Search::Fields::MEDIA.include?(field) ? 'DEFAULT' : field
149
+ end.uniq.join(',')
119
150
 
120
151
  api_request_facet_fields.each do |field_name, facet|
121
152
  api_parameters[:"f.#{facet.field}.facet.limit"] = (facet_limit_for(field_name) + 1) if facet_limit_for(field_name)
@@ -0,0 +1,39 @@
1
+ module Europeana
2
+ module Blacklight
3
+ class SearchBuilder
4
+ ##
5
+ # "Overlay" params do not replace others, but are combined with them, into
6
+ # multiple values for those param keys
7
+ module OverlayParams
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ default_processor_chain << :add_overlay_params_to_api
12
+ end
13
+
14
+ def with_overlay_params(overlay_params = {})
15
+ @overlay_params ||= []
16
+ @overlay_params << overlay_params
17
+ self
18
+ end
19
+
20
+ def add_overlay_params_to_api(api_parameters)
21
+ return unless @overlay_params
22
+
23
+ @overlay_params.each do |param_set|
24
+ param_set.each_pair do |k, v|
25
+ k = k.to_sym
26
+ if api_parameters.key?(k)
27
+ api_parameters[k] = [api_parameters[k]].flatten # in case it's not an Array
28
+ else
29
+ api_parameters[k] = []
30
+ end
31
+ api_parameters[k] += [v]
32
+ api_parameters[k] = api_parameters[k].flatten.uniq
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -2,7 +2,7 @@ module Europeana
2
2
  module Blacklight
3
3
  class SearchBuilder
4
4
  ##
5
- # Search builder with content channel qf
5
+ # Search builder methods for ranges
6
6
  module Ranges
7
7
  extend ActiveSupport::Concern
8
8
 
@@ -0,0 +1,41 @@
1
+ module Europeana
2
+ module Blacklight
3
+ ##
4
+ # Local overrides for {Blacklight::SearchHelper}
5
+ module SearchHelper
6
+ def previous_and_next_document_params(index, window = 1)
7
+ api_params = {}
8
+
9
+ if index > 1
10
+ api_params[:start] = index - window # get one before
11
+ api_params[:rows] = 2 * window + 1 # and one after
12
+ else
13
+ api_params[:start] = 1 # there is no previous doc
14
+ api_params[:rows] = 2 * window # but there should be one after
15
+ end
16
+
17
+ api_params
18
+ end
19
+
20
+ private
21
+
22
+ def fetch_many(ids = [], *args)
23
+ if args.length == 1
24
+ user_params = params
25
+ extra_controller_params = args.first || {}
26
+ else
27
+ user_params, extra_controller_params = args
28
+ user_params ||= params
29
+ extra_controller_params ||= {}
30
+ end
31
+
32
+ id_query = ids.map { |id| "europeana_id:\"/#{id}\"" }.join(' OR ')
33
+
34
+ query = search_builder.with(user_params).where(id_query)
35
+ api_response = repository.search(query.merge(extra_controller_params))
36
+
37
+ [api_response, api_response.documents]
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,5 +1,5 @@
1
1
  module Europeana
2
2
  module Blacklight
3
- VERSION = '0.2.7'
3
+ VERSION = '0.3.0'
4
4
  end
5
5
  end
@@ -0,0 +1,28 @@
1
+ module Europeana
2
+ module Blacklight
3
+ class Install < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ argument :europeana_api_key, type: :string
7
+ argument :controller_name, type: :string, default: 'catalog'
8
+
9
+ def disable_rsolr_gem
10
+ comment_lines('Gemfile', /gem 'rsolr'/)
11
+ end
12
+
13
+ def bundle_install
14
+ Bundler.with_clean_env do
15
+ run 'bundle install'
16
+ end
17
+ end
18
+
19
+ def create_configuration_files
20
+ template 'config/blacklight.yml', 'config/blacklight.yml'
21
+ end
22
+
23
+ def generate_controller
24
+ template 'catalog_controller.rb', "app/controllers/#{controller_name}_controller.rb"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,81 @@
1
+ # -*- encoding : utf-8 -*-
2
+ class <%= controller_name.classify %>Controller < ApplicationController
3
+ include Europeana::Blacklight::Catalog
4
+
5
+ configure_blacklight do |config|
6
+ # Europeana API caching
7
+ config.europeana_api_cache = Rails.cache
8
+ config.europeana_api_cache_expires_in = 24.hours
9
+
10
+ # Default parameters to send to Europeana for all search-like requests.
11
+ # @see SolrHelper#solr_search_params
12
+ config.default_solr_params = {
13
+ rows: 24
14
+ }
15
+
16
+ # items to show per page, each number in the array represents another
17
+ # option to choose from.
18
+ config.per_page = [12, 24, 48, 96]
19
+
20
+ # Field configuration for search results/index views
21
+ config.index.title_field = 'title'
22
+ config.index.display_type_field = 'type'
23
+ config.index.timestamp_field = nil # Europeana's is in microseconds
24
+ config.index.thumbnail_field = 'edmPreview'
25
+
26
+ # Fields to be displayed in the index (search results) view
27
+ # The ordering of the field names is the order of the display
28
+ # @see http://labs.europeana.eu/api/data-fields
29
+ config.add_index_field 'edmAgentLabelLangAware', label: 'Creator'
30
+ config.add_index_field 'dcDescription', label: 'Description'
31
+ config.add_index_field 'edmConceptPrefLabelLangAware',
32
+ separator: '; ', limit: 4, label: 'Subject'
33
+ config.add_index_field 'year', label: 'Year'
34
+ config.add_index_field 'dataProvider', label: 'Data provider'
35
+
36
+ # Facet fields in the order they should be displayed.
37
+ # @see http://labs.europeana.eu/api/search#individual-facets
38
+ config.add_facet_field 'TYPE'
39
+ config.add_facet_field 'REUSABILITY'
40
+ config.add_facet_field 'COUNTRY'
41
+ config.add_facet_field 'LANGUAGE'
42
+ config.add_facet_field 'PROVIDER'
43
+ config.add_facet_field 'DATA_PROVIDER'
44
+ config.add_facet_field 'YEAR'
45
+ config.add_facet_field 'RIGHTS'
46
+
47
+ # Send all facet field names to the API.
48
+ config.add_facet_fields_to_solr_request!
49
+
50
+ # Fields to be displayed in the object view, in the order of display.
51
+ config.add_show_field 'agents.prefLabel', label: 'Agents'
52
+ config.add_show_field 'agents.begin', label: 'Start date'
53
+ config.add_show_field 'agents.end', label: 'End date'
54
+ config.add_show_field 'proxies.dcType', label: 'Type'
55
+ config.add_show_field 'proxies.dcCreator', label: 'Creator'
56
+ config.add_show_field 'proxies.dcFormat', label: 'Format'
57
+ config.add_show_field 'proxies.dcIdentifier', label: 'Identifier'
58
+ config.add_show_field 'proxies.dctermsCreated', label: 'Created'
59
+ config.add_show_field 'aggregations.webResources.dctermsCreated', label: 'Created'
60
+ config.add_show_field 'proxies.dctermsExtent', label: 'Extent'
61
+ config.add_show_field 'europeanaAggregation.edmCountry', label: 'Country'
62
+ config.add_show_field 'edmDatasetName', label: 'Dataset name'
63
+ config.add_show_field 'aggregations.edmIsShownAt', label: 'Is shown at'
64
+ config.add_show_field 'aggregations.edmIsShownBy', label: 'Is shown by'
65
+ config.add_show_field 'europeanaAggregation.edmLanguage', label: 'Language'
66
+ config.add_show_field 'europeanaAggregation.edmPreview', label: 'Preview'
67
+ config.add_show_field 'aggregations.edmProvider', label: 'Provider'
68
+ config.add_show_field 'aggregations.edmDataProvider', label: 'Data provider'
69
+ config.add_show_field 'aggregations.edmRights', label: 'Rights'
70
+ config.add_show_field 'places.latitude', label: 'Latitude'
71
+ config.add_show_field 'places.longitude', label: 'Longitude'
72
+ config.add_show_field 'type', label: 'Type'
73
+ config.add_show_field 'year', label: 'Year'
74
+
75
+ # "fielded" search configuration.
76
+ config.add_search_field('', label: 'All Fields')
77
+ %w(title who what when where subject).each do |field_name|
78
+ config.add_search_field(field_name)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,8 @@
1
+ default: &default
2
+ europeana_api_key: <%= europeana_api_key %>
3
+ development:
4
+ <<: *default
5
+ test:
6
+ <<: *default
7
+ production:
8
+ <<: *default
metadata CHANGED
@@ -1,15 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: europeana-blacklight
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Doe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-28 00:00:00.000000000 Z
11
+ date: 2015-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '5.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '5.0'
13
33
  - !ruby/object:Gem::Dependency
14
34
  name: blacklight
15
35
  requirement: !ruby/object:Gem::Requirement
@@ -36,14 +56,14 @@ dependencies:
36
56
  requirements:
37
57
  - - "~>"
38
58
  - !ruby/object:Gem::Version
39
- version: 0.3.4
59
+ version: 0.4.0
40
60
  type: :runtime
41
61
  prerelease: false
42
62
  version_requirements: !ruby/object:Gem::Requirement
43
63
  requirements:
44
64
  - - "~>"
45
65
  - !ruby/object:Gem::Version
46
- version: 0.3.4
66
+ version: 0.4.0
47
67
  - !ruby/object:Gem::Dependency
48
68
  name: iso-639
49
69
  requirement: !ruby/object:Gem::Requirement
@@ -114,6 +134,34 @@ dependencies:
114
134
  - - "~>"
115
135
  - !ruby/object:Gem::Version
116
136
  version: '3.2'
137
+ - !ruby/object:Gem::Dependency
138
+ name: shoulda-matchers
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '2.8'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '2.8'
151
+ - !ruby/object:Gem::Dependency
152
+ name: webmock
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '1.21'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '1.21'
117
165
  description:
118
166
  email:
119
167
  - richard.doe@rwdit.net
@@ -129,8 +177,10 @@ files:
129
177
  - ".travis.yml"
130
178
  - Gemfile
131
179
  - LICENSE.md
180
+ - QUICKSTART.md
132
181
  - README.md
133
182
  - Rakefile
183
+ - app/controllers/concerns/europeana/blacklight/catalog.rb
134
184
  - bin/console
135
185
  - bin/setup
136
186
  - europeana-blacklight.gemspec
@@ -140,16 +190,23 @@ files:
140
190
  - lib/europeana/blacklight/document/more_like_this.rb
141
191
  - lib/europeana/blacklight/document/relations.rb
142
192
  - lib/europeana/blacklight/document_presenter.rb
193
+ - lib/europeana/blacklight/engine.rb
143
194
  - lib/europeana/blacklight/facet_paginator.rb
144
195
  - lib/europeana/blacklight/response.rb
145
196
  - lib/europeana/blacklight/response/facets.rb
146
197
  - lib/europeana/blacklight/response/more_like_this.rb
147
198
  - lib/europeana/blacklight/response/pagination.rb
199
+ - lib/europeana/blacklight/routes.rb
148
200
  - lib/europeana/blacklight/search_builder.rb
149
201
  - lib/europeana/blacklight/search_builder/facet_pagination.rb
150
202
  - lib/europeana/blacklight/search_builder/more_like_this.rb
203
+ - lib/europeana/blacklight/search_builder/overlay_params.rb
151
204
  - lib/europeana/blacklight/search_builder/ranges.rb
205
+ - lib/europeana/blacklight/search_helper.rb
152
206
  - lib/europeana/blacklight/version.rb
207
+ - lib/generators/europeana/blacklight/install_generator.rb
208
+ - lib/generators/europeana/blacklight/templates/catalog_controller.rb
209
+ - lib/generators/europeana/blacklight/templates/config/blacklight.yml
153
210
  homepage: https://github.com/europeana/europeana-blacklight
154
211
  licenses:
155
212
  - EUPL 1.1