1
2
3
4
5
6 """
7 Macros used by loaders. compatible with couchapp. It allow you to include code,
8 design docs members inside your views, shows and lists.
9
10 for example to include a code, add just after first function the line :
11
12 // !code relativepath/to/some/code.js
13
14 To include a member of design doc and use it as a simple javascript object :
15
16 // !json some.member.i.want.to.include
17
18 All in one, example of a view :
19
20 function(doc) {
21 !code _attachments/js/md5.js
22
23 if (doc.type == "user") {
24 doc.gravatar = hex_md5(user.email);
25 emit(doc.username, doc)
26 }
27 }
28
29 This example includes md5.js code and uses md5 function to create gravatar hash
30 in your views. So you could just store raw data in your docs and calculate
31 hash when views are updated.
32
33 """
34 import glob
35 from hashlib import md5
36 import logging
37 import os
38 import re
39
40 from ..exceptions import MacroError
41 from ..utils import read_file, read_json, to_bytestring, json
42
43 logger = logging.getLogger(__name__)
44
45
48
50 for view, funcs in views.iteritems():
51 if hasattr(funcs, "items"):
52 apply_lib(doc, funcs, app_dir, objs)
53
55 for k, v in funcs.items():
56 if not isinstance(v, basestring):
57 continue
58 else:
59 logger.debug("process function: %s" % k)
60 old_v = v
61 try:
62 funcs[k] = run_json_macros(doc,
63 run_code_macros(v, app_dir), app_dir)
64 except ValueError, e:
65 raise MacroError(
66 "Error running !code or !json on function \"%s\": %s" % (k, e))
67 if old_v != funcs[k]:
68 objs[md5(to_bytestring(funcs[k])).hexdigest()] = old_v
69
71 def rreq(mo):
72
73 path = os.path.join(app_dir, mo.group(2).strip())
74 library = ''
75 filenum = 0
76 for filename in glob.iglob(path):
77 logger.debug("process code macro: %s" % filename)
78 try:
79 cnt = read_file(filename)
80 if cnt.find("!code") >= 0:
81 cnt = run_code_macros(cnt, app_dir)
82 library += cnt
83 except IOError, e:
84 raise MacroError(str(e))
85 filenum += 1
86
87 if not filenum:
88 raise MacroError(
89 "Processing code: No file matching '%s'" % mo.group(2))
90 return library
91
92 re_code = re.compile('(\/\/|#)\ ?!code (.*)')
93 return re_code.sub(rreq, f_string)
94
96 included = {}
97 varstrings = []
98
99 def rjson(mo):
100 if mo.group(2).startswith('_attachments'):
101
102 path = os.path.join(app_dir, mo.group(2).strip())
103 filenum = 0
104 for filename in glob.iglob(path):
105 logger.debug("process json macro: %s" % filename)
106 library = ''
107 try:
108 if filename.endswith('.json'):
109 library = read_json(filename)
110 else:
111 library = read_file(filename)
112 except IOError, e:
113 raise MacroError(str(e))
114 filenum += 1
115 current_file = filename.split(app_dir)[1]
116 fields = current_file.split('/')
117 count = len(fields)
118 include_to = included
119 for i, field in enumerate(fields):
120 if i+1 < count:
121 include_to[field] = {}
122 include_to = include_to[field]
123 else:
124 include_to[field] = library
125 if not filenum:
126 raise MacroError(
127 "Processing code: No file matching '%s'" % mo.group(2))
128 else:
129 logger.debug("process json macro: %s" % mo.group(2))
130 fields = mo.group(2).strip().split('.')
131 library = doc
132 count = len(fields)
133 include_to = included
134 for i, field in enumerate(fields):
135 if not field in library:
136 logger.warning(
137 "process json macro: unknown json source: %s" % mo.group(2))
138 break
139 library = library[field]
140 if i+1 < count:
141 include_to[field] = include_to.get(field, {})
142 include_to = include_to[field]
143 else:
144 include_to[field] = library
145
146 return f_string
147
148 def rjson2(mo):
149 return '\n'.join(varstrings)
150
151 re_json = re.compile('(\/\/|#)\ ?!json (.*)')
152 re_json.sub(rjson, f_string)
153
154 if not included:
155 return f_string
156
157 for k, v in included.iteritems():
158 varstrings.append("var %s = %s;" % (k, json.dumps(v).encode('utf-8')))
159
160 return re_json.sub(rjson2, f_string)
161