Skip to content

Commit 73d2279

Browse files
committed
Add .find_by! method
Behaves the same as `.find_by`, accepting one or more attributes to find a matching record by. The key difference is the method raises a `StaticAssociation::RecordNotFound` exception if no record is found.
1 parent 403cfb5 commit 73d2279

File tree

3 files changed

+109
-1
lines changed

3 files changed

+109
-1
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@ The `Day` class will gain `.all`, `.find`, `.find_by_id`, `.find_by` and
4949
- The `.all` method returns all the static records defined in the class.
5050
- The `.ids` method returns an array of all the ids of the static records.
5151
- The `.find` method accepts a single id and returns the matching record. If the
52-
record does not exist, a `RecordNotFound` error is raised.
52+
record does not exist, a `StaticAssociation::RecordNotFound` error is raised.
5353
- The `.find_by_id` method behaves similarly to the `.find` method, except it
5454
returns `nil` when a record does not exist.
5555
- `find_by` finds the first record matching the specified conditions. If no
5656
record is found, returns `nil`.
57+
- `find_by!` behaves like `find_by` but raises a
58+
`StaticAssociation::RecordNotFound` error if no record is found.
5759
- The `.where` method accepts an array of ids and returns all records with
5860
matching ids.
5961

lib/static_association.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ def find_by(**args)
6262
all.find { |record| matches_attributes?(record: record, attributes: args) }
6363
end
6464

65+
def find_by!(**args)
66+
find_by(**args) or raise RecordNotFound.new(
67+
"Couldn't find #{name} with " +
68+
args.map { |k, v| "#{k}=#{v}" }.join(", ")
69+
)
70+
end
71+
6572
def record(settings, &block)
6673
settings.assert_valid_keys(:id)
6774
id = settings.fetch(:id)

spec/static_association_spec.rb

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,105 @@ class AssociationClass
286286
end
287287
end
288288

289+
describe ".find_by!" do
290+
context "when record exists with the specified attribute value" do
291+
it "returns the record" do
292+
record1 = DummyClass.record(id: 1) do |r|
293+
r.name = "foo"
294+
end
295+
_record2 = DummyClass.record(id: 2) do |r|
296+
r.name = "bar"
297+
end
298+
299+
found_record = DummyClass.find_by!(name: "foo")
300+
301+
expect(found_record).to eq(record1)
302+
end
303+
end
304+
305+
context "when no record exists that matches the specified attribute value" do
306+
it "raises exception" do
307+
DummyClass.record(id: 1) do |r|
308+
r.name = "foo"
309+
end
310+
311+
expect {
312+
DummyClass.find_by!(name: "bar")
313+
}.to raise_error(
314+
StaticAssociation::RecordNotFound,
315+
"Couldn't find DummyClass with name=bar"
316+
)
317+
end
318+
end
319+
320+
context "when multiple records match the specified attribute value" do
321+
it "returns the first matching record" do
322+
record1 = DummyClass.record(id: 1) do |r|
323+
r.name = "foo"
324+
end
325+
_record2 = DummyClass.record(id: 2) do |r|
326+
r.name = "foo"
327+
end
328+
329+
found_record = DummyClass.find_by!(name: "foo")
330+
331+
expect(found_record).to eq(record1)
332+
end
333+
end
334+
335+
context "when specifying multiple attribute values" do
336+
it "returns the record matching all attributes" do
337+
_record1 = DummyClass.record(id: 1) do |r|
338+
r.name = "foo"
339+
end
340+
record2 = DummyClass.record(id: 2) do |r|
341+
r.name = "foo"
342+
end
343+
344+
found_record = DummyClass.find_by!(id: 2, name: "foo")
345+
346+
expect(found_record).to eq(record2)
347+
end
348+
end
349+
350+
context "when specifying multiple attribute values but no record " \
351+
"matches all attributes" do
352+
it "returns nil" do
353+
_record1 = DummyClass.record(id: 1) do |r|
354+
r.name = "foo"
355+
end
356+
357+
expect {
358+
DummyClass.find_by!(id: 1, name: "bar")
359+
}.to raise_error(
360+
StaticAssociation::RecordNotFound,
361+
"Couldn't find DummyClass with id=1, name=bar"
362+
)
363+
end
364+
end
365+
366+
context "with undefined attributes" do
367+
it "raises a StaticAssociation::UndefinedAttribute" do
368+
DummyClass.record(id: 1)
369+
370+
expect {
371+
DummyClass.find_by!(undefined_attribute: 1)
372+
}.to raise_error(
373+
StaticAssociation::UndefinedAttribute,
374+
"Undefined attribute 'undefined_attribute'"
375+
)
376+
end
377+
end
378+
379+
context "with no attributes" do
380+
it "raises a StaticAssociation::ArgumentError" do
381+
expect {
382+
DummyClass.find_by!
383+
}.to raise_error(StaticAssociation::ArgumentError)
384+
end
385+
end
386+
end
387+
289388
describe ".belongs_to_static" do
290389
it "defines a reader method for the association" do
291390
associated_class = AssociationClass.new

0 commit comments

Comments
 (0)