Writing the patches ended up being pretty straightforward. Included are
the following: 

 * A patch to elixir/entity.py (assignment_syntax.patch) that adds a
process_attrs() method to EntityMeta, and calls it in __init__(). 

 * A patch to docs/tutorial.rst (tutorial_assignment_syntax.patch) which
updates the code samples to use class assignment rather than
with_fields. 

 * The test_attr_syntax.py file, which is mostly a copy of
test_movies.py, just with the class attribute syntax. 

All the patches are against SVN revision 193. 

All tests still run successfully with SQLAlchemy 0.4. I did have to
make an unrelated fix on elixir/ext/associable.py to get it to work
with 0.4, but that was the case before or after these patches. I don't
have access to a machine with 0.3 right now, but I will tomorrow. 

Let me know how this is. 

Adam


On Thu, 30 Aug 2007 08:12:47 -0700
AdamG <[EMAIL PROTECTED]> wrote:

> 
> 
> 
> ---------- Forwarded message ----------
> From: AdamG <[EMAIL PROTECTED]>
> Date: Aug 30, 8:54 am
> Subject: DSL vs. OOP Syntax
> To: SQLElixir
> 
> 
> On Aug 29, 10:36 am, Jonathan LaCour <[EMAIL PROTECTED]>
> wrote:
> 
> 
> 
> > Okay, I have had lots of discussions on this over the past few
> > months, and its becoming more and more clear that the majority of
> > people prefer the more traditional approach when it comes to
> > defining fields.  So, with that said, I think we need to listen to
> > our users here, and start supporting the traditional syntax, but
> > only for defining fields.  I think the relation style needs to
> > remain as a DSL-like approach.
> 
> > That being said, I think its time that we went ahead and deprecated
> > `with_fields` in favor of the "OO" approach, since the syntaxes
> > will end up being quite similar, with the added bonus of preserving
> > field order:
> 
> >      class Person(Entity):
> >          id = Field(Integer, primary_key=True)
> >          name = Field(String)
> 
> >          has_many('pets', of_kind='Pet', inverse='owner')
> 
> > In the long-term, we might want to deprecate `has_field` as well, so
> > that we will have only one way to do things.  I am hesitant to do
> > this right now, but I haven't been happy having two ways to do the
> > same thing for some time now, and I think we need to resolve this
> > at some point.
> 
> > So, the long and short of it is that I will finally break down, so I
> > don't have to hear about it anymore ;) So, whoever wants this,
> > please go ahead and work on a patch, and we'll try to get it into
> > the 0.4 release so that people can use it sooner rather than
> > later.  I want this to be the last major API breakage before we
> > release 0.4, and hopefully for a long time to come.  The patch
> > should contain tests and documentation updates before we'll accept
> > it.
> 
> Sounds good! I've got a test set up with that syntax, and am trying to
> add in the support by adding a function to handle the dict_ parameter
> on the init function of EntityMeta - would that be the right place to
> do this? I'm mostly using the source of Statement.process and one of
> the earlier TurboEntity releases as references, so if there's a better
> place for it to be, let me know. It's not working as of yet, but the
> long weekend coming up should be a good time to get it finished &
> submitted. Should the patches be submitted to Trac now, or directly to
> the devs, or ...?
> 
> Thanks,
> Adam Gomaa
> 


-- 
Adam Gomaa
[EMAIL PROTECTED]
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"SQLElixir" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlelixir?hl=en
-~----------~----~----~----~------~----~------~--~---

from elixir import *
from elixir.events import *

def setup():
    global Director, Movie, Actor, Media
    class Director(Entity):
        name = Field(Unicode(60))
        has_many('movies', of_kind='Movie', inverse='director')

    class Movie(Entity):
        """A simple Movie class

        """

        title = Field(Unicode(50))
        year = Field(Integer)

        belongs_to('director', of_kind="Director", inverse='movies')
        has_and_belongs_to_many('actors', of_kind="Actor", inverse='movies')
        has_one('media', of_kind="Media", inverse='movie')

    class Actor(Entity):
        name = Field(Unicode(60))
        has_and_belongs_to_many('movies', of_kind="Movie", inverse='actors')

    class Media(Entity):
        number = Field(Integer, primary_key=True)
        belongs_to('movie', of_kind='Movie', inverse='media')

    metadata.bind = "sqlite:///"

def teardown():
    cleanup_all()

class TestAttrSyntax(object):
    def setup(self):
        create_all()

    def teardown(self):
        drop_all()
        objectstore.clear()

    def test_backref(self):
        swars = Movie(title="Star Wars", year=1977)
        glucas = Director(name="George Lucas")
        swars.director = glucas

        # does it work before a flush?
        assert swars in glucas.movies

    def test_bidirectional(self):
        brunner = Movie(title="Blade Runner", year=1982)
        alien = Movie(title="Alien", year=1979)
        swars = Movie(title="Star Wars", year=1977)
        
        brunner.media = Media(number=1)
        m = Media(number=7)
        m.movie = alien
        
        rscott = Director(name="Ridley Scott")
        glucas = Director(name="George Lucas")
        
        hford = Actor(name="Harrison Ford")
        mhamill = Actor(name="Mark Hamill")
        sweaver = Actor(name="Sigourney Weaver")
        
        rscott.movies.append(brunner) 
        rscott.movies.append(alien)
        swars.director = glucas
        
        swars.actors.append(hford)
        swars.actors.append(mhamill)
        alien.actors.append(sweaver)
        brunner.actors.append(hford)
        
        objectstore.flush()
        objectstore.clear()
        
        # directors
        assert Movie.get_by(title="Alien").director is Director.get_by(name="Ridley Scott")
        assert Director.get_by(name="Ridley Scott").name == "Ridley Scott"
        assert Movie.get_by(title="Alien").director.name == "Ridley Scott"
        assert Movie.get_by(title="Star Wars").director is Director.get_by(name="George Lucas")
        
        # movie
        assert Movie.get_by(title="Blade Runner").year == 1982
        assert Movie.get_by(title="Alien").year == 1979
        
        # actors
        assert Actor.get_by(name="Harrison Ford") in Movie.get_by(title="Blade Runner").actors
        assert Actor.get_by(name="Harrison Ford") in Movie.get_by(title="Star Wars").actors
        assert Movie.get_by(title="Star Wars") in Actor.get_by(name="Mark Hamill").movies
        assert Movie.get_by(title="Blade Runner") in Actor.get_by(name="Harrison Ford").movies
        
        # media
        assert Movie.get_by(title="Blade Runner").media is Media.get_by(number=1)
        assert Movie.get_by(title="Blade Runner").media.number is Media.get_by(number=1).number
        assert Actor.get_by(name="Sigourney Weaver") in Media.get_by(number=7).movie.actors

if __name__ == '__main__':
    setup()
    test = TestAttrSyntax()
    test.setup()
    test.test_bidirectional()
    test.teardown()
    teardown()
        
    
        
        


diff -r cde87ce5bb7f -r 90f24f3ab661 elixir/entity.py
--- a/elixir/entity.py	Thu Aug 30 10:59:06 2007 -0400
+++ b/elixir/entity.py	Thu Aug 30 11:49:47 2007 -0400
@@ -406,6 +406,15 @@ class EntityDescriptor(object):
 
 
 class TriggerProxy(object):
+    """A class that serves as a "trigger" ; accessing it's attribues runs
+    the function that is set at initialization.
+
+    Primarily used for setup_all().
+
+    Note that the `setupfunc` parameter is called on each access of
+    the attribute.
+
+    """
     def __init__(self, class_, attrname, setupfunc):
         self.class_ = class_
         self.attrname = attrname
@@ -419,6 +428,7 @@ class TriggerProxy(object):
     def __repr__(self):
         proxied_attr = getattr(self.class_, self.attrname)
         return "<TriggerProxy (%s)>" % (self.class_.__name__)
+
 
 class EntityMeta(type):
     """
@@ -443,6 +453,9 @@ class EntityMeta(type):
 
         # process statements. Needed before the proxy for metadata
         Statement.process(cls)
+
+        # Process attributes, for the assignment syntax.
+        cls.process_attrs(dict_)
 
         # setup misc options here (like tablename etc.)
         desc.setup_options()
@@ -507,6 +520,37 @@ class EntityMeta(type):
         return Query(cls, session=cls._descriptor.objectstore.session)
     q = property(q)
 
+    def process_attrs(cls, attr_dict):
+        """Process attributes in the class, replacing with the
+        instantiated/bound Field objects.
+
+        The method used here is similar to the 0.1.0 TurboEntity
+        release; see ModelMeta.__init__. 
+
+        """
+
+        # For example, in:
+        # name = Field(String)
+        # We get {'name': Field(String)} here. 
+        for attr, field in attr_dict.iteritems():
+            # Check if it's an Elixir field. 
+            if isinstance(field, Field):
+                # No colname is defined, so set it to the name of the
+                # attr. See Field.__init__; it would be a 'colname'
+                # kwarg if it were overriden explicitely.
+                if field.colname is None:
+                    # So, for our example, the Field.colname will be 'name'. 
+                    field.colname = attr
+                # Call the EntityDescriptor.add_field method
+                cls._descriptor.add_field(field)
+            else:
+                # Not an Elixir field, let it be. 
+                pass
+        return
+                
+                
+        
+
 
 class Entity(object):
     '''
@@ -530,7 +574,6 @@ class Entity(object):
     For further information, please refer to the provided examples or
     tutorial.
     '''
-
     __metaclass__ = EntityMeta
 
     def __init__(self, **kwargs):
diff -r cde87ce5bb7f -r c8259088de05 docs/tutorial.rst
--- a/docs/tutorial.rst	Thu Aug 30 10:59:06 2007 -0400
+++ b/docs/tutorial.rst	Thu Aug 30 12:00:11 2007 -0400
@@ -37,11 +37,9 @@ containing the following lines:
     metadata.connect("sqlite:///movies.sqlite")
 
     class Movie(Entity):
-        with_fields(
-            title = Field(Unicode(30)),
-            year = Field(Integer),
-            description = Field(Unicode)
-        )
+        title = Field(Unicode(30))
+        year = Field(Integer)
+        description = Field(Unicode)
         
         def __repr__(self):
             return '<Movie "%s" (%d)>' % (self.title, self.year)
@@ -61,12 +59,12 @@ have, but fully optional.  We have put t
 have, but fully optional.  We have put this into our model so that we can 
 easily trace what is happening in an interactive python interpreter.
 
-Also, please note that elixir currently provide two different ways to declare
-the fields on your entities. We have not decided yet on which one we like best,
-or if we will always keep both. The other way to declare your fields is using
-the ``has_field`` statement, rather than the ``with_fields`` statement.  The
-``Movie`` example above can be declared using the ``has_field`` statement like
-so:
+Also, please note that elixir currently provide two different ways to
+declare the fields on your entities. We have not decided yet on which
+one we like best, or if we will always keep both. The other way to
+declare your fields is using the ``has_field`` statement, rather than
+assigning directly to the class attributes.  The ``Movie`` example
+above could be declared using the ``has_field`` statement like so:
 
 ::
 
@@ -150,9 +148,8 @@ entity "``Genre``" to our ``model.py``:
 ::
 
     class Genre(Entity):
-        with_fields(
-            name = Field(Unicode(15), unique=True)
-        )
+        name = Field(Unicode(15), unique=True)
+
         
         def __repr__(self):
             return '<Genre "%s">' % self.name
@@ -168,11 +165,9 @@ excitement. Add two lines to your ``mode
 ::
 
     class Movie(Entity):
-        with_fields(
-            title = Field(Unicode(30)),
-            year = Field(Integer),
-            description = Field(Unicode)
-        )
+        title = Field(Unicode(30)),
+        year = Field(Integer),
+        description = Field(Unicode)
         
         belongs_to('genre', of_kind='Genre')                # add this line
     
@@ -181,9 +176,7 @@ excitement. Add two lines to your ``mode
     
     
     class Genre(Entity):
-        with_fields(
-            name = Field(Unicode(15))
-        )
+        name = Field(Unicode(15))
         
         has_many('movies', of_kind='Movie')                 # and this one
         

Reply via email to