gooby 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/README +48 -67
  2. data/bin/20050305_corporate_cup_hm_to_csv.rb +9 -0
  3. data/bin/20050430_nashville_marathon_to_csv.rb +9 -0
  4. data/bin/20060115_phoenix_marathon_to_csv.rb +9 -0
  5. data/bin/activity_2007_03_10_13_02_32.xml_to_csv.rb +9 -0
  6. data/bin/{tests_gen.rb → code_scan.rb} +6 -4
  7. data/bin/example_usage.rb +2 -3
  8. data/bin/example_usage.sh +52 -0
  9. data/bin/gen_gap_phx.rb +10 -0
  10. data/bin/gen_gmap_cc_2005.rb +10 -0
  11. data/bin/gen_gmap_cc_2007.rb +10 -0
  12. data/bin/gen_gmap_nashville.rb +10 -0
  13. data/bin/gen_gmap_phx.rb +10 -0
  14. data/bin/phx_to_csv.rb +47 -0
  15. data/bin/split_forerunner_logbook_2007.rb +10 -0
  16. data/bin/split_training_center_2007.rb +8 -0
  17. data/data/{2007_03_04.tcx → 2007_03_10.tcx} +11694 -0
  18. data/data/activity_2007_03_10_13_02_32.csv +1168 -0
  19. data/data/activity_2007_03_10_13_02_32.xml +11695 -0
  20. data/data/forerunner_2007.xml +0 -227142
  21. data/img/gicons/readme.txt +3 -0
  22. data/lib/gooby.rb +625 -552
  23. data/samples/20050305_corporate_cup_hm.html +271 -269
  24. data/samples/20050430_nashville_marathon.html +1240 -246
  25. data/samples/20060115_phoenix_marathon.html +1596 -0
  26. data/samples/20070310_corporate_cup_hm.html +1367 -0
  27. data/samples/phoenix_marathon.html +1312 -258
  28. data/tests/ts_gooby.rb +423 -551
  29. data/tests/ts_gooby_min.rb +550 -0
  30. metadata +25 -67
  31. data/bin/tcx_ex.rb +0 -35
  32. data/data/20051124_hyatt_turkey_trot_8K.csv +0 -321
  33. data/data/20051124_hyatt_turkey_trot_8K.xml +0 -2651
  34. data/data/2007_03_03.tcx +0 -6207
  35. data/data/activity_2007_03_04_15_22_36.xml +0 -10545
  36. data/data/run_2007_01_01_16_38_27.xml +0 -2020
  37. data/data/run_2007_02_24_15_01_35.csv +0 -484
  38. data/data/run_2007_02_24_15_01_35.xml +0 -3884
  39. data/lib/gooby/cls_counter_hash.rb +0 -78
  40. data/lib/gooby/cls_delim_line.rb +0 -35
  41. data/lib/gooby/cls_dttm.rb +0 -79
  42. data/lib/gooby/cls_duration.rb +0 -79
  43. data/lib/gooby/cls_forerunner_xml_parser.rb +0 -178
  44. data/lib/gooby/cls_forerunner_xml_splitter.rb +0 -109
  45. data/lib/gooby/cls_geo_data.rb +0 -181
  46. data/lib/gooby/cls_gooby_command.rb +0 -46
  47. data/lib/gooby/cls_gooby_object.rb +0 -18
  48. data/lib/gooby/cls_google_map_generator.rb +0 -363
  49. data/lib/gooby/cls_history.rb +0 -33
  50. data/lib/gooby/cls_lap.rb +0 -22
  51. data/lib/gooby/cls_line.rb +0 -75
  52. data/lib/gooby/cls_options.rb +0 -67
  53. data/lib/gooby/cls_position.rb +0 -44
  54. data/lib/gooby/cls_run.rb +0 -194
  55. data/lib/gooby/cls_simple_xml_parser.rb +0 -41
  56. data/lib/gooby/cls_test_regen.rb +0 -182
  57. data/lib/gooby/cls_track.rb +0 -47
  58. data/lib/gooby/cls_trackpoint.rb +0 -200
  59. data/lib/gooby/cls_training_center_parser.rb +0 -183
  60. data/lib/gooby/cls_training_center_splitter.rb +0 -109
  61. data/lib/gooby/mod_introspect.rb +0 -26
  62. data/lib/gooby/mod_io.rb +0 -58
  63. data/lib/gooby/mod_project_info.rb +0 -80
  64. data/lib/gooby/mod_string.rb +0 -19
  65. data/lib/gooby/mod_test_helper.rb +0 -15
  66. data/samples/20041113_richmond_marathon.html +0 -532
  67. data/samples/run_2007_01_10_22_44_54.html +0 -201
  68. data/samples/run_2007_02_24_15_01_35.html +0 -298
  69. data/tests/tc_cls_counter_hash.rb +0 -107
  70. data/tests/tc_cls_delim_line.rb +0 -74
  71. data/tests/tc_cls_dttm.rb +0 -131
  72. data/tests/tc_cls_duration.rb +0 -51
  73. data/tests/tc_cls_forerunner_xml_parser.rb +0 -70
  74. data/tests/tc_cls_geo_data.xxx +0 -71
  75. data/tests/tc_cls_gooby_object.rb +0 -26
  76. data/tests/tc_cls_google_map_generator.rb +0 -109
  77. data/tests/tc_cls_history.rb +0 -46
  78. data/tests/tc_cls_lap.rb +0 -38
  79. data/tests/tc_cls_line.rb +0 -110
  80. data/tests/tc_cls_options.rb +0 -79
  81. data/tests/tc_cls_position.rb +0 -66
  82. data/tests/tc_cls_run.rb +0 -142
  83. data/tests/tc_cls_simple_xml_parser.rb +0 -50
  84. data/tests/tc_cls_track.rb +0 -70
  85. data/tests/tc_cls_trackpoint.rb +0 -145
  86. data/tests/tc_mod_introspect.rb +0 -32
  87. data/tests/tc_mod_io.rb +0 -53
  88. data/tests/tc_mod_project_info.rb +0 -79
  89. data/tests/tc_mod_string.rb +0 -58
  90. /data/data/{phx.csv → 20060115_phoenix_marathon.csv} +0 -0
  91. /data/data/{phx.xml → 20060115_phoenix_marathon.xml} +0 -0
@@ -1,75 +0,0 @@
1
- module Gooby
2
-
3
- =begin rdoc
4
-
5
- =end
6
-
7
- class Line < GoobyObject
8
-
9
- attr_accessor :raw_data, :tokens
10
-
11
- def initialize(raw='', delim=nil, strip=false)
12
- if strip
13
- @raw_data = raw.strip
14
- else
15
- @raw_data = raw
16
- end
17
-
18
- @tokens = tokenize(@raw_data, delim, strip=false)
19
- end
20
-
21
- public
22
-
23
- def token(idx)
24
- @tokens[idx]
25
- end
26
-
27
- def token_count
28
- @tokens.size
29
- end
30
-
31
- def token_idx_equals(idx, value)
32
- if idx < token_count
33
- if @tokens[idx] == value
34
- return true
35
- end
36
- end
37
- false
38
- end
39
-
40
- def match(pattern)
41
- @raw_data.match(pattern)
42
- end
43
-
44
- def is_comment
45
- s = @raw_data.strip
46
- (s.match('^#')) ? true : false
47
- end
48
-
49
- def is_populated_non_comment
50
- s = @raw_data.strip
51
- if s.size == 0
52
- return false
53
- end
54
- if is_comment
55
- return false
56
- end
57
- return true
58
- end
59
-
60
- def concatinate_tokens(start_idx = 0)
61
- s = ''
62
- idx = -1
63
- @tokens.each { |tok|
64
- idx = idx + 1
65
- if idx >= start_idx
66
- s << tok
67
- s << ' '
68
- end
69
- }
70
- s.strip!
71
- s
72
- end
73
- end
74
-
75
- end # end of module
@@ -1,67 +0,0 @@
1
- module Gooby
2
-
3
- class Options < GoobyObject
4
-
5
- attr_reader :yamlFilename, :options
6
-
7
- def initialize(filename) # Constructor.
8
- filename ? @yamlFilename = filename : @yamlFilename = 'gooby_options.yaml'
9
- loadFile()
10
- end
11
-
12
- public
13
-
14
- # Load the @yamlFilename
15
- def loadFile
16
- File.open("#{@yamlFilename}") { |fn|
17
- @options = YAML::load(fn)
18
- }
19
- end
20
-
21
- def get(name)
22
- if name == nil
23
- return ''
24
- end
25
- s = @options["#{name}"]
26
-
27
- # Provide "sensible defaults".
28
- if s == nil
29
- if (name == '')
30
- return ''
31
- elsif (name == 'gmap_first_tkpt_number')
32
- return 1
33
- elsif (name == 'gmap_last_tkpt_number')
34
- return 5000
35
- elsif (name == 'gmap_map_element_id')
36
- return 'map'
37
- elsif (name == 'gmap_height')
38
- return '600'
39
- elsif (name == 'gmap_key')
40
- return 'enter your Google Map Key here'
41
- elsif (name == 'gmap_type_control')
42
- return true
43
- elsif (name == 'gmap_approx_max_points')
44
- return '200'
45
- elsif (name == 'gmap_gen_comments')
46
- return true
47
- elsif (name == 'gmap_size_control')
48
- return nil
49
- elsif (name == 'gmap_type')
50
- return 'G_NORMAL_MAP'
51
- elsif (name == 'gmap_zoom_level')
52
- return 5
53
- else
54
- return ''
55
- end
56
- end
57
- s
58
- end
59
-
60
- # Return a String containing yaml filename and entry count.
61
- def to_s
62
- return "Options: filename: #{@yamlFilename} entries: #{@options.size}"
63
- end
64
-
65
- end
66
-
67
- end # end of module
@@ -1,44 +0,0 @@
1
- module Gooby
2
-
3
- =begin rdoc
4
- Instances of this class represent a <Position> aggregate object from a
5
- Forerunner XML file. Each contains a latitude and longitude.
6
- Instances within a Trackpoint will also contain an altitude.
7
- =end
8
-
9
- class Position < GoobyObject
10
-
11
- attr_accessor :latitude, :longitude, :altitude, :note
12
-
13
- def initialize(lat, lng, alt='0', note='')
14
- @latitude = lat.to_s
15
- @longitude = lng.to_s
16
- @altitude = alt.to_s
17
- @note = note.to_s
18
- end
19
-
20
- public
21
-
22
- def to_s
23
- return "lat: #{@latitude} lng: #{@longitude} alt: #{@altitude} note: #{@note}"
24
- end
25
-
26
- def to_csv
27
- return "#{@latitude} | #{@longitude} | #{@altitude}"
28
- end
29
-
30
- def latitude_as_float
31
- @latitude ? @latitude.to_f : invalid_latitude
32
- end
33
-
34
- def longitude_as_float
35
- @longitude ? @longitude.to_f : invalid_longitude
36
- end
37
-
38
- def altitude_as_float
39
- @altitude ? @altitude.to_f : invalid_altitude
40
- end
41
-
42
- end
43
-
44
- end # end of module
data/lib/gooby/cls_run.rb DELETED
@@ -1,194 +0,0 @@
1
- module Gooby
2
-
3
- =begin rdoc
4
- Instances of this class represent a <Run> aggregate object from a
5
- Forerunner XML file.
6
-
7
- Additionally, there is distance, pace, and Google Map generation logic
8
- in this class.
9
- =end
10
-
11
- class Run < GoobyObject
12
-
13
- attr_accessor :number, :descr, :notes, :tracks, :tkpts, :laps, :distance
14
-
15
- def initialize(number=0, descr='')
16
- @number = number
17
- @descr = descr
18
- @notes = ''
19
- @tracks = Array.new
20
- @tkpts = Array.new
21
- @laps = Array.new
22
- @distance = 0
23
- @options = Hash.new
24
- @logProgress = true
25
- @finished = false
26
- end
27
-
28
- public
29
-
30
- # This method is invoked at end-of-parsing.
31
- def finish()
32
- @logProgress = false
33
- unless @finished
34
- @tracks.each { |trk|
35
- trk.trackpoints().each { |tkpt|
36
- tkpt.runNumber = @number
37
- @tkpts.push(tkpt)
38
- }
39
- }
40
- compute_distance_and_pace
41
- compute_splits
42
- @finished = true
43
- end
44
- end
45
-
46
- public
47
-
48
- def add_track(trk)
49
- if trk != nil
50
- @tracks.push(trk)
51
- end
52
- end
53
-
54
- def trackpoint_count()
55
- @tkpts.size()
56
- end
57
-
58
- def add_lap(lap)
59
- @laps.push(lap)
60
- end
61
-
62
- def lapCount()
63
- @laps.size
64
- end
65
-
66
- def start_dttm()
67
- count = 0
68
- @tracks.each { |trk|
69
- trk.trackpoints().each { |tkpt|
70
- return tkpt.dttm()
71
- }
72
- }
73
- return nil
74
- end
75
-
76
- def end_dttm()
77
- lastOne = nil
78
- @tracks.each { |trk|
79
- trk.trackpoints().each { |tkpt|
80
- lastOne = tkpt.dttm()
81
- }
82
- }
83
- lastOne
84
- end
85
-
86
- def duration()
87
- first = start_dttm()
88
- last = end_dttm()
89
- if first
90
- if last
91
- return last.hhmmss_diff(first)
92
- end
93
- end
94
- return "??:??:??"
95
- end
96
-
97
- def start_yyyy_mm_dd
98
- if start_dttm()
99
- start_dttm().yyyy_mm_dd()
100
- else
101
- ""
102
- end
103
- end
104
-
105
- def start_hh_mm_ss
106
- if start_dttm()
107
- start_dttm().hh_mm_ss()
108
- else
109
- ""
110
- end
111
- end
112
-
113
- def end_hh_mm_ss
114
- if end_dttm()
115
- end_dttm().hh_mm_ss()
116
- else
117
- ""
118
- end
119
- end
120
-
121
- def to_s
122
- finish() unless @finished
123
- s = "Run: #{@number} date: #{start_yyyy_mm_dd} distance: #{distance} duration: #{duration} "
124
- s << " tracks: #{@tracks.size} tkpts: #{trackpoint_count} laps: #{lapCount} "
125
- s << " notes: #{@notes} "
126
- s
127
- end
128
-
129
- def print_string
130
- finish() unless @finished
131
- "Run number=#{@number} tracks=#{@tracks.size} tkpts=#{@tkpts.size} laps=#{@laps.size} distance=#{@distance} "
132
- end
133
-
134
- def put_csv()
135
- finish() unless @finished
136
- puts "#{@number}|#{}|#{start_yyyy_mm_dd()}|#{start_hh_mm_ss()}|#{end_hh_mm_ss}|#{duration()}|#{@distance}|#{@tracks.size}|#{trackpoint_count()}|#{lapCount}|#{@notes.strip}"
137
- end
138
-
139
- def put_tkpt_csv(with_header_comment=false)
140
- finish() unless @finished
141
- if with_header_comment
142
- puts "# Run: #{@number} date: #{start_yyyy_mm_dd} dist: #{distance} dur: #{duration} trks: #{@tracks.size} tkpts: #{trackpoint_count} laps: #{lapCount} "
143
- end
144
- @tkpts.each { | tkpt | puts tkpt.to_csv }
145
- end
146
-
147
- def put_laps
148
- @laps.each { | lap | puts lap.to_s }
149
- end
150
-
151
- private
152
-
153
- def compute_distance_and_pace
154
- cumulative_dist = 0.to_f;
155
- curr_index = -1
156
- prev_tkpt = nil
157
- start_dttm = nil
158
- @tkpts.each { | tkpt |
159
- curr_index = curr_index + 1
160
- if curr_index == 0
161
- start_dttm = tkpt.dttm()
162
- prev_tkpt = tkpt
163
- else
164
- cumulative_dist = tkpt.compute_distance_and_pace(curr_index, start_dttm, cumulative_dist, prev_tkpt, 'M')
165
- prev_tkpt = tkpt
166
- end
167
- }
168
- @distance = cumulative_dist
169
- end
170
-
171
- def compute_splits
172
- nextSplitDist = 1.00
173
- prevSplitTkpt = nil
174
- loop1Count = 0;
175
- @tkpts.each { |tkpt|
176
- loop1Count = loop1Count + 1
177
- if tkpt.cumulativeDistance() >= nextSplitDist
178
- tkpt.set_split(0 + nextSplitDist, prevSplitTkpt)
179
- nextSplitDist = nextSplitDist + 1.00
180
- prevSplitTkpt = tkpt
181
- end
182
- }
183
- # set first and last booleans
184
- count = 0
185
- @tkpts.each { |tkpt|
186
- count = count + 1
187
- tkpt.first = true if count == 1
188
- tkpt.last = true if count == loop1Count
189
- }
190
- end
191
-
192
- end
193
-
194
- end # end of module
@@ -1,41 +0,0 @@
1
- module Gooby
2
-
3
- =begin rdoc
4
- Sample implementation of a REXML::StreamListener SAX parser.
5
- =end
6
-
7
- class SimpleXmlParser
8
-
9
- include REXML::StreamListener
10
-
11
- attr_accessor :tag_count, :watched_tags
12
-
13
- def initialize
14
- @tag_count = 0
15
- @counter_hash = CounterHash.new
16
- end
17
-
18
- public
19
-
20
- # SAX API method. Increments the tagname in the counter hash.
21
- def tag_start(tag_name, attrs)
22
- @tag_count = @tag_count + 1
23
- @counter_hash.increment(tag_name)
24
- end
25
-
26
- # SAX API method. No impl.
27
- def tag_end(tagname)
28
- end
29
-
30
- # SAX API method. No impl.
31
- def text(txt)
32
- end
33
-
34
- # Prints the state of this object (the counter hash).
35
- def dump
36
- puts @counter_hash.to_s
37
- end
38
-
39
- end
40
-
41
- end # end of module
@@ -1,182 +0,0 @@
1
- module Gooby
2
-
3
- =begin rdoc
4
- This class is used to generate, on an ongoing basis, the various Gooby test
5
- classes. Regeneration retains the current test methods, and adds stubs for
6
- new test methods. All methods, old and new, appear in the merged output in
7
- propper sort sequence.
8
- =end
9
-
10
- class TestGenerator < GoobyObject
11
-
12
- def initialize
13
- tested_files.each { | base_rb_file |
14
- test_file = "tc_#{base_rb_file}"
15
- if true
16
- @codeLines = read_lines("lib/#{base_rb_file}")
17
- @testLines = read_lines("tests/#{test_file}")
18
- @codeHash = Hash.new
19
- @testHash = Hash.new
20
- @mergedHash = Hash.new
21
- @excludeClasses = %w( TestHelper TestGenerator )
22
- puts "current codeLines = #{@codeLines.size}"
23
- puts "current testLines = #{@testLines.size}"
24
- parse_code_lines
25
- parse_test_lines
26
- merge_keys
27
- regenerate(test_file)
28
- end
29
- }
30
- end
31
-
32
- private
33
-
34
- # Produce a Hash with keys and values like the following:
35
- # test|class|Trackpoint|deg2rad Trackpoint|deg2rad(degrees)
36
-
37
- def parse_code_lines
38
- type = ''
39
- type_name = ''
40
- meth_name = ''
41
- @codeLines.each { | line |
42
- line.strip!
43
- if (line.match(/^module /))
44
- type = 'module'
45
- tokens = line.split
46
- type_name = tokens[1]
47
- elsif (line.match(/^class /))
48
- type = 'class'
49
- tokens = line.split
50
- type_name = tokens[1]
51
- elsif (line.match(/^def /))
52
- signature = line[4...999]
53
- short_method = parse_meth_name("#{signature}")
54
- @codeHash["test_#{type}_#{type_name}"] = "#{type_name}"
55
- @codeHash["test_#{type}_#{type_name}_#{short_method}"] = "#{type_name}|#{signature}"
56
- end
57
- }
58
- end
59
-
60
- def parse_meth_name(string)
61
- string.gsub!('(', ' ')
62
- string.gsub!(')', ' ')
63
- tokens = string.split
64
- tokens[0]
65
- end
66
-
67
- def parse_test_lines
68
- in_method = true
69
- method_name = 'a_start'
70
- method_lines = Array.new
71
- line_num = 0
72
-
73
- @testLines.each { | line |
74
- line_num = line_num + 1
75
- line.chomp!
76
- prefix = line[0...5] # ' def'
77
- prefix21 = line[0...21] # ' ### Start of tests.'
78
-
79
- if ((prefix == ' def') || (prefix == "\tdef"))
80
- in_method = true
81
- tokens = line.split
82
- method_name = tokens[1]
83
- method_lines = Array.new
84
- end
85
- if in_method
86
- method_lines << "#{line}"
87
- end
88
- if prefix21 == ' ### Start of tests.'
89
- in_method = false
90
- @testHash["#{method_name}"] = method_lines
91
- end
92
- if ((prefix == ' end') || (prefix == "\tend"))
93
- in_method = false
94
- @testHash["#{method_name}"] = method_lines
95
- end
96
- }
97
- end
98
-
99
- def merge_keys
100
- @codeHash.keys.sort.each { |key| @mergedHash["#{key}"] = "code" }
101
- @testHash.keys.sort.each { |key| @mergedHash["#{key}"] = "test" }
102
- end
103
-
104
- def regenerate(test_file)
105
- code = ''
106
- @mergedHash.keys.sort.each { |key|
107
-
108
- tokens = key.split('_')
109
- type, name, meth = tokens[1], tokens[2], tokens[3]
110
-
111
- processThisKey = true
112
- @excludeClasses.each { |xc|
113
- if xc == name
114
- processThisKey = false
115
- end
116
- }
117
-
118
- next if !processThisKey
119
-
120
- if @testHash.has_key?(key)
121
- # We already have a test method written in the test class,
122
- # so keep this currently existing test code!
123
-
124
- if @codeHash.has_key?(key)
125
- comment = nil
126
- else
127
- if key != 'a_start'
128
- comment = "# Warning: possible obsolete test method - #{key}"
129
- end
130
- end
131
-
132
- code << "\n"
133
- if comment != nil
134
- code << "\n#{comment}"
135
- code << "\n"
136
- end
137
- array = @testHash["#{key}"]
138
- array.each { |line| code << "\n#{line}" }
139
- else
140
- # We don't have this test method in the current test class,
141
- # so generate a test method stub.
142
-
143
- code << "\n"
144
- code << "\n def #{key}"
145
- code << "\n"
146
-
147
- if @gen_impl_stub
148
- if type = 'class'
149
- code << "\n #obj = #{type}.new"
150
- code << "\n #result = obj.#{meth}"
151
- code << "\n #expected = ''"
152
- s = "\n"
153
- s << ' #assert_equal(expected, actual, "'
154
- s << "#{type}.#{meth} "
155
- s << 'values are not as expected; #{result} vs #{expected}")'
156
- code << s
157
- else
158
- code << "\n #result = #{type}.#{meth}"
159
- code << "\n #expected = ''"
160
- s = "\n"
161
- s << ' #assert_equal(expected, actual, "'
162
- s << "#{type}.#{meth} "
163
- s << 'values are not as expected; #{result} vs #{expected}")'
164
- code << s
165
- end
166
- end
167
- code << "\n end"
168
- end
169
- }
170
- code << "\nend" # end of class
171
- code << "\n"
172
- fn = "tests/#{test_file}"
173
- out = File.new fn, "w+"
174
- out.write code
175
- out.flush
176
- out.close
177
- puts "file written: #{fn}"
178
- end
179
-
180
- end
181
-
182
- end # end of module
@@ -1,47 +0,0 @@
1
- module Gooby
2
-
3
- =begin rdoc
4
- Instances of this class represent a <Track> aggregate object from a Forerunner
5
- XML file. Note that a <Run> may contain more than one <Track> aggregates.
6
- =end
7
-
8
- class Track < GoobyObject
9
-
10
- attr_reader :number, :descr, :trackpoints
11
-
12
- def initialize(num=0, descr='')
13
- @number = num
14
- @descr = descr
15
- @trackpoints = Array.new
16
- end
17
-
18
- public
19
-
20
- def add_trackpoint(tkpt)
21
- @trackpoints.push(tkpt)
22
- end
23
-
24
- def size
25
- @trackpoints.size
26
- end
27
-
28
- def first_tkpt
29
- @trackpoints.size > 0 ? @trackpoints[0] : nil
30
- end
31
-
32
- def last_tkpt
33
- @trackpoints.size > 0 ? @trackpoints[-1] : nil
34
- end
35
-
36
- def to_s
37
- return "Trk: #{@descr} tkpts: #{size}"
38
- end
39
-
40
- def dump
41
- puts "Track: '#{@descr}' tkpts: #{size}"
42
- @trackpoints.each { |tkpt| puts tkpt.to_csv } # to_geo_s
43
- end
44
-
45
- end
46
-
47
- end # end of module