Skip to content

Commit

Permalink
find,find_by,first now ensures T, call with trailing ? for …
Browse files Browse the repository at this point in the history
…`T?`
  • Loading branch information
maiha committed Feb 18, 2018
1 parent fab317b commit 76223f2
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 37 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,28 +126,34 @@ end
#### Find First

```crystal
post = Post.first
post = Post.first?
if post
puts post.name
end
post = Post.first # raises when no records exist
```

#### Find

```crystal
post = Post.find 1
post = Post.find? 1
if post
puts post.name
end
post = Post.find 1 # raises when no records found
```

#### Find By

```crystal
post = Post.find_by :slug, "example_slug"
post = Post.find_by? :slug, "example_slug"
if post
puts post.name
end
post = Post.find_by :slug, "foo" # raises when no records found
```

#### Insert
Expand Down
8 changes: 4 additions & 4 deletions spec/granite_orm/fields/timestamps_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module {{adapter.capitalize.id}}
describe "{{ adapter.id }} timestamps" do
it "consistently uses UTC for created_at" do
parent = Parent.new(name: "parent").tap(&.save)
found_parent = Parent.find(parent.id).not_nil!
found_parent = Parent.find(parent.id)

original_timestamp = parent.created_at
read_timestamp = found_parent.created_at
Expand All @@ -28,7 +28,7 @@ module {{adapter.capitalize.id}}

it "consistently uses UTC for updated_at" do
parent = Parent.new(name: "parent").tap(&.save)
found_parent = Parent.find(parent.id).not_nil!
found_parent = Parent.find(parent.id)

original_timestamp = parent.updated_at
read_timestamp = found_parent.updated_at
Expand All @@ -39,7 +39,7 @@ module {{adapter.capitalize.id}}

it "truncates the subsecond parts of created_at" do
parent = Parent.new(name: "parent").tap(&.save)
found_parent = Parent.find(parent.id).not_nil!
found_parent = Parent.find(parent.id)

original_timestamp = parent.created_at
read_timestamp = found_parent.created_at
Expand All @@ -49,7 +49,7 @@ module {{adapter.capitalize.id}}

it "truncates the subsecond parts of updated_at" do
parent = Parent.new(name: "parent").tap(&.save)
found_parent = Parent.find(parent.id).not_nil!
found_parent = Parent.find(parent.id)

original_timestamp = parent.updated_at
read_timestamp = found_parent.updated_at
Expand Down
25 changes: 20 additions & 5 deletions spec/granite_orm/querying/find_by_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ require "../../spec_helper"

{% for adapter in GraniteExample::ADAPTERS %}
module {{adapter.capitalize.id}}
describe "{{ adapter.id }} #find_by" do
describe "{{ adapter.id }} #find_by?, #find_by" do
it "finds an object with a string field" do
name = "robinson"

model = Parent.new
model.name = name
model.save

found = Parent.find_by("name", name)
found = Parent.find_by?("name", name)
found.not_nil!.id.should eq model.id

found = Parent.find_by("name", name)
found.should be_a(Parent)
end

it "finds an object with a symbol field" do
Expand All @@ -21,8 +24,11 @@ module {{adapter.capitalize.id}}
model.name = name
model.save

found = Parent.find_by(:name, name)
found = Parent.find_by?(:name, name)
found.not_nil!.id.should eq model.id

found = Parent.find_by(:name, name)
found.id.should eq model.id
end

it "also works with reserved words" do
Expand All @@ -32,11 +38,20 @@ module {{adapter.capitalize.id}}
model.all = value
model.save

found = ReservedWord.find_by("all", value)
found = ReservedWord.find_by?("all", value)
found.not_nil!.id.should eq model.id

found = ReservedWord.find_by(:all, value)
found.not_nil!.id.should eq model.id
found.id.should eq model.id
end

it "returns nil or raises if no result" do
found = Parent.find_by?("name", "xxx")
found.should be_nil

expect_raises(Granite::ORM::Querying::NotFound, /Couldn't find .*Parent.* with name=xxx/) do
Parent.find_by("name", "xxx")
end
end
end
end
Expand Down
26 changes: 22 additions & 4 deletions spec/granite_orm/querying/find_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ require "../../spec_helper"

{% for adapter in GraniteExample::ADAPTERS %}
module {{adapter.capitalize.id}}
describe "{{ adapter.id }} #find" do
describe "{{ adapter.id }} #find?, #find" do
it "finds an object by id" do
model = Parent.new
model.name = "Test Comment"
model.save

found = Parent.find model.id
found = Parent.find? model.id
found.should_not be_nil
found.not_nil!.id.should eq model.id

found = Parent.find model.id
found.id.should eq model.id
end

describe "with a custom primary key" do
Expand All @@ -20,8 +23,11 @@ module {{adapter.capitalize.id}}
school.save
primary_key = school.custom_id

found_school = School.find primary_key
found_school = School.find? primary_key
found_school.should_not be_nil

found_school = School.find primary_key
found_school.should be_a(School)
end
end

Expand All @@ -32,8 +38,20 @@ module {{adapter.capitalize.id}}
county.save
primary_key = county.id

found_county = Nation::County.find primary_key
found_county = Nation::County.find? primary_key
found_county.should_not be_nil

found_county = Nation::County.find primary_key
found_county.should be_a(Nation::County)
end
end

it "returns nil or raises if no result" do
found = Parent.find? 0
found.should be_nil

expect_raises(Granite::ORM::Querying::NotFound, /Couldn't find .*Parent.* with id=0/) do
Parent.find 0
end
end
end
Expand Down
20 changes: 15 additions & 5 deletions spec/granite_orm/querying/first_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ require "../../spec_helper"

{% for adapter in GraniteExample::ADAPTERS %}
module {{adapter.capitalize.id}}
describe "{{ adapter.id }} #first" do
describe "{{ adapter.id }} #first?, #first" do
it "finds the first object" do
first = Parent.new.tap do |model|
model.name = "Test 1"
Expand All @@ -14,8 +14,11 @@ module {{adapter.capitalize.id}}
model.save
end

found = Parent.first
found = Parent.first?
found.not_nil!.id.should eq first.id

found = Parent.first
found.id.should eq first.id
end

it "supports a SQL clause" do
Expand All @@ -29,18 +32,25 @@ module {{adapter.capitalize.id}}
model.save
end

found = Parent.first("ORDER BY id DESC")
found = Parent.first?("ORDER BY id DESC")
found.not_nil!.id.should eq second.id

found = Parent.first("ORDER BY id DESC")
found.id.should eq second.id
end

it "returns nil if no result" do
it "returns nil or raises if no result" do
first = Parent.new.tap do |model|
model.name = "Test 1"
model.save
end

found = Parent.first("WHERE name = 'Test 2'")
found = Parent.first?("WHERE name = 'Test 2'")
found.should be nil

expect_raises(Granite::ORM::Querying::NotFound, /Couldn't find .*Parent.* with first\(WHERE name = 'Test 2'\)/) do
Parent.first("WHERE name = 'Test 2'")
end
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions spec/granite_orm/transactions/destroy_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module {{adapter.capitalize.id}}

id = parent.id
parent.destroy
found = Parent.find id
found = Parent.find? id
found.should be_nil
end

Expand All @@ -22,7 +22,7 @@ module {{adapter.capitalize.id}}
primary_key = school.custom_id
school.destroy

found_school = School.find primary_key
found_school = School.find? primary_key
found_school.should be_nil
end
end
Expand All @@ -35,7 +35,7 @@ module {{adapter.capitalize.id}}
primary_key = county.id
county.destroy

found_county = Nation::County.find primary_key
found_county = Nation::County.find? primary_key
found_county.should be_nil
end
end
Expand Down
10 changes: 5 additions & 5 deletions spec/granite_orm/transactions/save_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module {{adapter.capitalize.id}}
parents.size.should eq 1

found = Parent.first
found.not_nil!.name.should eq parent.name
found.name.should eq parent.name
end

it "does not update an invalid object" do
Expand All @@ -38,7 +38,7 @@ module {{adapter.capitalize.id}}
parent.name = ""
parent.save
parent = Parent.find parent.id
parent.not_nil!.name.should eq "Test Parent"
parent.name.should eq "Test Parent"
end

describe "with a custom primary key" do
Expand All @@ -63,8 +63,8 @@ module {{adapter.capitalize.id}}
school.save

found_school = School.find primary_key
found_school.not_nil!.custom_id.should eq primary_key
found_school.not_nil!.name.should eq new_name
found_school.custom_id.should eq primary_key
found_school.name.should eq new_name
end
end

Expand All @@ -90,7 +90,7 @@ module {{adapter.capitalize.id}}
county.save

found_county = Nation::County.find primary_key
found_county.not_nil!.name.should eq new_name
found_county.name.should eq new_name
end
end

Expand Down
26 changes: 18 additions & 8 deletions src/granite_orm/querying.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
module Granite::ORM::Querying
class NotFound < Exception
end

macro extended
macro __process_querying
\{% primary_name = PRIMARY[:name] %}
Expand Down Expand Up @@ -46,31 +49,38 @@ module Granite::ORM::Querying
end

# First adds a `LIMIT 1` clause to the query and returns the first result
def first(clause = "", params = [] of DB::Any)
def first?(clause = "", params = [] of DB::Any)
all([clause.strip, "LIMIT 1"].join(" "), params).first?
end

def first(clause = "", params = [] of DB::Any)
first?(clause, params) || raise NotFound.new("Couldn't find " + {{@type.name.stringify}} + " with first(#{clause})")
end

# find returns the row with the primary key specified.
# it checks by primary by default, but one can pass
# another field for comparison
def find(value)
return find_by(@@primary_name, value)
def find?(value)
return find_by?(@@primary_name, value)
end

# find_by using symbol for field name.
def find_by(field : Symbol, value)
find_by(field.to_s, value)
def find(value)
return find_by(@@primary_name, value)
end

# find_by returns the first row found where the field maches the value
def find_by(field : String, value)
def find_by?(field : String | Symbol, value)
row = nil
@@adapter.select_one(@@table_name, fields([@@primary_name]), field, value) do |result|
@@adapter.select_one(@@table_name, fields([@@primary_name]), field.to_s, value) do |result|
row = from_sql(result) if result
end
return row
end

def find_by(field : String | Symbol, value)
find_by?(field, value) || raise NotFound.new("Couldn't find " + {{@type.name.stringify}} + " with #{field}=#{value}")
end

def find_each(clause = "", params = [] of DB::Any, batch_size limit = 100, offset = 0)
find_in_batches(clause, params, batch_size: limit, offset: offset) do |batch|
batch.each do |record|
Expand Down

0 comments on commit 76223f2

Please sign in to comment.