Skip to content

Commit

Permalink
Adding transaction save!, create!, destroy! methods (#232)
Browse files Browse the repository at this point in the history
* Added save! method

* Added destroy! method

* Added create! method

* Added nil? check on create!

* Changed nil check to errors check

* Review changes

* Update transactions.cr

* Added custom exceptions and added tests for them

* Removed Nil type because i fixed an bug than produced Nil values

* Added update and update! methods

* Changed other spec files with persisted? where possible

* Using callback model in the spec_models instead of introducing an new one

* Docker does not cleanup callback model properly. abort_at = 'temp' fixes

* Forgot to change to save! again

* RecordInvalid to RecordNotSaved and model is now attached to the errors

* Added new methods to the README.md
  • Loading branch information
Thellior authored and robacarp committed Jun 21, 2018
1 parent e346eb3 commit f2feca3
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 10 deletions.
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,13 +335,24 @@ post = Post.find_by!(slug: "foo") # raises when no records found.
other_post = Post.find_by(slug: "foo", type: "bar") # Also works for multiple arguments.
```

#### Create
```crystal
Post.create(name: "Granite Rocks!", body: "Check this out.") # Set attributes and call save
Post.create!(name: "Granite Rocks!", body: "Check this out.") # Set attributes and call save!. Will throw an exception when the save failed
```

#### Insert

```crystal
post = Post.new
post.name = "Granite Rocks!"
post.body = "Check this out."
post.save
post = Post.new
post.name = "Granite Rocks!"
post.body = "Check this out."
post.save! # raises when save failed
```

#### Update
Expand All @@ -350,6 +361,12 @@ post.save
post = Post.find 1
post.name = "Granite Really Rocks!"
post.save
post = Post.find 1
post.update(name: "Granite Really Rocks!") # Assigns attributes and calls save
post = Post.find 1
post.update!(name: "Granite Really Rocks!") # Assigns attributes and calls save!. Will throw an exception when the save failed
```

#### Delete
Expand All @@ -358,6 +375,9 @@ post.save
post = Post.find 1
post.destroy
puts "deleted" unless post
post = Post.find 1
post.destroy! # raises when delete failed
```

### Queries
Expand Down Expand Up @@ -423,9 +443,9 @@ class CustomView < Granite:Base
field commentbody : String
query <<-SQL
SELECT articles.articlebody, comments.commentbody
FROM articles
JOIN comments
SELECT articles.articlebody, comments.commentbody
FROM articles
JOIN comments
ON comments.articleid = articles.id
SQL
end
Expand Down
27 changes: 27 additions & 0 deletions spec/granite/exceptions/record_invalid_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require "../../spec_helper"

{% for adapter in GraniteExample::ADAPTERS %}
module {{adapter.capitalize.id}}
describe Granite::RecordNotSaved do
it "should have a message" do
parent = Parent.new
parent.save

Granite::RecordNotSaved
.new(Parent.name, parent)
.message
.should eq("Could not process {{adapter.capitalize.id}}::Parent")
end

it "should have a model" do
parent = Parent.new
parent.save

Granite::RecordNotSaved
.new(Parent.name, parent)
.model
.should eq(parent)
end
end
end
{% end %}
27 changes: 27 additions & 0 deletions spec/granite/exceptions/record_not_destroyed_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require "../../spec_helper"

{% for adapter in GraniteExample::ADAPTERS %}
module {{adapter.capitalize.id}}
describe Granite::RecordNotDestroyed do
it "should have a message" do
parent = Parent.new
parent.save

Granite::RecordNotDestroyed
.new(Parent.name, parent)
.message
.should eq("Could not destroy {{adapter.capitalize.id}}::Parent")
end

it "should have a model" do
parent = Parent.new
parent.save

Granite::RecordNotDestroyed
.new(Parent.name, parent)
.model
.should eq(parent)
end
end
end
{% end %}
22 changes: 18 additions & 4 deletions spec/granite/transactions/create_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ module {{adapter.capitalize.id}}
describe "{{ adapter.id }} .create" do
it "creates a new object" do
parent = Parent.create(name: "Test Parent")
parent.id.should_not be_nil
parent.persisted?.should be_true
parent.name.should eq("Test Parent")
end

it "does not create an invalid object" do
parent = Parent.create(name: "")
parent.id.should be_nil
parent.persisted?.should be_false
end

it "takes JSON::Any" do
Expand Down Expand Up @@ -51,15 +51,15 @@ module {{adapter.capitalize.id}}
describe "with a custom primary key" do
it "creates a new object" do
school = School.create(name: "Test School")
school.custom_id.should_not be_nil
school.persisted?.should be_true
school.name.should eq("Test School")
end
end

describe "with a modulized model" do
it "creates a new object" do
county = Nation::County.create(name: "Test School")
county.id.should_not be_nil
county.persisted?.should be_true
county.name.should eq("Test School")
end
end
Expand All @@ -72,5 +72,19 @@ module {{adapter.capitalize.id}}
end
end
end

describe "{{ adapter.id }} .create!" do
it "creates a new object" do
parent = Parent.create!(name: "Test Parent")
parent.persisted?.should be_true
parent.name.should eq("Test Parent")
end

it "does not save but raise an exception" do
expect_raises(Granite::RecordNotSaved, "{{adapter.capitalize.id}}::Parent") do
parent = Parent.create!(name: "")
end
end
end
end
{% end %}
30 changes: 30 additions & 0 deletions spec/granite/transactions/destroy_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,35 @@ module {{adapter.capitalize.id}}
end
end
end

describe "{{ adapter.id }} #destroy!" do
it "destroys an object" do
parent = Parent.new
parent.name = "Test Parent"
parent.save!

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

it "does not destroy but raise an exception" do
callback_with_abort = CallbackWithAbort.new
callback_with_abort.name = "DestroyRaisesException"
callback_with_abort.abort_at = "temp"
callback_with_abort.do_abort = false
callback_with_abort.save!
callback_with_abort.abort_at = "before_destroy"
callback_with_abort.do_abort = true


expect_raises(Granite::RecordNotDestroyed, "{{adapter.capitalize.id}}::CallbackWithAbort") do
callback_with_abort.destroy!
end

CallbackWithAbort.find_by(name: callback_with_abort.name).should_not be_nil
end
end
end
{% end %}
23 changes: 20 additions & 3 deletions spec/granite/transactions/save_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ module {{adapter.capitalize.id}}
parent = Parent.new
parent.name = "Test Parent"
parent.save
parent.id.should_not be_nil
parent.persisted?.should be_true
end

it "does not create an invalid object" do
parent = Parent.new
parent.name = ""
parent.save
parent.id.should be_nil
parent.persisted?.should be_false
end

it "updates an existing object" do
Expand Down Expand Up @@ -96,7 +96,7 @@ module {{adapter.capitalize.id}}
county = Nation::County.new
county.name = "Test School"
county.save
county.id.should_not be_nil
county.persisted?.should be_true
end

it "updates an existing object" do
Expand Down Expand Up @@ -132,5 +132,22 @@ module {{adapter.capitalize.id}}
end
end
end

describe "{{ adapter.id }} #save!" do
it "creates a new object" do
parent = Parent.new
parent.name = "Test Parent"
parent.save!
parent.persisted?.should be_true
end

it "does not create but raise an exception" do
parent = Parent.new

expect_raises(Granite::RecordNotSaved, "{{adapter.capitalize.id}}::Parent") do
parent.save!
end
end
end
end
{% end %}
50 changes: 50 additions & 0 deletions spec/granite/transactions/update_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require "../../spec_helper"

{% for adapter in GraniteExample::ADAPTERS %}
module {{adapter.capitalize.id}}
describe "{{ adapter.id }} #update" do
it "updates an object" do
parent = Parent.new(name: "New Parent")
parent.save!

parent.update(name: "Other parent").should be_true
parent.name.should eq "Other parent"

Parent.find!(parent.id).name.should eq "Other parent"
end

it "does not update an invalid object" do
parent = Parent.new(name: "New Parent")
parent.save!

parent.update(name: "").should be_false
parent.name.should eq ""

Parent.find!(parent.id).name.should eq "New Parent"
end
end

describe "{{ adapter.id }} #update!" do
it "updates an object" do
parent = Parent.new(name: "New Parent")
parent.save!

parent.update!(name: "Other parent")
parent.name.should eq "Other parent"

Parent.find!(parent.id).name.should eq "Other parent"
end

it "does not update but raises an exception" do
parent = Parent.new(name: "New Parent")
parent.save!

expect_raises(Granite::RecordNotSaved, "{{adapter.capitalize.id}}::Parent") do
parent.update!(name: "")
end

Parent.find!(parent.id).name.should eq "New Parent"
end
end
end
{% end %}
1 change: 1 addition & 0 deletions spec/spec_models.cr
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ require "uuid"
table_name callbacks_with_abort
primary abort_at : String, auto: false
field do_abort : Bool
field name : String

property history : IO::Memory = IO::Memory.new

Expand Down
21 changes: 21 additions & 0 deletions src/granite/exceptions.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Granite
class RecordNotSaved < ::Exception
getter model : Granite::Base

def initialize(class_name : String, model : Granite::Base)
super("Could not process #{class_name}")

@model = model
end
end

class RecordNotDestroyed < ::Exception
getter model : Granite::Base

def initialize(class_name : String, model : Granite::Base)
super("Could not destroy #{class_name}")

@model = model
end
end
end
Loading

0 comments on commit f2feca3

Please sign in to comment.