Syntax sugar

20 views
Skip to first unread message

Maksim

unread,
Apr 14, 2011, 6:39:03 PM4/14/11
to active_factory
Hi,

I was thinking that I'd prefer

factory :my, :class => User do
after_build do
object.save!
emulate_sign_in object
end
end

instance eval for callbacks like `after_build` to have

factory :my, :class => User do
after_build do
save!
emulate_sign_in self
end
end

How about that?

Alexey Tarasevich

unread,
Apr 15, 2011, 4:24:27 AM4/15/11
to active_factory
Hi Max,

Heh, thanks for catching my error in the example in README. It won't
work (context is missing). The correct code is:

factory :my, :class => User do
after_build do
object.save!
context.emulate_sign_in object
end
end

Actually it's instance_eval anyway. But for object with 3 methods:

class CreationContext < Struct.new :index, :context, :object
alias i index
end

object - the object under construction
index - index of the object in the factory
context - spec context where the models {} block was evaluated

So considering your proposal how would you like to have access to
index and context?

Maybe it's also worth to have after_create (like factory_girl) instead
of after_build. The difference is that after_build is called before
linking objects and saving. Unfortunately i use it only in one place
and don't know other needs.

Kind Regards,
Alexey

Maksim

unread,
Apr 15, 2011, 1:30:43 PM4/15/11
to active_...@googlegroups.com
Ok, I got it

From this prospective leaving current approach looks fine. Maybe just
add couple aliases to object and context methods (like model and spec
which, IMO, sounds more meaningful):

model.save!
spec.emulate_sign_in model

However these aliases could be easily added by developers if needed so
it's not critical. According to callbacks - I think it makes sense to
have both to make library more flexible. I believe that there should be
cases when you need callback before or after linking:

factory :order do

after_build do
object.calculate_fee
object.save!
end

after_create do
object.calculate_nested_orders_fee
object.save!
end
end


(and again I'd prefer more meaningful names, like after_build and
after_linking :) ).


Anyway, nice job, man!


Regards,
Maksim

Alexey Tarasevich

unread,
Apr 16, 2011, 2:58:16 AM4/16/11
to active_factory
Thanks :)

Considering accessor names i agree with you:
model would be more consistent then object (we have models section).
On the other side, it can be either spec or test (though only rspec is
supported right now).
Also spec could mean name of the spec, so for me it looks quite
ambiguous.
For now i think that context is quite acceptable compromize between
spec and test.

DECISION: rename object -> model, keep context, let developers monkey
patch better aliases if it makes them happier.

Alexey Tarasevich

unread,
Apr 16, 2011, 5:12:43 AM4/16/11
to active_factory
PROBLEM: will it be enough to have just before_save?

Other possible callbacks are after_build and after_create, but I
suspect they are not necessary.
I think it's good idea to avoid adding unnecessary features.

General possible workflow is following:

* new instance
* assign attributes
# after_build callback
* associate
# before_save callback - called when all associated but before any is
saved, order of instances or factories is not guaranteed
* save
# after_create callback - called when all instances are saved. order
is not guaranteed

DATA:
I looked through some callback use cases for factory girl:

http://groups.google.com/group/factory_girl/browse_thread/thread/6c37c59fa4d7ffc4
This case demonstrates need for before_save:
class Oui
belongs_to :phone
validates_something_of :value, :and => :phone
end
factory :oui do
before_save { model.value = f(model.phone) }
end
models { oui - phone }

http://stackoverflow.com/questions/1506556/has-many-while-respecting-build-strategy-in-factory-girl
This case demonstrates need for either after_build, or before_save,
additionally before_save should be called before any model is saved:
class Item
belongs_to :user
validates_presense_of :user_id
end
factory :user do
before_save { model.save! }
end
models { item - user }

http://groups.google.com/group/factory_girl/browse_thread/thread/f649d226bc4c0735/5ac1631fcd904081?lnk=gst&q=callback#5ac1631fcd904081
For this case any callback will work:
factory :verified_user, :class => User do
before_save do
u.register
u.verify
end
end

Max, I think for your case either before_save, or after_save would
work equally well:
factory :order do
before_save do
model.calculate_total_fee
end
end

class Order
def calculate_total_fee
unless @calculate_tatal_fee
@calculate_tatal_fee = true

calculate_fee
calculate_nested_orders_fee
end
end
end

Max, what do you think?

On Apr 15, 8:30 pm, Maksim <fantact...@gmail.com> wrote:

Alexey Tarasevich

unread,
Apr 16, 2011, 5:16:29 AM4/16/11
to active_factory
Also I had idea to use model name in callbacks.

factory :my do
before_save do
my.save!
context.sign_in my
end
end

My concern is that it can be confusing and buggy in case of factory
inheritance (:parent option like in FactoryGirl).

Alexey Tarasevich

unread,
Apr 18, 2011, 2:20:17 AM4/18/11
to active_factory
My own project proved that before_save is not enough.

Entry belongs_to User. When entry is saved it validates if user is
set. So I tried following code:

define :my do
...
before_save {
model.save!
context.sign_in model
}
end

models { my - entry }

So
1. objects are associated
2. before_save is called
2.1 user.save! is called
2.1.1 as result entry.save is called
2.1.1.1 validation checks user and it's not assigned yet

CONCLUSION: after_build should be used here instead of after_build.

DECISION: have two callbacks (after_build & before_save) for a
factory.
On Apr 16, 12:12 pm, Alexey Tarasevich <tarasevic...@gmail.com> wrote:
> PROBLEM: will it be enough to have just before_save?
>
> Other possible callbacks are after_build and after_create, but I
> suspect they are not necessary.
> I think it's good idea to avoid adding unnecessary features.
>
> General possible workflow is following:
>
> * new instance
> * assign attributes
> # after_build callback
> * associate
> # before_save callback - called when all associated but before any is
> saved, order of instances or factories is not guaranteed
> * save
> # after_create callback - called when all instances are saved. order
> is not guaranteed
>
> DATA:
> I looked through some callback use cases for factory girl:
>
> http://groups.google.com/group/factory_girl/browse_thread/thread/6c37...
> This case demonstrates need for before_save:
> class Oui
>   belongs_to :phone
>   validates_something_of :value, :and => :phone
> end
> factory :oui do
>   before_save { model.value = f(model.phone) }
> end
> models { oui - phone }
>
> http://stackoverflow.com/questions/1506556/has-many-while-respecting-...
> This case demonstrates need for either after_build, or before_save,
> additionally before_save should be called before any model is saved:
> class Item
>   belongs_to :user
>   validates_presense_of :user_id
> end
> factory :user do
>   before_save { model.save! }
> end
> models { item - user }
>
> http://groups.google.com/group/factory_girl/browse_thread/thread/f649...

Maksim

unread,
Apr 18, 2011, 2:28:22 AM4/18/11
to active_factory
Sounds good,

at least as you said before everyone is still free to add callbacks if
really needs it

Alexey Tarasevich

unread,
Apr 18, 2011, 4:18:54 AM4/18/11
to active_factory
correction:
> define :my do
factory :my do
...
Reply all
Reply to author
Forward
0 new messages