Skip to content

Commit 9372d5a

Browse files
seanpdoylerafaelfranca
authored andcommitted
Introduce ActiveResource::WhereClause
Closes [#408][] Introduce a simple chainable `WhereClause` class inspired by [Active Record][]. All methods (including those that integrate with [Enumerable][]) are delegated to `WhereClause#all`, which itself delegates to `Base.find`. By merging parameters through `.where`-chaining, this commit supports deferred fetching: ```ruby people = Person.where(id: 2).where(name: "david") # => GET /people.json?id=2&name=david people = Person.where(id: 2).all(params: { name: "david" }) # => GET /people.json?id=2&name=david people = Person.all(from: "/records.json").where(id: 2) # => GET /records.json?id=2 ``` [#408]: #408 [Active Record]: https://github.com/rails/rails/blob/main/activerecord/lib/active_record/relation/where_clause.rb [Enumerable]: https://ruby-doc.org/3.4.1/Enumerable.html
1 parent e677239 commit 9372d5a

File tree

6 files changed

+115
-3
lines changed

6 files changed

+115
-3
lines changed

lib/active_resource.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ module ActiveResource
4949
autoload :InheritingHash
5050
autoload :Validations
5151
autoload :Collection
52+
eager_autoload do
53+
autoload :WhereClause
54+
end
5255

5356
if ActiveSupport::VERSION::STRING >= "7.1"
5457
def self.deprecator

lib/active_resource/base.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,13 +1062,13 @@ def last(*args)
10621062
# This is an alias for find(:all). You can pass in all the same
10631063
# arguments to this method as you can to <tt>find(:all)</tt>
10641064
def all(*args)
1065-
find(:all, *args)
1065+
WhereClause.new(self, *args)
10661066
end
10671067

10681068
def where(clauses = {})
10691069
clauses = sanitize_forbidden_attributes(clauses)
10701070
raise ArgumentError, "expected a clauses Hash, got #{clauses.inspect}" unless clauses.is_a? Hash
1071-
find(:all, params: clauses)
1071+
all(params: clauses)
10721072
end
10731073

10741074

lib/active_resource/railtie.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
module ActiveResource
77
class Railtie < Rails::Railtie
8+
config.eager_load_namespaces << ActiveResource
9+
810
config.active_resource = ActiveSupport::OrderedOptions.new
911

1012
initializer "active_resource.set_configs" do |app|
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# frozen_string_literal: true
2+
3+
module ActiveResource
4+
class WhereClause < BasicObject # :nodoc:
5+
delegate_missing_to :resources
6+
7+
def initialize(resource_class, options = {})
8+
@resource_class = resource_class
9+
@options = options
10+
@resources = nil
11+
@loaded = false
12+
end
13+
14+
def where(clauses = {})
15+
all(params: clauses)
16+
end
17+
18+
def all(options = {})
19+
WhereClause.new(@resource_class, @options.deep_merge(options))
20+
end
21+
22+
def load
23+
unless @loaded
24+
@resources = @resource_class.find(:all, @options)
25+
@loaded = true
26+
end
27+
28+
self
29+
end
30+
31+
def reload
32+
reset
33+
load
34+
end
35+
36+
private
37+
def resources
38+
load
39+
@resources
40+
end
41+
42+
def reset
43+
@resources = nil
44+
@loaded = false
45+
end
46+
end
47+
end

test/cases/finder_test.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,66 @@ def test_where_with_clauses
6363
assert_kind_of StreetAddress, addresses.first
6464
end
6565

66+
def test_where_with_multiple_where_clauses
67+
ActiveResource::HttpMock.respond_to.get "/people.json?id=2&name=david", {}, @people_david
68+
69+
people = Person.where(id: 2).where(name: "david")
70+
assert_equal 1, people.size
71+
assert_kind_of Person, people.first
72+
assert_equal 2, people.first.id
73+
assert_equal "David", people.first.name
74+
end
75+
76+
def test_where_chained_from_all
77+
ActiveResource::HttpMock.respond_to.get "/records.json?id=2", {}, @people_david
78+
79+
people = Person.all(from: "/records.json").where(id: 2)
80+
assert_equal 1, people.size
81+
assert_kind_of Person, people.first
82+
assert_equal 2, people.first.id
83+
assert_equal "David", people.first.name
84+
end
85+
86+
def test_where_with_chained_into_all
87+
ActiveResource::HttpMock.respond_to.get "/records.json?id=2&name=david", {}, @people_david
88+
89+
people = Person.where(id: 2).all(from: "/records.json", params: { name: "david" })
90+
assert_equal 1, people.size
91+
assert_kind_of Person, people.first
92+
assert_equal 2, people.first.id
93+
assert_equal "David", people.first.name
94+
end
95+
96+
def test_where_loading
97+
ActiveResource::HttpMock.respond_to.get "/people.json?id=2", {}, @people_david
98+
people = Person.where(id: 2)
99+
100+
assert_changes -> { ActiveResource::HttpMock.requests.count }, from: 0, to: 1 do
101+
people.load
102+
end
103+
assert_no_changes -> { ActiveResource::HttpMock.requests.count }, from: 1 do
104+
10.times { people.load }
105+
end
106+
end
107+
108+
def test_where_reloading
109+
ActiveResource::HttpMock.respond_to.get "/people.json?id=2", {}, @people_david
110+
people = Person.where(id: 2)
111+
112+
assert_changes -> { ActiveResource::HttpMock.requests.count }, from: 0, to: 1 do
113+
assert_equal 1, people.size
114+
end
115+
assert_no_changes -> { ActiveResource::HttpMock.requests.count }, from: 1 do
116+
assert_equal 1, people.size
117+
end
118+
assert_changes -> { ActiveResource::HttpMock.requests.count }, from: 1, to: 2 do
119+
people.reload
120+
end
121+
assert_no_changes -> { ActiveResource::HttpMock.requests.count }, from: 2 do
122+
assert_equal 1, people.size
123+
end
124+
end
125+
66126
def test_where_clause_with_unpermitted_params
67127
params = StrongParameters.new(person_id: "1")
68128
assert_raises(ActiveModel::ForbiddenAttributesError) { StreetAddress.where(params) }

test/cases/notifications_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def setup
1515
end
1616

1717
def test_get_request_with_params
18-
payload = capture_notifications { Person.where(name: "Matz") }
18+
payload = capture_notifications { Person.where(name: "Matz").load }
1919

2020
assert_equal :get, payload[:method]
2121
assert_equal "http://37s.sunrise.i:3000/people.json?name=Matz", payload[:request_uri]

0 commit comments

Comments
 (0)