routes_coverage 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "routes_coverage/version"
2
4
  require "routes_coverage/result"
3
5
  require "routes_coverage/middleware"
@@ -12,24 +14,13 @@ module RoutesCoverage
12
14
  railtie_name :routes_coverage
13
15
 
14
16
  initializer "request_coverage.inject_test_middleware" do
15
- if RoutesCoverage.enabled?
16
- ::Rails.application.middleware.use RoutesCoverage::Middleware
17
- end
17
+ ::Rails.application.middleware.use RoutesCoverage::Middleware if RoutesCoverage.enabled?
18
18
  end
19
19
  end
20
20
 
21
21
  class Settings
22
- attr_reader :exclude_patterns
23
- attr_reader :exclude_namespaces
24
- attr_accessor :exclude_put_fallbacks
25
-
26
- attr_accessor :perform_report
27
- attr_accessor :minimum_coverage
28
- attr_accessor :round_precision
29
-
30
- attr_accessor :format
31
-
32
- attr_reader :groups
22
+ attr_reader :exclude_patterns, :exclude_namespaces, :groups
23
+ attr_accessor :exclude_put_fallbacks, :perform_report, :minimum_coverage, :round_precision, :format
33
24
 
34
25
  def initialize
35
26
  @exclude_patterns = []
@@ -63,100 +54,122 @@ module RoutesCoverage
63
54
  end
64
55
 
65
56
  def self.settings
66
- @@settings ||= Settings.new
57
+ @settings ||= Settings.new
67
58
  end
68
59
 
69
60
  def self.configure
70
- yield self.settings
61
+ yield settings
71
62
  end
72
63
 
73
- mattr_reader :pid
64
+ # used in at_exit adapter to skip subprocesses
65
+ def self.pid
66
+ @pid
67
+ end
68
+
69
+ def self.route_hit_count
70
+ @route_hit_count
71
+ end
74
72
 
75
73
  def self.reset!
76
- @@route_hit_count = Hash.new(0)
77
- @@pid = Process.pid
74
+ @route_hit_count = Hash.new(0)
75
+ @pid = Process.pid
78
76
  end
79
77
 
80
78
  def self.perform_report
81
79
  return unless settings.perform_report
82
80
 
81
+ all_routes = _collect_all_routes
82
+ all_result = Result.new(all_routes, route_hit_count, settings)
83
+ groups = _collect_route_groups(all_routes)
84
+
85
+ if groups.size > 1
86
+ ungroupped_routes = all_routes.reject do |r|
87
+ groups.values.any? do |group_routes|
88
+ group_routes.all_routes.include? r
89
+ end
90
+ end
91
+
92
+ if ungroupped_routes.any?
93
+ groups["Ungroupped"] = Result.new(ungroupped_routes, route_hit_count.slice(ungroupped_routes), settings)
94
+ end
95
+ end
96
+
97
+ puts
98
+ puts settings.formatter_class.new(all_result, groups, settings).format # rubocop:disable Rails/Output
99
+ end
100
+
101
+ def self._collect_all_routes
83
102
  all_routes = ::Rails.application.routes.routes.routes.dup
84
103
 
85
104
  if defined?(::Sprockets) && defined?(::Sprockets::Environment)
86
- all_routes.reject!{|r| r.app.is_a?(::Sprockets::Environment) }
105
+ all_routes.reject! { |r| r.app.is_a?(::Sprockets::Environment) }
87
106
  end
88
107
 
89
108
  if settings.exclude_put_fallbacks
90
- all_routes.reject!{|put_route|
109
+ all_routes.reject! do |put_route|
91
110
  (
92
111
  put_route.verb == /^PUT$/ ||
93
112
  put_route.verb == "PUT" # rails 5
94
113
  ) &&
95
- put_route.name.nil? &&
96
- @@route_hit_count[put_route] == 0 &&
97
- all_routes.any?{|patch_route|
98
- (
99
- patch_route.verb == /^PATCH$/ ||
100
- patch_route.verb == "PATCH" # rails5
101
- ) &&
102
- patch_route.defaults == put_route.defaults &&
103
- patch_route.ip == put_route.ip &&
104
- patch_route.path.spec.to_s == put_route.path.spec.to_s
105
- }
106
- }
114
+ put_route.name.nil? &&
115
+ route_hit_count[put_route].zero? &&
116
+ all_routes.any? do |patch_route|
117
+ (
118
+ patch_route.verb == /^PATCH$/ ||
119
+ patch_route.verb == "PATCH" # rails5
120
+ ) &&
121
+ patch_route.defaults == put_route.defaults &&
122
+ patch_route.ip == put_route.ip &&
123
+ patch_route.path.spec.to_s == put_route.path.spec.to_s
124
+ end
125
+ end
107
126
  end
127
+ all_routes
128
+ end
108
129
 
109
- all_result = Result.new(
110
- all_routes,
111
- @@route_hit_count,
112
- settings
113
- )
114
-
115
-
116
- groups = Hash[settings.groups.map{|group_name, regex|
117
- [group_name,
118
- Result.new(
119
- all_routes.select{|r| r.path.spec.to_s =~ regex},
120
- Hash[@@route_hit_count.select{|r,_hits| r.path.spec.to_s =~ regex}],
121
- settings
122
- )
123
- ]
124
- }]
125
-
126
- if groups.size > 1
127
- ungroupped_routes = all_routes.reject{|r|
128
- groups.values.any?{|group_routes|
129
- group_routes.all_routes.include? r
130
- }
131
- }
132
-
133
- if ungroupped_routes.any?
134
- groups["Ungroupped"] = Result.new(
135
- ungroupped_routes,
136
- Hash[@@route_hit_count.select{|r,_hits| ungroupped_routes.include? r}],
137
- settings
138
- )
130
+ def self._collect_route_groups(all_routes)
131
+ settings.groups.map do |group_name, matcher|
132
+ group_routes = all_routes.select do |route|
133
+ if matcher.respond_to?(:call)
134
+ matcher.call(route)
135
+ elsif matcher.is_a?(Hash)
136
+ matcher.all? do |key, value|
137
+ case key
138
+ when :path
139
+ route.path.spec.to_s =~ value
140
+ when :constraints
141
+ value.all? do |constraint_name, constraint_value|
142
+ if constraint_value.present?
143
+ route.constraints[constraint_name] && route.constraints[constraint_name].match?(constraint_value)
144
+ else
145
+ route.constraints[constraint_name].blank?
146
+ end
147
+ end
148
+ end
149
+ end
150
+ else
151
+ route.path.spec.to_s.match?(matcher)
152
+ end
139
153
  end
140
- end
141
154
 
142
- puts
143
- puts settings.formatter_class.new(all_result, groups, settings).format
155
+ [group_name, Result.new(group_routes, route_hit_count.slice(group_routes), settings)]
156
+ end.to_h
144
157
  end
145
158
 
146
-
147
- def self._touch_route route
148
- reset! unless @@route_hit_count
149
- @@route_hit_count[route] += 1
159
+ def self._touch_route(route)
160
+ reset! unless route_hit_count
161
+ route_hit_count[route] += 1
150
162
  end
151
163
  end
152
164
 
153
- if RoutesCoverage.enabled?
154
- if defined? SimpleCov
155
- #TODO: use SimpleCov.at_exit
156
- end
165
+ require "routes_coverage/adapters/rspec" if defined? RSpec
157
166
 
158
- if defined? RSpec
159
- require "routes_coverage/adapters/rspec"
167
+ if RoutesCoverage.enabled?
168
+ if defined?(SimpleCov) && SimpleCov.running
169
+ require 'routes_coverage/adapters/simplecov'
170
+ RoutesCoverage::Adapters::SimpleCov.use
171
+ elsif defined? RSpec
172
+ RoutesCoverage::Adapters::RSpec.use
160
173
  else
161
174
  require "routes_coverage/adapters/atexit"
162
175
  RoutesCoverage::Adapters::AtExit.use
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'routes_coverage/version'
5
6
 
@@ -9,18 +10,23 @@ Gem::Specification.new do |spec|
9
10
  spec.authors = ["Vasily Fedoseyev"]
10
11
  spec.email = ["vasilyfedoseyev@gmail.com"]
11
12
 
12
- spec.summary = %q{Provides coverage report for your rails routes}
13
- spec.description = %q{Generates coverage report for routes hit by your request/integration/feature tests including capybara ones}
13
+ spec.summary = "Provides coverage report for your rails routes"
14
+ spec.description = "Generates coverage report for routes hit by your request/integration/feature tests "\
15
+ "including capybara ones"
14
16
  spec.homepage = "https://github.com/Vasfed/routes_coverage"
15
17
  spec.license = "MIT"
16
18
 
17
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
- f.match(%r{^(test|spec|features|assets)/})
19
+ spec.required_ruby_version = ">= 2.0"
20
+
21
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
22
+ f.match(%r{^(test|spec|features|assets|bin|gemfiles)/}) ||
23
+ f.start_with?('.') ||
24
+ %w[Appraisals Gemfile Rakefile].include?(f)
19
25
  end
20
26
  spec.require_paths = ["lib"]
21
27
 
28
+ spec.add_development_dependency 'appraisal'
22
29
  spec.add_development_dependency "bundler", "~> 1.14"
23
- spec.add_development_dependency "rake", "~> 10.0"
24
30
  spec.add_development_dependency "minitest"
25
- spec.add_development_dependency 'appraisal'
31
+ spec.add_development_dependency "rake", "~> 10.0"
26
32
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: routes_coverage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vasily Fedoseyev
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-26 00:00:00.000000000 Z
11
+ date: 2021-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: appraisal
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.14'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.14'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '1.14'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '1.14'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -53,19 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: appraisal
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '10.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '10.0'
69
69
  description: Generates coverage report for routes hit by your request/integration/feature
70
70
  tests including capybara ones
71
71
  email:
@@ -74,33 +74,10 @@ executables: []
74
74
  extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
- - ".gitattributes"
78
- - ".gitignore"
79
- - ".travis.yml"
80
- - Appraisals
81
- - Gemfile
82
77
  - LICENSE.txt
83
78
  - README.md
84
- - Rakefile
85
- - bin/console
86
- - bin/setup
87
79
  - compiled_assets/routes.css
88
80
  - compiled_assets/routes.js
89
- - gemfiles/.bundle/config
90
- - gemfiles/rails_3.gemfile
91
- - gemfiles/rails_3.gemfile.lock
92
- - gemfiles/rails_40.gemfile
93
- - gemfiles/rails_40.gemfile.lock
94
- - gemfiles/rails_40_rspec.gemfile
95
- - gemfiles/rails_40_rspec.gemfile.lock
96
- - gemfiles/rails_40_simplecov.gemfile
97
- - gemfiles/rails_40_simplecov.gemfile.lock
98
- - gemfiles/rails_42.gemfile
99
- - gemfiles/rails_42.gemfile.lock
100
- - gemfiles/rails_5.gemfile
101
- - gemfiles/rails_5.gemfile.lock
102
- - gemfiles/rails_51.gemfile
103
- - gemfiles/rails_51.gemfile.lock
104
81
  - lib/routes_coverage.rb
105
82
  - lib/routes_coverage/adapters/atexit.rb
106
83
  - lib/routes_coverage/adapters/rspec.rb
@@ -119,7 +96,7 @@ homepage: https://github.com/Vasfed/routes_coverage
119
96
  licenses:
120
97
  - MIT
121
98
  metadata: {}
122
- post_install_message:
99
+ post_install_message:
123
100
  rdoc_options: []
124
101
  require_paths:
125
102
  - lib
@@ -127,16 +104,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
127
104
  requirements:
128
105
  - - ">="
129
106
  - !ruby/object:Gem::Version
130
- version: '0'
107
+ version: '2.0'
131
108
  required_rubygems_version: !ruby/object:Gem::Requirement
132
109
  requirements:
133
110
  - - ">="
134
111
  - !ruby/object:Gem::Version
135
112
  version: '0'
136
113
  requirements: []
137
- rubyforge_project:
138
- rubygems_version: 2.5.1
139
- signing_key:
114
+ rubygems_version: 3.0.9
115
+ signing_key:
140
116
  specification_version: 4
141
117
  summary: Provides coverage report for your rails routes
142
118
  test_files: []
data/.gitattributes DELETED
@@ -1 +0,0 @@
1
- compiled_assets/* linguist-generated
data/.gitignore DELETED
@@ -1,11 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- /log/
11
- /coverage/
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.3.0
5
- before_install: gem install bundler -v 1.14.6
data/Appraisals DELETED
@@ -1,33 +0,0 @@
1
- # to test against all, run: appraisal rake spec
2
-
3
-
4
- appraise "rails-40" do
5
- gem "rails", "~>4.0.0"
6
- end
7
-
8
- appraise "rails-42" do
9
- gem "rails", "~>4.2.0"
10
- end
11
-
12
- appraise "rails-5" do
13
- gem "rails", "~>5.0.0"
14
- end
15
-
16
- appraise "rails-40+rspec" do
17
- gem "rails", "~>4.0.0"
18
- gem "rspec-rails"
19
- end
20
-
21
- appraise "rails-40+simplecov" do
22
- gem "rails", "~>4.0.0"
23
- gem "simplecov"
24
- end
25
-
26
- appraise "rails-51" do
27
- gem "rails", "~>5.1.0"
28
- end
29
-
30
- appraise 'rails-3' do
31
- gem 'rails', '~>3.2.22'
32
- gem 'test-unit'
33
- end
data/Gemfile DELETED
@@ -1,15 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- #NB: gem's dependencies are in routes_coverage.gemspec
4
- #NB: all other non-listed gems should go into Appraisals,
5
- # this file is only for quick tests
6
-
7
- # rails should be included before us
8
- gem 'rails', '4.0.13'
9
- gem 'simplecov', require: false
10
-
11
- # for assets:
12
- gem 'sprockets'
13
- gem 'sass'
14
-
15
- gemspec
data/Rakefile DELETED
@@ -1,36 +0,0 @@
1
- require 'bundler'
2
- require 'bundler/setup'
3
- require "bundler/gem_tasks"
4
-
5
- require 'rake/testtask'
6
-
7
- Rake::TestTask.new(:spec) do |t|
8
- t.pattern = 'spec/**/*_spec.rb'
9
- t.libs.push 'spec'
10
- end
11
-
12
- Rake::TestTask.new(:dummytest) do |t| t.pattern = 'spec/fixtures/dummy_test.rb' end
13
- Rake::TestTask.new(:dummytest_html) do |t| t.pattern = 'spec/fixtures/dummy_html.rb' end
14
- Rake::TestTask.new(:dummytest_full) do |t| t.pattern = 'spec/fixtures/dummy_test_full.rb' end
15
- Rake::TestTask.new(:dummytest_filter) do |t| t.pattern = 'spec/fixtures/dummy_test_nsfilters.rb' end
16
- Rake::TestTask.new(:dummytest_groups) do |t| t.pattern = 'spec/fixtures/dummy_test_groups.rb' end
17
-
18
-
19
- task :default => :spec
20
-
21
- $:.push File.expand_path("../lib", __FILE__)
22
- require 'routes_coverage/version'
23
-
24
- namespace :assets do
25
- desc "Compiles all assets"
26
- task :compile do
27
- puts "Compiling assets"
28
- require "sprockets"
29
- assets = Sprockets::Environment.new
30
- assets.append_path "assets/javascripts"
31
- assets.append_path "assets/stylesheets"
32
- compiled_path = "compiled_assets"
33
- assets["application.js"].write_to("#{compiled_path}/routes.js")
34
- assets["application.css"].write_to("#{compiled_path}/routes.css")
35
- end
36
- end
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "rails"
5
- require "routes_coverage"
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require "irb"
15
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
- appraisal
@@ -1,2 +0,0 @@
1
- ---
2
- BUNDLE_RETRY: "1"
@@ -1,11 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rails", "~>3.2.22"
6
- gem "simplecov", require: false
7
- gem "sprockets"
8
- gem "sass"
9
- gem "test-unit"
10
-
11
- gemspec path: "../"
@@ -1,117 +0,0 @@
1
- PATH
2
- remote: ..
3
- specs:
4
- routes_coverage (0.4.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- actionmailer (3.2.22.5)
10
- actionpack (= 3.2.22.5)
11
- mail (~> 2.5.4)
12
- actionpack (3.2.22.5)
13
- activemodel (= 3.2.22.5)
14
- activesupport (= 3.2.22.5)
15
- builder (~> 3.0.0)
16
- erubis (~> 2.7.0)
17
- journey (~> 1.0.4)
18
- rack (~> 1.4.5)
19
- rack-cache (~> 1.2)
20
- rack-test (~> 0.6.1)
21
- sprockets (~> 2.2.1)
22
- activemodel (3.2.22.5)
23
- activesupport (= 3.2.22.5)
24
- builder (~> 3.0.0)
25
- activerecord (3.2.22.5)
26
- activemodel (= 3.2.22.5)
27
- activesupport (= 3.2.22.5)
28
- arel (~> 3.0.2)
29
- tzinfo (~> 0.3.29)
30
- activeresource (3.2.22.5)
31
- activemodel (= 3.2.22.5)
32
- activesupport (= 3.2.22.5)
33
- activesupport (3.2.22.5)
34
- i18n (~> 0.6, >= 0.6.4)
35
- multi_json (~> 1.0)
36
- appraisal (2.2.0)
37
- bundler
38
- rake
39
- thor (>= 0.14.0)
40
- arel (3.0.3)
41
- builder (3.0.4)
42
- docile (1.1.5)
43
- erubis (2.7.0)
44
- hike (1.2.3)
45
- i18n (0.8.4)
46
- journey (1.0.4)
47
- json (1.8.6)
48
- mail (2.5.4)
49
- mime-types (~> 1.16)
50
- treetop (~> 1.4.8)
51
- mime-types (1.25.1)
52
- minitest (5.10.2)
53
- multi_json (1.12.1)
54
- polyglot (0.3.5)
55
- power_assert (0.3.1)
56
- rack (1.4.7)
57
- rack-cache (1.6.1)
58
- rack (>= 0.4)
59
- rack-ssl (1.3.4)
60
- rack
61
- rack-test (0.6.3)
62
- rack (>= 1.0)
63
- rails (3.2.22.5)
64
- actionmailer (= 3.2.22.5)
65
- actionpack (= 3.2.22.5)
66
- activerecord (= 3.2.22.5)
67
- activeresource (= 3.2.22.5)
68
- activesupport (= 3.2.22.5)
69
- bundler (~> 1.0)
70
- railties (= 3.2.22.5)
71
- railties (3.2.22.5)
72
- actionpack (= 3.2.22.5)
73
- activesupport (= 3.2.22.5)
74
- rack-ssl (~> 1.3.2)
75
- rake (>= 0.8.7)
76
- rdoc (~> 3.4)
77
- thor (>= 0.14.6, < 2.0)
78
- rake (10.5.0)
79
- rdoc (3.12.2)
80
- json (~> 1.4)
81
- sass (3.4.24)
82
- simplecov (0.14.1)
83
- docile (~> 1.1.0)
84
- json (>= 1.8, < 3)
85
- simplecov-html (~> 0.10.0)
86
- simplecov-html (0.10.1)
87
- sprockets (2.2.3)
88
- hike (~> 1.2)
89
- multi_json (~> 1.0)
90
- rack (~> 1.0)
91
- tilt (~> 1.1, != 1.3.0)
92
- test-unit (3.2.2)
93
- power_assert
94
- thor (0.19.4)
95
- tilt (1.4.1)
96
- treetop (1.4.15)
97
- polyglot
98
- polyglot (>= 0.3.1)
99
- tzinfo (0.3.53)
100
-
101
- PLATFORMS
102
- ruby
103
-
104
- DEPENDENCIES
105
- appraisal
106
- bundler (~> 1.14)
107
- minitest
108
- rails (~> 3.2.22)
109
- rake (~> 10.0)
110
- routes_coverage!
111
- sass
112
- simplecov
113
- sprockets
114
- test-unit
115
-
116
- BUNDLED WITH
117
- 1.15.1
@@ -1,10 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rails", "~>4.0.0"
6
- gem "simplecov", require: false
7
- gem "sprockets"
8
- gem "sass"
9
-
10
- gemspec path: "../"