Changeset 234

Show
Ignore:
Timestamp:
04/21/08 19:35:22 (7 months ago)
Author:
akaihola
Message:

[yui] Added support for loading the default skin.

Location:
trunk/yui
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • trunk/yui/components.py

    r231 r234  
    3434        return self.data.get('rollup', 1) 
    3535 
     36    @property 
     37    def skinnable(self): 
     38        return self.data.get('skinnable', False) 
     39 
     40    @property 
     41    def fullpath(self): 
     42        return self.data.get('fullpath', None) 
     43 
    3644    def __getattr__(self, attname): 
    3745        return self.data[attname] 
     
    4048class Components(dict): 
    4149    def __init__(self, components_dict): 
    42         super(Components, self).__init__(components_dict) 
     50        super(Components, self).__init__() 
    4351        self.rollup_mapping = {} 
    4452        for component_name, data in components_dict.items(): 
    45             self.rollup_mapping.setdefault(component_name, set()) 
    46             if 'rollup' not in data: 
    47                 continue 
    48             for rolled_up in data['supersedes']: 
    49                 self.rollup_mapping.setdefault(rolled_up, set()).add( 
    50                     component_name) 
     53            self.add(component_name, data) 
    5154 
    5255    def __getitem__(self, component_name): 
     
    6669        return [name for name, data in self.items() 
    6770                if data['type'] == 'css'] 
     71 
     72    def add(self, component_name, data): 
     73        self[component_name] = data 
     74        self.rollup_mapping.setdefault(component_name, set()) 
     75        if 'rollup' not in data: 
     76            return 
     77        for rolled_up in data['supersedes']: 
     78            self.rollup_mapping.setdefault(rolled_up, set()).add( 
     79                component_name) 
  • trunk/yui/middleware.py

    r233 r234  
    6969 
    7070import re 
     71from shlex import shlex 
    7172 
    7273from django.conf import settings 
    7374 
    74 from module_info_2_5_1 import MODULE_INFO 
     75from module_info_2_5_1 import MODULE_INFO, SKIN 
    7576from components import Components 
    7677 
     
    8788VERSIONS = {'raw': '', '': '', 'min': '-min', 'debug': '-debug'} 
    8889 
    89 COMPONENTS = Components(MODULE_INFO) 
    90  
    9190 
    9291class YUILoader: 
    9392 
    9493    def __init__(self): 
     94        self._module_info = Components(MODULE_INFO) 
    9595        self._components = set() 
    9696        self._rolled_up_components = {} 
     
    112112                self._roll_up_superseded(new_component_name) 
    113113 
     114    def add_module(self, module_def): 
     115        module_data = {} 
     116        lexer = shlex(module_def, posix=True) 
     117 
     118        def expect(*patterns): 
     119            token = lexer.get_token() 
     120            if token not in patterns: 
     121                raise ValueError, '%s expected instead of %s' % \ 
     122                      (' or '.join(repr(s) for s in patterns), 
     123                       token and repr(token) or 'end of data') 
     124            return token 
     125 
     126        str_attrs = 'name', 'type', 'path', 'fullpath', 'varName' 
     127        list_attrs = 'requires', 'optional', 'after' 
     128        state = 'ATTR' 
     129        expect('{') 
     130        while state != 'STOP': 
     131            if state == 'ATTR': 
     132                token = expect(*str_attrs+list_attrs) 
     133                expect(':') 
     134                if token in str_attrs: 
     135                    module_data[token] = lexer.get_token() 
     136                    if module_data[token] is None: 
     137                        raise ValueError, \ 
     138                              'string expected instead of end of data' 
     139                    state = 'DELIM' 
     140                elif token in list_attrs: 
     141                    expect('[') 
     142                    lst = module_data[token] = [] 
     143                    state = 'LIST' 
     144            elif state == 'LIST': 
     145                lst.append(lexer.get_token()) 
     146                if re.search(r'\W', lst[-1]): 
     147                    raise ValueError, 'invalid component name %r' % token 
     148                if expect(',', ']') == ']': 
     149                    state = 'DELIM' 
     150            elif state == 'DELIM': 
     151                if expect(',', '}') == '}': 
     152                    expect(None) 
     153                    state = 'STOP' 
     154                else: 
     155                    state = 'ATTR' 
     156 
     157        if 'type' not in module_data: 
     158            raise ValueError, 'type missing in %r' % module_def 
     159        self._module_info.add(module_data['name'], module_data) 
     160        return module_data 
     161 
    114162    def render(self): 
    115163        return '\n'.join(self._render_component(component) 
     
    122170    def _get_satisfied_rollup(self, component_name): 
    123171        if self._version == '-min': 
    124             for rollup_name in COMPONENTS.get_rollups(component_name): 
     172            for rollup_name in self._module_info.get_rollups(component_name): 
    125173                rollup_status = self._rollup_counters.get(rollup_name, set()) 
    126                 if len(rollup_status) >= COMPONENTS[rollup_name].rollup: 
     174                if len(rollup_status) >= self._module_info[rollup_name].rollup: 
    127175                    return rollup_name 
    128176 
    129177    def _count_in_rollups(self, component_name): 
    130         for rollup_name in COMPONENTS.get_rollups(component_name): 
     178        for rollup_name in self._module_info.get_rollups(component_name): 
    131179            rolled_up = self._rollup_counters.setdefault(rollup_name, set()) 
    132180            rolled_up.add(component_name) 
    133         for superseded in COMPONENTS[component_name].supersedes: 
     181        for superseded in self._module_info[component_name].supersedes: 
    134182            self._count_in_rollups(superseded) 
    135183 
    136184    def _roll_up_superseded(self, component_name): 
    137         for superseded in COMPONENTS[component_name].supersedes: 
     185        for superseded in self._module_info[component_name].supersedes: 
    138186            self._rolled_up_components[superseded] = component_name 
    139187            if superseded in self._components: 
     
    141189 
    142190    def _add_requirements(self, component_name): 
    143         for requirement in COMPONENTS[component_name].requires: 
     191        component = self._module_info[component_name] 
     192        for requirement in component.requires: 
    144193            self.add_component(requirement) 
     194        if component.skinnable: 
     195            self.add_component(SKIN['defaultSkin']) 
    145196 
    146197    def _render_component(self, component_name): 
    147         component = COMPONENTS[component_name] 
    148         path = component.path 
     198        component = self._module_info[component_name] 
     199        path = component.fullpath or YUI_BASE + component.path 
    149200        if component.type == 'js': 
    150201            if self._version != '-min' and path.endswith('-min.js'): 
     
    153204            if self._version == '' and path.endswith('-min.css'): 
    154205                path = path[:-8] + '.css' 
    155         return TAGS[component.type] % (YUI_BASE + path,) 
     206        return TAGS[component.type] % path 
    156207 
    157208    def _sort_components(self, component_names=None): 
     
    162213        while comps: 
    163214            component_name = comps.pop() 
    164             component = COMPONENTS[component_name] 
     215            component = self._module_info[component_name] 
    165216            direct_deps = component.requires + component.after 
    166217            indirect_deps = [ 
     
    177228YUI_RE = re.compile( 
    178229    r'%s(include|version) +(.*?)%s' % (PREFIX_RE, SUFFIX_RE)) 
     230YUI_ADDMODULE_RE = re.compile( 
     231    r'(?s)%saddModule\s*(\{\s*.*?\s*})\s*%s' % (PREFIX_RE, SUFFIX_RE)) 
    179232YUI_INIT_RE = re.compile( 
    180233    '%sinit%s' % (PREFIX_RE, SUFFIX_RE)) 
     
    183236    def process_response(self, request, response): 
    184237        components = set() 
    185         node = YUILoader() 
     238        loader = YUILoader() 
     239 
     240        def add_module(match): 
     241            loader.add_module(match.group(1)) 
     242            return '' 
     243        content = YUI_ADDMODULE_RE.sub(add_module, response.content) 
     244 
    186245        def collect(match): 
    187246            cmd, data = match.groups() 
     
    189248                components.update(data.split()) 
    190249            elif cmd == 'version': 
    191                 node.set_version(data) 
     250                loader.set_version(data) 
    192251            else: 
    193252                return '<!-- UNKNOWN COMMAND YUI_%s -->' % cmd 
    194253            return '' 
    195         content = YUI_RE.sub(collect, response.content) 
     254        content = YUI_RE.sub(collect, content) 
     255 
    196256        for component in components: 
    197             node.add_component(component) 
    198         content = YUI_INIT_RE.sub(node.render(), content, 1) 
     257            loader.add_component(component) 
     258 
     259        content = YUI_INIT_RE.sub(loader.render(), content, 1) 
    199260        response.content = YUI_INIT_RE.sub( 
    200261            '<!-- WARNING: MULTIPLE YUI_init STATEMENTS -->', content) 
     262 
    201263        return response 
  • trunk/yui/module_info_2_5_1.py

    r231 r234  
    193193                'skinnable': True, 
    194194                'type': 'js'}} 
     195 
     196SKIN = { 
     197    'defaultSkin': 'sam', 
     198    'base': 'assets/skins/', 
     199    'path': 'skin.css', 
     200    'after': ['reset', 'fonts', 'grids', 'base'], 
     201    'rollup': 3 } 
     202 
     203MODULE_INFO[SKIN['defaultSkin']] = { 
     204    'type': 'css', 
     205    'path': SKIN['base'] + SKIN['defaultSkin'] + '/' + SKIN['path'], 
     206    'after': SKIN['after'] } 
  • trunk/yui/tests/test_middleware.py

    r231 r234  
    33__doc__ = r""" 
    44    >>> import sys ; sys.setrecursionlimit(100) 
     5    >>> from pprint import pprint 
    56    >>> from ambidjangolib.yui.middleware import \ 
    6     ...     COMPONENTS, YUILoader 
    7     >>> COMPONENTS['yahoo'].requires 
     7    ...     YUILoader 
     8 
     9    >>> n = YUILoader() 
     10 
     11    >>> n._module_info['yahoo'].requires 
    812    [] 
    9     >>> COMPONENTS['yahoo'].after 
    10     ['grids', 'reset-fonts', 'reset-fonts-grids', 'base', 'reset', 'fonts'] 
    11     >>> COMPONENTS['event'].requires 
     13    >>> n._module_info['yahoo'].after 
     14    ['sam', 'reset-fonts', 'grids', 'reset-fonts-grids', 'base', 'reset', 'fonts'] 
     15    >>> n._module_info['event'].requires 
    1216    ['yahoo'] 
    13     >>> COMPONENTS['yuiloader-dom-event'].after 
    14     ['grids', 'reset-fonts', 'reset-fonts-grids', 'base', 'reset', 'fonts'] 
    15     >>> COMPONENTS.get_rollups('event') 
     17    >>> n._module_info['yuiloader-dom-event'].after 
     18    ['sam', 'reset-fonts', 'grids', 'reset-fonts-grids', 'base', 'reset', 'fonts'] 
     19    >>> n._module_info.get_rollups('event') 
    1620    set(['utilities', 'yuiloader-dom-event', 'yahoo-dom-event']) 
    1721 
    18     >>> n = YUILoader() 
     22    >>> pprint(n.add_module('''{ 
     23    ...     name: 'name', type: 'type', path: 'path', fullpath: 'fullpath', 
     24    ...     requires: ['req1', 'req2'], after: ['after1', 'after2'], 
     25    ...     optional: ['opt1', 'opt2'] }''')) 
     26    {'after': ['after1', 'after2'], 
     27     'fullpath': 'fullpath', 
     28     'name': 'name', 
     29     'optional': ['opt1', 'opt2'], 
     30     'path': 'path', 
     31     'requires': ['req1', 'req2'], 
     32     'type': 'type'} 
     33 
     34    >>> n.add_module('[') 
     35    Traceback (most recent call last): 
     36    ValueError: '{' expected instead of '[' 
     37 
     38    >>> n.add_module('{') 
     39    Traceback (most recent call last): 
     40    ValueError: 'name' or 'type' or 'path' or 'fullpath' or 'varName' or 'requires' or 'optional' or 'after' expected instead of end of data 
     41 
     42    >>> n.add_module('{,') 
     43    Traceback (most recent call last): 
     44    ValueError: 'name' or 'type' or 'path' or 'fullpath' or 'varName' or 'requires' or 'optional' or 'after' expected instead of ',' 
     45 
     46    >>> n.add_module('{name') 
     47    Traceback (most recent call last): 
     48    ValueError: ':' expected instead of end of data 
    1949 
    2050    >>> def test_sort(*c): return list(n._sort_components(set(c))) 
     
    6999    >>> add(d, 'reset-fonts') 
    70100    ['reset-fonts-grids'] 
     101 
     102    >>> d.add_module("{name:'added',requires:['dom']}") 
     103    Traceback (most recent call last): 
     104    ValueError: type missing in "{name:'added',requires:['dom']}" 
     105 
     106    >>> pprint(d.add_module("{type:'js',name:'added',requires:['dom']}")) 
     107    {'name': 'added', 'requires': ['dom'], 'type': 'js'} 
     108 
     109    >>> add(d, 'added') 
     110    ['reset-fonts-grids', 'yahoo', 'dom', 'added'] 
    71111""" 
    72112