@@ -737,8 +737,121 @@ service = container.get_object("MovieLister")
737
737
</objects>
738
738
]]> </programlisting >
739
739
740
- </section >
740
+ <section id =" objects-config-xmlconfig-inheritance" >
741
+ <title >Object definition inheritance</title >
742
+
743
+ <para >
744
+ XMLConfig's definitions may be stacked up into hierarchies of abstract
745
+ parents and their children objects. A child object not
746
+ only inherits all the properties and constructor arguments from
747
+ its parent but it can also easily override any of the inherited
748
+ values. This can save a lot of typing when configuring
749
+ non-trivial application contexts which would otherwise need to
750
+ repeat the same configuration properties over many objects
751
+ definitions.
752
+ </para >
753
+ <para >
754
+ An abstract object is identified by having an
755
+ <emphasis >abstract="True"</emphasis > attribute and the child
756
+ ones are those which have a <emphasis >parent</emphasis > attribute
757
+ set to ID of an object from which the properties or constructor
758
+ arguments should be inherited. Child objects must not specify
759
+ the <emphasis >class</emphasis > attribute, its value is taken
760
+ from their parents.
761
+ </para >
762
+ <para >
763
+ An object may be both a child and an abstract one.
764
+ </para >
765
+
766
+ <para >
767
+ Here's a hypothetical configuration of a set of services exposed
768
+ by a server. Note how you can easily change the CRM environment
769
+ you're invoking by merely changing the concrete service's
770
+ (get_customer_id or get_customer_profile) parent ID.
771
+ </para >
772
+
773
+ <programlisting ><![CDATA[
774
+ <?xml version="1.0" encoding="UTF-8"?>
775
+ <objects xmlns="http://www.springframework.org/springpython/schema/objects/1.1"
776
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
777
+ xsi:schemaLocation="http://www.springframework.org/springpython/schema/objects/1.1
778
+ http://springpython.webfactional.com/schema/context/spring-python-context-1.1.xsd">
779
+
780
+ <object id="service" class="springpythontest.support.testSupportClasses.Service" scope="singleton" abstract="True" lazy-init="True">
781
+ <property name="ip"><value>192.168.1.153</value></property>
782
+ </object>
783
+
784
+ <object id="crm_service_dev" parent="service" abstract="True">
785
+ <property name="port"><value>3392</value></property>
786
+ </object>
787
+
788
+ <object id="crm_service_test" parent="service" abstract="True">
789
+ <property name="port"><value>3393</value></property>
790
+ </object>
791
+
792
+ <object id="get_customer_id" parent="crm_service_dev">
793
+ <property name="path"><value>/soap/invoke/get_customer_id</value></property>
794
+ </object>
795
+
796
+ <object id="get_customer_profile" parent="crm_service_test">
797
+ <property name="path"><value>/soap/invoke/get_customer_profile</value></property>
798
+ </object>
799
+
800
+ </objects>
801
+ ]]> </programlisting >
802
+
803
+ <para >
804
+ Here's how you can override inherited properties; both get_customer_id
805
+ and get_customer_profile object definitions will inherit the
806
+ <emphasis >path</emphasis > property however the actual objects returned by
807
+ the container will use local, overridden, values of the property.
808
+ </para >
809
+
810
+ <programlisting ><![CDATA[
811
+ <?xml version="1.0" encoding="UTF-8"?>
812
+ <objects xmlns="http://www.springframework.org/springpython/schema/objects/1.1"
813
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
814
+ xsi:schemaLocation="http://www.springframework.org/springpython/schema/objects/1.1
815
+ http://springpython.webfactional.com/schema/context/spring-python-context-1.1.xsd">
816
+
817
+ <object id="service" class="springpythontest.support.testSupportClasses.Service" scope="singleton" abstract="True" lazy-init="True">
818
+ <property name="ip"><value>192.168.1.153</value></property>
819
+ <property name="port"><value>3392</value></property>
820
+ <property name="path"><value>/DOES-NOT-EXIST</value></property>
821
+ </object>
822
+
823
+ <object id="get_customer_id" parent="service">
824
+ <property name="path"><value>/soap/invoke/get_customer_id</value></property>
825
+ </object>
826
+
827
+ <object id="get_customer_profile" parent="service">
828
+ <property name="path"><value>/soap/invoke/get_customer_profile</value></property>
829
+ </object>
830
+
831
+ </objects>
832
+ ]]> </programlisting >
833
+
834
+ <para >
835
+ If you need to get an abstract object from a container, use the .get_object's <emphasis >ignore_abstract</emphasis > parameter,
836
+ otherwise <emphasis >springpython.container.AbstractObjectException</emphasis > will be raised. Observe the difference:
837
+ </para >
838
+
839
+ <programlisting ><![CDATA[
840
+ # .. skip creating the context
841
+
842
+ # No exception will be raised, even though 'service' is an abstract object
843
+ service = ctx.get_object("service", ignore_abstract=True)
844
+
845
+ # Will show the object
846
+ print service
741
847
848
+ # Will raise AbstractObjectException
849
+ service = ctx.get_object("service")
850
+ ]]> </programlisting >
851
+ </section >
852
+
853
+ </section >
854
+
742
855
<section id =" objects-config-yamlconfig" >
743
856
<title ><classname >YamlConfig</classname > - Spring Python's YAML format</title >
744
857
@@ -1199,6 +1312,129 @@ constructor-args:
1199
1312
1200
1313
</section >
1201
1314
1315
+ <section id =" objects-config-yamlconfig-inheritance" >
1316
+ <title >Object definition inheritance</title >
1317
+
1318
+ <para >
1319
+ Just like XMLConfig, YamlConfig allows for wiring the objects
1320
+ definitions into hierarchies of abstract and children objects,
1321
+ thus this section is in most parts a repetition of what's
1322
+ documented
1323
+ <link linkend =" objects-config-xmlconfig-inheritance" >here</link >
1324
+ </para >
1325
+
1326
+ <para >
1327
+ Definitions may be stacked up into hierarchies of abstract
1328
+ parents and their children objects. A child object not
1329
+ only inherits all the properties and constructor arguments from
1330
+ its parent but it can also easily override any of the inherited
1331
+ values. This can save a lot of typing when configuring
1332
+ non-trivial application contexts which would otherwise need to
1333
+ repeat the same configuration properties over many objects
1334
+ definitions.
1335
+ </para >
1336
+ <para >
1337
+ An abstract object is identified by having an
1338
+ <emphasis >abstract</emphasis > attribute equal to True and the child
1339
+ ones are those which have a <emphasis >parent</emphasis > attribute
1340
+ set to ID of an object from which the properties or constructor
1341
+ arguments should be inherited. Child objects must not specify
1342
+ the <emphasis >class</emphasis > attribute, its value is taken
1343
+ from their parents.
1344
+ </para >
1345
+ <para >
1346
+ An object may be both a child and an abstract one.
1347
+ </para >
1348
+
1349
+ <para >
1350
+ Here's a hypothetical configuration of a set of services exposed
1351
+ by a server. Note how you can easily change the CRM environment
1352
+ you're invoking by merely changing the concrete service's
1353
+ (get_customer_id or get_customer_profile) parent ID.
1354
+ </para >
1355
+
1356
+ <programlisting ><![CDATA[
1357
+ objects:
1358
+ - object: service
1359
+ class: springpythontest.support.testSupportClasses.Service
1360
+ abstract: True
1361
+ scope: singleton
1362
+ lazy-init: True
1363
+ properties:
1364
+ ip: 192.168.1.153
1365
+
1366
+ - object: crm_service_dev
1367
+ abstract: True
1368
+ parent: service
1369
+ properties:
1370
+ port: "3392"
1371
+
1372
+ - object: crm_service_test
1373
+ abstract: True
1374
+ parent: service
1375
+ properties:
1376
+ port: "3393"
1377
+
1378
+ - object: get_customer_id
1379
+ parent: crm_service_dev
1380
+ properties:
1381
+ path: /soap/invoke/get_customer_id
1382
+
1383
+ - object: get_customer_profile
1384
+ parent: crm_service_test
1385
+ properties:
1386
+ path: /soap/invoke/get_customer_profile
1387
+ ]]> </programlisting >
1388
+
1389
+ <para >
1390
+ Here's how you can override inherited properties; both get_customer_id
1391
+ and get_customer_profile object definitions will inherit the
1392
+ <emphasis >path</emphasis > property however the actual objects returned by
1393
+ the container will use local, overridden, values of the property.
1394
+ </para >
1395
+
1396
+ <programlisting ><![CDATA[
1397
+ objects:
1398
+ - object: service
1399
+ class: foo.Service
1400
+ abstract: True
1401
+ scope: singleton
1402
+ lazy-init: True
1403
+ properties:
1404
+ ip: 192.168.1.153
1405
+ port: "3392"
1406
+ path: /DOES-NOT-EXIST
1407
+
1408
+ - object: get_customer_id
1409
+ parent: service
1410
+ properties:
1411
+ path: /soap/invoke/get_customer_id
1412
+
1413
+ - object: get_customer_profile
1414
+ parent: service
1415
+ properties:
1416
+ path: /soap/invoke/get_customer_profile
1417
+ ]]> </programlisting >
1418
+
1419
+ <para >
1420
+ If you need to get an abstract object from a container, use the .get_object's <emphasis >ignore_abstract</emphasis > parameter,
1421
+ otherwise <emphasis >springpython.container.AbstractObjectException</emphasis > will be raised. Observe the difference:
1422
+ </para >
1423
+
1424
+ <programlisting ><![CDATA[
1425
+ # .. skip creating the context
1426
+
1427
+ # No exception will be raised, even though 'service' is an abstract object
1428
+ service = ctx.get_object("service", ignore_abstract=True)
1429
+
1430
+ # Will show the object
1431
+ print service
1432
+
1433
+ # Will raise AbstractObjectException
1434
+ service = ctx.get_object("service")
1435
+ ]]> </programlisting >
1436
+ </section >
1437
+
1202
1438
</section >
1203
1439
1204
1440
<section id =" objects-config-object" >
@@ -1250,6 +1486,104 @@ from springpython.context import ApplicationContext
1250
1486
1251
1487
container = ApplicationContext(MovieBasedApplicationContext())
1252
1488
service = container.get_object("MovieLister")
1489
+ ]]> </programlisting >
1490
+
1491
+ <para >PythonConfig's support for abstract objects is different to
1492
+ that of
1493
+ <link linkend =" objects-config-xmlconfig-inheritance" >XMLConfig</link >
1494
+ or
1495
+ <link linkend =" objects-config-yamlconfig-inheritance" >YamlConfig</link >.
1496
+ With PythonConfig, the children object do not automatically
1497
+ inherit nor override the parents' properties, they in fact
1498
+ <emphasis >receive</emphasis > the values returned by their
1499
+ parents and it's up to them to decide, using Python code,
1500
+ whether to use or to discard the values received.
1501
+ </para >
1502
+
1503
+ <para >
1504
+ A child object must have as many optional arguments as there
1505
+ are expected to be returned by its parent.
1506
+ </para >
1507
+
1508
+ <para >
1509
+ Observe that in the following example the child definitions must
1510
+ define an optional 'req' argument; in runtime they will be passed
1511
+ its value basing on what their parent object will return.
1512
+ Note also that <emphasis >request</emphasis > is of PROTOTYPE
1513
+ scope, if it were a SINGLETON then both get_customer_id_request
1514
+ and get_customer_profile_request would receive the very same
1515
+ Request instance which, in other circumstances, could be a
1516
+ desirable effect but not in the example.
1517
+ </para >
1518
+
1519
+ <programlisting ><![CDATA[
1520
+
1521
+ # stdlib
1522
+ import uuid4
1523
+
1524
+ # .. skip Spring Python imports
1525
+
1526
+ class Request(object):
1527
+ def __init__(self, nonce=None, user=None, password=None):
1528
+ self.nonce = nonce
1529
+ self.user = user
1530
+ self.password = password
1531
+
1532
+ def __str__(self):
1533
+ return "<id=%s %s %s %s>" % (hex(id(self)), self.nonce, self.user, self.password)
1534
+
1535
+ class TestAbstractContext(PythonConfig):
1536
+
1537
+ @Object(scope.PROTOTYPE, abstract=True)
1538
+ def request(self):
1539
+ return Request()
1540
+
1541
+ @Object(parent="request")
1542
+ def request_dev(self, req=None):
1543
+ req.user = "dev-user"
1544
+ req.password = "dev-password"
1545
+
1546
+ return req
1547
+
1548
+ @Object(parent="request")
1549
+ def request_test(self, req=None):
1550
+ req.user = "test-user"
1551
+ req.password = "test-password"
1552
+
1553
+ return req
1554
+
1555
+ @Object(parent="request_dev")
1556
+ def get_customer_id_request(self, req=None):
1557
+ req.nonce = uuid4().hex
1558
+
1559
+ return req
1560
+
1561
+ @Object(parent="request_test")
1562
+ def get_customer_profile_request(self, req=None):
1563
+ req.nonce = uuid4().hex
1564
+
1565
+ return req
1566
+ ]]> </programlisting >
1567
+
1568
+ <para >
1569
+ Same as with the other configuration modes, if you need to get an
1570
+ abstract object from a container, use the .get_object's
1571
+ <emphasis >ignore_abstract</emphasis > parameter,
1572
+ otherwise <emphasis >springpython.container.AbstractObjectException</emphasis >
1573
+ will be raised:
1574
+ </para >
1575
+
1576
+ <programlisting ><![CDATA[
1577
+ # .. skip creating the context
1578
+
1579
+ # No exception will be raised, even though 'request' is an abstract object
1580
+ request = ctx.get_object("request", ignore_abstract=True)
1581
+
1582
+ # Will show the object
1583
+ print request
1584
+
1585
+ # Will raise AbstractObjectException
1586
+ request = ctx.get_object("request")
1253
1587
]]> </programlisting >
1254
1588
1255
1589
</section >
0 commit comments