Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[WIP] Adding a new belongs_to association [for factories. #950

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions spec/avram/factory_spec.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
require "../spec_helper"

class TempComment < BaseModel
skip_default_columns
table :comments do
primary_key custom_id : Int64
timestamps
column body : String
belongs_to post : Post
end
end

class TempCommentFactory < Avram::Factory
belongs_to post : PostFactory

def initialize
body("Test Comment")
create_post(&.title("Test Post"))
end
end

describe Avram::Factory do
it "can create a model without additional columns" do
PlainModelFactory.create.id.should_not be_nil
Expand Down Expand Up @@ -98,4 +117,21 @@ describe Avram::Factory do
line_item.scans_count.should eq 1
end
end

describe "belongs_to" do
it "creates an associated record" do
factory = TempCommentFactory.new
factory.body("a comment")
comment = factory.create
comment.body.should eq("a comment")
comment.post.title.should eq("Test Post")
end
it "allows overriding the association" do
post = PostFactory.create &.title("some title")
comment = TempCommentFactory.create &.post(post).body("some body")
comment.body.should eq("some body")
comment.post_id.should eq(post.id)
comment.post.title.should eq("some title")
end
end
end
31 changes: 31 additions & 0 deletions src/avram/factory.cr
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,37 @@ abstract class Avram::Factory
{% end %}
end

# Creates a method named from the TypeDeclaration for assigning the foreign key value
# from the object passed in. And a second method for creating the association on saves.
# ```
# class CommentFactory < Avram::Factory
# belongs_to post : PostFactory
#
# def initialize
# create_post
# end
# end
# ```
macro belongs_to(type_declaration)
{% unless type_declaration.is_a?(TypeDeclaration) %}
{% raise "belongs_to expected a type declaration like 'user : UserFactory', instead got: '#{type_declaration}'" %}
{% end %}
def {{ type_declaration.var }}(%m : {{ type_declaration.type.stringify.gsub(/Factory$/, "").id }})
{{ type_declaration.var }}_id(%m.id)
end
def create_{{ type_declaration.var }}(&blk : {{ type_declaration.type }} ->)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this allow calling the method without a block? I'm not sure it does

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooo, good call. I'll add that in.

before_save do
if operation.{{ type_declaration.var }}_id.value.nil?
%m = {{ type_declaration.type.resolve }}.create do |inst|
blk.call(inst)
inst
end
{{ type_declaration.var }}(%m)
end
end
end
end

macro setup_attribute_shortcuts(operation)
{% for attribute in operation.resolve.constant(:COLUMN_ATTRIBUTES) %}
def {{ attribute[:name] }}(value : {{ attribute[:type] }}{% if attribute[:nilable] %}?{% end %})
Expand Down