europeana-blacklight 0.2.7 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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