diff --git a/spec/avram/factory_spec.cr b/spec/avram/factory_spec.cr index 7972a18e5..f205526a3 100644 --- a/spec/avram/factory_spec.cr +++ b/spec/avram/factory_spec.cr @@ -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 @@ -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 diff --git a/src/avram/factory.cr b/src/avram/factory.cr index fda117b06..bd4b8c7e9 100644 --- a/src/avram/factory.cr +++ b/src/avram/factory.cr @@ -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 }} ->) + 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 %})