From b0aa6ce2078859a6f16d2e2759edcd46fd7d2e88 Mon Sep 17 00:00:00 2001 From: Greg Orlov Date: Thu, 31 May 2018 09:38:47 -0700 Subject: [PATCH 01/10] updating readme updating testing instructions to run all the tests --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bc2a712..1ccd6ad 100644 --- a/README.md +++ b/README.md @@ -816,7 +816,7 @@ Content-Type: application/vnd.api+json ## Development -After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. +After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec --pattern "**{,/*/**}/*_spec.rb"` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). From a02d36eb348b8ad5db11caedff4b4758d4920f35 Mon Sep 17 00:00:00 2001 From: Greg Orlov Date: Fri, 1 Jun 2018 11:29:58 -0700 Subject: [PATCH 02/10] adding support for non-array, non-ActiveRecord collections Moving counting logic into (.*)Counter classes to allow for extensibility Moving countind delegation into RecordCounter class to allow for a single entrypoint --- jsonapi-utils.gemspec | 1 + lib/jsonapi/utils/support/pagination.rb | 127 ++++++++++++------ spec/jsonapi/utils/support/pagination_spec.rb | 36 ++++- spec/spec_helper.rb | 2 + spec/support/paginators.rb | 13 ++ 5 files changed, 136 insertions(+), 43 deletions(-) diff --git a/jsonapi-utils.gemspec b/jsonapi-utils.gemspec index a8c63d5..33bcebd 100644 --- a/jsonapi-utils.gemspec +++ b/jsonapi-utils.gemspec @@ -30,4 +30,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'smart_rspec', '~> 0.1.6' spec.add_development_dependency 'pry', '~> 0.10.3' spec.add_development_dependency 'pry-byebug' + spec.add_development_dependency 'simplecov' end diff --git a/lib/jsonapi/utils/support/pagination.rb b/lib/jsonapi/utils/support/pagination.rb index c8f1eaf..7e2a789 100644 --- a/lib/jsonapi/utils/support/pagination.rb +++ b/lib/jsonapi/utils/support/pagination.rb @@ -150,11 +150,9 @@ def pagination_range def count_records(records, options) return options[:count].to_i if options[:count].is_a?(Numeric) - case records - when ActiveRecord::Relation then count_records_from_database(records, options) - when Array then records.length - else raise RecordCountError, "Can't count records with the given options" - end + records = apply_filter(records, options) if params[:filter].present? + + RecordCounter.count( records, options ) end # Count pages in order to build a proper pagination and to fill up the "page_count" response's member. @@ -170,44 +168,97 @@ def page_count_for(record_count) return 0 if record_count.to_i < 1 size = (page_params['size'] || page_params['limit']).to_i - size = JSONAPI.configuration.default_page_size unless size.nonzero? + size = JSONAPI.configuration.default_page_size if size.zero? (record_count.to_f / size).ceil end - # Count records from the datatase applying the given request filters - # and skipping things like eager loading, grouping and sorting. - # - # @param records [ActiveRecord::Relation, Array] collection of records - # e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }] - # - # @param options [Hash] JU's options - # e.g.: { resource: V2::UserResource, count: 100 } - # - # @return [Integer] - # e.g.: 42 - # - # @api private - def count_records_from_database(records, options) - records = apply_filter(records, options) if params[:filter].present? - count = -> (records, except:) do - records.except(*except).count(distinct_count_sql(records)) + module RecordCounter + @counter_mappings = {} + + class << self + + def add(counter_class) + @counter_mappings ||= {} + @counter_mappings[counter_class.type] = counter_class + end + + def count(records, options = {}) + @counter_mappings.each do |counted_class, counter_class| + if records.is_a? counted_class + return counter_class.new(records, options).count + end + end + + raise RecordCountError, "Can't count records with the given options" + end end - count.(records, except: %i(includes group order)) - rescue ActiveRecord::StatementInvalid - count.(records, except: %i(group order)) - end - # Build the SQL distinct count with some reflection on the "records" object. - # - # @param records [ActiveRecord::Relation] collection of records - # e.g.: User.all - # - # @return [String] - # e.g.: "DISTINCT users.id" - # - # @api private - def distinct_count_sql(records) - "DISTINCT #{records.table_name}.#{records.primary_key}" + class BaseCounter + attr_accessor :records, :options + + def initialize(records, options = {}) + @records = records + @options = options + end + + class << self + + attr_accessor :type + + def counts(type) + self.type = type.camelize.constantize + RecordCounter.add self + rescue NameError + Rails.logger.warn "Unable to register #{self}: uninitialized constant #{type.camelize}" if Rails.logger.present? + end + end + end + + class ArrayCounter < BaseCounter + counts "array" + + delegate :count, to: :records + end + + + class ActiveRecordCounter < BaseCounter + counts "active_record/relation" + + # Count records from the datatase applying the given request filters + # and skipping things like eager loading, grouping and sorting. + # + # @records [ActiveRecord::Relation, Array] collection of records + # e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }] + # + # @options [Hash] JU's options + # e.g.: { resource: V2::UserResource, count: 100 } + # + # @return [Integer] + # e.g.: 42 + # + # @api private + def count + count = -> (records, except:) do + records.except(*except).count(distinct_count_sql(records)) + end + count.(@records, except: %i(includes group order)) + rescue ActiveRecord::StatementInvalid + count.(@records, except: %i(group order)) + end + + # Build the SQL distinct count with some reflection on the "records" object. + # + # @param records [ActiveRecord::Relation] collection of records + # e.g.: User.all + # + # @return [String] + # e.g.: "DISTINCT users.id" + # + # @api private + def distinct_count_sql(records) + "DISTINCT #{records.table_name}.#{records.primary_key}" + end + end end end end diff --git a/spec/jsonapi/utils/support/pagination_spec.rb b/spec/jsonapi/utils/support/pagination_spec.rb index c1b2632..87e21df 100644 --- a/spec/jsonapi/utils/support/pagination_spec.rb +++ b/spec/jsonapi/utils/support/pagination_spec.rb @@ -20,7 +20,7 @@ let(:records) { User.all.to_a } it 'applies memoization on the record count' do - expect(records).to receive(:length).and_return(records.length).once + expect(records).to receive(:count).and_return(records.count).once 2.times { subject.record_count_for(records, options) } end end @@ -55,7 +55,7 @@ context 'with array' do let(:records) { User.all.to_a } - let(:count) { records.length } + let(:count) { records.count } it_behaves_like 'counting records' end @@ -113,8 +113,13 @@ it_behaves_like 'counting pages' end end +end + +describe JSONAPI::Utils::Support::Pagination::RecordCounter::ActiveRecordCounter do + let(:options) { {} } - describe '#count_records_from_database' do + subject { described_class.new( records, options ) } + describe '#count' do shared_examples_for 'skipping eager load SQL when counting records' do it 'skips any eager load for the SQL count query (default)' do expect(records).to receive(:except) @@ -126,7 +131,7 @@ .and_return(User.all) .exactly(0) .times - subject.send(:count_records_from_database, records, options) + subject.send(:count) end end @@ -152,7 +157,7 @@ .with(:group, :order) .and_return(User.all) .once - subject.send(:count_records_from_database, records, options) + subject.send(:count) end end end @@ -165,3 +170,24 @@ end end end + +describe JSONAPI::Utils::Support::Pagination::RecordCounter do + + describe '#add' do + context 'when adding an unusable counter type' do + it "doesn't explode" do + expect{ described_class.add( BogusCounter ) }.to_not raise_error( ) + end + end + + context 'when adding good counter type' do + subject { described_class.add( StringCounter ) } + it 'should add it' do + expect{ subject }.to_not( raise_error ) + end + it 'should count' do + expect( described_class.send( :count, "lol" ) ).to eq( 3 ) + end + end + end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d6c565f..f4310ff 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,8 @@ require 'smart_rspec' require 'factory_girl' require 'support/helpers' +require 'simplecov' +SimpleCov.start RSpec.configure do |config| config.include FactoryGirl::Syntax::Methods diff --git a/spec/support/paginators.rb b/spec/support/paginators.rb index 608a4b4..c762a5c 100644 --- a/spec/support/paginators.rb +++ b/spec/support/paginators.rb @@ -5,3 +5,16 @@ def pagination_range(page_params) offset..offset + limit - 1 end end + + +class BogusCounter < JSONAPI::Utils::Support::Pagination::RecordCounter::BaseCounter + counts "junk" +end + +class StringCounter < JSONAPI::Utils::Support::Pagination::RecordCounter::BaseCounter + counts "string" + + def count + @records.length + end +end \ No newline at end of file From fea1cafae5143ea885e7bd1d19cc743ae1a37dd3 Mon Sep 17 00:00:00 2001 From: Greg Orlov Date: Fri, 1 Jun 2018 15:04:35 -0700 Subject: [PATCH 03/10] adding params into record counters Passing params into RecordCounters to allow them to do complex counting based on the query --- lib/jsonapi/utils/support/pagination.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/jsonapi/utils/support/pagination.rb b/lib/jsonapi/utils/support/pagination.rb index 7e2a789..a62e0b3 100644 --- a/lib/jsonapi/utils/support/pagination.rb +++ b/lib/jsonapi/utils/support/pagination.rb @@ -150,9 +150,7 @@ def pagination_range def count_records(records, options) return options[:count].to_i if options[:count].is_a?(Numeric) - records = apply_filter(records, options) if params[:filter].present? - - RecordCounter.count( records, options ) + RecordCounter.count( records, params, options ) end # Count pages in order to build a proper pagination and to fill up the "page_count" response's member. @@ -182,7 +180,7 @@ def add(counter_class) @counter_mappings[counter_class.type] = counter_class end - def count(records, options = {}) + def count(records, params = {}, options = {}) @counter_mappings.each do |counted_class, counter_class| if records.is_a? counted_class return counter_class.new(records, options).count @@ -194,10 +192,11 @@ def count(records, options = {}) end class BaseCounter - attr_accessor :records, :options + attr_accessor :records, :params, :options - def initialize(records, options = {}) + def initialize(records, params = {}, options = {}) @records = records + @params = params @options = options end @@ -238,6 +237,7 @@ class ActiveRecordCounter < BaseCounter # # @api private def count + @records = apply_filter(records, options) if params[:filter].present? count = -> (records, except:) do records.except(*except).count(distinct_count_sql(records)) end From 8999ea563eaa635259ae5cd7bb87c19683381328 Mon Sep 17 00:00:00 2001 From: Greg Orlov Date: Mon, 4 Jun 2018 09:13:38 -0700 Subject: [PATCH 04/10] fixing tests moving filter application out of *Counter classes due to scoping issues --- lib/jsonapi/utils/support/pagination.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jsonapi/utils/support/pagination.rb b/lib/jsonapi/utils/support/pagination.rb index a62e0b3..3580d4b 100644 --- a/lib/jsonapi/utils/support/pagination.rb +++ b/lib/jsonapi/utils/support/pagination.rb @@ -150,6 +150,7 @@ def pagination_range def count_records(records, options) return options[:count].to_i if options[:count].is_a?(Numeric) + records = apply_filter(records, options) if params[:filter].present? RecordCounter.count( records, params, options ) end @@ -237,7 +238,6 @@ class ActiveRecordCounter < BaseCounter # # @api private def count - @records = apply_filter(records, options) if params[:filter].present? count = -> (records, except:) do records.except(*except).count(distinct_count_sql(records)) end From 4301dbfac1eb34a0e8b5add54495a72d6444deb4 Mon Sep 17 00:00:00 2001 From: Greg Orlov Date: Mon, 4 Jun 2018 09:15:55 -0700 Subject: [PATCH 05/10] fixing readme updating instructions to run tests --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ccd6ad..429025e 100644 --- a/README.md +++ b/README.md @@ -816,7 +816,7 @@ Content-Type: application/vnd.api+json ## Development -After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec --pattern "**{,/*/**}/*_spec.rb"` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. +After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). From 36160b1c21304ff70e267e498a176e6c7fda3b2d Mon Sep 17 00:00:00 2001 From: Greg Orlov Date: Mon, 4 Jun 2018 14:58:10 -0700 Subject: [PATCH 06/10] adding comments to pagination --- lib/jsonapi/utils/support/pagination.rb | 41 +++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/jsonapi/utils/support/pagination.rb b/lib/jsonapi/utils/support/pagination.rb index 3580d4b..cba0229 100644 --- a/lib/jsonapi/utils/support/pagination.rb +++ b/lib/jsonapi/utils/support/pagination.rb @@ -172,18 +172,44 @@ def page_count_for(record_count) end module RecordCounter + # mapping of record collenction types to classes responsible for counting them + # + # @api private @counter_mappings = {} class << self + # Add a new counter class to the mappings hash + # + # @param counter_class [ < BaseCounter] class to register with RecordCounter + # e.g.: ActiveRecordCounter + # + # @api public def add(counter_class) @counter_mappings ||= {} @counter_mappings[counter_class.type] = counter_class end + # Execute the appropriate counting call for a collection with controller params and opts + # + # @param records [ActiveRecord::Relation, Array] collection of records + # e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }] + # + # @param params [Hash] Rails params + # + # @param options [Hash] JU's options + # e.g.: { resource: V2::UserResource, count: 100 } + # + # @return [Integer] + # e.g.: 42 + # + #@api public def count(records, params = {}, options = {}) + # Go through the counter types to see if there's a counter class that + # knows how to handle the current record set type @counter_mappings.each do |counted_class, counter_class| if records.is_a? counted_class + # counter class found; execute the call return counter_class.new(records, options).count end end @@ -205,6 +231,15 @@ class << self attr_accessor :type + + # Register the class with RecordCounter to let it know that this class + # is responsible for counting the type + # + # @param @type: [String] snake_cased modultarized name of the record type the + # counter class is responsible for handling + # e.g.: 'arcive_record/relation' + # + # @api public def counts(type) self.type = type.camelize.constantize RecordCounter.add self @@ -227,16 +262,16 @@ class ActiveRecordCounter < BaseCounter # Count records from the datatase applying the given request filters # and skipping things like eager loading, grouping and sorting. # - # @records [ActiveRecord::Relation, Array] collection of records + # @param records [ActiveRecord::Relation, Array] collection of records # e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }] # - # @options [Hash] JU's options + # @param options [Hash] JU's options # e.g.: { resource: V2::UserResource, count: 100 } # # @return [Integer] # e.g.: 42 # - # @api private + # @api public def count count = -> (records, except:) do records.except(*except).count(distinct_count_sql(records)) From f0a7cd1f0f9fc4feb51297bca4c0a857d3a6a6fe Mon Sep 17 00:00:00 2001 From: Greg Orlov Date: Tue, 5 Jun 2018 13:48:16 -0700 Subject: [PATCH 07/10] moving record counter into its own file --- lib/jsonapi/utils/support/pagination.rb | 127 +---------------- .../support/pagination/record_counter.rb | 132 ++++++++++++++++++ 2 files changed, 134 insertions(+), 125 deletions(-) create mode 100644 lib/jsonapi/utils/support/pagination/record_counter.rb diff --git a/lib/jsonapi/utils/support/pagination.rb b/lib/jsonapi/utils/support/pagination.rb index cba0229..925a9c8 100644 --- a/lib/jsonapi/utils/support/pagination.rb +++ b/lib/jsonapi/utils/support/pagination.rb @@ -2,6 +2,8 @@ module JSONAPI module Utils module Support module Pagination + autoload :RecordCounter, 'jsonapi/utils/support/pagination/record_counter' + RecordCountError = Class.new(ArgumentError) # Apply proper pagination to the records. @@ -170,131 +172,6 @@ def page_count_for(record_count) size = JSONAPI.configuration.default_page_size if size.zero? (record_count.to_f / size).ceil end - - module RecordCounter - # mapping of record collenction types to classes responsible for counting them - # - # @api private - @counter_mappings = {} - - class << self - - # Add a new counter class to the mappings hash - # - # @param counter_class [ < BaseCounter] class to register with RecordCounter - # e.g.: ActiveRecordCounter - # - # @api public - def add(counter_class) - @counter_mappings ||= {} - @counter_mappings[counter_class.type] = counter_class - end - - # Execute the appropriate counting call for a collection with controller params and opts - # - # @param records [ActiveRecord::Relation, Array] collection of records - # e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }] - # - # @param params [Hash] Rails params - # - # @param options [Hash] JU's options - # e.g.: { resource: V2::UserResource, count: 100 } - # - # @return [Integer] - # e.g.: 42 - # - #@api public - def count(records, params = {}, options = {}) - # Go through the counter types to see if there's a counter class that - # knows how to handle the current record set type - @counter_mappings.each do |counted_class, counter_class| - if records.is_a? counted_class - # counter class found; execute the call - return counter_class.new(records, options).count - end - end - - raise RecordCountError, "Can't count records with the given options" - end - end - - class BaseCounter - attr_accessor :records, :params, :options - - def initialize(records, params = {}, options = {}) - @records = records - @params = params - @options = options - end - - class << self - - attr_accessor :type - - - # Register the class with RecordCounter to let it know that this class - # is responsible for counting the type - # - # @param @type: [String] snake_cased modultarized name of the record type the - # counter class is responsible for handling - # e.g.: 'arcive_record/relation' - # - # @api public - def counts(type) - self.type = type.camelize.constantize - RecordCounter.add self - rescue NameError - Rails.logger.warn "Unable to register #{self}: uninitialized constant #{type.camelize}" if Rails.logger.present? - end - end - end - - class ArrayCounter < BaseCounter - counts "array" - - delegate :count, to: :records - end - - - class ActiveRecordCounter < BaseCounter - counts "active_record/relation" - - # Count records from the datatase applying the given request filters - # and skipping things like eager loading, grouping and sorting. - # - # @param records [ActiveRecord::Relation, Array] collection of records - # e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }] - # - # @param options [Hash] JU's options - # e.g.: { resource: V2::UserResource, count: 100 } - # - # @return [Integer] - # e.g.: 42 - # - # @api public - def count - count = -> (records, except:) do - records.except(*except).count(distinct_count_sql(records)) - end - count.(@records, except: %i(includes group order)) - rescue ActiveRecord::StatementInvalid - count.(@records, except: %i(group order)) - end - - # Build the SQL distinct count with some reflection on the "records" object. - # - # @param records [ActiveRecord::Relation] collection of records - # e.g.: User.all - # - # @return [String] - # e.g.: "DISTINCT users.id" - # - # @api private - def distinct_count_sql(records) - "DISTINCT #{records.table_name}.#{records.primary_key}" - end - end - end end end end diff --git a/lib/jsonapi/utils/support/pagination/record_counter.rb b/lib/jsonapi/utils/support/pagination/record_counter.rb new file mode 100644 index 0000000..7093afe --- /dev/null +++ b/lib/jsonapi/utils/support/pagination/record_counter.rb @@ -0,0 +1,132 @@ +module JSONAPI + module Utils + module Support + module Pagination + module RecordCounter + # mapping of record collenction types to classes responsible for counting them + # + # @api private + @counter_mappings = {} + + class << self + + # Add a new counter class to the mappings hash + # + # @param counter_class [ < BaseCounter] class to register with RecordCounter + # e.g.: ActiveRecordCounter + # + # @api public + def add(counter_class) + @counter_mappings ||= {} + @counter_mappings[counter_class.type] = counter_class + end + + # Execute the appropriate counting call for a collection with controller params and opts + # + # @param records [ActiveRecord::Relation, Array] collection of records + # e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }] + # + # @param params [Hash] Rails params + # + # @param options [Hash] JU's options + # e.g.: { resource: V2::UserResource, count: 100 } + # + # @return [Integer] + # e.g.: 42 + # + #@api public + def count(records, params = {}, options = {}) + # Go through the counter types to see if there's a counter class that + # knows how to handle the current record set type + @counter_mappings.each do |counted_class, counter_class| + if records.is_a? counted_class + # counter class found; execute the call + return counter_class.new(records, options).count + end + end + + raise RecordCountError, "Can't count records with the given options" + end + end + + class BaseCounter + attr_accessor :records, :params, :options + + def initialize(records, params = {}, options = {}) + @records = records + @params = params + @options = options + end + + class << self + + attr_accessor :type + + + # Register the class with RecordCounter to let it know that this class + # is responsible for counting the type + # + # @param @type: [String] snake_cased modultarized name of the record type the + # counter class is responsible for handling + # e.g.: 'arcive_record/relation' + # + # @api public + def counts(type) + self.type = type.camelize.constantize + RecordCounter.add self + rescue NameError + Rails.logger.warn "Unable to register #{self}: uninitialized constant #{type.camelize}" if Rails.logger.present? + end + end + end + + class ArrayCounter < BaseCounter + counts "array" + + delegate :count, to: :records + end + + + class ActiveRecordCounter < BaseCounter + counts "active_record/relation" + + # Count records from the datatase applying the given request filters + # and skipping things like eager loading, grouping and sorting. + # + # @param records [ActiveRecord::Relation, Array] collection of records + # e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }] + # + # @param options [Hash] JU's options + # e.g.: { resource: V2::UserResource, count: 100 } + # + # @return [Integer] + # e.g.: 42 + # + # @api public + def count + count = -> (records, except:) do + records.except(*except).count(distinct_count_sql(records)) + end + count.(@records, except: %i(includes group order)) + rescue ActiveRecord::StatementInvalid + count.(@records, except: %i(group order)) + end + + # Build the SQL distinct count with some reflection on the "records" object. + # + # @param records [ActiveRecord::Relation] collection of records + # e.g.: User.all + # + # @return [String] + # e.g.: "DISTINCT users.id" + # + # @api private + def distinct_count_sql(records) + "DISTINCT #{records.table_name}.#{records.primary_key}" + end + end + end + end + end + end +end \ No newline at end of file From 6d90c01ece3b37815bde932d41027912824770c9 Mon Sep 17 00:00:00 2001 From: Greg Orlov Date: Tue, 5 Jun 2018 14:10:36 -0700 Subject: [PATCH 08/10] adding info message to notify about added counter type --- lib/jsonapi/utils/support/pagination/record_counter.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/jsonapi/utils/support/pagination/record_counter.rb b/lib/jsonapi/utils/support/pagination/record_counter.rb index 7093afe..6c577c4 100644 --- a/lib/jsonapi/utils/support/pagination/record_counter.rb +++ b/lib/jsonapi/utils/support/pagination/record_counter.rb @@ -73,6 +73,7 @@ class << self # @api public def counts(type) self.type = type.camelize.constantize + Rails.logger.info "Registered #{self} to count #{type.camelize}" if Rails.logger.present? RecordCounter.add self rescue NameError Rails.logger.warn "Unable to register #{self}: uninitialized constant #{type.camelize}" if Rails.logger.present? From 93e5c50efaa4a331dcf99a82a12352817c6b10fe Mon Sep 17 00:00:00 2001 From: Greg Orlov Date: Tue, 5 Jun 2018 15:58:57 -0700 Subject: [PATCH 09/10] fixing sorting for non-AR, non-array objects allowing apply_sort to fall through with no modifications for unexpected result sets --- lib/jsonapi/utils/support/sort.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/jsonapi/utils/support/sort.rb b/lib/jsonapi/utils/support/sort.rb index 520e10a..abac2fb 100644 --- a/lib/jsonapi/utils/support/sort.rb +++ b/lib/jsonapi/utils/support/sort.rb @@ -16,6 +16,8 @@ def apply_sort(records) records.sort { |a, b| comp = 0; eval(sort_criteria) } elsif records.respond_to?(:order) records.order(sort_params) + else + records end end From 48977c279d9ba1d1877b3811269754f5cbb5ed72 Mon Sep 17 00:00:00 2001 From: Greg Orlov Date: Tue, 5 Jun 2018 20:22:07 -0700 Subject: [PATCH 10/10] fixing param passing into counter fixing RecordCounter.count to pass request params into the counter classes --- .../utils/support/pagination/record_counter.rb | 2 +- spec/jsonapi/utils/support/pagination_spec.rb | 18 ++++++++++++++++++ spec/support/paginators.rb | 16 ++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/lib/jsonapi/utils/support/pagination/record_counter.rb b/lib/jsonapi/utils/support/pagination/record_counter.rb index 6c577c4..6f5357e 100644 --- a/lib/jsonapi/utils/support/pagination/record_counter.rb +++ b/lib/jsonapi/utils/support/pagination/record_counter.rb @@ -41,7 +41,7 @@ def count(records, params = {}, options = {}) @counter_mappings.each do |counted_class, counter_class| if records.is_a? counted_class # counter class found; execute the call - return counter_class.new(records, options).count + return counter_class.new(records, params, options).count end end diff --git a/spec/jsonapi/utils/support/pagination_spec.rb b/spec/jsonapi/utils/support/pagination_spec.rb index 87e21df..cb613b9 100644 --- a/spec/jsonapi/utils/support/pagination_spec.rb +++ b/spec/jsonapi/utils/support/pagination_spec.rb @@ -190,4 +190,22 @@ end end end + describe "#count" do + context "when params are present" do + let( :params ){ { a: :b } } + it "passes them into the counters" do + described_class.add HashParamCounter + + expect( described_class.send( :count, {}, params, {} ) ).to eq( { a: :b } ) + end + end + context "when options are present" do + let( :options ) { { a: :b } } + it "passes them into the counters" do + described_class.add HashOptionsCounter + + expect( described_class.send( :count, {}, {}, options ) ).to eq( { a: :b } ) + end + end + end end \ No newline at end of file diff --git a/spec/support/paginators.rb b/spec/support/paginators.rb index c762a5c..e68c1dd 100644 --- a/spec/support/paginators.rb +++ b/spec/support/paginators.rb @@ -17,4 +17,20 @@ class StringCounter < JSONAPI::Utils::Support::Pagination::RecordCounter::BaseCo def count @records.length end +end + +class HashParamCounter < JSONAPI::Utils::Support::Pagination::RecordCounter::BaseCounter + counts "Hash" + + def count + @params + end +end + +class HashOptionsCounter < JSONAPI::Utils::Support::Pagination::RecordCounter::BaseCounter + counts "Hash" + + def count + @options + end end \ No newline at end of file