constant_resolver 0.1.5 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c05074565a7f3112d32cf0a4968ea4193fbce4eafc41e4873279e3a1a9787f2
4
- data.tar.gz: 4eab3c4e64102b05497709501bb1bab4b718be511b7ccfc31d2c7c919f86b131
3
+ metadata.gz: 814a147bad07ced23e5a73270dd1beb72818f32410557ca1c7482ba4ec07017b
4
+ data.tar.gz: c346b11e325b91221977faae415df878f2ea75849b7e0028fb90466c273c45a3
5
5
  SHA512:
6
- metadata.gz: b3feb2e2646e09c6fc840dd406e36341fe5c3fac6d0ecd6f1dcf8e2e7aa2cd64d9cf659e3eb832b5b8240191bdf02ae1f5d9355362e8c36f3c684a314463d9d9
7
- data.tar.gz: 16922c192716b2ddb615cdc43503a27b878d4bc798ad7869ad0fa319a442f64df01da1510fea8510b2fd2299e4ea75dd14c2f76be267574664c8b767d1d9d7ad
6
+ metadata.gz: 262e46796e4984468bbecb89666e3503f72585e950b3cfc5ec245344e2b578855f877a292c115018d339e815e8746fae00bee72771d408c1a9957dbaa5c3c4e9
7
+ data.tar.gz: 19f7b87f20f5af3e8e73491e5810371776d96d27c33219275198cff62258462d6d8cf61217a869fb0705e6528feaa7f1d8d85208d82425806bf801dbd17d8bcf
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: "/"
5
+ schedule:
6
+ interval: weekly
@@ -5,27 +5,31 @@ on: [push, pull_request]
5
5
  jobs:
6
6
  test:
7
7
  runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby:
11
+ - "2.7"
12
+ - "3.0"
13
+ - "3.1"
14
+ - "3.2"
15
+ - "3.3"
8
16
  steps:
9
- - uses: actions/checkout@v1
10
- - name: Set up Ruby 2.6
11
- uses: actions/setup-ruby@v1
17
+ - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0
18
+ - name: Set up Ruby
19
+ uses: ruby/setup-ruby@e34163cd15f4bb403dcd72d98e295997e6a55798 # v1.238.0
12
20
  with:
13
- ruby-version: 2.6.x
21
+ ruby-version: ${{ matrix.ruby }}
14
22
  - name: Run tests
15
23
  run: |
16
- gem install bundler
17
24
  bundle install --jobs 4 --retry 3
18
25
  bundle exec rake
19
26
  lint:
20
27
  runs-on: ubuntu-latest
21
28
  steps:
22
- - uses: actions/checkout@v1
23
- - name: Set up Ruby 2.6
24
- uses: actions/setup-ruby@v1
25
- with:
26
- ruby-version: 2.6.x
29
+ - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0
30
+ - name: Set up Ruby
31
+ uses: ruby/setup-ruby@e34163cd15f4bb403dcd72d98e295997e6a55798 # v1.238.0
27
32
  - name: Run style checks
28
33
  run: |
29
- gem install bundler
30
34
  bundle install --jobs 4 --retry 3
31
35
  bundle exec rubocop
@@ -0,0 +1,22 @@
1
+ name: Contributor License Agreement (CLA)
2
+
3
+ on:
4
+ pull_request_target:
5
+ types: [opened, synchronize]
6
+ issue_comment:
7
+ types: [created]
8
+
9
+ jobs:
10
+ cla:
11
+ runs-on: ubuntu-latest
12
+ if: |
13
+ (github.event.issue.pull_request
14
+ && !github.event.issue.pull_request.merged_at
15
+ && contains(github.event.comment.body, 'signed')
16
+ )
17
+ || (github.event.pull_request && !github.event.pull_request.merged)
18
+ steps:
19
+ - uses: Shopify/shopify-cla-action@v1
20
+ with:
21
+ github-token: ${{ secrets.GITHUB_TOKEN }}
22
+ cla-token: ${{ secrets.CLA_TOKEN }}
data/.gitignore CHANGED
@@ -6,6 +6,5 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
- /Gemfile.lock
10
9
  /.bundle/
11
10
  /tmp/
data/.rubocop.yml CHANGED
@@ -1,9 +1,8 @@
1
- inherit_from:
2
- - https://shopify.github.io/ruby-style-guide/rubocop.yml
1
+ inherit_gem:
2
+ rubocop-shopify: rubocop.yml
3
3
 
4
4
  AllCops:
5
- TargetRubyVersion: 2.6
6
- UseCache: true
7
- CacheRootDirectory: tmp/rubocop
5
+ SuggestExtensions: false
6
+ NewCops: disable
8
7
 
9
8
  require: rubocop-performance
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3.1
data/Gemfile CHANGED
@@ -8,10 +8,11 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
8
8
  gemspec
9
9
 
10
10
  group :deployment do
11
- gem 'rake'
11
+ gem "rake"
12
12
  end
13
13
 
14
14
  group :development do
15
- gem 'rubocop', '~> 0.75.1', require: false # 0.76 currently not compatible with shopify style guide
16
- gem 'rubocop-performance'
15
+ gem "rubocop", require: false
16
+ gem "rubocop-performance", require: false
17
+ gem "rubocop-shopify", require: false
17
18
  end
data/Gemfile.lock ADDED
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ constant_resolver (0.3.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.2)
10
+ json (2.7.1)
11
+ language_server-protocol (3.17.0.3)
12
+ minitest (5.22.3)
13
+ parallel (1.24.0)
14
+ parser (3.3.0.5)
15
+ ast (~> 2.4.1)
16
+ racc
17
+ racc (1.7.3)
18
+ rainbow (3.1.1)
19
+ rake (13.2.1)
20
+ regexp_parser (2.9.0)
21
+ rexml (3.3.9)
22
+ rubocop (1.62.1)
23
+ json (~> 2.3)
24
+ language_server-protocol (>= 3.17.0)
25
+ parallel (~> 1.10)
26
+ parser (>= 3.3.0.2)
27
+ rainbow (>= 2.2.2, < 4.0)
28
+ regexp_parser (>= 1.8, < 3.0)
29
+ rexml (>= 3.2.5, < 4.0)
30
+ rubocop-ast (>= 1.31.1, < 2.0)
31
+ ruby-progressbar (~> 1.7)
32
+ unicode-display_width (>= 2.4.0, < 3.0)
33
+ rubocop-ast (1.31.2)
34
+ parser (>= 3.3.0.4)
35
+ rubocop-performance (1.21.0)
36
+ rubocop (>= 1.48.1, < 2.0)
37
+ rubocop-ast (>= 1.31.1, < 2.0)
38
+ rubocop-shopify (2.15.1)
39
+ rubocop (~> 1.51)
40
+ ruby-progressbar (1.13.0)
41
+ unicode-display_width (2.5.0)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ constant_resolver!
48
+ minitest (~> 5.0)
49
+ rake
50
+ rubocop
51
+ rubocop-performance
52
+ rubocop-shopify
53
+
54
+ BUNDLED WITH
55
+ 2.4.22
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2019 Shopify Inc
3
+ Copyright (c) 2019-present Shopify Inc
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -36,6 +36,21 @@ resolver = ConstantResolver.new(
36
36
  )
37
37
  ```
38
38
 
39
+ ### Default namespaces
40
+
41
+ If any load paths have a default namespace other than `Object`, these can be specified in a hash:
42
+
43
+ ```ruby
44
+ resolver = ConstantResolver.new(
45
+ root_path: "/app",
46
+ load_paths: {
47
+ "/app/models" => "::Object",
48
+ "/app/services" => "::Object",
49
+ "/app/internal" => "::Business",
50
+ }
51
+ )
52
+ ```
53
+
39
54
  ### Resolve a constant
40
55
 
41
56
  Resolve a constant from the contents of your load paths:
@@ -47,6 +62,23 @@ context.name # => "::Some::Nested::Model"
47
62
  context.location # => "models/some/nested/model.rb"
48
63
  ```
49
64
 
65
+ ### Ignoring paths
66
+
67
+ You may want to only resolve constants from certain sections of your application. If you want to leave any paths out, use `exclude`:
68
+
69
+ ```ruby
70
+ resolver = ConstantResolver.new(
71
+ root_path: "/app",
72
+ load_paths: [
73
+ "/app/models",
74
+ "/some/engine/app/models",
75
+ ],
76
+ exclude: [
77
+ "some/engine/**/*"
78
+ ],
79
+ )
80
+ ```
81
+
50
82
  ## Development
51
83
 
52
84
  After checking out the repo, run `bundle` to install dependencies. Then, run `rake test` to run the tests.
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path("../lib", __FILE__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require "constant_resolver/version"
3
+ require_relative "lib/constant_resolver/version"
6
4
 
7
5
  Gem::Specification.new do |spec|
8
6
  spec.name = "constant_resolver"
@@ -22,15 +20,21 @@ Gem::Specification.new do |spec|
22
20
  spec.metadata["homepage_uri"] = spec.homepage
23
21
  spec.metadata["source_code_uri"] = "https://github.com/Shopify/constant_resolver"
24
22
  spec.metadata["changelog_uri"] = "https://github.com/Shopify/constant_resolver/releases"
23
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
24
+ else
25
+ raise "RubyGems 2.0 or newer is required to protect against " \
26
+ "public gem pushes."
25
27
  end
26
28
 
27
29
  # Specify which files should be added to the gem when it is released.
28
30
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
29
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
31
+ spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
30
32
  %x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
31
33
  end
32
34
  spec.require_paths = ["lib"]
33
35
 
34
- spec.add_development_dependency("rake", "~> 10.0")
36
+ spec.required_ruby_version = ">= 2.7.0"
37
+
35
38
  spec.add_development_dependency("minitest", "~> 5.0")
39
+ spec.add_development_dependency("rake", "~> 13.2")
36
40
  end
data/dev.yml CHANGED
@@ -3,7 +3,7 @@ name: constant-resolver
3
3
  type: ruby
4
4
 
5
5
  up:
6
- - ruby: 2.6.2
6
+ - ruby
7
7
  - bundler
8
8
 
9
9
  commands:
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ConstantResolver
4
- VERSION = "0.1.5"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -13,6 +13,8 @@ require "constant_resolver/version"
13
13
  # have no way of inferring the file it is defined in. You could argue though that inheritance means that another
14
14
  # constant with the same name exists in the inheriting class, and this view is sufficient for all our use cases.
15
15
  class ConstantResolver
16
+ RUBY_FILES_GLOB = "**/*.rb"
17
+
16
18
  class Error < StandardError; end
17
19
  class ConstantContext < Struct.new(:name, :location); end
18
20
 
@@ -25,10 +27,12 @@ class ConstantResolver
25
27
  end
26
28
  end
27
29
 
30
+ private_constant :RUBY_FILES_GLOB
28
31
  private_constant :DefaultInflector
29
32
 
30
33
  # @param root_path [String] The root path of the application to analyze
31
34
  # @param load_paths [Array<String>] The autoload paths of the application.
35
+ # @param exclude [Array<String>] Paths to exclude to scan for constants.
32
36
  # @param inflector [Object] Any object that implements a `camelize` function.
33
37
  #
34
38
  # @example usage in a Rails app
@@ -39,14 +43,14 @@ class ConstantResolver
39
43
  # root_path: Rails.root.to_s,
40
44
  # load_paths: load_paths
41
45
  # )
42
- def initialize(root_path:, load_paths:, inflector: DefaultInflector.new)
46
+ def initialize(root_path:, load_paths:, exclude: [], inflector: DefaultInflector.new)
43
47
  root_path += "/" unless root_path.end_with?("/")
44
- load_paths = load_paths.map { |p| p.end_with?("/") ? p : p + "/" }.uniq
45
48
 
46
49
  @root_path = root_path
47
- @load_paths = load_paths
50
+ @load_paths = coerce_load_paths(load_paths)
48
51
  @file_map = nil
49
52
  @inflector = inflector
53
+ @exclude = exclude
50
54
  end
51
55
 
52
56
  # Resolve a constant via its name.
@@ -73,30 +77,44 @@ class ConstantResolver
73
77
  # @return [Hash<String, String>]
74
78
  def file_map
75
79
  return @file_map if @file_map
80
+
76
81
  @file_map = {}
77
82
  duplicate_files = {}
78
83
 
79
- @load_paths.each do |load_path|
80
- Dir[@root_path + load_path + "**/*.rb"].each do |file_path|
84
+ @load_paths.each_pair do |load_path, default_ns|
85
+ Dir[glob_path(load_path)].each do |file_path|
81
86
  root_relative_path = file_path.delete_prefix!(@root_path)
82
87
  const_name = @inflector.camelize(root_relative_path.delete_prefix(load_path).delete_suffix!(".rb"))
88
+ const_name = "#{default_ns}::#{const_name}" unless default_ns == "Object"
83
89
  existing_entry = @file_map[const_name]
84
90
 
85
91
  if existing_entry
86
92
  duplicate_files[const_name] ||= [existing_entry]
87
93
  duplicate_files[const_name] << root_relative_path
88
94
  end
89
- @file_map[const_name] = root_relative_path
95
+
96
+ if allowed?(root_relative_path)
97
+ @file_map[const_name] = root_relative_path
98
+ end
90
99
  end
91
100
  end
92
101
 
93
- unless duplicate_files.empty?
94
- message = duplicate_files.map do |const_name, full_paths|
95
- "ERROR: '#{const_name}' could refer to any of\n#{full_paths.map { |p| ' ' + p }.join("\n")}"
96
- end.join("\n")
97
- raise(Error, message)
102
+ if duplicate_files.any?
103
+ raise(Error, <<~MSG)
104
+ Ambiguous constant definition:
105
+
106
+ #{duplicate_files.map { |const_name, paths| ambiguous_constant_message(const_name, paths) }.join("\n")}
107
+ MSG
108
+ end
109
+
110
+ if @file_map.empty?
111
+ raise(Error, <<~MSG)
112
+ Could not find any ruby files. Searched in:
113
+
114
+ - #{@load_paths.keys.map { |load_path| glob_path(load_path) }.join("\n- ")}
115
+ MSG
98
116
  end
99
- raise(Error, "could not find any files") if @file_map.empty?
117
+
100
118
  @file_map
101
119
  end
102
120
 
@@ -110,6 +128,30 @@ class ConstantResolver
110
128
 
111
129
  private
112
130
 
131
+ def allowed?(path)
132
+ !@exclude.any? { |glob| File.fnmatch(glob, path, File::FNM_EXTGLOB | File::FNM_PATHNAME) }
133
+ end
134
+
135
+ def coerce_load_paths(load_paths)
136
+ load_paths = Hash[load_paths.map { |p| [p, "Object"] }] unless load_paths.respond_to?(:transform_keys)
137
+
138
+ load_paths.transform_keys! { |p| p.end_with?("/") ? p : p + "/" }
139
+ load_paths.transform_values! { |ns| ns.to_s.delete_prefix("::") }
140
+
141
+ load_paths
142
+ end
143
+
144
+ def ambiguous_constant_message(const_name, paths)
145
+ <<~MSG.chomp
146
+ "#{const_name}" could refer to any of
147
+ #{paths.join("\n ")}
148
+ MSG
149
+ end
150
+
151
+ def glob_path(path)
152
+ File.join(@root_path, path, RUBY_FILES_GLOB)
153
+ end
154
+
113
155
  def resolve_constant(const_name, current_namespace_path, original_name: const_name)
114
156
  namespace, location = resolve_traversing_namespace_path(const_name, current_namespace_path)
115
157
  if location
data/service.yml CHANGED
@@ -1,6 +1 @@
1
- ---
2
- classification: library
3
- owners:
4
- - Shopify/component-patterns
5
- slack_channels:
6
- - architecture-patterns
1
+ --- {}
@@ -0,0 +1 @@
1
+ # defaults
metadata CHANGED
@@ -1,43 +1,42 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: constant_resolver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Müller
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2019-12-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: rake
13
+ name: minitest
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '10.0'
18
+ version: '5.0'
20
19
  type: :development
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: '10.0'
25
+ version: '5.0'
27
26
  - !ruby/object:Gem::Dependency
28
- name: minitest
27
+ name: rake
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - "~>"
32
31
  - !ruby/object:Gem::Version
33
- version: '5.0'
32
+ version: '13.2'
34
33
  type: :development
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - "~>"
39
38
  - !ruby/object:Gem::Version
40
- version: '5.0'
39
+ version: '13.2'
41
40
  description: |
42
41
  Given a code base that adheres to certain conventions, ConstantResolver resolves any, even partially qualified,
43
42
  constant to the path of the file that defines it.
@@ -47,14 +46,16 @@ executables: []
47
46
  extensions: []
48
47
  extra_rdoc_files: []
49
48
  files:
50
- - ".github/probots.yml"
49
+ - ".github/dependabot.yml"
51
50
  - ".github/workflows/ci.yml"
51
+ - ".github/workflows/cla.yml"
52
52
  - ".gitignore"
53
- - ".rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml"
54
53
  - ".rubocop.yml"
54
+ - ".ruby-version"
55
55
  - CODE_OF_CONDUCT.md
56
56
  - CONTRIBUTING.md
57
57
  - Gemfile
58
+ - Gemfile.lock
58
59
  - LICENSE.txt
59
60
  - README.md
60
61
  - Rakefile
@@ -63,7 +64,7 @@ files:
63
64
  - lib/constant_resolver.rb
64
65
  - lib/constant_resolver/version.rb
65
66
  - service.yml
66
- - shipit.yml
67
+ - shipit.rubygems.yml
67
68
  homepage: https://github.com/Shopify/constant_resolver
68
69
  licenses:
69
70
  - MIT
@@ -71,7 +72,7 @@ metadata:
71
72
  homepage_uri: https://github.com/Shopify/constant_resolver
72
73
  source_code_uri: https://github.com/Shopify/constant_resolver
73
74
  changelog_uri: https://github.com/Shopify/constant_resolver/releases
74
- post_install_message:
75
+ allowed_push_host: https://rubygems.org
75
76
  rdoc_options: []
76
77
  require_paths:
77
78
  - lib
@@ -79,15 +80,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
79
80
  requirements:
80
81
  - - ">="
81
82
  - !ruby/object:Gem::Version
82
- version: '0'
83
+ version: 2.7.0
83
84
  required_rubygems_version: !ruby/object:Gem::Requirement
84
85
  requirements:
85
86
  - - ">="
86
87
  - !ruby/object:Gem::Version
87
88
  version: '0'
88
89
  requirements: []
89
- rubygems_version: 3.0.3
90
- signing_key:
90
+ rubygems_version: 3.6.9
91
91
  specification_version: 4
92
92
  summary: Statically resolve any ruby constant
93
93
  test_files: []
data/.github/probots.yml DELETED
@@ -1,2 +0,0 @@
1
- enabled:
2
- - cla