Archive for January, 2005

Gut feeling followup

Joe posted a comment about my entry on the new nofollow attribute. He has a good idea to fix the problem I had, it would allow blog owners to selectively promote links in comments, so valid links would get their due "Google juice." I think this is a good idea, but I dont think many blog owners would be diligent in promoting peoples links, especially people who get a lot of comments.

What I would like is some sort of keyword list that I could maintain and links that contain these keywords would have the rel="nofollow" attribute. Then meaningful links to real blogs would still their credit. I would also want to be able to retroactively apply the keyword filtering, so if I get a bunch of comment spam one day, and I dont happen to have any keywords for the new spam, I would want to add the keywords, and then have the blogging application check my comments against my updated keywords list.

Gut feeling

Its nice to see some people are not so giddy over the
new attribute that we can add to
links. I’ve heard people lauding this as the end of comment spam, and I
agree that it will be a big help, however, one important way that people
get “known” in the blog world is by getting moved up in the Google rank through
meaningful comments. I like the fact that when I make a comment, its
contributing to the conversation, but that I’m also getting a link to my blog
out there. Now, when I leave comments, it won’t be counted if the blog
owner applies this attribute to all incoming comments.

This was my first impression when I first learned of the
tag, and maybe I’m way off base, but this is just my knee jerk
reaction.

CSci 101 Part I

I’d like to do a little educational series a la target=_blank>Raymond Chen and
target=_blank>Larry Osterman
Now, this series will NOT be nearly as technical, nor as difficult or in depth
as these two can get, its just something that I thought I would try
out.  If I like it, maybe someday I will have some really cool stuff to
give a tutorial on, and I can do that, but for now, we’re going to keep it
fairly academic, being as I just graduated from college, this seems
appropriate.

I’m going to talk about the target=_blank>fibonacci numbers,
and a few different ways to go about generating them.  The fibonacci
numbers are a sequence of numbers that are calculated by adding the previous two
numbers in the sequence.  The first two numbers are given, they are 0 and
1, respectively. So the 3rd number in the sequence is 0 + 1 = 1, the 4th is 1 +
1 = 2, and so on.

In my first computer science class at school, one of the first concepts they
try to beat into you is recursion. A recursive function is one that makes a
call to itself.  A classic example of recursion is the way that you can
write a factorial function.

We can rewrite n! as n * (n-1)!, which we can rewrite as n *
(n - 1) * (n - 2)!, etc.  The way this would be represented in our code
would be:

style="MARGIN: 0in 0in 0pt; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">static style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"> style="COLOR: blue">int fact(int
n)

style="MARGIN: 0in 0in 0pt; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">{

style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">if style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"> (n ==
1)

style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">return style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">
1;

style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">else

style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">return style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"> n *
fact(n - 1);

style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">}

style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA">Notice how the fact function calls itself.  Its important
to have a base case so that the recursion ends at some point; in this example
the base case occurs when n == 1, at that point we simply return 1 because 1! =
1, so we can stop recursing.

style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA">The first way that we will generate the fibonacci numbers is
using recursion.

style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA">The base cases for our function will be when n == 0 or n == 1,
because those fibonacci numbers are given to us. If n > 1, then we
will calculate the 2 previous fibonacci numbers, by recursively calling the same
function, and adding them together.  Here’s the code:

style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA">

style="MARGIN: 0in 0in 0pt; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">static style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"> style="COLOR: blue">long Fibo(long
n)

style="MARGIN: 0in 0in 0pt; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">{

style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: gray; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">//

style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: gray; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">//
check the base cases

style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: gray; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">//

style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">if style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"> (n <=
1)

style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">return style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">
0;

style="MARGIN: 0in 0in 0pt 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">if style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"> (n ==
2)

style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">return style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">
1;

style="MARGIN: 0in 0in 0pt; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"> 

style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: gray; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">//

style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: gray; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">//
calculate the 2 previous

style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: gray; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">//
numbers and add them together

style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: gray; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">//

style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; mso-layout-grid-align: none"> style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">return style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"> Fibo(n -
1) + Fibo(n - 2);

style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">}

style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"> 

style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">This code is extremely simple, there are more lines of
comments than of actual code! Try this code out in a console application, start
out small by calling Fibo(4), Fibo(5), etc.  See if you notice anything as
you put in higher and higher numbers :-) Next, we’ll inspect this function a
little, and improve upon it a little bit.

style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"> 

style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes">Thanks for reading!

 

Date.AddMinutes()

I really wanted an AddMinutes() method to use in javascript like C# has. So I set out to write one myself.

I soon realized how complicated it could get, for instance, the minutes could move the date to the next day, month, or year. I started writing down all the conditions to check, to make sure that I could handle all situations. Since javascript uses the setMinutes(), setHours(), setMonth(), etc.. style of altering a date, I was going to parse the minutes into the number of months, days, hours, and minutes that were represented by the minutes. So 10080 minutes would equal 7 days. After I did that I could add all the appropriate fields and then get my new date.
Then I realized a MUCH easier way to get it done. There is a Date method called getTime() that returns the number of milliseconds since 1/1/1970. There is also a Date constructor that takes in an integer, which also represents the number of milliseconds since 1/1/1970. So all I had to do was use the getTime() method, convert the minutes that I want to add into milliseconds, add the milliseconds and the create a new date instance with this new value. Much, much easier and I don’t have the headache of managing all those situations mentioned above, whew!! Here’s the code I used:
function addMinutes(dateTimeObj, iMinutes) {

var length = iMinutes * 60 * 1000;

var iseconds = dateTimeObj.getTime();

iseconds += length;

var dt = new Date(iseconds);

return dt;

}

Maximum length of url in winamp

Winamp currently has a maximum string length of 260 for urls that point to
streaming audio.  After I finally got the streaming
to work
, I wanted to encrypt the querystring, so users couldn’t mess with
the url.  After using the encryption, I kept getting a bunch of errors,
most of them were ”Invalid length for a base 64 char array.”  So I
naturally assumed that my encryption was getting a little goofed up because of
the browser url encoding the string.  So I messed around with some diffent
encryption types, but nothing worked.

Then I noticed that the url in winamp was different from the
one that I was initially sending to the user, it was truncated.  Only then
did I do some digging and find that winamp
uses the MAX_PATH variable in the windows
api headers, which is set to 260.  This really sucks, because winamp
handles streams much better than Window Media Player, because you can set the
default action to “enqueue” rather than “clear my playlist and play the new
stream”, also, WMP doesn’t seem to read the track names correctly, so instead of
a playlist that has my list of songs, it just has the word “Play” for every
track, not real easy to tell whats playing next.

Well, at least now I know that it wasn’t something that I
did wrong, but I guess I should have verified the url before wasting all
that time…..

More on o/r

Brock Allen refers to his object
relational mapping tool as an RO mapper, as opposed to an OR mapper.

He does this because he builds the database first, and then
maps the database into objects, so its a relational-object mapper
instead.  I’ve never heard it called that before, but I like it, because
thats exactly the process that we follow too, build the database how you want
it, and then make the objects fit the data model.

Alternative NUnit GUI

I just read about this alternative gui for nunit over at Roy Osherove’s.

I just downloaded it and it looks pretty sweet! Haven’t had a chance to use it much yet, but I’m excited to see all the statistics that it produces….

Updates, Inserts, and checkboxes

When we were developing our data access objects, we added an
Update, and an Insert method, with the corresponding signatures:

Update(System.Collections.Specialized.NameValueCollection
form);
Insert(System.Collections.Specialized.NameValueCollection
form);

These methods take advantage of the fact that when you post a
form, the form values are contained in a NameValueCollection object, so you can
loop through the elements of the form, and update the respective data fields,
and then update (or insert) the database with the new values.

This scheme
is really convenient for when you want to add or remove items from the form,
because you can simply remove them from the html, without even worrying about
the backend, because those added or removed form elements are inconsequential to
the workings of the code behind.  You could have a form that contained none
of the data fields, and still call these methods without any problem.

The
only problem arises when you need to deal with checkboxes.

If the user checks the checkbox, the value of the checkbox
gets submitted and the data object can be updated with the checkbox value.
However, suppose the checkbox is initially checked, and the user unchecks the
checkbox.  Now, when the form is submitted, it appears as though the
checkbox never existed, because it will not be present in the
NameValueCollection, and the data object does not know to update the
appropriate value.  We typically use checkboxes to denote true/false
or 1/0 values in the database, so if a user were to leave a checkbox
unchecked it means that the database value should be false or 0, but since there
is no form value present, the data object does nothing to that field.

What we did to work around this problem, was to add a hidden
form element to our forms.  This hidden field is a comma separated list of
all the form elements that exist on the form.  This hidden field gets
submitted with the rest of the form, and as the data object loops the form
elements, it records them. When the looping is finished, it compares the
list of form elements that it found, to the comma separated list that was
submitted with the form.  If there are any form elements in the list, that
were not included in the form, then the data object knows that a checkbox was
left unchecked, and can then update the field
accordingly.

Can’t stream mp3 file in .net

I am trying to convert an internal application that we have
that streams mp3 files to winamp.  The app is currently written in asp, and
I’m moving it to asp.net.  I copied the streaming code almost exactly from
the vbscript to c#, but now when I try to listen to an mp3 from the new site, I
get the great error message “error syncing to mpeg.”  Does anyone have
experience in streaming mp3 files to winamp?  The asp code reads the file
from an ADODB.Stream object in 2048 size chunks and buffers the content.  I
initially did the same thing in c#, but after getting the error, I tried
simplifying the code and just using the Response.WriteFile(string path)
method.  The reason for reading the file in chunks is so that large files
don’t timeout.

I think that it might have something to do with a bad
header or something, but I don’t know if asp.net puts in extra headers
automatically, in any case, I don’t see why these don’t work the same way. 
I can load both examples in a firefox browser window, and the content is the
same, its the base64 content of the file, so I know that its actually reading
the correct data, winamp just won’t recognize the file as an mp3.

asp code (this works):

size=2>Response.Buffer = True
Response.Clear
Response.AddHeader
“Content-Disposition”, “inline; filename=” & FileName
Response.AddHeader
“Content-Length”, FileSize

Set objStream =
Server.CreateObject(”ADODB.Stream”)
objStream.Open
objStream.Type =
adTypeBinary
objStream.LoadFromFile FilePath

lSize =
objStream.size

CHUNK=2048
NumberofBlocks=(lSize -(lsize mod chunk))/chunk
For
lBlocks = 1 To NumberofBlocks
If Response.IsClientConnected = False Then Exit
For
Response.BinaryWrite objStream.Read(CHUNK)
Response.Flush
Next

objStream.Close
Set objStream = Nothing


c#
code:

HttpResponse Response = System.Web.HttpContext.Current.Response;

Response.Buffer = true;
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.AppendHeader(
“Content-Disposition”, “inline; filename=” +
System.Web.
HttpContext
.Current.Server.UrlEncode(SongName));
Response.AppendHeader(
“Content-Length”
,
Size.ToString());
Response.WriteFile(path);

size=2>/*

System.IO.FileStream fs = new
System.IO.FileStream(path, System.IO.FileMode.Open,
System.IO.FileAccess.Read);
int CHUNK = 2048;
byte[] buffer = new
byte[CHUNK];

while (fs.Read(buffer, 0, CHUNK) >
0)
{
      if
(!Response.IsClientConnected)
              
break;

     Response.OutputStream.Write(buffer, 0,
buffer.Length);
     Response.Flush();
}

fs.Close();
fs =
null;

*/

Update: I got it working, there was something in my base page class that was goofing the stream up somehow. I think it had to do with cookies, but I will investigate further.