diff --git a/lib/brainstem/presenter.rb b/lib/brainstem/presenter.rb index 65202dac..e86e133e 100644 --- a/lib/brainstem/presenter.rb +++ b/lib/brainstem/presenter.rb @@ -28,6 +28,32 @@ def self.default_sort_order(sort_string = nil) end end + # @overload exclude_count + # set exclude_count = true and allow_count = false (i.e. exclude the count and do not + # do not allow a request for the count via the url query string) + # @return [Boolean, Boolean] + # @overload exclude_count( allow: true ) + # set exclude_count = true and allow_count = false (i.e. exclude the count by default + # but allows a request for it via the url query string with (e.g. ?count=true) + # @return [Boolean, Boolean] + # @overload exclude_count( exclude: false ) + # set exclude_count = false. This is only needed for testing to toggle back to the + # including the count by default, otherwise some of the subsequent tests may fail + # @return [Boolean, Boolean] + def self.exclude_count(exclude: true, allow: false) + [@exclude_count = exclude, @allow_count = allow] + end + + def self.exclude_count? + @exclude_count == true ? true : false + end + + def self.allow_count? + (@exclude_count == true && @allow_count == true) ? true : false + end + + + # @overload sort_order(name, order) # @param [Symbol] name The name of the sort order. # @param [String] order The SQL string to use to sort the presented data. @@ -191,6 +217,19 @@ def default_sort_order self.class.default_sort_order end + # @!attribute [r] exclude_count + # The boolean value set in the definition of this presenter to indicate if the count is excluded + def exclude_count? + self.class.exclude_count? + end + + # @!attribute [r] allow_count + # The boolean value set in the definition of this presenter to indicate if the count may + # be included even if it has been excluded by default + def allow_count? + self.class.allow_count? + end + # @!attribute [r] sort_orders # The sort orders that were declared in the definition of this presenter. def sort_orders diff --git a/lib/brainstem/presenter_collection.rb b/lib/brainstem/presenter_collection.rb index 44f6a47b..2baf1263 100644 --- a/lib/brainstem/presenter_collection.rb +++ b/lib/brainstem/presenter_collection.rb @@ -93,7 +93,13 @@ def presenting(name, options = {}, &block) models = perform_preloading records, includes_hash primary_models, associated_models = gather_associations(models, includes_hash) - struct = { :count => count, options[:as] => [], :results => [] } + + + if present_count?(options) + struct = { :count => count, options[:as] => [], :results => [] } + else + struct = { options[:as] => [], :results => [] } + end associated_models.each do |json_name, models| models.flatten! @@ -154,7 +160,16 @@ def paginate(scope, options) offset = limit * (calculate_page(options) - 1) end - [scope.limit(limit).offset(offset).uniq, scope.select("distinct #{scope.connection.quote_table_name options[:table_name]}.id").count] # as of Rails 3.2.5, uniq.count generates the wrong SQL. + paginate_results = [] + paginate_results << scope.limit(limit).offset(offset).uniq + if present_count?(options) + paginate_results << scope.select("distinct #{scope.connection.quote_table_name options[:table_name]}.id").count + else + paginate_results << :no_count_retrieved + end + + paginate_results + end def calculate_per_page(options) @@ -387,6 +402,16 @@ def set_default_filters_option!(options) options[:apply_default_filters] = [true, "true", "TRUE", 1, "1"].include? options[:params].delete(:apply_default_filters) end + + # Return boolean indicating whether or not the count should be presented + def present_count?(options) + p = options[:presenter] + count_true = [true, "true", "TRUE", 1, "1"].include? options[:params][:count] + count_false = [false, "false", "FALSE", 0, "0"].include? options[:params][:count] + ( !p.exclude_count? && !count_false ) || ( p.allow_count? && count_true ) + end + + # Class Methods # In Rails 4.2, ActiveRecord::Base#reflections started being keyed by strings instead of symbols. diff --git a/spec/brainstem/presenter_collection_spec.rb b/spec/brainstem/presenter_collection_spec.rb index 65d7611c..a4ff020d 100644 --- a/spec/brainstem/presenter_collection_spec.rb +++ b/spec/brainstem/presenter_collection_spec.rb @@ -149,6 +149,33 @@ class MyException < Exception; end result = @presenter_collection.presenting("workspaces", :params => { :per_page => 2, :page => 1 }) { Workspace.order('id desc') } expect(result[:count]).to eq(Workspace.count) end + + it "are excluded when exclude_count method defined" do + WorkspacePresenter.exclude_count + result = @presenter_collection.presenting("workspaces", :params => { :per_page => 2, :page => 1 }) { Workspace.unscoped } + WorkspacePresenter.exclude_count(exclude: false) + expect(result[:count]).to be_nil + end + + it "are included when exclude_count method defined with 'allow = true' and 'count=true' passed in url query string" do + WorkspacePresenter.exclude_count(allow: true) + result = @presenter_collection.presenting("workspaces", :params => { :per_page => 2, :page => 1, :count => "true" }) { Workspace.unscoped } + WorkspacePresenter.exclude_count(exclude: false) + expect(result[:count]).to eq(Workspace.count) + end + + it "are excluded when exclude_count method defined without 'allow = true' and 'count=true' passed in url query string" do + WorkspacePresenter.exclude_count + result = @presenter_collection.presenting("workspaces", :params => { :per_page => 2, :page => 1, :count => "true" }) { Workspace.unscoped } + WorkspacePresenter.exclude_count(exclude: false) + expect(result[:count]).to be_nil + end + + it "are excluded when count=false appears in the url query string" do + result = @presenter_collection.presenting("workspaces", :params => { :per_page => 2, :page => 1, :count => "false" }) { Workspace.unscoped } + expect(result[:count]).to be_nil + end + end end