1) different apps can share one database db=SQLDB(....)
2) different apps can now share sessions using session.connect
(request,response,db=...,masterapp='appname')
3) we still need a convention for making code reusable (only a
convention since modules already provide the technology to do it).
About 3). The convention needs to encapsulate in a module a
functional unit that contains a db connection, a representation of
its own models, some business logic and perhaps expose its own
helpers. Let's call this object Enterprise Python Bean (EPB). Here
would be an example of a Bean defined in applications/epb/modules/
ShoutingBean.py
from gluon.sql import *
from gluon.sqlhtml import *
from gluon.validators import *
class ShoutingBean:
"""
This is s web2py Enterprise Python Bean example!
"""
def __init__(self):
self.db=SQLDB("sqlite://bean_shootout.db")
self.db.define_table('shout',SQLField('message'))
self.db.shout.message.requires=IS_NOT_IN_DB
(self.db,'shout.message')
def accepts(self,request_vars):
self.form=SQLFORM(self.db.shout)
return self.form.accepts(request_vars)
def get_form(self):
return self.form
def get_rows(self):
return self.db().select(self.db.shout.ALL)
def act_on(self,request,response):
return dict(rows=self.get_rows()) if self.accepts
(request.vars) else dict(form=self.get_form())
Now you can write a single controller (no model) that uses it, for
example in applications/epb/controllers/default.py
from applications.epb.modules.ShoutingBean import ShoutingBean
def index(): return ShoutingBean().act_on(request,response)
You can try it. It works! It creates the db, creates the tables,
makes an input form with validation, upon submissions - if validation
passes - it lists all messages.
The Bean is easy to reuse.
I also have a version of web2py that allows serialization of the
SQLDB objects (including its tables) and result of a select(). This
means that beans could also be serialized (as in Enterprise Java
Beans), stored in sessions, stored in database, transmitted via http/
xmlrpc, etc. (now you know what the previous post was about!).
I think of a EPB as a black-box mini sub-app that can be shared by
multiple apps and carries its own state and its own documentation
(__doc__).
Because of the EJB analogies this has great marketing potential.
The question is how strict should the specifications be? Any EJB
expert here? I am not.
Can you think of applications? Possibly weird but useful applications.
Is this the right direction? Certainly not for everybody but would
this be a good idea for those of you working on large applications?
Would you like to help in drafting specs?
Please share your thoughts.
Massimo
Hi Limodou,about sharing views: You currently can. Say that in a controller you want to use a view called "default/index.html" from another app "other" you just type:Â Â Â response.view='../../other/views/default/index.html'
I did not know about func_globals. I am sure there are ways it can be used to improve web2py. I do not like the idea of adding a decorator to every function. I like the second suggestion better but I need to think about it. Perhaps there is a way to do something like it that does not break backward compatibility. Let's keep thinking.About your framework. I see you went the route of using a number of existing components. It is interesting and you chose excellent components. When I made web2py my goal was to build everything from scratch since I needed it for teaching the inner workings and therefore needed to understand every little detail.I do know the answers to your final questions. We can already share modules. I think we need to write specs for applications that cooperate. I have been experimenting with this a lot these days. I think one way to achieve it is by making apps more modular and having strict specs on how these modules should look like.
On Mon, May 19, 2008 at 10:11 AM, Massimo Di Pierro <mdip...@cs.depaul.edu> wrote:Hi Limodou,about sharing views: You currently can. Say that in a controller you want to use a view called "default/index.html" from another app "other" you just type:Â Â Â response.view='../../other/views/default/index.html'
Good for this. But if we can make it easier. We can write view='<app>/<controller>/<function>.html', that's easier I think.
>
> I like the EPB idea, this seems to be one step in the direction of
> code re-usability. I really dont like the idea of decorators because I
> saw how they were used in Django and Pylons, some decorators were sort
> of "magical" oudated or one had to dig up in several modules to see
> exactly what is is doing. Are you suggestion this a s a replacement
> for the workaround which we are doing now be defining and "init"
> appliance to hold global data like databases for data interchange? If
> this would mean that I would have to code my Auth module as a bean to
> be used in an application that consists of several appliances I think
> ist a very good idea . I have a few questions, I have no idea how they
> apply since I am just thinking out loud, they might not make sense :-)
>
> 1. Would one be able to load beans without restarting the
> application(web2py)?
yes
> 2. Would it be feasible to strive for commercial EP beans just as in
> byte compiled appliances?
yes
> 3. How would changes to a certain bean affect the appliance as a
> whole?
It depends. This is why we need specifications. It should not affect
the appliance.
> 4. Where and how would tickets be generated?
Nothing changes.
> That all I can ramble about for now. I know I might sound like a
> broken record and Massimo would roll his eyes to the ceiling:-)), but
> I am still very concerned with scaling issues. Using an exotic
> filesystem that might not be portable is not an option. How would
> these beans react when trying to scale using different database
> connections? I really think that there should be an option to store
> all the stuff(tickets too) that gets written to the filesystem in a
> database
right now the only thing you cannot store in the database are
tickets. The reason is that (when in production, in my experience) it
is often the database to case the tickets. The Beans do not have any
special behavior here, you can store them in the DB right now.
> I hope I did not ramble and talk too much nonesense :-)
No. makes sense.
The main weakness of other frameworks is that they are constantly
under development.
Improving and bug fixing is good but constantly changing the API is
very bad because forces to rewrite code, documentation, and
ultimately limits adoption. It is also a symptom that the software
had a bottom-up design vs a top-down design. In other words the
software lacks design.
Web2py has a top-down design. First I designed how I expected users
to user and which API i wanted to expose and how. I wanted it to work
as much Rails but in Python, without configuration files, with the
additional features that Django provides and Rail doesn't. I also
wanted to develop everything from scratch since I realized it was
important to understand every detail of how it works.
In the design I adopted the following principles:
- make is as easy as possible to newbies, even if occasionally at
the expense of making it "different" to experienced python
programmers. Example: no import, no decorators, controller functions
are not wrapped in classes, etc.
- make it as fast as possible, even at the expenses of some oddities
in code (for example the templates are translated into python code
using regex instead of a parser).
- make it as lean as possible. Have you heard the "Pareto Rule" in
business? http://en.wikipedia.org/wiki/Pareto_principle
- try to force developers (as much as possible) to follow good SE
principles.
I realize some of it features may not be the best option for
everybody but I am going be inflexible about not breaking backward
compatibility.
There is no way I can please everybody and it took a long way yo get
where we are in terms of users.
There are two issues that have been brought up and they are different
issues:
1) have one app been able to access components of another app
2) building projects made of multiple apps.
Let me discuss every one of them in detail:
1)
Currently apps can share sessions and databases.
One app can also use the views from another app (you may not like the
syntax but it works). Anyway I do not think this is a good idea.
I have no objection to create an API to share models (which you can
already do using execfile) but bare in mind that this will affect the
portability of applications.
The only real issue is controllers. Controllers are not modules for a
reason: like in Rails they can contain authentication code outside
functions. Moreover web2py needs to write on this files using meta-
programming in order to to provide web based testing capabilities.
From an SE point of view I feel that you need to reuse a controller
you are doing something wrong: either you are putting in a controller
something that belongs to a module, or you should expose the
controller function as a service. Changing web2py to allow one app
to call a controller function from another app will result in a
maintenance nightmare for your application and I do not want to
encourage it.
2)
I am all for it but this is not a technical problem. The issue here
is writing specs. It also boils down to people having different
definitions for projects and applications and what is atomic and what
is not.
For example I tend to code an app with multiple controllers and
multiple models that share the same names (modes/auth.py and
controllers/auth.py) . In this way the app is the project (in some
people language) and auth is a component. I write auth to be reusable.
**I define an app as a set of models/views/controllers that does not
need other apps installed locally in order to run**
For example an app may need an authentication app to run but the
authentication app may be a web service running somewhere else. If
you make an app dependent on another app at the filesystem level you
may run into scalability issues because you will be unable to
distribute your apps and your apps will not be very portable nor
easily reusable.
Both issues 1) and 2) boil down to the fact that there are no
specification on how to write portable apps and reusable components.
The idea of PythonBeans was to write such specifications and create
small components can people can reuse, share among apps and share
over the network without creating problems.
It is always possible that I am not understanding what some of you
are saying. Perhaps you should send me example of shared/cooperative
apps that you have written or are planning to use or why you would
ever want to call the code in the controller of another app.
Massimo
Massimo
>
> Hi Massimo,
>
> Your EPB's as you've outlined them are a great idea. It seems much of
> this thread is more about ancillary issues that are related but not
> necessarily core to EPB's.
>
> Concerning some of the "ancillary" discussion, you mention:
>
>> For example I tend to code an app with multiple controllers and
>> multiple models that share the same names (modes/auth.py and
>> controllers/auth.py) . In this way the app is the project (in some
>> people language) and auth is a component. I write auth to be
>> reusable.
>
> What is your mechanism for reusability in this case? Since
> controllers and models are not objects or even in a module, I can't
> instantiate a new one or inherit from one or import one.
I am thinking of cas modules/controllers/views. I either expose them
and access as services or copy the files in any app then needs stand
alone authentication.
By reusability of controller functionality I do not mean importing
since controllers are associated to a request object and a URL.
> You also mention:
>
>> From an SE point of view I feel that you need to reuse a controller
>> you are doing something wrong...
>
> This seems to contradict your other statement.
You are right. What I mean here is reuse in the sense of importing,
as other people have requested.
> I'm just trying to
> understand your design pattern so I can better use web2py. As you
> mention, web2py does things differently than other frameworks (for
> good reasons). Developers then have to unlearn some of the patterns
> they have grown accustomed to.
I agree.
Massimo
> and access as services or copy the files in any app...
> I am thinking of cas modules/controllers/views. I either expose them
I think your latter suggestion, copying files (or even parts of code)
can happen more often than it should because "traditional" methods of
importing or subclassing functionality are not available. Â I can see
novice programmers developing bad habits of copy/paste "reuse" to
create an unmaintainable common functionality among applications. Â The
framework design may inadvertently lead users to bad practices.
Massimo
I have no objection to your code below. I will be happy to include your function (or some version of it) in the standard API.Anyway, as I always argued, anybody could use your function below without me changing web2py.I do not want to tell you how you use web2py. I just want to defend the current design that allows you to do what you are doing.Massimo
...the more I like it and want to include it in web2py. Some
suggestions for improvement:
1) if URI starts with '/' do what you do; elif starts with 'http:'
call the controller remotely using urllib (and pass cookies form env)
2) after run_controller allow for the possibility of calling
run_view_in as an option.
3) what is temp (lines[-1])?
4) perhaps there should be a separate build_environment(env) somewhere
to make this leaner since it could me used in main as well.
Massimo
Â
1) if URI starts with '/' do what you do; elif starts with 'http:'
call the controller remotely using urllib (and pass cookies form env)
2) after run_controller allow for the possibility of calling
run_view_in as an option.
3) what is temp (lines[-1])?
4) perhaps there should be a separate build_environment(env) somewhere
to make this leaner since it could me used in main as well.
I was thinking of something like:
1) For now I would call you function remote_run(url,env,...,view=True)
2) implement something like
if url[0]=='/':
do what you do...
if view: run_view as well
else: return urllib.urlopen(url).read()
I need to think more about cookies.
what do you think?
>
> 2) after run_controller allow for the possibility of calling
> run_view_in as an option.
> it can confuse the use?
Not necessarily.
> 3) what is temp (lines[-1])?
> what?
You use a variable temp that is not defined. Perhaps is just a cut-
and-paste problem.
I was thinking of something like:
>
> 1) if URI starts with '/' do what you do; elif starts with 'http:'
> call the controller remotely using urllib (and pass cookies form env)
> remotely ,I don't know how to do.
1) For now I would call you function remote_run(url,env,...,view=True)
2) implement something like
   if url[0]=='/':
     do what you do...
     if view: run_view as well
   else: return urllib.urlopen(url).read()
I need to think more about cookies.
what do you think?
> This would be my ideal path to MVC:
>
> - in controllers, prepare data to be rendered (no Html here) i.e. a
> form
> - call my (shared) view passing the data
> - make validation when form submitted
db=SQLDB(....)
db.define_table('person',SQLField('name',requires=IS_NOT_EMPTY()))
form=SQLFM(db.person) # in model or in controller
def test1():
response.view='form.html'
if form.accepts(request.vars,formname=None):
response.flash='inserted!'
return dict(form=form)
def test2():
response.view='form.html'
if form.accepts(request.vars,formname=None):
response.flash='inserted!'
return dict(form=form)
and if form.html
<html><body>
{{if response.flash;}}mind that: {{=response.flash}}<br/>{{pass}}
What is your name? <form><input name="name"/><input type="submit"/></
form>
{{if form.errors:}}errors: {{=BEAUTIFY(form.errors)}}<br/>{{pass}}
</body></html>
Is this that you were asking?
def test():
response.view='myview.html'
s=response.render(dict(... your variable...)
return s
Although this does not solve your problem.
Massimo
Right now the only problem I have (specifications aside) is with
serialization of the beans because of the database. That there are
two possibilities:
1) an object creates a SQLDB instance
2) an object uses a SQLDB instance defined outside
when the object is pickled if there is any reference to the SQLDB in
the object, the connection is serialized. When the object is unpicked
the connection is re-estabilished (both in case 1 and in case 2). The
fact is that in case 2 it should not because the connection was only
referenced, not "owned" by SQLDB.
This issue can be solved with strict specs on which beans can be
serialized or I need to implement a mechanism so that when an object
is unpicked check existing connections within the same thread. Can
make the code messy but it would a very smart bean.
Massimo
>
> yes I understand that problem is logical.
>
> I will try 1, just for testing.
>
> I am thinking about a proposal and I am studying your validation code.
> Some questions:
>
> 1- someone can explain how validation is managed by TG? It seems that
> @validate works the way I like but I am not sure: even in TG
> controllers are executed before views?
I think so. One cannot generate a view before knowing errors in the
input/
> 2- I thought that another similar issue, let alone MVC strict
> compliance, arises with validation via Ajax: suppose you have a form
> you want to validate through an ajax request so invoking a different
> controller. How could you that? I seem to understand you should pass
> the form object to that controller..
Ajax validation is done by the client. It can be done but it unsafe
and therefore a complement, not a replacement of the current mechanism.
def index():
     form=FORM(_action='validate',_validate=True..............)
and the controller validate could perform the validation.
These trivial thoughts make me think: could be validation encapsulated
in a module function(FORM,parameters) so that any controller can call
it and validate the passed form?
Makes sense?
IS_NOT_MEPTY is a class
IS_NOT_MEPTY(error_message='oops') is the validator object
IS_NOT_MEPTY()(value) calls the validator and returns (value,None) on
success and (value,error_message) on failure so.
Your code should be
> if IS_NOT_EMPTY()(x)[1]==None AND IS_IN_SET()(x,['max','john'])
> [1]==None:
> do something
> else:
> something else
Does it make sense?
Massimo