diff --git a/Gemfile b/Gemfile index 6f49549..ee98b01 100644 --- a/Gemfile +++ b/Gemfile @@ -1,17 +1,16 @@ -source 'https://rubygems.org' +source "https://rubygems.org" git_source(:github) do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") "https://github.com/#{repo_name}.git" end - # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '~> 5.2.0' +gem "rails", "~> 5.2.0" # Use sqlite3 as the database for Active Record -gem 'sqlite3' +gem "sqlite3" # Use Puma as the app server -gem 'puma', '~> 3.7' +gem "puma", "~> 3.7" # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder # gem 'jbuilder', '~> 2.5' # Use Redis adapter to run Action Cable in production @@ -25,25 +24,26 @@ gem 'puma', '~> 3.7' # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible # gem 'rack-cors' -gem 'bootsnap', require: false +gem "bootsnap", require: false group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console - gem 'byebug', platform: :mri - gem 'rspec', '~> 3.10.0' - gem 'rspec-rails', '~> 5.0.0' + gem "byebug", platform: :mri + gem "rspec", "~> 3.10.0" + gem "rspec-rails", "~> 5.0.0" + gem "standard" end group :development do - gem 'listen', '>= 3.0.5', '< 3.2' + gem "listen", ">= 3.0.5", "< 3.2" # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring - gem 'spring' - gem 'spring-watcher-listen', '~> 2.0.0' + gem "spring" + gem "spring-watcher-listen", "~> 2.0.0" end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] # using rest-client because I've had far more luck than the # stlib net/http -gem 'rest-client', '>= 2.1.0.rc1', '< 2.2' +gem "rest-client", ">= 2.1.0.rc1", "< 2.2" diff --git a/Gemfile.lock b/Gemfile.lock index 5530bf7..0615b4e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -43,6 +43,7 @@ GEM minitest (~> 5.1) tzinfo (~> 1.1) arel (9.0.0) + ast (2.4.2) bootsnap (1.8.1) msgpack (~> 1.0) builder (3.2.4) @@ -84,6 +85,9 @@ GEM nokogiri (1.12.4) mini_portile2 (~> 2.6.1) racc (~> 1.4) + parallel (1.21.0) + parser (3.0.2.0) + ast (~> 2.4.1) puma (3.12.6) racc (1.5.2) rack (2.2.3) @@ -113,15 +117,18 @@ GEM method_source rake (>= 0.8.7) thor (>= 0.19.0, < 2.0) + rainbow (3.0.0) rake (13.0.6) rb-fsevent (0.11.0) rb-inotify (0.10.1) ffi (~> 1.0) + regexp_parser (2.1.1) rest-client (2.1.0) http-accept (>= 1.7.0, < 2.0) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) + rexml (3.2.5) rspec (3.10.0) rspec-core (~> 3.10.0) rspec-expectations (~> 3.10.0) @@ -143,6 +150,21 @@ GEM rspec-mocks (~> 3.10) rspec-support (~> 3.10) rspec-support (3.10.2) + rubocop (1.22.3) + parallel (~> 1.10) + parser (>= 3.0.0.0) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml + rubocop-ast (>= 1.12.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.12.0) + parser (>= 3.0.1.1) + rubocop-performance (1.11.5) + rubocop (>= 1.7.0, < 2.0) + rubocop-ast (>= 0.4.0) + ruby-progressbar (1.11.0) ruby_dep (1.5.0) spring (2.1.1) spring-watcher-listen (2.0.1) @@ -156,6 +178,9 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) sqlite3 (1.4.2) + standard (1.4.0) + rubocop (= 1.22.3) + rubocop-performance (= 1.11.5) thor (1.1.0) thread_safe (0.3.6) tzinfo (1.2.9) @@ -163,6 +188,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.7) + unicode-display_width (2.1.0) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -182,6 +208,7 @@ DEPENDENCIES spring spring-watcher-listen (~> 2.0.0) sqlite3 + standard tzinfo-data BUNDLED WITH diff --git a/README.md b/README.md index e8b2d13..bd4aebc 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Apium is an API to access all public Center for Digital Research in the Humaniti This project is licensed under the terms of the [MIT license](LICENSE.md). -## Run tests +## Run tests and linter -Run tests with `rails spec` or `rspec` +Run tests with `rails spec` or `rspec`. + +Run linter with `standardrb`. diff --git a/Rakefile b/Rakefile index e85f913..9a5ea73 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,6 @@ # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. -require_relative 'config/application' +require_relative "config/application" Rails.application.load_tasks diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0404329..c8ce473 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,14 +1,12 @@ -require 'rest-client' +require "rest-client" class ApplicationController < ActionController::API - - def post_search(json, error_method=method(:display_error)) - res = RestClient.post("#{ES_URI}/_search", json.to_json, { "content-type" => "json" }) - raise - return JSON.parse(res.body) + def post_search(json, error_method = method(:display_error)) + res = RestClient.post("#{ES_URI}/_search", json.to_json, {"content-type" => "json"}) + raise JSON.parse(res.body) rescue => e error_method.call(e, json) - return nil + nil end # I am so pleased that this works @@ -30,5 +28,4 @@ def display_error(error, req_body) } })) and return end - end diff --git a/app/controllers/collection_controller.rb b/app/controllers/collection_controller.rb index 154caf8..977460c 100644 --- a/app/controllers/collection_controller.rb +++ b/app/controllers/collection_controller.rb @@ -1,5 +1,4 @@ class CollectionController < ApplicationController - def index res = SearchService.new(ES_URI, params, request.fullpath) .search_collections @@ -24,5 +23,4 @@ def show } }) end - end diff --git a/app/controllers/default_controller.rb b/app/controllers/default_controller.rb index c217c06..3d4e0a6 100644 --- a/app/controllers/default_controller.rb +++ b/app/controllers/default_controller.rb @@ -1,16 +1,13 @@ -=begin -CDRH API - -API to access all public Center for Digital Research in the Humanities resources - -OpenAPI spec version: 0.1.0 - -Generated by: https://github.com/swagger-api/swagger-codegen.git - -=end +# CDRH API +# +# API to access all public Center for Digital Research in the Humanities resources +# +# OpenAPI spec version: 0.1.0 +# +# Generated by: https://github.com/swagger-api/swagger-codegen.git +# class DefaultController < ApplicationController - def root json = JSON.pretty_generate({ "req" => { @@ -35,14 +32,14 @@ def root "/item/{id}", "/collections", "/collection/{collection}/info", - "/collection/{collection}/item/{id}", + "/collection/{collection}/item/{id}" ], # TODO get descriptions from ES mapping? # would rather not manually type in somewhere # or possibly by reading in the YML file for the # schema definitions, which would prevent project # specific fields from popping up in this result - "fields" => {}, + "fields" => {} } } }) diff --git a/app/controllers/item_controller.rb b/app/controllers/item_controller.rb index 6e98239..abcc099 100644 --- a/app/controllers/item_controller.rb +++ b/app/controllers/item_controller.rb @@ -1,6 +1,4 @@ class ItemController < ApplicationController - - def index if params["collection"].present? params["f"] = [] if params["f"].blank? @@ -18,5 +16,4 @@ def show code = res.dig("res", "code") render status: code, json: JSON.pretty_generate(res) end - end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 286b223..3c34c81 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,4 +1,4 @@ class ApplicationMailer < ActionMailer::Base - default from: 'from@example.com' - layout 'mailer' + default from: "from@example.com" + layout "mailer" end diff --git a/app/services/search_coll_res.rb b/app/services/search_coll_res.rb index 1958f4b..759ecfa 100644 --- a/app/services/search_coll_res.rb +++ b/app/services/search_coll_res.rb @@ -1,5 +1,4 @@ class SearchCollRes - @@collections = ["aggregations", "collections", "buckets"] def initialize(res) @@ -10,26 +9,25 @@ def build_response route_paths = Rails.application.routes.url_helpers collections = @body.dig(*@@collections) - collections = [] if !collections - collections.map! do |coll| - { - "collection_name" => coll["key"], - "description" => "TODO", - "image_id" => "TODO", - "uri" => "TODO", - "collection" => coll["key"], - "item_count" => coll["doc_count"], - "endpoint" => route_paths.collection_path(coll["key"]) - } - end + collections = [] if !collections + collections.map! do |coll| + { + "collection_name" => coll["key"], + "description" => "TODO", + "image_id" => "TODO", + "uri" => "TODO", + "collection" => coll["key"], + "item_count" => coll["doc_count"], + "endpoint" => route_paths.collection_path(coll["key"]) + } + end - return { - "code" => 200, - "info" => { - "count" => collections.length, - "collections" => collections - } + { + "code" => 200, + "info" => { + "count" => collections.length, + "collections" => collections } + } end - end diff --git a/app/services/search_item_req.rb b/app/services/search_item_req.rb index 117462d..812a325 100644 --- a/app/services/search_item_req.rb +++ b/app/services/search_item_req.rb @@ -1,5 +1,4 @@ class SearchItemReq - # whether request comes in as a pipe character or encoded pipe # make sure that it is being split correctly @@filter_separator = Regexp.new(SETTINGS["filter_separator"]) @@ -21,7 +20,7 @@ def build_request "from" => start, "highlight" => {}, "size" => num, - "query" => {}, + "query" => {} } bool = {} @@ -51,7 +50,7 @@ def build_request # add bool to request body req["query"]["bool"] = bool - return req + req end def self.escape_chars(query) @@ -61,8 +60,8 @@ def self.escape_chars(query) # those characters interfered with elasticsearch multifield searching # Also removed * and ? from the list because escaping those # characters meant queries with uncertainty couldn't be done - escaped_characters = Regexp.escape('\\+-&|!{}[]^~\/') - query.gsub(/([#{escaped_characters}])/, '\\\\\1') + escaped_characters = Regexp.escape("\\+-&|!{}[]^~\\/") + query.gsub(/([#{escaped_characters}])/, "\\\\\\1") end def facets @@ -101,7 +100,7 @@ def facets "interval" => interval, "format" => formatted, "min_doc_count" => 1, - "order" => { f_type => dir }, + "order" => {f_type => dir} } } # if nested, has extra syntax @@ -115,7 +114,7 @@ def facets f => { "terms" => { "field" => f, - "order" => { type => dir }, + "order" => {type => dir}, "size" => size } } @@ -130,13 +129,13 @@ def facets # "num_partitions" => 10 # }, "field" => f, - "order" => { type => dir }, + "order" => {type => dir}, "size" => size } } end end - return aggs + aggs end def filters @@ -144,7 +143,7 @@ def filters fields = Array.wrap(@params["f"]) # each filter should be length 3 for field, type 1, type 2 # (type 2 will only be used for dates) - filters = fields.map {|f| f.split(@@filter_separator, 3) } + filters = fields.map { |f| f.split(@@filter_separator, 3) } filters.each do |filter| # NESTED FIELD FILTER if filter[0].include?(".") @@ -156,7 +155,7 @@ def filters "query" => { "term" => { # Remove CR's added by hidden input field values with returns - filter[0] => filter[1].gsub(/\r/, "") + filter[0] => filter[1].delete("\r") } } } @@ -182,7 +181,6 @@ def filters elsif filter.length == 2 start = filter[0] stop = filter[1] - else # TODO how to raise error here but not render twice? # redirect to error action? end @@ -195,7 +193,7 @@ def filters field => { "gte" => start, "lte" => stop, - "format" => "yyyy-MM-dd", + "format" => "yyyy-MM-dd" } } } @@ -203,10 +201,10 @@ def filters # TRADITIONAL FILTERS else # Remove CR's added by hidden input field values with returns - filter_list << { "term" => { filter[0] => filter[1].gsub(/\r/, "") } } + filter_list << {"term" => {filter[0] => filter[1].delete("\r")}} end end - return filter_list + filter_list end def highlights @@ -217,7 +215,7 @@ def highlights if @params["hl"] != "false" # include "text" highlighting by default hl["fields"] = { - "text" => { "fragment_size" => hl_chars, "number_of_fragments" => hl_num } + "text" => {"fragment_size" => hl_chars, "number_of_fragments" => hl_num} } if @params["hl_fl"].present? @params["hl_fl"].split(@@fl_separator).each do |field| @@ -228,20 +226,19 @@ def highlights end end end - return hl + hl end def sort sort_obj = [] - sort_param = nil - if @params["sort"].blank? + sort_param = if @params["sort"].blank? if @params["q"].present? - sort_param = ["_score"] + ["_score"] else - sort_param = [SETTINGS["sort_fl"]] + [SETTINGS["sort_fl"]] end else - sort_param = Array.wrap(@params["sort"]) + Array.wrap(@params["sort"]) end sort_param.each do |sort| @@ -268,24 +265,23 @@ def sort # note: does not support nested fields inside of nested fields if term.include?(".") path = term.split(".").first - sort_setting[term]["nested"] = { "path" => path } + sort_setting[term]["nested"] = {"path" => path} end sort_obj << sort_setting end - end - return sort_obj + sort_obj end def source all = @params["fl"].split(@@fl_separator) blist, wlist = all.partition { |f| f.start_with?("!") } - blist.map! { |f| f[1..-1] } + blist.map! { |f| f[1..] } criteria = {} criteria["includes"] = wlist if !wlist.empty? criteria["excludes"] = blist if !blist.empty? - return criteria + criteria end def text_search @@ -307,9 +303,8 @@ def text_search must["query_string"]["default_field"] = "text" end else - must = { "match_all" => {} } + must = {"match_all" => {}} end - return must + must end - end diff --git a/app/services/search_item_res.rb b/app/services/search_item_res.rb index 4a2c5a7..7cd48f9 100644 --- a/app/services/search_item_res.rb +++ b/app/services/search_item_res.rb @@ -1,5 +1,4 @@ class SearchItemRes - attr_reader :body, :debug @@count = ["hits", "total"] @@ -7,7 +6,7 @@ class SearchItemRes @@item = ["hits", "hits", 0, "_source"] @@items = ["hits", "hits"] - def initialize(res, debug=false) + def initialize(res, debug = false) @body = res @debug = debug end @@ -18,23 +17,23 @@ def build_response items = combine_highlights facets = reformat_facets - return { + { "code" => 200, "count" => count, "facets" => facets, - "items" => items, + "items" => items } end def combine_highlights hits = @body.dig(*@@items) if hits - return hits.map do |hit| + hits.map do |hit| hit["_source"]["highlight"] = hit["highlight"] || {} hit["_source"] end else - return [] + [] end end @@ -44,14 +43,9 @@ def reformat_facets formatted = {} facets.each do |field, info| formatted[field] = {} - buckets = {} # nested fields do not have buckets # at this level in the response structure - if info.has_key?("buckets") - buckets = info["buckets"] - else - buckets = info.dig(field, "buckets") - end + buckets = info.key?("buckets") ? info["buckets"] : info.dig(field, "buckets") if buckets buckets.each do |b| # dates return in wonktastic ways, so grab key_as_string instead of gibberish number @@ -64,10 +58,9 @@ def reformat_facets formatted[field] = {} end end - return formatted + formatted else - return {} + {} end end - end diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 8724644..23e1ecf 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -1,20 +1,19 @@ -require 'rest-client' +require "rest-client" class SearchService - attr_accessor :url, :params, :user_req - def initialize(url, params={}, user_req) + def initialize(url, params = {}, user_req = "") @url = url @params = params @user_req = user_req end def post(url_ending, json) - res = RestClient.post("#{@url}/#{url_ending}", json.to_json, { "content-type" => "json" } ) - return JSON.parse(res.body) + res = RestClient.post("#{@url}/#{url_ending}", json.to_json, {"content-type" => "json"}) + JSON.parse(res.body) rescue => e - return e + e end def search_collections @@ -30,7 +29,7 @@ def search_collections "size" => 0 } raw_res = post("_search", req) - if raw_res.class == RuntimeError + if raw_res.instance_of?(RuntimeError) on_error(raw_res, req) else res = build_collections_response(raw_res) @@ -44,20 +43,20 @@ def search_item(id) "bool" => { "must" => [ { - "term" => { "identifier" => id } + "term" => {"identifier" => id} } ] } } } if @params["collection"].present? - req["query"]["bool"]["must"] << { "term" => { "collection" => @params["collection"] } } + req["query"]["bool"]["must"] << {"term" => {"collection" => @params["collection"]}} end raw_res = post("_search", req) - if raw_res.class == RuntimeError + if raw_res.instance_of?(RuntimeError) on_error(raw_res, req) - elsif raw_res.class == RestClient::BadRequest + elsif raw_res.instance_of?(RestClient::BadRequest) on_error(JSON.parse(raw_res.response), req) else res = build_item_response(raw_res) @@ -68,9 +67,9 @@ def search_item(id) def search_items req = build_item_request raw_res = post("_search", req) - if raw_res.class == RuntimeError + if raw_res.instance_of?(RuntimeError) on_error(raw_res.inspect, req) - elsif raw_res.class == RestClient::BadRequest + elsif raw_res.instance_of?(RestClient::BadRequest) on_error(JSON.parse(raw_res.response), req) else res = build_item_response(raw_res) @@ -80,7 +79,7 @@ def search_items protected - def on_error(error_msg, req, friendly_msg="Something went wrong") + def on_error(error_msg, req, friendly_msg = "Something went wrong") { "req" => { "query_string" => @user_req, @@ -108,7 +107,7 @@ def on_success(req, res) if @params["debug"].present? json["req"]["query_obj"] = req end - return json + json end def build_collections_response(res) @@ -122,5 +121,4 @@ def build_item_request def build_item_response(res) SearchItemRes.new(res).build_response end - end diff --git a/config.ru b/config.ru index f7ba0b5..441e6ff 100644 --- a/config.ru +++ b/config.ru @@ -1,5 +1,5 @@ # This file is used by Rack-based servers to start the application. -require_relative 'config/environment' +require_relative "config/environment" run Rails.application diff --git a/config/application.rb b/config/application.rb index c6d9b6c..e61b00f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,4 +1,4 @@ -require_relative 'boot' +require_relative "boot" require "rails" # Pick the frameworks you want: @@ -19,7 +19,6 @@ module Api class Application < Rails::Application - # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.0 diff --git a/config/boot.rb b/config/boot.rb index b9e460c..988a5dd 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,4 +1,4 @@ -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) -require 'bundler/setup' # Set up gems listed in the Gemfile. -require 'bootsnap/setup' # Speed up boot time by caching expensive operations. +require "bundler/setup" # Set up gems listed in the Gemfile. +require "bootsnap/setup" # Speed up boot time by caching expensive operations. diff --git a/config/environment.rb b/config/environment.rb index 426333b..cac5315 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,5 @@ # Load the Rails application. -require_relative 'application' +require_relative "application" # Initialize the Rails application. Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb index 4bdb9c3..72c872b 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -14,12 +14,12 @@ # Enable/disable caching. By default caching is disabled. # Run rails dev:cache to toggle caching. - if Rails.root.join('tmp', 'caching-dev.txt').exist? + if Rails.root.join("tmp", "caching-dev.txt").exist? config.action_controller.perform_caching = true config.cache_store = :memory_store config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{2.days.to_i}" + "Cache-Control" => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false @@ -44,7 +44,6 @@ # Highlight code that triggered database queries in logs. config.active_record.verbose_query_logs = true - # Raises error for missing translations # config.action_view.raise_on_missing_translations = true @@ -52,14 +51,11 @@ # routes, locales, etc. This feature depends on the listen gem. config.file_watcher = ActiveSupport::EventedFileUpdateChecker - - # LOCAL # Custom dev env logger to empty log more frequently config.logger = ActiveSupport::TaggedLogging.new( ActiveSupport::Logger.new(File.join(Rails.root.to_s, "log", "development.log"), # Keep one old log file, rotate after size reaches 32 MB - 1, 32 * 1024 * 1024 - ) + 1, 32 * 1024 * 1024) ) end diff --git a/config/environments/production.rb b/config/environments/production.rb index d8f6c28..3eef1e0 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -11,7 +11,7 @@ config.eager_load = true # Full error reports are disabled and caching is turned on. - config.consider_all_requests_local = false + config.consider_all_requests_local = false config.action_controller.perform_caching = true # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] @@ -20,7 +20,7 @@ # Disable serving static files from the `/public` folder by default since # Apache or NGINX already handles this. - config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.action_controller.asset_host = 'http://assets.example.com' @@ -42,14 +42,14 @@ # Handle STS here instead of Apache, or Rails duplicates header contents # Also unset cache-control header in HTTPS vhost for same reason - config.ssl_options = { hsts: { preload: true } } + config.ssl_options = {hsts: {preload: true}} # Use the lowest log level to ensure availability of diagnostic information # when problems arise. config.log_level = :debug # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] + config.log_tags = [:request_id] # Use a different cache store in production. # config.cache_store = :mem_cache_store @@ -79,9 +79,9 @@ # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') if ENV["RAILS_LOG_TO_STDOUT"].present? - logger = ActiveSupport::Logger.new(STDOUT) + logger = ActiveSupport::Logger.new($stdout) logger.formatter = config.log_formatter - config.logger = ActiveSupport::TaggedLogging.new(logger) + config.logger = ActiveSupport::TaggedLogging.new(logger) end # Do not dump schema after migrations. diff --git a/config/environments/test.rb b/config/environments/test.rb index 0a38fd3..55b1c28 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -15,11 +15,11 @@ # Configure public file server for tests with Cache-Control for performance. config.public_file_server.enabled = true config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{1.hour.to_i}" + "Cache-Control" => "public, max-age=#{1.hour.to_i}" } # Show full error reports and disable caching. - config.consider_all_requests_local = true + config.consider_all_requests_local = true config.action_controller.perform_caching = false # Raise exceptions instead of rendering exception templates. diff --git a/config/initializers/config.rb b/config/initializers/config.rb index f38f1e0..c2db0c6 100644 --- a/config/initializers/config.rb +++ b/config/initializers/config.rb @@ -1,6 +1,6 @@ config_path = Rails.root.join("config", "config.yml") config = YAML.load_file(config_path)[Rails.env] -ES_URI = "#{config['es_path']}/#{config['es_index']}" +ES_URI = "#{config["es_path"]}/#{config["es_index"]}" METADATA = config["metadata"] SETTINGS = config["settings"] diff --git a/config/initializers/new_framework_defaults.rb b/config/initializers/new_framework_defaults.rb index e943ba9..bca7250 100644 --- a/config/initializers/new_framework_defaults.rb +++ b/config/initializers/new_framework_defaults.rb @@ -12,4 +12,4 @@ Rails.application.config.active_record.belongs_to_required_by_default = true # Configure SSL options to enable HSTS with subdomains. Previous versions had false. -#Rails.application.config.ssl_options = { hsts: { subdomains: true } } +# Rails.application.config.ssl_options = { hsts: { subdomains: true } } diff --git a/config/puma.rb b/config/puma.rb index a5eccf8..97e9bfd 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -4,16 +4,16 @@ # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +threads_count = ENV.fetch("RAILS_MAX_THREADS", 5) threads threads_count, threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # -port ENV.fetch("PORT") { 3000 } +port ENV.fetch("PORT", 3000) # Specifies the `environment` that Puma will run in. # -environment ENV.fetch("RAILS_ENV") { "development" } +environment ENV.fetch("RAILS_MAX_THREADS", 5) # Specifies the number of `workers` to boot in clustered mode. # Workers are forked webserver processes. If using threads and workers together diff --git a/config/routes.rb b/config/routes.rb index c095fd0..9056af1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,14 +1,14 @@ id_regex = /[^\/]+/ Rails.application.routes.draw do - root 'default#root', as: :home + root "default#root", as: :home # collections - get '/collections' => 'collection#index', as: :collections - get '/collection/:collection' => 'collection#show', as: :collection + get "/collections" => "collection#index", :as => :collections + get "/collection/:collection" => "collection#show", :as => :collection # items - get '/items' => 'item#index', as: :items - get '/item/:id' => 'item#show', as: :item, :constraints => { :id => id_regex } - get '/collection/:collection/item/:id' => 'item#show', as: :collection_item, :constraints => { :id => id_regex } - get '/collection/:collection/items' => 'item#index', as: :collection_items + get "/items" => "item#index", :as => :items + get "/item/:id" => "item#show", :as => :item, :constraints => {id: id_regex} + get "/collection/:collection/item/:id" => "item#show", :as => :collection_item, :constraints => {id: id_regex} + get "/collection/:collection/items" => "item#index", :as => :collection_items end diff --git a/lib/tasks/spec.rb b/lib/tasks/spec.rb index 5bb6001..5f10165 100644 --- a/lib/tasks/spec.rb +++ b/lib/tasks/spec.rb @@ -1,9 +1,9 @@ begin - require 'rspec/core/rake_task' + require "rspec/core/rake_task" RSpec::Core::RakeTask.new(:spec) - task :default => :spec + task default: :spec rescue LoadError # no rspec available end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 00345af..d761692 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,10 +1,10 @@ # This file is copied to spec/ when you run 'rails generate rspec:install' -require 'spec_helper' -ENV['RAILS_ENV'] ||= 'test' -require File.expand_path('../config/environment', __dir__) +require "spec_helper" +ENV["RAILS_ENV"] ||= "test" +require File.expand_path("../config/environment", __dir__) # Prevent database truncation if the environment is production abort("The Rails environment is running in production mode!") if Rails.env.production? -require 'rspec/rails' +require "rspec/rails" # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in diff --git a/spec/services/search_item_req_spec.rb b/spec/services/search_item_req_spec.rb index 3dc29e2..8ce4212 100644 --- a/spec/services/search_item_req_spec.rb +++ b/spec/services/search_item_req_spec.rb @@ -27,11 +27,11 @@ it "escapes quotation marks" do expect(SearchItemReq.escape_chars('"something')).to eq "\"something" expect(SearchItemReq.escape_chars('"phrase" plus "other phrase"')) - .to eq "\"phrase\" plus \"other phrase\"" + .to eq "\"phrase\" plus \"other phrase\"" end it "escapes brackets and funky stuff" do - query = '{\\+~' + query = "{\\+~" expect(SearchItemReq.escape_chars(query)).to eq "\\{\\\\\\+\\~" end end @@ -46,73 +46,77 @@ end context "no overrides" do - let(:config) {{ "facet" => [ "title" ] }} + let(:config) { {"facet" => ["title"]} } it "returns defaults" do - expect(@facets).to match({"title"=>{"terms"=>{"field"=>"title", "order"=>{"_count"=>"desc"}, "size"=>20}}}) + expect(@facets).to match({"title" => {"terms" => {"field" => "title", "order" => {"_count" => "desc"}, "size" => 20}}}) end end context "some overrides" do - let(:config) {{ - "facet_num" => 10, - "facet_sort" => "term|asc", - "facet" => [ "title", "subcategory" ] - }} + let(:config) { + { + "facet_num" => 10, + "facet_sort" => "term|asc", + "facet" => ["title", "subcategory"] + } + } it "returns defaults and overridden values" do - expect(@facets).to match({"title"=>{"terms"=>{"field"=>"title", "order"=>{"_term"=>"asc"}, "size"=>10}}, "subcategory"=>{"terms"=>{"field"=>"subcategory", "order"=>{"_term"=>"asc"}, "size"=>10}}}) + expect(@facets).to match({"title" => {"terms" => {"field" => "title", "order" => {"_term" => "asc"}, "size" => 10}}, "subcategory" => {"terms" => {"field" => "subcategory", "order" => {"_term" => "asc"}, "size" => 10}}}) end end context "no facets provided" do - let(:config) {{ - "facet_num" => 1, - "facet_sort" => "nonterm|asc", - "facet" => [] - }} + let(:config) { + { + "facet_num" => 1, + "facet_sort" => "nonterm|asc", + "facet" => [] + } + } it "is blank" do expect(@facets).to eq({}) end end context "with dates" do - let(:config) {{"facet" => ["date.year", "date"]}} + let(:config) { {"facet" => ["date.year", "date"]} } it "returns date histogram" do - expect(@facets).to match({"date.year"=>{"date_histogram"=>{"field"=>"date", "interval"=>"year", "format"=>"yyyy", "min_doc_count"=>1, "order"=>{"_count"=>"desc"}}}, "date"=>{"date_histogram"=>{"field"=>"date", "interval"=>"day", "format"=>"yyyy-MM-dd", "min_doc_count"=>1, "order"=>{"_count"=>"desc"}}}}) + expect(@facets).to match({"date.year" => {"date_histogram" => {"field" => "date", "interval" => "year", "format" => "yyyy", "min_doc_count" => 1, "order" => {"_count" => "desc"}}}, "date" => {"date_histogram" => {"field" => "date", "interval" => "day", "format" => "yyyy-MM-dd", "min_doc_count" => 1, "order" => {"_count" => "desc"}}}}) end end context "with nested field" do - let(:config) {{"facet_sort" => "term|desc", "facet" => [ "creator.name" ]}} + let(:config) { {"facet_sort" => "term|desc", "facet" => ["creator.name"]} } it "returns nested aggregations" do - expect(@facets).to match({"creator.name"=>{"nested"=>{"path"=>"creator"}, "aggs"=>{"creator.name"=>{"terms"=>{"field"=>"creator.name", "order"=>{"_term"=>"desc"}, "size"=>20}}}}}) + expect(@facets).to match({"creator.name" => {"nested" => {"path" => "creator"}, "aggs" => {"creator.name" => {"terms" => {"field" => "creator.name", "order" => {"_term" => "desc"}, "size" => 20}}}}}) end end context "with a single facet" do - let(:config) {{ "facet" => "title" }} + let(:config) { {"facet" => "title"} } it "returns facet field and defaults" do - expect(@facets).to match({"title"=>{"terms"=>{"field"=>"title", "order"=>{"_count"=>"desc"}, "size"=>20}}}) + expect(@facets).to match({"title" => {"terms" => {"field" => "title", "order" => {"_count" => "desc"}, "size" => 20}}}) end end context "with term sorting using specified order" do - let(:config) {{ "facet" => ["title", "format"], "facet_sort" => "term|desc" }} + let(:config) { {"facet" => ["title", "format"], "facet_sort" => "term|desc"} } it "returns descending order by term" do - expect(@facets).to match({"title"=>{"terms"=>{"field"=>"title", "order"=>{"_term"=>"desc"}, "size"=>20}}, "format"=>{"terms"=>{"field"=>"format", "order"=>{"_term"=>"desc"}, "size"=>20}}}) + expect(@facets).to match({"title" => {"terms" => {"field" => "title", "order" => {"_term" => "desc"}, "size" => 20}}, "format" => {"terms" => {"field" => "format", "order" => {"_term" => "desc"}, "size" => 20}}}) end end context "with term sorting, no specified order" do - let(:config) {{ "facet" => ["title", "format"], "facet_sort" => "term" }} + let(:config) { {"facet" => ["title", "format"], "facet_sort" => "term"} } it "returns descending order by term" do - expect(@facets).to match({"title"=>{"terms"=>{"field"=>"title", "order"=>{"_term"=>"desc"}, "size"=>20}}, "format"=>{"terms"=>{"field"=>"format", "order"=>{"_term"=>"desc"}, "size"=>20}}}) + expect(@facets).to match({"title" => {"terms" => {"field" => "title", "order" => {"_term" => "desc"}, "size" => 20}}, "format" => {"terms" => {"field" => "format", "order" => {"_term" => "desc"}, "size" => 20}}}) end end context "with sort count but no order" do - let(:config) {{ "facet" => ["title"], "facet_sort" => "count" }} + let(:config) { {"facet" => ["title"], "facet_sort" => "count"} } it "returns descending order by count" do - expect(@facets).to match({"title"=>{"terms"=>{"field"=>"title", "order"=>{"_count"=>"desc"}, "size"=>20}}}) + expect(@facets).to match({"title" => {"terms" => {"field" => "title", "order" => {"_count" => "desc"}, "size" => 20}}}) end end end @@ -127,79 +131,79 @@ end context "single filter" do - let(:config) {{ "f" => ["category|Writings"] }} + let(:config) { {"f" => ["category|Writings"]} } it "returns single filter" do - expect(@filters).to match([{"term"=>{"category"=>"Writings"}}]) + expect(@filters).to match([{"term" => {"category" => "Writings"}}]) end end context "multiple filters" do - let(:config) {{ "f" => ["category|Writings", "author.name|Herriot, James"] }} + let(:config) { {"f" => ["category|Writings", "author.name|Herriot, James"]} } it "returns multiple filters, including nested" do - expect(@filters).to match([{"term"=>{"category"=>"Writings"}}, {"nested"=>{"path"=>"author", "query"=>{"term"=>{"author.name"=>"Herriot, James"}}}}]) + expect(@filters).to match([{"term" => {"category" => "Writings"}}, {"nested" => {"path" => "author", "query" => {"term" => {"author.name" => "Herriot, James"}}}}]) end end context "multiple filters, including one with CR present" do - let(:config) {{ "f" => ["category|Writings", "places_written_k|Jaffrey, New Hampshire, United\r\n States"] }} + let(:config) { {"f" => ["category|Writings", "places_written_k|Jaffrey, New Hampshire, United\r\n States"]} } it "returns multiple filters with same characters provided" do - expect(@filters).to match([{"term"=>{"category"=>"Writings"}}, {"term"=>{"places_written_k"=>"Jaffrey, New Hampshire, United\n States"}}]) + expect(@filters).to match([{"term" => {"category" => "Writings"}}, {"term" => {"places_written_k" => "Jaffrey, New Hampshire, United\n States"}}]) end end context "single year" do - let(:config) {{ "f" => ["date|1900"] }} + let(:config) { {"f" => ["date|1900"]} } it "returns range from Jan 1 to Dec 31 of year" do - expect(@filters).to match([{"range"=>{"date"=>{"gte"=>"1900-01-01", "lte"=>"1900-12-31", "format"=>"yyyy-MM-dd"}}}]) + expect(@filters).to match([{"range" => {"date" => {"gte" => "1900-01-01", "lte" => "1900-12-31", "format" => "yyyy-MM-dd"}}}]) end end context "double year" do - let(:config) {{ "f" => ["date|1900|1904"] }} + let(:config) { {"f" => ["date|1900|1904"]} } it "returns range from Jan 1 of first year to Dec 31 of last year" do - expect(@filters).to match([{"range"=>{"date"=>{"gte"=>"1900-01-01", "lte"=>"1904-12-31", "format"=>"yyyy-MM-dd"}}}]) + expect(@filters).to match([{"range" => {"date" => {"gte" => "1900-01-01", "lte" => "1904-12-31", "format" => "yyyy-MM-dd"}}}]) end end context "double day range" do - let(:config) {{ "f" => ["date|1904-01-03|1908-12-10"] }} + let(:config) { {"f" => ["date|1904-01-03|1908-12-10"]} } it "returns range from and to dates provided" do - expect(@filters).to match([{"range"=>{"date"=>{"gte"=>"1904-01-03", "lte"=>"1908-12-10", "format"=>"yyyy-MM-dd"}}}]) + expect(@filters).to match([{"range" => {"date" => {"gte" => "1904-01-03", "lte" => "1908-12-10", "format" => "yyyy-MM-dd"}}}]) end end context "nested field" do - let(:config) {{ "f" => ["creator.name|Willa, Cather"] }} + let(:config) { {"f" => ["creator.name|Willa, Cather"]} } it "returns nested field" do - expect(@filters).to match([{"nested"=>{"path"=>"creator", "query"=>{"term"=>{"creator.name"=>"Willa, Cather"}}}}]) + expect(@filters).to match([{"nested" => {"path" => "creator", "query" => {"term" => {"creator.name" => "Willa, Cather"}}}}]) end end context "multiple filters, including a nested field with CR present" do - let(:config) {{ "f" => ["category|Writings", "author.name|Herriot,\r\nJames"] }} + let(:config) { {"f" => ["category|Writings", "author.name|Herriot,\r\nJames"]} } it "returns multiple filters with same characters provided" do - expect(@filters).to match([{"term"=>{"category"=>"Writings"}}, {"nested"=>{"path"=>"author", "query"=>{"term"=>{"author.name"=>"Herriot,\nJames"}}}}]) + expect(@filters).to match([{"term" => {"category" => "Writings"}}, {"nested" => {"path" => "author", "query" => {"term" => {"author.name" => "Herriot,\nJames"}}}}]) end end context "dynamic field" do - let(:config) {{ "f" => ["publication_d|1900"] }} + let(:config) { {"f" => ["publication_d|1900"]} } it "returns dynamic date field with range for year" do - expect(@filters).to match([{"range"=>{"publication_d"=>{"gte"=>"1900-01-01", "lte"=>"1900-12-31", "format"=>"yyyy-MM-dd"}}}]) + expect(@filters).to match([{"range" => {"publication_d" => {"gte" => "1900-01-01", "lte" => "1900-12-31", "format" => "yyyy-MM-dd"}}}]) end end context "with non-array" do - let(:config) {{ "f" => "category|Writings" }} + let(:config) { {"f" => "category|Writings"} } it "returns single term filter" do - expect(@filters).to match([{"term"=>{"category"=>"Writings"}}]) + expect(@filters).to match([{"term" => {"category" => "Writings"}}]) end end context "where empty" do - let(:config) {{ "f" => "places|" }} + let(:config) { {"f" => "places|"} } it "returns term filter for empty string" do - expect(@filters).to match(["term"=>{"places"=>""}]) + expect(@filters).to match(["term" => {"places" => ""}]) end end end @@ -214,37 +218,37 @@ end context "no parameters" do - let(:config) {{}} + let(:config) { {} } it "returns defaults" do - expect(@highlights).to match({"fields"=>{"text"=>{"fragment_size"=>100, "number_of_fragments"=>3}}}) + expect(@highlights).to match({"fields" => {"text" => {"fragment_size" => 100, "number_of_fragments" => 3}}}) end end context "specifies fragment size and number" do - let(:config) {{ "hl_chars" => 20, "hl_num" => 1 }} + let(:config) { {"hl_chars" => 20, "hl_num" => 1} } it "returns correct settings" do - expect(@highlights).to match({"fields"=>{"text"=>{"fragment_size"=>20, "number_of_fragments"=>1}}}) + expect(@highlights).to match({"fields" => {"text" => {"fragment_size" => 20, "number_of_fragments" => 1}}}) end end context "sets highlight field and highlighting false" do - let(:config) {{ "hl_fl" => "annotations", "hl" => "false" }} + let(:config) { {"hl_fl" => "annotations", "hl" => "false"} } it "should not return any highlighting" do expect(@highlights).to match({}) end end context "specifies highlight field list" do - let(:config) {{ "hl_fl" => "annotations, text" }} + let(:config) { {"hl_fl" => "annotations, text"} } it "should return default settings for the specified fields" do - expect(@highlights).to match({"fields"=>{"text"=>{"fragment_size"=>100, "number_of_fragments"=>3}, "annotations"=>{"fragment_size"=>100, "number_of_fragments"=>3}}}) + expect(@highlights).to match({"fields" => {"text" => {"fragment_size" => 100, "number_of_fragments" => 3}, "annotations" => {"fragment_size" => 100, "number_of_fragments" => 3}}}) end end context "specifies fragment size and numbers for multiple fields" do - let(:config) {{ "hl_chars" => 20, "hl_num" => 1, "hl_fl" => "annotations,extra" }} + let(:config) { {"hl_chars" => 20, "hl_num" => 1, "hl_fl" => "annotations,extra"} } it "sets the fragment size and number on each field" do - expect(@highlights).to match({"fields"=>{"text"=>{"fragment_size"=>20, "number_of_fragments"=>1}, "annotations"=>{"fragment_size"=>20, "number_of_fragments"=>1}, "extra"=>{"fragment_size"=>20, "number_of_fragments"=>1}}}) + expect(@highlights).to match({"fields" => {"text" => {"fragment_size" => 20, "number_of_fragments" => 1}, "annotations" => {"fragment_size" => 20, "number_of_fragments" => 1}, "extra" => {"fragment_size" => 20, "number_of_fragments" => 1}}}) end end end @@ -259,58 +263,58 @@ end context "single sort" do - let(:config) {{ "sort" => ["title|asc"] }} + let(:config) { {"sort" => ["title|asc"]} } it "returns sort settings plus defaults" do - expect(@filters).to match([{"title"=>{"order"=>"asc", "mode"=>"min", "missing"=>"_last"}}]) + expect(@filters).to match([{"title" => {"order" => "asc", "mode" => "min", "missing" => "_last"}}]) end end context "multiple sorts and subfield" do - let(:config) {{ "sort" => ["title|desc", "author.name|asc"] }} + let(:config) { {"sort" => ["title|desc", "author.name|asc"]} } it "returns requested sort fields in order with nested sorting" do - expect(@filters).to match([{"title"=>{"order"=>"desc", "mode"=>"max", "missing"=>"_last"}}, {"author.name"=>{"order"=>"asc", "mode"=>"min", "missing"=>"_last", "nested"=>{"path"=>"author"}}}]) + expect(@filters).to match([{"title" => {"order" => "desc", "mode" => "max", "missing" => "_last"}}, {"author.name" => {"order" => "asc", "mode" => "min", "missing" => "_last", "nested" => {"path" => "author"}}}]) end end context "with non-array" do - let(:config) {{ "sort" => "title|asc" }} + let(:config) { {"sort" => "title|asc"} } it "returns requested sort" do - expect(@filters).to match([{"title"=>{"order"=>"asc", "mode"=>"min", "missing"=>"_last"}}]) + expect(@filters).to match([{"title" => {"order" => "asc", "mode" => "min", "missing" => "_last"}}]) end end context "no sort specified, query present" do - let(:config) {{ "q" => "water" }} + let(:config) { {"q" => "water"} } it "returns _score sorting" do expect(@filters).to match(["_score"]) end end context "no sort direction specified, query present" do - let(:config) {{ "q" => "water", "sort" => "date" }} + let(:config) { {"q" => "water", "sort" => "date"} } it "returns field sorted by order ascending" do - expect(@filters).to match([{"date"=>{"order"=>"asc", "mode"=>"min", "missing"=>"_last"}}]) + expect(@filters).to match([{"date" => {"order" => "asc", "mode" => "min", "missing" => "_last"}}]) end end context "sort specified, query present" do - let(:config) {{ "q" => "water", "sort" => "date|desc" }} + let(:config) { {"q" => "water", "sort" => "date|desc"} } it "returns sort by field order descending" do - expect(@filters).to match([{"date"=>{"order"=>"desc", "mode"=>"max", "missing"=>"_last"}}]) + expect(@filters).to match([{"date" => {"order" => "desc", "mode" => "max", "missing" => "_last"}}]) end end context "no sort specified, no query" do - let(:config) {{}} + let(:config) { {} } it "returns default sorting by identifier" do - expect(@filters).to match([{"identifier"=>{"order"=>"asc", "mode"=>"min", "missing"=>"_last"}}]) + expect(@filters).to match([{"identifier" => {"order" => "asc", "mode" => "min", "missing" => "_last"}}]) end end context "no sort direction specified, no query" do - let(:config) {{ "sort" => "title" }} + let(:config) { {"sort" => "title"} } it "returns sort by field ascending" do - expect(@filters).to match([{"title"=>{"order"=>"asc", "mode"=>"min", "missing"=>"_last"}}]) + expect(@filters).to match([{"title" => {"order" => "asc", "mode" => "min", "missing" => "_last"}}]) end end end @@ -325,65 +329,65 @@ end context "no text search" do - let(:config) {{}} + let(:config) { {} } it "returns a match all clause" do - expect(@search).to match({ "match_all" => {}}) + expect(@search).to match({"match_all" => {}}) end end context "simple text query" do - let(:config) {{ "q" => "water" }} + let(:config) { {"q" => "water"} } it "returns a query string and default text field" do - expect(@search).to match({"query_string"=>{"default_field"=>"text", "query"=>"water"}}) + expect(@search).to match({"query_string" => {"default_field" => "text", "query" => "water"}}) end end context "with boolean" do - let(:config) {{ "q" => "water AND college" }} + let(:config) { {"q" => "water AND college"} } it "returns boolean in query" do - expect(@search).to match({"query_string"=>{"default_field"=>"text", "query"=>"water AND college"}}) + expect(@search).to match({"query_string" => {"default_field" => "text", "query" => "water AND college"}}) end end context "multiple text fields" do - let(:config) {{ "q" => "(text:water) AND (annotations:water)" }} + let(:config) { {"q" => "(text:water) AND (annotations:water)"} } it "returns query string specifying multiple text fields" do - expect(@search).to match({"query_string"=>{"query"=>"(text:water) AND (annotations:water)"}}) + expect(@search).to match({"query_string" => {"query" => "(text:water) AND (annotations:water)"}}) end end context "multiple fields different input" do - let(:config) {{ "q" => "(text:water) OR (annotations:balcony)" }} + let(:config) { {"q" => "(text:water) OR (annotations:balcony)"} } it "returns different queries for each text field" do - expect(@search).to match({"query_string"=>{"query"=>"(text:water) OR (annotations:balcony)"}}) + expect(@search).to match({"query_string" => {"query" => "(text:water) OR (annotations:balcony)"}}) end end context "multiple fields with grouped inputs" do - let(:config) {{ "q" => '(text:water OR "fire in the fireplace") OR (annotations:water AND "fire in the fireplace")'}} + let(:config) { {"q" => '(text:water OR "fire in the fireplace") OR (annotations:water AND "fire in the fireplace")'} } it "returns " do - expect(@search).to match({"query_string"=>{"query"=>"(text:water OR \"fire in the fireplace\") OR (annotations:water AND \"fire in the fireplace\")"}}) + expect(@search).to match({"query_string" => {"query" => "(text:water OR \"fire in the fireplace\") OR (annotations:water AND \"fire in the fireplace\")"}}) end end context "non-text field search" do - let(:config) {{ "q" => "transcriptions_t:wouldnt" }} + let(:config) { {"q" => "transcriptions_t:wouldnt"} } it "returns query only for the non default text field specified" do - expect(@search).to match({"query_string"=>{"query"=>"transcriptions_t:wouldnt"}}) + expect(@search).to match({"query_string" => {"query" => "transcriptions_t:wouldnt"}}) end end context "text field search with colon making it almost look like a text field search" do - let(:config) {{ "q" => "yosemite: cool place to visit" }} + let(:config) { {"q" => "yosemite: cool place to visit"} } it "returns query with the colon and everything" do - expect(@search).to match({"query_string"=>{"default_field"=>"text", "query"=>"yosemite: cool place to visit"}}) + expect(@search).to match({"query_string" => {"default_field" => "text", "query" => "yosemite: cool place to visit"}}) end end context "text field search with single quotation mark" do - let(:config) {{ "q" => "Exploring the Text: Cather's Hand" }} + let(:config) { {"q" => "Exploring the Text: Cather's Hand"} } it "returns query string with unescaped single quotation mark" do - expect(@search).to match({"query_string"=>{"default_field"=>"text", "query"=>"Exploring the Text: Cather's Hand"}}) + expect(@search).to match({"query_string" => {"default_field" => "text", "query" => "Exploring the Text: Cather's Hand"}}) end end end @@ -398,23 +402,23 @@ end context "field list includes only" do - let(:config) {{ "fl" => "title, creator.name" }} + let(:config) { {"fl" => "title, creator.name"} } it "returns includes list" do - expect(@source).to match({"includes"=>["title", "creator.name"]}) + expect(@source).to match({"includes" => ["title", "creator.name"]}) end end context "field list excludes only" do - let(:config) {{ "fl" => "!title,!creator.name" }} + let(:config) { {"fl" => "!title,!creator.name"} } it "returns excludes list" do - expect(@source).to match({"excludes"=>["title", "creator.name"]}) + expect(@source).to match({"excludes" => ["title", "creator.name"]}) end end context "field list both include and exclude fields with wildcards" do - let(:config) {{ "fl" => "id, title, date, !dat*" }} + let(:config) { {"fl" => "id, title, date, !dat*"} } it "returns list with both includes and excludes" do - expect(@source).to match({"includes"=>["id", "title", "date"], "excludes"=>["dat*"]}) + expect(@source).to match({"includes" => ["id", "title", "date"], "excludes" => ["dat*"]}) end end end diff --git a/spec/services/search_item_res_spec.rb b/spec/services/search_item_res_spec.rb index 01b2da3..ddaa2a2 100644 --- a/spec/services/search_item_res_spec.rb +++ b/spec/services/search_item_res_spec.rb @@ -9,14 +9,14 @@ describe "#combine_highlights" do it "smushes highlights into an array by field type" do hl = @res.combine_highlights.dig(0, "highlight") - expect(hl).to match({"text"=>["View of the water from S. Lucia street in Naples. Napoli - Strada S. Lucia 35 Ediz Artistica RICTER"], "annotations"=>["This is a fake one that I made up to match water changes to highlighting"]}) + expect(hl).to match({"text" => ["View of the water from S. Lucia street in Naples. Napoli - Strada S. Lucia 35 Ediz Artistica RICTER"], "annotations" => ["This is a fake one that I made up to match water changes to highlighting"]}) end end describe "#reformat_facets" do it "arranges nested fields and date fields for standard facet response across the board" do facet = @res.reformat_facets - expect(facet).to match({"date.year"=>{"1896"=>4, "1920"=>4, "1934"=>4, "1908"=>3, "1938"=>3, "1942"=>3, "1916"=>2, "1929"=>2, "1933"=>2, "1936"=>2, "1941"=>2, "1899"=>1, "1905"=>1, "1909"=>1, "1911"=>1, "1917"=>1, "1918"=>1, "1925"=>1, "1930"=>1, "1931"=>1, "1935"=>1, "1940"=>1, "1944"=>1}, "person.name"=>{"Cather, Elsie"=>30, "Cather, Mary Virginia 'Jennie' Boak"=>22, "Cather, Roscoe"=>17, ""=>16, "Lewis, Edith"=>16, "Shannon, Margaret Cather"=>16, "Cather, Charles F."=>11, "Cather, Meta Schaper"=>8, "Gere, Mariel"=>8, "Auld, Jessica Cather"=>7, "Hambourg, Isabelle McClung"=>7, "Cather, Charles Douglass 'Douglass'"=>6, "Greenslet, Ferris"=>6, "Mellen, Mary Virginia Auld"=>6, "Sherwood, Carrie Miner"=>6, "Auld, William Thomas 'Tom, Will'"=>5, "Brockway, Virginia Cather"=>5, "Creighton, Mary Miner"=>5, "Hambourg, Jan"=>5, "Cather, James Donald"=>4}, "format"=>{"letter"=>50}}) + expect(facet).to match({"date.year" => {"1896" => 4, "1920" => 4, "1934" => 4, "1908" => 3, "1938" => 3, "1942" => 3, "1916" => 2, "1929" => 2, "1933" => 2, "1936" => 2, "1941" => 2, "1899" => 1, "1905" => 1, "1909" => 1, "1911" => 1, "1917" => 1, "1918" => 1, "1925" => 1, "1930" => 1, "1931" => 1, "1935" => 1, "1940" => 1, "1944" => 1}, "person.name" => {"Cather, Elsie" => 30, "Cather, Mary Virginia 'Jennie' Boak" => 22, "Cather, Roscoe" => 17, "" => 16, "Lewis, Edith" => 16, "Shannon, Margaret Cather" => 16, "Cather, Charles F." => 11, "Cather, Meta Schaper" => 8, "Gere, Mariel" => 8, "Auld, Jessica Cather" => 7, "Hambourg, Isabelle McClung" => 7, "Cather, Charles Douglass 'Douglass'" => 6, "Greenslet, Ferris" => 6, "Mellen, Mary Virginia Auld" => 6, "Sherwood, Carrie Miner" => 6, "Auld, William Thomas 'Tom, Will'" => 5, "Brockway, Virginia Cather" => 5, "Creighton, Mary Miner" => 5, "Hambourg, Jan" => 5, "Cather, James Donald" => 4}, "format" => {"letter" => 50}}) end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ce33d66..15a3872 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -44,53 +44,51 @@ # triggering implicit auto-inclusion in groups with matching metadata. config.shared_context_metadata_behavior = :apply_to_host_groups -# The settings below are suggested to provide a good initial experience -# with RSpec, but feel free to customize to your heart's content. -=begin - # This allows you to limit a spec run to individual examples or groups - # you care about by tagging them with `:focus` metadata. When nothing - # is tagged with `:focus`, all examples get run. RSpec also provides - # aliases for `it`, `describe`, and `context` that include `:focus` - # metadata: `fit`, `fdescribe` and `fcontext`, respectively. - config.filter_run_when_matching :focus - - # Allows RSpec to persist some state between runs in order to support - # the `--only-failures` and `--next-failure` CLI options. We recommend - # you configure your source control system to ignore this file. - config.example_status_persistence_file_path = "spec/examples.txt" - - # Limits the available syntax to the non-monkey patched syntax that is - # recommended. For more details, see: - # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ - # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ - # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode - config.disable_monkey_patching! - - # Many RSpec users commonly either run the entire suite or an individual - # file, and it's useful to allow more verbose output when running an - # individual spec file. - if config.files_to_run.one? - # Use the documentation formatter for detailed output, - # unless a formatter has already been configured - # (e.g. via a command-line flag). - config.default_formatter = "doc" - end - - # Print the 10 slowest examples and example groups at the - # end of the spec run, to help surface which specs are running - # particularly slow. - config.profile_examples = 10 - - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 - config.order = :random - - # Seed global randomization in this process using the `--seed` CLI option. - # Setting this allows you to use `--seed` to deterministically reproduce - # test failures related to randomization by passing the same `--seed` value - # as the one that triggered the failure. - Kernel.srand config.seed -=end + # The settings below are suggested to provide a good initial experience + # with RSpec, but feel free to customize to your heart's content. + # # This allows you to limit a spec run to individual examples or groups + # # you care about by tagging them with `:focus` metadata. When nothing + # # is tagged with `:focus`, all examples get run. RSpec also provides + # # aliases for `it`, `describe`, and `context` that include `:focus` + # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + # config.filter_run_when_matching :focus + # + # # Allows RSpec to persist some state between runs in order to support + # # the `--only-failures` and `--next-failure` CLI options. We recommend + # # you configure your source control system to ignore this file. + # config.example_status_persistence_file_path = "spec/examples.txt" + # + # # Limits the available syntax to the non-monkey patched syntax that is + # # recommended. For more details, see: + # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + # config.disable_monkey_patching! + # + # # Many RSpec users commonly either run the entire suite or an individual + # # file, and it's useful to allow more verbose output when running an + # # individual spec file. + # if config.files_to_run.one? + # # Use the documentation formatter for detailed output, + # # unless a formatter has already been configured + # # (e.g. via a command-line flag). + # config.default_formatter = "doc" + # end + # + # # Print the 10 slowest examples and example groups at the + # # end of the spec run, to help surface which specs are running + # # particularly slow. + # config.profile_examples = 10 + # + # # Run specs in random order to surface order dependencies. If you find an + # # order dependency and want to debug it, you can fix the order by providing + # # the seed, which is printed after each run. + # # --seed 1234 + # config.order = :random + # + # # Seed global randomization in this process using the `--seed` CLI option. + # # Setting this allows you to use `--seed` to deterministically reproduce + # # test failures related to randomization by passing the same `--seed` value + # # as the one that triggered the failure. + # Kernel.srand config.seed end