rubocop-nueca 1.1.0 → 1.1.3
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/workflows/publish.yml +52 -0
- data/lib/rubocop/cop/rails/model_association_sorting.rb +55 -19
- data/lib/rubocop/nueca/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e3ce6ea4962d0409ef753cd4cfb316f81a8f40228539b62871d968b54df17f2
|
4
|
+
data.tar.gz: 34d40d5a28a407927510a815ce108121d2ab60bfc157ce2eac671af5b3a18c16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 10f24a5fd49e0ee7344e4a260fb58af10ff80a6711a828ff76dfc204d7f17177ed79757fcea03488efba7cc46051c9bcb9c2bdf297ddef3207367340caebafab
|
7
|
+
data.tar.gz: c446cf64ec058628f6c7540ee9519844b83e81d28db772524d86438a96d1343407a7a8af5ef8f6172f5cd5e6afe200c66b7089433114af45ed36931625b7dbea
|
@@ -0,0 +1,52 @@
|
|
1
|
+
name: Publish Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
publish:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v4
|
13
|
+
|
14
|
+
- name: Set up Ruby
|
15
|
+
uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: '3.4'
|
18
|
+
bundler-cache: true
|
19
|
+
|
20
|
+
- name: Get gem version
|
21
|
+
id: gem_version
|
22
|
+
run: |
|
23
|
+
VERSION=$(ruby -e "require './lib/rubocop/nueca/version'; puts RuboCop::Nueca::VERSION")
|
24
|
+
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
25
|
+
|
26
|
+
- name: Check if version exists on RubyGems
|
27
|
+
id: version_check
|
28
|
+
run: |
|
29
|
+
if gem list -r rubocop-nueca | grep -q "${{ steps.gem_version.outputs.version }}"; then
|
30
|
+
echo "exists=true" >> $GITHUB_OUTPUT
|
31
|
+
else
|
32
|
+
echo "exists=false" >> $GITHUB_OUTPUT
|
33
|
+
fi
|
34
|
+
|
35
|
+
- name: Build gem
|
36
|
+
if: steps.version_check.outputs.exists == 'false'
|
37
|
+
run: gem build *.gemspec
|
38
|
+
|
39
|
+
- name: Publish to RubyGems
|
40
|
+
if: steps.version_check.outputs.exists == 'false'
|
41
|
+
run: |
|
42
|
+
mkdir -p $HOME/.gem
|
43
|
+
touch $HOME/.gem/credentials
|
44
|
+
chmod 0600 $HOME/.gem/credentials
|
45
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
46
|
+
gem push *.gem
|
47
|
+
env:
|
48
|
+
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_AUTH_TOKEN }}
|
49
|
+
|
50
|
+
- name: Skip publishing
|
51
|
+
if: steps.version_check.outputs.exists == 'true'
|
52
|
+
run: echo "Version ${{ steps.gem_version.outputs.version }} already exists on RubyGems. Skipping publish."
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
class ModelAssociationSorting < RuboCop::Cop::Base
|
6
|
+
class ModelAssociationSorting < RuboCop::Cop::Base # rubocop:disable Metrics/ClassLength
|
7
7
|
MSG = 'Sort associations of the same type alphabetically. Expected order: %<expected>s.'
|
8
8
|
ASSOCIATION_METHODS = [
|
9
9
|
:belongs_to,
|
@@ -93,14 +93,11 @@ module RuboCop
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def check_group_sorting(group_associations)
|
96
|
-
# Separate associations with and without through
|
97
96
|
through_associations = group_associations.select { |assoc| assoc[:through] }
|
98
97
|
regular_associations = group_associations.reject { |assoc| assoc[:through] }
|
99
98
|
|
100
|
-
# Sort regular associations alphabetically
|
101
99
|
regular_sorted = regular_associations.sort_by { |assoc| assoc[:name] }
|
102
100
|
|
103
|
-
# Build expected order respecting through dependencies
|
104
101
|
expected_order = build_expected_order(regular_sorted, through_associations)
|
105
102
|
actual_order = group_associations.map { |assoc| assoc[:name] }
|
106
103
|
|
@@ -113,28 +110,67 @@ module RuboCop
|
|
113
110
|
end
|
114
111
|
|
115
112
|
def build_expected_order(regular_associations, through_associations)
|
116
|
-
|
113
|
+
all_associations = regular_associations + through_associations
|
114
|
+
association_lookup = build_association_lookup(all_associations)
|
115
|
+
dependency_graph = build_dependency_graph(all_associations, association_lookup)
|
117
116
|
|
118
|
-
|
119
|
-
|
120
|
-
|
117
|
+
topological_sort_alphabetically(dependency_graph, all_associations.map { |a| a[:name] })
|
118
|
+
end
|
119
|
+
|
120
|
+
def build_association_lookup(associations)
|
121
|
+
associations.index_by { |assoc| assoc[:name] }
|
122
|
+
end
|
123
|
+
|
124
|
+
def build_dependency_graph(associations, lookup)
|
125
|
+
graph = associations.each_with_object({}) { |assoc, deps| deps[assoc[:name]] = [] }
|
126
|
+
|
127
|
+
associations.each do |assoc|
|
128
|
+
graph[assoc[:through]] << assoc[:name] if assoc[:through] && lookup[assoc[:through]]
|
129
|
+
end
|
130
|
+
|
131
|
+
graph
|
132
|
+
end
|
133
|
+
|
134
|
+
def topological_sort_alphabetically(dependency_graph, all_nodes)
|
135
|
+
in_degrees = calculate_in_degrees(dependency_graph, all_nodes)
|
136
|
+
available_nodes = nodes_with_zero_dependencies(in_degrees)
|
137
|
+
result = []
|
138
|
+
|
139
|
+
until available_nodes.empty?
|
140
|
+
current = available_nodes.shift
|
141
|
+
result << current
|
121
142
|
|
122
|
-
|
123
|
-
dependent_through = through_associations.select { |ta| ta[:through] == assoc[:name] }
|
124
|
-
dependent_through.sort_by { |ta| ta[:name] }.each do |ta|
|
125
|
-
expected << ta[:name]
|
126
|
-
end
|
143
|
+
process_dependents(current, dependency_graph, in_degrees, available_nodes)
|
127
144
|
end
|
128
145
|
|
129
|
-
|
130
|
-
|
131
|
-
|
146
|
+
result
|
147
|
+
end
|
148
|
+
|
149
|
+
def calculate_in_degrees(dependency_graph, all_nodes)
|
150
|
+
in_degrees = all_nodes.each_with_object({}) { |node, degrees| degrees[node] = 0 }
|
151
|
+
|
152
|
+
dependency_graph.each_value do |dependents|
|
153
|
+
dependents.each { |dependent| in_degrees[dependent] += 1 }
|
132
154
|
end
|
133
|
-
|
134
|
-
|
155
|
+
|
156
|
+
in_degrees
|
157
|
+
end
|
158
|
+
|
159
|
+
def nodes_with_zero_dependencies(in_degrees)
|
160
|
+
in_degrees.select { |_node, degree| degree.zero? }.keys.sort
|
161
|
+
end
|
162
|
+
|
163
|
+
def process_dependents(current_node, dependency_graph, in_degrees, available_nodes)
|
164
|
+
dependency_graph[current_node]&.each do |dependent|
|
165
|
+
in_degrees[dependent] -= 1
|
166
|
+
|
167
|
+
insert_alphabetically(available_nodes, dependent) if in_degrees[dependent].zero?
|
135
168
|
end
|
169
|
+
end
|
136
170
|
|
137
|
-
|
171
|
+
def insert_alphabetically(sorted_array, element)
|
172
|
+
insert_position = sorted_array.bsearch_index { |x| x > element } || sorted_array.length
|
173
|
+
sorted_array.insert(insert_position, element)
|
138
174
|
end
|
139
175
|
end
|
140
176
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-nueca
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tien
|
@@ -133,6 +133,7 @@ executables: []
|
|
133
133
|
extensions: []
|
134
134
|
extra_rdoc_files: []
|
135
135
|
files:
|
136
|
+
- ".github/workflows/publish.yml"
|
136
137
|
- CODE_OF_CONDUCT.md
|
137
138
|
- LICENSE.txt
|
138
139
|
- README.md
|