Specify route rules, and route to different components - mason

I know how to to specify routes for page components using Mason::Plugin::RouterSimple, for example given a url of:
/archives/2015/07
I can create a component archives.mc as this:
<%class>
route "{year:[0-9]{4}}/{month:[0-9]{2}}";
</%class>
Archives for the month of <% $.month %>/<% $.year %>
and similarly I can create a news.mc component that will handle urls of:
/news/2012/04
and that's fine (and very elegant!) but now what I want is to be able to handle urls like the following ones:
/john/archives/2014/12
/john/news/2014/03
/peter/news/2015/09
/bill/archives/2012/06
etc. I know I can write the route rules as:
<%class>
route "{user:[a-z]+}/archives/{year:[0-9]{4}}/{month:[0-9]{2}}", { action=> 'archives' };
route "{user:[a-z]+}/news/{year:[0-9]{4}}/{month:[0-9]{2}}", { action=> 'news' };
</%class>
but then the requests have to be handled by two different components. How can I route a request to different components? archives.mc and news.mc won't be matched by Mason because there's a username before the name of the component.

The problem is that, while urs like /archives/2014/12 can be easily handled by an /archives.mc component, for urls like /john/archives/2014/12 and /bill/archives/2012/06 it's not clear where to put the archives component.
Mason will try to match the following components (it's a simplified list, please see Mason::Manual::RequestDispatch):
...
/john/archives.{mp,mc}
/john/dhandler.{mp,mc}
/john.{mp,mc}
but finally...
/dhandler.{mp,mc}
So my idea is to put a dhandler.mc component in the root directory:
<%class>
route "{user:[a-z]+}/archives/{year:[0-9]{4}}/{month:[0-9]{2}}", { action=> 'archives' };
route "{user:[a-z]+}/news/{year:[0-9]{4}}/{month:[0-9]{2}}", { action=> 'news' };
</%class>
<%init>
$m->comp($.action.'.mi', user=>$.user, year=>$.year, month=>$.month);
</%init>
If the url matches the first route, it will call the archives.mi component:
<%class>
has 'user';
has 'year';
has 'month';
</%class>
<% $.user %>'s archives for the month of <% $.month %>/<% $.year %>
(I used a .mi component so it will be accessible only internally).
The dhandler can be improved (better regexp, can check users from a database table and deny the request, etc.)
Since my archives and news components can accept POST/GET data, and since I want to accept any data, I can just pass everything with:
$m->comp($._action.'.mi', %{$.args});
Not too elegand, but it looks like it does its work.

Related

Workbox redirect the clients page when resource is not cached and offline

Usually whenever I read a blog post about PWA's, the tutorial seems to just precache every single asset. But this seems to go against the app shell pattern a bit, which as I understand is: Cache the bare necessities (only the app shell), and runtime cache as you go. (Please correct me if I understood this incorrectly)
Imagine I have this single page application, it's a simple index.html with a web component: <my-app>. That <my-app> component sets up some routes which looks a little bit like this, I'm using Vaadin router and web components, but I imagine the problem would be the same using React with React Router or something similar.
router.setRoutes([
{
path: '/',
component: 'app-main', // statically loaded
},
{
path: '/posts',
component: 'app-posts',
action: () => { import('./app-posts.js');} // dynamically loaded
},
/* many, many, many more routes */
{
path: '/offline', // redirect here when a resource is not cached and failed to get from network
component: 'app-offline', // also statically loaded
}
]);
My app may have many many routes, and may get very large. I don't want to precache all those resources straight away, but only cache the stuff I absolutely need, so in this case: my index.html, my-app.js, app-main.js, and app-offline.js. I want to cache app-posts.js at runtime, when it's requested.
Setting up runtime caching is simple enough, but my problem arises when my user visits one of the potentially many many routes that is not cached yet (because maybe the user hasn't visited that route before, so the js file may not have loaded/cached yet), and the user has no internet connection.
What I want to happen, in that case (when a route is not cached yet and there is no network), is for the user to be redirected to the /offline route, which is handled by my client side router. I could easily do something like: import('./app-posts.js').catch(() => /* redirect user to /offline */), but I'm wondering if there is a way to achieve this from workbox itself.
So in a nutshell:
When a js file hasn't been cached yet, and the user has no network, and so the request for the file fails: let workbox redirect the page to the /offline route.
Option 1 (not always useful):
As far as I can see and according to this answer, you cannot open a new window or change the URL of the browser from within the service worker. However you can open a new window only if the clients.openWindow() function is called from within the notificationclick event.
Option 2 (hardest):
You could use the WindowClient.navigate method within the activate event of the service worker however is a bit trickier as you still need to check if the file requested exists in the cache or not.
Option 3 (easiest & hackiest):
Otherwise, you could respond with a new Request object to the offline page:
const cacheOnly = new workbox.strategies.CacheOnly();
const networkFirst = new workbox.strategies.NetworkFirst();
workbox.routing.registerRoute(
/\/posts.|\/articles/,
async args => {
const offlineRequest = new Request('/offline.html');
try {
const response = await networkFirst.handle(args);
return response || await cacheOnly.handle({request: offlineRequest});
} catch (error) {
return await cacheOnly.handle({request: offlineRequest})
}
}
);
and then rewrite the URL of the browser in your offline.html file:
<head>
<script>
window.history.replaceState({}, 'You are offline', '/offline');
</script>
</head>
The above logic in Option 3 will respond to the requested URL by using the network first. If the network is not available will fallback to the cache and even if the request is not found in the cache, will fetch the offline.html file instead. Once the offline.html file is parsed, the browser URL will be replaced to /offline.

Yii url route with parameters

At the moment i have a glossar controller with an actionAnzeige()-method.
For this action i need GET-paramter named item.
Now i could use this url: www.xy.de/glossar/anzeigen?item=programming
But i want to use this: www.xy.de/glossar/programming
I've added this route to the rules:
'glossar/<item:\d+>'=>'glossar/anzeigen',
and now i can generate the url i want to use:
<?php echo Yii::app()->createUrl('glossar/anzeigen', array('item' => $glossarItem->Url)); ?>
But if i visit the created url, i get a 404 error.
You can use this, which accepts characters or numbers:
'glossar/<item:.+>'=>'glossar/anzeigen',
You have to use w+ instead of d+ since item takes letters instead of digits
'glossar/<item:\w+>'=>'glossar/anzeigen',

Rails Gem: Ajaxful_rating route problem

I'm trying to get ajaxful_rating to work with my rails installation.
I have everything running find until a user clicks on a star to rate.
When I click a star, the browser url points http://localhost:3000/entries/1/rate?dimension=design&show_user_rating=false&small=true&stars=4
and I get this error: No route matches "/entries/1/rate"
But my routes say:
resources :entries do
collection do
...
end
member do
post 'rate'
put 'submit'
end
Is there something I'm missing? Some js not included? All I have included is jquery right now.
Edit
try {
Element.update("ajaxful_rating_design_no-small_entry_1", "<ul class=\"ajaxful-rating\"><li class=\"show-value\" style=\"width: 60.0%\">Global rating average: 3.0 out of 5</li><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>");
new Effect.Highlight("ajaxful_rating_design_no-small_entry_1",{});
} catch (e) { alert('RJS error:\n\n' + e.toString()); alert('Element.update(\"ajaxful_rating_design_no-small_entry_1\", \"<ul class=\\\"ajaxful-rating\\\"><li class=\\\"show-value\\\" style=\\\"width: 60.0%\\\">Global rating average: 3.0 out of 5</li><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>\");\nnew Effect.Highlight(\"ajaxful_rating_design_no-small_entry_1\",{});'); throw e }
This is what my server says when I click on a rating.
Your application is sending a GET request (the parameters are passed in the URL), but you declared a POST route.
Changing post 'rate' to get 'rate' will allow this request to go through.
The other possibility is that your application is generating a GET on the client side when it should be generating a POST. I'd need to look at your view code to diagnose that one.

Using the Symfony admin generator to let a user manage a subset of record

My first post here, hopefully It will be right! =)
I am creating a site to manage web application development using symfony 1.4 and doctrine.
My records consist for this problem of Project and ProjectFeatures
Now what I want to do is use the admin generator to let users manage the features for one project thru a link constraining all the returned features by project_id, that would look like: http://mysite/member/project/:project_id/features
in my routing.yml configuration, I have:
member_project_feature:
class: sfDoctrineRouteCollection
options:
model: ProjectFeature
module: memberProjectFeature
prefix_path: /member/project/:project_id/features
with_show: true
column: id
with_wildcard_routes: true
project_id is an existing column in the model ProjectFeature,
I will use a custom query to retrieve features only by that project_id.
Now I can generate a url to link to that admin generator module without error using:
url_for('member_project_feature', array('project_id' => $project['id']))
And the routing system does recognise the url:
May 04 14:30:59 symfony [info] {sfPatternRouting} Match route "member_project_feature" (/member/project/:project_id/features.:sf_format) for /member/project/1/features with parameters array ( 'module' => 'memberProjectFeature', 'action' => 'index', 'sf_format' => 'html', 'project_id' => '1',)
But the admin generator can't generate it's links inside it's templates with that prefix_path and returns error InvalidArgumentException with message The "/member/project/:project_id/features/:action/action.:sf_format" route has some missing mandatory parameters (:project_id).
Any idea?
Well I found my answer at this url: http://www.blogs.uni-osnabrueck.de/rotapken/?s=symfony
But I will give it here and shorten it because, stackoverflow is awesome and it should be there for a long time =)
1st - The routing configuration I used in my question is valid.
2nd - You need to add a method in the action file generated by the admin
public function execute($sfRequest)
{
// taken from http://www.blogs.uni-osnabrueck.de/rotapken/?s=symfony
$this->forward404Unless(
$project_id = $sfRequest->getUrlParameter('project_id'));
$this->forward404Unless(
$this->project = Doctrine::getTable('ttcWebProject')->find($project_id));
$this->getContext()->getRouting()
->setDefaultParameter('project_id', $project_id);
if ($id = $sfRequest->getUrlParameter('id'))
{
$this->getContext()->getRouting()->setDefaultParameter('id', $id);
}
$result = parent::execute($sfRequest);
return $result;
}
At this point the url gets generated correctly but here is the last step to get to the end result you most probably want to achieve:
3rd - To get the list by project_id I can either provide a table method in the generator.yml, a default value to the getFilterDefaults or this method in the action file:
protected function buildQuery ()
{
$q = parent::buildQuery();
$rootAlias = $q->getRootAlias();
$q->andWhere("{$rootAlias}.project_id = ?",
$this->getRequest()->getUrlParameter('project_id'));
return $q;
}
I'm not 100% certain about what you're trying to do here, but it sounds like you need the ProjectFeature::toParams method return the project_id.

Pass an & as Part of a Route Parameter

Unfortunately I need to accept a route parameter with an & in it. I have this route definition. The id parameter will sometimes have an & in it like this X&Y001.
routes.MapRoute(
"AffiliateEdit",
"Admin/EditAffiliate/{*id}",
new { controller = "UserAdministration", action = "EditAffiliate", id = ""}
);
I have tried working around this issue in the following ways however none of them have worked. All of these result in an HTTP 400 Bad Request error in the browser.
Edit
This gives me Edit
<%= Html.RouteLink("Edit", "AffiliateEdit", new { id = a.CustomerID }) %>
This gives me Edit
<%= Html.RouteLink("Edit", "AffiliateEdit", new { id = Url.Encode(a.CustomerID) }) %>
This gives me Edit
the only thing I can think of (which is a "dirty" solution) is to encode the & yourself. for example something like ##26##.
make sure to check the decoding algorithm only decodes the & ids and not some id that happens to contain ##26## for example.
A better solution depending on db size is to change the offending ids in the database.

Resources