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 +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +15 -11
- data/.github/workflows/cla.yml +22 -0
- data/.gitignore +0 -1
- data/.rubocop.yml +4 -5
- data/.ruby-version +1 -0
- data/Gemfile +4 -3
- data/Gemfile.lock +55 -0
- data/LICENSE.txt +1 -1
- data/README.md +32 -0
- data/constant_resolver.gemspec +9 -5
- data/dev.yml +1 -1
- data/lib/constant_resolver/version.rb +1 -1
- data/lib/constant_resolver.rb +54 -12
- data/service.yml +1 -6
- data/shipit.rubygems.yml +1 -0
- metadata +16 -16
- data/.github/probots.yml +0 -2
- data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +0 -1027
- data/shipit.yml +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 814a147bad07ced23e5a73270dd1beb72818f32410557ca1c7482ba4ec07017b
|
4
|
+
data.tar.gz: c346b11e325b91221977faae415df878f2ea75849b7e0028fb90466c273c45a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 262e46796e4984468bbecb89666e3503f72585e950b3cfc5ec245344e2b578855f877a292c115018d339e815e8746fae00bee72771d408c1a9957dbaa5c3c4e9
|
7
|
+
data.tar.gz: 19f7b87f20f5af3e8e73491e5810371776d96d27c33219275198cff62258462d6d8cf61217a869fb0705e6528feaa7f1d8d85208d82425806bf801dbd17d8bcf
|
data/.github/workflows/ci.yml
CHANGED
@@ -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
|
11
|
-
uses:
|
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:
|
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
|
24
|
-
uses:
|
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
data/.rubocop.yml
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
|
2
|
-
-
|
1
|
+
inherit_gem:
|
2
|
+
rubocop-shopify: rubocop.yml
|
3
3
|
|
4
4
|
AllCops:
|
5
|
-
|
6
|
-
|
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
|
11
|
+
gem "rake"
|
12
12
|
end
|
13
13
|
|
14
14
|
group :development do
|
15
|
-
gem
|
16
|
-
gem
|
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
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.
|
data/constant_resolver.gemspec
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
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(
|
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.
|
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
data/lib/constant_resolver.rb
CHANGED
@@ -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.
|
80
|
-
Dir[
|
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
|
-
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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
|
-
|
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
data/shipit.rubygems.yml
ADDED
@@ -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.
|
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:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
13
|
+
name: minitest
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
16
15
|
requirements:
|
17
16
|
- - "~>"
|
18
17
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
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: '
|
25
|
+
version: '5.0'
|
27
26
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
27
|
+
name: rake
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
30
29
|
requirements:
|
31
30
|
- - "~>"
|
32
31
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
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: '
|
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/
|
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
|
-
|
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:
|
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.
|
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