Skip to content

Commit b2a7dba

Browse files
committed
fix: refactor methods to reduce cyclomatic complexity
All changes maintain backward compatibility while improving code quality, maintainability, and readability. The refactoring follows SOLID principles by extracting methods with single responsibilities
1 parent cfbdadf commit b2a7dba

8 files changed

Lines changed: 269 additions & 207 deletions

File tree

app/controllers/api/v1/analytics/champions_controller.rb

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,42 +20,56 @@ module Analytics
2020
class ChampionsController < Api::V1::BaseController
2121
def show
2222
player = organization_scoped(Player).find(params[:player_id])
23+
stats = fetch_champion_stats(player)
24+
champion_stats = build_champion_stats(stats)
2325

24-
stats = PlayerMatchStat.where(player: player)
25-
.group(:champion)
26-
.select(
27-
'champion',
28-
'COUNT(*) as games_played',
29-
'SUM(CASE WHEN matches.victory THEN 1 ELSE 0 END) as wins',
30-
'AVG((kills + assists)::float / NULLIF(deaths, 0)) as avg_kda',
31-
'AVG(cs_per_min) as avg_cs_per_min',
32-
'AVG(damage_dealt_total) as avg_damage_dealt',
33-
'AVG(damage_taken) as avg_damage_taken',
34-
'AVG(gold_per_min) as avg_gold_per_min',
35-
'AVG(vision_score) as avg_vision_score'
36-
)
37-
.joins(:match)
38-
.order('games_played DESC')
26+
render_success(build_champion_data(player, champion_stats))
27+
end
28+
29+
private
3930

31+
def fetch_champion_stats(player)
32+
PlayerMatchStat.where(player: player)
33+
.group(:champion)
34+
.select(
35+
'champion',
36+
'COUNT(*) as games_played',
37+
'SUM(CASE WHEN matches.victory THEN 1 ELSE 0 END) as wins',
38+
'AVG((kills + assists)::float / NULLIF(deaths, 0)) as avg_kda',
39+
'AVG(cs_per_min) as avg_cs_per_min',
40+
'AVG(damage_dealt_total) as avg_damage_dealt',
41+
'AVG(damage_taken) as avg_damage_taken',
42+
'AVG(gold_per_min) as avg_gold_per_min',
43+
'AVG(vision_score) as avg_vision_score'
44+
)
45+
.joins(:match)
46+
.order('games_played DESC')
47+
end
48+
49+
def build_champion_stats(stats)
4050
riot_service = RiotCdnService.new
41-
champion_stats = stats.map do |stat|
42-
win_rate = stat.games_played.zero? ? 0 : (stat.wins.to_f / stat.games_played)
43-
{
44-
champion: stat.champion,
45-
games_played: stat.games_played,
46-
win_rate: win_rate,
47-
avg_kda: stat.avg_kda&.round(2) || 0,
48-
avg_cs_per_min: stat.avg_cs_per_min&.round(1) || 0.0,
49-
avg_damage_dealt: stat.avg_damage_dealt&.round(0) || 0,
50-
avg_damage_taken: stat.avg_damage_taken&.round(0) || 0,
51-
avg_gold_per_min: stat.avg_gold_per_min&.round(0) || 0,
52-
avg_vision_score: stat.avg_vision_score&.round(1) || 0.0,
53-
mastery_grade: calculate_mastery_grade(win_rate, stat.avg_kda),
54-
icon_url: riot_service.champion_icon_url(stat.champion)
55-
}
56-
end
51+
stats.map { |stat| build_champion_stat_hash(stat, riot_service) }
52+
end
53+
54+
def build_champion_stat_hash(stat, riot_service)
55+
win_rate = stat.games_played.zero? ? 0 : (stat.wins.to_f / stat.games_played)
56+
{
57+
champion: stat.champion,
58+
games_played: stat.games_played,
59+
win_rate: win_rate,
60+
avg_kda: stat.avg_kda&.round(2) || 0,
61+
avg_cs_per_min: stat.avg_cs_per_min&.round(1) || 0.0,
62+
avg_damage_dealt: stat.avg_damage_dealt&.round(0) || 0,
63+
avg_damage_taken: stat.avg_damage_taken&.round(0) || 0,
64+
avg_gold_per_min: stat.avg_gold_per_min&.round(0) || 0,
65+
avg_vision_score: stat.avg_vision_score&.round(1) || 0.0,
66+
mastery_grade: calculate_mastery_grade(win_rate, stat.avg_kda),
67+
icon_url: riot_service.champion_icon_url(stat.champion)
68+
}
69+
end
5770

58-
champion_data = {
71+
def build_champion_data(player, champion_stats)
72+
{
5973
player: PlayerSerializer.render_as_hash(player),
6074
champion_stats: champion_stats,
6175
top_champions: champion_stats.take(5),

app/controllers/api/v1/images_controller.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ module V1
1414
class ImagesController < BaseController
1515
skip_before_action :authenticate_request!, only: [:proxy]
1616

17+
ALLOWED_DOMAINS = [
18+
'upload.wikimedia.org',
19+
'ddragon.leagueoflegends.com',
20+
'raw.communitydragon.org',
21+
'static.wikia.nocookie.net',
22+
'commons.wikimedia.org'
23+
].freeze
24+
25+
HTTP_TIMEOUT_OPTIONS = { open_timeout: 5, read_timeout: 10 }.freeze
26+
1727
# GET /api/v1/images/proxy
1828
# Proxies and caches external images
1929
#
@@ -33,16 +43,6 @@ def proxy
3343

3444
private
3545

36-
ALLOWED_DOMAINS = [
37-
'upload.wikimedia.org',
38-
'ddragon.leagueoflegends.com',
39-
'raw.communitydragon.org',
40-
'static.wikia.nocookie.net',
41-
'commons.wikimedia.org'
42-
].freeze
43-
44-
HTTP_TIMEOUT_OPTIONS = { open_timeout: 5, read_timeout: 10 }.freeze
45-
4646
# Validates if the URL is from an allowed domain
4747
def valid_image_url?(url)
4848
return false if url.blank?

app/controllers/api/v1/team_goals_controller.rb

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,8 @@ class TeamGoalsController < Api::V1::BaseController
66
before_action :set_team_goal, only: %i[show update destroy]
77

88
def index
9-
goals = organization_scoped(TeamGoal).includes(:player, :assigned_to, :created_by)
10-
11-
goals = goals.by_status(params[:status]) if params[:status].present?
12-
goals = goals.by_category(params[:category]) if params[:category].present?
13-
goals = goals.for_player(params[:player_id]) if params[:player_id].present?
14-
15-
goals = goals.team_goals if params[:type] == 'team'
16-
goals = goals.player_goals if params[:type] == 'player'
17-
goals = goals.active if params[:active] == 'true'
18-
goals = goals.overdue if params[:overdue] == 'true'
19-
goals = goals.expiring_soon(params[:expiring_days]&.to_i || 7) if params[:expiring_soon] == 'true'
20-
21-
goals = goals.where(assigned_to_id: params[:assigned_to_id]) if params[:assigned_to_id].present?
22-
23-
# Whitelist for sort parameters to prevent SQL injection
24-
allowed_sort_fields = %w[created_at updated_at title status category start_date end_date progress]
25-
allowed_sort_orders = %w[asc desc]
26-
27-
sort_by = allowed_sort_fields.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
28-
sort_order = allowed_sort_orders.include?(params[:sort_order]&.downcase) ? params[:sort_order].downcase : 'desc'
29-
goals = goals.order(sort_by => sort_order)
30-
9+
goals = apply_goal_filters(organization_scoped(TeamGoal).includes(:player, :assigned_to, :created_by))
10+
goals = apply_goal_sorting(goals)
3111
result = paginate(goals)
3212

3313
render_success({
@@ -128,6 +108,43 @@ def team_goal_params
128108
)
129109
end
130110

111+
def apply_goal_filters(goals)
112+
goals = apply_basic_filters(goals)
113+
goals = apply_type_filters(goals)
114+
goals = apply_status_filters(goals)
115+
goals = goals.where(assigned_to_id: params[:assigned_to_id]) if params[:assigned_to_id].present?
116+
goals
117+
end
118+
119+
def apply_basic_filters(goals)
120+
goals = goals.by_status(params[:status]) if params[:status].present?
121+
goals = goals.by_category(params[:category]) if params[:category].present?
122+
goals = goals.for_player(params[:player_id]) if params[:player_id].present?
123+
goals
124+
end
125+
126+
def apply_type_filters(goals)
127+
goals = goals.team_goals if params[:type] == 'team'
128+
goals = goals.player_goals if params[:type] == 'player'
129+
goals
130+
end
131+
132+
def apply_status_filters(goals)
133+
goals = goals.active if params[:active] == 'true'
134+
goals = goals.overdue if params[:overdue] == 'true'
135+
goals = goals.expiring_soon(params[:expiring_days]&.to_i || 7) if params[:expiring_soon] == 'true'
136+
goals
137+
end
138+
139+
def apply_goal_sorting(goals)
140+
allowed_sort_fields = %w[created_at updated_at title status category start_date end_date progress]
141+
allowed_sort_orders = %w[asc desc]
142+
143+
sort_by = allowed_sort_fields.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
144+
sort_order = allowed_sort_orders.include?(params[:sort_order]&.downcase) ? params[:sort_order].downcase : 'desc'
145+
goals.order(sort_by => sort_order)
146+
end
147+
131148
def calculate_goals_summary(goals)
132149
{
133150
total: goals.count,

app/models/audit_log.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,21 @@ def entity_display
6161
def changes_summary
6262
return 'Created' if action == 'create'
6363
return 'Deleted' if action == 'delete'
64-
6564
return 'No changes recorded' if old_values.blank? && new_values.blank?
6665

67-
changes = []
66+
changes = build_changes_list
67+
changes.empty? ? 'No changes recorded' : changes.join(', ')
68+
end
69+
70+
def build_changes_list
71+
return [] unless old_values.present? && new_values.present?
6872

69-
if old_values.present? && new_values.present?
70-
new_values.each do |key, new_val|
71-
old_val = old_values[key]
72-
next if old_val == new_val
73+
new_values.each_with_object([]) do |(key, new_val), changes|
74+
old_val = old_values[key]
75+
next if old_val == new_val
7376

74-
changes << "#{key.humanize}: #{format_value(old_val)}#{format_value(new_val)}"
75-
end
77+
changes << "#{key.humanize}: #{format_value(old_val)}#{format_value(new_val)}"
7678
end
77-
78-
changes.empty? ? 'No changes recorded' : changes.join(', ')
7979
end
8080

8181
def ip_location

0 commit comments

Comments
 (0)