frecon 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|