sniffles 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/Gemfile +2 -8
  4. data/Guardfile +1 -1
  5. data/README.md +12 -13
  6. data/lib/sniffles.rb +9 -9
  7. data/lib/sniffles/html.rb +3 -3
  8. data/lib/sniffles/sniffers/advertising/adsense.rb +8 -14
  9. data/lib/sniffles/sniffers/advertising/kontera.rb +6 -6
  10. data/lib/sniffles/sniffers/analytics/chartbeat.rb +7 -7
  11. data/lib/sniffles/sniffers/analytics/facebook_insights.rb +7 -7
  12. data/lib/sniffles/sniffers/analytics/google_analytics.rb +8 -8
  13. data/lib/sniffles/sniffers/analytics/kissmetrics.rb +7 -7
  14. data/lib/sniffles/sniffers/analytics/mixpanel.rb +7 -7
  15. data/lib/sniffles/sniffers/analytics/quantcast.rb +6 -6
  16. data/lib/sniffles/sniffers/cms/cscart.rb +6 -6
  17. data/lib/sniffles/sniffers/{advertising/gumgum.rb → cms/ghost.rb} +8 -8
  18. data/lib/sniffles/sniffers/cms/invision.rb +9 -9
  19. data/lib/sniffles/sniffers/cms/joomla.rb +7 -7
  20. data/lib/sniffles/sniffers/cms/movabletype.rb +8 -8
  21. data/lib/sniffles/sniffers/cms/mybb.rb +6 -6
  22. data/lib/sniffles/sniffers/cms/phpbb.rb +7 -7
  23. data/lib/sniffles/sniffers/cms/shopify.rb +7 -7
  24. data/lib/sniffles/sniffers/cms/tumblr.rb +7 -7
  25. data/lib/sniffles/sniffers/cms/vanilla.rb +8 -8
  26. data/lib/sniffles/sniffers/cms/vbulletin.rb +10 -10
  27. data/lib/sniffles/sniffers/cms/wordpress.rb +12 -12
  28. data/lib/sniffles/sniffers/{advertising/admeld.rb → cms/xcart.rb} +3 -2
  29. data/lib/sniffles/sniffers/cms/zencart.rb +6 -6
  30. data/lib/sniffles/sniffers/javascript/jquery.rb +6 -6
  31. data/lib/sniffles/text.rb +4 -4
  32. data/lib/sniffles/version.rb +1 -1
  33. data/sniffles.gemspec +2 -2
  34. data/spec/cassettes/beenverified_com.yml +644 -784
  35. data/spec/cassettes/blackhatworld_com.yml +2298 -3292
  36. data/spec/cassettes/blog_mixpanel_com.yml +1351 -0
  37. data/spec/cassettes/community_invisionpower_com.yml +836 -2771
  38. data/spec/cassettes/cxxvi_com.yml +311 -0
  39. data/spec/cassettes/demo-store_prestashop_com.yml +897 -813
  40. data/spec/cassettes/demo_cs-cart_com.yml +2996 -1146
  41. data/spec/cassettes/demo_oscommerce_com.yml +136 -408
  42. data/spec/cassettes/digital-photography-school_com.yml +656 -0
  43. data/spec/cassettes/downlopedia_com.yml +1262 -1321
  44. data/spec/cassettes/drupal.yml +1540 -1507
  45. data/spec/cassettes/exprodirect_com.yml +1888 -0
  46. data/spec/cassettes/facebook_insights.yml +544 -474
  47. data/spec/cassettes/flatpress_org_home_blog.yml +287 -317
  48. data/spec/cassettes/foursquare_com.yml +1901 -348
  49. data/spec/cassettes/geomagik_com.yml +475 -0
  50. data/spec/cassettes/ghost_org.yml +207 -0
  51. data/spec/cassettes/google_blogspot_com.yml +574 -1059
  52. data/spec/cassettes/guistyles_com.yml +705 -1292
  53. data/spec/cassettes/installatron_com.yml +286 -0
  54. data/spec/cassettes/invisionmodding_com.yml +1124 -2442
  55. data/spec/cassettes/joomla.yml +280 -0
  56. data/spec/cassettes/kissmetrics_com.yml +165 -0
  57. data/spec/cassettes/lancelhoff_com.yml +637 -0
  58. data/spec/cassettes/magento.yml +459 -459
  59. data/spec/cassettes/metafilter_com.yml +1449 -1230
  60. data/spec/cassettes/movabletype_com_blog.yml +175 -1221
  61. data/spec/cassettes/ocbfashionjewelry_com.yml +588 -0
  62. data/spec/cassettes/pandoramoa_com.yml +290 -608
  63. data/spec/cassettes/pearsonified_com.yml +433 -435
  64. data/spec/cassettes/phpbb_com.yml +438 -416
  65. data/spec/cassettes/sitefinity_com.yml +443 -418
  66. data/spec/cassettes/sixflags_com.yml +560 -1014
  67. data/spec/cassettes/soulemama_typepad_com.yml +2378 -2581
  68. data/spec/cassettes/stackoverflow_com.yml +4762 -0
  69. data/spec/cassettes/staff_tumblr_com.yml +1956 -1658
  70. data/spec/cassettes/topix_com.yml +1748 -1417
  71. data/spec/cassettes/wellmedicated_com.yml +801 -0
  72. data/spec/cassettes/wordpress_com.yml +238 -863
  73. data/spec/cassettes/xenforo_com.yml +1143 -1933
  74. data/spec/sniffles/html_spec.rb +6 -6
  75. data/spec/sniffles/sniffers/advertising/adsense_spec.rb +7 -8
  76. data/spec/sniffles/sniffers/advertising/buysellads_spec.rb +5 -5
  77. data/spec/sniffles/sniffers/advertising/casale_spec.rb +5 -5
  78. data/spec/sniffles/sniffers/advertising/kontera_spec.rb +6 -6
  79. data/spec/sniffles/sniffers/advertising/thedeck_spec.rb +5 -5
  80. data/spec/sniffles/sniffers/advertising/tribalfusion_spec.rb +5 -5
  81. data/spec/sniffles/sniffers/analytics/chartbeat_spec.rb +3 -3
  82. data/spec/sniffles/sniffers/analytics/clicky_spec.rb +3 -3
  83. data/spec/sniffles/sniffers/analytics/facebook_insights_spec.rb +6 -6
  84. data/spec/sniffles/sniffers/analytics/google_analytics_spec.rb +7 -19
  85. data/spec/sniffles/sniffers/analytics/kissmetrics_spec.rb +5 -5
  86. data/spec/sniffles/sniffers/analytics/mixpanel_spec.rb +9 -9
  87. data/spec/sniffles/sniffers/analytics/quantcast_spec.rb +7 -7
  88. data/spec/sniffles/sniffers/cms/cscart_spec.rb +8 -8
  89. data/spec/sniffles/sniffers/cms/ghost_spec.rb +18 -0
  90. data/spec/sniffles/sniffers/cms/invision_spec.rb +9 -9
  91. data/spec/sniffles/sniffers/cms/joomla_spec.rb +9 -9
  92. data/spec/sniffles/sniffers/cms/movabletype_spec.rb +8 -8
  93. data/spec/sniffles/sniffers/cms/mybb_spec.rb +9 -9
  94. data/spec/sniffles/sniffers/cms/phpbb_spec.rb +8 -8
  95. data/spec/sniffles/sniffers/cms/shopify_spec.rb +9 -9
  96. data/spec/sniffles/sniffers/cms/tumblr_spec.rb +7 -7
  97. data/spec/sniffles/sniffers/cms/vanilla_spec.rb +9 -9
  98. data/spec/sniffles/sniffers/cms/vbulletin_spec.rb +11 -17
  99. data/spec/sniffles/sniffers/cms/wordpress_spec.rb +12 -12
  100. data/spec/sniffles/sniffers/cms/xcart_spec.rb +18 -0
  101. data/spec/sniffles/sniffers/cms/zencart_spec.rb +9 -9
  102. data/spec/sniffles/sniffers/javascript/jquery_spec.rb +8 -8
  103. data/spec/sniffles/sniffers_spec.rb +1 -1
  104. data/spec/sniffles_spec.rb +17 -17
  105. data/spec/spec_helper.rb +7 -3
  106. data/spec/support/helpers.rb +2 -2
  107. metadata +39 -39
  108. data/lib/sniffles/sniffers/cms/posterous.rb +0 -28
  109. data/spec/cassettes/blog_posterous_com.yml +0 -5015
  110. data/spec/cassettes/demo_forum-software_org_mybb.yml +0 -385
  111. data/spec/cassettes/forum-software_org_vanilla.yml +0 -223
  112. data/spec/cassettes/humemes_com.yml +0 -496
  113. data/spec/cassettes/joomla_org.yml +0 -733
  114. data/spec/cassettes/orangecrushbeads_com.yml +0 -751
  115. data/spec/cassettes/shop_angrybirds_com.yml +0 -455
  116. data/spec/cassettes/softpedia_com.yml +0 -3196
  117. data/spec/cassettes/squidoo_com.yml +0 -532
  118. data/spec/cassettes/tmz_com.yml +0 -2927
  119. data/spec/cassettes/vbulletin_com.yml +0 -2983
  120. data/spec/cassettes/wundergroud_com.yml +0 -907
  121. data/spec/sniffles/sniffers/advertising/admeld_spec.rb +0 -17
  122. data/spec/sniffles/sniffers/advertising/gumgum_spec.rb +0 -18
  123. data/spec/sniffles/sniffers/cms/posterous_spec.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7adf5007e07bf33ab32ef48c2310c3c509da4b98
4
- data.tar.gz: 76dca2802dac4d6eab077160afe0c7ef4f4c5167
3
+ metadata.gz: e732b8bf2f0cee7d3938c56fdb4d10df58cb67a9
4
+ data.tar.gz: 7214fcef7497e9cb0a73a79ff47bb66ef66fc26f
5
5
  SHA512:
6
- metadata.gz: 58032157b8845401a8d33982ab26069ed71aa76be7342413d242dc5dd7f60ff84640fd90159ad76c5726b71eaffe1eda9c28dafed48daf051b640a2415732afa
7
- data.tar.gz: 3fbaf321be318a649c5f2ec164b5c18c13d1f35fd47a11391be102b1342988ff97fa1e80d7fe4ad72aefab7c1806e0bac933e14933c4539e012731e78904f587
6
+ metadata.gz: 660f6a773f07fcd39a1eb399c8e06334959578a0c846ec37dfa7705699ac766b13aa736016fb2d2551ca97e65479dd5a3d123ad5dc592bfbbc4420959ca2a27d
7
+ data.tar.gz: 7bede792c9487430720d82a227c0f9eb51f1f4131051a6b62be29031accf8315f2d62b2861aa02cede1a3b76450f8142f3feb3d7bcf14c2da61706e7b7a25e73
@@ -1,5 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.8.7
4
- - 1.9.2
5
- - 1.9.3
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
data/Gemfile CHANGED
@@ -1,18 +1,12 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in sniffles.gemspec
4
3
  gemspec
5
4
 
6
5
  group :development, :test do
7
- gem 'guard-rspec'
6
+ gem 'guard-rspec', require: false
7
+ gem 'pry', require: false
8
8
  end
9
9
 
10
10
  group :test do
11
11
  gem 'rake'
12
12
  end
13
-
14
- require 'rbconfig'
15
-
16
- if RbConfig::CONFIG['target_os'] =~ /darwin/i
17
- gem 'growl', :require => false
18
- end
data/Guardfile CHANGED
@@ -1,4 +1,4 @@
1
- guard 'rspec', :cli => '--color --format=documentation', :version => 2 do
1
+ guard 'rspec', :cli => '--color --format=progress' do
2
2
  watch(%r{^spec/.+_spec\.rb$})
3
3
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
4
  watch('spec/spec_helper.rb') { "spec" }
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  ## Description
3
3
  Sniffles parses HTML pages and searches for common patterns suggesting a page is using a popular CMS or advertising platform as well as CSS and JS libraries.
4
4
 
5
- The master branch is continuously tested against Rubies 1.8.7, 1.9.2, and 1.9.3 thanks to the fantastic [Travis-CI](http://travis-ci.org/#!/ezkl/sniffles).
5
+ The master branch is continuously tested against Rubies 1.9.3, 2.0.0, 2.1.0 thanks to the fantastic [Travis-CI](http://travis-ci.org/#!/ezkl/sniffles).
6
6
 
7
7
  Current CI status: [![Build Status](https://secure.travis-ci.org/ezkl/sniffles.png?branch=master)](http://travis-ci.org/ezkl/sniffles)
8
8
 
@@ -25,48 +25,46 @@ Bundler:
25
25
  `gem 'sniffles'`
26
26
 
27
27
  ## Usage
28
- ```
28
+ ```ruby
29
29
  require 'sniffles'
30
30
  require 'typhoeus' # Optional: Use your favorite HTTP client.
31
31
 
32
- response = Typhoeus::Request.get("http://www.squidoo.com/")
32
+ response = Typhoeus::Request.get("http://www.fastly.com/")
33
33
  ```
34
34
 
35
35
  You can pass in a single sniffer:
36
36
 
37
- ```
37
+ ```ruby
38
38
  Sniffles.sniff(response.body, :google_analytics)
39
- # => { :google_analytics=> { :found=>true, :ua=>"UA-185209-2" } }
39
+ # => { :google_analytics=> { :found=>true, :ua=>"UA-25770359-1" } }
40
40
  ```
41
41
 
42
42
  Or multiple sniffers:
43
43
 
44
- ```
44
+ ```ruby
45
45
  Sniffles.sniff(response.body, :google_analytics, :kissmetrics)
46
- # => { :google_analytics=> { :found=>true, :ua=>"UA-185209-2"}, :kissmetrics=>{:found=>false} }
46
+ # => { :google_analytics=> { :found=>true, :ua=>"UA-25770359-1"}, :kissmetrics=>{:found=>false} }
47
47
  ```
48
48
 
49
49
  Or an entire group of sniffers:
50
50
 
51
- ```
51
+ ```ruby
52
52
  Sniffles.sniff(response.body, :analytics)
53
53
  # => {:chartbeat=>{:found=>false},
54
54
  # :clicky=>{:found=>false},
55
55
  # :google_analytics=>{:found=>true, :ua=>"UA-185209-2"},
56
56
  # :kissmetrics=>{:found=>false},
57
- # :mixpanel=>{:found=>true},
58
- # :quantcast=>{:found=>true}}
57
+ # :mixpanel=>{:found=>false},
58
+ # :quantcast=>{:found=>false}}
59
59
  ```
60
60
 
61
- ## Sniffers (v0.1.5)
61
+ ## Sniffers ([HEAD](https://github.com/ezkl/sniffles/tree/master))
62
62
  Here are a list of currently implemented sniffers, grouped by category. You can see a list of unimplemented sniffers by [filtering issues by "sniffer"](https://github.com/ezkl/sniffles/issues?labels=sniffer&state=open).
63
63
 
64
64
  ### Advertising
65
- * AdMeld
66
65
  * AdSense
67
66
  * BuySellAds
68
67
  * Casale
69
- * GumGum
70
68
  * Kontera
71
69
  * TheDeck
72
70
  * TribalFusion
@@ -103,6 +101,7 @@ Here are a list of currently implemented sniffers, grouped by category. You can
103
101
  * Vanilla
104
102
  * vBulletin
105
103
  * WordPress
104
+ * Xcart
106
105
  * XenForo
107
106
  * ZenCart
108
107
 
@@ -5,15 +5,15 @@ require 'sniffles/html'
5
5
  require 'sniffles/text'
6
6
  require 'sniffles/sniffers'
7
7
 
8
- module Sniffles
8
+ module Sniffles
9
9
  def self.sniff(response_body, *args)
10
10
  output = {}
11
-
11
+
12
12
  if args.empty?
13
13
  list_all.each do |sniffer|
14
14
  output[sniffer] = Sniffers.use(response_body, sniffer)
15
- end
16
- else
15
+ end
16
+ else
17
17
  args.each do |arg|
18
18
  if sniffer?(arg)
19
19
  output[arg] = Sniffers.use(response_body, arg)
@@ -28,7 +28,7 @@ module Sniffles
28
28
  end
29
29
  output
30
30
  end
31
-
31
+
32
32
  def self.list_all
33
33
  Sniffers.list_all
34
34
  end
@@ -36,18 +36,18 @@ module Sniffles
36
36
  def self.list_groups
37
37
  Sniffers.list_groups
38
38
  end
39
-
39
+
40
40
  def self.list_all_by_group
41
41
  Sniffers.list_all_by_group
42
42
  end
43
-
43
+
44
44
  def self.group?(name)
45
45
  list_groups.include?(name)
46
46
  end
47
-
47
+
48
48
  def self.sniffer?(name)
49
49
  list_all.include?(name)
50
50
  end
51
-
51
+
52
52
  class UnknownSniffer < StandardError; end
53
53
  end
@@ -3,7 +3,7 @@ module Sniffles
3
3
  def parse(html)
4
4
  @doc = Nokogiri::HTML(html)
5
5
  end
6
-
6
+
7
7
  def text_at(pattern)
8
8
  if (nodes = @doc.search(pattern)).any?
9
9
  nodes.text
@@ -11,9 +11,9 @@ module Sniffles
11
11
  false
12
12
  end
13
13
  end
14
-
14
+
15
15
  def text_match?(pattern, text)
16
16
  (@doc.search(pattern).text == text)
17
17
  end
18
18
  end
19
- end
19
+ end
@@ -2,29 +2,23 @@ module Sniffles
2
2
  module Sniffers
3
3
  class Adsense
4
4
  include Text
5
-
5
+
6
6
  attr_accessor :doc
7
7
  attr_reader :output
8
-
8
+
9
9
  def initialize(response_body)
10
- @output = {}
10
+ @output = {}
11
11
  parse(response_body) && process_document
12
12
  end
13
-
13
+
14
14
  def process_document
15
- if @output[:found] = found?
16
- parse_publisher_id
17
- end
15
+ @output[:found] = found?
18
16
  end
19
-
17
+
20
18
  private
21
19
  def found?
22
- match? /googleAddAdSenseService/
23
- end
24
-
25
- def parse_publisher_id
26
- @output[:publisher_id] = capture(/googleAddAdSenseService\(["|']+([a-z]+\-pub\-[\d]+)["|']+\)/)
20
+ match?(/googletag\.pubads\(\)/)
27
21
  end
28
22
  end
29
23
  end
30
- end
24
+ end
@@ -2,23 +2,23 @@ module Sniffles
2
2
  module Sniffers
3
3
  class Kontera
4
4
  include Text
5
-
5
+
6
6
  attr_accessor :doc
7
7
  attr_reader :output
8
-
8
+
9
9
  def initialize(response_body)
10
- @output = {}
10
+ @output = {}
11
11
  parse(response_body) && process_document
12
12
  end
13
-
13
+
14
14
  def process_document
15
15
  @output[:found] = found?
16
16
  end
17
-
17
+
18
18
  private
19
19
  def found?
20
20
  match? /kona\.kontera\.com/
21
21
  end
22
22
  end
23
23
  end
24
- end
24
+ end
@@ -2,23 +2,23 @@ module Sniffles
2
2
  module Sniffers
3
3
  class Chartbeat
4
4
  include Text
5
-
5
+
6
6
  attr_accessor :doc
7
7
  attr_reader :output
8
-
8
+
9
9
  def initialize(response_body)
10
- @output = {}
10
+ @output = {}
11
11
  parse(response_body) && process_document
12
12
  end
13
-
13
+
14
14
  def process_document
15
15
  @output[:found] = found?
16
16
  end
17
-
17
+
18
18
  private
19
19
  def found?
20
- match? /static\.chartbeat\.com\/js\/chartbeat\.js/
20
+ match? /\/chartbeat\.js/
21
21
  end
22
22
  end
23
23
  end
24
- end
24
+ end
@@ -2,26 +2,26 @@ module Sniffles
2
2
  module Sniffers
3
3
  class FacebookInsights
4
4
  include Text
5
-
5
+
6
6
  attr_accessor :doc
7
7
  attr_reader :output
8
-
8
+
9
9
  def initialize(response_body)
10
- @output = {}
10
+ @output = {}
11
11
  parse(response_body) && process_document
12
12
  end
13
-
13
+
14
14
  def process_document
15
15
  if @output[:found] = found?
16
16
  parse_extra_details
17
17
  end
18
18
  end
19
-
19
+
20
20
  private
21
21
  def found?
22
22
  match?(/property="fb:(admins|app_id|page_id)"/i)
23
23
  end
24
-
24
+
25
25
  def parse_extra_details
26
26
  @output[:admin_ids] = capture(/property="fb:admins" content="((\d+)(,\s*\d+)*)"/)
27
27
  @output[:app_id] = capture(/"fb:app_id" content="([\d]+)"/)
@@ -29,4 +29,4 @@ module Sniffles
29
29
  end
30
30
  end
31
31
  end
32
- end
32
+ end
@@ -2,29 +2,29 @@ module Sniffles
2
2
  module Sniffers
3
3
  class GoogleAnalytics
4
4
  include Text
5
-
5
+
6
6
  attr_accessor :doc
7
7
  attr_reader :output
8
-
8
+
9
9
  def initialize(response_body)
10
- @output = {}
10
+ @output = {}
11
11
  parse(response_body) && process_document
12
12
  end
13
-
13
+
14
14
  def process_document
15
15
  if @output[:found] = found?
16
16
  parse_google_analytics_ua
17
17
  end
18
18
  end
19
-
19
+
20
20
  private
21
21
  def found?
22
- match?(/google\-analytics\.com\/ga\.js|urchinTracker/i)
22
+ match?(/google\-analytics\.com\/ga\.js/)
23
23
  end
24
-
24
+
25
25
  def parse_google_analytics_ua
26
26
  @output[:ua] = capture(/[\"|\'](UA\-[\d]+\-[\d])[\"|\']/)
27
27
  end
28
28
  end
29
29
  end
30
- end
30
+ end
@@ -2,23 +2,23 @@ module Sniffles
2
2
  module Sniffers
3
3
  class Kissmetrics
4
4
  include Text
5
-
5
+
6
6
  attr_accessor :doc
7
7
  attr_reader :output
8
-
8
+
9
9
  def initialize(response_body)
10
- @output = {}
10
+ @output = {}
11
11
  parse(response_body) && process_document
12
12
  end
13
-
13
+
14
14
  def process_document
15
15
  @output[:found] = found?
16
16
  end
17
-
17
+
18
18
  private
19
19
  def found?
20
- match? /i\.kissmetrics\.com/
20
+ match?(/i\.kissmetrics\.com/)
21
21
  end
22
22
  end
23
23
  end
24
- end
24
+ end
@@ -2,23 +2,23 @@ module Sniffles
2
2
  module Sniffers
3
3
  class Mixpanel
4
4
  include Text
5
-
5
+
6
6
  attr_accessor :doc
7
7
  attr_reader :output
8
-
8
+
9
9
  def initialize(response_body)
10
- @output = {}
10
+ @output = {}
11
11
  parse(response_body) && process_document
12
12
  end
13
-
13
+
14
14
  def process_document
15
15
  @output[:found] = found?
16
16
  end
17
-
17
+
18
18
  private
19
19
  def found?
20
- match?(/api\.mixpanel\.com\S+mixpanel\.js/)
20
+ match?(/api\.mixpanel\.com\/.+mixpanel\.[0-9]?\.?js/)
21
21
  end
22
22
  end
23
23
  end
24
- end
24
+ end
@@ -2,23 +2,23 @@ module Sniffles
2
2
  module Sniffers
3
3
  class Quantcast
4
4
  include Text
5
-
5
+
6
6
  attr_accessor :doc
7
7
  attr_reader :output
8
-
8
+
9
9
  def initialize(response_body)
10
- @output = {}
10
+ @output = {}
11
11
  parse(response_body) && process_document
12
12
  end
13
-
13
+
14
14
  def process_document
15
15
  @output[:found] = found?
16
16
  end
17
-
17
+
18
18
  private
19
19
  def found?
20
20
  match?(/\.quantserve\.com\/quant\.js/)
21
21
  end
22
22
  end
23
23
  end
24
- end
24
+ end