While I Compile

… I compile my thoughts about programming

I’ve moved

I’ve finally moved my blog to my own hosting and my own domain. I did this primarily so I could have more control over the site, and a little more control over the styles and plug-ins.

Here’s a few of my recent posts:

Discovering Typemock is about my frustration mocking some static methods, the ridiculous hoops I was forced to jump through, and the clean implementation I was finally able to do with Typemock.

My take on identifier semantics (Id vs No vs Code vs Key) is just my simple conventions for these popular identifier names. I don’t believe they’re all the same, and should be used under different circumstances.

My new blog is at http://www.whileicompile.com

February 26, 2012 Posted by | Uncategorized | | Leave a comment

Response to Seth Godin’s – Where do ideas come from?

Seth Godin is wrong

I just read Seth Godin’s blog post Where do ideas come from? And in my opinion he completely missed it.

Sorry, I think Seth is awesome as do many others, but on this one, I don’t think he really answered the question; where do ideas come from?  Instead, in my opinion, what he wrote was more a list of favourable conditions

While I’m no Seth Godin, I’m going to share my understanding of ideas and their source[1].

I hope I don’t need to explain why a post about ideas and creativity is on a programming blog.

Ideas come from questions

Ideas are answers to those questions.

Although it may appear that ideas come out of nowhere while driving to work or zoning out in the shower, they are really answering previously asked questions burning in your subconscious.  There are many conditions which help answers come more easily, but they never come if you don’t have the question in the first place.

Questions need to be installed into your subconscious

A fleeting question doesn’t quite make it into your subconscious to be answered later.  The question needs to be important enough to be prioritized by your mind.

Installations depth comes from emotion

The importance of the question is largely based on the intensity of the emotion driving the question.  That’s why many innovations come during massive change and crisis.  Fear and greed inspire many ideas.

New information is assimilated to answer these questions

New questions are never installed for questions you already have the information needed to answer it.  So you need more information to answer the question and will need time to assimilate new information which is filtered against your question.

This information can be from; feedback from action, random thoughts in the shower, synergy, social media, blogs, news, conversations, books, radio, lectures, etc…  And yes, Seth even television[2]

Fear constrains ideas

Fear eliminates ideas.  Whether criticism of a bad idea, or constraint, or even a well-meaning, ‘helpful’ person who’s experienced proves it cannot be done.

When people fear criticism, ideas are never put forth which may inspire better ideas.  All ideas must be valued for an innovative environment.

Forcing ideas constrains you to existing information

You’ll hear many people say that you sit down, brainstorm, make your decisions quickly and take immediate action, which is great, when you have all the information you need.  But unless you have all the information you need, forcing it quickly usually won’t give you an awesome idea.

Synergy is creativity’s compound interest

Ideas usually require time, but they can be sped up with synergy[3].

Synergy is instantaneous compound interest for creativity.

One idea (good or bad) leads to another idea, then another, and another.  Before you know it, the feedback cycle has taken you down an entirely different path.

But Seth was right about one thing

Ideas that don’t ship are worthless


[1] I’m also not a psychologist, so take this as more my own, possibly misunderstood, idea of how ideas happen.

[2] Television may not appear to be a motivator for ideas, because most people watch TV passively, but if your questions are installed deeply enough, it can still be a half decent source of information.

[3] I realize the word Synergy has been over used as a business buzzword, but it is a real concept.  Don’t let the buzzword throw you off.

November 24, 2010 Posted by | Uncategorized | 1 Comment

The UI programmers (not so) secret weapon

An Example:

Suppose you had software which matches buyers and sellers, and new users are created via a ‘new user’ wizard[1].  Let’s say the wizard has 4 pages for Basic User Info, Review, Processing, and Completion Status Report.  And there are 5 buttons; Cancel, Previous, Next, Run, and Finish.

Cancel is displayed from the Basic User Info, Review, and Processing pages.
Previous is displayed from the Review page.
Next is displayed from the Basic User Info page.
Run is displayed from the Review page.
Finish is displayed from the Completion Status Report page.

This isn’t difficult to manage the display from the events[2] right?

Well … in reality, it doesn’t take much of a change for your simple display functionality to become …

The Problem

Manipulating application display during events quickly turns into a complex, bug riddled, difficult to maintain, mess.

It may not seem like that big of a deal when you have only a few controls and a very simple (or no) workflow.  Actually, you may argue, managing visual display in the events may even seem like the most efficient strategy.  After all, you will never display, disable, or otherwise needlessly manipulate a control, but as your application grows more complex, this approach can leave you tearing your hair out[3].  This is especially true when you have multiple execution paths leading to the same event handlers with different state.

What if you received a change request for the above requirements, where additional info is required for buyers or sellers, resulting in a new wizard page for each, and in addition to that, business sellers require a page to gather Tax Information?

Your complexity has started to grow, resulting in buyers have a workflow of
Basic User Info -> Buyer Info -> Review -> Processing -> Completion Status Report

Individual sellers have a workflow of
Basic User Info -> Individual Seller Info -> Review -> Processing -> Completion Status Report

And commercial sellers have a workflow of
Basic User Info -> Commercial Seller Info -> Tax Info -> Review -> Processing -> Completion Status Report

Notice how these changes lead to different execution paths all arriving at the Review page.  The question then, is; where does the back button on the Review page send the user?  The back button requires logic to know which wizard page to display.

It’s not difficult to imagine the need for logic to be added in more than one place[4], which can result in your display being processed differently from every event.  Not only does this lead to repeated code, but can also create bugs where buttons do/don’t display where they should.

Eventually, as this logic grows increasingly complex, simple, 5 minute, change requests will eventually take you hours to determine there won’t be any side effects.

At this point, developers usually find …

Inadequate Solutions

So how do people manage this?  I mean, beyond having the logic at the control level.

Well, you can put controls for different states on their own panel for their states.  So in the example above, you could add the buttons directly onto the wizard page control[5].  This would ensure the correct buttons, for the panel, are displayed, but it doesn’t solve the ‘Back’ button logic of which page to display.

Or you could add some kind of control grouping mechanism, like a collection of the controls, to display or hide all controls at once.  The problem with this, is what happens when you have functionality which will enable/disable a specific control, or do something else?  Do you add another grouping or another function to manipulate your control group, or do you have that one function do enabling/disabling as well?  What happens if the enabling/disabling logic is different from the display logic?  You also have the problem of one control being in multiple groups; will your groupings need to be called in the order of hide, then show, just to get the control that’s in both groups to display?  And if you do, will you let the control flicker? Or will you add extra functionality to prevent specific controls from hiding in the first place, so it won’t flicker?

Or maybe you can make a centralized function to manage the controls where the display is based on the passed in parameters.  Well this is very close, but no cigar.  Not only are the parameters needless, as I’ll show later, but this won’t completely remove logic from the events and can needlessly increase logic in that function.

The solution most have preferred is to make screens as minimal as possible, with a new screen for each piece of functionality.  This is often effective, at the cost of usability, but sometimes has its own problems like managing state while gathering data from multiple screens to persist it all in one shot.  Not even to mention that this strategy would be ineffective in our example.  Besides, in my experience, you can never really get away from at least some display manipulation.

This is a small example; imagine having a screen with 50 controls, where many of them change state.  Imagine how difficult it would be to manage the display this way.

Hopefully you can see how this can turn into a major pain in the butt.

Well, the good news is this problem has been solved …. Solved a long time ago actually[6].

The solution is ….

The UpdateUI() Method

It’s pretty simple really; just create a single function for the entire page, view, form, or screen (whatever it’s called in your technology).  All this function does, is manage the control display.  Then call this function at the end of every event, after all data manipulation.

It’s pretty simple; there are just …

5 Rules for the UpdateUI() method

1.     Fast – Don’t do anything which isn’t fast.  The state data driving the display decisions should already be calculated.

2.     No parameters – You should be able to make all decisions about display using the current screens state.

3.     No side effects – Don’t manipulate data within this method.  Not only can it slow it down, but it can also lead to inconsistent display evaluations.

4.     Manage each control individually – Don’t hide all controls, then show the controls you want displayed.  This causes flicker and adds unnecessary complexity.

5.     Call it last – call this function at the end of every event.

Code Sample

Here’s a little sample of what the wizard UpdateUI() code might look like.

public void UpdateUI()
{
 basicUserPg.Visible = (CurrentPage == NewUserWizardPage.BasicUserInfo);
 buyerPg.Visible = (CurrentPage == NewUserWizardPage.BuyerInfo);
 individualSellerPg.Visible = (CurrentPage == NewUserWizardPage.IndividualSellerInfo);
 commercialSellerPg.Visible = (CurrentPage == NewUserWizardPage.CommercialSellerInfo);
 commercialSellerTaxPg.Visible = (CurrentPage == NewUserWizardPage.CommercialSellerTaxInfo);
 reviewPg.Visible = (CurrentPage == NewUserWizardPage.Review);
 processingPg.Visible = (CurrentPage == NewUserWizardPage.Processing);
 compeletedPg.Visible = (CurrentPage == NewUserWizardPage.CompletionStatusReport);

 cancelButton.Visible = (CurrentPage == NewUserWizardPage.BasicUserInfo
                     || CurrentPage == NewUserWizardPage.BuyerInfo
                     || CurrentPage == NewUserWizardPage.IndividualSellerInfo
                     || CurrentPage == NewUserWizardPage.CommercialSellerInfo
                     || CurrentPage == NewUserWizardPage.CommercialSellerTaxInfo
                     || CurrentPage == NewUserWizardPage.Review);
 previousButton.Visible = (CurrentPage == NewUserWizardPage.BuyerInfo
                     || CurrentPage == NewUserWizardPage.IndividualSellerInfo
                     || CurrentPage == NewUserWizardPage.CommercialSellerInfo
                     || CurrentPage == NewUserWizardPage.CommercialSellerTaxInfo
                     || CurrentPage == NewUserWizardPage.Review);
 nextButton.Visible = (CurrentPage == NewUserWizardPage.BasicUserInfo
                     || CurrentPage == NewUserWizardPage.BuyerInfo
                     || CurrentPage == NewUserWizardPage.IndividualSellerInfo
                     || CurrentPage == NewUserWizardPage.CommercialSellerInfo
                     || CurrentPage == NewUserWizardPage.CommercialSellerTaxInfo);
 runButton.Visible = (CurrentPage == NewUserWizardPage.Review);
 finishButton.Visible = (CurrentPage == NewUserWizardPage.CompletionStatusReport);
}

This is quite a bit simpler than having the code sprinkled throughout the screen’s logic.

You may also want to move many expressions into their own protected properties. For example, you may want to change …

 cancelButton.Visible = (CurrentPage == NewUserWizardPage.BasicUserInfo
                     || CurrentPage == NewUserWizardPage.BuyerInfo
                     || CurrentPage == NewUserWizardPage.IndividualSellerInfo
                     || CurrentPage == NewUserWizardPage.CommercialSellerInfo
                     || CurrentPage == NewUserWizardPage.CommercialSellerTaxInfo
                     || CurrentPage == NewUserWizardPage.Review);

… to …

cancelButton.Visible = DisplayCancelButton;

Unfortunately, if your screen has a lot of controls, you may end up with a lot of these properties.  Because the state used to make the display decisions is usually in that screens class, it’s difficult to move these expressions into a separate class.  Or at least it is difficult to make a generalized statement about how to do it.

Where to use

This technique can be used in every GUI language or technology I’ve ever worked with.  I’ve used this strategy in Classic VB, C++ (MFC[7], Win32), Classic ASP, PHP, JavaScript, ASP.NET webforms[8] , etc…

This technique can significantly reduce Ajax initiated display adjustments.

Costs

The cost to managing your display this way is a slight increase in computing power required to calculate every display decision on every event.  However, in reality, if your UpdateUI() function causes a performance issue that’s noticeable, you’ve probably missed one of the rules stated above.

In my opinion, the increased simplicity, and cleanliness of you code, more than makes up for the slight increase in processing.

Conclusion

The UpdateUI() method is an efficient and effective way to keep your display manipulation code clean & bug free.  Even if your screen is relatively simple, you still benefit from having all your code in one place, and if it does grow in complexity, it will be manageable.  This technique has saved me countless hours of frustration.

Copyright © John MacIntyre 2010, All rights reserved


[1] These examples are really difficult to come up with, so let’s just go with this.  In reality many screens will be much more complex as well.

[2] I don’t necessarily mean ‘code behind your buttons’, but any logic executed specifically for this button, as a result of clicking it.

[3] For proof, see my avatar.  😉

[4] As if one place isn’t enough

[5] Wizard page controls are on their own panel of course, I mean we’ve all definitely learned that haven’t we?

[6] MFC had UpdateUI() as a virtual method on its main window class in the mid-1990s.  Fortunately, this was one of the only new paradigms Microsoft has ever added which did not undo the solution I came up with in the last paradigm

[7] The name actually comes from the CView::UpdateUI() method in MFC which was a core method for all UI related activity, before MFC I was calling it EnableCtrls().

[8] In ASP.NET WebForms, this function is called Page.PreRender.  However, this solution isn’t the greatest, since any control with an event needs to be populated in Page.OnInit just to wire up events properly.

Thanks to Cory Fowler for reviewing my post.

October 3, 2010 Posted by | C#, Code, Programming, Uncategorized | , | 2 Comments

My week (09/18/2010)

Blog Posts

Earlier this week I posted What is too simple and small to refactor? about a  follow up to my first Clean Code experience where I took a very small function, and refactored it.  In the end I was truly questioning; how small is too small to refactor?  This post received quite a bit of a response, including a response from Uncle Bob Martin and several refactors from Cliff Mees, Neal Blomfield (his response), Cory Fowler Ben Alabaster begin_of_the_skype_highlighting     end_of_the_skype_highlighting, and even Jon Skeet.

My Twitter Worth Mentioning(?)

“..I’d explain why, but I have to, like, go put on lipstick.”-@aalear responding to a comment “females are too busy being beautiful” #gogirl 8:25 PM Sep 15th

My recent blog posts have generated a lot of feedback among my friends & colleagues. I’m glad. It’s a great conversation. 12:09 AM Sep 15th

I’m going to create a restaurant review site & call it StickyTables.com 12:51 PM Sep 13th

“Clean Code is a design philosophy more than a naming convention.” – me #justQuotedMyself #dealWithIt 😉 12:36 PM Sep 13th

If somebody says my code sucks & they’ll redo it, I’d be hurt. But for my design, I’m relieved. #msPaintSucks 😉 10:56 AM Sep 13th

Just saw a really cool job title on LinkedIn “Experienced Code Poet” 11:04 PM Sep 12th

When I hear “the cloud” I know I no longer understand what the other person is talking about. #widelyMisusedTerm 9:58 PM Sep 12th

Friday; I love you, but you come way too fast. #in 7:12 AM Sep 17th

Even when they score a major coup to attract & add value to users, the announcement is littered with comments like “Who uses @MySpace? ” 11:16 AM Sep 16th

Got to say; one of the biggest challenges I have blogging is coming up /w relevant examples. 8:17 AM Sep 16th

#FF @unclebobmartin not for his Clean Code msg, which is awesome, but for addressing your questions & concerns. #wayToGo about 19 hours ago

A programmer started to cuss Cause getting 2 sleep was a fuss As he lay there in bed Looping round in his head Was while(!asleep()) sheep++; 9:13 PM Sep 8th (this wasn’t mine, it was quoted from a StackOverflow question)

In my opinion ‘no written requirements’ is the biggest kiss of death a project can have. #stackexchange http://bit.ly/d3rho2 5:42 PM Sep 8th

I don’t hate technical buzzwords, only the ones non-tech people have hijacked. 4:17 PM Sep 16th

Wisdom from Twitter

utahkay @utahkay We’ve outsourced your project to a colony of bacteria. Not the best programmers, but you can get a lot of them for cheap. 6:08 PM Aug 28th

Karen Lopez @datachick “repay technical debt as soon as possible” @drsql #24HOP #DarcVC 6:19 PM Sep 16th

Justice Gray @justicegray I have a dream that one day it will take more to be an “industry expert” in software development than just calling yourself one publicly. 8:37 PM Sep 14th

Ben Alabaster @BenAlabaster You had me at “Hello World!” #ProgrammerPickupLines 7:40 PM Sep 17th

Jim Minatel @wrox Stupid password policies just make people use stupid passwords 12:01 PM Sep 17th

Michael Kramer @MichaelKramer Sorry IE9, I can’t use you. I’m still too pissed about IE6. 4:08 PM Sep 15th

Carlos Figueroa @carlosfigueroa If you can’t be replaced, you probably can’t be promoted. 12:56 PM Sep 9th

Chris Ammerman @cammerman Everyone thinks their opinions are rational, but often they’re merely rationalized. 11:07 AM Sep 9th

Kent Beck @KentBeck if you don’t have a good name for it, give it a bad name. a really, really bad name so you’ll fix it later. 7:31 PM Sep 8th

Knowles Atchison Jr @_KYA “The beauty of the Internet is you can just pull a quote out of your ass and attribute it to whoever the fuck you want.” – George Washington 8:19 PM Sep 7th

Recommended links found this week

Periodic Table of the Elements (via John D Cook)

Threading in C#

Why software development metaphors always fail

Finally The Entire Interview with Uncle Bob Martin

Khan Academy

Tools and Utilities for the .NET Developer

Videos this week

This one is freakin hilarious.  Here’s a quote “Does /dev/null/ support sharding?”  Mongo DB is web scale Unfortunately, I can’t embed it.  Here’s the sequel Episode 2 – All The Cool Kids Use Ruby.  I also saw MySQL is Not ACID Compliant, which is basically the same as Mongo DB is Web Scale, but about MySQL.

Here’s a great quote from the following video:  “How many languages to you have to know to get a god damn web page up?” – @unclebobmartin

September 19, 2010 Posted by | Uncategorized | Leave a comment

Let’s turn off all features to improve UX

Here’s a thought; what if all features on a new software upgrade were turned off by default?

What if when you bought a software upgrade, all the new features were disabled and the software worked exactly as if it did before you installed it? What if new software had all but the most structurally necessary features disabled?

Yes, I’m serious.

But why?

For a maximum user experience of course …

WTF? … how can you have a maximum user experience with everything turned off?

Because the biggest issue with user experience is surprises.

Well duh! But now we’re talking bugs, and everybody knows bugs suck.

Yeah, bugs suck, but that’s not what I’m talking about.

I’m talking about features that work to spec, but unfortunately the user doesn’t even know exist! Notice that I’m not talking about features that users don’t know how to use. I’m talking specifically about features, often intrusive features, which the user doesn’t know about.

Language Bar

Language Bar

Like what? … oh, I don’t know … like having my keyboard cycling between US, Canadian French, and Canadian Multilingual, in an apparently random pattern last week. This happened because the default hot keys on the Language Bar software were set to the same keys as is standard for highlighting text for cut & pasting. This took me days to figure out, for a variety of reasons, the biggest being that, the change is not obvious until I type a letter that has a different mapping, turning my your single quotes ‘, into “ or è! I was completely oblivious to the fact that utility even had keyboard shortcuts, as I’m sure the person who selected those keys was oblivious to the fact they are standard shortcuts. This was extremely frustrating, and notice it was not a bug.

The problem is that things did not work as expected.

Unfortunately, when the user doesn’t know ‘about’, let alone ‘how to use’, all the funky bells & whistles we added to improve their lives, their experience more closely resembles a series of surprises. And surprises suck … oh yes they do suck. I’m not talking “Ed McMahon showing up on your door step” surprises, I’m talking “your meeting starts in 5 minutes, and the printer doesn’t work, and when you reboot, a large Windows Update starts installing” surprises.

Surprises are frustrating and unproductive.

So why the surprises? Because users are diving into the software to get stuff done, and don’t have the time or are just unwilling to put in the effort to learn all the bells and whistles. And who can blame them? Who wants to read hundreds of pages of documentation just to use the one new piece of functionality they bought the upgrade for.

Maybe if we just gave them what they wanted, they might invest the 5 minutes to learn what they need, and we’d have a more educated user base. Maybe if we gave them functionality and an education in bite sized chunks, they’d be more inclined to learn more because the commitment is a 2-5 minute video, not a huge, 1000 page, $75 book, or worse, a help file which provides absolutely no indication of scope.

So what’s my solution?

I think software should install with all features disabled.

Then when the user wants to use the awesome feature he was sold on, they just kick up some kind of feature start up dialog, which prompts the user to watch a minimal length tutorial for the feature and make the decision to enable the feature right in the tutorial. Yes, there would need to be the option to bypass the tutorial of course, for those who already know it, or just want to fumble around in it, but just knowing the feature exists will make their fumbling more likely to succeed. Maybe functionality could even be presented (marketed) to the user in order of popularity, helping users decide what features to look into if they don’t already know them. It might also help software companies know what functionality they should not be spending time on.

Yes, I realize there will be some things that must be enabled due to structural integrity issues, where enabling / disabling a feature is just too much work. Or business has decided to require it, regardless, to force feed some type of functionality to the user (think Apple iTunes). Or it’s just a design decision. But maybe those auto-enabled features could have their (again, bitesized) tutorials queued according to the most invasive functionality so the user could still skip them, but again, at least they’d know what changed.

I think the biggest argument against this idea would come from the business end of software product companies who feel that it’s got to be setup immediately based on the most common users, assumed workflow, and I’m sure there will be many who believe it must be enabled because users just won’t enable it themselves … yeah … just think about that statement for a second please. I know that would be an argument from somebody.

So, in summary, turn off as many features as possible, preferably all of them and push the user through a bite sized tutorial before allowing them to enable each one.

July 23, 2010 Posted by | Uncategorized | 3 Comments

New Job

New job

I’m starting at a new job this morning. 

I’ve been looking since April. Well sort of; it took me a long time to accept the fact that full time might be the best choice for me at this point in my life. But once I accepted that, I went straight to Infusion Development and got in their interview process. 

I’ve known about Infusion since about 2006 via their ads on DotNetRocks. As I understood, they’ve been growing super fast, have reputation for having bright developers, & a bleeding edge development strategy.  For example, Infusion is a leading developer of Microsoft Surface apps … If you’re reading this, I’m sure you know about Surface already, but if you don’t, go to Google Video and do a search on it.  It’s an awesome product.

I won’t be working on the Surface though. I really want to work in ASP.NET MVC / jQuery, and that’s what I’ll be doing. 

I’ve wanted to work at Infusion for a long time, but didn’t act on it since I had a pretty good thing going as an independent consultant. But when my contract ended in April, a bit of soul searching led me to the conclusion that I needed to push my career to another level, and this might be the best strategy.

As I understand it, Infusion has over 100 developers in their Toronto offices. If you’ve ever looked my Twitter feed, you know I like talking to smart developers. So, yeah, I’ll be in heaven.  🙂

One thing I’ve noticed since I started looking for a job is that I seem to have missed an important exit in my career. Nobody looks for developers with more than 10 years experience. It seems that after 10 years most dev’s have moved to management or architecture, which makes sense, but as a ‘do it all’ independent consultant, I never made that distinctive leap. I made more of a indistinctive transition.

Based on conversations during interviews, I got the impression they would like to see me pursue a move to architect and will help me do it. So that’s nice … Assuming I did interpret that correctly. 

Infusion also has a wickedly awesome intensive training program as well, where you do intensive, 8 hours a day, in house, until you learn the new technologies.  I believe they even have their own training team. Freakin awesome!  

The HR lady was happy when I expressed excitement about the training. She said a lot of people don’t like it. I’m like WHAT!?!?. In all seriousness, I expect it won’t be too long before my manager gives me an ass kicking for signing up for too many training courses.

You know, I’m really too old to be naively enthusiastic about a new job, and I’m sure there will be things that suck, there always are. But for right now, I don’t think there is a job I would like more, is better for my career, or can best maximize my talents.

I am very excited about starting at Infusion today, helping them kick ass and take names. 😉

Wish me luck.

July 12, 2010 Posted by | Uncategorized | 3 Comments

Outsourcing Dilemma

I just read a post on outsourcing, and think they nailed it when they said “it’s easy to imagine a project turning into a game of telephone”.

I’d love to be able to outsource, but in the dynamic world of software development, the person you outsource to needs to be proactively thinking about all situations, they need context, and be focused on quality. And quite frankly, I think most people you outsource to, will view it as a transaction, where they do the minimum amount of work possible. Then you have to figure out what they didn’t think of and start a new transaction to get it done … this could go on indefinitely.

To be fair, I’m an outsourcing consultant and communication failures between the client and myself are hardly fun and they cost me money. However I do make an extra effort to meet their needs so the project works for them since I see every transaction, not as a one night stand, but as one link in a chain of a long marriage.

Outsourcing as a software developer then turns into a huge specification, review, and testing job, where the communication of the other persons work is more work than actually doing it yourself.

Personally, I’ve come to the conclusion that for now at least, I should turn down work that I can’t personally manage … although I’m still struggling to figure out a way to make this work.

Got any ideas?

March 26, 2009 Posted by | Uncategorized | Leave a comment

   

%d bloggers like this: