Introduce architettura multi-sport con catalogo, engine scoring e overlay.
Catalogo sport in YAML, board implicito, engine per volley/basket/timed/racket/timer/generic, regia e app Android allineate. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,105 +6,10 @@ module Scoring
|
||||
@session = session
|
||||
@match = session.match
|
||||
@action = action.to_s
|
||||
@rules = Rules.from_match(@match)
|
||||
end
|
||||
|
||||
def call
|
||||
score = @session.score_state || @session.create_score_state!(
|
||||
home_sets: 0, away_sets: 0, home_points: 0, away_points: 0, current_set: 1, set_partials: []
|
||||
)
|
||||
|
||||
case @action
|
||||
when "home_point"
|
||||
apply_point(score, :home)
|
||||
when "away_point"
|
||||
apply_point(score, :away)
|
||||
when "home_undo"
|
||||
apply_undo(score, :home)
|
||||
when "away_undo"
|
||||
apply_undo(score, :away)
|
||||
when "close_set"
|
||||
apply_close_set(score)
|
||||
else
|
||||
raise ArgumentError, "Azione non valida"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def apply_point(score, side)
|
||||
if side == :home
|
||||
score.home_points += 1
|
||||
else
|
||||
score.away_points += 1
|
||||
end
|
||||
score.save!
|
||||
broadcast(score)
|
||||
winner = @rules.set_winner_from_points(
|
||||
home_points: score.home_points,
|
||||
away_points: score.away_points,
|
||||
current_set: score.current_set
|
||||
)
|
||||
Result.new(score: score, set_won: winner.present?, match_won: false, winner: winner)
|
||||
end
|
||||
|
||||
def apply_undo(score, side)
|
||||
if side == :home
|
||||
score.home_points = [score.home_points - 1, 0].max
|
||||
else
|
||||
score.away_points = [score.away_points - 1, 0].max
|
||||
end
|
||||
score.save!
|
||||
broadcast(score)
|
||||
Result.new(score: score, set_won: false, match_won: false, winner: nil)
|
||||
end
|
||||
|
||||
def apply_close_set(score)
|
||||
winner = @rules.set_winner_from_points(
|
||||
home_points: score.home_points,
|
||||
away_points: score.away_points,
|
||||
current_set: score.current_set
|
||||
)
|
||||
winner ||= score.home_points > score.away_points ? :home : :away if score.home_points != score.away_points
|
||||
return Result.new(score: score, set_won: false, match_won: false, winner: nil) unless winner
|
||||
|
||||
close_set_for(score, winner)
|
||||
end
|
||||
|
||||
def close_set_for(score, winner)
|
||||
partials = Array(score.set_partials)
|
||||
partials << {
|
||||
"set" => score.current_set,
|
||||
"home" => score.home_points,
|
||||
"away" => score.away_points
|
||||
}
|
||||
|
||||
if winner == :home
|
||||
score.home_sets += 1
|
||||
else
|
||||
score.away_sets += 1
|
||||
end
|
||||
|
||||
match_winner = @rules.match_winner(home_sets: score.home_sets, away_sets: score.away_sets)
|
||||
|
||||
score.assign_attributes(
|
||||
set_partials: partials,
|
||||
home_points: 0,
|
||||
away_points: 0,
|
||||
current_set: score.current_set + 1
|
||||
)
|
||||
score.save!
|
||||
broadcast(score)
|
||||
Result.new(
|
||||
score: score,
|
||||
set_won: true,
|
||||
match_won: match_winner.present?,
|
||||
winner: match_winner || winner
|
||||
)
|
||||
end
|
||||
|
||||
def broadcast(score)
|
||||
SessionChannel.broadcast_message(@session, score.as_cable_payload)
|
||||
Engine.for(@match).apply(session: @session, action: @action)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
26
backend/app/services/scoring/engine.rb
Normal file
26
backend/app/services/scoring/engine.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
module Scoring
|
||||
class Engine
|
||||
ENGINES = {
|
||||
"volley" => Engines::VolleyEngine,
|
||||
"racket" => Engines::RacketEngine,
|
||||
"generic" => Engines::GenericEngine,
|
||||
"basket" => Engines::BasketEngine,
|
||||
"timed" => Engines::TimedEngine,
|
||||
"timer" => Engines::TimerEngine
|
||||
}.freeze
|
||||
|
||||
def self.for(match)
|
||||
board = match.effective_board_type
|
||||
klass = ENGINES.fetch(board) { Engines::GenericEngine }
|
||||
klass.new(match: match)
|
||||
end
|
||||
|
||||
def self.for_session(session)
|
||||
for(session.match)
|
||||
end
|
||||
|
||||
def self.ensure_score_for(session)
|
||||
for(session.match).ensure_score(session)
|
||||
end
|
||||
end
|
||||
end
|
||||
45
backend/app/services/scoring/engines/base_engine.rb
Normal file
45
backend/app/services/scoring/engines/base_engine.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
module Scoring
|
||||
module Engines
|
||||
class BaseEngine
|
||||
Result = Scoring::ApplyAction::Result
|
||||
|
||||
def initialize(match:)
|
||||
@match = match
|
||||
end
|
||||
|
||||
def apply(session:, action:)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def sync(session:, payload:)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def ensure_score(session)
|
||||
board = @match.effective_board_type
|
||||
score = session.score_state
|
||||
return score if score
|
||||
|
||||
attrs = {
|
||||
board_type: board,
|
||||
home_sets: 0,
|
||||
away_sets: 0,
|
||||
home_points: 0,
|
||||
away_points: 0,
|
||||
current_set: 1,
|
||||
set_partials: [],
|
||||
data: default_data
|
||||
}
|
||||
session.create_score_state!(attrs)
|
||||
end
|
||||
|
||||
def default_data
|
||||
{}
|
||||
end
|
||||
|
||||
def broadcast(session, score)
|
||||
SessionChannel.broadcast_message(session, score.as_cable_payload)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
32
backend/app/services/scoring/engines/basket_engine.rb
Normal file
32
backend/app/services/scoring/engines/basket_engine.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
module Scoring
|
||||
module Engines
|
||||
class BasketEngine < BaseEngine
|
||||
include PeriodEngineMixin
|
||||
|
||||
def apply(session:, action:)
|
||||
score = ensure_score(session)
|
||||
|
||||
case action.to_s
|
||||
when "home_point" then adjust_score(score, :home, 1)
|
||||
when "away_point" then adjust_score(score, :away, 1)
|
||||
when "home_undo" then adjust_score(score, :home, -1)
|
||||
when "away_undo" then adjust_score(score, :away, -1)
|
||||
when "home_point_2" then adjust_score(score, :home, 2)
|
||||
when "away_point_2" then adjust_score(score, :away, 2)
|
||||
when "home_point_3" then adjust_score(score, :home, 3)
|
||||
when "away_point_3" then adjust_score(score, :away, 3)
|
||||
when "advance_period" then advance_period(score)
|
||||
when "clock_toggle" then toggle_clock(score)
|
||||
when "clock_reset" then reset_clock(score)
|
||||
when "clock_tick" then tick_clock(score)
|
||||
else
|
||||
raise ArgumentError, "Azione non valida"
|
||||
end
|
||||
end
|
||||
|
||||
def sync(session:, payload:)
|
||||
sync_period(session: session, payload: payload.stringify_keys)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
42
backend/app/services/scoring/engines/generic_engine.rb
Normal file
42
backend/app/services/scoring/engines/generic_engine.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
module Scoring
|
||||
module Engines
|
||||
class GenericEngine < BaseEngine
|
||||
def apply(session:, action:)
|
||||
score = ensure_score(session)
|
||||
|
||||
case action.to_s
|
||||
when "home_point" then adjust(score, :home, 1)
|
||||
when "away_point" then adjust(score, :away, 1)
|
||||
when "home_undo" then adjust(score, :home, -1)
|
||||
when "away_undo" then adjust(score, :away, -1)
|
||||
else
|
||||
raise ArgumentError, "Azione non valida"
|
||||
end
|
||||
end
|
||||
|
||||
def sync(session:, payload:)
|
||||
score = ensure_score(session)
|
||||
attrs = {}
|
||||
%w[home_points away_points].each do |key|
|
||||
attrs[key] = payload[key] if payload.key?(key)
|
||||
end
|
||||
score.update!(attrs)
|
||||
broadcast(session, score)
|
||||
score
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def adjust(score, side, delta)
|
||||
if side == :home
|
||||
score.home_points = [score.home_points + delta, 0].max
|
||||
else
|
||||
score.away_points = [score.away_points + delta, 0].max
|
||||
end
|
||||
score.save!
|
||||
broadcast(score.stream_session, score)
|
||||
Result.new(score: score, set_won: false, match_won: false, winner: nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
117
backend/app/services/scoring/engines/period_engine_mixin.rb
Normal file
117
backend/app/services/scoring/engines/period_engine_mixin.rb
Normal file
@@ -0,0 +1,117 @@
|
||||
module Scoring
|
||||
module Engines
|
||||
module PeriodEngineMixin
|
||||
private
|
||||
|
||||
def period_rules
|
||||
@period_rules ||= Rules.from_match(@match)
|
||||
end
|
||||
|
||||
def period_data(score)
|
||||
score.data.is_a?(Hash) ? score.data.deep_dup : {}
|
||||
end
|
||||
|
||||
def default_data
|
||||
rules = period_rules
|
||||
{
|
||||
"period" => 1,
|
||||
"clock_secs" => rules.period_duration_secs,
|
||||
"clock_running" => false,
|
||||
"home_score" => 0,
|
||||
"away_score" => 0,
|
||||
"overtime" => false
|
||||
}
|
||||
end
|
||||
|
||||
def ensure_period_data(score)
|
||||
data = period_data(score)
|
||||
default_data.each { |k, v| data[k] = v unless data.key?(k) }
|
||||
data
|
||||
end
|
||||
|
||||
def save_period_score!(score, data)
|
||||
score.assign_attributes(
|
||||
data: data,
|
||||
home_points: data["home_score"].to_i,
|
||||
away_points: data["away_score"].to_i,
|
||||
period: period_label(data)
|
||||
)
|
||||
score.save!
|
||||
broadcast(score.stream_session, score)
|
||||
end
|
||||
|
||||
def period_label(data)
|
||||
period = data["period"].to_i
|
||||
return "OT" if data["overtime"]
|
||||
|
||||
board = @match.effective_board_type
|
||||
board == "basket" ? "Q#{period}" : "#{period}° tempo"
|
||||
end
|
||||
|
||||
def adjust_score(score, side, delta)
|
||||
data = ensure_period_data(score)
|
||||
key = side == :home ? "home_score" : "away_score"
|
||||
data[key] = [data[key].to_i + delta, 0].max
|
||||
save_period_score!(score, data)
|
||||
Result.new(score: score, set_won: false, match_won: false, winner: nil)
|
||||
end
|
||||
|
||||
def advance_period(score)
|
||||
data = ensure_period_data(score)
|
||||
rules = period_rules
|
||||
max_periods = rules.periods
|
||||
|
||||
if data["overtime"]
|
||||
data["period"] = data["period"].to_i + 1
|
||||
data["clock_secs"] = rules.overtime_duration_secs
|
||||
elsif data["period"].to_i >= max_periods
|
||||
data["overtime"] = true
|
||||
data["period"] = max_periods + 1
|
||||
data["clock_secs"] = rules.overtime_duration_secs
|
||||
else
|
||||
data["period"] = data["period"].to_i + 1
|
||||
data["clock_secs"] = rules.period_duration_secs
|
||||
end
|
||||
data["clock_running"] = false
|
||||
save_period_score!(score, data)
|
||||
Result.new(score: score, set_won: false, match_won: false, winner: nil)
|
||||
end
|
||||
|
||||
def toggle_clock(score)
|
||||
data = ensure_period_data(score)
|
||||
data["clock_running"] = !data["clock_running"]
|
||||
save_period_score!(score, data)
|
||||
Result.new(score: score, set_won: false, match_won: false, winner: nil)
|
||||
end
|
||||
|
||||
def reset_clock(score)
|
||||
data = ensure_period_data(score)
|
||||
rules = period_rules
|
||||
duration = data["overtime"] ? rules.overtime_duration_secs : rules.period_duration_secs
|
||||
data["clock_secs"] = duration
|
||||
data["clock_running"] = false
|
||||
save_period_score!(score, data)
|
||||
Result.new(score: score, set_won: false, match_won: false, winner: nil)
|
||||
end
|
||||
|
||||
def tick_clock(score, secs: 1)
|
||||
data = ensure_period_data(score)
|
||||
return Result.new(score: score, set_won: false, match_won: false, winner: nil) unless data["clock_running"]
|
||||
|
||||
data["clock_secs"] = [data["clock_secs"].to_i - secs, 0].max
|
||||
save_period_score!(score, data)
|
||||
Result.new(score: score, set_won: false, match_won: false, winner: nil)
|
||||
end
|
||||
|
||||
def sync_period(session:, payload:)
|
||||
score = ensure_score(session)
|
||||
data = ensure_period_data(score)
|
||||
%w[period clock_secs clock_running home_score away_score overtime].each do |key|
|
||||
data[key] = payload[key] if payload.key?(key)
|
||||
end
|
||||
save_period_score!(score, data)
|
||||
score
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
7
backend/app/services/scoring/engines/racket_engine.rb
Normal file
7
backend/app/services/scoring/engines/racket_engine.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
module Scoring
|
||||
module Engines
|
||||
# Fase 1: stessa logica del volley (set + punti).
|
||||
class RacketEngine < VolleyEngine
|
||||
end
|
||||
end
|
||||
end
|
||||
28
backend/app/services/scoring/engines/timed_engine.rb
Normal file
28
backend/app/services/scoring/engines/timed_engine.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
module Scoring
|
||||
module Engines
|
||||
class TimedEngine < BaseEngine
|
||||
include PeriodEngineMixin
|
||||
|
||||
def apply(session:, action:)
|
||||
score = ensure_score(session)
|
||||
|
||||
case action.to_s
|
||||
when "home_point" then adjust_score(score, :home, 1)
|
||||
when "away_point" then adjust_score(score, :away, 1)
|
||||
when "home_undo" then adjust_score(score, :home, -1)
|
||||
when "away_undo" then adjust_score(score, :away, -1)
|
||||
when "advance_period" then advance_period(score)
|
||||
when "clock_toggle" then toggle_clock(score)
|
||||
when "clock_reset" then reset_clock(score)
|
||||
when "clock_tick" then tick_clock(score)
|
||||
else
|
||||
raise ArgumentError, "Azione non valida"
|
||||
end
|
||||
end
|
||||
|
||||
def sync(session:, payload:)
|
||||
sync_period(session: session, payload: payload.stringify_keys)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
49
backend/app/services/scoring/engines/timer_engine.rb
Normal file
49
backend/app/services/scoring/engines/timer_engine.rb
Normal file
@@ -0,0 +1,49 @@
|
||||
module Scoring
|
||||
module Engines
|
||||
class TimerEngine < BaseEngine
|
||||
def apply(session:, action:)
|
||||
score = ensure_score(session)
|
||||
data = timer_data(score)
|
||||
|
||||
case action.to_s
|
||||
when "clock_toggle"
|
||||
data["clock_running"] = !data["clock_running"]
|
||||
when "clock_reset"
|
||||
data["clock_secs"] = 0
|
||||
data["clock_running"] = false
|
||||
when "clock_tick"
|
||||
data["clock_secs"] = data["clock_secs"].to_i + 1 if data["clock_running"]
|
||||
else
|
||||
raise ArgumentError, "Azione non valida"
|
||||
end
|
||||
|
||||
score.update!(data: data)
|
||||
broadcast(session, score)
|
||||
Result.new(score: score, set_won: false, match_won: false, winner: nil)
|
||||
end
|
||||
|
||||
def sync(session:, payload:)
|
||||
score = ensure_score(session)
|
||||
data = timer_data(score)
|
||||
%w[clock_secs clock_running].each do |key|
|
||||
data[key] = payload[key] if payload.key?(key)
|
||||
end
|
||||
score.update!(data: data)
|
||||
broadcast(session, score)
|
||||
score
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def default_data
|
||||
{ "clock_secs" => 0, "clock_running" => false }
|
||||
end
|
||||
|
||||
def timer_data(score)
|
||||
base = default_data
|
||||
stored = score.data.is_a?(Hash) ? score.data : {}
|
||||
base.merge(stored)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
105
backend/app/services/scoring/engines/volley_engine.rb
Normal file
105
backend/app/services/scoring/engines/volley_engine.rb
Normal file
@@ -0,0 +1,105 @@
|
||||
module Scoring
|
||||
module Engines
|
||||
class VolleyEngine < BaseEngine
|
||||
def apply(session:, action:)
|
||||
@rules = Rules.from_match(@match)
|
||||
score = ensure_score(session)
|
||||
|
||||
case action.to_s
|
||||
when "home_point" then apply_point(score, :home)
|
||||
when "away_point" then apply_point(score, :away)
|
||||
when "home_undo" then apply_undo(score, :home)
|
||||
when "away_undo" then apply_undo(score, :away)
|
||||
when "close_set" then apply_close_set(score)
|
||||
else
|
||||
raise ArgumentError, "Azione non valida"
|
||||
end
|
||||
end
|
||||
|
||||
def sync(session:, payload:)
|
||||
score = ensure_score(session)
|
||||
attrs = {}
|
||||
%w[home_sets away_sets home_points away_points current_set].each do |key|
|
||||
attrs[key] = payload[key] if payload.key?(key)
|
||||
end
|
||||
attrs[:set_partials] = payload["set_partials"] if payload.key?("set_partials")
|
||||
score.update!(attrs)
|
||||
broadcast(session, score)
|
||||
score
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def apply_point(score, side)
|
||||
if side == :home
|
||||
score.home_points += 1
|
||||
else
|
||||
score.away_points += 1
|
||||
end
|
||||
score.save!
|
||||
broadcast(score.stream_session, score)
|
||||
winner = @rules.set_winner_from_points(
|
||||
home_points: score.home_points,
|
||||
away_points: score.away_points,
|
||||
current_set: score.current_set
|
||||
)
|
||||
Result.new(score: score, set_won: winner.present?, match_won: false, winner: winner)
|
||||
end
|
||||
|
||||
def apply_undo(score, side)
|
||||
if side == :home
|
||||
score.home_points = [score.home_points - 1, 0].max
|
||||
else
|
||||
score.away_points = [score.away_points - 1, 0].max
|
||||
end
|
||||
score.save!
|
||||
broadcast(score.stream_session, score)
|
||||
Result.new(score: score, set_won: false, match_won: false, winner: nil)
|
||||
end
|
||||
|
||||
def apply_close_set(score)
|
||||
winner = @rules.set_winner_from_points(
|
||||
home_points: score.home_points,
|
||||
away_points: score.away_points,
|
||||
current_set: score.current_set
|
||||
)
|
||||
winner ||= score.home_points > score.away_points ? :home : :away if score.home_points != score.away_points
|
||||
return Result.new(score: score, set_won: false, match_won: false, winner: nil) unless winner
|
||||
|
||||
close_set_for(score, winner)
|
||||
end
|
||||
|
||||
def close_set_for(score, winner)
|
||||
partials = Array(score.set_partials)
|
||||
partials << {
|
||||
"set" => score.current_set,
|
||||
"home" => score.home_points,
|
||||
"away" => score.away_points
|
||||
}
|
||||
|
||||
if winner == :home
|
||||
score.home_sets += 1
|
||||
else
|
||||
score.away_sets += 1
|
||||
end
|
||||
|
||||
match_winner = @rules.match_winner(home_sets: score.home_sets, away_sets: score.away_sets)
|
||||
|
||||
score.assign_attributes(
|
||||
set_partials: partials,
|
||||
home_points: 0,
|
||||
away_points: 0,
|
||||
current_set: score.current_set + 1
|
||||
)
|
||||
score.save!
|
||||
broadcast(score.stream_session, score)
|
||||
Result.new(
|
||||
score: score,
|
||||
set_won: true,
|
||||
match_won: match_winner.present?,
|
||||
winner: match_winner || winner
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,13 +3,31 @@ module Scoring
|
||||
Side = Struct.new(:home, :away, keyword_init: true)
|
||||
|
||||
def self.from_match(match)
|
||||
overrides = match.scoring_rules.is_a?(Hash) ? match.scoring_rules : {}
|
||||
new(
|
||||
sets_to_win: match.sets_to_win,
|
||||
points_per_set: overrides["points_per_set"] || overrides[:points_per_set] || 25,
|
||||
points_deciding_set: overrides["points_deciding_set"] || overrides[:points_deciding_set] || 15,
|
||||
min_point_lead: overrides["min_point_lead"] || overrides[:min_point_lead] || 2
|
||||
)
|
||||
rules = match.effective_scoring_rules
|
||||
board = match.effective_board_type
|
||||
|
||||
case board
|
||||
when "volley", "racket"
|
||||
new(
|
||||
sets_to_win: rules["sets_to_win"].to_i,
|
||||
points_per_set: rules["points_per_set"].to_i,
|
||||
points_deciding_set: rules["points_deciding_set"].to_i,
|
||||
min_point_lead: rules["min_point_lead"].to_i
|
||||
)
|
||||
when "basket", "timed"
|
||||
PeriodRules.new(
|
||||
periods: rules["periods"].to_i,
|
||||
period_duration_secs: rules["period_duration_secs"].to_i,
|
||||
overtime_duration_secs: rules["overtime_duration_secs"].to_i
|
||||
)
|
||||
else
|
||||
new(
|
||||
sets_to_win: rules["sets_to_win"].to_i.positive? ? rules["sets_to_win"].to_i : 3,
|
||||
points_per_set: rules["points_per_set"].to_i.positive? ? rules["points_per_set"].to_i : 25,
|
||||
points_deciding_set: rules["points_deciding_set"].to_i.positive? ? rules["points_deciding_set"].to_i : 15,
|
||||
min_point_lead: rules["min_point_lead"].to_i.positive? ? rules["min_point_lead"].to_i : 2
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(sets_to_win:, points_per_set: 25, points_deciding_set: 15, min_point_lead: 2)
|
||||
@@ -42,5 +60,15 @@ module Scoring
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
class PeriodRules
|
||||
attr_reader :periods, :period_duration_secs, :overtime_duration_secs
|
||||
|
||||
def initialize(periods:, period_duration_secs:, overtime_duration_secs: 300)
|
||||
@periods = periods
|
||||
@period_duration_secs = period_duration_secs
|
||||
@overtime_duration_secs = overtime_duration_secs
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,27 +1,13 @@
|
||||
module Scoring
|
||||
# Sincronizza lo stato completo del tabellone (app mobile / Action Cable).
|
||||
class SyncState
|
||||
SCORE_KEYS = %w[home_sets away_sets home_points away_points current_set].freeze
|
||||
|
||||
def initialize(session:, payload:)
|
||||
@session = session
|
||||
@payload = payload.stringify_keys
|
||||
end
|
||||
|
||||
def call
|
||||
score = @session.score_state || @session.create_score_state!(
|
||||
home_sets: 0, away_sets: 0, home_points: 0, away_points: 0, current_set: 1, set_partials: []
|
||||
)
|
||||
|
||||
attrs = {}
|
||||
SCORE_KEYS.each do |key|
|
||||
attrs[key] = @payload[key] if @payload.key?(key)
|
||||
end
|
||||
attrs[:set_partials] = @payload["set_partials"] if @payload.key?("set_partials")
|
||||
|
||||
score.update!(attrs)
|
||||
SessionChannel.broadcast_message(@session, score.as_cable_payload)
|
||||
score
|
||||
Engine.for(@session.match).sync(session: @session, payload: @payload)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user