Skip to content

Commit 14c6941

Browse files
committed
Add .find_by! method
Behaves the same as `.find_by`, accepting one or more attributes to find a matching record by its ID. The only difference is the method raises a `StaticAssociation::RecordNotFound` exception if no record is found.
1 parent 9959d1e commit 14c6941

3 files changed

Lines changed: 119 additions & 1 deletion

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ The `Day` class will gain the following methods:
5353
`nil` when a record does not exist.
5454
- `.find_by`: finds the first record matching the specified conditions. If no
5555
record is found, returns `nil`.
56+
- `find_by!` behaves like `find_by` but raises a
57+
`StaticAssociation::RecordNotFound` error if no record is found.
5658
- `.where`: accepts an array of ids and returns all records with matching ids.
5759

5860
### Associations

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: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,16 @@ class AssociationClass
119119
)
120120
end
121121
end
122+
123+
context "when argument is numeric string" do
124+
it "returns the record" do
125+
record = DummyClass.record(id: 1)
126+
127+
found_record = DummyClass.find("1")
128+
129+
expect(found_record).to eq(record)
130+
end
131+
end
122132
end
123133

124134
describe ".find_by_id" do
@@ -264,7 +274,7 @@ class AssociationClass
264274
end
265275

266276
context "with undefined attributes" do
267-
it "raises a StaticAssociation::UndefinedAttribute" do
277+
it "raises an error" do
268278
DummyClass.record(id: 1)
269279

270280
expect {
@@ -286,6 +296,105 @@ class AssociationClass
286296
end
287297
end
288298

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

0 commit comments

Comments
 (0)