Skip to content

Commit 953ec76

Browse files
authored
Merge pull request #20 from speee/fix/rails7-activemodel-resources-1467
Add tests for Issue JSONAPI-Resources#1467: ActiveModel resources support
2 parents 17c0160 + d5d116b commit 953ec76

3 files changed

Lines changed: 186 additions & 0 deletions

File tree

test/fixtures/active_record.rb

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2691,6 +2691,92 @@ class RobotResource < ::JSONAPI::Resource
26912691
end
26922692
end
26932693

2694+
# Models and Resources for testing Issue #1467: ActiveModel-based resources
2695+
# ActiveModel class without ActiveRecord
2696+
class SimpleModel
2697+
include ActiveModel::Model
2698+
2699+
attr_accessor :id, :name, :description
2700+
2701+
def initialize(attributes = {})
2702+
@id = attributes[:id]
2703+
@name = attributes[:name]
2704+
@description = attributes[:description]
2705+
end
2706+
2707+
# Simple in-memory store for testing
2708+
@@store = {}
2709+
2710+
def self.reset_store!
2711+
@@store = {}
2712+
end
2713+
2714+
def self.create(attributes)
2715+
model = new(attributes)
2716+
@@store[model.id] = model
2717+
model
2718+
end
2719+
2720+
def self.find(id)
2721+
@@store[id]
2722+
end
2723+
2724+
def self.all
2725+
@@store.values
2726+
end
2727+
2728+
def self.find_by(attributes)
2729+
all.find { |model| attributes.all? { |key, value| model.send(key) == value } }
2730+
end
2731+
end
2732+
2733+
class SimpleModelResource < JSONAPI::BasicResource
2734+
model_name 'SimpleModel'
2735+
model_hint model: SimpleModel, resource: :simple_model
2736+
2737+
attribute :name
2738+
attribute :description
2739+
2740+
# Override find_by_key for non-ActiveRecord models
2741+
def self.find_by_key(key, options = {})
2742+
model = _model_class.find(key.to_i)
2743+
return nil unless model
2744+
new(model, options[:context])
2745+
end
2746+
2747+
# Override find_fragments for non-ActiveRecord models
2748+
def self.find_fragments(filters, options = {})
2749+
models = if filters.empty?
2750+
_model_class.all
2751+
else
2752+
# Simple filtering implementation
2753+
_model_class.all.select do |model|
2754+
filters.all? { |key, value| model.send(key).to_s == value.to_s }
2755+
end
2756+
end
2757+
2758+
models.each_with_object({}) do |model, hash|
2759+
resource = new(model, options[:context])
2760+
hash[resource.identity] = {
2761+
identity: resource.identity,
2762+
cache: nil,
2763+
attributes: nil,
2764+
related: nil
2765+
}
2766+
end
2767+
end
2768+
end
2769+
2770+
# This resource uses the default JSONAPI::Resource (ActiveRelationResource)
2771+
# which will fail with ActiveModel models - reproduces Issue #1467
2772+
class BrokenActiveModelResource < JSONAPI::Resource
2773+
model_name 'SimpleModel'
2774+
model_hint model: SimpleModel, resource: :broken_active_model
2775+
2776+
attribute :name
2777+
attribute :description
2778+
end
2779+
26942780
### PORO Data - don't do this in a production app
26952781
$breed_data = BreedData.new
26962782
$breed_data.add(Breed.new(0, 'persian'))
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
require File.expand_path('../../../test_helper', __FILE__)
2+
3+
# Test for Issue #1467: Demonstrate the problem with ActiveRelationResource and ActiveModel
4+
# https://github.com/cerebris/jsonapi-resources/issues/1467
5+
#
6+
# This test shows that using JSONAPI::Resource (which inherits from ActiveRelationResource)
7+
# with an ActiveModel-based model will fail because ActiveRelationResource expects
8+
# ActiveRecord methods like 'where'
9+
10+
class ActiveModelBrokenTest < ActiveSupport::TestCase
11+
def setup
12+
# Seed some test data in the mock store
13+
SimpleModel.reset_store!
14+
@model1 = SimpleModel.create(id: 1, name: 'Test 1', description: 'First test')
15+
@model2 = SimpleModel.create(id: 2, name: 'Test 2', description: 'Second test')
16+
end
17+
18+
def teardown
19+
SimpleModel.reset_store!
20+
end
21+
22+
# This test demonstrates the problem: JSONAPI::Resource (ActiveRelationResource)
23+
# attempts to use ActiveRecord methods on ActiveModel models
24+
def test_activerelation_resource_fails_with_activemodel
25+
resource_klass = BrokenActiveModelResource
26+
context = {}
27+
28+
# This WILL raise NoMethodError because ActiveRelationResource expects ActiveRecord methods
29+
# The error could be 'order', 'where', or other ActiveRecord-specific methods
30+
error = assert_raises(NoMethodError) do
31+
fragments = resource_klass.find_fragments({}, { context: context })
32+
end
33+
34+
# Verify it's trying to use ActiveRecord-like query methods
35+
assert_match(/undefined method.*(order|where|limit|offset)/, error.message)
36+
end
37+
end
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
require File.expand_path('../../../test_helper', __FILE__)
2+
3+
# Test for Issue #1467: ActiveModel-based resources fail with 'where' method error
4+
# https://github.com/cerebris/jsonapi-resources/issues/1467
5+
#
6+
# This test reproduces the scenario where:
7+
# - A model uses ActiveModel::Model instead of ActiveRecord::Base
8+
# - The resource should work with BasicResource or proper configuration
9+
# - In v0.10.7+, these resources fail with "undefined method 'where'"
10+
11+
class ActiveModelResourceTest < ActiveSupport::TestCase
12+
def setup
13+
# Seed some test data in the mock store
14+
SimpleModel.reset_store!
15+
@model1 = SimpleModel.create(id: 1, name: 'Test 1', description: 'First test')
16+
@model2 = SimpleModel.create(id: 2, name: 'Test 2', description: 'Second test')
17+
end
18+
19+
def teardown
20+
SimpleModel.reset_store!
21+
end
22+
23+
# Test that ActiveModel-based resources can be found without using ActiveRecord methods
24+
def test_find_activemodel_resources
25+
resource_klass = SimpleModelResource
26+
context = {}
27+
28+
# This should not raise "undefined method 'where'"
29+
assert_nothing_raised do
30+
fragments = resource_klass.find_fragments({}, { context: context })
31+
assert fragments.any?, "Should find SimpleModel fragments"
32+
assert_equal 2, fragments.size, "Should find 2 fragments"
33+
end
34+
end
35+
36+
# Test that ActiveModel-based resources can be filtered
37+
def test_filter_activemodel_resources
38+
resource_klass = SimpleModelResource
39+
context = {}
40+
41+
# Test with a filter
42+
filters = { name: 'Test 1' }
43+
44+
# This should not attempt to call _model_class.where(name: 'Test 1')
45+
assert_nothing_raised do
46+
fragments = resource_klass.find_fragments(filters, { context: context })
47+
assert_equal 1, fragments.size, "Should find 1 filtered fragment"
48+
end
49+
end
50+
51+
# Test that ActiveModel-based resources work with find_by_key
52+
def test_find_by_key_activemodel_resources
53+
resource_klass = SimpleModelResource
54+
context = {}
55+
56+
# find_by_key should work for ActiveModel resources
57+
assert_nothing_raised do
58+
resource = resource_klass.find_by_key(1, context: context)
59+
assert resource, "Should find resource by key"
60+
assert_equal 'Test 1', resource.name
61+
end
62+
end
63+
end

0 commit comments

Comments
 (0)