From bef7411de6dca21f4966804c0fd9e8677cb55dfb Mon Sep 17 00:00:00 2001 From: Vedran Hrncic Date: Tue, 7 Apr 2026 15:22:34 +0200 Subject: [PATCH 1/5] Remove lockfile --- Gemfile.lock | 178 --------------------------------------------------- 1 file changed, 178 deletions(-) delete mode 100644 Gemfile.lock diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 45c0cbe..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,178 +0,0 @@ -PATH - remote: . - specs: - jsonapi-query_builder (0.4.0) - activerecord (>= 7.2) - pagy (>= 3.5) - -GEM - remote: https://rubygems.org/ - specs: - actionview (8.0.2.1) - activesupport (= 8.0.2.1) - builder (~> 3.1) - erubi (~> 1.11) - rails-dom-testing (~> 2.2) - rails-html-sanitizer (~> 1.6) - activemodel (8.0.2.1) - activesupport (= 8.0.2.1) - activerecord (8.0.2.1) - activemodel (= 8.0.2.1) - activesupport (= 8.0.2.1) - timeout (>= 0.4.0) - activesupport (8.0.2.1) - base64 - benchmark (>= 0.3) - bigdecimal - concurrent-ruby (~> 1.0, >= 1.3.1) - connection_pool (>= 2.2.5) - drb - i18n (>= 1.6, < 2) - logger (>= 1.4.2) - minitest (>= 5.1) - securerandom (>= 0.3) - tzinfo (~> 2.0, >= 2.0.5) - uri (>= 0.13.1) - ast (2.4.2) - base64 (0.3.0) - benchmark (0.4.1) - bigdecimal (3.2.3) - builder (3.2.4) - bundler-audit (0.9.0.1) - bundler (>= 1.2.0, < 3) - thor (~> 1.0) - coderay (1.1.3) - concurrent-ruby (1.3.5) - connection_pool (2.5.4) - crass (1.0.6) - diff-lcs (1.4.4) - drb (2.2.3) - erubi (1.13.1) - i18n (1.8.10) - concurrent-ruby (~> 1.0) - json (2.13.2) - kaminari (1.2.1) - activesupport (>= 4.1.0) - kaminari-actionview (= 1.2.1) - kaminari-activerecord (= 1.2.1) - kaminari-core (= 1.2.1) - kaminari-actionview (1.2.1) - actionview - kaminari-core (= 1.2.1) - kaminari-activerecord (1.2.1) - activerecord - kaminari-core (= 1.2.1) - kaminari-core (1.2.1) - language_server-protocol (3.17.0.5) - lefthook (0.7.6) - lint_roller (1.1.0) - logger (1.7.0) - loofah (2.24.1) - crass (~> 1.0.2) - nokogiri (>= 1.12.0) - method_source (1.0.0) - mini_portile2 (2.8.9) - minitest (5.14.4) - nokogiri (1.18.9) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) - pagy (3.11.0) - parallel (1.21.0) - parser (3.3.9.0) - ast (~> 2.4.1) - racc - prism (1.4.0) - pry (0.14.1) - coderay (~> 1.1) - method_source (~> 1.0) - racc (1.5.2) - rails-dom-testing (2.3.0) - activesupport (>= 5.0.0) - minitest - nokogiri (>= 1.6) - rails-html-sanitizer (1.6.2) - loofah (~> 2.21) - nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - rainbow (3.0.0) - rake (13.0.6) - regexp_parser (2.11.2) - rspec (3.10.0) - rspec-core (~> 3.10.0) - rspec-expectations (~> 3.10.0) - rspec-mocks (~> 3.10.0) - rspec-core (3.10.1) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.1) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-mocks (3.10.2) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-support (3.10.2) - rubocop (1.80.2) - json (~> 2.3) - language_server-protocol (~> 3.17.0.2) - lint_roller (~> 1.1.0) - parallel (~> 1.10) - parser (>= 3.3.0.2) - rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.46.0, < 2.0) - ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.46.0) - parser (>= 3.3.7.2) - prism (~> 1.4) - rubocop-performance (1.25.0) - lint_roller (~> 1.1) - rubocop (>= 1.75.0, < 2.0) - rubocop-ast (>= 1.38.0, < 2.0) - rubocop-rspec (2.5.0) - rubocop (~> 1.19) - ruby-progressbar (1.11.0) - securerandom (0.4.1) - sqlite3 (2.7.3) - mini_portile2 (~> 2.8.0) - standard (1.51.0) - language_server-protocol (~> 3.17.0.2) - lint_roller (~> 1.0) - rubocop (~> 1.80.2) - standard-custom (~> 1.0.0) - standard-performance (~> 1.8) - standard-custom (1.0.2) - lint_roller (~> 1.0) - rubocop (~> 1.50) - standard-performance (1.8.0) - lint_roller (~> 1.1) - rubocop-performance (~> 1.25.0) - standardrb (1.0.0) - standard - thor (1.4.0) - timeout (0.4.3) - tzinfo (2.0.6) - concurrent-ruby (~> 1.0) - unicode-display_width (3.2.0) - unicode-emoji (~> 4.1) - unicode-emoji (4.1.0) - uri (1.0.4) - -PLATFORMS - ruby - -DEPENDENCIES - activerecord - bundler (~> 2.0) - bundler-audit - jsonapi-query_builder! - kaminari (~> 1.2) - lefthook - pry - rake (~> 13.0) - rspec (~> 3.0) - rubocop-rspec - sqlite3 - standard - standardrb - -BUNDLED WITH - 2.7.2 From 33715ea92f142e88f3faf5b248f4b68f9f6604a5 Mon Sep 17 00:00:00 2001 From: Vedran Hrncic Date: Mon, 20 Apr 2026 18:23:02 +0200 Subject: [PATCH 2/5] Update RuboCop config --- .rubocop.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index e04dbb8..4e957fa 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,6 +1,13 @@ -require: +plugins: - rubocop-rspec +Layout/LineLength: + Max: 120 + AllowedPatterns: ['\A#'] + +Style/Documentation: + Enabled: false + RSpec/NestedGroups: Max: 4 From 26d5504fac0c510e7beaba9141f9b97a7e5106e3 Mon Sep 17 00:00:00 2001 From: Vedran Hrncic Date: Mon, 20 Apr 2026 18:28:14 +0200 Subject: [PATCH 3/5] Move development gems to Gemfile --- Gemfile | 14 +++++++++++++- jsonapi-query_builder.gemspec | 13 ------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Gemfile b/Gemfile index b4ba478..296012f 100644 --- a/Gemfile +++ b/Gemfile @@ -4,5 +4,17 @@ source "https://rubygems.org" git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } -# Specify your gem's dependencies in jsonapi-query_builder.gemspec +gem 'activerecord' +gem 'bundler', '~> 2.0' +gem 'bundler-audit' +gem 'kaminari', '~> 1.2' +gem 'lefthook' +gem 'pry' +gem 'rake', '~> 13.0' +gem 'rspec', '~> 3.0' +gem 'rubocop-rspec' +gem 'sqlite3' +gem 'standard' +gem 'standardrb' + gemspec diff --git a/jsonapi-query_builder.gemspec b/jsonapi-query_builder.gemspec index 9e9e855..410408b 100644 --- a/jsonapi-query_builder.gemspec +++ b/jsonapi-query_builder.gemspec @@ -40,17 +40,4 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency "activerecord", ">= 7.2" spec.add_runtime_dependency "pagy", ">= 3.5" - - spec.add_development_dependency "bundler", "~> 2.0" - spec.add_development_dependency "bundler-audit" - spec.add_development_dependency "rake", "~> 13.0" - spec.add_development_dependency "rspec", "~> 3.0" - spec.add_development_dependency "standardrb" - spec.add_development_dependency "standard" - spec.add_development_dependency "rubocop-rspec" - spec.add_development_dependency "lefthook" - spec.add_development_dependency "kaminari", "~> 1.2" - spec.add_development_dependency "activerecord" - spec.add_development_dependency "sqlite3" - spec.add_development_dependency "pry" end From c3bb57f7a7a99572103dcb3a48e2df804a29e25d Mon Sep 17 00:00:00 2001 From: Vedran Hrncic Date: Mon, 20 Apr 2026 18:48:58 +0200 Subject: [PATCH 4/5] Fix quotes on string literals --- Gemfile | 2 +- Rakefile | 4 +- bin/console | 6 +- jsonapi-query_builder.gemspec | 43 +++---- lib/jsonapi/query_builder.rb | 18 +-- lib/jsonapi/query_builder/base_query.rb | 21 +-- lib/jsonapi/query_builder/dynamic_sort.rb | 2 +- .../errors/unpermitted_sort_parameters.rb | 6 +- lib/jsonapi/query_builder/mixins/filter.rb | 37 +++--- lib/jsonapi/query_builder/mixins/include.rb | 6 +- lib/jsonapi/query_builder/mixins/paginate.rb | 2 +- lib/jsonapi/query_builder/mixins/sort.rb | 30 +++-- .../query_builder/mixins/sort/param.rb | 2 +- lib/jsonapi/query_builder/paginator.rb | 4 +- .../query_builder/paginator/kaminari.rb | 8 +- lib/jsonapi/query_builder/paginator/keyset.rb | 2 +- lib/jsonapi/query_builder/paginator/pagy.rb | 9 +- .../query_builder/paginator/pagy_countless.rb | 12 +- lib/jsonapi/query_builder/version.rb | 2 +- .../jsonapi/query_builder/base_filter_spec.rb | 6 +- spec/jsonapi/query_builder/base_query_spec.rb | 47 +++---- spec/jsonapi/query_builder/base_sort_spec.rb | 14 +- .../query_builder/dynamic_sort_spec.rb | 10 +- .../unpermitted_sort_parameters_spec.rb | 16 +-- .../query_builder/mixins/filter_spec.rb | 91 ++++++------- .../query_builder/mixins/include_spec.rb | 22 ++-- .../query_builder/mixins/paginate_spec.rb | 32 ++--- .../query_builder/mixins/sort/dynamic_spec.rb | 32 ++--- .../query_builder/mixins/sort/param_spec.rb | 36 +++--- .../query_builder/mixins/sort/static_spec.rb | 16 +-- .../jsonapi/query_builder/mixins/sort_spec.rb | 120 +++++++++--------- .../paginator/base_paginator_spec.rb | 12 +- .../query_builder/paginator/kaminari_spec.rb | 16 +-- .../query_builder/paginator/keyset_spec.rb | 28 ++-- .../paginator/pagy_countless_spec.rb | 12 +- .../query_builder/paginator/pagy_spec.rb | 10 +- spec/jsonapi/query_builder_spec.rb | 62 ++++----- spec/spec_helper.rb | 7 +- spec/support/active_record.rb | 4 +- 39 files changed, 408 insertions(+), 401 deletions(-) diff --git a/Gemfile b/Gemfile index 296012f..0e3f511 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ # frozen_string_literal: true -source "https://rubygems.org" +source 'https://rubygems.org' git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } diff --git a/Rakefile b/Rakefile index b6ae734..82bb534 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,7 @@ # frozen_string_literal: true -require "bundler/gem_tasks" -require "rspec/core/rake_task" +require 'bundler/gem_tasks' +require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) diff --git a/bin/console b/bin/console index b07038e..fd950cd 100755 --- a/bin/console +++ b/bin/console @@ -1,8 +1,8 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require "bundler/setup" -require "jsonapi/query_builder" +require 'bundler/setup' +require 'jsonapi/query_builder' # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. @@ -11,5 +11,5 @@ require "jsonapi/query_builder" # require "pry" # Pry.start -require "irb" +require 'irb' IRB.start(__FILE__) diff --git a/jsonapi-query_builder.gemspec b/jsonapi-query_builder.gemspec index 410408b..5cfc5ae 100644 --- a/jsonapi-query_builder.gemspec +++ b/jsonapi-query_builder.gemspec @@ -1,43 +1,42 @@ # frozen_string_literal: true -lib = File.expand_path("../lib", __FILE__) +lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require "jsonapi/query_builder/version" +require 'jsonapi/query_builder/version' Gem::Specification.new do |spec| - spec.name = "jsonapi-query_builder" + spec.name = 'jsonapi-query_builder' spec.version = Jsonapi::QueryBuilder::VERSION - spec.authors = ["Jure Cindro"] - spec.email = ["jure.cindro@infinum.co"] + spec.authors = ['Jure Cindro'] + spec.email = ['jure.cindro@infinum.co'] - spec.summary = "Support `json:api` querying with ease!" + spec.summary = 'Support `json:api` querying with ease!' spec.description = <<~MD `Jsonapi::QueryBuilder` serves the purpose of adding the json api query related SQL conditions to the already scoped collection, usually used in controller index actions. With the query builder we can easily define logic for query filters, attributes by which we can sort, and delegate pagination parameters to the underlying paginator. Included relationships are automatically included via the `ActiveRecord::QueryMethods#includes`, to prevent N+1 query problems. MD - spec.homepage = "https://github.com/infinum/jsonapi-query_builder" - spec.license = "MIT" - - if spec.respond_to?(:metadata) - spec.metadata["homepage_uri"] = spec.homepage - spec.metadata["source_code_uri"] = "https://github.com/infinum/jsonapi-query_builder" - spec.metadata["changelog_uri"] = "https://github.com/infinum/jsonapi-query_builder/blob/master/CHANGELOG.md" - else - raise "RubyGems 2.0 or newer is required to protect against public gem pushes." - end + spec.homepage = 'https://github.com/infinum/jsonapi-query_builder' + spec.license = 'MIT' + + raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' unless spec.respond_to?(:metadata) + + spec.metadata['homepage_uri'] = spec.homepage + spec.metadata['source_code_uri'] = 'https://github.com/infinum/jsonapi-query_builder' + spec.metadata['changelog_uri'] = 'https://github.com/infinum/jsonapi-query_builder/blob/master/CHANGELOG.md' + spec.metadata['rubygems_mfa_required'] = 'true' # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. - spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do + spec.files = Dir.chdir(File.expand_path(__dir__)) do `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end - spec.bindir = "exe" + spec.bindir = 'exe' spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } - spec.require_paths = ["lib"] + spec.require_paths = ['lib'] - spec.required_ruby_version = ">= 3.2" + spec.required_ruby_version = '>= 3.2' - spec.add_runtime_dependency "activerecord", ">= 7.2" - spec.add_runtime_dependency "pagy", ">= 3.5" + spec.add_dependency 'activerecord', '>= 7.2' + spec.add_dependency 'pagy', '~> 3.5' end diff --git a/lib/jsonapi/query_builder.rb b/lib/jsonapi/query_builder.rb index 2c5da7b..35690ab 100644 --- a/lib/jsonapi/query_builder.rb +++ b/lib/jsonapi/query_builder.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -require "active_support/concern" -require "active_support/core_ext/array/conversions" -require "active_support/core_ext/hash/keys" -require "active_support/core_ext/string/inflections" +require 'active_support/concern' +require 'active_support/core_ext/array/conversions' +require 'active_support/core_ext/hash/keys' +require 'active_support/core_ext/string/inflections' -require "jsonapi/query_builder/version" -require "jsonapi/query_builder/base_query" -require "jsonapi/query_builder/base_filter" -require "jsonapi/query_builder/base_sort" -require "jsonapi/query_builder/dynamic_sort" +require 'jsonapi/query_builder/version' +require 'jsonapi/query_builder/base_query' +require 'jsonapi/query_builder/base_filter' +require 'jsonapi/query_builder/base_sort' +require 'jsonapi/query_builder/dynamic_sort' diff --git a/lib/jsonapi/query_builder/base_query.rb b/lib/jsonapi/query_builder/base_query.rb index d4d9c01..99e3b9d 100644 --- a/lib/jsonapi/query_builder/base_query.rb +++ b/lib/jsonapi/query_builder/base_query.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require "jsonapi/query_builder/mixins/filter" -require "jsonapi/query_builder/mixins/include" -require "jsonapi/query_builder/mixins/paginate" -require "jsonapi/query_builder/mixins/sort" +require 'jsonapi/query_builder/mixins/filter' +require 'jsonapi/query_builder/mixins/include' +require 'jsonapi/query_builder/mixins/paginate' +require 'jsonapi/query_builder/mixins/sort' -require "jsonapi/query_builder/paginator" +require 'jsonapi/query_builder/paginator' module Jsonapi module QueryBuilder @@ -26,13 +26,14 @@ def initialize(collection, params) # @return [ActiveRecord::Relation] A collection with eager loaded relationships based on include params, filtered, # ordered and lastly, paginated. - # @note Pagination details are saved to an instance variable and can be accessed via the #pagination_details attribute reader + # @note Pagination details are saved to an instance variable and can be accessed via the #pagination_details + # attribute reader def results collection - .yield_self(&method(:add_includes)) - .yield_self(&method(:sort)) - .yield_self(&method(:filter)) - .yield_self(&method(:paginate)) + .then(&method(:add_includes)) + .then(&method(:sort)) + .then(&method(:filter)) + .then(&method(:paginate)) end # @param [integer, string] id diff --git a/lib/jsonapi/query_builder/dynamic_sort.rb b/lib/jsonapi/query_builder/dynamic_sort.rb index 2056393..dd3d6fe 100644 --- a/lib/jsonapi/query_builder/dynamic_sort.rb +++ b/lib/jsonapi/query_builder/dynamic_sort.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require "jsonapi/query_builder/base_sort" +require 'jsonapi/query_builder/base_sort' module Jsonapi module QueryBuilder diff --git a/lib/jsonapi/query_builder/errors/unpermitted_sort_parameters.rb b/lib/jsonapi/query_builder/errors/unpermitted_sort_parameters.rb index 10d169a..7eecb5c 100644 --- a/lib/jsonapi/query_builder/errors/unpermitted_sort_parameters.rb +++ b/lib/jsonapi/query_builder/errors/unpermitted_sort_parameters.rb @@ -7,9 +7,9 @@ class UnpermittedSortParameters < ArgumentError def initialize(unpermitted_parameters) super([ unpermitted_parameters.to_sentence, - (unpermitted_parameters.count == 1) ? "is not a" : "are not", - "permitted sort attribute".pluralize(unpermitted_parameters.count) - ].join(" ")) + unpermitted_parameters.one? ? 'is not a' : 'are not', + 'permitted sort attribute'.pluralize(unpermitted_parameters.count) + ].join(' ')) end end end diff --git a/lib/jsonapi/query_builder/mixins/filter.rb b/lib/jsonapi/query_builder/mixins/filter.rb index 5671868..7c51ce5 100644 --- a/lib/jsonapi/query_builder/mixins/filter.rb +++ b/lib/jsonapi/query_builder/mixins/filter.rb @@ -34,15 +34,15 @@ def supported_filters # # => collection.where(first_name: params.dig(:filter, :first_name)) if params[:filter]&.key?(:first_name) # # @example Change the filter condition - # filters_by :first_name, if: ->(query) { query.length >= 2 } - # # => collection.where(first_name: params.dig(:filter, :first_name)) if params.dig(:filter, :first_name) >= 2 - # filters_by :first_name, unless: ->(query) { query.length < 2 } - # # => collection.where(first_name: params.dig(:filter, :first_name)) unless params.dig(:filter, :first_name) < 2 + # filters_by :name, if: ->(query) { query.length >= 2 } + # # => collection.where(name: params.dig(:filter, :name)) if params.dig(:filter, :name) >= 2 + # filters_by :name, unless: ->(query) { query.length < 2 } + # # => collection.where(name: params.dig(:filter, :name)) unless params.dig(:filter, :name) < 2 # filters_by :type, TypeFilter, if: :correct_type? # # => TypeFilter.new(collection, query).yield_self { |filter| filter.results if filter.correct_type? } def filters_by(attribute, filter = nil, **options) filter ||= ->(collection, query) { collection.where(attribute => query) } - @supported_filters = {**supported_filters, attribute => [filter, options]} + @supported_filters = { **supported_filters, attribute => [filter, options] } end end @@ -61,9 +61,9 @@ def filter(collection, filter_params = send(:filter_params)) self.class.supported_filters.reduce(collection) do |filtered_collection, supported_filter| filter, options = serialize_filter(supported_filter, collection: filtered_collection, params: filter_params) - next filtered_collection unless options[:conditions].all? { |type, condition| + next filtered_collection unless options[:conditions].all? do |type, condition| check_condition(condition, type, filter: filter, query: options[:query]) - } + end filter.respond_to?(:call) ? filter.call(filtered_collection, options[:query]) : filter.results end @@ -75,28 +75,29 @@ def filter_params params[:filter] || {} end - def serialize_filter(supported_filter, collection:, params:) + def serialize_filter(supported_filter, collection:, params:) # rubocop:disable Metrics/AbcSize attribute, (filter, options) = *supported_filter options[:query_parameter] = options[:query_parameter]&.to_sym || attribute options[:query] = params[options[:query_parameter]].presence - options[:conditions] = options.slice(:if, :unless).presence || - {if: options[:query].present? || options[:allow_nil] && params.key?(options[:query_parameter])} + options[:conditions] = + options.slice(:if, :unless).presence || + { if: options[:query].present? || (options[:allow_nil] && params.key?(options[:query_parameter])) } filter = filter.new(collection, options[:query]) unless filter.respond_to?(:call) [filter, options] end - def check_condition(condition, type, **opts) + def check_condition(condition, type, **opts) # rubocop:disable Naming/PredicateMethod (type == :if) == case condition - when Proc - condition.call(opts[:query]) - when Symbol - opts[:filter].send(condition) - else - condition - end + when Proc + condition.call(opts[:query]) + when Symbol + opts[:filter].send(condition) + else + condition + end end end end diff --git a/lib/jsonapi/query_builder/mixins/include.rb b/lib/jsonapi/query_builder/mixins/include.rb index 897f64a..2afb9ad 100644 --- a/lib/jsonapi/query_builder/mixins/include.rb +++ b/lib/jsonapi/query_builder/mixins/include.rb @@ -23,17 +23,17 @@ def formatted_include_params(include_params) return [] unless include_params include_params - .split(",") + .split(',') .map(&:strip) .map(&method(:formatted_includes_relationship)) end def formatted_includes_relationship(relationship) - parent, children = relationship.split(".", 2) + parent, children = relationship.split('.', 2) return parent.to_sym unless children - {parent.to_sym => formatted_includes_relationship(children)} + { parent.to_sym => formatted_includes_relationship(children) } end end end diff --git a/lib/jsonapi/query_builder/mixins/paginate.rb b/lib/jsonapi/query_builder/mixins/paginate.rb index 412f44c..d3d0fd2 100644 --- a/lib/jsonapi/query_builder/mixins/paginate.rb +++ b/lib/jsonapi/query_builder/mixins/paginate.rb @@ -35,7 +35,7 @@ def paginate(collection, page_params = send(:page_params)) private def page_params - {number: 1, **params.fetch(:page, {})} + { number: 1, **params.fetch(:page, {}) } end end end diff --git a/lib/jsonapi/query_builder/mixins/sort.rb b/lib/jsonapi/query_builder/mixins/sort.rb index ce23e0c..43ea24e 100644 --- a/lib/jsonapi/query_builder/mixins/sort.rb +++ b/lib/jsonapi/query_builder/mixins/sort.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require "jsonapi/query_builder/mixins/sort/param" -require "jsonapi/query_builder/mixins/sort/static" -require "jsonapi/query_builder/mixins/sort/dynamic" -require "jsonapi/query_builder/errors/unpermitted_sort_parameters" +require 'jsonapi/query_builder/mixins/sort/param' +require 'jsonapi/query_builder/mixins/sort/static' +require 'jsonapi/query_builder/mixins/sort/dynamic' +require 'jsonapi/query_builder/errors/unpermitted_sort_parameters' module Jsonapi module QueryBuilder @@ -15,7 +15,7 @@ module Sort attr_reader :_default_sort def _unique_sort_attributes - @_unique_sort_attributes || [id: :asc] + @_unique_sort_attributes || [{ id: :asc }] end # @return [Hash] Supported sorts @@ -41,7 +41,8 @@ def unique_sort_attributes(*attributes) # if no sort parameter is set, unlike the `unique_sort_attribute` which is always appended as the last sort # attribute. The parameters are passed directly to the underlying active record relation, so the usual # ordering options are possible. - # @param [Symbol, Hash] options A default sort attribute or a Hash with the attribute and it's order direction. + # @param [Symbol, Hash] options A default sort attribute or a Hash with the attribute and it's order + # direction. def default_sort(options) @_default_sort = options end @@ -54,8 +55,10 @@ def sorts_by(attribute, sort = nil) supported_sorts[attribute] = Sort::Static.new(attribute, sort) end - # Registers an attribute prefix that can be dynamically used for sorting. Attribute prefix is stripped from parsed sort parameter and passed to the sort proc or class. - # @param [Symbol] attribute_prefix The "sortable" attribute prefix, e.g. `:'data.'` for sorting by `data.name` and `data.created_at` + # Registers an attribute prefix that can be dynamically used for sorting. Attribute prefix is stripped from + # parsed sort parameter and passed to the sort proc or class. + # @param [Symbol] attribute_prefix The "sortable" attribute prefix, e.g. `:'data.'` for sorting by `data.name` + # and `data.created_at` # @param [proc, Class] sort A proc or a sort class, defaults to a simple order(attribute => direction) def dynamically_sorts_by(attribute_prefix, sort) supported_dynamic_sorts << Sort::Dynamic.new(attribute_prefix, sort) @@ -79,8 +82,8 @@ def sort(collection, sort_params = send(:sort_params)) ensure_permitted_sort_params!(sort_params) if sort_params collection - .yield_self { |c| add_order_attributes(c, sort_params) } - .yield_self(&method(:add_unique_order_attributes)) + .then { |c| add_order_attributes(c, sort_params) } + .then(&method(:add_unique_order_attributes)) end private @@ -91,14 +94,15 @@ def sort_params def ensure_permitted_sort_params!(sort_params) unpermitted_parameters = sort_params.map(&:attribute).filter do |attribute| - !self.class.supported_sorts.key?(attribute.to_sym) && self.class.supported_dynamic_sorts.none? { |dynamic_sort| dynamic_sort.matches?(attribute) } + !self.class.supported_sorts.key?(attribute.to_sym) && + self.class.supported_dynamic_sorts.none? { |dynamic_sort| dynamic_sort.matches?(attribute) } end - return if unpermitted_parameters.size.zero? + return if unpermitted_parameters.empty? raise Errors::UnpermittedSortParameters, unpermitted_parameters end - def add_order_attributes(collection, sort_params) + def add_order_attributes(collection, sort_params) # rubocop:disable Metrics/AbcSize return collection if self.class._default_sort.nil? && sort_params.blank? return sort_by_default(collection) if sort_params.blank? diff --git a/lib/jsonapi/query_builder/mixins/sort/param.rb b/lib/jsonapi/query_builder/mixins/sort/param.rb index aa821ca..b199a2c 100644 --- a/lib/jsonapi/query_builder/mixins/sort/param.rb +++ b/lib/jsonapi/query_builder/mixins/sort/param.rb @@ -13,7 +13,7 @@ def initialize(param) class << self def deserialize_params(sort_params) - (sort_params || "").split(",").map(&method(:new)) + (sort_params || '').split(',').map(&method(:new)) end end diff --git a/lib/jsonapi/query_builder/paginator.rb b/lib/jsonapi/query_builder/paginator.rb index 4708ab0..c5b0560 100644 --- a/lib/jsonapi/query_builder/paginator.rb +++ b/lib/jsonapi/query_builder/paginator.rb @@ -1,4 +1,4 @@ # frozen_string_literal: true -require "jsonapi/query_builder/paginator/base_paginator" -require "jsonapi/query_builder/paginator/pagy" +require 'jsonapi/query_builder/paginator/base_paginator' +require 'jsonapi/query_builder/paginator/pagy' diff --git a/lib/jsonapi/query_builder/paginator/kaminari.rb b/lib/jsonapi/query_builder/paginator/kaminari.rb index 76a7f67..a4e790c 100644 --- a/lib/jsonapi/query_builder/paginator/kaminari.rb +++ b/lib/jsonapi/query_builder/paginator/kaminari.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require "kaminari" +require 'kaminari' module Jsonapi module QueryBuilder @@ -8,9 +8,9 @@ module Paginator class Kaminari < BasePaginator def paginate(page_params) paged_collection = collection - .page(page_params[:number]) - .per(page_params[:size]) - .padding(page_params[:offset]) + .page(page_params[:number]) + .per(page_params[:size]) + .padding(page_params[:offset]) [paged_collection, pagination_details(paged_collection, page_params)] end diff --git a/lib/jsonapi/query_builder/paginator/keyset.rb b/lib/jsonapi/query_builder/paginator/keyset.rb index c53e91c..89bbdcc 100644 --- a/lib/jsonapi/query_builder/paginator/keyset.rb +++ b/lib/jsonapi/query_builder/paginator/keyset.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require "active_record" +require 'active_record' module Jsonapi module QueryBuilder diff --git a/lib/jsonapi/query_builder/paginator/pagy.rb b/lib/jsonapi/query_builder/paginator/pagy.rb index dd108c3..7a078ec 100644 --- a/lib/jsonapi/query_builder/paginator/pagy.rb +++ b/lib/jsonapi/query_builder/paginator/pagy.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require "pagy" -require "pagy/extras/items" +require 'pagy' +require 'pagy/extras/items' module Jsonapi module QueryBuilder @@ -10,9 +10,10 @@ class Pagy < BasePaginator include ::Pagy::Backend def paginate(page_params) - @params = {page: page_params} + @params = { page: page_params } - pagination_details, records = pagy(collection, page: page_params[:number], items: page_params[:size], outset: page_params[:offset]) + pagination_details, records = pagy(collection, page: page_params[:number], items: page_params[:size], + outset: page_params[:offset]) [records, pagination_details] end diff --git a/lib/jsonapi/query_builder/paginator/pagy_countless.rb b/lib/jsonapi/query_builder/paginator/pagy_countless.rb index 68d72c7..fdacb3f 100644 --- a/lib/jsonapi/query_builder/paginator/pagy_countless.rb +++ b/lib/jsonapi/query_builder/paginator/pagy_countless.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require "pagy" -require "pagy/extras/items" -require "pagy/extras/countless" +require 'pagy' +require 'pagy/extras/items' +require 'pagy/extras/countless' module Jsonapi module QueryBuilder @@ -11,11 +11,11 @@ class PagyCountless < BasePaginator include ::Pagy::Backend def paginate(page_params) - @params = {page: page_params} + @params = { page: page_params } pagination_details, records = pagy_countless(collection, page: page_params[:number], - items: page_params[:size], - outset: page_params[:offset]) + items: page_params[:size], + outset: page_params[:offset]) [records, pagination_details] end diff --git a/lib/jsonapi/query_builder/version.rb b/lib/jsonapi/query_builder/version.rb index 429e477..920e039 100644 --- a/lib/jsonapi/query_builder/version.rb +++ b/lib/jsonapi/query_builder/version.rb @@ -2,6 +2,6 @@ module Jsonapi module QueryBuilder - VERSION = "0.4.0" + VERSION = '0.4.0' end end diff --git a/spec/jsonapi/query_builder/base_filter_spec.rb b/spec/jsonapi/query_builder/base_filter_spec.rb index 5ce1afe..b77305c 100644 --- a/spec/jsonapi/query_builder/base_filter_spec.rb +++ b/spec/jsonapi/query_builder/base_filter_spec.rb @@ -2,15 +2,15 @@ RSpec.describe Jsonapi::QueryBuilder::BaseFilter do before do - stub_const "FakeFilter", filter_class + stub_const 'FakeFilter', filter_class end - context "with required interface methods" do + context 'with required interface methods' do let(:filter_class) { Class.new(described_class) } - it "raises an error for results method" do expect { FakeFilter.new(instance_double("collection"), "query").results }.to raise_error( NotImplementedError, "FakeFilter should implement #results" + it 'raises an error for results method' do ) end end diff --git a/spec/jsonapi/query_builder/base_query_spec.rb b/spec/jsonapi/query_builder/base_query_spec.rb index 04d6aeb..9dd91ba 100644 --- a/spec/jsonapi/query_builder/base_query_spec.rb +++ b/spec/jsonapi/query_builder/base_query_spec.rb @@ -8,19 +8,20 @@ allow(query).to receive(:add_includes).and_return(collection) end - describe "#results" do + describe '#results' do subject(:results) { query.results } let(:filtered_collection) { instance_double "filtered_collection" } let(:paged_filtered_collection) { instance_double "paged_filtered_collection" } let(:params) { + let(:params) do { - sort: "last_name,first_name", - include: "books", - filter: {first_name: "John"}, - page: {number: 1, size: 20, offset: 0} + sort: 'last_name,first_name', + include: 'books', + filter: { first_name: 'John' }, + page: { number: 1, size: 20, offset: 0 } } - } + end before do allow(query).to receive(:sort).and_return(collection) @@ -28,62 +29,62 @@ allow(query).to receive(:paginate).and_return(paged_filtered_collection) end - it "returns the collection" do + it 'returns the collection' do expect(results).to eql paged_filtered_collection end - it "sorts the collection" do + it 'sorts the collection' do results expect(query).to have_received(:sort).with(collection) end - it "adds includes to the collection" do + it 'adds includes to the collection' do results expect(query).to have_received(:add_includes).with(collection) end - it "filters the collection" do + it 'filters the collection' do results expect(query).to have_received(:filter).with(collection) end - it "paginates the collection" do + it 'paginates the collection' do results expect(query).to have_received(:paginate).with(filtered_collection) end end - describe "#find" do + describe '#find' do subject(:find) { query.find(1) } - let(:params) { {id: 2, include: "books.tags"} } + let(:params) { { id: 2, include: 'books.tags' } } before do allow(query).to receive(:find_by!) end - it "finds by id" do + it 'finds by id' do find expect(query).to have_received(:find_by!).with(id: 1) end - it "aliases record to find with default parameters" do + it 'aliases record to find with default parameters' do query.record expect(query).to have_received(:find_by!).with(id: 2) end end - describe "#find_by!" do + describe '#find_by!' do subject(:find_by!) { query.find_by!(id: 1) } - let(:params) { {include: "books.tags"} } - let(:record) { instance_double "record" } + let(:params) { { include: 'books.tags' } } + let(:record) { instance_double Object } before do allow(collection).to receive(:find_by!).and_return(record) @@ -91,22 +92,22 @@ it { is_expected.to eql record } - it "adds includes to the collection" do + it 'adds includes to the collection' do find_by! expect(query).to have_received(:add_includes).with(collection) end - it "finds a record" do + it 'finds a record' do find_by! expect(collection).to have_received(:find_by!).with(id: 1) end - it "finds by multiple kwargs" do - query.find_by!(id: 1, first_name: "John") + it 'finds by multiple kwargs' do + query.find_by!(id: 1, first_name: 'John') - expect(collection).to have_received(:find_by!).with(id: 1, first_name: "John") + expect(collection).to have_received(:find_by!).with(id: 1, first_name: 'John') end end end diff --git a/spec/jsonapi/query_builder/base_sort_spec.rb b/spec/jsonapi/query_builder/base_sort_spec.rb index c0bd356..69d435f 100644 --- a/spec/jsonapi/query_builder/base_sort_spec.rb +++ b/spec/jsonapi/query_builder/base_sort_spec.rb @@ -4,17 +4,17 @@ let(:sort_class) { Class.new(described_class) } before do - stub_const "FakeSort", sort_class + stub_const 'FakeSort', sort_class end - it "defaults to ascending sort direction" do - expect(FakeSort.new(instance_double("collection"))).to have_attributes(direction: :asc) + it 'defaults to ascending sort direction' do + expect(FakeSort.new(instance_double(ActiveRecord::Relation))).to have_attributes(direction: :asc) end - context "with required interface methods" do - it "raises an error for results method" do - expect { FakeSort.new(instance_double("collection"), :desc).results }.to raise_error( - NotImplementedError, "FakeSort should implement #results" + context 'with required interface methods' do + it 'raises an error for results method' do + expect { FakeSort.new(instance_double(ActiveRecord::Relation), :desc).results }.to raise_error( + NotImplementedError, 'FakeSort should implement #results' ) end end diff --git a/spec/jsonapi/query_builder/dynamic_sort_spec.rb b/spec/jsonapi/query_builder/dynamic_sort_spec.rb index 10c8a83..a6da7ae 100644 --- a/spec/jsonapi/query_builder/dynamic_sort_spec.rb +++ b/spec/jsonapi/query_builder/dynamic_sort_spec.rb @@ -4,17 +4,17 @@ let(:sort_class) { Class.new(described_class) } before do - stub_const "FakeSort", sort_class + stub_const 'FakeSort', sort_class end - it "defaults to ascending sort direction" do expect(FakeSort.new(instance_double("collection"), "attribute")).to have_attributes(direction: :asc) + it 'defaults to ascending sort direction' do end - context "with required interface methods" do - it "raises an error for results method" do expect { FakeSort.new(instance_double("collection"), "attribute", :desc).results }.to raise_error( - NotImplementedError, "FakeSort should implement #results" + context 'with required interface methods' do + it 'raises an error for results method' do + NotImplementedError, 'FakeSort should implement #results' ) end end diff --git a/spec/jsonapi/query_builder/errors/unpermitted_sort_parameters_spec.rb b/spec/jsonapi/query_builder/errors/unpermitted_sort_parameters_spec.rb index e15eff4..4d8aa20 100644 --- a/spec/jsonapi/query_builder/errors/unpermitted_sort_parameters_spec.rb +++ b/spec/jsonapi/query_builder/errors/unpermitted_sort_parameters_spec.rb @@ -1,23 +1,23 @@ # frozen_string_literal: true RSpec.describe Jsonapi::QueryBuilder::Errors::UnpermittedSortParameters do - context "when there is a single unpermitted sort parameter" do + context 'when there is a single unpermitted sort parameter' do let(:unpermitted_parameters) { %w[first_name] } - it "raises an exception with a single unpermitted parameter error message" do - expect { + it 'raises an exception with a single unpermitted parameter error message' do + expect do raise described_class, unpermitted_parameters - }.to raise_error(described_class, "first_name is not a permitted sort attribute") + end.to raise_error(described_class, 'first_name is not a permitted sort attribute') end end - context "when there are multiple unpermitted sort parameters" do + context 'when there are multiple unpermitted sort parameters' do let(:unpermitted_parameters) { %w[first_name last_name email] } - it "raises an exception with multiple unpermitted parameters error message" do - expect { + it 'raises an exception with multiple unpermitted parameters error message' do + expect do raise described_class, unpermitted_parameters - }.to raise_error(described_class, "first_name, last_name, and email are not permitted sort attributes") + end.to raise_error(described_class, 'first_name, last_name, and email are not permitted sort attributes') end end end diff --git a/spec/jsonapi/query_builder/mixins/filter_spec.rb b/spec/jsonapi/query_builder/mixins/filter_spec.rb index c7ef9ec..c551881 100644 --- a/spec/jsonapi/query_builder/mixins/filter_spec.rb +++ b/spec/jsonapi/query_builder/mixins/filter_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe Jsonapi::QueryBuilder::Mixins::Filter do - describe "DSL" do + describe 'DSL' do subject(:filterable_query_class) do Class.new do include Jsonapi::QueryBuilder::Mixins::Filter @@ -9,23 +9,23 @@ end before do - stub_const "FilterableQuery", filterable_query_class + stub_const 'FilterableQuery', filterable_query_class end - describe ".filters_by" do - it "registers a new filter for an attribute" do + describe '.filters_by' do + it 'registers a new filter for an attribute' do FilterableQuery.filters_by :first_name expect(FilterableQuery.supported_filters).to include(:first_name) end - it "adds a default filter proc" do + it 'adds a default filter proc' do FilterableQuery.filters_by :first_name expect(FilterableQuery.supported_filters).to include(first_name: include(an_instance_of(Proc))) end - it "adds a custom filter" do + it 'adds a custom filter' do filter = ->(collection, query) { collection.where(first_name: query) } FilterableQuery.filters_by :first_name, filter @@ -33,39 +33,40 @@ expect(FilterableQuery.supported_filters).to include(first_name: include(filter)) end - it "saves filter options" do - FilterableQuery.filters_by :first_name, query_parameter: "name" + it 'saves filter options' do + FilterableQuery.filters_by :first_name, query_parameter: 'name' - expect(FilterableQuery.supported_filters).to include(first_name: include(query_parameter: "name")) + expect(FilterableQuery.supported_filters).to include(first_name: include(query_parameter: 'name')) end - it "can add multiple different filters" do - FilterableQuery.filters_by :first_name, query_parameter: "name" - FilterableQuery.filters_by :last_name, query_parameter: "surname" + it 'can add multiple different filters' do + FilterableQuery.filters_by :first_name, query_parameter: 'name' + FilterableQuery.filters_by :last_name, query_parameter: 'surname' - expect(FilterableQuery.supported_filters).to include(first_name: include(query_parameter: "name"), - last_name: include(query_parameter: "surname")) + expect(FilterableQuery.supported_filters).to include(first_name: include(query_parameter: 'name'), + last_name: include(query_parameter: 'surname')) end end end - describe "#filter" do + describe '#filter' do subject(:filter) { FilterableQuery.new(params).filter(collection) } let(:filterable_query_class) do - Class.new { + Class.new do include Jsonapi::QueryBuilder::Mixins::Filter attr_reader :params filters_by :first_name - filters_by :email, ->(collection, query) { collection.where("email ilike ?", "%#{query}%") } + filters_by :email, ->(collection, query) { collection.where('email ilike ?', "%#{query}%") } filters_by :type, TypeFilter def initialize(params) @params = params end - } + end + end end let(:type_filter_class) { class_double "TypeFilter", new: type_filter_instance } let(:type_filter_instance) { instance_double "type_filter", results: collection } @@ -81,26 +82,26 @@ def initialize(params) it { is_expected.to eql collection } - it "filters by present simple filter" do + it 'filters by present simple filter' do filter - expect(collection).to have_received(:where).with(first_name: "John") + expect(collection).to have_received(:where).with(first_name: 'John') end - it "filters by present lambda filter" do + it 'filters by present lambda filter' do filter - expect(collection).to have_received(:where).with("email ilike ?", "%john%") + expect(collection).to have_received(:where).with('email ilike ?', '%john%') end - it "filters by present class filter", :aggregate_failures do + it 'filters by present class filter', :aggregate_failures do filter - expect(TypeFilter).to have_received(:new).with(collection, "user") + expect(TypeFilter).to have_received(:new).with(collection, 'user') expect(type_filter_instance).to have_received(:results) end - it "does not filter if parameters are omitted", :aggregate_failures do + it 'does not filter if parameters are omitted', :aggregate_failures do params[:filter].delete(:first_name) filter @@ -109,30 +110,30 @@ def initialize(params) expect(collection).not_to have_received(:where).with(first_name: anything) end - context "with additional options" do - it "overrides the filter query parameter name" do - FilterableQuery.filters_by :first_name, query_parameter: "name" + context 'with additional options' do + it 'overrides the filter query parameter name' do + FilterableQuery.filters_by :first_name, query_parameter: 'name' params[:filter][:name] = params[:filter].delete(:first_name) filter - expect(collection).to have_received(:where).with(first_name: "John") + expect(collection).to have_received(:where).with(first_name: 'John') end - context "with allow nil" do + context 'with allow nil' do before do FilterableQuery.filters_by :first_name, allow_nil: true end - it "allows blank values for explicit checks against null" do - params[:filter][:first_name] = "" + it 'allows blank values for explicit checks against null' do + params[:filter][:first_name] = '' filter expect(collection).to have_received(:where).with(first_name: nil) end - it "does not check against null if parameter is not present" do + it 'does not check against null if parameter is not present' do params[:filter].delete(:first_name) filter @@ -141,30 +142,30 @@ def initialize(params) end end - context "with custom conditions" do - let(:params) { {filter: {first_name: "J", last_name: "Wi"}} } + context 'with custom conditions' do + let(:params) { { filter: { first_name: 'J', last_name: 'Wi' } } } - it "adds custom if condition", :aggregate_failures do + it 'adds custom if condition', :aggregate_failures do FilterableQuery.filters_by :first_name, if: ->(query) { query.length >= 2 } FilterableQuery.filters_by :last_name, if: ->(query) { query.length >= 2 } filter expect(collection).not_to have_received(:where).with(first_name: anything) - expect(collection).to have_received(:where).with(last_name: "Wi") + expect(collection).to have_received(:where).with(last_name: 'Wi') end - it "adds custom unless condition", :aggregate_failures do + it 'adds custom unless condition', :aggregate_failures do FilterableQuery.filters_by :first_name, unless: ->(query) { query.length < 2 } FilterableQuery.filters_by :last_name, unless: ->(query) { query.length < 2 } filter expect(collection).not_to have_received(:where).with(first_name: anything) - expect(collection).to have_received(:where).with(last_name: "Wi") + expect(collection).to have_received(:where).with(last_name: 'Wi') end - it "check the predicate method of a filter passed by a symbol", :aggregate_failures do + it 'check the predicate method of a filter passed by a symbol', :aggregate_failures do FilterableQuery.filters_by :type, TypeFilter, if: :correct_type? allow(type_filter_instance).to receive(:correct_type?).and_return(true) @@ -174,7 +175,7 @@ def initialize(params) expect(type_filter_instance).to have_received(:results) end - it "does not filter if predicate method is false" do + it 'does not filter if predicate method is false' do FilterableQuery.filters_by :type, TypeFilter, if: :correct_type? allow(type_filter_instance).to receive(:correct_type?).and_return(false) @@ -185,9 +186,9 @@ def initialize(params) end end - context "when no filters are set" do + context 'when no filters are set' do let(:filterable_query_class) do - Class.new { + Class.new do include Jsonapi::QueryBuilder::Mixins::Filter attr_reader :params @@ -195,10 +196,10 @@ def initialize(params) def initialize(params) @params = params end - } + end end - it "returns the collection" do + it 'returns the collection' do expect(filter).to eql collection end end diff --git a/spec/jsonapi/query_builder/mixins/include_spec.rb b/spec/jsonapi/query_builder/mixins/include_spec.rb index df2e8e6..ec90681 100644 --- a/spec/jsonapi/query_builder/mixins/include_spec.rb +++ b/spec/jsonapi/query_builder/mixins/include_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Jsonapi::QueryBuilder::Mixins::Include do let(:author_query_class) do - Class.new { + Class.new do include Jsonapi::QueryBuilder::Mixins::Include attr_reader :params @@ -10,14 +10,14 @@ def initialize(params) @params = params end - } + end end before do - stub_const "AuthorQuery", author_query_class + stub_const 'AuthorQuery', author_query_class end - describe "#includes" do + describe '#includes' do subject(:add_includes) { AuthorQuery.new(include_params).add_includes(collection) } let(:collection) { instance_double "collection" } @@ -27,27 +27,27 @@ def initialize(params) allow(collection).to receive(:includes).and_return(collection) end - it "returns the collection" do + it 'returns the collection' do expect(add_includes).to eql(collection) end - context "when include params are empty" do + context 'when include params are empty' do let(:include_params) { {} } - it "includes an empty array" do + it 'includes an empty array' do add_includes expect(collection).to have_received(:includes).with([]) end end - context "when include params are present and includable" do - let(:include_params) { {include: "books,posts.tags,comments"} } + context 'when include params are present and includable' do + let(:include_params) { { include: 'books,posts.tags,comments' } } - it "splits includes params to an array of nested includes" do + it 'splits includes params to an array of nested includes' do add_includes - expect(collection).to have_received(:includes).with([:books, {posts: :tags}, :comments]) + expect(collection).to have_received(:includes).with([:books, { posts: :tags }, :comments]) end end end diff --git a/spec/jsonapi/query_builder/mixins/paginate_spec.rb b/spec/jsonapi/query_builder/mixins/paginate_spec.rb index cf2324c..fe91774 100644 --- a/spec/jsonapi/query_builder/mixins/paginate_spec.rb +++ b/spec/jsonapi/query_builder/mixins/paginate_spec.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true RSpec.describe Jsonapi::QueryBuilder::Mixins::Paginate do - describe "DSL" do + describe 'DSL' do subject(:paginatable_query_class) do - require "jsonapi/query_builder/paginator/kaminari" + require 'jsonapi/query_builder/paginator/kaminari' Class.new do include Jsonapi::QueryBuilder::Mixins::Paginate @@ -11,25 +11,25 @@ end before do - stub_const "PaginatableQuery", paginatable_query_class + stub_const 'PaginatableQuery', paginatable_query_class end - describe ".paginator" do - it "registers a new paginator" do + describe '.paginator' do + it 'registers a new paginator' do PaginatableQuery.paginator Jsonapi::QueryBuilder::Paginator::Kaminari expect(PaginatableQuery._paginator).to eql Jsonapi::QueryBuilder::Paginator::Kaminari end end - describe "._paginator" do - it "defaults to Pagy paginator" do + describe '._paginator' do + it 'defaults to Pagy paginator' do expect(PaginatableQuery._paginator).to eql Jsonapi::QueryBuilder::Paginator::Pagy end end end - describe "#paginate" do + describe '#paginate' do subject(:paginate) { paged_query.paginate(collection) } let(:paged_query_class) do @@ -52,28 +52,28 @@ def initialize(params) end before do - stub_const "PagedQuery", paged_query_class + stub_const 'PagedQuery', paged_query_class allow(Jsonapi::QueryBuilder::Paginator::Pagy).to receive(:new).and_return(paginator) end - it "returns the paged collection" do + it 'returns the paged collection' do expect(paginate).to eql(paged_collection) end - it "sets pagination details" do + it 'sets pagination details' do paginate expect(paged_query.pagination_details).to eql(pagination_details) end - it "passes the collection and params to default Pagy paginator" do # rubocop:disable RSpec/MultipleExpectations + it 'passes the collection and params to default Pagy paginator' do # rubocop:disable RSpec/MultipleExpectations paginate expect(Jsonapi::QueryBuilder::Paginator::Pagy).to have_received(:new).with(collection) expect(paginator).to have_received(:paginate).with(number: 2, size: 20, offset: 0) end - it "defaults to page number 1" do + it 'defaults to page number 1' do paged_query.params[:page].delete(:number) paginate @@ -81,12 +81,12 @@ def initialize(params) expect(paginator).to have_received(:paginate).with(hash_including(number: 1)) end - context "when paginate params are passed explicitly to #paginate" do + context 'when paginate params are passed explicitly to #paginate' do subject(:paginate) { paged_query.paginate(collection, page_params) } - let(:page_params) { {number: 3, size: 30} } + let(:page_params) { { number: 3, size: 30 } } - it "overrides with the passed page params" do + it 'overrides with the passed page params' do paginate expect(paginator).to have_received(:paginate).with(number: 3, size: 30) diff --git a/spec/jsonapi/query_builder/mixins/sort/dynamic_spec.rb b/spec/jsonapi/query_builder/mixins/sort/dynamic_spec.rb index 4859862..3b206c2 100644 --- a/spec/jsonapi/query_builder/mixins/sort/dynamic_spec.rb +++ b/spec/jsonapi/query_builder/mixins/sort/dynamic_spec.rb @@ -1,51 +1,51 @@ # frozen_string_literal: true RSpec.describe Jsonapi::QueryBuilder::Mixins::Sort::Dynamic do - subject(:dynamic_sort) { described_class.new(:"data.", sort) } + subject(:dynamic_sort) { described_class.new(:'data.', sort) } - let(:collection) { instance_double("collection") } + let(:collection) { instance_double ActiveRecord::Relation } let(:sort) { ->(collection, attribute, direction) { collection.order(attribute => direction) } } - let(:sort_param) { Jsonapi::QueryBuilder::Mixins::Sort::Param.new("-data.description") } + let(:sort_param) { Jsonapi::QueryBuilder::Mixins::Sort::Param.new('-data.description') } before do allow(collection).to receive(:order).and_return(collection) end - describe "#matches?" do - context "when sort attribute starts with configured prefix" do - it "returns true" do - expect(dynamic_sort.matches?(:"data.description")).to be true + describe '#matches?' do + context 'when sort attribute starts with configured prefix' do + it 'returns true' do + expect(dynamic_sort.matches?(:'data.description')).to be true end end - context "when sort attribute does not match" do - it "returns false" do + context 'when sort attribute does not match' do + it 'returns false' do expect(dynamic_sort.matches?(:description)).to be false end end end - describe "#results" do - context "when sort is a Proc" do - it "calls the provided proc" do + describe '#results' do + context 'when sort is a Proc' do + it 'calls the provided proc' do dynamic_sort.results(collection, sort_param) - expect(collection).to have_received(:order).with("description" => :desc) + expect(collection).to have_received(:order).with('description' => :desc) end end - context "when sort is a class" do let(:sort_class_instance) { instance_double("SortClass", results: collection) } + context 'when sort is a class' do let(:sort) { SortClass } before do class_double("SortClass", new: sort_class_instance).as_stubbed_const end - it "uses the provided sort class", :aggregate_failures do + it 'uses the provided sort class', :aggregate_failures do dynamic_sort.results(collection, sort_param) - expect(SortClass).to have_received(:new).with(collection, "description", :desc) + expect(SortClass).to have_received(:new).with(collection, 'description', :desc) expect(sort_class_instance).to have_received(:results) end end diff --git a/spec/jsonapi/query_builder/mixins/sort/param_spec.rb b/spec/jsonapi/query_builder/mixins/sort/param_spec.rb index 7dea185..77d0260 100644 --- a/spec/jsonapi/query_builder/mixins/sort/param_spec.rb +++ b/spec/jsonapi/query_builder/mixins/sort/param_spec.rb @@ -3,47 +3,47 @@ RSpec.describe Jsonapi::QueryBuilder::Mixins::Sort::Param do subject(:sort_param) { described_class.new(parameter) } - let(:parameter) { "-attribute_name" } + let(:parameter) { '-attribute_name' } - it "deserializes the parameter into desc and attribute" do - expect(sort_param).to have_attributes descending: "-", attribute: "attribute_name" + it 'deserializes the parameter into desc and attribute' do + expect(sort_param).to have_attributes descending: '-', attribute: 'attribute_name' end - it "descending equals null if parameter has ascending direction" do - expect(described_class.new("attribute_name").descending).to be_nil + it 'descending equals null if parameter has ascending direction' do + expect(described_class.new('attribute_name').descending).to be_nil end - it "strips param" do - expect(described_class.new(" -attribute_name ")).to have_attributes descending: "-", attribute: "attribute_name" + it 'strips param' do + expect(described_class.new(' -attribute_name ')).to have_attributes descending: '-', attribute: 'attribute_name' end - describe ".deserialize_params" do - subject { described_class.deserialize_params("first_name,-last_name") } + describe '.deserialize_params' do + subject { described_class.deserialize_params('first_name,-last_name') } it { is_expected.to be_an Array } it { is_expected.to all be_an_instance_of described_class } - it { is_expected.to include have_attributes descending: nil, attribute: "first_name" } - it { is_expected.to include have_attributes descending: "-", attribute: "last_name" } + it { is_expected.to include have_attributes descending: nil, attribute: 'first_name' } + it { is_expected.to include have_attributes descending: '-', attribute: 'last_name' } end - describe "#serialize" do + describe '#serialize' do subject { sort_param.serialize } it { is_expected.to be_a String } - it { is_expected.to eql "-attribute_name" } + it { is_expected.to eql '-attribute_name' } end - describe "#direction" do + describe '#direction' do subject { sort_param.direction } - context "when parameter starts with -" do - let(:parameter) { "-attribute_name" } + context 'when parameter starts with -' do + let(:parameter) { '-attribute_name' } it { is_expected.to be :desc } end - context "when parameter does not start with -" do - let(:parameter) { "attribute_name" } + context 'when parameter does not start with -' do + let(:parameter) { 'attribute_name' } it { is_expected.to be :asc } end diff --git a/spec/jsonapi/query_builder/mixins/sort/static_spec.rb b/spec/jsonapi/query_builder/mixins/sort/static_spec.rb index 0abd77f..875c0b1 100644 --- a/spec/jsonapi/query_builder/mixins/sort/static_spec.rb +++ b/spec/jsonapi/query_builder/mixins/sort/static_spec.rb @@ -5,42 +5,42 @@ let(:collection) { instance_double("collection") } - let(:sort_param) { Jsonapi::QueryBuilder::Mixins::Sort::Param.new("-description") } + let(:sort_param) { Jsonapi::QueryBuilder::Mixins::Sort::Param.new('-description') } before do allow(collection).to receive(:order).and_return(collection) end - describe "#results" do - context "when sort is not given" do + describe '#results' do + context 'when sort is not given' do let(:sort) { nil } - it "defaults to ordering collection by attribute name" do + it 'defaults to ordering collection by attribute name' do static_sort.results(collection, sort_param) expect(collection).to have_received(:order).with(description: :desc) end end - context "when sort is a Proc" do + context 'when sort is a Proc' do let(:sort) { ->(collection, direction) { collection.order(foobar: direction) } } - it "calls the provided proc" do + it 'calls the provided proc' do static_sort.results(collection, sort_param) expect(collection).to have_received(:order).with(foobar: :desc) end end - context "when sort is a class" do let(:sort_class_instance) { instance_double("SortClass", results: collection) } + context 'when sort is a class' do let(:sort) { SortClass } before do class_double("SortClass", new: sort_class_instance).as_stubbed_const end - it "uses the provided sort class", :aggregate_failures do + it 'uses the provided sort class', :aggregate_failures do static_sort.results(collection, sort_param) expect(SortClass).to have_received(:new).with(collection, :desc) diff --git a/spec/jsonapi/query_builder/mixins/sort_spec.rb b/spec/jsonapi/query_builder/mixins/sort_spec.rb index acbbea6..972ffeb 100644 --- a/spec/jsonapi/query_builder/mixins/sort_spec.rb +++ b/spec/jsonapi/query_builder/mixins/sort_spec.rb @@ -1,72 +1,72 @@ # frozen_string_literal: true RSpec.describe Jsonapi::QueryBuilder::Mixins::Sort do - describe "DSL" do - subject(:sortable_query_class) { - Class.new { + describe 'DSL' do + subject(:sortable_query_class) do + Class.new do include Jsonapi::QueryBuilder::Mixins::Sort - } - } + end + end before do - stub_const "SortableQuery", sortable_query_class + stub_const 'SortableQuery', sortable_query_class end - describe ".unique_sort_attribute" do - it "defaults to id ascending if not set" do - expect(SortableQuery._unique_sort_attributes).to eql [id: :asc] + describe '.unique_sort_attribute' do + it 'defaults to id ascending if not set' do + expect(SortableQuery._unique_sort_attributes).to eql [{ id: :asc }] end - it "sets the unique sort attribute" do + it 'sets the unique sort attribute' do SortableQuery.unique_sort_attribute :email expect(SortableQuery._unique_sort_attributes).to eql [:email] end - it "sets compound unique sort attributes" do + it 'sets compound unique sort attributes' do SortableQuery.unique_sort_attributes :created_at, id: :asc - expect(SortableQuery._unique_sort_attributes).to eql [:created_at, id: :asc] + expect(SortableQuery._unique_sort_attributes).to eql [:created_at, { id: :asc }] end end - describe ".default_sort" do - it "sets the default sort setting" do + describe '.default_sort' do + it 'sets the default sort setting' do SortableQuery.default_sort created_at: :desc expect(SortableQuery._default_sort).to eql created_at: :desc end - it "sets the default sort to a proc" do + it 'sets the default sort to a proc' do SortableQuery.default_sort ->(collection) { collection.order(created_at: :desc) } expect(SortableQuery._default_sort).to be_an_instance_of(Proc) end - it "sets the default sort to a sort class" do - street_sort_class = class_double "StreetSort" + it 'sets the default sort to a sort class' do + street_sort_class = Class.new(Jsonapi::QueryBuilder::Mixins::Sort::Static) SortableQuery.default_sort street_sort_class expect(SortableQuery._default_sort).to be street_sort_class end end - describe ".sorts_by" do - it "registers a supported sort attribute" do + describe '.sorts_by' do + it 'registers a supported sort attribute' do SortableQuery.sorts_by :first_name expect(SortableQuery.supported_sorts).to include(:first_name) end - it "adds a default sort proc" do + it 'adds a default sort proc' do SortableQuery.sorts_by :first_name expect(SortableQuery.supported_sorts) .to include(first_name: an_instance_of(Jsonapi::QueryBuilder::Mixins::Sort::Static) - .and(have_attributes(attribute: :first_name, sort: an_instance_of(Proc)))) + .and(have_attributes(attribute: :first_name, sort: an_instance_of(Proc)))) end - it "adds a custom sort" do + it 'adds a custom sort' do sort = ->(collection, direction) { collection.order(first_name: direction) } SortableQuery.sorts_by :first_name, sort @@ -74,7 +74,7 @@ expect(SortableQuery.supported_sorts).to include(first_name: have_attributes(sort: sort)) end - it "can add multiple different sorts" do + it 'can add multiple different sorts' do SortableQuery.sorts_by :first_name SortableQuery.sorts_by :last_name @@ -82,32 +82,32 @@ end end - describe ".dynamically_sorts_by" do - it "registers a supported dynamic sort attribute" do + describe '.dynamically_sorts_by' do + it 'registers a supported dynamic sort attribute' do sort = Class.new(Jsonapi::QueryBuilder::DynamicSort) SortableQuery.dynamically_sorts_by :address, sort expect(SortableQuery.supported_dynamic_sorts) .to include(an_instance_of(Jsonapi::QueryBuilder::Mixins::Sort::Dynamic) - .and(have_attributes(attribute_prefix: "address", sort: sort))) + .and(have_attributes(attribute_prefix: 'address', sort: sort))) end - it "can add multiple different dynamic sorts" do + it 'can add multiple different dynamic sorts' do sort = Class.new(Jsonapi::QueryBuilder::DynamicSort) SortableQuery.dynamically_sorts_by :address, sort SortableQuery.dynamically_sorts_by :email, sort - expect(SortableQuery.supported_dynamic_sorts).to include(have_attributes(attribute_prefix: "address"), - have_attributes(attribute_prefix: "email")) + expect(SortableQuery.supported_dynamic_sorts).to include(have_attributes(attribute_prefix: 'address'), + have_attributes(attribute_prefix: 'email')) end end end - describe "#sort" do + describe '#sort' do subject(:sort) { SortableQuery.new(collection, params).sort(collection) } - let(:sortable_query_class) { - Class.new { + let(:sortable_query_class) do + Class.new do include Jsonapi::QueryBuilder::Mixins::Sort attr_reader :params @@ -115,15 +115,13 @@ unique_sort_attribute id: :asc sorts_by :last_name sorts_by :first_name, ->(collection, direction) { collection.order(name: direction) } - sorts_by :"address.street", StreetSort - dynamically_sorts_by :"data.", DynamicSort + sorts_by :'address.street', StreetSort + dynamically_sorts_by :'data.', DynamicSort def initialize(collection, params) @collection = collection @params = params end - } - } let(:street_sort_class) { class_double "StreetSort", new: street_sort_instance } let(:street_sort_instance) { instance_double "street_sort", results: collection } let(:dynamic_sort_class) { class_double "DynamicSort", new: dynamic_sort_instance } @@ -141,42 +139,42 @@ def initialize(collection, params) it { is_expected.to eql collection } - it "sorts by the present simple sort" do + it 'sorts by the present simple sort' do sort expect(collection).to have_received(:order).with(last_name: :desc) end - it "sorts by the present lambda sort" do + it 'sorts by the present lambda sort' do sort expect(collection).to have_received(:order).with(name: :asc) end - it "sorts by present class sort", :aggregate_failures do + it 'sorts by present class sort', :aggregate_failures do sort expect(StreetSort).to have_received(:new).with(collection, :asc) expect(street_sort_instance).to have_received(:results) end - it "sorts by present dynamic sort", :aggregate_failures do + it 'sorts by present dynamic sort', :aggregate_failures do sort - expect(DynamicSort).to have_received(:new).with(collection, "foobar", :asc) + expect(DynamicSort).to have_received(:new).with(collection, 'foobar', :asc) expect(dynamic_sort_instance).to have_received(:results) end - it "adds the unique sort attribute" do + it 'adds the unique sort attribute' do sort expect(collection).to have_received(:order).with(id: :asc) end - context "when sort params are empty and a default sort is not set" do + context 'when sort params are empty and a default sort is not set' do let(:params) { {} } - it "fallbacks to the unique sort attributes", :aggregate_failures do + it 'fallbacks to the unique sort attributes', :aggregate_failures do sort expect(collection).to have_received(:order).once @@ -184,55 +182,55 @@ def initialize(collection, params) end end - context "when sort params are empty and a default sort is set" do + context 'when sort params are empty and a default sort is set' do let(:params) { {} } before do SortableQuery.default_sort :first_name end - it "adds a default sort" do + it 'adds a default sort' do sort expect(collection).to have_received(:order).with(:first_name) end - it "ensures a unique sort attribute" do + it 'ensures a unique sort attribute' do sort expect(collection).to have_received(:order).with(id: :asc) end - context "when default sort has a direction set" do + context 'when default sort has a direction set' do before do SortableQuery.default_sort first_name: :desc end - it "add the default sort with the direction" do + it 'add the default sort with the direction' do sort expect(collection).to have_received(:order).with(first_name: :desc) end end - context "when default sort is a proc" do + context 'when default sort is a proc' do before do SortableQuery.default_sort ->(collection) { collection.order(first_name: :desc) } end - it "applies the sort proc" do + it 'applies the sort proc' do sort expect(collection).to have_received(:order).with(first_name: :desc) end end - context "when default sort is a sort object" do + context 'when default sort is a sort object' do before do SortableQuery.default_sort StreetSort end - it "sorts with the sort object", :aggregate_failures do + it 'sorts with the sort object', :aggregate_failures do sort expect(StreetSort).to have_received(:new).with(collection) @@ -241,25 +239,25 @@ def initialize(collection, params) end end - context "when one or more of sort params is not permitted" do - let(:params) { {sort: "first_name,-data.some_nested_prop,email,-birth_date"} } + context 'when one or more of sort params is not permitted' do + let(:params) { { sort: 'first_name,-data.some_nested_prop,email,-birth_date' } } - context "when query does not support nested parameters" do - it "raises an unpermitted sort parameters error" do + context 'when query does not support nested parameters' do + it 'raises an unpermitted sort parameters error' do expect { sort }.to raise_error( Jsonapi::QueryBuilder::Errors::UnpermittedSortParameters, - "email and birth_date are not permitted sort attributes" + 'email and birth_date are not permitted sort attributes' ) end end end - context "when sort params are passed explicitly to #sort" do + context 'when sort params are passed explicitly to #sort' do subject(:sort) do - SortableQuery.new(collection, {sort: "email"}).sort(collection, "first_name,-last_name") + SortableQuery.new(collection, { sort: 'email' }).sort(collection, 'first_name,-last_name') end - it "overrides with the passed sort string", :aggregate_failures do + it 'overrides with the passed sort string', :aggregate_failures do sort expect(collection).not_to have_received(:order).with(email: :asc) diff --git a/spec/jsonapi/query_builder/paginator/base_paginator_spec.rb b/spec/jsonapi/query_builder/paginator/base_paginator_spec.rb index 5034705..693df52 100644 --- a/spec/jsonapi/query_builder/paginator/base_paginator_spec.rb +++ b/spec/jsonapi/query_builder/paginator/base_paginator_spec.rb @@ -2,16 +2,16 @@ RSpec.describe Jsonapi::QueryBuilder::Paginator::BasePaginator do before do - stub_const "FakePaginator", paginator_class + stub_const 'FakePaginator', paginator_class end - context "with required interface methods" do + context 'with required interface methods' do let(:paginator_class) { Class.new described_class } - it "raises an error for paginate method" do - expect { - FakePaginator.new(instance_double("collection")).paginate(instance_double("page_params")) - }.to raise_error(NotImplementedError, "FakePaginator should implement #paginate") + it 'raises an error for paginate method' do + expect do + FakePaginator.new(instance_double(ActiveRecord::Relation)).paginate({}) + end.to raise_error(NotImplementedError, 'FakePaginator should implement #paginate') end end end diff --git a/spec/jsonapi/query_builder/paginator/kaminari_spec.rb b/spec/jsonapi/query_builder/paginator/kaminari_spec.rb index 8249c88..7e2f53e 100644 --- a/spec/jsonapi/query_builder/paginator/kaminari_spec.rb +++ b/spec/jsonapi/query_builder/paginator/kaminari_spec.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require "jsonapi/query_builder/paginator/kaminari" +require 'jsonapi/query_builder/paginator/kaminari' RSpec.describe Jsonapi::QueryBuilder::Paginator::Kaminari do - describe "#paginate" do + describe '#paginate' do subject(:paginate) { described_class.new(collection).paginate(number: 2, size: 20, offset: 3) } let(:collection) { instance_double "collection" } @@ -24,19 +24,19 @@ it { is_expected.to be_an Array } - it "returns the paged collection as first item of the returned array" do + it 'returns the paged collection as first item of the returned array' do expect(paginate[0]).to eql paged_collection end - it "returns the pagination details as the second item of the returned array" do - expected_pagination_details = {number: 2, size: 20, offset: 3, - total: 35, total_pages: 2, - next_page: nil, prev_page: 1} + it 'returns the pagination details as the second item of the returned array' do + expected_pagination_details = { number: 2, size: 20, offset: 3, + total: 35, total_pages: 2, + next_page: nil, prev_page: 1 } expect(paginate[1]).to eql expected_pagination_details end - it "calls the kaminari pagination methods on the passed collection", :aggregate_failures do + it 'calls the kaminari pagination methods on the passed collection', :aggregate_failures do paginate expect(collection).to have_received(:page).with(2) diff --git a/spec/jsonapi/query_builder/paginator/keyset_spec.rb b/spec/jsonapi/query_builder/paginator/keyset_spec.rb index 6579c6b..d293873 100644 --- a/spec/jsonapi/query_builder/paginator/keyset_spec.rb +++ b/spec/jsonapi/query_builder/paginator/keyset_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require "support/active_record" -require "jsonapi/query_builder/paginator/keyset" +require 'support/active_record' +require 'jsonapi/query_builder/paginator/keyset' # rubocop:disable RSpec/ExampleLength RSpec.describe Jsonapi::QueryBuilder::Paginator::Keyset do @@ -16,8 +16,8 @@ SQL end - it "orders collection by the selected column" do - params = {column: :id} + it 'orders collection by the selected column' do + params = { column: :id } paginated_collection, _details = keyset_paginator.paginate(params) expect(paginated_collection.to_sql).to eq <<~SQL.squish @@ -28,8 +28,8 @@ SQL end - it "applies selected limit" do - params = {column: :id, limit: 10} + it 'applies selected limit' do + params = { column: :id, limit: 10 } paginated_collection, _details = keyset_paginator.paginate(params) expect(paginated_collection.to_sql).to eq <<~SQL.squish @@ -41,8 +41,8 @@ end context "when direction isn't provided" do - it "filters records after the selected position" do - params = {column: :id, limit: 10, position: 5} + it 'filters records after the selected position' do + params = { column: :id, limit: 10, position: 5 } paginated_collection, _details = keyset_paginator.paginate(params) expect(paginated_collection.to_sql).to eq <<~SQL.squish @@ -55,9 +55,9 @@ end end - context "when selecting records after the position" do - it "filters records after the selected position" do - params = {column: :id, limit: 10, position: 5, direction: :after} + context 'when selecting records after the position' do + it 'filters records after the selected position' do + params = { column: :id, limit: 10, position: 5, direction: :after } paginated_collection, _details = keyset_paginator.paginate(params) expect(paginated_collection.to_sql).to eq <<~SQL.squish @@ -70,9 +70,9 @@ end end - context "when selecting records before the position" do - it "filters records before the selected position" do - params = {column: :id, limit: 10, position: 5, direction: :before} + context 'when selecting records before the position' do + it 'filters records before the selected position' do + params = { column: :id, limit: 10, position: 5, direction: :before } paginated_collection, _details = keyset_paginator.paginate(params) expect(paginated_collection.to_sql).to eq <<~SQL.squish diff --git a/spec/jsonapi/query_builder/paginator/pagy_countless_spec.rb b/spec/jsonapi/query_builder/paginator/pagy_countless_spec.rb index aa3858d..71f11e2 100644 --- a/spec/jsonapi/query_builder/paginator/pagy_countless_spec.rb +++ b/spec/jsonapi/query_builder/paginator/pagy_countless_spec.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require "jsonapi/query_builder/paginator/pagy_countless" +require 'jsonapi/query_builder/paginator/pagy_countless' RSpec.describe Jsonapi::QueryBuilder::Paginator::PagyCountless do - describe "#paginate" do + describe '#paginate' do subject(:paginate) { pagy_paginator.paginate(number: 2, size: 20, offset: 3) } let(:pagy_paginator) { described_class.new(collection) } @@ -17,20 +17,20 @@ it { is_expected.to be_an Array } - it "returns the records and pagination details" do + it 'returns the records and pagination details' do expect(paginate).to eql [paged_collection, pagination_details] end - it "passes the collection and pagination params to pagy_countless method" do + it 'passes the collection and pagination params to pagy_countless method' do paginate expect(pagy_paginator).to have_received(:pagy_countless).with(collection, page: 2, items: 20, outset: 3) end - it "sets the private params attribute reader used internally by pagy backend" do + it 'sets the private params attribute reader used internally by pagy backend' do paginate - expect(pagy_paginator.send(:params)).to eql page: {number: 2, size: 20, offset: 3} + expect(pagy_paginator.send(:params)).to eql page: { number: 2, size: 20, offset: 3 } end end end diff --git a/spec/jsonapi/query_builder/paginator/pagy_spec.rb b/spec/jsonapi/query_builder/paginator/pagy_spec.rb index ea5a6fd..2b88b67 100644 --- a/spec/jsonapi/query_builder/paginator/pagy_spec.rb +++ b/spec/jsonapi/query_builder/paginator/pagy_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe Jsonapi::QueryBuilder::Paginator::Pagy do - describe "#paginate" do + describe '#paginate' do subject(:paginate) { pagy_paginator.paginate(number: 2, size: 20, offset: 3) } let(:pagy_paginator) { described_class.new(collection) } @@ -15,20 +15,20 @@ it { is_expected.to be_an Array } - it "returns the records and pagination details" do + it 'returns the records and pagination details' do expect(paginate).to eql [paged_collection, pagination_details] end - it "passes the collection and pagination params to pagy method" do + it 'passes the collection and pagination params to pagy method' do paginate expect(pagy_paginator).to have_received(:pagy).with(collection, page: 2, items: 20, outset: 3) end - it "sets the private params attribute reader used internally by pagy backend" do + it 'sets the private params attribute reader used internally by pagy backend' do paginate - expect(pagy_paginator.send(:params)).to eql page: {number: 2, size: 20, offset: 3} + expect(pagy_paginator.send(:params)).to eql page: { number: 2, size: 20, offset: 3 } end end end diff --git a/spec/jsonapi/query_builder_spec.rb b/spec/jsonapi/query_builder_spec.rb index 57f8e22..b556c04 100644 --- a/spec/jsonapi/query_builder_spec.rb +++ b/spec/jsonapi/query_builder_spec.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true RSpec.describe Jsonapi::QueryBuilder do - it "has a version number" do - expect(Jsonapi::QueryBuilder::VERSION).not_to be nil + it 'has a version number' do + expect(Jsonapi::QueryBuilder::VERSION).not_to be_nil end - context "with controller usage" do + context 'with controller usage' do subject(:query) { Query.new(collection, params) } - let(:query_class) { - Class.new(Jsonapi::QueryBuilder::BaseQuery) { + let(:query_class) do + Class.new(Jsonapi::QueryBuilder::BaseQuery) do paginator Jsonapi::QueryBuilder::Paginator::Pagy default_sort :last_name @@ -17,12 +17,12 @@ sorts_by :last_name filters_by :first_name - filters_by :email, ->(collection, query) { collection.where("email ilike ?", "%#{query}%") } + filters_by :email, ->(collection, query) { collection.where('email ilike ?', "%#{query}%") } filters_by :type, TypeFilter, if: :correct_type? - } - } - let(:type_filter_class) { - Class.new(Jsonapi::QueryBuilder::BaseFilter) { + end + end + let(:type_filter_class) do + Class.new(Jsonapi::QueryBuilder::BaseFilter) do def query super.camelize end @@ -34,19 +34,19 @@ def results def correct_type? %w[User Admin].include?(query) end - } - } + end + end let(:collection) { instance_double "collection" } - let(:params) { + let(:params) do { - "sort" => "first_name,-last_name", - "include" => "books.tags", - "filter" => {"email" => "j", "type" => "user"}, - "page" => {"number" => 1, "size" => 20, "offset" => 0} + 'sort' => 'first_name,-last_name', + 'include' => 'books.tags', + 'filter' => { 'email' => 'j', 'type' => 'user' }, + 'page' => { 'number' => 1, 'size' => 20, 'offset' => 0 } } - } + end before do stub_const "TypeFilter", type_filter_class @@ -62,38 +62,38 @@ def correct_type? it { is_expected.to have_attributes(results: collection, pagination_details: an_instance_of(Pagy)) } - describe "#results" do + describe '#results' do subject(:results) { query.results } it { is_expected.to eql collection } - it "includes included relationships" do + it 'includes included relationships' do results - expect(collection).to have_received(:includes).with([{books: :tags}]) + expect(collection).to have_received(:includes).with([{ books: :tags }]) end - it "orders the collection", :aggregate_failures do + it 'orders the collection', :aggregate_failures do results expect(collection).to have_received(:order).with(first_name: :asc) expect(collection).to have_received(:order).with(last_name: :desc) end - it "adds unique sort attribute" do + it 'adds unique sort attribute' do results expect(collection).to have_received(:order).with(id: :asc) end - it "filters the collection by passed filter params", :aggregate_failures do + it 'filters the collection by passed filter params', :aggregate_failures do results - expect(collection).to have_received(:where).with("email ilike ?", "%j%") - expect(collection).to have_received(:where).with(type: "User") + expect(collection).to have_received(:where).with('email ilike ?', '%j%') + expect(collection).to have_received(:where).with(type: 'User') end - it "paginates the collection", :aggregate_failures do + it 'paginates the collection', :aggregate_failures do results expect(collection).to have_received(:count) @@ -102,7 +102,7 @@ def correct_type? end end - describe "#find" do + describe '#find' do subject(:find) { query.find(1) } let(:record) { instance_double "record" } @@ -113,13 +113,13 @@ def correct_type? it { is_expected.to eql record } - it "includes included relationships" do + it 'includes included relationships' do find - expect(collection).to have_received(:includes).with([{books: :tags}]) + expect(collection).to have_received(:includes).with([{ books: :tags }]) end - it "finds the record" do + it 'finds the record' do find expect(collection).to have_received(:find_by!).with(id: 1) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index bbf0f25..2ea3487 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true -require "bundler/setup" -require "jsonapi/query_builder" +require 'bundler/setup' +require 'jsonapi/query_builder' +require 'active_record' RSpec.configure do |config| # Enable flags like --only-failures and --next-failure - config.example_status_persistence_file_path = ".rspec_status" + config.example_status_persistence_file_path = '.rspec_status' # Disable RSpec exposing methods globally on `Module` and `main` config.disable_monkey_patching! diff --git a/spec/support/active_record.rb b/spec/support/active_record.rb index f6013d4..f60c3f3 100644 --- a/spec/support/active_record.rb +++ b/spec/support/active_record.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require "active_record" +require 'active_record' -ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") +ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') ActiveRecord::Schema.define do create_table :posts, force: true do |t| t.string :title From 5874cedf43af40a6f2d1b302237ad9e543ec4418 Mon Sep 17 00:00:00 2001 From: Vedran Hrncic Date: Mon, 20 Apr 2026 18:52:19 +0200 Subject: [PATCH 5/5] Use verified doubles in specs --- .../jsonapi/query_builder/base_filter_spec.rb | 4 +-- spec/jsonapi/query_builder/base_query_spec.rb | 12 ++++----- .../query_builder/dynamic_sort_spec.rb | 4 +-- .../query_builder/mixins/filter_spec.rb | 16 +++++++----- .../query_builder/mixins/include_spec.rb | 2 +- .../query_builder/mixins/paginate_spec.rb | 8 +++--- .../query_builder/mixins/sort/dynamic_spec.rb | 7 +++-- .../query_builder/mixins/sort/static_spec.rb | 9 ++++--- .../jsonapi/query_builder/mixins/sort_spec.rb | 22 +++++++++------- .../query_builder/paginator/kaminari_spec.rb | 26 ++++++++++++------- .../paginator/pagy_countless_spec.rb | 6 ++--- .../query_builder/paginator/pagy_spec.rb | 6 ++--- spec/jsonapi/query_builder_spec.rb | 18 +++++-------- 13 files changed, 77 insertions(+), 63 deletions(-) diff --git a/spec/jsonapi/query_builder/base_filter_spec.rb b/spec/jsonapi/query_builder/base_filter_spec.rb index b77305c..32844b5 100644 --- a/spec/jsonapi/query_builder/base_filter_spec.rb +++ b/spec/jsonapi/query_builder/base_filter_spec.rb @@ -8,9 +8,9 @@ context 'with required interface methods' do let(:filter_class) { Class.new(described_class) } - expect { FakeFilter.new(instance_double("collection"), "query").results }.to raise_error( - NotImplementedError, "FakeFilter should implement #results" it 'raises an error for results method' do + expect { FakeFilter.new(instance_double(ActiveRecord::Relation), 'query').results }.to raise_error( + NotImplementedError, 'FakeFilter should implement #results' ) end end diff --git a/spec/jsonapi/query_builder/base_query_spec.rb b/spec/jsonapi/query_builder/base_query_spec.rb index 9dd91ba..533efd0 100644 --- a/spec/jsonapi/query_builder/base_query_spec.rb +++ b/spec/jsonapi/query_builder/base_query_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Jsonapi::QueryBuilder::BaseQuery do let(:query) { described_class.new(collection, params) } - let(:collection) { instance_double "collection" } + let(:collection) { instance_double ActiveRecord::Relation } before do allow(query).to receive(:add_includes).and_return(collection) @@ -11,9 +11,8 @@ describe '#results' do subject(:results) { query.results } - let(:filtered_collection) { instance_double "filtered_collection" } - let(:paged_filtered_collection) { instance_double "paged_filtered_collection" } - let(:params) { + let(:filtered_collection) { instance_double ActiveRecord::Relation } + let(:paged_filtered_collection) { instance_double ActiveRecord::Relation } let(:params) do { sort: 'last_name,first_name', @@ -24,9 +23,8 @@ end before do - allow(query).to receive(:sort).and_return(collection) - allow(query).to receive(:filter).and_return(filtered_collection) - allow(query).to receive(:paginate).and_return(paged_filtered_collection) + allow(query).to receive_messages(sort: collection, filter: filtered_collection, + paginate: paged_filtered_collection) end it 'returns the collection' do diff --git a/spec/jsonapi/query_builder/dynamic_sort_spec.rb b/spec/jsonapi/query_builder/dynamic_sort_spec.rb index a6da7ae..6f7219a 100644 --- a/spec/jsonapi/query_builder/dynamic_sort_spec.rb +++ b/spec/jsonapi/query_builder/dynamic_sort_spec.rb @@ -7,13 +7,13 @@ stub_const 'FakeSort', sort_class end - expect(FakeSort.new(instance_double("collection"), "attribute")).to have_attributes(direction: :asc) it 'defaults to ascending sort direction' do + expect(FakeSort.new(instance_double(ActiveRecord::Relation), 'attribute')).to have_attributes(direction: :asc) end - expect { FakeSort.new(instance_double("collection"), "attribute", :desc).results }.to raise_error( context 'with required interface methods' do it 'raises an error for results method' do + expect { FakeSort.new(instance_double(ActiveRecord::Relation), 'attribute', :desc).results }.to raise_error( NotImplementedError, 'FakeSort should implement #results' ) end diff --git a/spec/jsonapi/query_builder/mixins/filter_spec.rb b/spec/jsonapi/query_builder/mixins/filter_spec.rb index c551881..909820c 100644 --- a/spec/jsonapi/query_builder/mixins/filter_spec.rb +++ b/spec/jsonapi/query_builder/mixins/filter_spec.rb @@ -67,16 +67,20 @@ def initialize(params) end end end + let(:type_filter_class) do + Class.new(Jsonapi::QueryBuilder::BaseFilter) do + def correct_type? = true + end end - let(:type_filter_class) { class_double "TypeFilter", new: type_filter_instance } - let(:type_filter_instance) { instance_double "type_filter", results: collection } - let(:collection) { instance_double "collection" } - let(:params) { {filter: {first_name: "John", email: "john", type: "user"}} } + let(:type_filter_instance) { instance_double(TypeFilter, results: collection) } + let(:collection) { instance_double ActiveRecord::Relation } + let(:params) { { filter: { first_name: 'John', email: 'john', type: 'user' } } } before do - stub_const "TypeFilter", type_filter_class - stub_const "FilterableQuery", filterable_query_class + stub_const 'TypeFilter', type_filter_class + stub_const 'FilterableQuery', filterable_query_class + allow(TypeFilter).to receive(:new).and_return(type_filter_instance) allow(collection).to receive(:where).and_return(collection) end diff --git a/spec/jsonapi/query_builder/mixins/include_spec.rb b/spec/jsonapi/query_builder/mixins/include_spec.rb index ec90681..2799088 100644 --- a/spec/jsonapi/query_builder/mixins/include_spec.rb +++ b/spec/jsonapi/query_builder/mixins/include_spec.rb @@ -20,7 +20,7 @@ def initialize(params) describe '#includes' do subject(:add_includes) { AuthorQuery.new(include_params).add_includes(collection) } - let(:collection) { instance_double "collection" } + let(:collection) { instance_double ActiveRecord::Relation } let(:include_params) { {} } before do diff --git a/spec/jsonapi/query_builder/mixins/paginate_spec.rb b/spec/jsonapi/query_builder/mixins/paginate_spec.rb index fe91774..f191669 100644 --- a/spec/jsonapi/query_builder/mixins/paginate_spec.rb +++ b/spec/jsonapi/query_builder/mixins/paginate_spec.rb @@ -43,10 +43,10 @@ def initialize(params) end end end - let(:collection) { instance_double "collection" } - let(:paged_query) { PagedQuery.new page: {number: 2, size: 20, offset: 0} } - let(:paged_collection) { instance_double "paged-collection" } - let(:pagination_details) { instance_double "pagination-details" } + let(:collection) { instance_double ActiveRecord::Relation } + let(:paged_query) { PagedQuery.new page: { number: 2, size: 20, offset: 0 } } + let(:paged_collection) { instance_double Object } + let(:pagination_details) { instance_double Object } let(:paginator) do instance_double Jsonapi::QueryBuilder::Paginator::Pagy, paginate: [paged_collection, pagination_details] end diff --git a/spec/jsonapi/query_builder/mixins/sort/dynamic_spec.rb b/spec/jsonapi/query_builder/mixins/sort/dynamic_spec.rb index 3b206c2..c7103a3 100644 --- a/spec/jsonapi/query_builder/mixins/sort/dynamic_spec.rb +++ b/spec/jsonapi/query_builder/mixins/sort/dynamic_spec.rb @@ -34,12 +34,15 @@ end end - let(:sort_class_instance) { instance_double("SortClass", results: collection) } context 'when sort is a class' do + let(:sort_class) { Class.new(described_class) } + let(:sort_class_instance) { instance_double(SortClass, results: collection) } let(:sort) { SortClass } before do - class_double("SortClass", new: sort_class_instance).as_stubbed_const + stub_const('SortClass', sort_class) + + allow(SortClass).to receive(:new).and_return(sort_class_instance) end it 'uses the provided sort class', :aggregate_failures do diff --git a/spec/jsonapi/query_builder/mixins/sort/static_spec.rb b/spec/jsonapi/query_builder/mixins/sort/static_spec.rb index 875c0b1..f577a86 100644 --- a/spec/jsonapi/query_builder/mixins/sort/static_spec.rb +++ b/spec/jsonapi/query_builder/mixins/sort/static_spec.rb @@ -3,7 +3,7 @@ RSpec.describe Jsonapi::QueryBuilder::Mixins::Sort::Static do subject(:static_sort) { described_class.new(:description, sort) } - let(:collection) { instance_double("collection") } + let(:collection) { instance_double(ActiveRecord::Relation) } let(:sort_param) { Jsonapi::QueryBuilder::Mixins::Sort::Param.new('-description') } @@ -32,12 +32,15 @@ end end - let(:sort_class_instance) { instance_double("SortClass", results: collection) } context 'when sort is a class' do + let(:sort_class) { Class.new(described_class) } + let(:sort_class_instance) { instance_double(SortClass, results: collection) } let(:sort) { SortClass } before do - class_double("SortClass", new: sort_class_instance).as_stubbed_const + stub_const('SortClass', sort_class) + + allow(SortClass).to receive(:new).and_return(sort_class_instance) end it 'uses the provided sort class', :aggregate_failures do diff --git a/spec/jsonapi/query_builder/mixins/sort_spec.rb b/spec/jsonapi/query_builder/mixins/sort_spec.rb index 972ffeb..58db2ac 100644 --- a/spec/jsonapi/query_builder/mixins/sort_spec.rb +++ b/spec/jsonapi/query_builder/mixins/sort_spec.rb @@ -122,18 +122,22 @@ def initialize(collection, params) @collection = collection @params = params end - let(:street_sort_class) { class_double "StreetSort", new: street_sort_instance } - let(:street_sort_instance) { instance_double "street_sort", results: collection } - let(:dynamic_sort_class) { class_double "DynamicSort", new: dynamic_sort_instance } - let(:dynamic_sort_instance) { instance_double "dynamic_sort", results: collection } - let(:collection) { instance_double "collection" } - let(:params) { {sort: "first_name,-last_name,address.street,data.foobar"} } + end + end + let(:street_sort_class) { Class.new(Jsonapi::QueryBuilder::Mixins::Sort::Static) } + let(:street_sort_instance) { instance_double StreetSort, results: collection } + let(:dynamic_sort_class) { Class.new(Jsonapi::QueryBuilder::Mixins::Sort::Dynamic) } + let(:dynamic_sort_instance) { instance_double DynamicSort, results: collection } + let(:collection) { instance_double ActiveRecord::Relation } + let(:params) { { sort: 'first_name,-last_name,address.street,data.foobar' } } before do - stub_const "StreetSort", street_sort_class - stub_const "DynamicSort", dynamic_sort_class - stub_const "SortableQuery", sortable_query_class + stub_const 'StreetSort', street_sort_class + stub_const 'DynamicSort', dynamic_sort_class + stub_const 'SortableQuery', sortable_query_class + allow(StreetSort).to receive(:new).and_return(street_sort_instance) + allow(DynamicSort).to receive(:new).and_return(dynamic_sort_instance) allow(collection).to receive(:order).and_return(collection) end diff --git a/spec/jsonapi/query_builder/paginator/kaminari_spec.rb b/spec/jsonapi/query_builder/paginator/kaminari_spec.rb index 7e2f53e..ab05672 100644 --- a/spec/jsonapi/query_builder/paginator/kaminari_spec.rb +++ b/spec/jsonapi/query_builder/paginator/kaminari_spec.rb @@ -6,20 +6,26 @@ describe '#paginate' do subject(:paginate) { described_class.new(collection).paginate(number: 2, size: 20, offset: 3) } - let(:collection) { instance_double "collection" } + let(:collection_class) do + Class.new(Kaminari::PaginatableArray) do + include Kaminari::PageScopeMethods + + def page(*); end + end + end + + let(:collection) { instance_double collection_class } let(:paged_collection) do - instance_double("paged-collection", current_page: 2, - limit_value: 20, - total_count: 35, - total_pages: 2, - next_page: nil, - prev_page: 1) + instance_double(collection_class, current_page: 2, + limit_value: 20, + total_count: 35, + total_pages: 2, + next_page: nil, + prev_page: 1) end before do - allow(collection).to receive(:page).and_return(collection) - allow(collection).to receive(:per).and_return(collection) - allow(collection).to receive(:padding).and_return(paged_collection) + allow(collection).to receive_messages(page: collection, per: collection, padding: paged_collection) end it { is_expected.to be_an Array } diff --git a/spec/jsonapi/query_builder/paginator/pagy_countless_spec.rb b/spec/jsonapi/query_builder/paginator/pagy_countless_spec.rb index 71f11e2..e6f0a49 100644 --- a/spec/jsonapi/query_builder/paginator/pagy_countless_spec.rb +++ b/spec/jsonapi/query_builder/paginator/pagy_countless_spec.rb @@ -7,9 +7,9 @@ subject(:paginate) { pagy_paginator.paginate(number: 2, size: 20, offset: 3) } let(:pagy_paginator) { described_class.new(collection) } - let(:collection) { instance_double "collection" } - let(:paged_collection) { instance_double "paged-collection" } - let(:pagination_details) { instance_double Pagy, "pagination-details" } + let(:collection) { instance_double ActiveRecord::Relation } + let(:paged_collection) { instance_double Object } + let(:pagination_details) { instance_double Pagy, 'pagination-details' } before do allow(pagy_paginator).to receive(:pagy_countless).and_return([pagination_details, paged_collection]) diff --git a/spec/jsonapi/query_builder/paginator/pagy_spec.rb b/spec/jsonapi/query_builder/paginator/pagy_spec.rb index 2b88b67..9f8b4d4 100644 --- a/spec/jsonapi/query_builder/paginator/pagy_spec.rb +++ b/spec/jsonapi/query_builder/paginator/pagy_spec.rb @@ -5,9 +5,9 @@ subject(:paginate) { pagy_paginator.paginate(number: 2, size: 20, offset: 3) } let(:pagy_paginator) { described_class.new(collection) } - let(:collection) { instance_double "collection" } - let(:paged_collection) { instance_double "paged-collection" } - let(:pagination_details) { instance_double Pagy, "pagination-details" } + let(:collection) { instance_double ActiveRecord::Relation } + let(:paged_collection) { instance_double Object } + let(:pagination_details) { instance_double Pagy, 'pagination-details' } before do allow(pagy_paginator).to receive(:pagy).and_return([pagination_details, paged_collection]) diff --git a/spec/jsonapi/query_builder_spec.rb b/spec/jsonapi/query_builder_spec.rb index b556c04..dbec25c 100644 --- a/spec/jsonapi/query_builder_spec.rb +++ b/spec/jsonapi/query_builder_spec.rb @@ -37,7 +37,7 @@ def correct_type? end end - let(:collection) { instance_double "collection" } + let(:collection) { instance_double ActiveRecord::Relation } let(:params) do { @@ -49,15 +49,11 @@ def correct_type? end before do - stub_const "TypeFilter", type_filter_class - stub_const "Query", query_class - - allow(collection).to receive(:order).and_return(collection) - allow(collection).to receive(:includes).and_return(collection) - allow(collection).to receive(:where).and_return(collection) - allow(collection).to receive(:count).and_return(2) - allow(collection).to receive(:offset).and_return(collection) - allow(collection).to receive(:limit).and_return(collection) + stub_const 'TypeFilter', type_filter_class + stub_const 'Query', query_class + + allow(collection).to receive_messages(order: collection, includes: collection, where: collection, count: 2, + offset: collection, limit: collection) end it { is_expected.to have_attributes(results: collection, pagination_details: an_instance_of(Pagy)) } @@ -105,7 +101,7 @@ def correct_type? describe '#find' do subject(:find) { query.find(1) } - let(:record) { instance_double "record" } + let(:record) { instance_double Object } before do allow(collection).to receive(:find_by!).and_return(record)