packwerk 1.4.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -0
  3. data/Gemfile.lock +22 -19
  4. data/README.md +7 -2
  5. data/UPGRADING.md +54 -0
  6. data/USAGE.md +5 -40
  7. data/lib/packwerk/application_validator.rb +88 -93
  8. data/lib/packwerk/association_inspector.rb +1 -1
  9. data/lib/packwerk/cache.rb +169 -0
  10. data/lib/packwerk/cli.rb +32 -28
  11. data/lib/packwerk/configuration.rb +40 -5
  12. data/lib/packwerk/constant_discovery.rb +21 -5
  13. data/lib/packwerk/constant_name_inspector.rb +1 -1
  14. data/lib/packwerk/deprecated_references.rb +1 -1
  15. data/lib/packwerk/file_processor.rb +34 -15
  16. data/lib/packwerk/files_for_processing.rb +49 -22
  17. data/lib/packwerk/formatters/offenses_formatter.rb +1 -1
  18. data/lib/packwerk/formatters/progress_formatter.rb +1 -1
  19. data/lib/packwerk/generators/configuration_file.rb +4 -19
  20. data/lib/packwerk/generators/templates/packwerk.yml.erb +5 -5
  21. data/lib/packwerk/node.rb +2 -1
  22. data/lib/packwerk/node_processor.rb +6 -6
  23. data/lib/packwerk/node_processor_factory.rb +3 -4
  24. data/lib/packwerk/node_visitor.rb +3 -0
  25. data/lib/packwerk/offense.rb +10 -2
  26. data/lib/packwerk/package.rb +1 -1
  27. data/lib/packwerk/package_set.rb +3 -2
  28. data/lib/packwerk/parse_run.rb +37 -17
  29. data/lib/packwerk/parsed_constant_definitions.rb +4 -4
  30. data/lib/packwerk/parsers/erb.rb +2 -0
  31. data/lib/packwerk/parsers/factory.rb +2 -0
  32. data/lib/packwerk/parsers/parser_interface.rb +17 -0
  33. data/lib/packwerk/parsers/ruby.rb +2 -0
  34. data/lib/packwerk/parsers.rb +1 -0
  35. data/lib/packwerk/reference_checking/checkers/checker.rb +1 -1
  36. data/lib/packwerk/reference_checking/reference_checker.rb +2 -1
  37. data/lib/packwerk/reference_extractor.rb +78 -20
  38. data/lib/packwerk/reference_offense.rb +8 -3
  39. data/lib/packwerk/result.rb +2 -2
  40. data/lib/packwerk/run_context.rb +62 -38
  41. data/lib/packwerk/spring_command.rb +1 -1
  42. data/lib/packwerk/unresolved_reference.rb +10 -0
  43. data/lib/packwerk/version.rb +1 -1
  44. data/lib/packwerk.rb +5 -9
  45. data/packwerk.gemspec +1 -0
  46. data/sorbet/config +1 -0
  47. data/sorbet/rbi/gems/tapioca@0.4.19.rbi +1 -1
  48. data/sorbet/tapioca/require.rb +1 -1
  49. metadata +21 -7
  50. data/lib/packwerk/generators/inflections_file.rb +0 -43
  51. data/lib/packwerk/generators/templates/inflections.yml +0 -6
  52. data/lib/packwerk/inflections/custom.rb +0 -33
  53. data/lib/packwerk/inflections/default.rb +0 -73
  54. data/lib/packwerk/inflector.rb +0 -49
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "constant_resolver"
@@ -8,76 +8,105 @@ module Packwerk
8
8
  class RunContext
9
9
  extend T::Sig
10
10
 
11
- attr_reader(
12
- :root_path,
13
- :load_paths,
14
- :package_paths,
15
- :inflector,
16
- :custom_associations,
17
- :checker_classes,
18
- )
19
-
20
- DEFAULT_CHECKERS = [
21
- ::Packwerk::ReferenceChecking::Checkers::DependencyChecker,
22
- ::Packwerk::ReferenceChecking::Checkers::PrivacyChecker,
23
- ]
11
+ DEFAULT_CHECKERS = T.let([
12
+ ::Packwerk::ReferenceChecking::Checkers::DependencyChecker.new,
13
+ ::Packwerk::ReferenceChecking::Checkers::PrivacyChecker.new,
14
+ ], T::Array[ReferenceChecking::Checkers::Checker])
24
15
 
25
16
  class << self
17
+ extend T::Sig
18
+
19
+ sig do
20
+ params(configuration: Configuration).returns(RunContext)
21
+ end
26
22
  def from_configuration(configuration)
27
- inflector = ::Packwerk::Inflector.from_file(configuration.inflections_file)
23
+ inflector = ActiveSupport::Inflector
24
+
28
25
  new(
29
26
  root_path: configuration.root_path,
30
27
  load_paths: configuration.load_paths,
31
28
  package_paths: configuration.package_paths,
32
29
  inflector: inflector,
33
- custom_associations: configuration.custom_associations
30
+ custom_associations: configuration.custom_associations,
31
+ cache_enabled: configuration.cache_enabled?,
32
+ cache_directory: configuration.cache_directory,
33
+ config_path: configuration.config_path,
34
34
  )
35
35
  end
36
36
  end
37
37
 
38
+ sig do
39
+ params(
40
+ root_path: String,
41
+ load_paths: T::Array[String],
42
+ inflector: T.class_of(ActiveSupport::Inflector),
43
+ cache_directory: Pathname,
44
+ config_path: T.nilable(String),
45
+ package_paths: T.nilable(T.any(T::Array[String], String)),
46
+ custom_associations: AssociationInspector::CustomAssociations,
47
+ checkers: T::Array[ReferenceChecking::Checkers::Checker],
48
+ cache_enabled: T::Boolean,
49
+ ).void
50
+ end
38
51
  def initialize(
39
52
  root_path:,
40
53
  load_paths:,
54
+ inflector:,
55
+ cache_directory:,
56
+ config_path: nil,
41
57
  package_paths: nil,
42
- inflector: nil,
43
58
  custom_associations: [],
44
- checker_classes: DEFAULT_CHECKERS
59
+ checkers: DEFAULT_CHECKERS,
60
+ cache_enabled: false
45
61
  )
46
62
  @root_path = root_path
47
63
  @load_paths = load_paths
48
64
  @package_paths = package_paths
49
65
  @inflector = inflector
50
66
  @custom_associations = custom_associations
51
- @checker_classes = checker_classes
67
+ @checkers = checkers
68
+ @cache_enabled = cache_enabled
69
+ @cache_directory = cache_directory
70
+ @config_path = config_path
71
+
72
+ @file_processor = T.let(nil, T.nilable(FileProcessor))
73
+ @context_provider = T.let(nil, T.nilable(ConstantDiscovery))
74
+ # We need to initialize this before we fork the process, see https://github.com/Shopify/packwerk/issues/182
75
+ @cache = T.let(
76
+ Cache.new(enable_cache: @cache_enabled, cache_directory: @cache_directory, config_path: @config_path), Cache
77
+ )
52
78
  end
53
79
 
54
- sig { params(file: String).returns(T::Array[Packwerk::Offense]) }
55
- def process_file(file:)
56
- references = file_processor.call(file)
57
-
58
- reference_checker = ReferenceChecking::ReferenceChecker.new(checkers)
59
- references.flat_map { |reference| reference_checker.call(reference) }
80
+ sig { params(absolute_file: String).returns(T::Array[Packwerk::Offense]) }
81
+ def process_file(absolute_file:)
82
+ unresolved_references_and_offenses = file_processor.call(absolute_file)
83
+ references_and_offenses = ReferenceExtractor.get_fully_qualified_references_and_offenses_from(
84
+ unresolved_references_and_offenses,
85
+ context_provider
86
+ )
87
+ reference_checker = ReferenceChecking::ReferenceChecker.new(@checkers)
88
+ references_and_offenses.flat_map { |reference| reference_checker.call(reference) }
60
89
  end
61
90
 
62
91
  private
63
92
 
64
93
  sig { returns(FileProcessor) }
65
94
  def file_processor
66
- @file_processor ||= FileProcessor.new(node_processor_factory: node_processor_factory)
95
+ @file_processor ||= FileProcessor.new(node_processor_factory: node_processor_factory, cache: @cache)
67
96
  end
68
97
 
69
98
  sig { returns(NodeProcessorFactory) }
70
99
  def node_processor_factory
71
100
  NodeProcessorFactory.new(
72
101
  context_provider: context_provider,
73
- root_path: root_path,
102
+ root_path: @root_path,
74
103
  constant_name_inspectors: constant_name_inspectors
75
104
  )
76
105
  end
77
106
 
78
107
  sig { returns(ConstantDiscovery) }
79
108
  def context_provider
80
- ::Packwerk::ConstantDiscovery.new(
109
+ @context_provider ||= ::Packwerk::ConstantDiscovery.new(
81
110
  constant_resolver: resolver,
82
111
  packages: package_set
83
112
  )
@@ -86,27 +115,22 @@ module Packwerk
86
115
  sig { returns(ConstantResolver) }
87
116
  def resolver
88
117
  ConstantResolver.new(
89
- root_path: root_path,
90
- load_paths: load_paths,
91
- inflector: inflector,
118
+ root_path: @root_path,
119
+ load_paths: @load_paths,
120
+ inflector: @inflector,
92
121
  )
93
122
  end
94
123
 
95
124
  sig { returns(PackageSet) }
96
125
  def package_set
97
- ::Packwerk::PackageSet.load_all_from(root_path, package_pathspec: package_paths)
98
- end
99
-
100
- sig { returns(T::Array[ReferenceChecking::Checkers::Checker]) }
101
- def checkers
102
- checker_classes.map(&:new)
126
+ ::Packwerk::PackageSet.load_all_from(@root_path, package_pathspec: @package_paths)
103
127
  end
104
128
 
105
129
  sig { returns(T::Array[ConstantNameInspector]) }
106
130
  def constant_name_inspectors
107
131
  [
108
132
  ::Packwerk::ConstNodeInspector.new,
109
- ::Packwerk::AssociationInspector.new(inflector: inflector, custom_associations: custom_associations),
133
+ ::Packwerk::AssociationInspector.new(inflector: @inflector, custom_associations: @custom_associations),
110
134
  ]
111
135
  end
112
136
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- # typed: false
2
+ # typed: true
3
3
 
4
4
  require "spring/commands"
5
5
 
@@ -0,0 +1,10 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Packwerk
5
+ # An unresolved reference from a file in one package to a constant that may be defined in a different package.
6
+ # Unresolved means that we know how it's referred to in the file,
7
+ # and we have enough context on that reference to figure out the fully qualified reference such that we
8
+ # can produce a Reference in a separate pass. However, we have not yet resolved it to its fully qualified version.
9
+ UnresolvedReference = Struct.new(:constant_name, :namespace_path, :relative_path, :source_location)
10
+ end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Packwerk
5
- VERSION = "1.4.0"
5
+ VERSION = "2.1.1"
6
6
  end
data/lib/packwerk.rb CHANGED
@@ -5,6 +5,9 @@ require "sorbet-runtime"
5
5
  require "active_support"
6
6
  require "fileutils"
7
7
 
8
+ # Provides String#pluralize
9
+ require "active_support/core_ext/string"
10
+
8
11
  module Packwerk
9
12
  extend ActiveSupport::Autoload
10
13
 
@@ -12,6 +15,7 @@ module Packwerk
12
15
  autoload :ApplicationValidator
13
16
  autoload :AssociationInspector
14
17
  autoload :OffenseCollection
18
+ autoload :Cache
15
19
  autoload :Cli
16
20
  autoload :Configuration
17
21
  autoload :ConstantDiscovery
@@ -21,7 +25,6 @@ module Packwerk
21
25
  autoload :FileProcessor
22
26
  autoload :FilesForProcessing
23
27
  autoload :Graph
24
- autoload :Inflector
25
28
  autoload :Node
26
29
  autoload :NodeProcessor
27
30
  autoload :NodeProcessorFactory
@@ -34,6 +37,7 @@ module Packwerk
34
37
  autoload :ParsedConstantDefinitions
35
38
  autoload :Parsers
36
39
  autoload :ParseRun
40
+ autoload :UnresolvedReference
37
41
  autoload :Reference
38
42
  autoload :ReferenceExtractor
39
43
  autoload :ReferenceOffense
@@ -42,13 +46,6 @@ module Packwerk
42
46
  autoload :Version
43
47
  autoload :ViolationType
44
48
 
45
- module Inflections
46
- extend ActiveSupport::Autoload
47
-
48
- autoload :Custom
49
- autoload :Default
50
- end
51
-
52
49
  module OutputStyles
53
50
  extend ActiveSupport::Autoload
54
51
 
@@ -71,7 +68,6 @@ module Packwerk
71
68
  extend ActiveSupport::Autoload
72
69
 
73
70
  autoload :ConfigurationFile
74
- autoload :InflectionsFile
75
71
  autoload :RootPackage
76
72
  end
77
73
 
data/packwerk.gemspec CHANGED
@@ -45,6 +45,7 @@ Gem::Specification.new do |spec|
45
45
  spec.add_dependency("parallel")
46
46
  spec.add_dependency("sorbet-runtime")
47
47
  spec.add_dependency("bundler")
48
+ spec.add_dependency("digest")
48
49
 
49
50
  spec.add_development_dependency("rake")
50
51
  spec.add_development_dependency("sorbet")
data/sorbet/config CHANGED
@@ -1,2 +1,3 @@
1
1
  --dir
2
2
  .
3
+ --enable-experimental-requires-ancestor
@@ -562,7 +562,7 @@ module Tapioca::GenericTypeRegistry
562
562
  def name_of(constant); end
563
563
  sig { params(object: BasicObject).returns(Integer) }
564
564
  def object_id_of(object); end
565
- sig { params(constant: T.untyped, type_variable_type: T.enum([:type_member, :type_template]), type_variable: T::Types::TypeVariable, fixed: T.untyped, lower: T.untyped, upper: T.untyped).void }
565
+ sig { params(constant: T.untyped, type_variable_type: T.deprecated_enum([:type_member, :type_template]), type_variable: T::Types::TypeVariable, fixed: T.untyped, lower: T.untyped, upper: T.untyped).void }
566
566
  def register_type_variable(constant, type_variable_type, type_variable, fixed, lower, upper); end
567
567
  sig { params(type_variable_type: Symbol, variance: Symbol, fixed: T.untyped, lower: T.untyped, upper: T.untyped).returns(String) }
568
568
  def serialize_type_variable(type_variable_type, variance, fixed, lower, upper); end
@@ -3,7 +3,7 @@
3
3
  # This is an autogenerated file for explicit gem requires.
4
4
  # Please instead update this file by running `tapioca require`.
5
5
 
6
- # typed: false
6
+ # typed: strict
7
7
 
8
8
  require "ast"
9
9
  require "ast/node"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: packwerk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-24 00:00:00.000000000 Z
11
+ date: 2022-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: digest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: rake
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -195,6 +209,7 @@ files:
195
209
  - ".gitignore"
196
210
  - ".rubocop.yml"
197
211
  - ".ruby-version"
212
+ - CHANGELOG.md
198
213
  - CODEOWNERS
199
214
  - CODE_OF_CONDUCT.md
200
215
  - CONTRIBUTING.md
@@ -204,6 +219,7 @@ files:
204
219
  - README.md
205
220
  - Rakefile
206
221
  - TROUBLESHOOT.md
222
+ - UPGRADING.md
207
223
  - USAGE.md
208
224
  - bin/console
209
225
  - bin/m
@@ -220,6 +236,7 @@ files:
220
236
  - lib/packwerk/application_load_paths.rb
221
237
  - lib/packwerk/application_validator.rb
222
238
  - lib/packwerk/association_inspector.rb
239
+ - lib/packwerk/cache.rb
223
240
  - lib/packwerk/cli.rb
224
241
  - lib/packwerk/configuration.rb
225
242
  - lib/packwerk/const_node_inspector.rb
@@ -231,15 +248,10 @@ files:
231
248
  - lib/packwerk/formatters/offenses_formatter.rb
232
249
  - lib/packwerk/formatters/progress_formatter.rb
233
250
  - lib/packwerk/generators/configuration_file.rb
234
- - lib/packwerk/generators/inflections_file.rb
235
251
  - lib/packwerk/generators/root_package.rb
236
- - lib/packwerk/generators/templates/inflections.yml
237
252
  - lib/packwerk/generators/templates/package.yml
238
253
  - lib/packwerk/generators/templates/packwerk.yml.erb
239
254
  - lib/packwerk/graph.rb
240
- - lib/packwerk/inflections/custom.rb
241
- - lib/packwerk/inflections/default.rb
242
- - lib/packwerk/inflector.rb
243
255
  - lib/packwerk/node.rb
244
256
  - lib/packwerk/node_processor.rb
245
257
  - lib/packwerk/node_processor_factory.rb
@@ -257,6 +269,7 @@ files:
257
269
  - lib/packwerk/parsers.rb
258
270
  - lib/packwerk/parsers/erb.rb
259
271
  - lib/packwerk/parsers/factory.rb
272
+ - lib/packwerk/parsers/parser_interface.rb
260
273
  - lib/packwerk/parsers/ruby.rb
261
274
  - lib/packwerk/reference.rb
262
275
  - lib/packwerk/reference_checking/checkers/checker.rb
@@ -269,6 +282,7 @@ files:
269
282
  - lib/packwerk/run_context.rb
270
283
  - lib/packwerk/sanity_checker.rb
271
284
  - lib/packwerk/spring_command.rb
285
+ - lib/packwerk/unresolved_reference.rb
272
286
  - lib/packwerk/version.rb
273
287
  - lib/packwerk/violation_type.rb
274
288
  - library.yml
@@ -1,43 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- module Packwerk
5
- module Generators
6
- class InflectionsFile
7
- extend T::Sig
8
-
9
- class << self
10
- def generate(root:, out:)
11
- new(root, out: out).generate
12
- end
13
- end
14
-
15
- def initialize(root, out: $stdout)
16
- @root = root
17
- @out = out
18
- end
19
-
20
- sig { returns(T::Boolean) }
21
- def generate
22
- ruby_inflection_file_exist = Dir.glob("#{@root}/**/inflections.rb").any?
23
- yaml_inflection_file_exist = Dir.glob("#{@root}/**/inflections.yml").any?
24
-
25
- if !ruby_inflection_file_exist || yaml_inflection_file_exist
26
- return true
27
- end
28
-
29
- @out.puts("📦 Generating `inflections.yml` file...")
30
-
31
- destination_file_path = File.join(@root, "config")
32
- FileUtils.mkdir_p(destination_file_path)
33
-
34
- source_file_path = File.join(__dir__, "/templates/inflections.yml")
35
- FileUtils.cp(source_file_path, destination_file_path)
36
-
37
- @out.puts("✅ `inflections.yml` generated in #{destination_file_path}")
38
-
39
- true
40
- end
41
- end
42
- end
43
- end
@@ -1,6 +0,0 @@
1
- # List your inflections in this file instead of `inflections.rb`
2
- # See steps to set up custom inflections:
3
- # https://github.com/Shopify/packwerk/blob/main/USAGE.md#Inflections
4
-
5
- # acronym:
6
- # - "GraphQL"
@@ -1,33 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- require "yaml"
5
-
6
- module Packwerk
7
- module Inflections
8
- class Custom
9
- SUPPORTED_INFLECTION_METHODS = %w(acronym human irregular plural singular uncountable)
10
-
11
- attr_accessor :inflections
12
-
13
- def initialize(custom_inflection_file = nil)
14
- if custom_inflection_file && File.exist?(custom_inflection_file)
15
- @inflections = YAML.load_file(custom_inflection_file) || {}
16
-
17
- invalid_inflections = @inflections.keys - SUPPORTED_INFLECTION_METHODS
18
- raise ArgumentError, "Unsupported inflection types: #{invalid_inflections}" if invalid_inflections.any?
19
- else
20
- @inflections = []
21
- end
22
- end
23
-
24
- def apply_to(inflections_object)
25
- @inflections.each do |inflection_type, inflections|
26
- inflections.each do |inflection|
27
- inflections_object.public_send(inflection_type, *Array(inflection))
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,73 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- module Packwerk
5
- module Inflections
6
- module Default
7
- class << self
8
- def apply_to(inflections_object)
9
- # copied from active_support/inflections
10
- # https://github.com/rails/rails/blob/d2ae2c3103e93783971d5356d0b3fd1b4070d6cf/activesupport/lib/active_support/inflections.rb#L12
11
- inflections_object.plural(/$/, "s")
12
- inflections_object.plural(/s$/i, "s")
13
- inflections_object.plural(/^(ax|test)is$/i, '\1es')
14
- inflections_object.plural(/(octop|vir)us$/i, '\1i')
15
- inflections_object.plural(/(octop|vir)i$/i, '\1i')
16
- inflections_object.plural(/(alias|status)$/i, '\1es')
17
- inflections_object.plural(/(bu)s$/i, '\1ses')
18
- inflections_object.plural(/(buffal|tomat)o$/i, '\1oes')
19
- inflections_object.plural(/([ti])um$/i, '\1a')
20
- inflections_object.plural(/([ti])a$/i, '\1a')
21
- inflections_object.plural(/sis$/i, "ses")
22
- inflections_object.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
23
- inflections_object.plural(/(hive)$/i, '\1s')
24
- inflections_object.plural(/([^aeiouy]|qu)y$/i, '\1ies')
25
- inflections_object.plural(/(x|ch|ss|sh)$/i, '\1es')
26
- inflections_object.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
27
- inflections_object.plural(/^(m|l)ouse$/i, '\1ice')
28
- inflections_object.plural(/^(m|l)ice$/i, '\1ice')
29
- inflections_object.plural(/^(ox)$/i, '\1en')
30
- inflections_object.plural(/^(oxen)$/i, '\1')
31
- inflections_object.plural(/(quiz)$/i, '\1zes')
32
-
33
- inflections_object.singular(/s$/i, "")
34
- inflections_object.singular(/(ss)$/i, '\1')
35
- inflections_object.singular(/(n)ews$/i, '\1ews')
36
- inflections_object.singular(/([ti])a$/i, '\1um')
37
- inflections_object.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
38
- inflections_object.singular(/(^analy)(sis|ses)$/i, '\1sis')
39
- inflections_object.singular(/([^f])ves$/i, '\1fe')
40
- inflections_object.singular(/(hive)s$/i, '\1')
41
- inflections_object.singular(/(tive)s$/i, '\1')
42
- inflections_object.singular(/([lr])ves$/i, '\1f')
43
- inflections_object.singular(/([^aeiouy]|qu)ies$/i, '\1y')
44
- inflections_object.singular(/(s)eries$/i, '\1eries')
45
- inflections_object.singular(/(m)ovies$/i, '\1ovie')
46
- inflections_object.singular(/(x|ch|ss|sh)es$/i, '\1')
47
- inflections_object.singular(/^(m|l)ice$/i, '\1ouse')
48
- inflections_object.singular(/(bus)(es)?$/i, '\1')
49
- inflections_object.singular(/(o)es$/i, '\1')
50
- inflections_object.singular(/(shoe)s$/i, '\1')
51
- inflections_object.singular(/(cris|test)(is|es)$/i, '\1is')
52
- inflections_object.singular(/^(a)x[ie]s$/i, '\1xis')
53
- inflections_object.singular(/(octop|vir)(us|i)$/i, '\1us')
54
- inflections_object.singular(/(alias|status)(es)?$/i, '\1')
55
- inflections_object.singular(/^(ox)en/i, '\1')
56
- inflections_object.singular(/(vert|ind)ices$/i, '\1ex')
57
- inflections_object.singular(/(matr)ices$/i, '\1ix')
58
- inflections_object.singular(/(quiz)zes$/i, '\1')
59
- inflections_object.singular(/(database)s$/i, '\1')
60
-
61
- inflections_object.irregular("person", "people")
62
- inflections_object.irregular("man", "men")
63
- inflections_object.irregular("child", "children")
64
- inflections_object.irregular("sex", "sexes")
65
- inflections_object.irregular("move", "moves")
66
- inflections_object.irregular("zombie", "zombies")
67
-
68
- inflections_object.uncountable(%w(equipment information rice money species series fish sheep jeans police))
69
- end
70
- end
71
- end
72
- end
73
- end
@@ -1,49 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- require "active_support/inflector"
5
-
6
- module Packwerk
7
- # A custom inflector used, among other things, to map between constant names and file names.
8
- class Inflector
9
- class << self
10
- extend T::Sig
11
-
12
- def default
13
- @default ||= new(custom_inflector: Inflections::Custom.new)
14
- end
15
-
16
- sig { params(inflections_file: String).returns(::Packwerk::Inflector) }
17
- def from_file(inflections_file)
18
- new(custom_inflector: Inflections::Custom.new(inflections_file))
19
- end
20
- end
21
-
22
- extend T::Sig
23
- include ::ActiveSupport::Inflector # For #camelize, #classify, #pluralize, #singularize
24
-
25
- sig do
26
- params(
27
- custom_inflector: Inflections::Custom
28
- ).void
29
- end
30
- def initialize(custom_inflector:)
31
- @inflections = ::ActiveSupport::Inflector::Inflections.new
32
-
33
- Inflections::Default.apply_to(@inflections)
34
- custom_inflector.apply_to(@inflections)
35
- end
36
-
37
- def pluralize(word, count = nil)
38
- if count == 1
39
- singularize(word)
40
- else
41
- super(word)
42
- end
43
- end
44
-
45
- def inflections(_ = nil)
46
- @inflections
47
- end
48
- end
49
- end