Commits

Anonymous committed ba52a7c

LazyRecorderFactory resolves nested LazyRecorderFactory objects found in args

class fixture(Fixture):

butter = Factory(str, 'butter')
fly = butter.replace('b', Factory(str, 'fl'))

The ``Factory(str, 'fl')`` argument will now be resolved correctly during
fixture setup.

This is most useful for the data mapper, where you can now do things like this:

F = StormFactory.configure(...)

class fixture(Fixture):

color = F.mapper.find(Color, name='yellow')
item = F.mapper.find(Widget, color_id=color.id)

  • Participants
  • Parent commits 6a81a69

Comments (0)

Files changed (2)

File test_toffee.py

         mf.setup()
         expect(setup_complete_calls) == [(CustomFactory, mf, [mf.a, mf.b])]
 
+    def test_factories_can_be_used_as_arguments(self):
+
+        class fixture(Fixture):
+
+            butter = Factory(str, 'butter')
+            fly = butter.replace('b', Factory(str, 'fl'))
+
+        f = fixture().setup()
+        assert f.fly == 'flutter', f.fly
+
+    def test_lazy_recorder_factories_can_be_used_as_arguments(self):
+
+        class fixture(Fixture):
+
+            butter = Factory(str, 'butter')
+            fly = Factory(str, butter.replace('b', Factory(str, 'fl').upper()))
+
+        f = fixture()
+        f.setup()
+        assert f.fly == 'FLutter', f.fly
+
 
 class TestSeq(object):
 
 
     def setup(self):
         self.mapper = Mock()
-        self.factory = StormFactory.configure(lambda: self.mapper)
+        self.F = StormFactory.configure(lambda: self.mapper)
 
     def test_calls_are_propagated(self):
-        f = Fixture(x=self.factory.mapper.find('thing', id=42).one())
+        f = Fixture(x=self.F.mapper.find('thing', id=42).one())
         f.setup()
         assert self.mapper.find.call_args == call('thing', id=42)
         assert self.mapper.find().one.call_args == call()
 
     def test_calls_are_independent(self):
-        f = Fixture(x=self.factory.mapper.find('thing 1', id=42).one(),
-                    y=self.factory.mapper.find('thing 2').any())
+        f = Fixture(x=self.F.mapper.find('thing 1', id=42).one(),
+                    y=self.F.mapper.find('thing 2').any())
         f.setup()
 
         assert self.mapper.find.call_args_list == [call('thing 1', id=42),
             pass
 
         self.mapper.pick_a_number.return_value = 42
-        f = Fixture(x=self.factory(Thing,
-                                   value=self.factory.mapper.pick_a_number()))
+        f = Fixture(x=self.F(Thing,
+                                   value=self.F.mapper.pick_a_number()))
         f.setup()
         assert f.x.value == 42
 
 from itertools import count
+from operator import getitem
 
 __version__ = '0.1.4dev'
 
 
     created_order = 0
 
+    def __get__(self, instance, context):
+        """
+        Factories have magic behaviour in the context of fixtures, so that
+        accessing 'my_fixture.foo' auto-delegates to 'my_fixture.o.foo'
+        """
+        if isinstance(instance, Fixture):
+            try:
+                return instance.o[instance.factory_names[self]]
+            except KeyError:
+                raise AttributeError()
+        return self
+
     def create_object(self, context):
         raise NotImplementedError()
 
                               *(args or self.args),
                               **dict(self.kwargs, **kwargs))
 
-    def __get__(self, instance, context):
-        """
-        Factories have magic behaviour in the context of fixtures, so that
-        accessing 'my_fixture.foo' auto-delegates to 'my_fixture.o.foo'
-        """
-        if isinstance(instance, Fixture):
-            try:
-                return instance.o[instance.factory_names[self]]
-            except KeyError:
-                raise AttributeError()
-        return self
-
     def create_object(self, context):
         args = tuple(context.resolve(a) for a in self.args)
         kwargs = dict((k, context.resolve(v)) for k, v in self.kwargs.items())
     Records all calls and attribute access for later replay, eg:
 
         >>> p = LazyRecorder().upper().replace('c', 'm')[0]
-        >>> p.replay('cat')
+        >>> p.replay_with('cat')
         'M'
     """
 
         self._recorded_events = []
 
     def __call__(self, *args, **kwargs):
-        self._recorded_events.append(lambda ob: ob(*args, **kwargs))
+        def caller(ob, *args, **kwargs):
+            return ob(*args, **kwargs)
+        self._recorded_events.append((caller, args, kwargs))
         return self
 
     def __getattr__(self, attr):
-        self._recorded_events.append(lambda ob: getattr(ob, attr))
+        self._recorded_events.append((getattr, (attr,), {}))
         return self
 
     def __getitem__(self, item):
-        self._recorded_events.append(lambda ob: ob[item])
+        self._recorded_events.append((getitem, (item,), {}))
         return self
 
     def replay_with(self, ob):
-        for event in self._recorded_events:
-            ob = event(ob)
+        for func, args, kwargs in self._recorded_events:
+            ob = func(ob, *args, **kwargs)
         return ob
 
 
     def destroy_object(self, context, ob):
         return self.factory.destroy_object(ob)
 
+    def replay_with_resolved(self, fixture, ob):
+        """
+        Like :meth:`LazyRecorder.replay_with`, but resolves any intermediate
+        LazyRecorderFactory objects encountered.
+        """
+
+        def resolve_ob(n):
+            if isinstance(n, LazyRecorderFactory):
+                ob = n.factory.create_object(fixture)
+                ob = n.replay_with_resolved(fixture, ob)
+                return ob
+            elif isinstance(n, Factory):
+                return n.factory.create_object(fixture)
+            else:
+                return n
+
+        for func, args, kwargs in self._recorded_events:
+            args = tuple(resolve_ob(n) for n in args)
+            kwargs = dict((k, resolve_ob(n)) for k, n in kwargs.items())
+            ob = func(ob, *args, **kwargs)
+
+        return ob
+
 
 class DjangoFactory(Factory):
 
                            for k in dir(self.__class__))
         class_factories = ((k, v)
                            for k, v in class_factories
-                           if isinstance(v, Factory))
+                           if isinstance(v, BaseFactory))
 
         self.update_factories(dict(class_factories, **kwargs))
 
         self.created.append((name, ob, factory))
 
         if isinstance(factory, LazyRecorderFactory):
-            ob = factory.replay_with(ob)
+            ob = factory.replay_with_resolved(self, ob)
 
         if name is not None:
             self.o[name] = ob
             else:
                 ob = self._create_object_from_factory(what.factory)
 
-            return what.replay_with(ob)
+            return what.replay_with_resolved(self, ob)
 
         return what