[General] About the MVC pattern

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

[General] About the MVC pattern

Juhani Ränkimies
Hi,

I'm trying to grasp the MVC pattern in LK. There seems to be
alternative approaches for constructing the model.@least
subclassing the Model class and using Records.

I'd appreciate if someone could explain the motivations of the
different approaches. I'd be also very interested in your assessment
of the stability of the current implementation, and of course possible
future plans.

After some experiments and reading the code, I've got the impression
that the Records approach is suitable when the model is a simple data
model and you can use the Record's getter/setter magic. And when the
model is more complex and should contain some logic, you should
subclass Model. Please comment weather this is to the point or not.

BR,
Juhani R?nkimies



Reply | Threaded
Open this post in threaded view
|

[General] About the MVC pattern

Krzysztof Palacz
Hi Junani,
        the current codebase is indeed confusing in this area, mostly because  
it contains two different architectures for MVC, the new one and the  
old one, preserved for compatibility (since not all the code has been  
ported to the new architecture).
        In both the new and the old architecture, in principle every object  
can act as a Model. Objects not specifically designed to be models can  
act as models, as long some of their methods are designated as model  
variable getters and setters. Those model variables may or may not  
correspond to actual JavaScript properties, i.e., they may be  
computed. The View.connectModel() method specifies which  concrete  
JavaScript methods of the model object act as getters and setters of  
the View's abstract model (*).
        But since any object can be a model, there's no universal mechanism  
for the model to notify views that a model value has changed. To  
address this need, the old mechanism provides the Model class, which  
keeps track of the views it's connected to (its dependents), and  
notifies them when its variable values change. The new mechanism  
doesn't use Model any more, and instead relies on the Record class,  
which itself doesn't know anything about MVC, but it knows how to  
notify observers when its property values change. Thus Record is a  
convenient Model, but not every Model has to be a Record (Records  
could also be used independently of the MVC pattern). On the other  
hand, Records currently do not support computed variables, and so for  
every getter/setter pair there is a JavaScript property defined in the  
Record instance.

        In terms of specific examples, NetRequest typically uses a model that  
is not a Record, since it only writes to the model and is typically  
not interested in receiving updates when model values change. The  
WeatherWidget, on the other hand, uses a Record for its model, because  
its views are interested in receiving notifications when model  
variables change.
       
        I hope this is useful, if not, please keep asking. As far as the  
stability of the implementation, well, I think there's room to  
simplify and generalize the new MVC architecture, although it's  
already used quite extensively by Fabrik and other parts of the  
system. Ideally the old MVC architecture would be phased out and  
pruned from the codebase, but it hasn't happened yet.

        Krzysztof


(*) I try to use the words 'formal' and 'actual' in this context, in  
analogy to function invocation terminology: every View has its own  
notion of what its model is, and uses formal model variable names,  
much like a function body uses formal argument names. When the Model  
is connected to the View, the model's variables are in a way similar  
to actual arguments of a function invocation. The Model-View hookup  
provides a mapping between View's model variables (formals) and the  
getters and setters of the Model (actuals), much like a function  
invocation creates a mapping between formal and actual arguments.


On Dec 14, 2008,@4:28 AM, Juhani R?nkimies wrote:

> Hi,
>
> I'm trying to grasp the MVC pattern in LK. There seems to be
> alternative approaches for constructing the model.@least
> subclassing the Model class and using Records.
>
> I'd appreciate if someone could explain the motivations of the
> different approaches. I'd be also very interested in your assessment
> of the stability of the current implementation, and of course possible
> future plans.
>
> After some experiments and reading the code, I've got the impression
> that the Records approach is suitable when the model is a simple data
> model and you can use the Record's getter/setter magic. And when the
> model is more complex and should contain some logic, you should
> subclass Model. Please comment weather this is to the point or not.
>
> BR,
> Juhani R?nkimies
>
> _______________________________________________
> General mailing list
> [hidden email]
> http://livelykernel.sunlabs.com/mailman/listinfo/general




Reply | Threaded
Open this post in threaded view
|

[General] About the MVC pattern

Juhani Ränkimies
Thank you for the information, it is useful.

My project has a model that is a graph of objects, where changing one
object may propagate changes to other objects in the graph. Some
attributes are computed. There is a need to connect and disconnect
Views to different objects of the graph, depending on which portions
of the model the user wants to view and manipulate.

Currently I'm subclassing Model. I tried to use Records as the Model,
but quickly ran into problems with subclassing the Record. A Record
object is usually instantiated with Record.newPlainInstance(spec) or
Record.newDOMInstance(spec), which, if I understand correctly,
generate a new class with properties, getters and setters, according
to the information in spec, and then create an instance of the new
class.

Wouldn't it be easy to add something like Record.newPlainClass(spec)?
(at least if the default values for the properties are ignored)
It could then be used like this:

Record.newPlainClass(spec).subclass('MyModelClass', ...);

where MyModelClass could define functionality around the generated
getters and setters.

BR,
Juhani


On Thu, Dec 18, 2008@8:52 AM, Krzysztof Palacz
<[hidden email]> wrote:

> Hi Junani,
>        the current codebase is indeed confusing in this area, mostly because
> it contains two different architectures for MVC, the new one and the old
> one, preserved for compatibility (since not all the code has been ported to
> the new architecture).
>        In both the new and the old architecture, in principle every object
> can act as a Model. Objects not specifically designed to be models can act
> as models, as long some of their methods are designated as model variable
> getters and setters. Those model variables may or may not correspond to
> actual JavaScript properties, i.e., they may be computed. The
> View.connectModel() method specifies which  concrete JavaScript methods of
> the model object act as getters and setters of the View's abstract model
> (*).
>        But since any object can be a model, there's no universal mechanism
> for the model to notify views that a model value has changed. To address
> this need, the old mechanism provides the Model class, which keeps track of
> the views it's connected to (its dependents), and notifies them when its
> variable values change. The new mechanism doesn't use Model any more, and
> instead relies on the Record class, which itself doesn't know anything about
> MVC, but it knows how to notify observers when its property values change.
> Thus Record is a convenient Model, but not every Model has to be a Record
> (Records could also be used independently of the MVC pattern). On the other
> hand, Records currently do not support computed variables, and so for every
> getter/setter pair there is a JavaScript property defined in the Record
> instance.
>
>        In terms of specific examples, NetRequest typically uses a model that
> is not a Record, since it only writes to the model and is typically not
> interested in receiving updates when model values change. The WeatherWidget,
> on the other hand, uses a Record for its model, because its views are
> interested in receiving notifications when model variables change.
>
>        I hope this is useful, if not, please keep asking. As far as the
> stability of the implementation, well, I think there's room to simplify and
> generalize the new MVC architecture, although it's already used quite
> extensively by Fabrik and other parts of the system. Ideally the old MVC
> architecture would be phased out and pruned from the codebase, but it hasn't
> happened yet.
>
>        Krzysztof
>
>
> (*) I try to use the words 'formal' and 'actual' in this context, in analogy
> to function invocation terminology: every View has its own notion of what
> its model is, and uses formal model variable names, much like a function
> body uses formal argument names. When the Model is connected to the View,
> the model's variables are in a way similar to actual arguments of a function
> invocation. The Model-View hookup provides a mapping between View's model
> variables (formals) and the getters and setters of the Model (actuals), much
> like a function invocation creates a mapping between formal and actual
> arguments.
>
>
> On Dec 14, 2008,@4:28 AM, Juhani R?nkimies wrote:
>
>> Hi,
>>
>> I'm trying to grasp the MVC pattern in LK. There seems to be
>> alternative approaches for constructing the model.@least
>> subclassing the Model class and using Records.
>>
>> I'd appreciate if someone could explain the motivations of the
>> different approaches. I'd be also very interested in your assessment
>> of the stability of the current implementation, and of course possible
>> future plans.
>>
>> After some experiments and reading the code, I've got the impression
>> that the Records approach is suitable when the model is a simple data
>> model and you can use the Record's getter/setter magic. And when the
>> model is more complex and should contain some logic, you should
>> subclass Model. Please comment weather this is to the point or not.
>>
>> BR,
>> Juhani R?nkimies
>>
>> _______________________________________________
>> General mailing list
>> [hidden email]
>> http://livelykernel.sunlabs.com/mailman/listinfo/general
>
>



Reply | Threaded
Open this post in threaded view
|

[General] About the MVC pattern

Juhani Ränkimies
On Thu, Dec 18, 2008@1:59 PM, Juhani R?nkimies <[hidden email]> wrote:

> .....
> Wouldn't it be easy to add something like Record.newPlainClass(spec)?
> (at least if the default values for the properties are ignored)
> It could then be used like this:
>
> Record.newPlainClass(spec).subclass('MyModelClass', ...);
>
> where MyModelClass could define functionality around the generated
> getters and setters.
>

I experimented by modifying Base.js:
- added Record.newPlainClass, Record.newNodeClass, Record.newClass
- modified Record.newPlainInstance, Record.newNodeInstance and
Record.newInstance

Quick test with Record.newPlainClass and Record.newPlainInstance worked.

The modification is on SVN revision 2415 and it potentially breaks
something because I changed the signature of Record.newInstance.

-juhani

-----------------------------------------

Object.extend(Record, {
       
    newPlainClass: function(spec) {
        return this.newClass(spec, {});
    },

    newNodeClass: function(spec) { // backed by a DOM node
        return this.newClass(spec, NodeFactory.create("record"));
    },

    newClass: function(spec, optStore) {
        if (arguments.length < 1) throw new Error("call with one or more arguments");
        var storeClass;
        var argSpec = {};
        var fieldSpec = {};
        Properties.forEachOwn(spec, function (key, value) {
            fieldSpec[key] = {};
            argSpec[key] = value;
        });
        if (!optStore) {
            storeClass = lively.data.DOMNodeRecord; // FXIME forward reference
            optStore = NodeFactory.create("record"); // FIXME flat JavaScript
instead by default?
        } else {
            storeClass = optStore instanceof Global.Node ?
lively.data.DOMNodeRecord : PlainRecord;
        }

        var Rec = storeClass.prototype.create(fieldSpec);
        Rec.addMethods({initialize: function() {
                    this.rawNode = optStore; // DOM or plain JS Object
                    Properties.forEachOwn(argSpec, function(key, value) {
                            this["set" + key].call(this, value);
                        }, this);
                }});
        return Rec;
    },

    newPlainInstance: function(spec) {
        return this.newInstance(spec, {});
    },

    newNodeInstance: function(spec) { // backed by a DOM node
        return this.newInstance(spec, NodeFactory.create("record"));
    },

    newInstance: function(spec, optStore) {
        var Rec = this.newClass(spec, optStore);
        return new Rec();
    },



Reply | Threaded
Open this post in threaded view
|

[General] About the MVC pattern

Juhani Ränkimies
On Fri, Dec 19, 2008@12:33 AM, Juhani R?nkimies <[hidden email]> wrote:
>
> I experimented by modifying Base.js:
> - added Record.newPlainClass, Record.newNodeClass, Record.newClass
> - modified Record.newPlainInstance, Record.newNodeInstance and
> Record.newInstance
>
> Quick test with Record.newPlainClass and Record.newPlainInstance worked.

Oops, cannot share rawNode with record instances of the same class.

-juhani


------------------------------
Object.extend(Record, {
       
    newPlainClass: function(spec) {
            return this.newClass(spec, function() {return {};});
    },

    newNodeClass: function(spec) { // backed by a DOM node
            return this.newClass(spec, function() {return
NodeFactory.create("record");});
    },

    newClass: function(spec, optStore) {
        if (arguments.length < 1) throw new Error("call with one or more arguments");
        var storeClass;
        var argSpec = {};
        var fieldSpec = {};
        Properties.forEachOwn(spec, function (key, value) {
            fieldSpec[key] = {};
            argSpec[key] = value;
        });
        if (!optStore) {
            storeClass = lively.data.DOMNodeRecord; // FXIME forward reference
            optStore = function() {return NodeFactory.create("record");}; //
FIXME flat JavaScript instead by default?
        } else {
            var tmpStoreInst = optStore(); // This is probably VERY
BAD for DOM records
            storeClass = tmpStoreInst instanceof Global.Node ?
lively.data.DOMNodeRecord : PlainRecord;
        }

        var Rec = storeClass.prototype.create(fieldSpec);
        Rec.addMethods({initialize: function() {
                    this.rawNode = optStore(); // DOM or plain JS Object
                    Properties.forEachOwn(argSpec, function(key, value) {
                            this["set" + key].call(this, value);
                        }, this);
                }});
        return Rec;
    },

    newPlainInstance: function(spec) {
        return this.newInstance(spec, function() {return {};});
    },

    newNodeInstance: function(spec) { // backed by a DOM node
            return this.newInstance(spec, function() {return
NodeFactory.create("record");});
    },

    newInstance: function(spec, optStore) {
        var Rec = this.newClass(spec, optStore);
        return new Rec();
    },