packwerk 1.4.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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