I was wondering about what the best practice is for writing, and mostly maintaining, an ICU resourcebundle. More specifically the best way to handle recurring strings.
For instance, say you have following resourcebundle:
root:table {
remove_page:string { "Remove this page" }
remove_widget:sring { "Remove this widget" }
}
Off course this is minimal, but I'm implying a big project with lots of similar strings and "sub-tables". Would it be best to keep it like this, viz. using a specific string for every action in the code, or would it be better practice to combine strings for example, as such:
root:table {
remove_this:string { "Remove this " }
page:string { "page" }
widget:string { "widget" }
}
Being an amateur I don't have much experience with ICU resourcebundles so far, but if they are properly built they should be very handy for i18n and maintenance, hence the question.
Thank you very much in advance for your time.
Edit: ICU info on Recourse Bundle Format - These formats might also be good to keep in mind in structuring a resource bundle, arrays take less memory than tables for instance. Off course these are "nameless", which might be a huge pain for reading the code.
See Formatting Messages. and MessageFormat::format() with named arguments. You do not want to be "concatenating" strings. Instead you might do something like this:
root {
remove_this { "Remove this {thing}." }
page { "page" }
widget { "widget" }
}
Note that because of rules in various languages, it may be easier to translate "Remove: {thing}", because the word "this" may need to be inflected due to gender, case, number, etc., for which see SelectFormat.
Related
My situation is this: I have multiple components in my view that ultimately depend on the same data, but in some cases the view state is derived from the data. How do I make sure my whole view stays in sync when the underlying data changes? I'll illustrate with an example using everyone's favorite Star Wars API.
First, I show a list of all the films, with a query like this:
# ALL_FILMS
query {
allFilms {
id
title
releaseDate
}
}
Next, I want a separate component in the UI to highlight the most recent film. There's no query for that, so I'll implement it with a client-side resolver. The query would be:
# MOST_RECENT_FILM
query {
mostRecentFilm #client {
id
title
}
}
And the resolver:
function mostRecentFilmResolver(parent, variables, context) {
return context.client.query({ query: ALL_FILMS }).then(result => {
// Omitting the implementation here since it's not relevant
return deriveMostRecentFilm(result.data);
})
}
Now, where it gets interesting is when SWAPI gets around to adding The Last Jedi and The Rise of Skywalker to its film list. We can suppose I'm polling on the list so that it gets periodically refetched. That's great, now my list UI is up to date. But my "most recent film" UI isn't aware that anything has changed — it's still stuck in 2015 showing The Force Awakens, even though the user can clearly see there are newer films.
Maybe I'm spoiled; I come from the world of MobX where stuff like this Just Works™. But this doesn't feel like an uncommon problem. Is there a best practice in the realm of Apollo/GraphQL for keeping things in sync? Am I approaching this problem in entirely the wrong way?
A few ideas I've had:
My "most recent film" query could also poll periodically. But you don't want to poll too often; after all, Star Wars films only come out every other year or so. (Thanks, Disney!) And depending on how the polling intervals overlap there will still be a big window where things are out of sync.
Instead putting the deriveMostRecentFilm logic in a resolver, just put it in the component and share the ALL_FILMS query between components. That would work, but that's basically answering "How do I get this to work in Apollo?" with "Don't use Apollo."
Some complicated system of keeping track of the dependencies between queries and chaining refreshes based on that. (I'm not keen to invent this if I can avoid it!)
In Apollo observables are (in components) over queried values (cached data 'slots') but your mostRecentFilm is not an observable, is not based on cached values (they are cached) but on one time fired query result (updated on demand).
You're only missing an 'updating connection', f.e. like this:
# ALL_FILMS
query {
allFilms {
id
title
releaseDate
isMostRecentFilm #client
}
}
Use isMostRecentFilm local resolver to update mostRecentFilm value in cache.
Any query (useQuery) related to mostRecentFilm #client will be updated automatically. All without additional queries, polling etc. - Just Works? (not tested, it should work) ;)
I am testing a page with a form that has two components - There are identical labeled fields in each section. With Capybara, I want to ensure that not only is there a "Name" field on the page (should have_field "Name"), but that there are, in fact, two of them.
Obviously, I can do this with xpath, but that's not the optimal solution. Is there a better way to handle this built into either Capybara or Rspec?
Take a look at within - it works for both actions and matchers. For example:
within("#some_id") { page.should_have("some content") }
within("#other_id") { page.should_have("some content") }
I think this is what you are looking for, see the docs. For example:
all('a').each { |a| a[:href] }
I have an IEnumerable that I'd like to add to Azure Table in the most efficient way possible. Since every batch write has to be directed to the same PartitionKey, with a limit of 100 rows per write...
Does anyone want to take a crack at implementing this the "right" way as referenced in the TODO section? I'm not sure why MSFT didn't finish the task here...
Also I'm not sure if error handling will complicate this, or the correct way to implement it. Here is the code from the Microsoft Patterns and Practices team for Windows Azure "Tailspin Toys" demo
public void Add(IEnumerable<T> objs)
{
// todo: Optimize: The Add method that takes an IEnumerable parameter should check the number of items in the batch and the size of the payload before calling the SaveChanges method with the SaveChangesOptions.Batch option. For more information about batches and Windows Azure table storage, see the section, "Transactions in aExpense," in Chapter 5, "Phase 2: Automating Deployment and Using Windows Azure Storage," of the book, Windows Azure Architecture Guide, Part 1: Moving Applications to the Cloud, available at http://msdn.microsoft.com/en-us/library/ff728592.aspx.
TableServiceContext context = this.CreateContext();
foreach (var obj in objs)
{
context.AddObject(this.tableName, obj);
}
var saveChangesOptions = SaveChangesOptions.None;
if (objs.Distinct(new PartitionKeyComparer()).Count() == 1)
{
saveChangesOptions = SaveChangesOptions.Batch;
}
context.SaveChanges(saveChangesOptions);
}
private class PartitionKeyComparer : IEqualityComparer<TableServiceEntity>
{
public bool Equals(TableServiceEntity x, TableServiceEntity y)
{
return string.Compare(x.PartitionKey, y.PartitionKey, true, System.Globalization.CultureInfo.InvariantCulture) == 0;
}
public int GetHashCode(TableServiceEntity obj)
{
return obj.PartitionKey.GetHashCode();
}
}
Well, we (the patterns & practices team) just optimized for showing other things we considered useful. The code above is not really a "general purpose library", but rather a specific method for the sample that uses it.
At that moment we thought that adding that extra error handling would not add much, and we diceided to keep it simple, but....we might have been wrong.
Anyway, if you follow the link in the //TODO:, you will find another section of a previous guide we wrote that talks a little bit more on error handling in "complex" storage transactions (not in the "ACID" form though as transactions "ala DTC" are not supported in Windows Azure Storage).
Link is this: http://msdn.microsoft.com/en-us/library/ff803365.aspx
The limitations are listed in more detail there:
Only one instance of the entity should be present in the batch
Max 100 entities or 4 MB payload
Same PartitionKey (which is being handled in the code: notice that "batch" is only specified if there's a single Partition key)
etc.
Adding some extra error handling should not overcomplicate things too much, but depends on the type of app you are building on top of this and your preference to handle this higher or lower in your app stack. In our example, the app would never expect > 100 entities anyway, so it would simply bubble the exception up if that situation happens (because it should be truly exceptional). Same with the total size. The use cases implemented in the app make it impossible to have the same entity in the same collection, so again, that should never happen (and if it happens, it wouls simply throw)
All "entity group transactions" limitations are documented here: http://msdn.microsoft.com/en-us/library/dd894038.aspx
Let us know how it goes! I'm also interested to know if other pieces of the guide were useful for you.
I use Catalyst (MVC framework for Perl), but the question probably apply to all MVC framework.
Until now, I used the Apache log files to get statistics about the visitors: user agent, URL accessed, time, etc. But now that I moved to an MVC framework, I don't think this is adequate. If a request to /1/foo and /1/bar are the same for me, I want to show /1/ in my log only, for example.
So I am wondering what is the best way to generate my own log files for statistics. Should I treat it as just another log file in my application?
These statistics can be logged at any time. Ideally they would be logged after the page is sent to the user, so it will not feel the additional time required for logging.
Given that Catalyst already uses subroutine attributes liberally, one useful approach might be to use an attribute to wrap the relevant subs in a custom logging mechanism. I wrote an article about this technique which uses logging as an example. The basic idea is:
use Attribute::Handlers;
sub Log : ATTR(CODE) {
my ($pkg, $sym, $code) = #_;
my $name = *{ $sym }{NAME};
no warnings 'redefine';
*{ $sym } = sub {
log_message("Entering sub $pkg\:\:$name");
$code->( #_ );
};
}
sub foo : Log {
# this will be logged
}
friedo's answer is incredibly nifty if that degree of flexibility is required.
OTOH you can continue to use the Apache error_log to record this data just by using $c->log->info() or one of its siblings. It's fairly trivial to extend Catalyst::Log to report other sorts of messages. I use a $c->log->sql() variant that writes SQL out to the error_log that's been run through SQL::Beautify, for example.
I can imagine something along the lines of
sub auto {
...
$c->log->audit(sprintf("%s called by %s", $c->action->reverse, $c->userid));
...
}
Putting it at the start (in auto) is not what you wanted, but it's definitely less problematic, since you know it will always get called, unlike end handlers.
Let's say we have a web app out there that is supposed to have a user fill out a form, and then it creates a ticket in the backend workflow engine. This form is going to continue to be the portal for the customer to view what's going on. Some forms go straight to ticket creation; others have to get approved by someone else before generating a ticket, and they can also be denied. This thing sends out emails, tracks answers to the questions of the form, tracks any uploaded attachments, and also logs "updates" as various actions are made to change the state of the form.
The business logic to decide what all to do when the form is first submitted or saved is starting to get hairy and I'm looking on ways to refactor it. I've started to look at state/strategy patterns, but it seems like all the logic just needs to get lumped together in one place eventually anyway. Plus, with all the dependencies on answers/attachments/log entries, it makes it complicated to inject mocks into because it has so much that it has to track.
Here's a pseudocode-ish layout of the form object's "save" functionality, simplified down...it's starting to get nasty and I'm trying to see if I can make it cleaner somehow.
if(this.isvalid)
{
if(isNewForm && !this.needsApproval) //just created, so start up a ticket
{
CreateTicket();
}
if(!isNewForm && justApproved) //pulled from the DB earlier, and was just approved
{
CreateTicket();
}
if(!isNewForm && justDenied) //pulled from the DB earlier, and was just denied
{
this.needsApproval = false;
this.closed = true;
}
if(isNewForm)
{
SendNewFormEmail();
if(this.NeedsApproval)
{
SendNeedsApprovalEmail();
}
this.CommentEntries.Add("Request submitted.");
}
else if(justApproved)
{
SendApprovalEmail();
this.CommentEntries.Add("Request approved.");
}
else if(justDenied)
{
SendDenialEmail();
this.CommentEntries.Add("Request denied.");
}
this.Save();
this.Answers.Save();
this.Attachments.Save();
this.CommentEntries.Save();
}
I've been thinking about state machines alot lately and I found the following proposal very promising. I wish I could tell you it works really great but I have not gotten that far yet. I can tell you that it looks better than any solution I've tried to date. Here's the link.. Hierarchical State Machine
if (isNewForm) {
if (JustDenied) {
...
}
if (JustApproved) {
....
}
} else {
... not a new form ...
}
I'm not sure how your handling JustDenied, but perhaps:
switch (FormState) {
case JustApproved:
....
case JustDenied:
....
}
Its psuedo code, so hard to say if that would work. But, yes, I agree that what you posted is starting to resemble pasta.
I think this is an excellent candidate for Workflow Foundation.
Problem is not to write a complicated state machine, it's dealing with the dynamics of a business (which can also seem like pasta sometimes ;)). The rules change, code needs to be changed, and as you spotted this yourself, maintainability can easily become a nightmare here...