<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -5,3 +5,4 @@ Couchapp.egg-info
 build
 dist
 setuptools-*
+.DS_Store</diff>
      <filename>.gitignore</filename>
    </modified>
    <modified>
      <diff>@@ -4,3 +4,4 @@ include README.md
 include THANKS.txt
 include ez_setup.py
 recursive-include app-template *
+recursive-include vendor *
\ No newline at end of file</diff>
      <filename>MANIFEST.in</filename>
    </modified>
    <modified>
      <diff>@@ -110,12 +110,18 @@ class FileManager(object):
     @classmethod
     def generate_app(cls, app_dir):
         &quot;&quot;&quot;Generates a CouchApp in app_dir&quot;&quot;&quot;
-        paths = ['app-template', '../../app-template']
+        template_paths = ['app-template', '../../app-template']
+        vendor_paths = ['vendor', '../../vendor']
         
-        for path in paths:
+        for path in template_paths:
             template_dir = os.path.normpath(os.path.join(
                 os.path.dirname(__file__), path))
             if os.path.isdir(template_dir): break
+            
+        for path in vendor_paths:
+            vendor_dir = os.path.normpath(os.path.join(
+                os.path.dirname(__file__), path))
+            if os.path.isdir(vendor_dir): break
         
         try:
             shutil.copytree(template_dir, app_dir)
@@ -124,6 +130,16 @@ class FileManager(object):
             print &gt;&gt;sys.stderr, &quot;Can't create a CouchApp in %s: %s&quot; % (
                     app_dir, message)
             return
+            
+               
+        if vendor_dir:
+            vendor_path = os.path.join(app_dir, 'vendor')
+            try:
+                shutil.copytree(vendor_dir, vendor_path)
+            except OSError, e:
+                errno, message = e
+                print &gt;&gt;sys.stderr, &quot;Can't create a CouchApp, bad vendor dir: %s&quot; % message
+                return
 
         cls.init(app_dir)
 
@@ -231,8 +247,9 @@ class FileManager(object):
                 self.merge_js(attach_dir, doc['couchapp']['js'],
                     docid, verbose=verbose)
 
+                    
         self.push_directory(attach_dir, docid, verbose=verbose)
-
+        self.vendor_attachments(app_dir, docid, verbose=verbose)
         
 
     @classmethod
@@ -303,6 +320,8 @@ class FileManager(object):
         # create files from manifest
         if manifest:
             for filename in manifest:
+                if verbose &gt;=2:
+                    print &quot;clone property: %s&quot; % filename
                 file_path = os.path.join(app_dir, filename)
                 if filename.endswith('/'): 
                     if not os.path.isdir(file_path):
@@ -383,6 +402,8 @@ class FileManager(object):
                         filename = os.path.join(vs_item_dir, '%s.js' % 
                                 func_name)
                         open(filename, 'w').write(func)
+                        if verbose &gt;=2:
+                            print &quot;clone view not in manifest: %s&quot; % filename
             elif key in ('shows', 'lists'):
                 dir = os.path.join(app_dir, key)
                 if not os.path.isdir(dir):
@@ -391,8 +412,12 @@ class FileManager(object):
                     filename = os.path.join(dir, '%s.js' % 
                             func_name)
                     open(filename, 'w').write(func)
+                    if verbose &gt;=2:
+                        print &quot;clone show or list not in manifest: %s&quot; % filename
             else:
                 file_dir = os.path.join(app_dir, key)
+                if verbose &gt;=2:
+                    print &quot;clone property not in manifest: %s&quot; % key
                 if isinstance(design[key], (list, tuple,)):
                     write_json(file_dir + &quot;.json&quot;, design[key])
                 elif isinstance(design[key], dict):
@@ -403,7 +428,7 @@ class FileManager(object):
                         if isinstance(value, basestring):
                             write_content(field_path, value)
                         else:
-                            write_json(field_path + '.json', value)
+                            write_json(field_path + '.json', value)        
                 else:
                     value = design[key]
                     if not isinstance(value, basestring):
@@ -417,7 +442,13 @@ class FileManager(object):
             if not os.path.isdir(attach_dir):
                 os.makedirs(attach_dir)
             for filename in design['_attachments'].iterkeys():
-                file_path = os.path.join(attach_dir, filename)
+                if filename.startswith('vendor'):
+                    attach_parts = filename.split('/')
+                    vendor_attach_dir = os.path.join(app_dir, attach_parts.pop(0),
+                            attach_parts.pop(0), '_attachments')
+                    file_path = os.path.join(vendor_attach_dir, '/'.join(attach_parts))
+                else:
+                    file_path = os.path.join(attach_dir, filename)
                 current_dir = os.path.dirname(file_path)
                 if not os.path.isdir(current_dir):
                     os.makedirs(current_dir)
@@ -425,6 +456,8 @@ class FileManager(object):
                 if signatures.get(filename) != sign_file(file_path):
                     content = db.get_attachment(docid, filename)
                     write_content(file_path, content)
+                    if verbose&gt;=2:
+                        print &quot;clone attachment: %s&quot; % filename
 
     def dir_to_fields(self, app_dir, current_dir='', depth=0,
             manifest=[], verbose=False):
@@ -436,7 +469,8 @@ class FileManager(object):
             rel_path = current_path.split(&quot;%s/&quot; % app_dir)[1]
             if name.startswith('.'):
                 continue
-            elif depth == 0 and name.startswith('_'):
+            elif name.startswith('_'):
+                # files starting with &quot;_&quot; are always &quot;special&quot;
                 continue
             elif depth == 0 and name in ('couchapp', 'couchapp.json'):
                 # we are in app_meta
@@ -469,9 +503,7 @@ class FileManager(object):
                         verbose=verbose)
             else:
                 if verbose &gt;= 2:
-                    print &gt;&gt;sys.stderr, &quot;push %s&quot; % rel_path
-
-                manifest.append(rel_path)
+                    print &gt;&gt;sys.stderr, &quot;push %s&quot; % rel_path                
                 content = ''
                 try:
                     content = read_file(current_path)
@@ -486,13 +518,14 @@ class FileManager(object):
                 
                 # remove extension
                 name, ext = os.path.splitext(name)
-                if 'name' in fields:
+                if name in fields:
                     if verbose &gt;= 2:
-                        print &gt;&gt;sys.stderr, &quot;%(name)s is already in properties. Can't add (%(name)s.%(ext)s)&quot; % {
+                        print &gt;&gt;sys.stderr, &quot;%(name)s is already in properties. Can't add (%(name)s%(ext)s)&quot; % {
                         &quot;name&quot;: name,
                         &quot;ext&quot;: ext
                         }
                 else:
+                    manifest.append(rel_path)
                     fields[name] = content
         return fields
     
@@ -514,11 +547,25 @@ class FileManager(object):
                 if verbose &gt;= 2:
                     print &gt;&gt;sys.stderr, &quot;%s file not uploaded, sorry.&quot; % filename
                 break
-
-    def push_directory(self, attach_dir, docid, verbose):
+                
+    def vendor_attachments(self, app_dir, docid, verbose):
+        vendor_dir = os.path.join(app_dir, 'vendor')
+        if not os.path.isdir(vendor_dir):
+            return
+            
+        for name in os.listdir(vendor_dir):
+            current_path = os.path.join(vendor_dir, name)
+            if os.path.isdir(current_path):
+                attach_dir = os.path.join(current_path, '_attachments')
+                if os.path.isdir(attach_dir):
+                    self.push_directory(attach_dir, docid, verbose, 
+                                    vendor=name)
+                    
+    def push_directory(self, attach_dir, docid, verbose=False, vendor=None):
         # get attachments
         _signatures = {}
         _attachments = {}
+        all_signatures = {}
         for root, dirs, files in os.walk(attach_dir):
             if files:
                 for filename in files:
@@ -526,7 +573,9 @@ class FileManager(object):
                         continue
                     else:
                         file_path = os.path.join(root, filename) 
-                        name = file_path.split('%s/' % attach_dir)[1] 
+                        name = file_path.split('%s/' % attach_dir)[1]
+                        if vendor is not None:
+                            name = os.path.join('vendor/%s' % vendor, name)
                         signature = sign_file(file_path)
                         _signatures[name] = signature
                         _attachments[name] = open(file_path, 'rb')
@@ -538,12 +587,24 @@ class FileManager(object):
             metadata = design.get('couchapp', {})
             attachments = _attachments.copy()
             if 'signatures' in metadata:
+                all_signatures = metadata['signatures'].copy()
                 for filename in metadata['signatures'].iterkeys():
-                    if filename not in _signatures:
-                        db.delete_attachment(design, filename)
+                    if vendor is not None:
+                        if filename.startswith('vendor/%s' % vendor):
+                            del all_signatures[filename]
+                            if filename not in _signatures:
+                                db.delete_attachment(design, filename)
+                            elif _signatures[filename] == metadata['signatures'][filename]:
+                                del attachments[filename]
+                            
                     else:
-                        if _signatures[filename] == metadata['signatures'][filename]:
-                            del attachments[filename]
+                        if not filename.startswith('vendor'):
+                            del all_signatures[filename]
+                            if filename not in _signatures:
+                                db.delete_attachment(design, filename)
+                            else:
+                                if _signatures[filename] == metadata['signatures'][filename]:
+                                    del attachments[filename]
 
             for filename, value in attachments.iteritems():
                 if verbose &gt;= 2:
@@ -557,7 +618,10 @@ class FileManager(object):
             design = db[docid]
             if not 'couchapp' in design:
                 design['couchapp'] = {}
-            design['couchapp'].update({'signatures': _signatures})
+
+            all_signatures.update(_signatures)
+                
+            design['couchapp'].update({'signatures': all_signatures})
             db[docid] = design
 
     def package_shows(self, funcs, app_dir, verbose=False):</diff>
      <filename>python/couchapp/file_manager.py</filename>
    </modified>
    <modified>
      <diff>@@ -19,6 +19,10 @@ for dir, dirs, files in os.walk('app-template'):
     data_files.append((os.path.join('couchapp', dir), 
         [os.path.join(dir, file_) for file_ in files]))
 
+for dir, dirs, files in os.walk('vendor'):
+    data_files.append((os.path.join('couchapp', dir), 
+        [os.path.join(dir, file_) for file_ in files]))
+
 for dir, dirs, files in os.walk('python/couchapp'):
     for i, dirname in enumerate(dirs):
         if dirname.startswith('.'): del dirs[i]</diff>
      <filename>setup.py</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>a47b86cecbd487478574a3d213900c1219239a88</id>
    </parent>
  </parents>
  <author>
    <name>Benoit Chesneau</name>
    <email>benoitc@enlil.local</email>
  </author>
  <url>http://github.com/jchris/couchapp/commit/75c41d460a6ec2ee1e0ee0a6e569a4e27e53754e</url>
  <id>75c41d460a6ec2ee1e0ee0a6e569a4e27e53754e</id>
  <committed-date>2009-03-02T12:08:13-08:00</committed-date>
  <authored-date>2009-03-02T12:08:13-08:00</authored-date>
  <message>manage vendor folder. For now it manage couchapp vendor from the egg or source dir only.

So couchapp generate will add this vendor folder.
couchapp push , push vendors attachments in from vendor/X/_attachments to _attachments/vendor/X
couchapp clone put attachments in the right place, ie : _attachments/vendor/X to vendor/X/_attachments.

couchapp update-vendor is coming.</message>
  <tree>9531429ddfea4b362038fbf688eeb1d6f612d2b5</tree>
  <committer>
    <name>Benoit Chesneau</name>
    <email>benoitc@enlil.local</email>
  </committer>
</commit>
