1111 Source code published at https://github.com/s-cork/HashRouting
1212"""
1313
14-
1514import anvil as _anvil
15+
1616from collections import namedtuple as _namedtuple
1717from ._logging import logger
1818
@@ -62,7 +62,7 @@ def __init__(self, **properties):
6262 self .on_navigation () # called the first time the form is loaded to start routing...
6363
6464
65- def on_navigation (self , url_hash = None , url_pattern = None , url_dict = None , path = None ):
65+ def on_navigation (self , url_hash = None , url_pattern = None , url_dict = None , path = None , dynamic_vars = None ):
6666 # when there is navigation - back/forward navigation
6767 # or when the user changes the url_hash or when you call anvil.set_url_hash in code
6868
@@ -108,7 +108,7 @@ def on_navigation(self, url_hash=None, url_pattern=None, url_dict=None, path=Non
108108
109109 try :
110110 if url_hash not in _cache and path is None :
111- path = self .find_path (url_hash , url_pattern , url_dict )
111+ path , dynamic_vars = self .find_path (url_hash , url_pattern , url_dict )
112112 except KeyError :
113113 logger .print (f'no route form with url_pattern={ url_pattern } and url_keys={ url_dict .keys ()} ' )
114114 if _error_form is not None :
@@ -119,31 +119,57 @@ def on_navigation(self, url_hash=None, url_pattern=None, url_dict=None, path=Non
119119 raise # this was an unexpected error so raise it
120120 else :
121121 self .content_panel .clear () # clear the form now just incase we end up with a new to cache form that is slow to load later
122- self .load_form (url_hash = url_hash , url_pattern = url_pattern , url_dict = url_dict , path = path )
122+ self .load_form (url_hash = url_hash , url_pattern = url_pattern , url_dict = url_dict , path = path , dynamic_vars = dynamic_vars )
123123
124124 _on_navigation_stack_depth -= 1
125125
126126
127127 def find_path (self , url_hash , url_pattern , url_dict ):
128128 # this method is called whenever we have form to load from navigation that is not in the cache
129+ given_url_parts = url_pattern .split ('/' ) # determine the individual portions
130+ # of this deep link. N.B. this disallows using a '/' within the dynamic variable
131+ num_given_url_parts = len (given_url_parts )
132+ logger .print (f'[find_path] searching { _paths } ' )
129133 for path in _paths :
130- if path .url_pattern == url_pattern and set (url_dict ) == set (path .url_keys ):
131- return path
134+ dynamic_vars = {}
135+ target_url_parts = path .url_pattern .split ('/' )
136+ logger .print (f'[find_path] trying { path .url_pattern } against { url_pattern } '
137+ f'; { given_url_parts } -> { target_url_parts } ' )
138+ if num_given_url_parts != len (target_url_parts ):
139+ # url pattern CANNOT fit, skip deformatting
140+ logger .print ('[find_path] pattern cannot fit' )
141+ continue
142+ for given , expected in zip (given_url_parts , target_url_parts ):
143+ logger .print (f'[find_path] testing { given } against { expected } ' )
144+ if expected .startswith ('{' ) and expected .endswith ('}' ):
145+ # dynamic variable
146+ logger .print (f'[find_path] found dynamic variable { expected [1 :- 1 ]} ' )
147+ dynamic_vars [expected [1 :- 1 ]] = given
148+ elif given != expected :
149+ logger .print (f"[find_path] { expected } isn't a dynamic variable and doesn't match { given } " )
150+ break
151+ else :
152+ # given path matches expected path
153+ logger .print (f'[find_path] { url_pattern } matches { path .url_pattern } ' )
154+ if set (url_dict ) == set (path .url_keys ):
155+ # only return if the url_dict matches
156+ logger .print (f'[find_path] { url_dict } matches { path .url_keys } ' )
157+ return path , dynamic_vars
132158 else :
133159 #if no break we haven't found a valid hash so load the error form
134160 raise KeyError (f'{ url_hash } does not exist' )
135161
136162
137- def load_form (self , url_hash , url_pattern , url_dict , path = None ):
163+ def load_form (self , url_hash , url_pattern , url_dict , dynamic_vars , path = None ):
138164 global _current_form
139165
140166 if url_hash in _cache :
141167 logger .print (_cache )
142168 logger .print (f"loaded { _cache [url_hash ].__name__ } from cache" )
143169 _current_form = _cache [url_hash ]
144170 elif path :
145- title = path .title if path .title is None else path .title .format (** url_dict )
146- _cache [url_hash ] = path .form (url_hash = url_hash , url_pattern = url_pattern , url_dict = url_dict ,
171+ title = path .title if path .title is None else path .title .format (** url_dict , ** dynamic_vars )
172+ _cache [url_hash ] = path .form (url_hash = url_hash , url_pattern = url_pattern , url_dict = url_dict , dynamic_vars = dynamic_vars ,
147173 _route_title = title , f_w_r = path .f_w_r , from_routing = True , ** _properties )
148174 logger .print (f"loaded { _cache [url_hash ].__name__ } , added to cache" )
149175 else :
@@ -182,7 +208,7 @@ def __call__(self, Cls):
182208 raise TypeError (f'title must be type str or None not { type (self .title ).__name__ } in { Cls .__name__ } ' )
183209
184210 class Route (Cls ):
185- def __init__ (self , url_hash = None , url_pattern = None , url_dict = None , _route_title = None , f_w_r = False ,
211+ def __init__ (self , url_hash = None , url_pattern = None , url_dict = None , dynamic_vars = None , _route_title = None , f_w_r = False ,
186212 route = True , from_routing = False , ** properties ):
187213 global _properties
188214 _properties = {} # reset _properties as early as possible
@@ -193,6 +219,7 @@ def __init__(self, url_hash=None, url_pattern=None, url_dict=None, _route_title=
193219 self .url_hash = url_hash
194220 self .url_pattern = url_pattern
195221 self .url_dict = url_dict
222+ self .dynamic_vars = dynamic_vars
196223 self ._route_title = _route_title
197224 self ._f_w_r = f_w_r
198225
@@ -399,7 +426,9 @@ def load_form(form, url_pattern=None, url_keys=[], *, replace_current_url=False,
399426 except :
400427 raise
401428
402- url_pattern = path .url_pattern
429+ dynamic_varnames = {i [1 :- 1 ] for i in path .url_pattern .split ('/' ) if i .startswith ('{' ) and i .endswith ('}' )}
430+ dynamic_vars = {key : value for key , value in properties .items () if key in dynamic_varnames }
431+ url_pattern = path .url_pattern .format (** dynamic_vars ) # expand dynamic variables
403432 url_dict = _get_url_dict (path .url_keys , form , ** properties )
404433 url_hash = _get_url_hash (url_pattern , url_dict )
405434
@@ -427,7 +456,7 @@ def load_form(form, url_pattern=None, url_keys=[], *, replace_current_url=False,
427456 global _properties
428457 _properties = properties
429458
430- _main_router .on_navigation (url_hash = url_hash , url_pattern = url_pattern , url_dict = url_dict , path = path )
459+ _main_router .on_navigation (url_hash = url_hash , url_pattern = url_pattern , url_dict = url_dict , path = path , dynamic_vars = dynamic_vars )
431460
432461
433462def load_error_form ():
@@ -536,3 +565,5 @@ def set_warning_before_app_unload(warning=True):
536565 _anvil .js .call_js ('setAppUnloadBehaviour' , warning )
537566
538567
568+
569+
0 commit comments