apachecrunch 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,88 @@
1
+ require 'test/stub'
2
+ require 'test/mock'
3
+
4
+ class TestLogParser < Test::Unit::TestCase
5
+ def setup
6
+ @entry_parser = StubEntryParser.new
7
+ @inst = ApacheCrunch::LogParser.new(@entry_parser)
8
+ end
9
+
10
+ def teardown
11
+ @inst = nil
12
+ end
13
+
14
+ # Tests setting the log file to a given file
15
+ def test_set_file
16
+ first_log_file = MockFile.new
17
+ assert_nothing_thrown("#{@inst.class}#set_file! threw an exception") do
18
+ @inst.set_file!(first_log_file)
19
+ end
20
+
21
+ second_log_file = MockFile.new
22
+ assert_nothing_thrown("#{@inst.class}#set_file! threw an exception") do
23
+ @inst.set_file!(second_log_file)
24
+ end
25
+ assert_equal(1, first_log_file.close_count,
26
+ "#{@inst.class}#set_file! didn't close old file")
27
+ end
28
+
29
+ # Tests replacing the log file with another file
30
+ def test_replace_file
31
+ mock_file_class = MockFileClass.new
32
+ @inst.dep_inject!(mock_file_class)
33
+
34
+ first_log_file = MockFile.new
35
+ first_log_file.path = "/first/path"
36
+ @inst.set_file!(first_log_file)
37
+
38
+ second_log_file = MockFile.new
39
+ second_log_file.path = "/second/path"
40
+ assert_nothing_thrown("#{@inst.class}#replace_file! threw an exception") do
41
+ @inst.replace_file!(second_log_file)
42
+ end
43
+ assert_equal([["/second/path", "/first/path"]], mock_file_class.rename_calls,
44
+ "#{@inst.class}#replace_file! didn't move the file into place")
45
+ assert_equal(1, first_log_file.close_count,
46
+ "#{@inst.class}#replace_file! didn't close old file")
47
+ end
48
+
49
+ # Tests resetting the log file to its beginning
50
+ def test_reset_file
51
+ mock_file_class = MockFileClass.new
52
+ @inst.dep_inject!(mock_file_class)
53
+
54
+ log_file = MockFile.new
55
+ @inst.set_file!(log_file)
56
+
57
+ assert_nothing_thrown("#{@inst.class}#reset_file! threw an exception") do
58
+ @inst.reset_file!
59
+ end
60
+ assert_equal(1, log_file.close_count,
61
+ "#{@inst.class}#reset_file! didn't close the log file")
62
+ assert_equal(1, mock_file_class.open_calls.length,
63
+ "#{@inst.class}#reset_file! didn't reopen the log file")
64
+ end
65
+
66
+ # Tests retrieving the next entry in the log
67
+ def test_next_entry
68
+ @entry_parser.parse_return_values = [
69
+ StubEntry.new,
70
+ StubEntry.new,
71
+ nil,
72
+ StubEntry.new
73
+ ]
74
+
75
+ log_file = MockFile.new
76
+ log_file.lines = ["a\n", "b\n", "c\n", "d\n"]
77
+
78
+ @inst.set_file!(log_file)
79
+ @inst.set_format!(StubFormat.new)
80
+ assert_instance_of(StubEntry, @inst.next_entry,
81
+ "#{@inst.class}#next_entry returned wrong type of thing")
82
+ assert_instance_of(StubEntry, @inst.next_entry,
83
+ "#{@inst.class}#next_entry returned wrong second value")
84
+ assert_instance_of(StubEntry, @inst.next_entry,
85
+ "#{@inst.class}#next_entry returned wrong value when it should have skipped a malformatted entry")
86
+ assert_nil(@inst.next_entry, "#{@inst.class}#next_entry returned non-nil at EOF")
87
+ end
88
+ end
@@ -0,0 +1,36 @@
1
+ require 'test/stub'
2
+
3
+ class TestRawValueFetcher < Test::Unit::TestCase
4
+ def setup
5
+ @inst = ApacheCrunch::RawValueFetcher.new
6
+ end
7
+
8
+ def teardown
9
+ @inst = nil
10
+ end
11
+
12
+ # Tests a successful fetch call
13
+ def test_fetch
14
+ entry = StubEntry.new
15
+ alnum_element = StubElement.new
16
+ alnum_element.populate!(StubAlphanumericFormatToken.new, "foo123")
17
+ num_element = StubElement.new
18
+ num_element.populate!(StubNumericFormatToken.new, 54321)
19
+
20
+ entry.captured_elements = {:alnum => alnum_element, :num => num_element}
21
+ assert_equal("foo123", @inst.fetch(entry, StubAlphanumericFormatToken.new.name))
22
+ assert_equal(54321, @inst.fetch(entry, StubNumericFormatToken.new.name))
23
+ end
24
+
25
+ # Tests a fetch call for an element that's not there
26
+ def test_fetch_missing
27
+ entry = StubEntry.new
28
+ alnum_element = StubElement.new
29
+ alnum_element.populate!(StubAlphanumericFormatToken.new, "foo123")
30
+ num_element = StubElement.new
31
+ num_element.populate!(StubNumericFormatToken.new, 54321)
32
+
33
+ entry.captured_elements = {:alnum => alnum_element, :num => num_element}
34
+ assert_nil(@inst.fetch(entry, :missing_element))
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ class TestReqheaderToken < Test::Unit::TestCase
2
+ def setup
3
+ @inst = ApacheCrunch::RegexToken.new
4
+ end
5
+
6
+ def teardown
7
+ @inst = nil
8
+ end
9
+
10
+ # Tests that the token figures out its name correctly from the header
11
+ def test_name
12
+ @inst.populate!("foobar", "[A-Za-z0-9]+")
13
+
14
+ assert_equal(:regex_foobar, @inst.name,
15
+ "#{@inst.class} got wrong name based on regex name 'foobar'")
16
+ end
17
+ end
@@ -0,0 +1,41 @@
1
+ require 'test/stub'
2
+
3
+ class TestReqFirstlineDerivationRule < Test::Unit::TestCase
4
+ def setup
5
+ @inst = ApacheCrunch::ReqFirstlineDerivationRule.new
6
+ end
7
+
8
+ def teardown
9
+ @inst = nil
10
+ end
11
+
12
+ def test_derive
13
+ expected_names = @inst.target_names
14
+ datasets = [
15
+ ["GET / HTTP/1.1", {:req_method => "GET",
16
+ :url_path => "/",
17
+ :query_string => "",
18
+ :protocol => "HTTP/1.1"}],
19
+ ["HEAD /?herp=derp&foo=bar HTTP/1.0", {:req_method => "HEAD",
20
+ :url_path => "/",
21
+ :query_string => "?herp=derp&foo=bar",
22
+ :protocol => "HTTP/1.0"}],
23
+ ["POST /some/page?never=gonna HTTP/1.1", {:req_method => "POST",
24
+ :url_path => "/some/page",
25
+ :query_string => "?never=gonna",
26
+ :protocol => "HTTP/1.1"}]
27
+ ]
28
+
29
+ expected_names = @inst.target_names
30
+ datasets.each do |ds|
31
+ firstline_value = ds[0]
32
+ expected_values = ds[1]
33
+
34
+ expected_names = [:req_method, :url_path, :query_string, :protocol]
35
+ expected_names.each do |name|
36
+ assert_equal(expected_values[name], @inst.derive(name, firstline_value),
37
+ "#{@inst.class}#derive returned wrong value for element '#{name}'")
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,26 @@
1
+ class TestReqheaderToken < Test::Unit::TestCase
2
+ def setup
3
+ @inst = ApacheCrunch::ReqheaderToken.new
4
+ end
5
+
6
+ def teardown
7
+ @inst = nil
8
+ end
9
+
10
+ # Tests that the token figures out its name correctly from the header
11
+ def test_name
12
+ datasets = [
13
+ ['Host', :reqheader_host],
14
+ ['X-Two-Words', :reqheader_x_two_words]
15
+ ]
16
+
17
+ datasets.each do |ds|
18
+ header_name = ds[0]
19
+ expected_token_name = ds[1]
20
+
21
+ @inst.populate!(header_name)
22
+ assert_equal(expected_token_name, @inst.name,
23
+ "#{@inst.class} got wrong name based on header name '#{header_name}'")
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ require 'test/stub'
2
+
3
+ class TestStringToken < Test::Unit::TestCase
4
+ def setup
5
+ @inst = ApacheCrunch::StringToken.new
6
+ end
7
+
8
+ def teardown
9
+ @inst = nil
10
+ end
11
+
12
+ def test_regex
13
+ datasets = [
14
+ [' ', '\ '],
15
+ [' {has (regex [chars', '\ \{has\ \(regex\ \[chars']
16
+ ]
17
+
18
+ datasets.each do |ds|
19
+ string_value = ds[0]
20
+ expected_regex = ds[1]
21
+
22
+ @inst.populate!(string_value)
23
+ assert_equal(expected_regex, @inst.regex,
24
+ "#{@inst.class}#regex returned incorrect value")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ require 'test/stub'
2
+
3
+ class TestTimeDerivationRule < Test::Unit::TestCase
4
+ def setup
5
+ @inst = ApacheCrunch::TimeDerivationRule.new
6
+ end
7
+
8
+ def teardown
9
+ @inst = nil
10
+ end
11
+
12
+ # Tests that the expected list of derived elements matches what actually gets derived
13
+ def test_derive
14
+ expected_names = [:year, :month, :day, :hour, :minute, :second]
15
+ expected_values = {
16
+ :year => 2011,
17
+ :month => 7,
18
+ :day => 15,
19
+ :hour => 9,
20
+ :minute => 55,
21
+ :second => 41
22
+ }
23
+
24
+ expected_names.each do |name|
25
+ assert_equal(expected_values[name], @inst.derive(name, "[15/Jul/2011:09:55:41 +0400]"),
26
+ "#{@inst.class}#derive_all returned incorrect value for element #{name}")
27
+ end
28
+ end
29
+ end
metadata CHANGED
@@ -1,12 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apachecrunch
3
3
  version: !ruby/object:Gem::Version
4
- hash: 3
5
- prerelease:
6
- segments:
7
- - 0
8
- - 4
9
- version: "0.4"
4
+ version: "0.5"
10
5
  platform: ruby
11
6
  authors:
12
7
  - Dan Slimmon
@@ -14,7 +9,7 @@ autorequire:
14
9
  bindir: bin
15
10
  cert_chain: []
16
11
 
17
- date: 2011-07-12 00:00:00 -04:00
12
+ date: 2011-09-17 00:00:00 -04:00
18
13
  default_executable:
19
14
  dependencies: []
20
15
 
@@ -31,20 +26,36 @@ extra_rdoc_files: []
31
26
 
32
27
  files:
33
28
  - lib/apachecrunch.rb
29
+ - lib/cast.rb
34
30
  - lib/config.rb
31
+ - lib/derivation.rb
32
+ - lib/element.rb
33
+ - lib/element_value_fetcher.rb
35
34
  - lib/entry.rb
36
35
  - lib/format.rb
37
- - lib/log_element.rb
36
+ - lib/format_token.rb
37
+ - lib/format_token_definition.rb
38
38
  - lib/log_parser.rb
39
39
  - lib/procedure_dsl.rb
40
40
  - lib/progress.rb
41
41
  - bin/apachecrunch
42
42
  - LICENSE
43
+ - test/mock.rb
43
44
  - test/runner.rb
44
45
  - test/stub.rb
45
- - test/test_entry.rb
46
+ - test/test_derived_value_fetcher.rb
47
+ - test/test_element.rb
48
+ - test/test_element_value_fetcher.rb
49
+ - test/test_entry_parser.rb
46
50
  - test/test_format.rb
47
51
  - test/test_format_parser.rb
52
+ - test/test_log_parser.rb
53
+ - test/test_raw_value_fetcher.rb
54
+ - test/test_regex_token.rb
55
+ - test/test_req_firstline_derivation_rule.rb
56
+ - test/test_reqheader_token.rb
57
+ - test/test_string_token.rb
58
+ - test/test_time_derivation_rule.rb
48
59
  has_rdoc: true
49
60
  homepage: https://github.com/danslimmon/apachecrunch/
50
61
  licenses:
@@ -55,27 +66,21 @@ rdoc_options: []
55
66
  require_paths:
56
67
  - lib
57
68
  required_ruby_version: !ruby/object:Gem::Requirement
58
- none: false
59
69
  requirements:
60
70
  - - ">="
61
71
  - !ruby/object:Gem::Version
62
- hash: 3
63
- segments:
64
- - 0
65
72
  version: "0"
73
+ version:
66
74
  required_rubygems_version: !ruby/object:Gem::Requirement
67
- none: false
68
75
  requirements:
69
76
  - - ">="
70
77
  - !ruby/object:Gem::Version
71
- hash: 3
72
- segments:
73
- - 0
74
78
  version: "0"
79
+ version:
75
80
  requirements: []
76
81
 
77
82
  rubyforge_project:
78
- rubygems_version: 1.6.2
83
+ rubygems_version: 1.3.5
79
84
  signing_key:
80
85
  specification_version: 3
81
86
  summary: Apache log analysis tool designed for ease of use
data/lib/log_element.rb DELETED
@@ -1,351 +0,0 @@
1
- # Converts a string to an integer
2
- class IntegerCast
3
- def self.cast(string_value)
4
- string_value.to_i
5
- end
6
- end
7
-
8
-
9
- # Converts a CLF-formatted string to an integer
10
- #
11
- # "CLF-formatted" means that if the value is 0, the string will be a single hyphen instead of
12
- # a number. Like %b, for instance.
13
- class CLFIntegerCast
14
- def self.cast(string_value)
15
- if string_value == "-"
16
- return 0
17
- end
18
- string_value.to_i
19
- end
20
- end
21
-
22
-
23
- # An element in a log format. Abstract from which all elements inherit.
24
- #
25
- # Exposes:
26
- # abbrev: The Apache abbreviation for the element (such as "%h" or "%u" or "%{Referer}i")
27
- # name: A short name for the element (such as "remote_host", "remote_user", or "reqhead_referer")
28
- # regex: A regex that should match such an element ("[A-Za-z0-9.-]+", "[^:]+", ".+")
29
- #
30
- # If '_caster' is not nil, it should be a class with a method called "cast" that
31
- # transforms a string to the appropriate data type or format for consumption.
32
- # For example, the IntegerCast class transforms "562" to 562. The correct cast
33
- # of a string can then be performed by passing that string to this LogFormaElement
34
- # instance's "cast" method.
35
- #
36
- # 'derive_elements' manages elements that can be derived from the instance's value. See
37
- # ReqFirstlineElement for an example.
38
- class LogFormatElement
39
- @_caster = nil
40
-
41
- attr_accessor :abbrev, :name, :regex
42
- # Class variables that determine the _default_ for abbrev, name, and regex in an instance.
43
- # That is, an instance will initialize with these values for the instance variables @abbrev,
44
- # @name, and @regex.
45
- class << self; attr_accessor :abbrev, :name, :regex end
46
- # Additionally we need to access this from within the instance:
47
- class << self; attr_accessor :_caster end
48
-
49
- def initialize
50
- @abbrev = self.class.abbrev
51
- @name = self.class.name
52
- @regex = self.class.regex
53
- end
54
-
55
- # Casts a string found in the log to the correct type, using the class's @_caster attribute.
56
- def cast(string_value)
57
- if _caster.nil?
58
- return string_value
59
- else
60
- return _caster.cast(string_value)
61
- end
62
- end
63
-
64
- # Derives the named element (e.g. "url_path") from a given value for this one.
65
- #
66
- # See ReqFirstlineElement for an example.
67
- def self.derive(name, our_own_value)
68
- raise NotImplementedError
69
- end
70
-
71
- # Returns a list of the element classes that can be derived from this one.
72
- #
73
- # See ReqFirstlineElement for an example.
74
- def derived_elements
75
- []
76
- end
77
- end
78
-
79
-
80
- class RemoteHostElement < LogFormatElement
81
- @abbrev = "%h"
82
- @name = :remote_host
83
- @regex = %q![A-Za-z0-9.-]+!
84
- end
85
-
86
-
87
- class LogNameElement < LogFormatElement
88
- @abbrev = "%l"
89
- @name = :log_name
90
- @regex = %q!\S+!
91
- end
92
-
93
-
94
- class RemoteUserElement < LogFormatElement
95
- @abbrev = "%u"
96
- @name = :remote_user
97
- @regex = %q![^:]+!
98
- end
99
-
100
-
101
- class TimeElement < LogFormatElement
102
- @abbrev = "%t"
103
- @name = :time
104
- @regex = %q!\[\d\d/[A-Za-z]{3}/\d\d\d\d:\d\d:\d\d:\d\d [-+]\d\d\d\d\]!
105
-
106
- @_derivation_regex = nil
107
- @_month_map = {"Jan" => 1, "Feb" => 2, "Mar" => 3, "Apr" => 4, "May" => 5, "Jun" => 6,
108
- "Jul" => 7, "Aug" => 8, "Sep" => 9, "Oct" => 10, "Nov" => 11, "Dec" => 12}
109
-
110
- def self.derive(name, our_own_value)
111
- if @_derivation_regex.nil?
112
- @_derivation_regex = Regexp.compile(%q!^\[(\d\d)/([A-Za-z]{3})/(\d\d\d\d):(\d\d):(\d\d):(\d\d)!)
113
- end
114
-
115
- hsh = {}
116
- if our_own_value =~ @_derivation_regex
117
- hsh[:year] = $3.to_i
118
- hsh[:month] = @_month_map[$2]
119
- hsh[:day] = $1.to_i
120
-
121
- hsh[:hour] = $4.to_i
122
- hsh[:minute] = $5.to_i
123
- hsh[:second] = $6.to_i
124
- end
125
-
126
- hsh[name]
127
- end
128
-
129
- def derived_elements
130
- [YearElement, MonthElement, DayElement, HourElement, MinuteElement, SecondElement]
131
- end
132
- end
133
-
134
-
135
- # Elements derived from TimeElement
136
- class YearElement < LogFormatElement
137
- @name = :year
138
- @regex = %q!\d{4}!
139
- end
140
- class MonthElement < LogFormatElement
141
- @name = :month
142
- @regex = %q![A-Za-z]{3}!
143
- end
144
- class DayElement < LogFormatElement
145
- @name = :day
146
- @regex = %q!\d{2}!
147
- end
148
- class HourElement < LogFormatElement
149
- @name = :hour
150
- @regex = %q!\d{2}!
151
- end
152
- class MinuteElement < LogFormatElement
153
- @name = :minute
154
- @regex = %q!\d{2}!
155
- end
156
- class SecondElement < LogFormatElement
157
- @name = :second
158
- @regex = %q!\d{2}!
159
- end
160
-
161
-
162
- class ReqFirstlineElement < LogFormatElement
163
- @abbrev = "%r"
164
- @name = :req_firstline
165
- @regex = %q![^"]+!
166
-
167
- @_derivation_regex = nil
168
-
169
- def self.derive(name, our_own_value)
170
- if @_derivation_regex.nil?
171
- @_derivation_regex = Regexp.compile("^(#{ReqMethodElement.regex})\s+(#{UrlPathElement.regex})(#{QueryStringElement.regex})\s+(#{ProtocolElement.regex})$")
172
- end
173
-
174
- hsh = {}
175
- if our_own_value =~ @_derivation_regex
176
- hsh[ReqMethodElement.name] = $1
177
- hsh[UrlPathElement.name] = $2
178
- hsh[QueryStringElement.name] = $3
179
- hsh[ProtocolElement.name] = $4
180
- end
181
-
182
- hsh[name]
183
- end
184
-
185
- def derived_elements
186
- return [ReqMethodElement, UrlPathElement, QueryStringElement, ProtocolElement]
187
- end
188
- end
189
-
190
-
191
- class StatusElement < LogFormatElement
192
- @abbrev = "%s"
193
- @name = :status
194
- @regex = %q!\d+|-!
195
- end
196
-
197
-
198
- class BytesSentElement < LogFormatElement
199
- @abbrev = "%b"
200
- @name = :bytes_sent
201
- @regex = %q!\d+!
202
-
203
- @@_caster = IntegerCast
204
- end
205
-
206
-
207
- class BytesSentElement < LogFormatElement
208
- @abbrev = "%b"
209
- @name = :bytes_sent
210
- @regex = %q![\d-]+!
211
-
212
- @_caster = CLFIntegerCast
213
- end
214
-
215
-
216
- class BytesSentWithHeadersElement < LogFormatElement
217
- @abbrev = "%O"
218
- @name = :bytes_sent_with_headers
219
- @regex = %q!\d+!
220
-
221
- @_caster = IntegerCast
222
- end
223
-
224
-
225
- class ServeTimeMicroElement < LogFormatElement
226
- @abbrev = "%D"
227
- @name = :serve_time_micro
228
- @regex = %q!\d+!
229
-
230
- @_caster = IntegerCast
231
- end
232
-
233
-
234
- class UrlPathElement < LogFormatElement
235
- @abbrev = "%U"
236
- @name = :url_path
237
- @regex = %q!/[^?]*!
238
- end
239
-
240
-
241
- class QueryStringElement < LogFormatElement
242
- @abbrev = "%q"
243
- @name = :query_string
244
- @regex = %q!\??\S*!
245
- end
246
-
247
-
248
- class ReqMethodElement < LogFormatElement
249
- @abbrev = "%m"
250
- @name = :req_method
251
- @regex = %q![A-Z]+!
252
- end
253
-
254
-
255
- class ProtocolElement < LogFormatElement
256
- @abbrev = "%H"
257
- @name = :protocol
258
- @regex = %q!\S+!
259
- end
260
-
261
-
262
- class ReqheaderElement < LogFormatElement
263
- end
264
-
265
-
266
- class RegexElement < LogFormatElement
267
- end
268
-
269
-
270
- # Finds log format elements given information about them.
271
- class ElementDictionary
272
- @@_ELEMENTS = [
273
- RemoteHostElement,
274
- LogNameElement,
275
- RemoteUserElement,
276
- TimeElement,
277
- ReqFirstlineElement,
278
- StatusElement,
279
- BytesSentElement,
280
- BytesSentElement,
281
- BytesSentWithHeadersElement,
282
- ServeTimeMicroElement,
283
- UrlPathElement,
284
- QueryStringElement,
285
- ReqMethodElement,
286
- ProtocolElement
287
- ]
288
-
289
- # Returns the LogFormatElement subclass with the given format-string abbreviation.
290
- #
291
- # If none exists, returns nil.
292
- def self.find_by_abbrev(abbrev)
293
- @@_ELEMENTS.each do |element|
294
- if element.abbrev == abbrev
295
- return element
296
- end
297
- end
298
-
299
- nil
300
- end
301
- end
302
-
303
-
304
- # Generates LogFormatElement instances.
305
- #
306
- # This class does the work of figuring out which LogFormatElement subclass to make and makes it.
307
- class LogFormatElementFactory
308
- # Takes an Apache log format abbreviation and returns a corresponding LogFormatElement
309
- def from_abbrev(abbrev)
310
- element_cls = ElementDictionary.find_by_abbrev(abbrev)
311
- if element_cls
312
- # We found it in the dictionary, so just return an instance
313
- return element_cls.new
314
- elsif abbrev =~ /^%\{([A-Za-z0-9-]+)\}i/
315
- # HTTP request header
316
- return _reqheader_element(abbrev, $1)
317
- elsif abbrev =~ /^%\{(.*?):([^}]+)\}r/
318
- # Arbitrary regex
319
- return _regex_element(abbrev, $1, $2)
320
- end
321
-
322
- raise "Unknown element format '#{abbrev}'"
323
- end
324
-
325
- # Returns a format element based on an HTTP header
326
- def _reqheader_element(abbrev, header_name)
327
- element = ReqheaderElement.new
328
-
329
- element.abbrev = abbrev
330
- element.regex = %q![^"]*!
331
- element.name = _header_name_to_element_name(header_name)
332
-
333
- element
334
- end
335
-
336
- # Returns a format element based on an arbitrary regex
337
- def _regex_element(abbrev, regex_name, regex)
338
- element = RegexElement.new
339
-
340
- element.abbrev = abbrev
341
- element.regex = regex
342
- element.name = "regex_#{regex_name}".to_sym
343
-
344
- element
345
- end
346
-
347
- # Lowercases header name and turns hyphens into underscores
348
- def _header_name_to_element_name(header_name)
349
- ("reqheader_" + header_name.downcase().gsub("-", "_")).to_sym
350
- end
351
- end