[Kimchi-devel] [PATCH] Break the 'sample' plugin's monolithic model into several smaller ones
Mark Wu
wudxw at linux.vnet.ibm.com
Thu Jan 23 05:43:59 UTC 2014
On 01/23/2014 12:30 AM, Aline Manera wrote:
>
> The tests are failing.
>
> ======================================================================
> FAIL: test_bad_params (test_plugin.PluginTests)
> ----------------------------------------------------------------------
> Traceback (most recent call last):
> File "test_plugin.py", line 119, in test_bad_params
> self.assertEquals(400, resp.status)
> AssertionError: 400 != 404
>
> ======================================================================
> FAIL: test_rectangles (test_plugin.PluginTests)
> ----------------------------------------------------------------------
> Traceback (most recent call last):
> File "test_plugin.py", line 92, in test_rectangles
> self._create_rectangle_and_assert('small', 10, 8)
> File "test_plugin.py", line 77, in _create_rectangle_and_assert
> self.assertEquals(201, resp.status)
> AssertionError: 201 != 404
>
>
> I will fix it and resend with model refactoring patch set as Zheng
> Sheng is on vacation these days.
It's caused by the failure of importing kimchi.plugins. I have fixed it
in v2 by moving it to kimchi.model
because I think it can be used in your model refactoring code.
>
> On 01/22/2014 01:48 PM, Aline Manera wrote:
>>
>> Reviewed-by: Aline Manera <alinefm at linux.vnet.ibm.com>
>>
>> On 01/21/2014 05:46 AM, Zhou Zheng Sheng wrote:
>>> In kimchi the model is always a monolithic class. This is not good from
>>> the point of modulization, and it is not necessary to put everything
>>> into one big model. Unfortunately the problem will not get solved
>>> unless we conduct enormous refactor against the whole controller-model
>>> architecture.
>>>
>>> This patch tries to mitigate the problem by firstly splitting the model
>>> class into some smaller ones, than automatically mapping all sub-model
>>> instance methods to a RootModel instance. As a demonstration, this
>>> patch
>>> splits the 'sample' plugin's model.
>>>
>>> Signed-off-by: Zhou Zheng Sheng <zhshzhou at linux.vnet.ibm.com>
>>> ---
>>> plugins/__init__.py | 47 +++++++++++++++++++++
>>> plugins/sample/model.py | 109
>>> ++++++++++++++++++++++++++++++------------------
>>> 2 files changed, 115 insertions(+), 41 deletions(-)
>>>
>>> diff --git a/plugins/__init__.py b/plugins/__init__.py
>>> index e69de29..f0b800c 100644
>>> --- a/plugins/__init__.py
>>> +++ b/plugins/__init__.py
>>> @@ -0,0 +1,47 @@
>>> +#
>>> +# Project Kimchi
>>> +#
>>> +# Copyright IBM, Corp. 2014
>>> +#
>>> +# Authors:
>>> +# Zhou Zheng Sheng <zhshzhou at linux.vnet.ibm.com>
>>> +#
>>> +# This library is free software; you can redistribute it and/or
>>> +# modify it under the terms of the GNU Lesser General Public
>>> +# License as published by the Free Software Foundation; either
>>> +# version 2.1 of the License, or (at your option) any later version.
>>> +#
>>> +# This library is distributed in the hope that it will be useful,
>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>>> +# Lesser General Public License for more details.
>>> +#
>>> +# You should have received a copy of the GNU Lesser General Public
>>> +# License along with this library; if not, write to the Free Software
>>> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>> 02110-1301 USA
>>> +
>>> +
>>> +class RootModel(object):
>>> + '''
>>> + This model squashes all sub-model's public callable methods to
>>> itself.
>>> +
>>> + Model methods are not limited to get_list, create, lookup,
>>> update, delete.
>>> + Controller can call generate_action_handler to generate new
>>> actions, which
>>> + call the related model methods. So all public callable methods
>>> of a
>>> + sub-model should be mapped to this model.
>>> + '''
>>> + def __init__(self, model_instances):
>>> + for model_instance in model_instances:
>>> + cls_name = model_instance.__class__.__name__
>>> + if cls_name.endswith('Model'):
>>> + method_prefix = cls_name[:-len('Model')].lower()
>>> + else:
>>> + method_prefix = cls_name.lower()
>>> +
>>> + callables = [m for m in dir(model_instance)
>>> + if not m.startswith('_') and
>>> + callable(getattr(model_instance, m))]
>>> +
>>> + for member_name in callables:
>>> + m = getattr(model_instance, member_name, None)
>>> + setattr(self, '%s_%s' % (method_prefix,
>>> member_name), m)
>>> diff --git a/plugins/sample/model.py b/plugins/sample/model.py
>>> index 9a2f22f..d194e67 100644
>>> --- a/plugins/sample/model.py
>>> +++ b/plugins/sample/model.py
>>> @@ -21,79 +21,106 @@
>>> # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>> 02110-1301 USA
>>>
>>> from kimchi.exception import InvalidOperation, NotFoundError
>>> +from kimchi.plugins import RootModel
>>>
>>>
>>> -class Model(object):
>>> +class CirclesModel(object):
>>> + def __init__(self):
>>> + self._circles = {}
>>> +
>>> + def create(self, params):
>>> + name = params['name']
>>> + if name in self._circles:
>>> + raise InvalidOperation("Circle %s already exists" % name)
>>> + self._circles[name] = Circle(params['radius'])
>>> + return name
>>>
>>> + def get_list(self):
>>> + return sorted(self._circles)
>>> +
>>> +
>>> +class CircleModel(object):
>>> + def __init__(self, parent_model):
>>> + # Circel and Circles models are friends, it's OK to share
>>> _circles.
>>> + self._circles = parent_model._circles
>>> +
>>> + def lookup(self, name):
>>> + try:
>>> + circle = self._circles[name]
>>> + except KeyError:
>>> + raise NotFoundError("Circle %s not found" % name)
>>> + return {'radius': circle.radius}
>>> +
>>> + def update(self, name, params):
>>> + if name not in self._circles:
>>> + raise NotFoundError("Circle %s not found" % name)
>>> + self._circles[name].radius = params['radius']
>>> + return name
>>> +
>>> + def delete(self, name):
>>> + try:
>>> + del self._circles[name]
>>> + except KeyError:
>>> + pass
>>> +
>>> +
>>> +class RectanglesModel(object):
>>> def __init__(self):
>>> - self.rectangles = {}
>>> - self.circles = {}
>>> + self._rectangles = {}
>>>
>>> - def rectangles_create(self, params):
>>> + def create(self, params):
>>> name = params['name']
>>> - if name in self.rectangles:
>>> + if name in self._rectangles:
>>> raise InvalidOperation("Rectangle %s already exists" %
>>> name)
>>> - self.rectangles[name] = Rectangle(params['length'],
>>> params['width'])
>>> + self._rectangles[name] = Rectangle(params['length'],
>>> params['width'])
>>> return name
>>>
>>> - def rectangles_get_list(self):
>>> - return sorted(self.rectangles)
>>> + def get_list(self):
>>> + return sorted(self._rectangles)
>>> +
>>>
>>> - def rectangle_lookup(self, name):
>>> +class RectangleModel(object):
>>> + def __init__(self, parent_model):
>>> + self._rectangles = parent_model._rectangles
>>> +
>>> + def lookup(self, name):
>>> try:
>>> - rectangle = self.rectangles[name]
>>> + rectangle = self._rectangles[name]
>>> except KeyError:
>>> raise NotFoundError("Rectangle %s not found" % name)
>>> return {'length': rectangle.length, 'width': rectangle.width}
>>>
>>> - def rectangle_update(self, name, params):
>>> - if name not in self.rectangles:
>>> + def update(self, name, params):
>>> + if name not in self._rectangles:
>>> raise NotFoundError("Rectangle %s not found" % name)
>>> try:
>>> - self.rectangles[name].length = params['length']
>>> + self._rectangles[name].length = params['length']
>>> except KeyError:
>>> pass
>>>
>>> try:
>>> - self.rectangles[name].width = params['width']
>>> + self._rectangles[name].width = params['width']
>>> except KeyError:
>>> pass
>>> return name
>>>
>>> - def rectangle_delete(self, name):
>>> + def delete(self, name):
>>> try:
>>> - del self.rectangles[name]
>>> + del self._rectangles[name]
>>> except KeyError:
>>> pass
>>>
>>> - def circles_create(self, params):
>>> - name = params['name']
>>> - if name in self.circles:
>>> - raise InvalidOperation("Circle %s already exists" % name)
>>> - self.circles[name] = Circle(params['radius'])
>>> - return name
>>>
>>> - def circles_get_list(self):
>>> - return sorted(self.circles)
>>> -
>>> - def circle_lookup(self, name):
>>> - try:
>>> - circle = self.circles[name]
>>> - except KeyError:
>>> - raise NotFoundError("Circle %s not found" % name)
>>> - return {'radius': circle.radius}
>>> +class Model(RootModel):
>>> + def __init__(self):
>>> + circles = CirclesModel()
>>> + circle = CircleModel(circles)
>>>
>>> - def circle_update(self, name, params):
>>> - if name not in self.circles:
>>> - raise NotFoundError("Circle %s not found" % name)
>>> - self.circles[name].radius = params['radius']
>>> - return name
>>> + rectangles = RectanglesModel()
>>> + rectangle = RectangleModel(rectangles)
>>>
>>> - def circle_delete(self, name):
>>> - try:
>>> - del self.circles[name]
>>> - except KeyError:
>>> - pass
>>> + return super(Model, self).__init__(
>>> + [circle, circles, rectangle, rectangles])
>>>
>>>
>>> class Rectangle(object):
>>
>> _______________________________________________
>> Kimchi-devel mailing list
>> Kimchi-devel at ovirt.org
>> http://lists.ovirt.org/mailman/listinfo/kimchi-devel
>>
>
> _______________________________________________
> Kimchi-devel mailing list
> Kimchi-devel at ovirt.org
> http://lists.ovirt.org/mailman/listinfo/kimchi-devel
>
More information about the Kimchi-devel
mailing list