Skip to content

Commit de7f632

Browse files
authored
Merge pull request #31 from speee/fix/poro-create-without-find-by-key
Fix PORO resource creation without find_by_key override
2 parents 512a6b9 + c845be8 commit de7f632

2 files changed

Lines changed: 82 additions & 10 deletions

File tree

lib/jsonapi/processor.rb

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -234,16 +234,31 @@ def create_resource
234234
resource = resource_klass.create(context)
235235
result = resource.replace_fields(data)
236236

237-
options = {
238-
context: context,
239-
fields: fields,
240-
filters: { resource_klass._primary_key => resource.id },
241-
include_directives: include_directives
242-
}
243-
244-
resource_set = find_resource_set(include_directives, options)
245-
246-
resource_set.populate!(serializer, context, options)
237+
# PORO compatibility: if model doesn't respond to :all, use created resource directly
238+
# This restores 0.9.x behavior for PORO models without requiring find_by_key override
239+
if resource_klass._model_class.respond_to?(:all)
240+
# ActiveRecord path: use find_resource_set for optimized queries with includes
241+
options = {
242+
context: context,
243+
fields: fields,
244+
filters: { resource_klass._primary_key => resource.id },
245+
include_directives: include_directives
246+
}
247+
248+
resource_set = find_resource_set(include_directives, options)
249+
resource_set.populate!(serializer, context, options)
250+
else
251+
# PORO path: use created resource directly (0.9.x behavior)
252+
# ResourceSet will use the resource without calling find
253+
resource_set = JSONAPI::ResourceSet.new(resource, include_directives[:include_related], {
254+
context: context,
255+
fields: fields
256+
})
257+
resource_set.populate!(serializer, context, {
258+
context: context,
259+
fields: fields
260+
})
261+
end
247262

248263
JSONAPI::ResourceSetOperationResult.new((result == :completed ? :created : :accepted), resource_set, result_options)
249264
end

test/unit/resource/poro_resource_test.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ def initialize(id, name)
1111
end
1212

1313
# Note: No .all method - this is a PORO, not ActiveRecord
14+
15+
# Required for jsonapi-resources compatibility
16+
def valid?(_context = nil)
17+
true
18+
end
19+
20+
def save(_options = {})
21+
true
22+
end
1423
end
1524

1625
# Resource for PORO model
@@ -32,6 +41,22 @@ def create(context)
3241
end
3342
end
3443

44+
# PORO Resource without find_by_key override (mimics CallbackTelephoneIncomingResource)
45+
class MinimalPoroModelResource < JSONAPI::Resource
46+
model_name 'PoroModel'
47+
attributes :name
48+
49+
class << self
50+
def create(context)
51+
model = PoroModel.new(SecureRandom.uuid, "Minimal PORO")
52+
new(model, context)
53+
end
54+
55+
# Note: find_by_key is NOT overridden
56+
# This should work with the 0.9.x-style create behavior
57+
end
58+
end
59+
3560
class PoroResourceTest < ActiveSupport::TestCase
3661
def test_poro_model_does_not_respond_to_all
3762
# Verify our test PORO doesn't have .all method
@@ -98,4 +123,36 @@ def test_active_record_resource_still_uses_original_find_fragments
98123
assert_kind_of JSONAPI::ResourceFragment, fragment
99124
end
100125
end
126+
127+
def test_create_poro_without_find_by_key_override
128+
# This tests the fix for PORO resources that don't override find_by_key
129+
# Previously, create would call find_resource_set -> find_fragments -> _model_class.all
130+
# which would fail for PORO models without .all method
131+
# With the fix, create should return the created resource directly (0.9.x behavior)
132+
133+
params = {
134+
data: {
135+
type: 'minimal_poro_models',
136+
attributes: {
137+
name: 'Test PORO'
138+
}
139+
},
140+
include_directives: JSONAPI::IncludeDirectives.new(MinimalPoroModelResource, []),
141+
fields: {},
142+
serializer: JSONAPI::ResourceSerializer.new(MinimalPoroModelResource)
143+
}
144+
145+
processor = JSONAPI::Processor.new(MinimalPoroModelResource, :create_resource, params)
146+
147+
# This should not raise an error (no call to _model_class.all)
148+
result = processor.create_resource
149+
150+
assert_kind_of JSONAPI::ResourceSetOperationResult, result
151+
assert result.code == :created || result.code == 201, "Expected :created or 201, got #{result.code.inspect}"
152+
assert_not_nil result.resource_set
153+
154+
# Verify the resource set contains the created resource
155+
resources = result.resource_set.instance_variable_get(:@resource_klasses)
156+
assert_equal 1, resources[MinimalPoroModelResource].length
157+
end
101158
end

0 commit comments

Comments
 (0)