1 class RunLoggerController < ApplicationController
 2 
 3   # NOTE: These classes are huge, but only because they are 80% comments. Normally,
 4   # there isn't much too controllers. Each action method is usually pretty compact.
 5 
 6   # the controller gets mapped to a url, and each method is accessed via that url.
 7   # for example, this controller (if nothing is changed via url routing tables) is
 8   # accessed at http://applicationurl/run_logger/action_method_name
 9   #
10   # If an action method is called which doesn't have a method here, but does have a 
11   # view template (as determined by filename), then the view is simply rendered.
12   # This is the case with run_logger/log_time. 
13 
14   def log_time_process
15     # the params hash holds the parameters sent with the request - it automagically maps
16     # parameters with names like 'runinfo[foo]' into a nested hash, which is cool.
17     
18     # this pulls the runinfo nested hash out of the params hash, and then coerces each 
19     # value to a Fixnum (via the #to_f method)
20     runinfo = params['runinfo']
21     runinfo.each { |key, value| runinfo[key] = value.to_f }
22 
23     # parses the date information into a Date object
24     date = params['date']
25     date.each { |key, value| date[key] = value.to_f }
26     date = Date.new date['year'], date['month'], date['day']
27     
28     # since we can't prepopulate the session at initialization (it can't exist until
29     # there is a request to key the session off of), we have to initialize the session
30     # variable run_log if it's not set already.
31     #
32     # note: a ruby idiom is that this could also be said "session[:run_log] ||= []"
33     session[:run_log] = [] if session[:run_log].nil?
34 
35     # now we _know_ we have an array in the session, go ahead and push the new entry
36     # object onto it.
37     session[:run_log] << RunLogEntry.new(runinfo['time'], runinfo['distance'], date)
38     
39     # we're done processing the new entry, go ahead and redirect to the report_time
40     # action (right below us)
41     redirect_to :action => 'report_time'
42   end
43 
44   def report_time
45     # same idea as above, we avoid a nil reference by taking either session[:run_log] or
46     # an empty array if that is nil
47     @run_log = session[:run_log] || []
48 
49     # default our average hash
50     @average = {'distance' => 0, 'time' => 0}
51     
52     # if the array is empty, don't calculate averages. I added the #average method to the
53     # array class below - it's not very robust, but hey, it works for this application.
54     unless @run_log.length == 0
55       @average['distance'] = @run_log.collect { |e| e.distance }.average
56       @average['time'] = @run_log.collect { |e| e.time }.average
57     end
58 
59     # if we don't do any sort of redirect or render, the controller automatically falls
60     # through to rendering the view template named the same as the action. In this case
61     # app/views/run_logger/report_time.rhtml
62     #
63     # Also, the reason we put the @run_log array and @average hash into instance variables
64     # is that rails makes those instance variables visible to the view as instance variables
65     # in the view template rhtml file.
66   end
67 
68   def clear_log
69     # reset the run_log array in the session to an empty array
70     session[:run_log] = []
71 
72     # go back to our time reporting action
73     redirect_to :action => 'report_time'
74   end
75 end
76 
77 # One of the coolest (and most dangerous if you're trying to break things) things in ruby
78 # is that you can open up classes and insert methods into them (or override them). Here, 
79 # we add an #average method to the Array class, which then is available to _all_ arrays, 
80 # including the ones we calculate for averages above.
81 class Array
82   def average
83     sum = 0
84     self.each { |v| sum += v }
85     sum / self.length
86   end
87 end