From 5e4f7bb1629baffc6e7ab1a9f0c61d59c49b6682 Mon Sep 17 00:00:00 2001 From: Dave Lasley Date: Fri, 18 Nov 2016 13:30:42 -0800 Subject: [PATCH 1/7] [ADD] api_foreach: Provide api.one replacement * Create api method to match operation of api.one, call it foreach --- api_foreach/README.rst | 52 +++++++++++++++++++++++++++++++++++++ api_foreach/__init__.py | 3 +++ api_foreach/__manifest__.py | 17 ++++++++++++ api_foreach/api.py | 30 +++++++++++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 api_foreach/README.rst create mode 100644 api_foreach/__init__.py create mode 100644 api_foreach/__manifest__.py create mode 100644 api_foreach/api.py diff --git a/api_foreach/README.rst b/api_foreach/README.rst new file mode 100644 index 00000000000..4e16cee4e0b --- /dev/null +++ b/api_foreach/README.rst @@ -0,0 +1,52 @@ +.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 + +=========== +API Foreach +=========== + +This module provides an api decorator that iterates a Recordset and returns a +list of the results. This decorator is identical to the operation of the deprecated +``api.one``. + +Known Issues / Roadmap +====================== + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us to smash it by providing detailed and welcomed feedback. + + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Dave Lasley + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/api_foreach/__init__.py b/api_foreach/__init__.py new file mode 100644 index 00000000000..08d9d6b0b87 --- /dev/null +++ b/api_foreach/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). diff --git a/api_foreach/__manifest__.py b/api_foreach/__manifest__.py new file mode 100644 index 00000000000..5c5d38305e1 --- /dev/null +++ b/api_foreach/__manifest__.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +{ + + 'name': 'API Foreach', + "summary": "It provides an API decorator that auto iterates recordsets.", + 'version': '10.0.1.0.0', + 'author': "LasLabs, Odoo Community Association (OCA)", + 'category': 'Base', + 'depends': [ + 'base', + ], + "website": "https://laslabs.com", + "license": "LGPL-3", + 'installable': True, +} diff --git a/api_foreach/api.py b/api_foreach/api.py new file mode 100644 index 00000000000..230eaef5ef4 --- /dev/null +++ b/api_foreach/api.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Copyright 2004-2015 Odoo S.A. +# Copyright 2016 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from odoo.api import aggregate, decorator + + +def foreach(method): + """ Decorate a record-style method where ``self`` is expected to be a + singleton instance. The decorated method automatically loops on + records, and makes a list with the results. In case the method is + decorated with :func:`returns`, it concatenates the resulting + instances. Such as + method:: + @api.foreach + def method(self, args): + return self.name + may be called in both record and traditional styles, like:: + # recs = model.browse(cr, uid, ids, context) + names = recs.method(args) + names = model.method(cr, uid, ids, args, context=context) + """ + def loop(method, self, *args, **kwargs): + result = [method(rec, *args, **kwargs) for rec in self] + return aggregate(method, result, self) + + wrapper = decorator(loop, method) + wrapper._api = 'foreach' + return wrapper From be8a96a91fea7a922fc971b0c237e973adec2bfd Mon Sep 17 00:00:00 2001 From: Dave Lasley Date: Fri, 18 Nov 2016 14:41:32 -0800 Subject: [PATCH 2/7] Attempt api monkey patch and add test --- api_foreach/__init__.py | 8 ++++++++ api_foreach/__manifest__.py | 1 + api_foreach/tests/__init__.py | 5 +++++ api_foreach/tests/test_api_foreach.py | 15 +++++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 api_foreach/tests/__init__.py create mode 100644 api_foreach/tests/test_api_foreach.py diff --git a/api_foreach/__init__.py b/api_foreach/__init__.py index 08d9d6b0b87..bef5eb5f046 100644 --- a/api_foreach/__init__.py +++ b/api_foreach/__init__.py @@ -1,3 +1,11 @@ # -*- coding: utf-8 -*- # Copyright 2016 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from odoo import api + +from .api import foreach + + +def _patch_api(cr, registry): + api.foreach = foreach diff --git a/api_foreach/__manifest__.py b/api_foreach/__manifest__.py index 5c5d38305e1..265fcd3335a 100644 --- a/api_foreach/__manifest__.py +++ b/api_foreach/__manifest__.py @@ -13,5 +13,6 @@ ], "website": "https://laslabs.com", "license": "LGPL-3", + 'post_init_hook': '_patch_api', 'installable': True, } diff --git a/api_foreach/tests/__init__.py b/api_foreach/tests/__init__.py new file mode 100644 index 00000000000..2d139d471df --- /dev/null +++ b/api_foreach/tests/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from . import test_api_foreach diff --git a/api_foreach/tests/test_api_foreach.py b/api_foreach/tests/test_api_foreach.py new file mode 100644 index 00000000000..4440cbef749 --- /dev/null +++ b/api_foreach/tests/test_api_foreach.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from odoo import api +from odoo.tests.common import TransactionCase + + +class TestApiForeach(TransactionCase): + + def test_api_monkey_patch(self): + """ It should monkey-patch api.foreach into odoo.api """ + self.assertTrue( + callable(api.foreach), + ) From bc307574609b3d49b28900698af51187d57088e6 Mon Sep 17 00:00:00 2001 From: Dave Lasley Date: Fri, 18 Nov 2016 15:05:03 -0800 Subject: [PATCH 3/7] Switch to setattr --- api_foreach/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_foreach/__init__.py b/api_foreach/__init__.py index bef5eb5f046..4d676bb4556 100644 --- a/api_foreach/__init__.py +++ b/api_foreach/__init__.py @@ -8,4 +8,4 @@ def _patch_api(cr, registry): - api.foreach = foreach + setattr(api, 'foreach', foreach) From f00256e7169915e723805a9df513e83e0a49f1ec Mon Sep 17 00:00:00 2001 From: Dave Lasley Date: Fri, 18 Nov 2016 15:36:42 -0800 Subject: [PATCH 4/7] Move api monkeypatch to be from odoo namespace --- api_foreach/__init__.py | 6 +++--- api_foreach/__manifest__.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api_foreach/__init__.py b/api_foreach/__init__.py index 4d676bb4556..d3c9643035c 100644 --- a/api_foreach/__init__.py +++ b/api_foreach/__init__.py @@ -2,10 +2,10 @@ # Copyright 2016 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). -from odoo import api +import odoo from .api import foreach -def _patch_api(cr, registry): - setattr(api, 'foreach', foreach) +def _patch_api(*arg, **kwargs): + setattr(odoo.api, 'foreach', foreach) diff --git a/api_foreach/__manifest__.py b/api_foreach/__manifest__.py index 265fcd3335a..80eb3753250 100644 --- a/api_foreach/__manifest__.py +++ b/api_foreach/__manifest__.py @@ -13,6 +13,6 @@ ], "website": "https://laslabs.com", "license": "LGPL-3", - 'post_init_hook': '_patch_api', + 'post_load': '_patch_api', 'installable': True, } From a5323a653180402125e635f5d7b919b7ea7386e9 Mon Sep 17 00:00:00 2001 From: Dave Lasley Date: Fri, 18 Nov 2016 15:41:45 -0800 Subject: [PATCH 5/7] Prefer attribute assignment over setattr --- api_foreach/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_foreach/__init__.py b/api_foreach/__init__.py index d3c9643035c..428ba86190a 100644 --- a/api_foreach/__init__.py +++ b/api_foreach/__init__.py @@ -8,4 +8,4 @@ def _patch_api(*arg, **kwargs): - setattr(odoo.api, 'foreach', foreach) + odoo.api.foreach = foreach From 700a098f8278a22905b571c7d4e0076c4cebe2e7 Mon Sep 17 00:00:00 2001 From: Dave Lasley Date: Fri, 18 Nov 2016 15:50:20 -0800 Subject: [PATCH 6/7] Add test for iteration --- api_foreach/tests/test_api_foreach.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/api_foreach/tests/test_api_foreach.py b/api_foreach/tests/test_api_foreach.py index 4440cbef749..38fe431c8aa 100644 --- a/api_foreach/tests/test_api_foreach.py +++ b/api_foreach/tests/test_api_foreach.py @@ -6,10 +6,36 @@ from odoo.tests.common import TransactionCase +class TestRecordset(object): + """ It provides a mock recordset for testing foreach api """ + + records = ['test1', 'test2'] + + def __iter__(self): + for record in self.records: + yield record + + @api.foreach + def decorated_method(self): + """ It provides a method decorated with foreach """ + return self + + class TestApiForeach(TransactionCase): + def setUp(self): + super(TestApiForeach, self).setUp() + self.count = 0 + def test_api_monkey_patch(self): """ It should monkey-patch api.foreach into odoo.api """ self.assertTrue( callable(api.foreach), ) + + def test_api_iterate(self): + """ It should iterate and return list of method results """ + res = TestRecordset().decorated_method() + self.assertEqual( + res, TestRecordset.records, + ) From 031c87e122a51dcacbeed405fb49645797b64ce3 Mon Sep 17 00:00:00 2001 From: Dave Lasley Date: Fri, 18 Nov 2016 15:54:28 -0800 Subject: [PATCH 7/7] Add foreach to api.__all__ --- api_foreach/__init__.py | 1 + api_foreach/tests/test_api_foreach.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/api_foreach/__init__.py b/api_foreach/__init__.py index 428ba86190a..af97f4d4852 100644 --- a/api_foreach/__init__.py +++ b/api_foreach/__init__.py @@ -9,3 +9,4 @@ def _patch_api(*arg, **kwargs): odoo.api.foreach = foreach + odoo.api.__all__.append('foreach') diff --git a/api_foreach/tests/test_api_foreach.py b/api_foreach/tests/test_api_foreach.py index 38fe431c8aa..4a5c0facb65 100644 --- a/api_foreach/tests/test_api_foreach.py +++ b/api_foreach/tests/test_api_foreach.py @@ -39,3 +39,7 @@ def test_api_iterate(self): self.assertEqual( res, TestRecordset.records, ) + + def test_api_foreach_append_all(self): + """ It should add ``foreach`` to ``__all__`` of ``odoo.api`` """ + self.assertIn('foreach', api.__all__)