The Secret Life of Connection Strings In Oracle! (Oracle DBA's Hate Me.)

I don't know if you've ever had to work with Oracle, but if you have, and if you're primarily a .net developer like me, I wonder if your experience is anything like my own.

The very first thing you do with Oracle, as a developer, is to connect to it. And you soon discover that the way connections are specified in Oracle is batshit insane.

With a SQL Server connection string, there are a couple of quirks*, sure. But essentially, the connection string holds all of the details that are used to connect. Every detail you could possibly want goes into the connection string.

(* Regarding connection strings. I've always wanted to register the site "www.FuckingConnectionStrings.com" which simply redirects to "www.ConnectionStrings.com" but is much much easier to remember.)

In Oracle, a connection string is just the first mirror in a long hallway full of mirrors. A hallway that leads into a maze of mirrors, that takes you to a forest filled with monsters and more mirrors.

We had an issue recently where a connection string that worked perfectly well on 7 different machines did not work on an 8th machine. When we looked into how the connection worked on those first 7 machines, we found it was different on every single one. When we took this knowledge to the 8th machine, we began to feel a terrible sense of foreboding. The further into the issue we looked, the more terrified we became. Eventually the authorities found our skeletal remains at the bottom of a steep ravine clinging to a well thumbed printout of Oracle documentation.

Here is how you usually connect to Oracle:

You follow some simple instructions, and you get some generic error messages. You're not panicking, you're just a little out of your depth. Eventually some oracle DBA scoffs into the room (that's the way they walk, they scoff as they walk) pushes you off your keyboard and does some magic incantations in front of your computer. You keep looking over their shoulder, trying to learn about this strange magic. You see strange registry keys, shocking environment variables, keywords like "tnsnames.ora" and some other disturbing flashes of technical wizard-pokery. Was that LISP!? For a moment you get one sickening glimpse into the infinite pit of sorrow.

After that it just works, and you soon forget how tricky it was, you confine it to a dark corner of your psyche. The rest of your experience with Oracle is never as daunting as that first connection. But by then the Oracle DBA has cemented their role as an irreplaceable technical God.

Well I'm here to blow the lid open on their whole racket.

I'm here to teach you all the things they don't want you to know. I'm going to expose their little shitshow and all of its pathetic quirks.

Get ready.

In an Oracle connection string, the Data Source attribute is used, not for connecting to remote machines, but for searching your local machine. I'll give you an example. If the connection string looked like this:

User Id=scott;Password=tiger;Data Source=bbSales12

What do you think happens with that Data Source value? (bbSales12)

Here's what happens....

The value of Data Source is an Alias. And Oracle wants to Resolve that alias.

According to the documentation, Oracle first looks in "the connection pool" to see if it has already "Resolved" that alias.

Given that, at this point, we haven't started talking to any remote machine, we must be referring to some kind of local connection pool. Probably some kind of in-memory dictionary. Documentation is sketchy of what that is. But let's press on. Ignore that bit.

Assuming we haven't resolved this alias before, then we need to look at tnsnames.ora.

What is tnsnames.ora I hear you ask?

It is the third mirror. It is magic. It is a file that contains enough configuration detail that your machine should be able to connect to the remote machine.

It is likely to be this file that the administrator was futzing with. (Or some other things that will be explained shortly)

First up: how does Oracle find the tnsnames.ora file?

To find this magical file, Oracle relies on an environment variable, TNS_ADMIN. That is the second mirror.

If you don't have this environment variable set, then Oracle will not be able to find the tnsnames.ora file (or the sqlnet.ora file, which will be described later)

With powershell you can look at all your "environment variables" like this:

Get-ChildItem Env:

Get-ChildItem has these two aliases that you might find more comfortable: dir, ls,

So you can type

dir env:

Or

ls env:

And to see value of the TNS_ADMIN environment variable, type $ENV:TNS_ADMIN

Now, assuming you have that variable set, it will show you a path. Follow that path, and look for a file named tnsnames.ora

(Some people will tell you that tnsnames.ora is located in ORACLE HOME\NETWORK\ADMIN. This is confusing advice that you can ignore. More on that later)

Was tnsnames.ora there? Good. Now look inside. It is a text file. So you open it with sublime or vs code, or whatever the cool kids are using at this time of day.

You should find the alias defined via a piece of configuration language like this:

bbSales12=
 (DESCRIPTION= 
  (ADDRESS= (PROTOCOL=tcp)(HOST=bb12Sales-server3)(PORT=1521))
  (CONNECT_DATA= 
     (SERVICE_NAME=sales.us.acme.com)))

What is that language? It looks a bit like lisp, or a bit like JSON. It sounds a lot like Greenspun:

Any sufficiently complicated program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
Greenspun's Tenth Rule

I haven't been able to find any official name for this syntax, other than "the syntax of the .ORA files". It's used by Listener.ora, tnsnames.ora, sqlnet.ora, protocol.ora and tnsnav.ora

(By the way, whitespace is insignificant, but recommended.)

Now -- what if you don't have a tnsnames.ora file? You could of course go ahead and create one, then try and put the right protocol, host, port and servicename into it.

But there's another intriguing possibility.

Way back at the start of the process, inside your connection string, you can embed one of these weird lisp-like strings directly.

Instead of

User Id=scott;Password=tiger;Data Source=bbSales12

You can have:

User Id=scott;Password=tiger;Data Source=
    (DESCRIPTION= 
        (ADDRESS= (PROTOCOL=tcp)(HOST=bb12Sales-server3)(PORT=1521))
        (CONNECT_DATA= 
        (SERVICE_NAME=sales.us.acme.com)))

How's that for a snappy connection string that rolls off the tongue. In official parlance what we've done is add a "connect descriptor" to our connection string.

Now you must be wondering.... what other syntaxes can this Oracle connection string withstand?

The answer is "pretty much all of them."

As an attempt at irony the wise folk of Oracle invented something called "the Easy Connect Naming Method".

This lets you specify a Data Source like this:

//host:[port]/[service_name]

For example:

"user id=scott;password=tiger;data source=//bb12Sales-server3:1521/sales.us.acme.com"

Now that looks much simpler doesn't it? Why am I stating that this is ironic? Because of this little footnote from the documentation:

Prior to using the easy connect naming method, make sure that EZCONNECT is specified by the NAMES.DIRECTORY_PATH parameter in the sqlnet.ora file.

This introduces the "sqlnet.ora" file which is also searched for in the TNS_ADMIN folder.

Inside that file, if you specify

NAMES.DIRECTORY_PATH= (TNSNAMES, EZCONNECT)

...then you'll be able to use "the Easy Connect Naming Method".

If you're brave you can also add LDAP and ONAMES into the mix. But that's outside the scope of this rant.

This EZCONNECT syntax really is a bit easier than the alternatives, so I don't think their attempt at ironic naming was a complete success ;).

Oracle Home.

Now I mentioned that some people will tell you that tnsnames.ora is located in ORACLE HOME\NETWORK\ADMIN

I'll cover what that means, but first I have to tell you, No! tnsnames.ora is not necessarily located in ORACLE HOME\NETWORK\ADMIN. It can be located anywhere! That's what the TNS_ADMIN environment variable is for: for allowing it to be located anywhere, irrespective of where "ORACLE HOME" is. (Your friendly system admins may locate tnsnames.ora on a network drive where it is easier to administer en-masse.)

Now what is this "Oracle Home" business, hmm?

That is the third mirror down the first hallway on the right. It is a dead end. But I will tell you about it anyway.

When (or rather "if, against all common sense") you installed Oracle, it will have been installed into a location that it refers to as the "Oracle Home".

It's possible that there is an environment variable called "ORACLE_HOME". But failing that -- the value of "Oracle Home" may be stored in the registry.

Where in the registry, exactly? Any one of these places my poor dear friend.

HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE
HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\HOMEID
HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\ALL_HOMES
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services

But as long as TNS_ADMIN is set correctly, you don't need to worry about those registry entries, or the absence of an ORACLE_HOME environment variable.

Okay, that's all I wanted to say about Oracle connection strings. In order to cleanse your mind of this horrible mess, I suggest you learn about how .net locates an assembly at runtime or look into error handling with asp.net MVC.

 

The little known beauty of .ensure files

Ever broken the build?

Everyone who uses a revision control system has, at some point, accidentally committed a file that should never have been committed. For example, a ".dll" or a dreaded ".suo" file.

To save us from this entire category of problem, the ".ignore" concept was created, and implemented as a built-in feature of many revision control systems (for example ".gitignore" and ".hgignore")

Similarly: everyone has, at some time, forgotten to "commit" a file that other parts of the code relied upon. Thus, the build has been broken, time and time again. Throughout the world, builds are breaking at this very moment, for the lack of some file that could've so easily been committed.

There is a widespread but little known feature, as simple as the ".ignore" file, that addresses this problem.

If you're not familiar with it, let me tell you about the ".ensure" file.

A ".ensure" file informs the repository of a few basic standards of file hygiene that must be achieved before a push can be issued.

Much like a ".ignore" file, You can copy a ".ensure" from some one else's similar project into your own, and everything will just work.

You can copy a ".ensure" from some one else's similar project into your own, and everything will just work.

For example, if someone else has written a C# based project, where the ".ensure" file says, in effect:

"If there is a '.sln' file in the repo, each referenced project file must also be part of the repo 
"If there is a '.csproj' file in the repo, each referenced file or resource must also be part of the repo

...then you can copy their ".ensure" file into your own C# project, and you will be protected against so many entirely predictable build breakages.

Admittedly, the syntax of a ".ensure" file is a little baroque, cramming up to four "selection" DSL into the one handy file format (regex, xpath, jsonquery and globs).

But most people never write them: they simply copy them from place to place. (Copying files comes naturally to developers at every rung of the ladder.)

And testing the effectiveness of a given ".ensure" file is very easy (you just try to break the build with a missing file). So the community easily converged on a working set of files for any given project type.

The only problem that anyone has with ".ensure" files, is that they are something Leon simply dreamed up, five minutes ago, while doing the dishes. Implementations are yet to exist.

I for one say: go for it.

 

The Laggard's Guide to Getting Started with Asp.net 5 Using Yeoman.

Note: Asp.net 5 has been renamed to ASP.NET Core 1.0. The instructions below are unaffected.

Asp.net 5 represents a complete re-invention of the .net platform. The foundations have shifted completely. Throw out everything you ever thought you knew about computers, and get ready to have your hair blown back, your face melted and your whole outlook on life re-defined.

If you are anything like me then you've let much of this stuff zoom past you over the last year. Well it's time to get up to speed my friend.

Here are the steps to get up and running with Asp.net 5, using Yeoman. In summary:

  1. Optional: Install vs2015 including "Microsoft Web Developer Tools"
  2. Install Asp.Net 5
  3. Use dnvm to upgrade your dot net versions and set active version
  4. Install chocolatey
  5. Install node.js, using choco (in order to get npm)
  6. Install visual studio 'code'
  7. Install yo, using npm
  8. Install generator-aspnet, using npm
  9. Generate a new site, using 'yo aspnet'
  10. Visit the new site in vscode
  11. Restore all nuget packages for the project
  12. Serve the website locally using dnx web
  13. Browse to http://localhost:5000

And now I'll go through them in slightly more detail -- but still very quickly.

If you want to go through this in a more comprehensive manner, I recommend this pluralsight course "Building a Web App with ASP.NET 5, MVC 6, EF7 and AngularJS by Shawn Wildermuth".

1. Optional: Install vs2015 and make sure to include "Microsoft Web Developer Tools"

We're not going to be using vs2015, but using vs code instead. Still I expect the installation gods will devour your soul if you don't install vs 2015 before installing asp.net 5. So consider doing this first.

  1. Install Visual Studio 2015
  2. And be sure to specify that you want the "Microsoft Web Developer Tools"

Come back when you have that done, a few hours later. If you're living in Australia where our "internet" is made from a few frayed old copper strands, add one extra month.

2. Install Asp.Net 5

This step, and all subsequent steps, are mandatory.

  1. Go to Get.Asp.Net
  2. Click on "Install For Windows".

If you're not on windows, then follow for the other instructions.

Alternatively, read the detailed install instructions.

3. Use dnvm to upgrade your dot net versions, get new versions and set active version

Once asp.net is installed, you can run dnvm. dnvm is a commandline tool for managing which version of the .NET runtime to use.

At a command prompt, type each of these commands in turn:

dnvm upgrade
dnvm install 1.0.0-rc1-final -arch x64
dnvm install 1.0.0-rc1-final -r coreclr 
dnvm install 1.0.0-rc1-final -r coreclr -arch x86
dnvm use 1.0.0-rc1-final -r clr -arch x64 -p
dnvm alias default 1.0.0-rc1-final -r clr -a x64
dnvm list
dnx --version

These commands, in order, will:

dnvm upgrade:
Install the latest 32 bit version of the runtime, make it active and assign the default alias to point at it

dnvm install 1.0.0-rc1-final -arch x64:
Install the 64 bit version of the common language runtime (clr)

dnvm install 1.0.0-rc1-final -r coreclr:
Install the 32 bit version of the "core" clr

dnvm install 1.0.0-rc1-final -r coreclr -arch x86:
Install the 64 bit version of the "core" clr

dnvm use 1.0.0-rc1-final -r clr -arch x64 -p:
make the 64 bit clr "active", and persist this setting. (i.e. not just for the life of this console session)

dnvm alias default 1.0.0-rc1-final -r clr -a x64:
apply the alias "default" to the 64 bit clr.

dnvm list:
list all of the dot net versions.

dnx --version:
show the version of the dot net execution environment.

You don't need to install all of those clr's. But knowing that you can is fundamental to the shift that is occurring.

(I don't know why .net is now back at version 1.0. Perhaps they forgot to drink their ration of the semantic versioning kool-aid.)

4. Install chocolatey

You may already have chocolatey, in which case, skip this step. Otherwise. From a console window with administrative privileges type:

@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin

Chocolatey is now installed on your machine and available in your path.

If you're using an OS other than windows you probably have a package manager already, with which to perform the next step.

By the way: Rob Reynolds who maintains Chocolatey: what a hard worker! Thanks for your efforts Rob!

5. Install node.js, using choco (in order to get npm)

Use chocolatey to install node.js. We're doing this in order to get npm, so that we can get yo, so that we can generate aspnet projects at the commandline.

Yes -- it is a convoluted process. But don't let that stop you. Just do it once and get on with your life.

Here's a simplified diagram that might help:
rube.jpg

Sorry, wrong diagram, this is the one I meant:

get_yo.png

choco install nodejs

You now have node.js and with it, npm.

At this point you can add "rockstar web developer" to your json resume.

To test out node.js, type "node" in a console. Now enter:

console.log("hello from node");

And you should see "hello from node" echoed to the screen. Exit node with ".exit". Or by hitting Ctrl-C a few times.

6. Install Visual Studio 'Code'

Visual Studio Code is a cross-platform code editor optimized for building and debugging last year's crop of web and cloud applications. In 6 months time it will probably be abandoned on the side of the information superhighway, but for now it works beautifully.

  1. Install VSCodeSetup.exe

Or follow the windows install instructions.

7. Install yo (using npm)

yeoman-character-sticker.c30c59fb9e.png

yo is the scaffolding tool that is part of Yeoman

You install it using npm (the nodejs package manager) that was installed as part of node.js, above.

npm install yo -g     

The "-g" here means: 'global'. So it is available from future console sessions. Not unlike the "-p" above.

8. Install generator-aspnet (using npm)

yo's purpose is to "scaffold" applications, i.e. construct the bare-bones of an application, that serve as a starting point for further development. Think 'Dos On Dope', but with slightly larger concept count.

Yo doesn't actually do the generating itself -- it gets "generators" to do all the actual work. This seems like a pretty good deal for Yeoman who outsources all of his work but still takes the credit. For example, to scaffold an aspnet5 application yo needs the aspnet generator. Download it, using npm, like this:

npm install -g yo generator-aspnet

Now you have all of the pieces in place and can build a website. Try not to burst with the anticipation that pushes all of your blood into your head.

(To see a list of yeoman generators available from npm, type: npm search yeoman-generator, and to see the generators installed locally type: yo --help)

9. Generate a new site (using 'yo aspnet')

Simply type:

yo aspnet

Press "3" to select "Web Application".

Type the name of the name application, e.g. "HelloFromYo".

Press enter. A whole lot of very interesting text will scroll by in a flash.

At the end of it, you'll have a directory structure like this:

HelloFromYo
    +---Controllers
    +---Migrations
    +---Models
    +---Services
    +---ViewModels
    ¦   +---Account
    ¦   +---Manage
    +---Views
    ¦   +---Account
    ¦   +---Home
    ¦   +---Manage
    ¦   +---Shared
    +---wwwroot
        +---css
        +---images
        +---js

hellofromYo.png

10. Visit the new site in vscode

from the console type:

cd HelloFromYo
code .

And visual studio code will open the current folder.

Otherwise, from within vscode choose "open folder" and navigate to the newly created website's root folder.

11. Restore all nuget packages for the project

Within Visual Studio Code, navigate to any of the code files within the project.

VS Code will soon wake up and notice that the relevant nuget packages are not available.

A note at the top of the screen will say:

"There are unresolved dependencies from 'project.json'. Please execute the restore command to continue."

Click "Restore."

This will call the dnu commandline utility, to download the nuget packages used by the project. (You could've performed this yourself using 'dnu restore' and 'dnu build')

It will download something like 265 nuget packages. Because "componentization" is the special something that has been missing from your life all this time.

Your generated web app is now ready for launch. Be excited.

12. Serve the website locally, using dnx web

From the console, while in the root folder of the webapplication, type:

dnx web

This will launch the kestrel webserver, hosting the website. You will see from the console the exact port number, but it will probably be 5000.

Why does it launch the kestrel webserver? Because in the file "project.json" under "Commands" it says the Web command uses "Microsoft.AspNet.Server.Kestrel". I mention that, just to give you a taste of the magic.

magic.gif

13. Browse to http://localhost:5000

Browse to http://localhost:5000 to visit the site.

And there it is, in all its stupendous glory.

Now sit down and have a long think about the good old days, when all you needed to do to get into a development environment was restart the Commodore 64 and type '10 print "hello"[enter]20 Goto 10[enter]Run'.

Then stop thinking about all that and start looking through the code to see how it works. There's some fascinating magic in there to be sure.

Better learn it quick, before it's time to throw it all out again and rewrite everything in $NextLang.

 

Persistence Framework for Accepting Nonsense.

This site uses cookies. By browsing this site you accept that the site uses cookies.
—One million different sites.

I GET IT.

"Unauthorised reproduction of this DVD is bad. Opinions expressed do not represent the views of the yada-yada, and you can't show this DVD on a prison oil-rig etc."
—Every time I put in a new Breaking Bad DVD.

I GET IT.

There should be just one check box.

Do you get it?
Don't ever ask me again. EVER!

Perhaps at the moment someone turns 18, a representative of the bureaucrat-class could show up at your house with a clipboard and ask:

"Do you get it?"

You sign something, just once, and the lawyers can leave you alone with their repetitive questions.

Perhaps if you paint the top of your door frame with lamb's blood, they'll just tick the "Gets it" box and move on without interrupting your day.

I can't fix the DVD problem, but the cookies on the web problem is easy to fix.

At a browser level, we, the computer peeps, could agree on a micro-format. It's about time we did something useful with the semantic web!

Those nagging cookie questions could have an attribute, a data-cookie-nag='true'. And if, in about:settings you've set the flag for 'I accept that the internet uses cookies and i know how to inspect and or disable them', then the browser will quietly delete that node from the DOM. (Or if it's a form with a single checkbox, check it and submit it).

I see no possible problems or roadblocks to this idea being immediately implemented world wide.

It starts with you. If you are being forced to implement one of those cookie-nag checks, then you can add the micro-data today. There's nothing stopping you.

Once you've done it, you can inform me here:

Once it's implemented on 1000 sites, I'll let you know, so you can patch chrome and firefox to take advantage of it.

 

Buying a car: A text adventure game.

You are standing on the street. Before you there are three car dealerships, with a sign on top of each one. One says:
"MAZDA", one says "HYUNDAI", one says "SUBARU".

> Walk HYUNDAI

You walk into the Hyundai dealership.

> Look

You look around. There is a car and a car salesman.

> Look car

You move toward the car, hoping to look at it. The car salesman detects your presence and moves between you and the car.

"Hi there, I see you're looking for a car there. Got any questions, just ask me!" 

The salesman blocks your path.

> Ask Salesman.

The salesman doesn't know what you are asking.

> Ask salesman about car.

The salesman doesn't know anything about "car"

> Ask salesman about price.

The salesman doesn't know anything about "price"

> Ask salesman about car.

The salesman has no fricking clue as he is a complete idiot.

> Leave.

You leave the car dealership.

You are standing on the street. Before you there are three car dealerships, with a sign on top of each one. One says:
"MAZDA", one says "HYUNDAI", one says "SUBARU".

> Quit

You cannot quit. You must acquire transport.

I hate this game.

 

Dungeon.css: Dungeon Generator

I've been writing about Random Dungeon Generators over at wiki.secretGeek.net, and as a consequence, ended up putting a new random dungeon generator online.

Dungeon.css: Dungeon Generator

dungeon example

The layout of the maps is generated by a C# library, Karcero, by Oded Welgreen. A very nice library if you need to generate dungeons or mazes. (And why wouldn't you need that, hmm?)

The idea of the page I've put up is that you can choose a theme, and customize the background colors. Themes are entirely based on css. The maze is just a HTML table, with classes and styles.

Each theme is a style sheet, a pair of default colors, and a few gray scale pngs (with transparent backgrounds).

It's very easy to add a new style, or to improve an existing style.

Rock for Dungeon theme Door for Dungeon theme Wall for Dungeon theme Rock for Castle theme Door for Castle theme Wall for Castle theme Rock for Forest theme Floor for Forest theme Wall for Forest theme Rock for SpaceBase theme Floor for SpaceBase theme Door for SpaceBase theme

If I was really serious, I could've gone with SVG. Then *all* aspects of the color and scaling could be configurable through css. But it would also be trickier to author the pictures, for someone like me (with my MVP in "MS Paint").

The Cicada Principle

In the SpaceBase and Dungeon themes, I want a few randomly selected background tiles to use a different image, to give the rocks or space base a more natural feel. CSS doesn't currently include any kind of random function, so I used 'the cicada principle', whereby a set of nth-child rules with varying prime numbers will select a random-looking set of tiles. To wit:

tr:nth-child(13n)    td:nth-child(11n-10).rock { background-image:url(image/rock_geo2.png)}
tr:nth-child(7n-3)   td:nth-child(17n-12).rock { background-image:url(image/rock_geo2.png)}
tr:nth-child(17n)    td:nth-child(23n).rock { background-image:url(image/rock_geo2.png)}
tr:nth-child(7n)     td:nth-child(13n).rock { background-image:url(image/rock_geo2.png);}
tr:nth-child(19n-12) td:nth-child(21n).rock { background-image:url(image/rock_rare.png);}
tr:nth-child(5n)     td:nth-child(7n).rock { background-image:url(image/rock_geo.png);}
tr:nth-child(7n)     td:nth-child(5n).rock { background-image:url(image/rock_geo2.png);}
tr:nth-child(10n)    td:nth-child(10n).rock { background-image:url(image/rock.png);}

Finally

Note that depending on the parameters you choose, you can also generate complex, unthemed mazes.

complex_maze.png

Anyhow, the online dungeon generator is here, and the source code is available here.

 

Mercurial Workflow with Branching

I've written a bit about mercurial over the years, but never once mentioned branching. This is my basic guide to branching (I'm a basic kind of person) and it's based on a short email I wrote myself a long time ago, which I still occasionally rely upon. If you're unsure about anything, simply test it for yourself.

Our story begins, some happy day, when you are working very hard, in an existing repository. I don't know or care how you got the repository. Maybe you created it (with 'hg init') or you copied it (with 'hg clone').

Now, as you are working you realize that you want your current work to be put into a branch, to keep it out of harm's way.

Let's say you are working on a feature called WEAPONISE_SPACE, and you don't want to accidentally release it before the public have been warned. A branch is a nice place to perform your work, away from the boring, safe work the rest of your team are engaged in.

> hg branch WEAPONISE_SPACE
marked working directory as branch WEAPONISE_SPACE
(branches are permanent and global, did you want a bookmark?)

And obviously, if you are brave enough to weaponise space, you are brave enough to create branches that are permanent and global. If you do regret the 'hg branch {branchname}' command, then it can be immediately undone with 'hg branch -C' (or 'hg branch --clean').

The beautiful thing here is that you didn't need to create the branch before you started writing code. You just need to decide before your first commit.

Now when you commit, you will be committing on that branch:

> hg commit -m "Adding a small laser to the space shuttle"

When you try to push your work, let's see what happens...

> hg push
pushing to https://Nasa.gov/repo/shuttle
searching for changes
abort: push creates new remote branches: WEAPONISE_SPACE!
(use 'hg push --new-branch' to create new remote branches)

This is good... maybe you wanted to finish working on that feature before you pushed it? Mercurial won't let you accidentally push out a new branch. But it is an OK thing to do, if you want someone else to help you with your work, so let's use the new-branch option:

> hg push --new-branch
pushing to https://Nasa.gov/repo/shuttle
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 4 changes to 4 files

Now, your comrade Veronika is helping you weaponise space, as she is a specialist in weapon targeting systems.

She can see that the repository now has two heads (the default branch, which she last worked on, and your new branch)

> hg heads
changeset:   35:099848c53b6c
branch:      WEAPONISE_SPACE
tag:         tip
user:        laserKid
date:        Thu Sep 17 16:19:44 2015 +1000
summary:     further improvements to laser

changeset:   34:c836214f7238
user:        Veronika
date:        Wed Sep 16 17:04:16 2015 +1000
summary:     initial

She gets all changes:

> hg pull
pulling from https://Nasa.gov/repo/shuttle
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
(run 'hg heads' to see heads)

Now Veronika has your code on her machine, but not in her working folder, as she hasn't 'update'd yet. If she's unsure what branch she's currently on, she can easily check:

> hg branch
default

She's still on default. If you haven't branched, then you are in the "default" branch.

How do we switch to the branch of our choice? We update to it...

> hg up WEAPONISE_SPACE
4 files updated, 0 files merged, 0 files removed, 0 files unresolved

If she had some local uncommitted work (or uncommitted merges) she would've gotten one of these two error messages:

abort: crosses branches (merge branches or use --clean to discard changes)
abort: outstanding uncommitted merges

...and she would've needed to commit her current work to default (or revert it)(or commit it to a different new branch, I guess), before proceeding.

Now, she's updated to the WEAPONISE_SPACE branch, she can work on it, commit her changes, and push:

> hg commit -m "Added a targeting system to LaserKid's laser."
> hg push

Back on your machine, you can pull Veronika's changes and update your local files.

> hg pull
...
> hg up
...

The manager rushes into the room and tells you that global tensions have increased and we must weaponise space TODAY! There's no time to inform the public, or test the lasers! You must ship the code as is! So, with a heavy heart, you close the branch, using a commit just for this purpose. (This is an optional step... but good repo hygiene! Any subsequent work on that branch will automatically cause it to reopen.)

> hg commit -m 'Finished Weaponising Space!' --close-branch

Now you want to merge that feature into the default branch. First, switch back to the default branch. Then merge the feature branch in. After that you can commit the merge and push it to the world.

> hg up default
...
> hg merge WEAPONISE_SPACE
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
> hg commit -m "Space... get ready for Weapons!"
> hg push
pushing to https://Nasa.gov/..

The rest, of course, is written in the history books. The targeting systems were good, but the laser intensity was poorly calibrated. Two continents were destroyed in the ensuing war.

Live fast, die young, leave a well-maintained repo history.

For extra credit....

When pushing, *only* push the current branch (the branch you are working on)

hg push
> hg push -b .

So, when pushing a new branch...

hg push
> hg push -b . --new-branch

When committing, include the A option (short for '--addremove') to ensure you're adding any new files.

hg commit -m "My commit message"
> hg commit -Am "My commit message"

(Make sure your .hgignore file is fully fleshed out, so you are not committing new files that don't belong in the repository)

For fullest extra credit... when pulling in other people's changes, if you want to merge them with your local changes, without creating spurious extra commits, use the --rebase option (requires the rebase extension in your mercurial config)

> hg pull -b . --rebase

To enable the rebase extension, you need to... actually, I've written all about that in this guide to mercurial extensions so just go there.

Summary/Cheatsheet

Command Comment
hg branch Check which branch you're working on
hg branch WEAPONISE_SPACE Create a branch
hg push -b . --new-branch Push only the current branch (which happens to be new)
hg push -b . Push only the current branch
hg heads See if there are any open "heads" (e.g. open branches)
hg pull -b . --rebase Pull just the current branch
hg pull -b WEAPONISE_SPACE --rebase Pull only the contents of a named branch (and merge any local commits you've already made)
hg up WEAPONISE_SPACE Switch to a named branch. So this will update the current working folder to contain the work from the named branch.
hg commit -Am "committing here!" Commit to the current branch, while addremoving files
hg commit -m 'Finished Weaponising Space!' --close-branch Close the current branch (good hygiene)
hg up default Update so that the current branch is now the default branch
hg merge WEAPONISE_SPACE Merge a named branch into the current branch

External Links

See Also

 

Fixing Keyboard Functionality on MacBook Air after Upgrading to Windows 10.

Full title: "Fixing Function Key Support (pageup, pagedown, home, end, print screen, delete, etc.) on MacBook Air running Boot Camp, after Upgrading to Windows 10.

I upgraded my MacBook Air from Windows 8.1 to Windows 10. Everything seemed good. A little *too* good. Even the trackpad seemed to work. Then I realised there was one terrible terrible problem. The 'fn' key had stopped working. That handy dandy little sucker that unlocks the power of Page Up, Page Down and so on -- was now just a useless obsolete chunk of good for nothing.

I soon worked out the solution, and since I don't see it documented elsewhere on the inkernets, I'd better publish the deets myself.

  1. Download the latest Bootcamp files, from here: Boot Camp Support Software 5.1.5640
  2. Unzip the file
  3. In the resulting folder: "\BootCamp5.1.5640\BootCamp\Drivers\Apple", find "AppleKeyboardInstaller64.exe".
  4. Install that.
  5. Reboot.
  6. Use Page Up, Page Down and so on, all day long.
 

The Spectator Sport of Vendor Topping

Salesmen representing five different software vendors shuffled nervously into the room, viewing one another with thinly veiled enmity. They'd each received my meeting invite with details of the solution required, and a few choice hints that our budget was in the millions, but didn't realize they'd be pitching at the same time as one another.

The first vendor approached the white-board and nervously drew a simple square.

an_application.png

He explained that his application completely fulfills *all* of the requirements.

The next vendor immediately interrupted and drew a series of boxes next to that box.

a_series_of_applications.png

He explained that his *series* of applications solves not just today's problems but anticipates tomorrow's challenges.

Before he said another word, the next vendor took a pen and drew a box right around all of the existing boxes.

an_application_suite.png

He explained that his solution is not just a series of applications, but a *suite* of application that perform seamlessly together in an orchestrated...

Before he could finish the next vendor had drawn a box under that box.

a_platform_offering.png

He explained that his suite of applications are part of a *platform*. The platform provides *robustness* that the others lack.

Before he could continue the next vendor seized a white-board marker and drew a big box around the outside of all the other boxes.

a_managed_platform.png

He explained that his suite of applications come with a platform that is part of a fully *managed* service, with SLAs and compliance that other vendors can't hope to match.

By this time, all hell had broken loose and the vendors were openly brawling in the middle of the room. Arms, legs, fists and white-board markers were flying everywhere. One vendor was slamming another vendor's head against the floor and another was being forced to eat a whitepaper.

The fighting continued for several minutes before a security guard, Kev, entered the room, asked all of the vendors to leave and then he asked me how I managed to infiltrate the building and the meeting-room booking system, at a company I left five years ago.

My hobby? Physical infiltration. Watching software vendors try to top each other is just an added bonus. Kevin and I wiped the white-board clean for the 3rd time that week.

 

Which Uri Encoding method should i use in C#/.net?

This too is one of the boring "factual" posts. Sorry Lachlan.

I never know which .net uri encoding (or url encoding?) method to use in any given scenario.

So I've built this informative lookup table you can use, whenever you're wondering what sort of encoding to apply to a string that is part of, or all of, a URL. Or URI, url, Url, Uri, or uri.

(This is exactly the sort of fun stuff you need to think about constantly when writing a brilliant and much-loved tool like NimbleText, so that hopefully, sometimes, just sometimes less other people have to worry about it.)

uri_encoding.png

↑ That was a picture, which you can download and print out. ↓ This is a HTML table which you can download, view source and edit.

Character Url.Encode(s) HttpUtility.UrlEncode(s) HttpUtility.UrlPathEncode(s) Uri.EscapeDataString(s) Uri.EscapeUriString(s)
{space} + + %20 %20 %20
{tab} %09 %09 %09 %09 %09
{\r} %0d %0d %0d %0D %0D
{\n} %0a %0a %0a %0A %0A
{\0} %00 %00 %00 %00 %00
~ %7e %7e ~ ~ ~
! ! ! ! ! !
@ %40 %40 @ %40 @
# %23 %23 # %23 #
$ %24 %24 $ %24 $
% %25 %25 % %25 %25
^ %5e %5e ^ %5E %5E
& %26 %26 & %26 &
* * * * * *
( ( ( ( ( (
) ) ) ) ) )
_ _ _ _ _ _
+ %2b %2b + %2B +
{ %7b %7b { %7B %7B
} %7d %7d } %7D %7D
| %7c %7c | %7C %7C
: %3a %3a : %3A :
" %22 %22 " %22 %22
< %3c %3c < %3C %3C
> %3e %3e > %3E %3E
? %3f %3f ? %3F ?
` %60 %60 ` %60 %60
[ %5b %5b [ %5B %5B
] %5d %5d ] %5D %5D
\ %5c %5c \ %5C %5C
; %3b %3b ; %3B ;
' %27 %27 ' ' '
, %2c %2c , %2C ,
. . . . . .
/ %2f %2f / %2F /
' %27 %27 ' ' '

Remember kids: Cool URIs don't change. People do.