frecon 0.1.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 +7 -0
- data/bin/frecon +13 -0
- data/lib/frecon/base/object.rb +3 -0
- data/lib/frecon/base/variables.rb +4 -0
- data/lib/frecon/base.rb +2 -0
- data/lib/frecon/console.rb +28 -0
- data/lib/frecon/controller.rb +129 -0
- data/lib/frecon/controllers/competitions_controller.rb +18 -0
- data/lib/frecon/controllers/dump_controller.rb +20 -0
- data/lib/frecon/controllers/matches_controller.rb +14 -0
- data/lib/frecon/controllers/participations_controller.rb +28 -0
- data/lib/frecon/controllers/records_controller.rb +59 -0
- data/lib/frecon/controllers/robots_controller.rb +14 -0
- data/lib/frecon/controllers/teams_controller.rb +112 -0
- data/lib/frecon/controllers.rb +13 -0
- data/lib/frecon/database.rb +18 -0
- data/lib/frecon/error_formatter.rb +14 -0
- data/lib/frecon/match_number.rb +194 -0
- data/lib/frecon/model.rb +13 -0
- data/lib/frecon/models/competition.rb +26 -0
- data/lib/frecon/models/match.rb +16 -0
- data/lib/frecon/models/participation.rb +11 -0
- data/lib/frecon/models/record.rb +18 -0
- data/lib/frecon/models/robot.rb +10 -0
- data/lib/frecon/models/team.rb +44 -0
- data/lib/frecon/models.rb +6 -0
- data/lib/frecon/mongoid.yml +19 -0
- data/lib/frecon/position.rb +117 -0
- data/lib/frecon/routes.rb +119 -0
- data/lib/frecon/server.rb +23 -0
- data/lib/frecon.rb +5 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f815f2ffe36eae99a451ce057c3f2bc41a355783
|
4
|
+
data.tar.gz: 0b415f99c4fdc44bc23236bd259cddec01bd505b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 86b2d39602ae88bce2c4c10312ac8f9f91f29170b8774104e954d7300bb6e56cecf97ee3d72ec6c5c7c656b7c9c100227efa72cf5df74849a1f944b176c13fb7
|
7
|
+
data.tar.gz: 0ce13da7ae08cffc748ad6e4ca3baa30e8a0606dfc98b5bc4131b7f59a2a24edc730540a028a2797ae780aed853d03fa87e9b108878465f1eac37a63611b7a89
|
data/bin/frecon
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# Allow running locally.
|
4
|
+
lib_directory = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
|
5
|
+
$LOAD_PATH.unshift(lib_directory) unless $LOAD_PATH.include?(lib_directory)
|
6
|
+
|
7
|
+
require "frecon"
|
8
|
+
|
9
|
+
unless ARGV.include?("c") || ARGV.include?("console")
|
10
|
+
FReCon::Server.start
|
11
|
+
else
|
12
|
+
FReCon::Console.start
|
13
|
+
end
|
data/lib/frecon/base.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "frecon/database"
|
2
|
+
require "frecon/server"
|
3
|
+
|
4
|
+
module FReCon
|
5
|
+
class Console
|
6
|
+
def self.start
|
7
|
+
Database.setup
|
8
|
+
|
9
|
+
# Use pry if it is installed.
|
10
|
+
# Use the context of the FReCon module;
|
11
|
+
# this allows for writing "Team" instead of "FReCon::Team".
|
12
|
+
begin
|
13
|
+
require "pry"
|
14
|
+
|
15
|
+
FReCon.pry
|
16
|
+
rescue LoadError
|
17
|
+
require "irb"
|
18
|
+
|
19
|
+
IRB.setup nil
|
20
|
+
IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
|
21
|
+
|
22
|
+
require "irb/ext/multi-irb"
|
23
|
+
|
24
|
+
IRB.irb nil, FReCon
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require "frecon/base"
|
2
|
+
|
3
|
+
module FReCon
|
4
|
+
class Controller
|
5
|
+
def self.model_name
|
6
|
+
# Removes the namespace "FReCon::" and "Controller" from
|
7
|
+
# the class name, then singularizes the result.
|
8
|
+
self.name.gsub(/FReCon::|Controller\Z/, "").singularize
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.model
|
12
|
+
# Removes the trailing "Controller" from the class name,
|
13
|
+
# singularizes the result, and turns it into the class.
|
14
|
+
self.name.gsub(/Controller\Z/, "").singularize.constantize
|
15
|
+
end
|
16
|
+
|
17
|
+
# The 404 error message.
|
18
|
+
def self.could_not_find(value, attribute = "id", model = model_name.downcase)
|
19
|
+
"Could not find #{model_name.downcase} of #{attribute} #{value}!"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Processes a POST/PUT request and returns the post data.
|
23
|
+
def self.process_request(request)
|
24
|
+
# Rewind the request body (an IO object)
|
25
|
+
# in case someone else has already played
|
26
|
+
# through it.
|
27
|
+
request.body.rewind
|
28
|
+
|
29
|
+
begin
|
30
|
+
# Parse the POST data as a JSON hash
|
31
|
+
# (because that's what it is)
|
32
|
+
post_data = JSON.parse(request.body.read)
|
33
|
+
rescue JSON::ParserError => e
|
34
|
+
# If we have malformed JSON (JSON::ParserError is
|
35
|
+
# raised), escape out of the function.
|
36
|
+
return [400, ErrorFormatter.format(e.message)]
|
37
|
+
end
|
38
|
+
|
39
|
+
post_data.is_an?(Array) ? [422, ErrorFormatter.format("Must pass a JSON object!")] : post_data
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.create(request, params)
|
43
|
+
post_data = process_request request
|
44
|
+
return post_data if post_data.is_an?(Array)
|
45
|
+
|
46
|
+
@model = model.new
|
47
|
+
@model.attributes = post_data
|
48
|
+
|
49
|
+
if @model.save
|
50
|
+
[201, @model.to_json]
|
51
|
+
else
|
52
|
+
[422, ErrorFormatter.format(@model.errors.full_messages)]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.update(request, params)
|
57
|
+
return [400, "Must supply a #{model_name.downcase}!"] unless params[:id]
|
58
|
+
|
59
|
+
post_data = process_request request
|
60
|
+
return post_data if post_data.is_an?(Array)
|
61
|
+
|
62
|
+
@model = model.find params[:id]
|
63
|
+
|
64
|
+
return [404, ErrorFormatter.format(could_not_find(params[:id]))] unless @model
|
65
|
+
|
66
|
+
if @model.update_attributes(post_data)
|
67
|
+
@model.to_json
|
68
|
+
else
|
69
|
+
[422, ErrorFormatter.format(@model.errors.full_messages)]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.delete(params)
|
74
|
+
@model = model.find params[:id]
|
75
|
+
|
76
|
+
if @model
|
77
|
+
if @model.destroy
|
78
|
+
204
|
79
|
+
else
|
80
|
+
[422, ErrorFormatter.format(@model.errors.full_messages)]
|
81
|
+
end
|
82
|
+
else
|
83
|
+
[404, ErrorFormatter.format(could_not_find(params[:id]))]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.show(params)
|
88
|
+
@model = model.find params[:id]
|
89
|
+
|
90
|
+
if @model
|
91
|
+
@model.to_json
|
92
|
+
else
|
93
|
+
[404, ErrorFormatter.format(could_not_find(params[:id]))]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.index(params)
|
98
|
+
params.delete("_")
|
99
|
+
|
100
|
+
@models = params.empty? ? model.all : model.where(params)
|
101
|
+
|
102
|
+
@models.to_json
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.show_attribute(params, attribute)
|
106
|
+
@model = model.find params[:id]
|
107
|
+
|
108
|
+
if @model
|
109
|
+
@model.send(attribute).to_json
|
110
|
+
else
|
111
|
+
[404, ErrorFormatter.format(could_not_find(params[:id]))]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.team_number_to_team_id(post_data)
|
116
|
+
if post_data["team_number"] && !post_data["team_id"]
|
117
|
+
unless (team = Team.number post_data["team_number"]).nil?
|
118
|
+
post_data["team_id"] = team.id
|
119
|
+
|
120
|
+
post_data.delete("team_number")
|
121
|
+
|
122
|
+
post_data
|
123
|
+
else
|
124
|
+
return [404, ErrorFormatter.format(could_not_find(post_data["team_number"], "number", "team"))]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "json"
|
2
|
+
require "frecon/models"
|
3
|
+
|
4
|
+
module FReCon
|
5
|
+
class CompetitionsController < Controller
|
6
|
+
def self.teams(params)
|
7
|
+
show_attribute params, :teams
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.matches(params)
|
11
|
+
show_attribute params, :matches
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.records(params)
|
15
|
+
show_attribute params, :records
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "json"
|
2
|
+
require "frecon/models"
|
3
|
+
|
4
|
+
module FReCon
|
5
|
+
class DumpController
|
6
|
+
def self.full(params)
|
7
|
+
competitions = Competition.all
|
8
|
+
teams = Team.all
|
9
|
+
matches = Match.all
|
10
|
+
records = Record.all
|
11
|
+
|
12
|
+
{
|
13
|
+
"competitions" => competitions,
|
14
|
+
"teams" => teams,
|
15
|
+
"matches" => matches,
|
16
|
+
"records" => records
|
17
|
+
}.to_json
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "json"
|
2
|
+
require "frecon/models"
|
3
|
+
|
4
|
+
module FReCon
|
5
|
+
class MatchesController < Controller
|
6
|
+
def self.competition(params)
|
7
|
+
show_attribute params, :competition
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.records(params)
|
11
|
+
show_attribute params, :records
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module FReCon
|
2
|
+
class ParticipationsController < Controller
|
3
|
+
def self.create(request, params)
|
4
|
+
post_data = process_request request
|
5
|
+
return post_data if post_data.is_an?(Array)
|
6
|
+
|
7
|
+
# Convert team number to team_id.
|
8
|
+
post_data = team_number_to_team_id(post_data)
|
9
|
+
|
10
|
+
@model = model.new
|
11
|
+
@model.attributes = post_data
|
12
|
+
|
13
|
+
if @model.save
|
14
|
+
[201, @model.to_json]
|
15
|
+
else
|
16
|
+
[422, ErrorFormatter.format(@model.errors.full_messages)]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.competition(params)
|
21
|
+
show_attribute params, :competition
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.team(params)
|
25
|
+
show_attribute params, :team
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "json"
|
2
|
+
require "frecon/base"
|
3
|
+
require "frecon/models"
|
4
|
+
|
5
|
+
module FReCon
|
6
|
+
class RecordsController < Controller
|
7
|
+
def self.create(request, params)
|
8
|
+
post_data = process_request request
|
9
|
+
return post_data if post_data.is_an?(Array)
|
10
|
+
|
11
|
+
# Change special post_data attributes.
|
12
|
+
# Convert team number to team id.
|
13
|
+
post_data = team_number_to_team_id(post_data)
|
14
|
+
|
15
|
+
# Convert match number and competition name to match id.
|
16
|
+
if post_data["match_number"] && !post_data["match_id"]
|
17
|
+
if post_data["competition_name"] && (competition = Competition.find_by name: post_data["competition_name"])
|
18
|
+
# Try to set the match to the already existing match.
|
19
|
+
match = competition.matches.find_by number: post_data["match_number"]
|
20
|
+
|
21
|
+
# Create the match if necessary.
|
22
|
+
match ||= Match.create(number: post_data["match_number"], competition_id: competition.id)
|
23
|
+
|
24
|
+
post_data["match_id"] = match.id
|
25
|
+
|
26
|
+
post_data.delete("match_number")
|
27
|
+
post_data.delete("competition_name")
|
28
|
+
elsif post_data["competition"] && post_data["competition"]["_id"] && post_data["competition"]["_id"]["$oid"] && (competition = Competition.find_by(id: post_data["competition"]["_id"]["$oid"]))
|
29
|
+
# Try to set the match to the already existing match.
|
30
|
+
match = competition.matches.find_by number: post_data["match_number"]
|
31
|
+
|
32
|
+
# Create the match if necessary.
|
33
|
+
match ||= Match.create(number: post_data["match_number"], competition_id: competition.id)
|
34
|
+
|
35
|
+
post_data["match_id"] = match.id
|
36
|
+
|
37
|
+
post_data.delete("match_number")
|
38
|
+
post_data.delete("competition")
|
39
|
+
else
|
40
|
+
return [422, ErrorFormatter.format("A current competition is not set. Please set it.")]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@record = Record.new
|
45
|
+
@record.attributes = post_data
|
46
|
+
|
47
|
+
if @record.save
|
48
|
+
# Use to_json for now; we can filter it later.
|
49
|
+
[201, @record.to_json]
|
50
|
+
else
|
51
|
+
[422, ErrorFormatter.format(@record.errors.full_messages)]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.competition(params)
|
56
|
+
show_attribute params, :competition
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "json"
|
2
|
+
require "frecon/models/robot"
|
3
|
+
|
4
|
+
module FReCon
|
5
|
+
class RobotsController < Controller
|
6
|
+
def self.competition(params)
|
7
|
+
show_attribute params, :competition
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.team(params)
|
11
|
+
show_attribute params, :team
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require "json"
|
2
|
+
require "frecon/base"
|
3
|
+
require "frecon/models"
|
4
|
+
|
5
|
+
module FReCon
|
6
|
+
class TeamsController < Controller
|
7
|
+
def self.create(request, params)
|
8
|
+
post_data = process_request request
|
9
|
+
return post_data if post_data.is_an?(Array)
|
10
|
+
|
11
|
+
@team = Team.new
|
12
|
+
@team.attributes = post_data
|
13
|
+
|
14
|
+
if @team.save
|
15
|
+
# Use to_json for now; we can filter it later.
|
16
|
+
[201, @team.to_json]
|
17
|
+
else
|
18
|
+
[422, ErrorFormatter.format(@team.errors.full_messages)]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.update(request, params)
|
23
|
+
return [400, "Must supply a team number!"] unless params[:number]
|
24
|
+
|
25
|
+
post_data = process_request request
|
26
|
+
return post_data if post_data.is_an?(Array)
|
27
|
+
|
28
|
+
@team = Team.find_by number: params[:number]
|
29
|
+
|
30
|
+
if @team.nil?
|
31
|
+
return [404, ErrorFormatter.format(could_not_find(params[:number], "number"))]
|
32
|
+
end
|
33
|
+
|
34
|
+
if @team.update_attributes(post_data)
|
35
|
+
@team.to_json
|
36
|
+
else
|
37
|
+
[422, ErrorFormatter.format(@team.errors.full_messages)]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.delete(params)
|
42
|
+
@team = Team.find_by number: params[:number]
|
43
|
+
|
44
|
+
if @team
|
45
|
+
if @team.destroy
|
46
|
+
204
|
47
|
+
else
|
48
|
+
[422, ErrorFormatter.format(@team.errors.full_messages)]
|
49
|
+
end
|
50
|
+
else
|
51
|
+
[404, ErrorFormatter.format(could_not_find(params[:number], "number"))]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.show(params)
|
56
|
+
@team = Team.find_by number: params[:number]
|
57
|
+
|
58
|
+
if @team
|
59
|
+
@team.to_json
|
60
|
+
else
|
61
|
+
[404, ErrorFormatter.format(could_not_find(params[:number], "number"))]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.records(params)
|
66
|
+
@team = Team.find_by number: params[:number]
|
67
|
+
|
68
|
+
if @team
|
69
|
+
if params[:competition_id]
|
70
|
+
@competition = Competition.find params[:competition_id]
|
71
|
+
|
72
|
+
if @competition
|
73
|
+
@team.records.in(match_id: @competition.matches.map { |match| match.id }).to_json
|
74
|
+
else
|
75
|
+
[404, ErrorFormatter.format(could_not_find(params[:competition_id], "id", "competition"))]
|
76
|
+
end
|
77
|
+
else
|
78
|
+
@team.records.to_json
|
79
|
+
end
|
80
|
+
else
|
81
|
+
[404, ErrorFormatter.format(could_not_find(params[:number], "number"))]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.matches(params)
|
86
|
+
@team = Team.find_by number: params[:number]
|
87
|
+
|
88
|
+
if @team
|
89
|
+
# Ensure that the competition ID is valid.
|
90
|
+
if params[:competition_id]
|
91
|
+
@competition = Competition.find params[:competition_id]
|
92
|
+
|
93
|
+
return [404, ErrorFormatter.format(could_not_find(params[:competition_id], "id", "competition"))] if @competition.nil?
|
94
|
+
end
|
95
|
+
|
96
|
+
@team.matches(params[:competition_id]).to_json
|
97
|
+
else
|
98
|
+
[404, ErrorFormatter.format(could_not_find(params[:number], "number"))]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.competitions(params)
|
103
|
+
@team = Team.find_by number: params[:number]
|
104
|
+
|
105
|
+
if @team
|
106
|
+
@team.competitions.to_json
|
107
|
+
else
|
108
|
+
[404, ErrorFormatter.format(could_not_find(params[:number], "number"))]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Libraries that all controllers require.
|
2
|
+
require "json"
|
3
|
+
require "frecon/error_formatter"
|
4
|
+
|
5
|
+
require "frecon/controller"
|
6
|
+
|
7
|
+
require "frecon/controllers/competitions_controller"
|
8
|
+
require "frecon/controllers/dump_controller"
|
9
|
+
require "frecon/controllers/matches_controller"
|
10
|
+
require "frecon/controllers/participations_controller"
|
11
|
+
require "frecon/controllers/records_controller"
|
12
|
+
require "frecon/controllers/robots_controller"
|
13
|
+
require "frecon/controllers/teams_controller"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "logger"
|
2
|
+
require "mongoid"
|
3
|
+
|
4
|
+
require "frecon/models"
|
5
|
+
|
6
|
+
module FReCon
|
7
|
+
class Database
|
8
|
+
def self.setup()
|
9
|
+
Mongoid.load!(File.join(File.dirname(__FILE__), "mongoid.yml"), ENVIRONMENT)
|
10
|
+
|
11
|
+
Mongoid.logger.level = Logger::DEBUG
|
12
|
+
Mongoid.logger = Logger.new($stdout)
|
13
|
+
|
14
|
+
Moped.logger.level = Logger::DEBUG
|
15
|
+
Moped.logger = Logger.new($stdout)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require "frecon/base"
|
2
|
+
|
3
|
+
module FReCon
|
4
|
+
class MatchNumber
|
5
|
+
POSSIBLE_TYPES = [:practice, :qualification, :quarterfinal, :semifinal, :final]
|
6
|
+
ELIMINATION_TYPES = [:quarterfinal, :semifinal, :final]
|
7
|
+
|
8
|
+
attr_reader :number, :round
|
9
|
+
|
10
|
+
# MongoDB compatibility methods.
|
11
|
+
def self.demongoize(object)
|
12
|
+
# `object' should *always* be a string (since MatchNumber#mongoize returns a
|
13
|
+
# String which is what is stored in the database)
|
14
|
+
raise ArgumentError, "`object' must be a String" unless object.is_a?(String)
|
15
|
+
|
16
|
+
MatchNumber.new(object)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.mongoize(object)
|
20
|
+
case object
|
21
|
+
when MatchNumber
|
22
|
+
object.mongoize
|
23
|
+
when String, Hash
|
24
|
+
MatchNumber.new(object).mongoize
|
25
|
+
else
|
26
|
+
object
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.evolve(object)
|
31
|
+
case object
|
32
|
+
when MatchNumber
|
33
|
+
object.mongoize
|
34
|
+
when String, Hash
|
35
|
+
MatchNumber.new(object).mongoize
|
36
|
+
else
|
37
|
+
object
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def mongoize
|
42
|
+
to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(args)
|
46
|
+
if args.is_a?(String)
|
47
|
+
# Match `args' against the regular expression, described below.
|
48
|
+
#
|
49
|
+
# This regular expression matches all values where the first group of
|
50
|
+
# characters is one of either [ "p", "q", "qf", "sf", "f" ], which is
|
51
|
+
# parsed as the "type" of the match. This is followed by an "m" and a
|
52
|
+
# group of digits, which is parsed as the "number" of the match.
|
53
|
+
#
|
54
|
+
# In addition, one can specify a "round number" following the first group
|
55
|
+
# of characters such as in eliminations and finals. Often times, there
|
56
|
+
# are multiple so-called "rounds" in eliminations, and so the system will
|
57
|
+
# optionally capture that round.
|
58
|
+
#
|
59
|
+
# Also, one can specify a "replay number" following the match number.
|
60
|
+
# this is done by appending "r" and a group of digits which is the replay
|
61
|
+
# number.
|
62
|
+
#
|
63
|
+
# Below are listed the match groups and what they are:
|
64
|
+
#
|
65
|
+
# 1: Match type
|
66
|
+
# 2: Round number (optional)
|
67
|
+
# 3: Match number
|
68
|
+
# 4: Replay string (optional)
|
69
|
+
# 5: Replay number (required if #4 is supplied)
|
70
|
+
#
|
71
|
+
# This behavior may change in the future.
|
72
|
+
match_data = args.match(/(p|q|qf|sf|f)([\d]+)?m([\d]+)(r)?([\d]+)?/i)
|
73
|
+
|
74
|
+
# Whine if we don't have a match (string is incorrectly formatted)
|
75
|
+
raise ArgumentError, "string is improperly formatted" unless match_data
|
76
|
+
|
77
|
+
# Check and set required stuff first, everything else later.
|
78
|
+
|
79
|
+
# Whine if we don't have a match type
|
80
|
+
raise ArgumentError, "match type must be supplied" unless match_data[1]
|
81
|
+
|
82
|
+
# Parse the match type string
|
83
|
+
@type = case match_data[1].downcase
|
84
|
+
when "p"
|
85
|
+
:practice
|
86
|
+
when "q"
|
87
|
+
:qualification
|
88
|
+
when "qf"
|
89
|
+
:quarterfinal
|
90
|
+
when "sf"
|
91
|
+
:semifinal
|
92
|
+
when "f"
|
93
|
+
:final
|
94
|
+
else
|
95
|
+
raise ArgumentError, "match type must be in [\"p\", \"q\", \"qf\", \"sf\", \"f\"]"
|
96
|
+
end
|
97
|
+
|
98
|
+
# Whine if we don't have a match number
|
99
|
+
raise ArgumentError, "match number must be supplied" unless match_data[3]
|
100
|
+
|
101
|
+
# Parse the match number
|
102
|
+
@number = match_data[3].to_i
|
103
|
+
raise ArgumentError, "match number must be greater than 0" unless @number > 0
|
104
|
+
|
105
|
+
# Parse the round number, if it is present
|
106
|
+
if match_data[2]
|
107
|
+
@round = match_data[2].to_i
|
108
|
+
raise ArgumentError, "round number must be greater than 0" unless @round > 0
|
109
|
+
end
|
110
|
+
|
111
|
+
# Parse replay match group, store replay number if present.
|
112
|
+
@replay_number = match_data[5].to_i if match_data[4] == "r"
|
113
|
+
elsif args.is_a?(Hash)
|
114
|
+
# type (Symbol or String)
|
115
|
+
# number (Integer)
|
116
|
+
# round (Integer), optional
|
117
|
+
# replay_number (Integer), optional
|
118
|
+
|
119
|
+
raise TypeError, "type must be a Symbol or String" unless args[:type].is_a?(Symbol) || args[:type].is_a?(String)
|
120
|
+
raise ArgumentError, "type must be in #{POSSIBLE_TYPES.inspect}" unless POSSIBLE_TYPES.include?(args[:type].to_sym)
|
121
|
+
|
122
|
+
@type = args[:type].to_sym
|
123
|
+
|
124
|
+
raise TypeError, "match number must be an Integer" unless args[:number].is_an?(Integer)
|
125
|
+
raise ArgumentError, "match number must be greater than 0" unless args[:number] > 0
|
126
|
+
|
127
|
+
@number = args[:number]
|
128
|
+
|
129
|
+
if args[:round]
|
130
|
+
raise TypeError, "round number must be an Integer" unless args[:round].is_an?(Integer)
|
131
|
+
raise ArgumentError, "round number must be greater than 0" unless args[:round] > 0
|
132
|
+
|
133
|
+
@round = args[:round]
|
134
|
+
end
|
135
|
+
|
136
|
+
if args[:replay_number]
|
137
|
+
raise TypeError, "replay number must be an Integer" unless args[:replay_number].is_an?(Integer)
|
138
|
+
raise ArgumentError, "replay number must be greater than 0" unless args[:replay_number] > 0
|
139
|
+
|
140
|
+
@replay_number = args[:replay_number]
|
141
|
+
end
|
142
|
+
else
|
143
|
+
raise TypeError, "argument must be a String or Hash"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def to_s
|
148
|
+
type_string = case @type
|
149
|
+
when :practice
|
150
|
+
"p"
|
151
|
+
when :qualification
|
152
|
+
"q"
|
153
|
+
when :quarterfinal
|
154
|
+
"qf"
|
155
|
+
when :semifinal
|
156
|
+
"sf"
|
157
|
+
when :final
|
158
|
+
"f"
|
159
|
+
end
|
160
|
+
match_string = "m#{@number}"
|
161
|
+
replay_string = "r#{@replay_number}" if replay?
|
162
|
+
|
163
|
+
"#{type_string}#{@round}#{match_string}#{replay_string}"
|
164
|
+
end
|
165
|
+
|
166
|
+
def replay?
|
167
|
+
!@replay_number.nil? && @replay_number > 0
|
168
|
+
end
|
169
|
+
|
170
|
+
def practice?
|
171
|
+
@type == :practice
|
172
|
+
end
|
173
|
+
|
174
|
+
def qualification?
|
175
|
+
@type == :qualification
|
176
|
+
end
|
177
|
+
|
178
|
+
def quarterfinal?
|
179
|
+
@type == :quarterfinal
|
180
|
+
end
|
181
|
+
|
182
|
+
def semifinal?
|
183
|
+
@type == :semifinal
|
184
|
+
end
|
185
|
+
|
186
|
+
def final?
|
187
|
+
@type == :final
|
188
|
+
end
|
189
|
+
|
190
|
+
def elimination?
|
191
|
+
ELIMINATION_TYPES.include?(@type)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
data/lib/frecon/model.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "frecon/model"
|
2
|
+
|
3
|
+
module FReCon
|
4
|
+
class Competition < Model
|
5
|
+
field :location, type: String
|
6
|
+
field :name, type: String
|
7
|
+
|
8
|
+
has_many :matches, dependent: :destroy
|
9
|
+
has_many :participations, dependent: :destroy
|
10
|
+
|
11
|
+
validates :location, :name, presence: true
|
12
|
+
validates :name, uniqueness: true
|
13
|
+
|
14
|
+
def records
|
15
|
+
matches = self.matches
|
16
|
+
|
17
|
+
Record.in match_id: matches.map(&:id)
|
18
|
+
end
|
19
|
+
|
20
|
+
def teams
|
21
|
+
participations = self.participations
|
22
|
+
|
23
|
+
Team.in id: participations.map(&:team_id)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "frecon/model"
|
2
|
+
require "frecon/match_number"
|
3
|
+
|
4
|
+
module FReCon
|
5
|
+
class Match < Model
|
6
|
+
field :number, type: MatchNumber
|
7
|
+
|
8
|
+
field :blue_score, type: Integer, default: 0
|
9
|
+
field :red_score, type: Integer, default: 0
|
10
|
+
|
11
|
+
belongs_to :competition
|
12
|
+
has_many :records, dependent: :destroy
|
13
|
+
|
14
|
+
validates :number, :competition_id, presence: true
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "frecon/model"
|
2
|
+
require "frecon/position"
|
3
|
+
|
4
|
+
module FReCon
|
5
|
+
class Record < Model
|
6
|
+
field :notes, type: String
|
7
|
+
field :position, type: Position
|
8
|
+
|
9
|
+
belongs_to :match
|
10
|
+
belongs_to :team
|
11
|
+
|
12
|
+
validates :position, :match_id, :team_id, presence: true
|
13
|
+
|
14
|
+
def competition
|
15
|
+
self.match.competition
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "frecon/model"
|
2
|
+
|
3
|
+
module FReCon
|
4
|
+
class Team < Model
|
5
|
+
field :number, type: Integer
|
6
|
+
|
7
|
+
field :location, type: String
|
8
|
+
field :logo_path, type: String
|
9
|
+
field :name, type: String
|
10
|
+
|
11
|
+
has_many :participations, dependent: :destroy
|
12
|
+
has_many :records, dependent: :destroy
|
13
|
+
|
14
|
+
validates :number, presence: true, uniqueness: true, numericality: { greater_than: 0 }
|
15
|
+
|
16
|
+
def self.number(team_number)
|
17
|
+
# Team.find_by number: team_number
|
18
|
+
find_by number: team_number
|
19
|
+
end
|
20
|
+
|
21
|
+
def competitions
|
22
|
+
Competition.in id: participations.map(&:competition_id)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns all of the matches that this team has been in.
|
26
|
+
# Optionally, returns the matches that this team has played
|
27
|
+
# in a certain competition.
|
28
|
+
def matches(competition_id = nil)
|
29
|
+
matches = Match.in record_id: self.records.map(&:id)
|
30
|
+
matches = matches.where competition_id: competition_id unless competition_id.nil?
|
31
|
+
|
32
|
+
matches
|
33
|
+
end
|
34
|
+
|
35
|
+
# alias_method works by default solely on instance
|
36
|
+
# methods, so change context to the metaclass of
|
37
|
+
# Team and do aliasing there.
|
38
|
+
class << self
|
39
|
+
alias_method :with_number, :number
|
40
|
+
alias_method :that_has_number, :number
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
development:
|
2
|
+
sessions:
|
3
|
+
default:
|
4
|
+
database: frecon
|
5
|
+
hosts:
|
6
|
+
- localhost:27017
|
7
|
+
options:
|
8
|
+
use_utc: true
|
9
|
+
raise_not_found_error: false
|
10
|
+
|
11
|
+
production:
|
12
|
+
sessions:
|
13
|
+
default:
|
14
|
+
database: frecon
|
15
|
+
hosts:
|
16
|
+
- localhost:27017
|
17
|
+
options:
|
18
|
+
use_utc: true
|
19
|
+
raise_not_found_error: false
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require "frecon/base"
|
2
|
+
|
3
|
+
module FReCon
|
4
|
+
class Position
|
5
|
+
attr_reader :alliance, :number
|
6
|
+
|
7
|
+
# MongoDB compatibility methods.
|
8
|
+
def self.demongoize(object)
|
9
|
+
# `object' should *always* be a string (since MatchNumber#mongoize returns a
|
10
|
+
# String which is what is stored in the database)
|
11
|
+
raise ArgumentError, "`object' must be a String" unless object.is_a?(String)
|
12
|
+
|
13
|
+
Position.new(object)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Allows passing a String or Hash instead of a Position.
|
17
|
+
# i.e. record.position = "r3"
|
18
|
+
def self.mongoize(object)
|
19
|
+
case object
|
20
|
+
when Position
|
21
|
+
object.mongoize
|
22
|
+
when String
|
23
|
+
Position.new(object).mongoize
|
24
|
+
when Hash
|
25
|
+
Position.new(object[:alliance], object[:number]).mongoize
|
26
|
+
else
|
27
|
+
object
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Used for queries.
|
32
|
+
def self.evolve(object)
|
33
|
+
case object
|
34
|
+
when Position
|
35
|
+
object.mongoize
|
36
|
+
when String
|
37
|
+
Position.new(object).mongoize
|
38
|
+
when Hash
|
39
|
+
Position.new(object[:alliance], object[:number]).mongoize
|
40
|
+
else
|
41
|
+
object
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def mongoize
|
46
|
+
to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(*args)
|
50
|
+
if args.length == 1
|
51
|
+
# Match `string' against the regular expression, described below.
|
52
|
+
#
|
53
|
+
# This regular expression matches all values for `string' where
|
54
|
+
# the first letter is either "r" or "b" (case-insensitive due to /i
|
55
|
+
# at the end of the regular expression) and the last one-or-more
|
56
|
+
# characters in the string are digits 0-9. Anything between those two
|
57
|
+
# that is either a letter or an underscore is not retained, but
|
58
|
+
# if other characters exist (e.g. spaces as of right now) `string'
|
59
|
+
# will not match.
|
60
|
+
#
|
61
|
+
# You can use any words you like if you have more than just
|
62
|
+
# "r<n>" or "b<n>", for example "red_2" matches just the same
|
63
|
+
# as "r2", or, just for fun, just the same as "royal______2".
|
64
|
+
#
|
65
|
+
# This behavior may change in the future.
|
66
|
+
match_data = args[0].match(/^([rb])[a-z\_]*([0-9]+)/i)
|
67
|
+
|
68
|
+
# Note: if matched at all, match_data[0] is the entire
|
69
|
+
# string that was matched, hence the indices that start
|
70
|
+
# at one.
|
71
|
+
|
72
|
+
raise ArgumentError, "string is improperly formatted" unless match_data
|
73
|
+
|
74
|
+
@alliance = case match_data[1].downcase
|
75
|
+
when "b"
|
76
|
+
:blue
|
77
|
+
when "r"
|
78
|
+
:red
|
79
|
+
else
|
80
|
+
raise ArgumentError, "alliance character must be in [\"b\", \"r\"]"
|
81
|
+
end
|
82
|
+
|
83
|
+
position_number = match_data[2].to_i
|
84
|
+
raise ArgumentError, "position number must be in [1, 2, 3]" unless [1, 2, 3].include?(position_number)
|
85
|
+
|
86
|
+
@number = position_number
|
87
|
+
elsif args.length == 2
|
88
|
+
raise TypeError, "alliance must be a Symbol or String" unless args[0].is_a?(Symbol) || args[0].is_a?(String)
|
89
|
+
raise ArgumentError, "alliance must be in [:blue, :red]" unless [:blue, :red].include?(args[0].to_sym)
|
90
|
+
|
91
|
+
@alliance = args[0].to_sym
|
92
|
+
|
93
|
+
raise TypeError, "second argument must be an Integer" unless args[1].is_an?(Integer)
|
94
|
+
raise ArgumentError, "second argument must be in [1, 2, 3]" unless [1, 2, 3].include?(args[1])
|
95
|
+
|
96
|
+
@number = args[1]
|
97
|
+
else
|
98
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for [1, 2])"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def to_s
|
103
|
+
"#{@alliance[0]}#{@number}"
|
104
|
+
end
|
105
|
+
|
106
|
+
def is_blue?
|
107
|
+
@alliance == :blue
|
108
|
+
end
|
109
|
+
|
110
|
+
def is_red?
|
111
|
+
@alliance == :red
|
112
|
+
end
|
113
|
+
|
114
|
+
alias_method :was_blue?, :is_blue?
|
115
|
+
alias_method :was_red?, :is_red?
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require "frecon/controllers"
|
2
|
+
|
3
|
+
module FReCon
|
4
|
+
module Routes
|
5
|
+
def self.resource_routes(base, name, controller, methods = [:create, :update, :delete, :show, :index])
|
6
|
+
if methods.include?(:create)
|
7
|
+
base.post "/#{name}" do
|
8
|
+
controller.create request, params
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
if methods.include?(:update)
|
13
|
+
base.put "/#{name}/:id" do
|
14
|
+
controller.update request, params
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
if methods.include?(:delete)
|
19
|
+
base.delete "/#{name}/:id" do
|
20
|
+
controller.delete params
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
if methods.include?(:show)
|
25
|
+
base.get "/#{name}/:id" do
|
26
|
+
controller.show params
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
if methods.include?(:index)
|
31
|
+
base.get "/#{name}" do
|
32
|
+
controller.index params
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.included(base)
|
38
|
+
resource_routes base, "teams", TeamsController, [:create, :index]
|
39
|
+
|
40
|
+
base.put "/teams/:number" do
|
41
|
+
TeamsController.update request, params
|
42
|
+
end
|
43
|
+
|
44
|
+
base.delete "/teams/:number" do
|
45
|
+
TeamsController.delete params
|
46
|
+
end
|
47
|
+
|
48
|
+
base.get "/teams/:number" do
|
49
|
+
TeamsController.show params
|
50
|
+
end
|
51
|
+
|
52
|
+
base.get "/teams/:number/records/?:competition_id?" do
|
53
|
+
TeamsController.records params
|
54
|
+
end
|
55
|
+
|
56
|
+
base.get "/teams/:number/matches/?:competition_id?" do
|
57
|
+
TeamsController.matches params
|
58
|
+
end
|
59
|
+
|
60
|
+
base.get "/teams/:number/competitions" do
|
61
|
+
TeamsController.competitions params
|
62
|
+
end
|
63
|
+
|
64
|
+
resource_routes base, "competitions", CompetitionsController
|
65
|
+
|
66
|
+
base.get "/competitions/:id/teams" do
|
67
|
+
CompetitionsController.teams params
|
68
|
+
end
|
69
|
+
|
70
|
+
base.get "/competitions/:id/matches" do
|
71
|
+
CompetitionsController.matches params
|
72
|
+
end
|
73
|
+
|
74
|
+
base.get "/competitions/:id/records" do
|
75
|
+
CompetitionsController.records params
|
76
|
+
end
|
77
|
+
|
78
|
+
resource_routes base, "matches", MatchesController
|
79
|
+
|
80
|
+
base.get "/matches/:id/records" do
|
81
|
+
MatchesController.records params
|
82
|
+
end
|
83
|
+
|
84
|
+
base.get "/matches/:id/competition" do
|
85
|
+
MatchesController.competition params
|
86
|
+
end
|
87
|
+
|
88
|
+
resource_routes base, "records", RecordsController
|
89
|
+
|
90
|
+
base.get "/records/:id/competition" do
|
91
|
+
RecordsController.competition params
|
92
|
+
end
|
93
|
+
|
94
|
+
resource_routes base, "robots", RobotsController
|
95
|
+
|
96
|
+
base.get "/robots/:id/competition" do
|
97
|
+
RobotsController.competition params
|
98
|
+
end
|
99
|
+
|
100
|
+
base.get "/robots/:id/team" do
|
101
|
+
RobotsController.team params
|
102
|
+
end
|
103
|
+
|
104
|
+
resource_routes base, "participations", ParticipationsController
|
105
|
+
|
106
|
+
base.get "/participations/:id/competition" do
|
107
|
+
ParticipationsController.competition params
|
108
|
+
end
|
109
|
+
|
110
|
+
base.get "/participations/:id/team" do
|
111
|
+
ParticipationsController.team params
|
112
|
+
end
|
113
|
+
|
114
|
+
base.get "/dump" do
|
115
|
+
DumpController.full params
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "sinatra/base"
|
2
|
+
|
3
|
+
require "frecon/database"
|
4
|
+
require "frecon/routes"
|
5
|
+
require "frecon/controllers"
|
6
|
+
|
7
|
+
module FReCon
|
8
|
+
class Server < Sinatra::Base
|
9
|
+
include Routes
|
10
|
+
|
11
|
+
configure do
|
12
|
+
Database.setup
|
13
|
+
end
|
14
|
+
|
15
|
+
before do
|
16
|
+
content_type "application/json"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.start
|
20
|
+
run!
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/frecon.rb
ADDED
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: frecon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sam Craig
|
8
|
+
- Kristofer Rye
|
9
|
+
- Christopher Cooper
|
10
|
+
- Sam Mercier
|
11
|
+
- Tiger Huang
|
12
|
+
- Vincent Mai
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
date: 2015-09-01 00:00:00.000000000 Z
|
17
|
+
dependencies:
|
18
|
+
- !ruby/object:Gem::Dependency
|
19
|
+
name: sinatra
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - "~>"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '1.4'
|
25
|
+
type: :runtime
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - "~>"
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '1.4'
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: thin
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - "~>"
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '1.6'
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - "~>"
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.6'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: mongoid
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - "~>"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '4.0'
|
53
|
+
type: :runtime
|
54
|
+
prerelease: false
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '4.0'
|
60
|
+
description: A JSON API for scouting FRC competitions that manages the database for
|
61
|
+
the user.
|
62
|
+
email: sammidysam@gmail.com
|
63
|
+
executables:
|
64
|
+
- frecon
|
65
|
+
extensions: []
|
66
|
+
extra_rdoc_files: []
|
67
|
+
files:
|
68
|
+
- bin/frecon
|
69
|
+
- lib/frecon.rb
|
70
|
+
- lib/frecon/base.rb
|
71
|
+
- lib/frecon/base/object.rb
|
72
|
+
- lib/frecon/base/variables.rb
|
73
|
+
- lib/frecon/console.rb
|
74
|
+
- lib/frecon/controller.rb
|
75
|
+
- lib/frecon/controllers.rb
|
76
|
+
- lib/frecon/controllers/competitions_controller.rb
|
77
|
+
- lib/frecon/controllers/dump_controller.rb
|
78
|
+
- lib/frecon/controllers/matches_controller.rb
|
79
|
+
- lib/frecon/controllers/participations_controller.rb
|
80
|
+
- lib/frecon/controllers/records_controller.rb
|
81
|
+
- lib/frecon/controllers/robots_controller.rb
|
82
|
+
- lib/frecon/controllers/teams_controller.rb
|
83
|
+
- lib/frecon/database.rb
|
84
|
+
- lib/frecon/error_formatter.rb
|
85
|
+
- lib/frecon/match_number.rb
|
86
|
+
- lib/frecon/model.rb
|
87
|
+
- lib/frecon/models.rb
|
88
|
+
- lib/frecon/models/competition.rb
|
89
|
+
- lib/frecon/models/match.rb
|
90
|
+
- lib/frecon/models/participation.rb
|
91
|
+
- lib/frecon/models/record.rb
|
92
|
+
- lib/frecon/models/robot.rb
|
93
|
+
- lib/frecon/models/team.rb
|
94
|
+
- lib/frecon/mongoid.yml
|
95
|
+
- lib/frecon/position.rb
|
96
|
+
- lib/frecon/routes.rb
|
97
|
+
- lib/frecon/server.rb
|
98
|
+
homepage: https://github.com/scouting-project/scouting-project
|
99
|
+
licenses: []
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.4.5.1
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: A JSON API for scouting FRC competitions.
|
121
|
+
test_files: []
|
122
|
+
has_rdoc:
|