samsalisbury.net

RepositoryHosting.com backup script

by on Mar.07, 2010, under Scripts

We all know the importance of backups, and backups of backups, but even some of the most respected developers occasionally fail to keep their stuff backed up, as we recently learned… So here’s my tiny contribution to the subject, specifically for anyone using the amazingly low-cost RepositoryHosting.com for their SCM (it supports Git, Mercurial and SVN) and project management (by way of Trac).

Whilst RepositoryHosting.com already offers automatic copying of backups to an Amazon S3 bucket, it’s always best to not rely on a single backup location, as many stories confirm. In this light, I whizzed up the following script which you can use to pull your backups down to your local workstation or office file server. It’s a bit rough around the edges, and doesn’t handle errors, so you will have to check the results after each run, but it sure beats logging in and manually downloading the files through your browser each day… (Thanks to Matt White at RepositoryHosting.com for changing the backup URLs to make them more predictable, and suggesting the initial idea that spawned this script.)

This script relies on a Bash shell with cURL available, like the one that comes with msysgit if you’re using Windows. You’ll also need to make sure you set up daily backups in your RepositoryHosting.com project’s settings.

I’m no expert on Bash shell scripting, so if you can think of any improvements (to make the script more concise or handle errors etc.) then please feel free to educate us all in the comments!

Here’s the script… (I named it “backup.sh”). See the comments in-line for configuration instructions.

#!/bin/sh
# RepositoryHosting.com backup download script
#
# This script downloads your daily RepositoryHosting.com backups.

##################################################################
##
### Configuration
##

# Subdomain of your repository
RepoSubdomain=mysubdomain

# Local backup directory
BackupDir=~/Backups/RepositoryHosting.com

# RepositoryHosting.com administrator credentials
Username=myadminusername
Password=mypassword

# List of project names (for naming the backups, the order of these
# equate to the project IDs in your RepositoryHosting.com account.)
ProjectNames=(
	MyProject
	CoolProject
	OtherProject
)

# Backup date formats (e.g. "+%Y%m%d" for YYYYMMDD, or "+%d" for DD)
# NOTE: This affects the number of backups that will be kept, e.g.
# "+%d" will store a month's worth of backups, each numbered as the
# day of the month when it was created, "+%Y%m%d will keep an infinite
# number of backups, as each one will be uniquely dated.
#
# NOTE: This is used only if you specify @Date in the filename format.
#
FileDateFormat="+%d"

# Subdirectory date format (same as above, used only if you specify
# @Date in your SubDirFormat).
#
#SubDirDateFormat="+%m"

# Backup subdirectory format. If specified will place your backups into
# a separate subdirectory for each project.
#
SubDirFormat="@ProjectName"

# Backup file name format (e.g. "@ProjectName.@Date").
# This will automatically be prepended with the $BackupDir/$SubDir/ and
# appended with ".tar.gz".
#
FileNameFormat="@ProjectName.@Date"

##
### End configuration (no need to edit past here)
##
##################################################################

# Resolve URLs
RepoBaseUrl="https://$RepoSubdomain.repositoryhosting.com"
SessionStartUrl="$RepoBaseUrl/session"
ProjectsBaseUrl="$RepoBaseUrl/projects"

# Messages
echo -e "\nDownloading today's backups \n\tFrom:\t$RepoBaseUrl"\
"\n\tTo:\t$BackupDir"

# Cookie file name
CookieFileName="cookies.txt"

# Create backup directory if it doesn't exist
mkdir -p "$BackupDir"
# Go to backup directory
cd "$BackupDir"

# Login
echo -e "\nLogging in..."
curl -sS -X POST $SessionStartUrl -d \
"username=$Username&password=$Password" -c $CookieFileName -o /dev/null

# Copy project backups
ProjectNumber=0
while [ "x${ProjectNames[ProjectNumber]}" != "x" ]
do
# Specify & create backup subdirectory for this project
ProjectBackupSubdir=\
"${SubDirFormat//@ProjectName/${ProjectNames[ProjectNumber]}}"
ProjectBackupSubdir=\
"${ProjectBackupSubdir//@Date/`date $SubDirDateFormat`}"
test "x$ProjectBackupSubdir" != "x" && mkdir -p $ProjectBackupSubdir && \
ProjectBackupSubdir="$ProjectBackupSubdir/"
# Resolve backup filename
ProjectBackupFile=\
"${FileNameFormat//@ProjectName/${ProjectNames[ProjectNumber]}}"
ProjectBackupFile=\
"${ProjectBackupFile//@Date/`date $FileDateFormat`}.tar.gz"
# Copy project backup
echo -e "\nCopying ${ProjectNames[ProjectNumber]} backup to "\
"\n\t$ProjectBackupSubdir$ProjectBackupFile"
curl -L "$ProjectsBaseUrl/$[ProjectNumber+1]/backups/`date +%Y/%m/%d/00`" \
-b $CookieFileName -# \
-o "$BackupDir/$ProjectBackupSubdir$ProjectBackupFile"
ProjectNumber=$[$ProjectNumber+1]
done

# Delete cookies.txt
rm $CookieFileName

echo -e "\nFinished downloading backups!"

If you want to launch this easily in Windows, you can use a simple batch file…

REM Start the RepositoryHosting.com backup download script
C:
"\Program Files (x86)\Git\bin\sh.exe" -login -c /c/Users/Sam/backup.sh
Leave a Comment :, , more...

Automatic properties in VB.NET

by on Sep.01, 2009, under ASP.NET, VB.NET

The mind sometimes boggles at Microsoft’s implementation of VB.NET. One of these logic-defying mysteries is why VB.NET, in the .NET Framework 3.5, does not support automatic properties using similar syntax to C#. Automatic properties are a way to generate a private field, along with a two accessor methods (get and set) with independant levels of data hiding. They look like this in C#…

// A publically readable but only privately writeable string
public string aStringProperty { get; private set; }

The code above is expanded by the compiler to create a private string field, a private set method and a public get method, something that would look something akin to this (with the addition of having the ‘=’ operator overloaded to provide automatic calls to these accessor methods when necessary)…

// A publically readable but only privately writeable string
private string _StringProperty;
public string get_StringProperty { return _aStringProperty; }
private string set_StringProperty { _aStringProperty = value; }

However in VB.NET the situation is much worse! Look at this mess…

Private _StringProperty As String
Public Property StringProperty As String
	Get
		Return _StringProperty
	End Get
	Private Set(ByVal value As String)
		_StringProperty = value
	End Set
End Property

That’s right, 9 lines of code in VB.NET, that can be written using only 1 in C#. And VB is meant to be easier?!

What’s so great about properties anyway?

One of the main reasons I use properties as opposed to just public data members in my ASP.NET applications is that once you have defined a web user control with public properties, these properties added to the Intellisense database, helping you out when inserting the web user control into another control or page. This is mighty useful when writing a control that will be consumed by people who don’t know, or shouldn’t need to know the internal workings of the control. However, simple public fields, i.e. those declared in VB.NET thusly…

Public SomePublicField As String

…do not get this Intellisense treatment. They can be manually typed into the aspx code as attributes to the web user control, however without Intellisense to guide the way, many developers will thus assume that the attribute with this name is not available to them.

Maybe Microsoft should look into adding Intellisense support for such simple public data members? But I guess that’s something for the commenters to discuss! Even better would be proper automatic properties for VB.NET, as there seems to be something good about only exposing properly encapsulated properties to the aspx code.

3 Comments :, more...

Paginated ListView with ObjectDataSource minimal example

by on Aug.06, 2009, under ASP.NET, Minimal Examples

I’ve been wrestling with the ObjectDataSource control recently, and couldn’t find a minimal example of how it all hooks up, for simple, paged, read-only usage, anywhere. Therefore, I now present the paginated ListView with ObjectDataSource minimal example. Try to stay awake…

Introduction

In this minimal example, we will create an ASP.NET web form that contains a paginated (or “paged” in .Net parlance) ListView control, bound to an ObjectDataSource. In order to avoid any complex database access code mucking up the important part of this example, I’ll instead use the built-in collection of CultureInfo objects that come with the .Net Framework. So, the ‘business object’ collection we are displaying in the ListView will be a List<CultureInfo> (that’s a List(Of CultureInfo) for any VB.NET folk out there. Hopefully this will make for a semi-interesting tutorial…

Part 1: The markup

Of course, the first thing we will need is an aspx page containing the necessary controls for our minimal example — the important ones to include are ObjectDataSource, ListView and DataPager. Their opening tags are highlighted in the code below, note that the DataPager is inside the LayoutTemplate of the ListView. This doesn’t have to be the case, in which case you’d have to also provide the attribute PagedControlID to tell it which IPageableItemContainer compatible control to paginate.

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
	EnablePaging="true" OnSelecting="ObjectDataSource1_Selecting"
	TypeName="WebApplication1.MinimalObjectDataSourceObject"
	SelectMethod="MinimalSelectMethod" SelectCountMethod="MinimalSelectCountMethod" />

<asp:ListView ID="ListView1" runat="server" DataSourceID="ObjectDataSource1">
	<LayoutTemplate>Select page...
		<asp:DataPager ID="DataPager1" runat="server">
			<Fields><asp:NumericPagerField /></Fields>
		</asp:DataPager>
		<ul><li id="itemPlaceholder" runat="server" /></ul>
	</LayoutTemplate>
	<ItemTemplate>
		<li runat="server">
			<%#Eval("EnglishName") %> &mdash; <em><%#Eval("NativeName") %></em>
		</li>
	</ItemTemplate>
</asp:ListView>

We’ll refer back to this code later, important points to note for now are that

  • the ObjectDataSource has EnablePaging set to true and has its OnSelecting, TypeName, SelectMethod and SelectCountMethod attributes set, and
  • the ListView has its DataSourceID set to the ID of the ObjectDataSource.

Part 2: The enfunctioning

The ObjectDataSource control requires that you implement at least 2 functions (if you only want to READ data, as we are doing here). The first function, referred to as the SelectMethod, must return the IEnumerable of items for the page of data you want to display. The second, referred to as the SelectCountMethod should return the count of all the items in the data set. Not just the number on the first page! No no no! The number of items in total across all the pages! Yes yes yes! This often forces us to do some caching if we are to be efficient, however, we are dealing with an absolutely minimal example here, so I’ll leave that for a future article.

These methods must be public and can exist practically anywhere accessible to your ObjectDataSource. For the purposes of this article, they should also be static (that’s Shared for any VB.NET folk). In fact, this is not strictly necessary, but we are dealing with a simple, minimal example here, so we don’t need to worry about this for the moment.

SelectMethod (MinimalSelectMethod)
The SelectMethod must accept as arguments any parameters that you want to use to select your data (e.g. any search terms or filters you want applied), plus 2 more parameters: startRowIndex and maximumRows. These final two are used for paging. They must have the exact names I’ve used here, unless you want to specify custom names in the properties of the ObjectDataSource. Personally I’d just use the default names, if you’ve come this far it’s unlikely your class will be used to do anything else other than provide data to an ObjectDataSource.
SelectCountMethod (MinimalSelectCountMethod)
The SelectCountMethod needs to provide the total number of rows that should be returned by your query. Therefore, it also needs to be passed any parameters needed to filter the data. It doesn’t care about which page you want to view, so your custom parameters are all that’s needed.

So, here is a complete class exposing both of these methods (it also uses a helper method I’ve called “GetSomeKindOfList” to do the list filtering, it’s pretty simple so I won’t explain it here).

public class MinimalObjectDataSourceObject
{
	// A nice list for demonstration purposes.
	private static List<CultureInfo> baseList =
		new List<CultureInfo>(CultureInfo.GetCultures(CultureTypes.AllCultures));

	// Our minimal SelectMethod.
	public static List<CultureInfo> MinimalSelectMethod(
		string parameter1, string parameter2, int startRowIndex, int maximumRows)
	{
		List<CultureInfo> someList = GetSomeKindOfList(parameter1, parameter2);
		// Make sure we don't try to get objects that don't exist, ArgumentOutOfRangeException otherwise!
		if (startRowIndex + maximumRows > someList.Count)
		{ maximumRows = someList.Count - startRowIndex; }
		return someList.GetRange(startRowIndex, maximumRows);
	}

	// Our minimal SelectCountMethod.
	public static int MinimalSelectCountMethod(string parameter1, string parameter2)
	{
		return GetSomeKindOfList(parameter1, parameter2).Count;
	}

	// A method to get a filtered list for our primary data source.
	public static List<CultureInfo> GetSomeKindOfList(string parameter1, string parameter2)
	{
		return baseList.FindAll(x => x.EnglishName.ToLower().StartsWith(parameter1))
			.FindAll(x => string.IsNullOrEmpty(parameter2.ToLower()) ||
				x.EnglishName.ToLower().EndsWith(parameter2.ToLower()));
	}
}

So, as you might have guessed, lines 8 and 19 are the important ones here, containing our 2 methods. These methods can be called anything you like here, as long as the attributes in your ObjectDataSource tag are spelt exactly the same.

We’re almost there! Don’t have a baby yet though, the nasty bit’s coming…

Part 3: Pass the [parameter(s)]

Now we have the basic functions in place, we need to find some way to pass the parameters to the ObjectDataSource, so the results can be selected in any way that pleases us. This is the part that I found particularly difficult when implementing this for the first time, so pay attention!

The ObjectDataSource will automatically call its Selecting event, each time it needs to load a page of data for the ListView. It is here, and nowhere else, that we must programmatically set the parameters for our SelectMethod and our SelectCountMethod. This event receives an ObjectDataSourceSelectingEventArgs object as its second parameter, which in turn contains an IOrderedDictionary collection called InputParameters. So, you guessed it, we just need to set this up with the values we want to pass to our SelectMethod (and our SelectCountMethod). The names of the parameters must be exactly the same as the names we used in our definition of the SelectMethod and SelectCountMethod (the functions I’ve named MinimalSelectMethod and MinimalSelectCountMethod. We do this thusly…

protected void ObjectDataSource1_Selecting(object sender, ObjectDataSourceSelectingEventArgs e)
{
	e.InputParameters["parameter1"] = TextBox1.Text;
	e.InputParameters["parameter2"] = TextBox2.Text;
}

Remember the OnSelecting property of our ObjectDataSource tag in the aspx code from before? Well, this is the function whose name it must be set to. (Note, if you’re using VB.NET, then you don’t need to bother with defining the OnSelecting property in the aspx code, you just need a Sub that says Handles ObjectDataSource1.Selecting at the end of the first line of its definition. You also need to give it the same parameters as specified here.)

And that’s it, it should now work. Give yourself a big pat on the back.

Just one final note though. Since the Selecting event of the ObjectDataSource will be called each time a page is requested, the code I’ve used here could potentially cause an exception. “Really? Wow, how’s that?” I hear a little voice in my ear ask. Well, let’s go on a journey…

Daemons and devels

Actually, let’s not. Here’s why: the number of pages available when the DataPager was rendered may be different than the number of pages available after one of the page buttons is selected, in our example. This could happen for one of two reasons.

  • Firstly, the parameters may end up reducing the number of pages available, if they have been changed since the DataPager was rendered.
  • Secondly, the actual source dataset itself may have changed since the DataPager was rendered, meaning that any pages that are selected beyond the new number will not exist, and will therefore cause exceptions to be thrown.

I want to keep this article short, so I’ll simply outline a strategy to mitigate these potential exceptions. I’ve included a project in the accompanying download that has this implemented.

Avoiding exceptions

In my opinion, going to a different page in our dataset oughtn’t to be able to change the filter parameters, it just seems like unexpected behaviour to me. Therefore, as you’ll see in the attached project, one solution is to store the parameters’ current values separately from the controls’ current values, in the Session collection, only updating them when the ‘Search’ button is clicked.

This is the end

I’m going to try and draw a nice little diagram to illustrate the relationships between the pieces of code discussed, and post it here to serve as a quick reference. I’ll also upload the sample project very soon. Watch this space.

So, there you go. If you have anything to add, whinge about, or just want to give me a little ego boost (who wouldn’t?), then comments go in the box underneath. Ta.

11 Comments :, , , , , , , more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Archives

All entries, chronologically...