diff --git a/doc/03_module_dev_01.rst b/doc/03_module_dev_01.rst index 0fbca9d14b4..adc32c4a4e9 100644 --- a/doc/03_module_dev_01.rst +++ b/doc/03_module_dev_01.rst @@ -172,40 +172,73 @@ is as follows: -Record Tag -////////// +```` +//////////// -**Description** +Defines a new record in a specified OpenERP model. -The addition of new data is made with the record tag. This one takes a -mandatory attribute : model. Model is the object name where the insertion has -to be done. The tag record can also take an optional attribute: id. If this -attribute is given, a variable of this name can be used later on, in the same -file, to make reference to the newly created resource ID. +``@model`` (required) -A record tag may contain field tags. They indicate the record's fields value. -If a field is not specified the default value will be used. + Name of the model in which this record will be created/inserted. -The Record Field tag -//////////////////// +``@id`` (optional) -The attributes for the field tag are the following: + :term:`external ID` for the record, also allows referring to this record in + the rest of this file or in other files (through ``field/@ref`` or the + :py:func:`ref() ` function) -name : mandatory - the field name +A record tag generally contains multiple ``field`` tags specifying the values +set on the record's fields when creating it. Fields left out will be set to +their default value unless required. -eval : optional - python expression that indicating the value to add - -ref - reference to an id defined in this file +```` +/////////// -model - model to be looked up in the search +In its most basic use, the ``field`` tag will set its body (as a string) as +the value of the corresponding ``record``'s ``@name`` field. -search - a query +Extra attributes can either preprocess the body or replace its use entirely: +``@name`` (mandatory) + + Name of the field in the containing ``record``'s model + +``@type`` (optional) + + One of ``char``, ``int``, ``float``, ``list``, ``tuple``, ``xml`` or + ``html`` or ``file``. Converts the ``field``'s body to the specified type + (or validates the body's content) + + * ``xml`` will join multiple XML nodes under a single ```` root + * in ``xml`` and ``html``, external ids can be referenced using + ``%(id_name)s`` + * ``list`` and ``tuple``'s element are specified using ```` + sub-nodes with the same attributes as ``field``. + * ``file`` expects a module-local path and will save the path prefixed with + the current module's name, separated by a ``,`` (comma). For use with + :py:func:`~openerp.modules.module.get_module_resource`. + +``@model`` + + Model used for ``@search``'s search, or registry object put in context for + ``@eval``. Required if ``@search`` but optional if ``@eval``. + +``@eval`` (optional) + + A Python expression evaluated to obtain the value to set on the record + +``@ref`` (optional) + + Links to an other record through its :term:`external id`. The module prefix + may be ommitted to link to a record defined in the same module. + +``@search`` (optional) + + Search domain (evaluated Python expression) into ``@model`` to get the + records to set on the field. + + Sets all the matches found for m2m fields, the first id for other field + types. **Example** diff --git a/openerp/tests/__init__.py b/openerp/tests/__init__.py index 3d129e06924..5f262374694 100644 --- a/openerp/tests/__init__.py +++ b/openerp/tests/__init__.py @@ -21,6 +21,7 @@ import test_osv import test_translate import test_uninstall import test_view_validation +import test_convert # This need a change in `oe run-tests` to only run fast_suite + checks by default. # import test_xmlrpc @@ -41,6 +42,7 @@ checks = [ test_misc, test_osv, test_translate, + test_convert, ] # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/tests/addons/test_convert/__init__.py b/openerp/tests/addons/test_convert/__init__.py new file mode 100644 index 00000000000..40a96afc6ff --- /dev/null +++ b/openerp/tests/addons/test_convert/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/openerp/tests/addons/test_convert/__openerp__.py b/openerp/tests/addons/test_convert/__openerp__.py new file mode 100644 index 00000000000..40d9eb3a87f --- /dev/null +++ b/openerp/tests/addons/test_convert/__openerp__.py @@ -0,0 +1,5 @@ +{ + 'name': 'test_convert', + 'description': "Data for xml conversion tests", + 'version': '0.0.1', +} diff --git a/openerp/tests/addons/test_convert/test_file.txt b/openerp/tests/addons/test_convert/test_file.txt new file mode 100644 index 00000000000..e69b2e08d55 --- /dev/null +++ b/openerp/tests/addons/test_convert/test_file.txt @@ -0,0 +1 @@ +nothing to see here, move along diff --git a/openerp/tests/test_convert.py b/openerp/tests/test_convert.py new file mode 100644 index 00000000000..87c3000fba9 --- /dev/null +++ b/openerp/tests/test_convert.py @@ -0,0 +1,83 @@ +import collections +import unittest2 +from lxml import etree as ET +from lxml.builder import E + +from . import common + +from openerp.tools.convert import _eval_xml + +Field = E.field +Value = E.value +class TestEvalXML(common.TransactionCase): + def eval_xml(self, node, obj=None, idref=None): + return _eval_xml(obj, node, pool=None, cr=self.cr, uid=self.uid, + idref=idref, context=None) + + def test_char(self): + self.assertEqual( + self.eval_xml(Field("foo")), + "foo") + self.assertEqual( + self.eval_xml(Field("None")), + "None") + + def test_int(self): + self.assertIsNone( + self.eval_xml(Field("None", type='int')), + "what the fuck?") + self.assertEqual( + self.eval_xml(Field(" 42 ", type="int")), + 42) + + with self.assertRaises(ValueError): + self.eval_xml(Field("4.82", type="int")) + + with self.assertRaises(ValueError): + self.eval_xml(Field("Whelp", type="int")) + + def test_float(self): + self.assertEqual( + self.eval_xml(Field("4.78", type="float")), + 4.78) + + with self.assertRaises(ValueError): + self.eval_xml(Field("None", type="float")) + + with self.assertRaises(ValueError): + self.eval_xml(Field("Foo", type="float")) + + def test_list(self): + self.assertEqual( + self.eval_xml(Field(type="list")), + []) + + self.assertEqual( + self.eval_xml(Field( + Value("foo"), + Value("5", type="int"), + Value("4.76", type="float"), + Value("None", type="int"), + type="list" + )), + ["foo", 5, 4.76, None]) + + def test_file(self): + Obj = collections.namedtuple('Obj', 'module') + obj = Obj('test_convert') + self.assertEqual( + self.eval_xml(Field('test_file.txt', type='file'), obj), + 'test_convert,test_file.txt') + + with self.assertRaises(IOError): + self.eval_xml(Field('test_nofile.txt', type='file'), obj) + + @unittest2.skip("not tested") + def test_xml(self): + pass + + @unittest2.skip("not tested") + def test_html(self): + pass + + diff --git a/openerp/tools/convert.py b/openerp/tools/convert.py index cdcaff35b9e..8541550a6d5 100644 --- a/openerp/tools/convert.py +++ b/openerp/tools/convert.py @@ -161,6 +161,12 @@ def _eval_xml(self, node, pool, cr, uid, idref, context=None): if t == 'html': return _process("".join([etree.tostring(n, encoding='utf-8') for n in node]), idref) + if t == 'file': + from ..modules import module + path = node.text.strip() + if not module.get_module_resource(self.module, path): + raise IOError("No such file or directory: '%s'" % path) + return '%s,%s' % (self.module, path) if t in ('char', 'int', 'float'): d = node.text if t == 'int':