Web 2.0 Tutorial

First of all, I have to make a disappointing confession: this is not a Web 2.0 tutorial – but fear not, at least the logical and absolutely valid question to this dilemma (i.e. why the hell is the article entitled ‘Web 2.0 tutorial’ then?) will be provided.

Although this blog’s tagline is ‘Ruby, Rails, Web2.0’ and I am blogging/planning to blog about all these topics in the future, I did not have an exclusively-and-only-about-Web2.0 post yet (as far as I remember). That’s why it strikes me odd that according to google analytics, a lot of people are finding this site via the keyword combination ‘Web2.0 tutorial’. This post was inspired by them and for them!

Since this trend is nearly as old as this blog – and it seems to continue, and even rise as time goes by – I am now really curious what the heck are people imagining behind the term ‘Web2.0 tutorial’. Why? Well, there are more reasons to ponder about:

– Nobody knows what Web 2.0 actually is (or if does, the others don’t agree :-)). Since coined by Tim O’Reilly back in 2005, ‘Web 2.0’ has been redefined, argued about, glorified, despised, parodied, upgraded to Web 3.0, regarded as vapor, bubble etc. (and who knows what else…) countless times – just one thing did not happen: A commonly accepted, concise (or even lengthy) definition with which everybody would agree.
You won’t find anybody interested in the Web today who would not have his own definition associated with Web2.0 – however, these definitions (although more overlapping and similar than ever) will be varying from person to person.

– The conjunction itself is kind of absurd – even if we accept that there is a common understanding of the term ‘Web2.0’, it definitely has more facets: Look (Apple aqua reinvented, round corners galore, reflections of reflections etc), social aspect (digg, del.icio.us, youTube, myspace et al), theoretical backend (ontologies, folksonomies, openAPIs, microformats, mashups etc), standards (XHTML (2.0! :-)), RDF, FOAF, ATOM, SVG, SOAP), innovative ways of communication and catering to the users (WS, REST, Podcasts, Videocasts), typical Web2.0-purpose pages (wikis, blogs), development tools and frameworks (AJAX, Ruby on Rails, …) and other buzzwords 🙂

– Even if we define Web2.0 as a collection of the things from the previous point, the term ‘Web 2.0 tutorial’ is too broad-sense to get you too much relevant results (I believe – maybe some smart webmasters engaged in the ways of SEO tricking found out the carving after a Web2.0 tutorial already and wrote up a few for you :-)). Just as someone would not search a ‘programming language tutorial’ (but a ‘Ruby tutorial’ instead) or a ‘sport tutorial’ (rather a ‘squash tutorial’), searching after a *real* ‘Web2.0 tutorial’ could be ineffective, too. I suggest to look for ’rounded corners tutorial’, ‘mashup tutorial’ or ‘Ruby on Rails tutorial’ etc. instead. Additionally, if you are really keen on Web2.0-ness of these documents, don’t forget to add ‘Web2.0’ to the query – just in case :-).

– Related to the previous point: attack the problem from bottom up rather than the other way around – i.e. try to look for solutions of concrete problems and assemble them into a Web2.0 style whatever once you are done, rather than trying to do something which is Web2.0 in the first place. In my opinion you should think like ‘I would like to create a great mashup in Ruby on Rails with AJAX and a Web2.0 look – how should I go about this?’ rather than ‘Let’s see a good Web 2.0 tutorial and then I will cook something great’. You should strive for creating great looking websites with great content and functionality, and people will like it and use it – whether you call it Web2.0, Web3.0 or whatever – even if the URL of the site will be www.thissiteisnotweb2.0.com :-).

Now that I have mentioned ‘Web2.0’ and ‘Web 2.0 tutorial’ more times in this article, I guess I’ll be receiving even more hits through this query – though this was definitely not the reason for writing this article. However, if you already got this far, please take a few seconds and share with us your thoughts on this. After all Web2.0 is also about collaboration, you know. Heck, I might even write a few Web2.0 tutorials in the future – just tell me what a ‘Web2.0 tutorial’ means… :-).

Implementing ’15 Exercises for Learning a new Programming Language’

A short time ago in a galaxy not so far, far away I came across a nice blog post: 15 Exercises for Learning a new Programming Language.

One could argue if these are *really* the most appropriate 15(+) exercises to learn a new programming language – however, the task of answering this rather complex question is left as an exercise for the reader. Instead of this I will show you their implementation in Ruby – rubyrailways.com style.

Why did I bother to solve these problems (including not really trivial ones, like a scientific calculator with a GUI) ? Well, actually to learn a new programming language! I still consider myself a beginner Ruby apprentice just playing it by ear in my somewhat scarce free time, so I thought that systematically implementing a task list like this will mean great step forward for me compared to just coding random things at random times. Fortunately I was perfectly right!

Before we move onto the code, one last disclaimer: the fact that I am still a Ruby n00b implies that the code can be somewhat hairy/not optimal/[insert any other language than Ruby here]-ish so don’t use these snippets as a textbook solution of the problems or anything like that. I would be glad if someone could suggest a bit of refactoring of the bad parts but I also hope that that there are some nice parts which you can learn from (actually I am quite sure about this since I used some magick formulas from a few Ruby (grand)masters in some cases).

OK, enough talk for now. Let’s see the stuff!

1. Problem: “Display series of numbers (1,2,3,4, 5….etc) in an infinite loop. The program should quit if someone hits a specific key (Say ESCAPE key).”

Solution: Hmm, well, errr…uh-oh… I could not solve this problem fully (what a terrific start :-)). If Henry Ford would sit beside me now, he would say : You can hit any key to exit – so long as it’s ‘C’ – and one more advice: don’t forget to hold CTRL during this action :-). More on this after the code snippet:

i = 0
loop { print "#{i+=1}, " }

Comments :
If anyone knows how to add code which will cause this program to stop with a specific keyhit (say ‘ESC’) please, please, please drop me a note. I have been researching this for at least 10% of the time of solving all the tasks, nearly spitting blood when I gave up :-). It seems (to me) that there is no simple (i.e. no threads and similar) and clean platform-independent solution for this problem. I guess (hope) the author’s idea here was different than to introduce threading or writing platform specific-code…

2. Problem: “Fibonacci series, swapping two variables, finding maximum/minimum among a list of numbers.”

Solution:

#Fibonacci series
Fib = Hash.new{ |h, n| n < 2 ? h[n] = n : h[n] = h[n - 1] + h[n - 2] }
puts Fib[50]

#Swapping two variables
x,y = y,x

#Finding maximum/minimum among a list of numbers
puts [1,2,3,4,5,6].max
puts [7,8,9,10,11].min

Comments: The Fibonacci code was written by Andrew Johnson (found via Ruby Quiz). I like it so much that I think it would be a shame to present a trivial version here. I guess the rest of the code is self-explanatory.

3. Problem: "Accepting series of numbers, strings from keyboard and sorting them ascending, descending order."

Solution:

a = []
loop { break if (c = gets.chomp) == 'q'; a << c }
p a.sort
p a.sort { |a,b| b<=>a }

Comments: This version is accepting strings - I think anybody who got to this point can adapt it to work with numbers.

4. Problem: "Reynolds number is calculated using formula (D*v*rho)/mu Where D = Diameter, V= velocity, rho = density mu = viscosity Write a program that will accept all values in appropriate units (Don't worry about unit conversion) If number is < 2100, display Laminar flow, If it’s between 2100 and 4000 display 'Transient flow' and if more than '4000', display 'Turbulent Flow' (If, else, then...)"

Solution:

vars = %w{D V Rho Mu}

vars.each do |var|
  print "#{var} = "
  val = gets
  eval("#{var}=#{val.chomp}")
end

reynolds = (D*V*Rho)/Mu.to_f

if (reynolds < 2100)
  puts "Laminar Flow"
elsif (reynolds > 4000)
  puts "Turbulent Flow"
else
  puts "Transient Flow"
end

Comments: Can you spot the trick in the part which is filling up the variables? They don't go out of scope after the loop ends because they are constants. Other possibility would be to use $global variables but I guess it is usually not a very good programming practice to do that.

5. Problem: "Modify the above program such that it will ask for 'Do you want to calculate again (y/n), if you say 'y', it'll again ask the parameters. If 'n', it'll exit. (Do while loop)
While running the program give value mu = 0. See what happens. Does it give 'DIVIDE BY ZERO' error? Does it give 'Segmentation fault..core dump?'. How to handle this situation. Is there something built in the language itself? (Exception Handling)"

Solution:

vars = { "d" => nil, "v" => nil, "rho" => nil, "mu" => nil }

begin
  vars.keys.each do |var|
    print "#{var} = "
    val = gets
    vars[var] = val.chomp.to_i
  end

  reynolds = (vars["d"]*vars["v"]*vars["rho"]) / vars["mu"].to_f
  puts reynolds

  if (reynolds < 2100)
    puts "Laminar Flow"
  elsif (reynolds > 4000)
    puts "Turbulent Flow"
  else
    puts "Transient Flow"
  end

  print "Do you want to calculate again (y/n)? "
end while gets.chomp != "n"

Comments: As you can see, I could not use the same trick here when asking for the variables, because when somebody wants to calculate again, Ruby will complain (although by printing a warning only) that the constants have been already set up. Therefore I went for the hash solution. I think the do-you-want-to-calculate-again part is straightforward so I won't analyze that here.
"While running the program give value mu = 0."
Ruby gives a rather interesting result in this case: infinity :-).
"Is there something built in the language itself?"
Sure: exception handling. Division by zero could be caught with a ZeroDivisionError rescue clause.

6. Problem: "Scientific calculator supporting addition, subtraction, multiplication, division, square-root, square, cube, sin, cos, tan, Factorial, inverse, modulus"

Solution:
Since this code snippet is longer It would look ugly here - you can download it from here instead.

Screenshot:


screenshot of the scientific calculator in action

If you would like to try it, you will need the Tk bindings for Ruby (maybe you have them already, here on Ubuntu I did not). Also note that only the regular 0-9 keys (and of course the mouse) work, the numpad ones do not. One more little detail: % stands for modulo, not percent.

Comments: Phew, this was a real challenge, mostly because I never did any GUI in Ruby before. I was amazed that I could code up a relatively feature rich calculator in 100+ lines of code, without any golfing or trying to optimize for shortness. What I wanted to say with this is that the shortness does not praise my programming skills (since I did not eve try to golf) but the superb terseness of Ruby. OK, of course there are some problems (e.g. cube, cos, tan, inverse are not implemented) but the usability/amount of code ratio is unbelievably high.

The GUI is also not the nicest since I have used Tk - wxRuby or qt-ruby would produce much nicer results, but since I did not code any GUI in Ruby previously, I have decided to try the good-old-skool Tk for the first time.

7. Problem: "Printing output in different formats (say rounding up to 5 decimal places, truncating after 4 decimal places, padding zeros to the right and left, right and left justification)(Input output operations)"

Solution:

#rounding up to 5 decimal pleaces
puts sprintf("%.5f", 124.567896)

#truncating after 4 decimal places
def truncate(number, places)
  (number * (10 ** places)).floor / (10 ** places).to_f
end

puts truncate(124.56789, 4)

#padding zeroes to the left
puts 'hello'.rjust(10,'0')

#padding zeroes to the right
puts 'hello'.ljust(10,'0')

#right justification
puts ">>#{'hello'.rjust(20)}<<"

#left justification
puts ">>#{'hello'.ljust(20)}<<"

Comments: Amazingly lot of things can be done with sprintf() - I could solve nearly all the problems with it - but that would not really be rubyish, so I have decided for built-in (and one homegrown) functions. However, mastering (s)printf() is a very handy thing, since nearly all big players (C (of course :-)), C++, Java, PHP, ... ) have it so you get a powerful function in more languages for the price of learning one). As you can see, r/ljust is a nice one, too.

8. Problem: "Open a text file and convert it into HTML file. (File operations/Strings)"

Solution: Well, this problem was not specified in a great detail, to say the least - or to put it otherwise, the solvers are given a great freedom to provide a solution spiced up with their fantasy. This is what I came up with:

doc = <strong tag.
DOC

final_doc = <
  
    Text to HTML fun!
  
  
    

embed_doc_here

FINAL_DOC rules = {'*something*' => 'something', '/something/' => 'something'} rules.each do |k,v| re = Regexp.escape(k).sub(/something/) {"(.+?)"} doc.gsub!(Regexp.new(re)) do content = $1 v.sub(/something/) { content } end end doc.gsub!("\n\n") {"

\n

"} final_doc.sub!(/embed_doc_here/) {doc} puts final_doc

Comments: As you can see, besides that the text is wrapped around with a minimal HTML, every occurrence of words between asterisks is outputted in strong and between slashes in italic. You can add as many such rules as you like, they will be (hopefully) substituted in the final output.

9. Problem: "Time and Date : Get system time and convert it in different formats 'DD-MON-YYYY', 'mm-dd-yyyy', 'dd/mm/yy' etc."

Solution: Well, it was not really clear (for me) what should be the difference between 'yyyy' and 'YYYY' (resp. 'dd' vs 'DD') so again I had to use my imagination. However, I guess it does not matter too much, the solution has to be changed by 1-2 characters only if the original author had something different on his mind.

require 'date'

time = Time.now
#'DD-MON-YYYY', e.g. 12-Nov-2006 in my interpetation
puts time.strftime("%d-%b-%Y")

#'mm-dd-yyyy', e.g. 11-12-2006 in my interpetation
puts time.strftime("%m-%d-%Y")

#'dd/mm/yy', e.g. 12/11/2006 in my interpetation
puts time.strftime("%d/%m/%Y")

10. Problem: "Create files with date and time stamp appended to the name"

Solution:

#Create files with date and time stamp appended to the name
require 'date'

def file_with_timestamp(name)
  t = Time.now
  open("#{name}-#{t.strftime('%m.%d')}-#{t.strftime('%H.%M')}", 'w')
end

my_file = file_with_timestamp('test.txt')
my_file.write('This is a test!')
my_file.close

Comments: Maybe a more elegant solution could be to subclass File and override its constructor - but maybe that would be an overkill. I have voted for the latter option in this case :-).

11. Problem: "Input is HTML table. Remove all tags and put data in a comma/tab separated file."

Solution: Since web extraction is both my PhD topic and my everyday job (and even my free-time activity :-)) I will present 3 solutions for this problem. First, the classic old-school regexp way (by Paul Lutus), then with HPricot and finally with scRUBYt!, a simple yet powerful Ruby web extraction framework currently developed by me.

table = <
  
    1
    2
  
  
    3
    4
    5
  
  
    6
  

DOC

rows = table.scan(%r{.*?}m)

rows.each do |row|
   fields = row.scan(%r{(.*?)}m)
   puts fields.join(",")
end

Now for the HPricot solution (in the further examples let's consider that table is initialized as in the previous example):

require 'rubygems'
require 'hpricot'

h_table = Hpricot(table)

rows = h_table/"//tr"
rows.each do |row|
  child_text = (row/"//td").collect {|elem| elem.innerHTML }
  puts child_text.join(',')
end

and last, but not least scRUBYt!

require 'scrubyt'

table_data = P.table do
               P.cell '1'
             end

table_data.generalize :cell

puts table_data.to_csv

Some explanation: first of all, at the moment scRUBYt! is avaliable on my hard disk (and partially in my head) only - it should be released around XMAS 2006. I am using this solution for a little bit of self-promotion :-).

The example works like this: extract something (in this case a HTML <table>) which has something (in this case <td>) which has '1' as its text (well in reality much more is going on in the background, but roughly along these lines). This little code snippet will extract the first <td>s of ALL <tables> on a HTML page. With the 'generalize' call we tell the extractor that it should not extract just the first <td> in a table (which is the default setting), but all of them.

scRUBYt! can handle much, much, MUCH more complicated examples than this (like an ebay or amazon page) and has loads of sophisticated functions... so stay tuned!

12. Problem: "Extract uppercase words from a file, extract unique words."

Solution: (you can find some_uppercase_words.txt here and some_repeating_words.txt here

open('some_uppercase_words.txt').read.split().each { |word| puts word if word =~ /^[A-Z]+$/ }

words = open('some_repeating_words.txt').read.split()
histogram = words.inject(Hash.new(0)) { |hash, x| hash[x] += 1; hash}
histogram.each { |k,v| puts k if v == 1 }

13. Problem: "Implement word wrapping feature (Observe how word wrap works in windows 'notepad')."

Solution: Unfortunately I am not a Windows user and I have seen notepad a *quite* long time ago - so I am not sure the task and it's implementation are fully in-line - I have tried my best. Here we go:

input = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

def wrap(s, len)
  result = ''
  line_length = 0
  s.split.each do |word|
    if line_length + word.length + 1  < len
      line_length += word.length + 1
      result += (word + ' ')
    else
      result += "\n"
      line_length = 0
    end
  end
  result
end

puts wrap(input, 30)

14. Problem: "Adding/removing items in the beginning, middle and end of the array."

Solution:

x = [1,3]

#adding to beginning
x.unshift(0)

#adding to the end
x << 4

#adding to the middle
x.insert(2,2)

#removing from the beginning
x.shift

#removing from the end
x.pop

#removing from the middle
x.delete(2)

#we have arrived at the original array!

15. Problem: "Are these features supported by your language: Operator overloading, virtual functions, references, pointers etc."

Solution: Well this is not a real problem (not in Ruby, at least). Ruby is a very high level language ant these things are a must :).

Finally, you can download all the solutions in a single archive from here.
I would like to see the implementation of these tasks in both Ruby (different (more optimal) solutions of course) as well as in anything else. If you set