Skip to content

Instantly share code, notes, and snippets.

@jbonhag
Created February 23, 2026 21:54
Show Gist options
  • Select an option

  • Save jbonhag/fa2861598f07a15fef57f59fe8a87745 to your computer and use it in GitHub Desktop.

Select an option

Save jbonhag/fa2861598f07a15fef57f59fe8a87745 to your computer and use it in GitHub Desktop.
ProjectSerializer: Fix count attributes with empty maps (.key? vs .present?)

ProjectSerializer Count Attribute Fix

Problem

Empty hash {} is not .present? in Rails, causing count attributes to be excluded even when the controller intends to include them.

{}.present?  # => false ❌

Solution

Use .key?() instead of .present?() to check if the option was passed:

# Before
attribute :workspace_count, if: -> { @instance_options[:workspace_count_map].present? }

# After  
attribute :workspace_count, if: -> { @instance_options.key?(:workspace_count_map) }

Safety Analysis

When maps ARE passed (index, show):

options = { workspace_count_map: {} }
options.key?(:workspace_count_map)  # => true ✅
  • Attribute IS included
  • Method returns workspace_count_map[object.id] || 0
  • Missing IDs default to 0 ✅

When maps are NOT passed (create, update):

options = {}  # or { other: :options }
options.key?(:workspace_count_map)  # => false ✅
  • Attribute is NOT included
  • Method is never called
  • No nil[object.id] error ✅

Testing

All 156 tests in spec/controllers/api/v2/projects_controller_spec.rb pass, including:

  • index with empty results (returns 0 counts)
  • show with counts
  • create without count maps
  • update without count maps

Impact

Enables future controller optimizations where we only query projects with non-zero counts:

# Controller can return empty map for all-zero case
proj_with_workspace_count = {}  

# Serializer correctly includes attribute and defaults to 0
# Before: {} not .present?, attribute excluded ❌
# After:  .key?() true, attribute included ✅
diff --git i/app/serializers/api/v2/project_serializer.rb w/app/serializers/api/v2/project_serializer.rb
index 3824acdb1b5..ef968146b37 100644
--- i/app/serializers/api/v2/project_serializer.rb
+++ w/app/serializers/api/v2/project_serializer.rb
@@ -15,11 +15,11 @@ module Api
attribute :description
attribute :created_at
attribute :permissions, if: :include_permissions?
- attribute :workspace_count, if: -> { @instance_options[:workspace_count_map].present? }
- attribute :team_count, if: -> { @instance_options[:team_count_map].present? }
+ attribute :workspace_count, if: -> { @instance_options.key?(:workspace_count_map) }
+ attribute :team_count, if: -> { @instance_options.key?(:team_count_map) }
attribute :hcp_id, if: :is_unified?
attribute :is_unified, if: :is_unified?
- attribute :stack_count, if: -> { @instance_options[:stack_count_map].present? }
+ attribute :stack_count, if: -> { @instance_options.key?(:stack_count_map) }
attribute :auto_destroy_activity_duration
attribute :default_execution_mode
attribute :setting_overwrites
@@ -75,23 +75,17 @@ module Api
def workspace_count
workspace_count_map = @instance_options[:workspace_count_map]
- if workspace_count_map.present?
- workspace_count_map[object.id] || 0
- end
+ workspace_count_map[object.id] || 0
end
def team_count
team_count_map = @instance_options[:team_count_map]
- if team_count_map.present?
- team_count_map[object.id] || 0
- end
+ team_count_map[object.id] || 0
end
def stack_count
stack_count_map = @instance_options[:stack_count_map]
- if stack_count_map.present?
- stack_count_map[object.id] || 0
- end
+ stack_count_map[object.id] || 0
end
def id
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment