Skip to content

Commit 0b12874

Browse files
authored
Merge pull request #196 from plural/advanced-deck-validation
Advanced deck validation
2 parents bd1b174 + 65d2a95 commit 0b12874

20 files changed

+1827
-111
lines changed
Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,47 @@
11
module API
2-
module V3
3-
module Public
4-
class Api::V3::Public::ValidateDeckController < ::ApplicationController
5-
def index
6-
out = params[:data]
2+
module V3
3+
module Public
4+
class Api::V3::Public::ValidateDeckController < ::ApplicationController
5+
def index
6+
out = params[:data]
77

8-
# Check for presence of everything needed to perform deck validation and error if not present.
9-
if out.nil? or not (out.has_key?(:attributes) and out[:attributes].has_key?(:identity_card_id) and out[:attributes].has_key?(:side_id) and out[:attributes].has_key?(:cards))
10-
return render json: {
11-
:errors => [{
12-
:title => "Invalid request",
13-
:detail => "Valid requests must be of the form `{'data': { 'attributes': { 'identity_card_id': 'foo', 'side_id': 'bar', 'cards': { } } }
14-
}`. Extra fields are allowed.",
15-
:code => "400",
16-
:status => "400"
17-
}]}, :status => :bad_request
18-
end
8+
# Check for presence of everything needed to perform deck validation and error if not present.
9+
if out.nil? or not (
10+
out.has_key?(:attributes) and
11+
out[:attributes].has_key?(:identity_card_id) and
12+
out[:attributes].has_key?(:side_id) and
13+
out[:attributes].has_key?(:cards) and
14+
out[:attributes].has_key?(:validations))
15+
return render json: {
16+
:errors => [{
17+
:title => "Invalid request",
18+
:detail => "Valid requests must be of the form `{'data': { 'attributes': { 'identity_card_id': 'foo', 'side_id': 'bar', 'cards': { }, 'validations': [] } }}`. Extra fields are allowed.",
19+
:code => "400",
20+
:status => "400"
21+
}]}, :status => :bad_request
22+
end
23+
24+
# Deck validation takes in a simple datastructure, so construct it instead of passing around ActionController::Parameters
25+
deck = {
26+
'identity_card_id' => params[:data][:attributes][:identity_card_id],
27+
'side_id' => params[:data][:attributes][:side_id],
28+
'cards' => {},
29+
'validations' => [],
30+
}
31+
params[:data][:attributes][:cards].each {|c,q| deck['cards'][c] = q}
32+
params[:data][:attributes][:validations].each do |v|
33+
deck['validations'] << v
34+
end
1935

20-
# Deck validation works off of a simple datastructure, so construct it instead of passing around ActionController::Parameters
21-
deck = {
22-
'identity_card_id' => params[:data][:attributes][:identity_card_id],
23-
'side_id' => params[:data][:attributes][:side_id],
24-
'cards' => {}
25-
}
26-
params[:data][:attributes][:cards].each {|c,q| deck['cards'][c] = q}
36+
v = DeckValidator.new(deck)
2737

28-
v = DeckValidator.new(deck)
38+
out[:attributes][:is_valid] = v.is_valid?
39+
out[:attributes][:validation_errors] = v.errors
40+
out[:attributes][:validations] = v.validations
2941

30-
out[:attributes][:is_valid] = v.is_valid?
31-
out[:attributes][:validation_errors] = v.errors
32-
render json: { data: out }, :status => :ok
33-
end
42+
render json: { data: out }, :status => :ok
3443
end
3544
end
3645
end
3746
end
47+
end

app/models/format.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@ class Format < ApplicationRecord
66
has_many :card_pools, :through => :snapshots
77
has_many :restrictions, :through => :snapshots
88

9+
has_one :snapshot, primary_key: :active_snapshot_id, foreign_key: :id
10+
911
validates :name, uniqueness: true
1012
end

app/models/unified_restriction.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,8 @@ def readonly?
88
belongs_to :card_pool
99
belongs_to :restriction
1010
belongs_to :card
11+
12+
scope :cards_restricted_by, ->(restriction_id) { where(
13+
'restriction_id = ? AND (in_restriction OR is_banned OR is_restricted OR eternal_points > 0 ' +
14+
'OR has_global_penalty OR universal_faction_cost > 0)', restriction_id) }
1115
end

db/seeds.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,8 @@
55
# data for all the tests and the API doc generation.
66
if Rails.env == 'test'
77
Rake::Task["db:fixtures:load"].invoke
8+
9+
Scenic.database.refresh_materialized_view(:unified_restrictions, concurrently: false, cascade: false)
10+
Scenic.database.refresh_materialized_view(:unified_cards, concurrently: false, cascade: false)
11+
Scenic.database.refresh_materialized_view(:unified_printings, concurrently: false, cascade: false)
812
end

lib/deck_validation.rb

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# A class to hold specifications and results from validations.
2+
class DeckValidation
3+
attr_reader :basic_deckbuilding_rules
4+
attr_reader :label
5+
attr_reader :errors
6+
attr_reader :format_id
7+
attr_reader :restriction_id
8+
attr_reader :card_pool_id
9+
attr_reader :snapshot_id
10+
11+
def initialize(validation_hash)
12+
@label = nil
13+
if validation_hash.has_key?('label')
14+
@label = validation_hash['label']
15+
end
16+
@basic_deckbuilding_rules = false
17+
if validation_hash.has_key?('basic_deckbuilding_rules')
18+
@basic_deckbuilding_rules = validation_hash['basic_deckbuilding_rules']
19+
end
20+
@format_id = nil
21+
if validation_hash.has_key?('format_id')
22+
@format_id = validation_hash['format_id']
23+
end
24+
@restriction_id = nil
25+
if validation_hash.has_key?('restriction_id')
26+
@restriction_id = validation_hash['restriction_id']
27+
end
28+
@card_pool_id = nil
29+
if validation_hash.has_key?('card_pool_id')
30+
@card_pool_id = validation_hash['card_pool_id']
31+
end
32+
@snapshot_id = nil
33+
if validation_hash.has_key?('snapshot_id')
34+
@snapshot_id = validation_hash['snapshot_id']
35+
end
36+
37+
expand_implied_ids
38+
39+
@errors = []
40+
end
41+
42+
def expand_implied_ids
43+
if !@snapshot_id.nil? and (@format_id.nil? or @card_pool_id.nil? or @restriction_id.nil?)
44+
if Snapshot.exists?(@snapshot_id)
45+
snapshot = Snapshot.find(@snapshot_id)
46+
if @format_id.nil?
47+
@format_id = snapshot.format_id
48+
end
49+
if @card_pool_id.nil?
50+
@card_pool_id = snapshot.card_pool_id
51+
end
52+
if @restriction_id.nil?
53+
@restriction_id = snapshot.restriction_id
54+
end
55+
end
56+
elsif !@format_id.nil? and (@snapshot_id.nil? or @card_pool_id.nil? or @restriction_id.nil?)
57+
if Format.exists?(@format_id)
58+
format = Format.find(@format_id)
59+
if @snapshot_id.nil?
60+
@snapshot_id = format.active_snapshot_id
61+
end
62+
active_snapshot = format.snapshot
63+
if !active_snapshot.nil?
64+
if @card_pool_id.nil?
65+
@card_pool_id = active_snapshot.card_pool_id
66+
end
67+
if @restriction_id.nil?
68+
@restriction_id = active_snapshot.restriction_id
69+
end
70+
end
71+
end
72+
elsif !@card_pool_id.nil? and @format_id.nil?
73+
if CardPool.exists?(@card_pool_id)
74+
card_pool = CardPool.find(@card_pool_id)
75+
@format_id = card_pool.format_id
76+
end
77+
elsif !@restriction_id.nil? and @format_id.nil?
78+
if Restriction.exists?(@restriction_id)
79+
restriction = Restriction.find(@restriction_id)
80+
@format_id = restriction.format_id
81+
end
82+
end
83+
end
84+
85+
def add_error(e)
86+
@errors << e
87+
end
88+
89+
def is_valid?
90+
return @errors.size == 0
91+
end
92+
end

0 commit comments

Comments
 (0)