Skip to content
12 changes: 5 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: crystal
script:
- crystal spec
- crystal spec -D $DB_TYPE
- crystal tool format --check
services:
- mysql
Expand All @@ -18,11 +18,9 @@ before_script:
- psql -c 'create database crecto_test;' -U postgres
- psql $PG_URL < spec/migrations/pg_migrations.sql
- sqlite3 ./crecto_test.db < spec/migrations/sqlite3_migrations.sql
- if [ ! -z "$PG_URL" ]; then cp ./spec/travis_pg_repo.cr ./spec/repo.cr; fi
- if [ ! -z "$MYSQL_URL" ]; then cp ./spec/travis_mysql_repo.cr ./spec/repo.cr; fi
- if [ ! -z "$SQLITE3_PATH" ]; then cp ./spec/travis_sqlite_repo.cr ./spec/repo.cr; fi
- cp ./spec/travis_${DB_TYPE}_repo.cr ./spec/repo.cr
env:
matrix:
- PG_URL=postgres://postgres@localhost:5432/crecto_test
- MYSQL_URL=mysql://root@localhost/crecto_test
- SQLITE3_PATH=sqlite3://./crecto_test.db
- PG_URL=postgres://postgres@localhost:5432/crecto_test DB_TYPE=pg
- MYSQL_URL=mysql://root@localhost/crecto_test DB_TYPE=mysql
- SQLITE3_PATH=sqlite3://./crecto_test.db DB_TYPE=sqlite
90 changes: 72 additions & 18 deletions spec/transactions_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ describe Crecto do
user = User.new

expect_raises Crecto::InvalidChangeset do
Repo.transaction! do
Repo.insert!(user)
Repo.transaction! do |tx|
tx.insert!(user)
end
end
end
Expand All @@ -193,8 +193,8 @@ describe Crecto do
user = User.new
user.name = "this should insert in the transaction"

Repo.transaction! do
Repo.insert(user)
Repo.transaction! do |tx|
tx.insert(user)
end

users = Repo.all(User, Query.where(name: "this should insert in the transaction"))
Expand All @@ -207,8 +207,8 @@ describe Crecto do

user = quick_create_user("this should delete")

Repo.transaction! do
Repo.delete!(user)
Repo.transaction! do |tx|
tx.delete!(user)
end

users = Repo.all(User, Query.where(id: user.id))
Expand All @@ -222,8 +222,8 @@ describe Crecto do

Repo.delete_all(Post)

Repo.transaction! do
Repo.delete_all(User)
Repo.transaction! do |tx|
tx.delete_all(User)
end

users = Repo.all(User)
Expand All @@ -235,8 +235,8 @@ describe Crecto do

user.name = "this should have changed 89ffsf"

Repo.transaction! do
Repo.update(user)
Repo.transaction! do |tx|
tx.update(user)
end

user = Repo.get!(User, user.id)
Expand All @@ -248,8 +248,8 @@ describe Crecto do
quick_create_user_with_things("testing_update_all", 123)
quick_create_user_with_things("testing_update_all", 123)

Repo.transaction! do
Repo.update_all(User, Query.where(name: "testing_update_all"), {things: 9494})
Repo.transaction! do |tx|
tx.update_all(User, Query.where(name: "testing_update_all"), {things: 9494})
end

Repo.all(User, Query.where(things: 123)).size.should eq 0
Expand All @@ -268,12 +268,12 @@ describe Crecto do
insert_user = User.new
insert_user.name = "all_transactions_insert_user"

Repo.transaction! do
Repo.insert!(insert_user)
Repo.delete!(delete_user)
Repo.delete_all(Post)
Repo.update!(update_user)
Repo.update_all(User, Query.where(name: "perform_all"), {name: "perform_all_io2oj999"})
Repo.transaction! do |tx|
tx.insert!(insert_user)
tx.delete!(delete_user)
tx.delete_all(Post)
tx.update!(update_user)
tx.update_all(User, Query.where(name: "perform_all"), {name: "perform_all_io2oj999"})
end

# check insert happened
Expand Down Expand Up @@ -336,6 +336,60 @@ describe Crecto do
Repo.all(User, Query.where(name: "perform_all")).size.should eq 2
Repo.all(User, Query.where(name: "perform_all_io2oj999")).size.should eq 0
end

# This only works for postgres for now
{% begin %}
{{ flag?(:pg) ? :it.id : :pending.id }} "allows reading records inserted inside the transaction" do
insert_user = User.new
insert_user.name = "insert_user"

Repo.transaction! do |tx|
id = tx.insert!(insert_user).instance.id
tx.get(User, id).should_not eq(nil)
tx.get!(User, id).should_not eq(nil)
tx.get(User, id, Query.new).should_not eq(nil)
tx.get!(User, id, Query.new).should_not eq(nil)
tx.get_by(User, id: id).should_not eq(nil)
tx.get_by!(User, id: id).should_not eq(nil)
tx.get_by(User, id: id).should_not eq(nil)
tx.get_by!(User, id: id).should_not eq(nil)
tx.get_by(User, Query.where(id: id)).should_not eq(nil)
tx.get_by!(User, Query.where(id: id)).should_not eq(nil)
tx.all(User, Query.where(id: id)).first.should_not eq(nil)
tx.all(User, Query.where(id: id), preload: [] of Symbol).first.should_not eq(nil)
end
end
{% end %}

# Sqlite doesn't support nesting transactions
{% unless flag?(:sqlite) %}
it "allows nesting transactions" do
Repo.delete_all(Post)
Repo.delete_all(User)

insert_user = User.new
insert_user.name = "nested_transactions_insert_user"
invalid_user = User.new
delete_user = quick_create_user("nested_transactions_delete_user")

Repo.transaction! do |tx|
tx.insert!(insert_user)

expect_raises Crecto::InvalidChangeset do
Repo.transaction! do |inner_tx|
inner_tx.delete!(delete_user)
inner_tx.insert!(invalid_user)
end
end
end

# check insert happened
Repo.all(User, Query.where(name: "nested_transactions_insert_user")).size.should eq 1

# check delete didn't happen
Repo.all(User, Query.where(name: "nested_transactions_delete_user")).size.should eq 1
end
{% end %}
end
end
end
98 changes: 94 additions & 4 deletions src/crecto/live_transaction.cr
Original file line number Diff line number Diff line change
@@ -1,13 +1,89 @@
require "./multi"
require "./repo/query"

module Crecto
class LiveTransaction(T)
alias Query = Repo::Query

def initialize(@tx : DB::Transaction, @repo : T)
end

def raw_exec(args : Array)
@repo.raw_exec(args, tx: @tx)
end

def raw_exec(*args)
@repo.raw_exec(*args, tx: @tx)
end

def raw_query(query, *args)
@repo.raw_query(query, *args, tx: @tx) do |rs|
yield rs
end
end

def raw_query(query, args : Array)
@repo.raw_query(query, args, tx: @tx)
end

def raw_query(query, *args)
@repo.raw_query(query, *args)
end

def raw_scalar(*args)
@repo.raw_scalar(*args, tx: @tx)
end

def all(queryable, query : Query, *, preload = [] of Symbol)
@repo.all(queryable, query, tx: @tx, preload: preload)
end

def all(queryable, query = Query.new)
@repo.all(queryable, query, tx: @tx)
end

def get(queryable, id)
@repo.get(queryable, id, tx: @tx)
end

def get!(queryable, id)
@repo.get!(queryable, id, tx: @tx)
end

def get(queryable, id, query : Query)
@repo.get(queryable, id, query, tx: @tx)
end

def get!(queryable, id, query : Query)
@repo.get!(queryable, id, query, tx: @tx)
end

def get_by(queryable, **opts)
@repo.get_by(queryable, @tx, **opts)
end

def get_by(queryable, query)
@repo.get_by(queryable, query, tx: @tx)
end

def get_by!(queryable, **opts)
@repo.get_by!(queryable, @tx, **opts)
end

def get_by!(queryable, query)
@repo.get_by!(queryable, query, tx: @tx)
end

def get_association(queryable_instance, association_name : Symbol)
@repo.get_association(queryable_instance, association_name, tx: @tx)
end

def get_association!(queryable_instance, association_name : Symbol)
@repo.get_association!(queryable_instance, association_name, tx: @tx)
end

{% for type in %w[insert insert! delete delete! update update!] %}
def {{type.id}}(queryable : Crecto::Model)
@repo.{{type.id}}(queryable, @tx)
@repo.{{type.id}}(queryable, tx: @tx)
end

def {{type.id}}(changeset : Crecto::Changeset::Changeset)
Expand All @@ -16,15 +92,29 @@ module Crecto
{% end %}

def delete_all(queryable, query = Crecto::Repo::Query.new)
@repo.delete_all(queryable, query, @tx)
@repo.delete_all(queryable, query, tx: @tx)
end

def update_all(queryable, query, update_hash : Multi::UpdateHash)
@repo.update_all(queryable, query, update_hash, @tx)
@repo.update_all(queryable, query, update_hash, tx: @tx)
end

def update_all(queryable, query, update_tuple : NamedTuple)
update_all(queryable, query, update_tuple.to_h)
end

def aggregate(queryable, aggregate_function : Symbol, field : Symbol)
@repo.aggregate(queryable, aggregate_function, field, tx: @tx)
end

def aggregate(queryable, aggregate_function : Symbol, field : Symbol, query : Crecto::Repo::Query)
@repo.aggregate(queryable, aggregate_function, field, query, tx: @tx)
end

def transaction!
@repo.transaction!(@tx) do |tx|
yield tx
end
end
end
end
Loading