Put your message here! Contact me for more information
 
 







 

Archive for the ‘Web Scripting’ Category


 


{{http://alexle.net/wp-content/uploads/2007/06/rails.thumbnail.png }}Working on Wars of Earth by myself, I run into several situations when I’d like to move migration files around to get them executed in a particular order. The trick is simple: just rename the sequence of the migration files to get them to be executed in the order you want.

Besides sloppy schema design (hey, I’m trying to be agile here - fix it as you go), an example of a situation when you want to move migration files around: table A has a foreign key to table B. Migration file for A is at 005, while that of B is at 010. If you try to put in the FORIEGN KEY constraint in A migration file after the table creation, migration will fail since table B hasn’t been created yet, not until migration #010. You can add another migration file just to add the FORIEGN KEY constraint from A to B, say at #020. But then you will have fragmented migration code all over the place. For production environment, this is the only solid and proven way to perform database changes. However, during development, you have the luxury to drop and recreate the entire database from scratch, it’s just a lot more convenient and makes more sense to be able to move migration files around. Ideally, you just need to run migration for B first, then you can run migration for A, which also execute the SQL to add in the FOREIGN KEY constraint properly.

Until now, you have to it manually. Imagine if you have 20 migration files in between, from 5 to 25 (like what I have), you will probably have to renumber 20 migration files. And if your project is in SVN, it would be more time-consuming and error-prone. You can either do “svn rename”, or just rename in the shell, then deleted the previous migration files and added the newly-named ones back into the repository. But what would happen if you yawn for a second and misnumber a file at #6, so you have two 006 migration files. Oops.

Worry no more, here comes **numergrate** utility to the rescue. In short, numergrate is “to numerate migration files” (or to renumber migration files)

What you need is to drop in the numergrate script (download below) inside your Rails’ script folder and you are ready to go. With this utility, you can execute at the root of the Rails project

ruby script/numergrate 5 before 25
to (test) move 005 to position 024 instead.

or

ruby script/numergrate 10 after 2 --svn
to move 010 before 002 and also rename the files in the Subversion repository.

I also took the opportunity to integrate the utility with Subversion in case your migration files are in a repository. There’s no easy way for CVS so I didn’t implement it. However, implementing renaming mechanism for other SCM should be straight forward if you know their command line renaming tool.

I hope this comes handy for you.

==== Supported tools ====

* {{http://alexle.net/wp-content/uploads/2007/06/subversion_logo.thumbnail.jpg}} Subversion (renaming files in repository)
* {{http://alexle.net/wp-content/uploads/2007/06/utilities-terminal.png}} Shell (just like renaming manually)

==== Script ====

#!/usr/bin/env ruby
# (c) 2007 Alex Le
# www.alexle.net - nworld3d@yahoo.com
# This script is developed for www.warsofearth.com. (shameless self-promotion)
# Released under the same license as Ruby
# Disclaimer: The author is not responsible for any incorrect results from running
# the script. Use it at your own risk.
#
# USAGE:
# ------
# This script is used to re-number the migration files into the desired sequence
# In development, this comes handy as you can organize migration files into logical groups
# by running a simple command line utility instead of manually renaming the filenames.
#
# numergration = numerate migration files
#
# INSTALL
# -------
# Copy numergration into script/ folder of your Rails application.
# On linux system you may have to chmod the script to be executable (+x)
#
# HOW TO RUN
# ----------
# At your Rails application root, run:
# On Windows:
# > ruby script/numergrate before|after [mode]
# On Linux: users can just run the script without calling the ruby executable since
# there’s a #! on top, provided that you set the permission correctly. (chmod to +x)
# $ script/numergrate before|after [mode]
#
# The [mode] options are
# –test Default mode to test the result before you run
# with –shell or –svn
#
# –shell Renaming file as you would do manually in the shell
#
# –svn Integrate with Subversion by executing `svn rename` on each file
# This option alters your working copy so please be extra careful.
# (a.k.a. use it at your own risk)”
#
# Example:
# ——–
# a. Move Migration file 50 to position 3, hence shifting migration files
# from 3 to 49 to the right by 1.
# > ruby script/numergrate 50 before 3
# (the above will just execute with the –test default option)
#
# Or to actually rename the files,
# > ruby script/numergrate 50 before 3 –shell (or –svn)
#
# Or you can even run
# > ruby script/numergrate 50 after 3
# to put migration file 50 after migration file 3 (shifting migration
# file 4 to 49 to the right by 1)
#
# TODO:
# —-
# 1. Better sequence handling. Currently it’s default to 000 for the
# sequence series. However, there can be potentially a lot migration files.
# (more than 999 files). The solution is to find the max sequence and
# use that as the series template
# 2. Better SVN integration.
# a. Do some checking to see if the svn client exists before running.
# Otherwise throw an error
# 3. Better sequence checking. Currently it doesn’t check for input
# range so we can have “index out of bound” errors.
#

require ‘fileutils’
include FileUtils

# which folder we would skip while iterate through
SKIPPED_FILES = [’.', ‘..’,’.svn’]

# check for arguments
unless ARGV.size == 3 or ARGV.size == 4
puts ‘invalid syntax’
exit
end

# this class hold the information about the migration file
class MigrationFile
attr_accessor :sequence, :name, :new_sequence

def to_s(options={})
if @new_sequence != @sequence
s = sprintf(”%03d”,@new_sequence) << "_#{@name}"
else
s = sprintf("%03d",@sequence) << "_#{@name}"
end
end

def initialize(sequence, name)
@sequence = sequence
@name = name
@new_sequence = @sequence
end

def shift_left()
@new_sequence -= 1
end

def shift_right()
@new_sequence += 1
end

def is_changed?
return @new_sequence != @sequence
end

def old_name
s = sprintf("%03d",@sequence) << "_#{@name}"
end

def new_name
s = sprintf("%03d",@new_sequence) << "_#{@name}"
end
end

# 123 after 234 --test
src, task, dest, mode = [ARGV[0].to_i, ARGV[1], ARGV[2].to_i, ARGV[3]] # got to explicitly convert to number for comparision
# exit if don't have to move
exit if src == dest

# default mode to --test
mode ||= "--test"

#grab the migration files
files = []
Dir.entries("db/migrate").each { |file|
unless SKIPPED_FILES.include?file
files << MigrationFile.new(file.to_i, file.match(/.+?_(.*)/)[1])
end
}

# now perform shifting
files.each{ |file|
if src > dest
if file.sequence == src
if task == “before”
file.new_sequence = dest
elsif task == “after”
file.new_sequence = dest + 1
end
else
# shift the innner range files
if file.sequence >= dest && file.sequence < src
if task == "before"
file.shift_right
else
file.shift_right unless file.sequence == dest # if insert after, we don't need to shift the dest
end
end
end
elsif src < dest
if file.sequence == src
if task == "before"
file.new_sequence = dest - 1
elsif task == "after"
file.new_sequence = dest
end
else
# shift the innner range files
if file.sequence <= dest && file.sequence > src
if task == “before”
file.shift_left unless file.sequence == dest # if insert before, we don’t need to shift the dest
else
file.shift_left
end
end
end
end # if src > dest
}

#files.each{ |f| puts f if f.new_sequence != f.sequence }

# now issue
puts “”
puts ” Execute using #{mode} option”
puts “”
puts ” You can execute with these options: ”
puts “”
puts ” –test Default mode to test the result before you run”
puts ” with –shell or –svn”
puts “”
puts ” –shell Renaming file as you would do manually in the shell”
puts “”
puts ” –svn Integrate with Subversion by executing `svn rename` on each file”
puts ” This option alters your working copy so please be extra careful.”
puts ” (a.k.a. use it at your own risk)”
puts “”

files.each{ |file|
if file.is_changed?
if mode == “–shell”
puts ” rename ” << "db/migrate/" << file.old_name
puts " to " << "db/migrate/" << file.new_name
cp("db/migrate/" << file.old_name, "db/migrate/" << file.new_name )
rm("db/migrate/" << file.old_name)
elsif mode.downcase == "--svn"
#puts "executing svn commmand here"
puts " svn rename " << "db/migrate/" << file.old_name
puts " to " << "db/migrate/" << file.new_name
system 'svn rename --force db/migrate/' << file.old_name << " db/migrate/" << file.new_name
elsif mode.downcase == "--test"
puts " [TEST] rename " << "db/migrate/" << file.old_name
puts " to " << "db/migrate/" << file.new_name
end
end
}

==== Download ====
* To download, please click here: [[http://alexle.net/wp-content/uploads/2007/06/numergrate.zip|numergrate]] (version 1.0, 06/17/2007)

==== Note ====
* Moving migration files around can do serious damage to your database. Please be careful. This comes extremely dangerous if you are working with other people on the same repository. Please be smart about it. Use it at your own rick.
* Comments and suggestions are very welcome.
* Support me at [[http://www.warsofearth.com|Wars of Earth]] if you can. Another shameless self-promotion.

view comments
 


{{http://alexle.net/wp-content/uploads/2007/06/rails.png }}It’s been a week since I started messing with Ruby on Rails seriously. My current Rails project is rebuilding Wars of Earth, the game that my friend Giao and I started almost a year and a half ago. Since Rails is so flexible and powerful, within a span of a few days (like 5 days), I was able to achieve as much as 2 weeks worth of work compared to the past. Before, as Giao is much better at programming than I am, I asked him do the framework design in PHP (we used Pear’s DB_DataObject, Smarty, and home-grown controller for a MVC architecture.) I did the design (game designs, coming up with ideas, some coding) instead as well as preparing the site in Joomla (check it out [[http://www.warsofearth.com|here]]). Nonetheless, I was getting ready to graduate, and my buddy had to take on freelance work to make some money, I decided to put Wars of Earth aside and focus on what mattered most at the time (mainly job and money). As I have more free time on my own now, I am restarting the Woe project but this time, I am rebuilding it from scratch using Rails. And I don’t think I would go back to PHP for any web-based project if I don’t have to.

The major bottle-neck between Giao and I, as I would boldly claim for any other projects that involved geographically distributed team, was the communications. Giao is still back in Vietnam, and I’m in Chicago. I’m 12 hours behind him. Whatever we needed to do, we put in TOTO tasks and message in our Basecamp site. At the time I haven’t thought too thoroughly about the story and the game play, so we just kept on moving until we hit a problem, then we would sit down and talk. But words can only describe so much. Sometimes what I tried to get across, or what my buddy tried to tell me, just failed to get both of us on the same page. Giao would implemented something and it was not the same as I thought, so we just kept on building, discussing, and sadly, compromising. Moreover, I constantly having more ideas for the project, creating our eternal scope-creep: I just tried to add many features right at once (I was trying to get it right the first time, but of course it was almost impossible)

Now with Rails, things are much different. Rails are so flexible and powerful that what I only need to put down is mostly business logic, not low-level, database accessing code. The syntax of Ruby is much superior to that of Perl (I hated Perl with passion, and still do) and Rails is plainly a joy to use. Suddenly I realize the meaning of the [[http://www.loudthinking.com|blog]] of [[http://www.loudthinking.com/about/|David H. Hasson]], Rails’ creator, “**Loud Thinking**.” With Ruby, you can actually __think out loud__ through your code. Since Ruby’s syntax is so flexible, it allows the user to write the code in an unheard-of conversational way. I can almost read the code as I would read a novel (since reading code requires some imagination and the code is no longer boring in Ruby, so I can’t compare the program with a textbook - that would be too boring don’t you think?). I don’t have to restrain myself to the curly braces of C#/Java/PHP style, instead I can tell the Model to fetch all the record then smash the id’s in to an array with some logic in one line. Something like CharacteUnit.get_all_units().collect{|unit| unit.id unless unit.is_inactive} and of course you can add a lot more to that as you can stack as many operators/methods as possible. Pure programming joy.

One interesting part about Rails is the testing tool: unit test and functional test. I can’t wait until I setup the first functional test for the application to simulate a web user. And of course the best part is: everything comes in an elegant, well-thought, scalability-proven web framework that took you 10 minutes to get started. Suddenly the barrier to entry to mid/ large-scale web projects is dropped significantly (it’s __free__ now, only costs you your opportunity cost). In stead of just WYSIWYG, it’s now **What-You-Think-Is-What-You-Can-Get**, **WYTIWYCG** (weet-tee-wic). Imagination goes wild, how awesome.

Whomever I talk to nowadays, I can’t stop talking non-stop about Ruby on Rails. I feel good sharing it to anyone I know so they can start feeling the same way as I do, as sharing is caring.

I feel good. Do you?

view comments
 


Since I’m learning Ruby on Rails, I feel the need to know the basic syntax of the language. So here is my own primer guide to basic Ruby syntax. The guide was wrote largely based on the [[http://docs.huihoo.com/ruby/ruby-man-1.4/syntax.htmll|original manua]] by Ruby’s author, [[http://en.wikipedia.org/wiki/Yukihiro_Matsumoto|Yukihiro Matsumoto]]

===== Ruby =====
Ruby is case-sensitive. Whitespace characters are space, tab, vertical tab, backspace, carriage return, and form feed. Newlines works as whitespace only when expressions obviously continues to the next line.

==== String literals ====


"remeber to escape \" and \\ #{expression_substituted}" # streng enquoted with doublequote is subjected to expression substitution
'remember to escape \' and \\ #{expression_not_substituted}' # streng enquoted with doublequote is not evaluated
%q!Some string with "double quote" and 'single quote'! # equivalent to '' and don't have to escape double quote or single quote
%Q('Some string with "double quote" and \'single quote\' and back parenthesis \) ') # equivalent to "" and don't have to escape double quote or single quote
%!Some string with "double quote" and 'single quote'! # equivalent to "" and don't have to escape double quote or single quote

==== Expression subtitiion in strings ====

"hello my name is #{$name}" # sub #{$name} with $name

$ and @ variables don’t need to be enclosed within #{}. The # only has special meanings when it’s followed by {,@, and $

==== Regular Expressions ====

%r/STRING/
/regexp/

* **Modifiers**

/regexp/i # case insensitive
/regexp/x # extended expr, no whitespaces and comments are allowed
/regexp/p # POSIX mode, newlines are treated as normal charactor

==== Variables ====

$var # global
@var # instance variable of self
VAR # constant
SomeClass::VAR # constant within a class
var or _var # local variable

=== Pseudo Variables ===
* self
* nil
* true
* false
* __FILE__ : current source file
* __LINE__ : current line

=== Array ===
* [1,2,3]
* %w(foo bar baz): create a new string, space-separated array, equivalient to [”foo”, “bar”, “baz”]

=== Hash ===
* {expr1 => exprA, expr2 => exprB }

===== Method invocation =====
* **method(args1, arg2)**
* **method(*array)** is equivalent to **method(array_member1, array_member2, …)**. The * expands the argument array.)

===== Operators =====

+, -, *, /, %, **, &, |, ^, <<, >>, &&, ||
foo += 5
foo, bar, baz = 1, 2, 3
foo, bar = 1 # foo = 1; bar = nil
foo,*bar = 1, 2, 3 # equivalent to foo = 1; bar = [2, 3] ( the * multiple assignment is used to assign to an array)

===== Control Structure =====
**false** and **nil** are false, everything else are true.
* If statement
if expr [then]
expr...
[elsif expr [then]
expr...]...
[else
expr...]
end

* unless
unless expr [then]
expr...
[else
expr...]
end

=== if and unless modifier ===
* expr1 **if** expr2: if expr2 is true, execute expr1
* expr1 **unless** expr2: if expr2 is false, execute expr1

=== case ===
* case comparison is via the === operator
case expr
[when expr [, expr]...[then]
expr..]..
[else
expr..]
end


case $age
when 0 .. 2
"baby"
when 3 .. 6
"little child"
when 7 .. 12
"child"
when 12 .. 18
# Note: 12 already matched by "child"
"youth"
else
"adult"
end

==== Range ====
* expr1 .. expr2 (similar to awk)
* expr1 … expr2 (similar to sed)

==== Loop ====

while expr [do]
#code here
end

until expr [do]
#code here
end

=== while and until modifier ===

expr1 while expr2 # keep evaluating expr1 while expr2 is true.
expr1 untill expr2 # keep evaluating expr1 until expr2 is true
begin expr1 until expr2 # evaluating expr1 at least 1 time until expr2 is true

==== Iterators ====
* [1,2,3].each do |i| print i*2, “\n” end
* [1,2,3].each{|i| print i*2, “\n”}

==== For (similar to foreach) ====

for lhs... in expr [do]
expr..
end

* Example: for i in [1, 2, 3]
print i*2, "\n"
end

There are 2 special keywords that can be used in the loop body: **next** (jump to next iteration of the inner-most loop) and **redo** (restart the current iteration of the most inner-most loop without checking loop condition)

==== Raising Errors ====
* Raise syntax
raise # raise general exception
raise message_or_exception
raise error_type, message
raise error_type, message, traceback

* Examples
raise "you lose" # raise RuntimeError
# both raises SyntaxError
raise SyntaxError, "invalid syntax"
raise SyntaxError.new("invalid syntax")
raise # re-raise last exception

* $! contains the exception and $@ contains the position in source file.

==== begin block ====
**begin** is different from the uppercase **BEGIN**, which has a totally different meaning

begin
expr1..
[rescue [error_type,..]
expr2..]..
[else
expr3..]
[ensure
expr4..]
end

If exception occurs in **expr1**, **rescue** will execute **expr2**. The matching of **error_type** is done by **kind_of?**. **else** clause has to follow after **rescue** and is executed if no exception occurs in **expr1**. And example:
begin
do_something # exception raised
rescue
# handles error
retry # restart from beginning
end

for i in 1..5
retry if some_condition # restart from i == 1
end

# user defined "until loop"
def UNTIL(cond)
yield
retry if not cond
end

==== BEGIN and END blocks ====

BEGIN {
...
}

BEGIN registers initializing blocks in the appearing order, which will then get executed before any statement in the file. BEGIN has its own internal scope (don’t share local variables with outer scopes) and has to appear at toplevel.


END {
...
}

END register finalizing blocks. END blocks share their local variables. The END statement can only appear at the toplevel. Also you cannot cancel finalize routine registered by END.

==== Class ====

class Classname < SuperClass
expr..
end

class SingleTonClassname << SuperClass
expr..
end

==== Module ====

module Foo
def test
:
end
:
end

==== Method ====
Method has to be defined before it is invoked. It cannot be nested. Method’s return can be defined explicitly with “return”, or implicitly using the last evaluated expression

def fact(n)
if n == 1 then
1
else
n * fact(n-1)
end
end

Method can be declared private inside the function form. If Method is declared outside the class definition, it’s marked private by default. Method declared within class definition is marked public by default.

==== Singleton-method ====

def foo.test
print "this is foo\n"
end

The singleton-method definitions can be nested and are inherited to subclasses. Singleton-method acts as typical class’s method in other OOP languages.

==== alias, undef, and defined? ====

alias method-name method-name
alias global-variable-name global-variable-name

Aliasing numbered global variables is forbidden.

=== undef ===
undef method_name
Used to cancel method definition.

=== defined? ===
defined? expr
If expr is not defined, return false, otherwise return a string describing the kind of expression.

view comments
 

Stock QuoteI’m back from a period of hibernation. Lots of exciting stuff and new things, just barely have enough time to sleep and catch up with all the pending projects that I’ve been crazily taking on. Anyway, today I’d like to share something that maybe useful for someone out there: how to retrieve live stock quote data using Yahoo quote service with ColdFusion.

I was working on a project which originally utilizes the WebServiceX.net’s [[http://www.webservicex.net/stockquote.asmx/GetQuote|StockQuote web service]]. Things were working fine until WebServicex.net is down, as of today, March 22nd, 2007, with a big fat “Service Unavailable”

“Crap!” was what I said to myself. Luckily my work buddy forwarded a [[http://aspalliance.com/articleViewer.aspx?aId=112&pId=#Page2|link]] to an article on how to access the Yahoo Stock Quote service using ASP.NET (C#). Bingo! This was exactly what I needed. Based on the article, I was able to hack out the code using ColdFusion to retrieve stock info in just a few lines of code (or markup since I don’t really consider ColdFusion a real “language”)

===== Yahoo Stock quote CSV service =====
First of all, I didn’t even know if this service ever exists. I don’t really know where the official Yahoo documentations for the Stock Quote CSV service is (anybody knows the link please let me know). More ironically, I used Google to search for tutorials on how to use the Yahoo Stock Quote API. Anyway, this is the URL to request a CSV (comma-separated values) file containing stock information for a particular symbol, in this case, YHOO (I think I do have to give Yahoo some credits for the free service)http://quote.yahoo.com/d/quotes.csv?s=YHOO&f=l1c1v
with
* **s=SYMBOL**: with SYMBOL is the actual stock symbol
* **f=REQUESTED_FORMAT_STRING** where the REQUESTED_FORMAT_STRING is a string of letters representing what fields to be requested. The oder of the letters will be used as the order of the fields in the returned CSV.\\ I messed around and found out about the meaning of the letters used in the **f** parameter. If you want the full information about the symbol, you can use **f=sl1d1t1c1ohgvj1pp2wern** instead.http://quote.yahoo.com/d/quotes.csv?s=YHOO&f=sl1d1t1c1ohgvj1pp2wern Don’t sweat, here comes the explanation for each letter (or letter-digit pair)
* s: the symbol name
* l: last value (or current price). If you use l alone, you will get back a string similar to Mar 22 - 31.26
* l1: (letter l and the number 1) to just get the last value of the symbol
* d: d alone will give you 0, while d1 will return the current date (e.g. 3/22/2007)
* t: t by itself will request the yahoo-generated chart. However, you will get back the chart image with a whole bunch of other HTML garbage, e.g. src=http://chart.yahoo.com/c//y/yhoo.gif” alt=”Chart”>


* t1: the time of last update, for example 4:00pm.
* c: the change amount. Can either be used as c or c1.
* o: opening value
* h: high value
* g: low value
* v: volume
* j: 52-week low.
* j1: the market cap. This is string like “42.405B” for $42 billion. Man… that can buy **a lot** of hamburger or bowls of phở
* p: after hour price (?)
* p1: (?)
* p2: change percentage e.g. “-0.10%”
* w: 52-week range
* e: EPS (Earning per share)
* r: P/E (Prince/Earning) ratio
* n: Company name

(This list is hand-compiled by me based on my own interpretation. If you find any explanation being inaccurate, please let me know)

===== The Code =====
ColdFusionThe code is pretty simple: we will read from the result the Yahoo CSV service using the tag into a variable. This variable will then contain the comma-separated values list of what we put in for the f parameter. Back to our example, the URL we are requesting is http://quote.yahoo.com/d/quotes.csv?s=YHOO&f=l1c1v, which will return to us a string 31.26,-0.03,13249801. Since the response is in CSV format, we can utilize the ColdFusion List functions like ListLen(), ListGetAt() to access the values.

Here is the CFML code snippet





#stockInfo# 

* First, we construct the Yahoo Stock Quote service URL. We are requesting for last price, change amount, and the stock volume.
* Next we use the cfhttp tag to request the url, then grab the content into the cfhttp.FileContent variable and assign to the stockInfo variable. stockInfo will contain something similar to 31.26,-0.03,13249801
* To get the different values in the list, we use ListGetAt() with the appropriate index. And remember, indices of Array and List in ColdFusion start at 1, not 0.

That’s it!

Final Remarks
I hope you will find this useful. Thanks to Yahoo for providing a free stock quote service. I know I can **abuse** this CSV service as much as possible before getting banned because it’s Yahoo. Any services other than those of Yahoo, Google, Microsoft, I will probably have some guilty thoughts before doing anthing. But it’s Yahoo so I really don’t have no guilt abusing it. Just kidding though. I do appreciate the free (as in free beer) stock quote service.

===== References =====
* Bromberg, Peter, PhD. [[http://www.eggheadcafe.com/articles/20010404.asp|Build a C# Stock Quote WebService and Client using the WebService Behavior HTC (Part I)]]
* Perry, Jason. [[http://aspalliance.com/articleViewer.aspx?aId=112&pId=#Page2|Building a Yahoo stock quote ticker]]

view comments
 


Until recently I have decided to setup a local SVN repository on my computer, which runs Windows XP pro. Setting up SVN with Apache is the best way to access a SVN repository but the thing is, my computer has also IIS installed for ASP.NET development. Normally I have XAMPP installed and have to switch start/ stop Apache and IIS manually whenever I need to do something in either ASP.NET or PHP. To clarify, I do have PHP 5.x setup for IIS but Just didn’t bother to fix the session file permission issues of IIS; I’d rather just switch to the working Apache installation instead using the XAMPP control panel.

I started by installing the [[http://svn1clicksetup.tigris.org/|SVN1clickSetup]]. The installation was painless, however, I needed to restart the service to change some configurations. To register svnserve.exe to run as a serice again, I used this full command:


I:\Program Files\Subversion\bin\svnservice -install -d --listen-port 3690 --listen-host 0.0.0.0 -r K:\svnrepos

The -d param enables svnserve to run as a daemon. We specify the list-host of SVNServe.exe to be 0.0.0.0, which means it will bind to any IP available to the system. The -r specifies which folder to serve as the SVN repository. What left was opening up the 3690 port on the router and have the requests for the SVN service on this port routed to my box. Done!

* If you run `svnservice -debug` and see the `RegQueryValueEx failed with error 0, type 1`, it means that SVNService has successfully registered the SVNServe to run as a service. In fact, `RegQueryValueEx failed with error 0` is not an error or a failure.

* In case you have already run the above svnservice to register the SVNserve as a service but still cannot access the repository, you may have to start the SVNService manually by going through Start > Run > services.msc. Find the service called SVNService and start it. Also switch its Startup Type to Automatic so that SVNService is started on start up.

Cheers!

view comments