Skip to content

Commit

Permalink
Merge pull request #18 from fridgerator/has_many
Browse files Browse the repository at this point in the history
has_many and belongs_to associations
  • Loading branch information
fridgerator authored Dec 17, 2016
2 parents 0485e20 + 26666ef commit 96b48ca
Show file tree
Hide file tree
Showing 17 changed files with 443 additions and 98 deletions.
2 changes: 1 addition & 1 deletion .crystal-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.20.0
0.20.1
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## Unreleased


# [0.3.0] 2016-12-17
* Check for result.rows.size in queries - [@neovintage](https://github.com/neovintage)
* `or_where` queries
* `update_all` queries
* `delete_all` queries
* raw (aribtrary) sql queries (i.e. `Crecto::Repo.query("select * from users")`) - [@neovintage](https://github.com/neovintage)
* now using [`crystal-db`](https://github.com/crystal-lang/crystal-db)
* `has_many` associations, with preload
* `belongs_to` assocaitions

## [0.2.0] 2016-11-30
* Added this changelog
Expand All @@ -24,4 +30,5 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
* Query
* Postgres Adapter

[0.3.0]: https://github.com/fridgerator/crecto/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/fridgerator/crecto/compare/0.1.0...v0.2.0
51 changes: 37 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ Make sure you have `ENV['PG_URL']` set

#### Roadmap (in no particular order)

- [ ] `select` in query
- [ ] MySQL adapter
- [ ] Choose adapter in config
- [ ] Associations
- [ ] Preload
- [ ] Joins
- [ ] SQLite adapter
- [ ] Choose adapter in config
- [x] Associations
- [x] Preload
- [ ] Joins

## Usage

Expand All @@ -42,21 +44,28 @@ require "crecto"
#
# Define table name, fields and validations in your class
#
class User
include Crecto::Schema
extend Crecto::Changeset(User)
class User < Crecto::Model
schema "users" do
field :age, Int32
field :name, String
field :is_admin, Bool
field :temporary_info, Float64, virtual: true
has_many :posts
end
validate_required [:name, :age]
validate_format :name, /[*a-zA-Z]/
end
class Post < Crecto::Model
schema "posts" do
field :user_id, PkValue
belongs_to :user
end
end
user = User.new
user.name = "123"
user.age = 123
Expand Down Expand Up @@ -97,27 +106,43 @@ query = Crecto::Repo::Query
.limit(1)
#
# all
# All
#
users = Crecto::Repo.all(User, query)
users.as(Array) unless users.nil?
#
# get by primary key
# Get by primary key
#
user = Crecto::Repo.get(User, 1)
user.as(User) unless user.nil?
#
# get by fields
# Get by fields
#
Crecto::Repo.get_by(User, name: "new name", id: 1121)
user.as(User) unless user.nil?
#
# delete
# Delete
#
changeset = Crecto::Repo.delete(user)
#
# Associations
#
user = Crecto::Repo.get(User, id).as(User)
posts = Crecto::Repo.all(user, :posts)
#
# Preload associations
#
users = Crecto::Repo.all(User, Crecto::Query.new, preload: [:posts])
users[0].posts # has_many relation is preloaded
posts = Crecto::Repo.all(Post, Crecto::Query.new, preload: [:user])
posts[0].user # belongs_to relation preloaded
```

## Performance
Expand All @@ -131,9 +156,7 @@ changeset = Crecto::Repo.delete(user)
```Crystal
require "crecto"
class User
include Crecto::Schema
extend Crecto::Changeset(User)
class User < Crecto::Model
schema "users" do
field :name, String
Expand Down
42 changes: 42 additions & 0 deletions spec/migrations/pg_users.sql
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ DROP INDEX IF EXISTS users_different_defaults_kljl3kj;
DROP TABLE IF EXISTS users_different_defaults;
DROP INDEX IF EXISTS users_4asdf;
DROP TABLE IF EXISTS users_large_defaults;
DROP INDEX IF EXISTS posts_df8sdd;
DROP TABLE IF EXISTS posts;
DROP INDEX IF EXISTS addresses_dfd7fs7ss;
DROP TABLE IF EXISTS addresses;

CREATE TABLE users(
id INTEGER NOT NULL,
Expand Down Expand Up @@ -67,4 +71,42 @@ ALTER TABLE ONLY users_large_defaults ADD CONSTRAINT users_large_defaults_pkey P
ALTER TABLE ONLY users_large_defaults ALTER COLUMN id SET DEFAULT nextval('users_large_defaults_id_seq'::regclass);
CREATE UNIQUE INDEX users_4asdf ON users_large_defaults (id);

CREATE TABLE posts(
id INTEGER NOT NULL,
user_id INTEGER references users(id),
created_at timestamp without time zone,
updated_at timestamp without time zone
);

CREATE SEQUENCE posts_id_seq
START WITH 1121
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;

ALTER SEQUENCE posts_id_seq OWNED BY posts.id;
ALTER TABLE ONLY posts ADD CONSTRAINT posts_pkey PRIMARY KEY (id);
ALTER TABLE ONLY posts ALTER COLUMN id SET DEFAULT nextval('posts_id_seq'::regclass);
CREATE UNIQUE INDEX posts_df8sdd ON posts (id);

CREATE TABLE addresses(
id INTEGER NOT NULL,
user_id INTEGER references users(id),
created_at timestamp without time zone,
updated_at timestamp without time zone
);

CREATE SEQUENCE addresses_id_seq
START WITH 1121
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;

ALTER SEQUENCE addresses_id_seq OWNED BY addresses.id;
ALTER TABLE ONLY addresses ADD CONSTRAINT addresses_pkey PRIMARY KEY (id);
ALTER TABLE ONLY addresses ALTER COLUMN id SET DEFAULT nextval('addresses_id_seq'::regclass);
CREATE UNIQUE INDEX addresses_dfd7fs7ss ON addresses (id);

COMMIT;
88 changes: 88 additions & 0 deletions spec/repo_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,99 @@ describe Crecto do
end
end

describe "has_many" do
it "should load associations" do
user = User.new
user.name = "fridge"
user = Crecto::Repo.insert(user).instance

post = Post.new
post.user_id = user.id.as(Int32)
Crecto::Repo.insert(post)
post = Crecto::Repo.insert(post).instance

address = Address.new
address.user_id = user.id.as(Int32)
Crecto::Repo.insert(address)

posts = Crecto::Repo.all(user, :posts).as(Array)
posts.size.should eq(2)
posts[0].as(Post).user_id.should eq(user.id)

addresses = Crecto::Repo.all(user, :addresses).as(Array)
addresses.size.should eq(1)
addresses[0].as(Address).user_id.should eq(user.id)
end
end

describe "belongs_to" do
it "should set the belongs_to property" do
user = User.new
user.name = "fridge"
user = Crecto::Repo.insert(user).instance

post = Post.new
post.user_id = user.id.as(Int32)
post = Crecto::Repo.insert(post).instance
post.user = user

post.user.should eq(user)
end
end

describe "preload" do
it "should preload the has_many association" do
user = User.new
user.name = "tester"
user = Crecto::Repo.insert(user).instance

post = Post.new
post.user_id = user.id.as(Int32)
Crecto::Repo.insert(post)
Crecto::Repo.insert(post)

users = Crecto::Repo.all(User, Crecto::Repo::Query.where(id: user.id), preload: [:posts]).as(Array)
users[0].posts.as(Array).size.should eq(2)
end

it "should preload the belongs_to association" do
user = User.new
user.name = "tester"
user = Crecto::Repo.insert(user).instance

post = Post.new
post.user_id = user.id.as(Int32)
post = Crecto::Repo.insert(post).instance

posts = Crecto::Repo.all(Post, Crecto::Repo::Query.where(id: post.id), preload: [:user]).as(Array)
posts[0].user.as(User).id.should eq(user.id)
end

it "should set the foreign key when setting the object" do
user = User.new
user.name = "tester"
user = Crecto::Repo.insert(user).instance

post = Post.new
post.user = user
post.user_id.should eq(user.id)
end
end

describe "#delete_all" do
it "should remove all records" do
Crecto::Repo.delete_all(Post)
Crecto::Repo.delete_all(Address)
Crecto::Repo.delete_all(User)
Crecto::Repo.delete_all(UserDifferentDefaults)
Crecto::Repo.delete_all(UserLargeDefaults)

posts = Crecto::Repo.all(Post).as(Array)
posts.size.should eq 0

addresses = Crecto::Repo.all(Address).as(Array)
addresses.size.should eq 0

users = Crecto::Repo.all(User).as(Array)
users.size.should eq 0

Expand All @@ -281,5 +368,6 @@ describe Crecto do
users.size.should eq 0
end
end

end
end
41 changes: 41 additions & 0 deletions spec/schema_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -116,5 +116,46 @@ describe Crecto do
u.xyz.as(Time).epoch_ms.should be_close(Time.now.epoch_ms, 2000)
end
end

describe "#klass_for_association" do
it "should return the correct class" do
User.klass_for_association(:posts).should eq(Post)
User.klass_for_association(:addresses).should eq(Address)
UserDifferentDefaults.klass_for_association(:things).should eq(Thing)
Address.klass_for_association(:user).should eq(User)
Post.klass_for_association(:user).should eq(User)
end
end

describe "#foreign_key_for_association" do
it "should return the correct foreign key symbol" do
User.foreign_key_for_association(:posts).should eq(:user_id)
User.foreign_key_for_association(:addresses).should eq(:user_id)
Post.foreign_key_for_association(:user).should eq(:user_id)
end
end

describe "#foreign_key_value_for_association" do
it "should return the correct foreign key value for associations" do
user = User.new
user.name = "tester"
user = Crecto::Repo.insert(user).instance

post = Post.new
post.user_id = user.id.as(Int32)
post = Crecto::Repo.insert(post).instance

User.foreign_key_value_for_association(:posts, post).should eq(post.user_id)
Post.foreign_key_value_for_association(:user, post).should eq(user.id)
end
end

describe "#association_type_for_association" do
it "should return the association type symbol" do
User.association_type_for_association(:posts).should eq(:has_many)
User.association_type_for_association(:addresses).should eq(:has_many)
Post.association_type_for_association(:user).should eq(:belongs_to)
end
end
end
end
Loading

0 comments on commit 96b48ca

Please sign in to comment.