frecon 0.5.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3695507c98a8055222ee241b01a4811d02f0aa96
4
- data.tar.gz: 152e3d581b3f941762b1cba4b6dfb871408b042d
3
+ metadata.gz: 4e2c810e2a8cf4f910ac9a90d52a449b61285df9
4
+ data.tar.gz: 8a3f9339a0b6539ec355458f8e1b9e7aa3a995da
5
5
  SHA512:
6
- metadata.gz: e8da7107dd168433d62a49c08104e3c4f675543e0837c6c3041b67edcfd78710ea2b9ec3c0608f46ac4fc2c39269c005e4536258bd6a17be324cea4f6502757b
7
- data.tar.gz: f9811d2b31a5c30fc0597a9357be77122df2d052686857e506a7945b0e85b8635e7626e14b4b1f7312e3774c464508b23c2b6bdc62056609905d35251aa9ca7c
6
+ metadata.gz: 52ce69ae4af6d7137426fd79a6ea39deffdc0d71900a18cbff137ee986a6ff74dfad0aad45284dbc5840629670088e9b1c8c5a50911822382d354a7b843d22ed
7
+ data.tar.gz: 362cea6a6e6110246ba69320b856d9439c52c0c14c659734f61eeb78fadc0f5bbdb25127239b10bd3ffc9646498ca0f5cffc780fba4ccda21db6883b30ad8152
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Dependencies loaded from frecon.gemspec
4
+ gemspec
@@ -0,0 +1,15 @@
1
+ require "yard"
2
+
3
+ YARD::Config.load_plugin('tomdoc')
4
+ YARD::Config.load_plugin('mongoid')
5
+
6
+ namespace :docs do
7
+ YARD::Rake::YardocTask.new :generate do |task|
8
+ task.files = ['lib/**/*.rb', 'bin/**/*']
9
+ end
10
+
11
+ YARD::Rake::YardocTask.new :list_undoc do |task|
12
+ task.files = ['lib/**/*.rb', 'bin/**/*']
13
+ task.stats_options = ['--list-undoc']
14
+ end
15
+ end
@@ -11,10 +11,20 @@ require "mongoid"
11
11
 
12
12
  require "frecon/base"
13
13
 
14
+ require "frecon/controller"
15
+ require "frecon/controllers"
16
+ require "frecon/model"
17
+ require "frecon/models"
18
+ require "frecon/scraper"
19
+ require "frecon/scrapers"
20
+
14
21
  require "frecon/configuration"
15
22
  require "frecon/configuration_file"
23
+ require "frecon/match_number"
24
+ require "frecon/position"
25
+ require "frecon/request_error"
26
+ require "frecon/routes"
27
+
16
28
  require "frecon/database"
17
29
  require "frecon/server"
18
30
  require "frecon/console"
19
-
20
- require "frecon/scrapers"
@@ -7,8 +7,17 @@
7
7
  # license with this program. If not, please see
8
8
  # <http://opensource.org/licenses/MIT>.
9
9
 
10
+ # Public: An extension for the BSON module.
10
11
  module BSON
12
+ # Public: A monkey-patch for the BSON::ObjectId class which introduces an
13
+ # #as_json method.
11
14
  class ObjectId
15
+ # Public: Get produce a JSON representation of this ObjectId.
16
+ #
17
+ # Since we don't want to produce a JSON Object for every ID, this method
18
+ # instead just returns the String _id value within this object.
19
+ #
20
+ # Returns a String containing the value of this ObjectId.
12
21
  def as_json(*args)
13
22
  to_s
14
23
  end
@@ -7,15 +7,23 @@
7
7
  # license with this program. If not, please see
8
8
  # <http://opensource.org/licenses/MIT>.
9
9
 
10
+ # Public: The FReCon API module.
10
11
  module FReCon
11
- VERSION ||= "0.5.0"
12
+ # Public: A String representing the current version of FReCon.
13
+ VERSION = "0.5.2"
12
14
 
13
15
  @environment_variable = :development
14
16
 
17
+ # Public: Returns the current environment.
15
18
  def self.environment
16
19
  @environment_variable
17
20
  end
18
21
 
22
+ # Public: Sets the environment.
23
+ #
24
+ # arg - The new environment.
25
+ #
26
+ # Returns the result from setting the current environment.
19
27
  def self.environment=(arg)
20
28
  @environment_variable = arg
21
29
  end
@@ -10,13 +10,23 @@
10
10
  require "frecon/configuration_file"
11
11
 
12
12
  module FReCon
13
+ # Public: A wrapper to allow the manipulation of configurations.
13
14
  class Configuration < Hash
15
+ # Public: Initialize a Configuration.
16
+ #
17
+ # data - a Hash representing the data.
14
18
  def initialize(data)
15
19
  data.each do |key, value|
16
20
  self[key] = value
17
21
  end
18
22
  end
19
23
 
24
+ # Public: Convert self to a Hash.
25
+ #
26
+ # Recursively converts instances of Configuration within self
27
+ # to hashes by calling this method.
28
+ #
29
+ # Returns a Hash representing self.
20
30
  def to_h
21
31
  hash = {}
22
32
 
@@ -32,6 +42,11 @@ module FReCon
32
42
  hash
33
43
  end
34
44
 
45
+ # Public: Merge with another Configuration.
46
+ #
47
+ # Sets all key-value pairs within Configuration to the same within self.
48
+ #
49
+ # other - A Configuration or Hash to be merged with.
35
50
  def merge(other)
36
51
  case other
37
52
  when Configuration, Hash
@@ -49,11 +64,20 @@ module FReCon
49
64
  end
50
65
  end
51
66
 
67
+ # Public: Constructs a configuration.
68
+ #
69
+ # options - A Hash containing various configurations.
70
+ # :default_configuration - The default configuration's values
71
+ # :system_configuration - The system's configuration's values
72
+ # :user_configuration - The user's configuration's values
73
+ # :argument_configuration - The configuration values from command-line arguments
74
+ #
75
+ # Returns a Configuration generated by merging all of the given
76
+ # configurations together.
52
77
  def self.construct!(default_configuration: ConfigurationFile.default.read,
53
78
  system_configuration: ConfigurationFile.system.read,
54
79
  user_configuration: ConfigurationFile.user.read,
55
80
  argument_configuration: nil)
56
-
57
81
  configuration_hierarchy = [default_configuration, system_configuration, user_configuration, argument_configuration]
58
82
 
59
83
  configuration = Configuration.new({})
@@ -11,13 +11,23 @@ require "yaml"
11
11
  require "frecon/configuration"
12
12
 
13
13
  module FReCon
14
+ # Public: A class to handle configuration files.
14
15
  class ConfigurationFile
16
+ # Public: The filename for the file.
15
17
  attr_accessor :filename
16
18
 
19
+ # Public: Initialize a ConfigurationFile.
20
+ #
21
+ # filename - The name of the file.
17
22
  def initialize(filename)
18
23
  @filename = filename
19
24
  end
20
25
 
26
+ # Public: Read from the file and generate a Configuration
27
+ # from the YAML data therein.
28
+ #
29
+ # Returns a Configuration representing the file's data or nil if it didn't
30
+ # exist.
21
31
  def read
22
32
  begin
23
33
  data = open(@filename, "rb") do |io|
@@ -30,16 +40,22 @@ module FReCon
30
40
  end
31
41
  end
32
42
 
43
+ # Public: Create a new ConfigurationFile corresponding to the default
44
+ # defaults configuration location.
33
45
  def self.default
34
46
  self.new(File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "config", "default.yml")))
35
47
  end
36
48
 
49
+ # Public: Create a new ConfigurationFile corresponding to the default
50
+ # system configuration location.
37
51
  def self.system
38
52
  self.new(File.join("", "etc", "frecon", "config.yml"))
39
53
  end
40
54
 
55
+ # Public: Create a new ConfigurationFile corresponding to the default
56
+ # user configuration location.
41
57
  def self.user
42
- self.new(File.join(Dir.home, "config", "frecon.yml"))
58
+ self.new(File.join(Dir.home, ".config", "frecon.yml"))
43
59
  end
44
60
  end
45
61
  end
@@ -13,11 +13,17 @@ require "frecon/database"
13
13
  require "frecon/server"
14
14
 
15
15
  module FReCon
16
+ # Public: The wrapper system for a pry console.
16
17
  class Console
18
+ # Public: Starts the FReCon console.
19
+ #
20
+ # :configuration - The Configuration to use when starting the console.
21
+ #
22
+ # Returns the result of running pry on FReCon.
17
23
  def self.start(configuration: Configuration.construct!)
18
24
  environment = configuration["frecon"]["console"]["environment"]
19
25
  mongoid = configuration["frecon"]["database"]["mongoid"]
20
- Database.setup(environment: environment, mongoid: mongoid)
26
+ Database.setup(environment, mongoid)
21
27
 
22
28
  require "pry"
23
29
 
@@ -10,30 +10,54 @@
10
10
  require "frecon/base"
11
11
 
12
12
  module FReCon
13
+ # Public: A base class to represent a controller.
13
14
  class Controller
15
+ # Public: Converts the class's name to a Model name.
16
+ #
17
+ # Returns a Symbol that is the Model name.
14
18
  def self.model_name
15
19
  # Removes the namespace "FReCon::" and "Controller" from
16
20
  # the class name, then singularizes the result.
17
21
  self.name.gsub(/FReCon::|Controller\Z/, "").singularize
18
22
  end
19
23
 
24
+ # Public: Converts the class's name to a Model.
25
+ #
26
+ # Returns the Model's class.
20
27
  def self.model
21
28
  # Removes the trailing "Controller" from the class name,
22
29
  # singularizes the result, and turns it into the class.
23
30
  self.name.gsub(/Controller\Z/, "").singularize.constantize
24
31
  end
25
32
 
26
- # Some models have to find themselves in special ways,
27
- # so this can be overridden with those ways.
33
+ # Public: Find a model.
34
+ #
35
+ # params - A Hash containing the parameters. This should contain an
36
+ # 'id' key, which is deleted and used for the find.
37
+ #
38
+ # Returns either the found model value or nil.
28
39
  def self.find_model(params)
29
40
  model.find params.delete("id")
30
41
  end
31
42
 
32
- # The 404 error message.
43
+ # Public: Generate a could-not-find message.
44
+ #
45
+ # value - The value that was tested.
46
+ # attribute - The attribute that was used for the search.
47
+ # model - The model that the search was performed upon.
48
+ #
49
+ # Returns a String containing the error message.
33
50
  def self.could_not_find(value, attribute = "id", model = model_name.downcase)
34
51
  "Could not find #{model} of #{attribute} #{value}!"
35
52
  end
36
53
 
54
+ # Public: Process a JSON request.
55
+ #
56
+ # request - The internal Sinatra request object that is available to
57
+ # request handling.
58
+ #
59
+ # Returns a Hash corresponding to the request's body.
60
+ # Raises a RequestError if the JSON parse fails.
37
61
  def self.process_json_request(request)
38
62
  # Rewind the request body (an IO object)
39
63
  # in case someone else has already played
@@ -49,6 +73,20 @@ module FReCon
49
73
  post_data
50
74
  end
51
75
 
76
+ # Public: Process a creation request (HTTP POST)
77
+ #
78
+ # If `post_data` is an Array, iterates through the array and calls itself
79
+ # with each element within. Otherwise, performs the creation using
80
+ # the attribute key-value pairings within the `post_data`.
81
+ #
82
+ # request - The internal Sinatra request object that is available to
83
+ # request handling.
84
+ # params - The internal params Hash that is available to request
85
+ # handling.
86
+ # post_data - The data that was sent in the request body.
87
+ #
88
+ # Returns an Array, a formatted response that can be passed back through
89
+ # Sinatra's request processing.
52
90
  def self.create(request, params, post_data = nil)
53
91
  post_data ||= process_json_request request
54
92
 
@@ -85,6 +123,19 @@ module FReCon
85
123
  end
86
124
  end
87
125
 
126
+ # Public: Process an update request (HTTP PUT)
127
+ #
128
+ # Processes the JSON request, finds the model, then updates it.
129
+ #
130
+ # request - The internal Sinatra request object that is available to
131
+ # request handling.
132
+ # params - The internal params Hash that is available to request
133
+ # handling.
134
+ # post_data - The data that was sent in the request body.
135
+ #
136
+ # Returns a String with the JSON representation of the model.
137
+ # Raises a RequestError if the request is malformed or if the attributes
138
+ # can't be updated.
88
139
  def self.update(request, params, post_data = nil)
89
140
  raise RequestError.new(400, "Must supply a #{model_name.downcase} id!") unless params[:id]
90
141
 
@@ -101,6 +152,19 @@ module FReCon
101
152
  end
102
153
  end
103
154
 
155
+ # Public: Process a deletion request (HTTP DELETE)
156
+ #
157
+ # Processes the JSON request, finds the model, then deletes it.
158
+ #
159
+ # request - The internal Sinatra request object that is available to
160
+ # request handling.
161
+ # params - The internal params Hash that is available to request
162
+ # handling.
163
+ # post_data - The data that was sent in the request body.
164
+ #
165
+ # Returns 204 if successful.
166
+ # Raises a RequestError if the request is malformed or if the model can't be
167
+ # destroyed
104
168
  def self.delete(params)
105
169
  @model = find_model params
106
170
 
@@ -115,6 +179,20 @@ module FReCon
115
179
  end
116
180
  end
117
181
 
182
+ # Public: Process a show request (HTTP GET) for a specific instance of a
183
+ # model.
184
+ #
185
+ # Processes the JSON request, finds the model, then shows it.
186
+ #
187
+ # request - The internal Sinatra request object that is available to
188
+ # request handling.
189
+ # params - The internal params Hash that is available to request
190
+ # handling.
191
+ # post_data - The data that was sent in the request body.
192
+ #
193
+ # Returns a String with the JSON representation of the model.
194
+ # Raises a RequestError if the request is malformed or if the model can't be
195
+ # found.
118
196
  def self.show(params)
119
197
  @model = find_model params
120
198
 
@@ -125,6 +203,19 @@ module FReCon
125
203
  end
126
204
  end
127
205
 
206
+ # Public: Process an index request (HTTP GET) for all instances of a model.
207
+ #
208
+ # Processes the JSON request, and returns a filtered list of all of the
209
+ # models.
210
+ #
211
+ # request - The internal Sinatra request object that is available to
212
+ # request handling.
213
+ # params - The internal params Hash that is available to request
214
+ # handling.
215
+ # post_data - The data that was sent in the request body.
216
+ #
217
+ # Returns a String with the JSON representation of the list of models.
218
+ # Raises a RequestError if the request is malformed.
128
219
  def self.index(params)
129
220
  if params.empty?
130
221
  @models = model.all
@@ -11,6 +11,7 @@ require "json"
11
11
  require "frecon/models/competition"
12
12
 
13
13
  module FReCon
14
+ # Public: The Competitions controller.
14
15
  class CompetitionsController < Controller
15
16
  end
16
17
  end
@@ -11,7 +11,11 @@ require "json"
11
11
  require "frecon/models"
12
12
 
13
13
  module FReCon
14
+ # Public: The Dump controller.
14
15
  class DumpController
16
+ # Public: Creates a dump.
17
+ #
18
+ # Returns a String containing a dump of the database.
15
19
  def self.full(params)
16
20
  dump = {}
17
21
 
@@ -30,6 +34,14 @@ module FReCon
30
34
  dump.to_json
31
35
  end
32
36
 
37
+ # Public: Converts a Model's name to a dump-compliant name.
38
+ #
39
+ # Examples
40
+ #
41
+ # DumpController.dump_compliant_name(FReCon::Team)
42
+ # # => "teams"
43
+ #
44
+ # Returns a dump-compliant string.
33
45
  def self.dump_compliant_name(model)
34
46
  model.name.gsub(/FReCon::/, "").downcase.pluralize
35
47
  end
@@ -11,6 +11,7 @@ require "json"
11
11
  require "frecon/models/match"
12
12
 
13
13
  module FReCon
14
+ # Public: The Matches controller.
14
15
  class MatchesController < Controller
15
16
  end
16
17
  end
@@ -11,6 +11,7 @@ require "json"
11
11
  require "frecon/models/participation"
12
12
 
13
13
  module FReCon
14
+ # Public: The Participations controller.
14
15
  class ParticipationsController < Controller
15
16
  end
16
17
  end
@@ -11,6 +11,7 @@ require "json"
11
11
  require "frecon/models/record"
12
12
 
13
13
  module FReCon
14
+ # Public: The Records controller.
14
15
  class RecordsController < Controller
15
16
  end
16
17
  end
@@ -11,6 +11,7 @@ require "json"
11
11
  require "frecon/models/robot"
12
12
 
13
13
  module FReCon
14
+ # Public: The Robots controller.
14
15
  class RobotsController < Controller
15
16
  end
16
17
  end
@@ -8,10 +8,10 @@
8
8
  # <http://opensource.org/licenses/MIT>.
9
9
 
10
10
  require "json"
11
- require "frecon/base"
12
11
  require "frecon/models/team"
13
12
 
14
13
  module FReCon
14
+ # Public: The Teams controller.
15
15
  class TeamsController < Controller
16
16
  end
17
17
  end
@@ -18,8 +18,16 @@ require "yaml"
18
18
  require "frecon/models"
19
19
 
20
20
  module FReCon
21
+ # Public: A system to set up the database.
21
22
  class Database
22
- def self.setup(environment: FReCon.environment, mongoid: nil)
23
+ # Public: Set up the database.
24
+ #
25
+ # environment - Symbol containing environment to start the database in.
26
+ # mongoid - Hash containing the configuration for Mongoid. If not
27
+ # present, the lib/frecon/mongoid.yml file is given to
28
+ # Mongoid.load!. If present, the Hash is dumped to a
29
+ # tempfile which is given to Mongoid.load!.
30
+ def self.setup(environment = FReCon.environment, mongoid = nil)
23
31
  if mongoid.is_a?(Hash)
24
32
  mongoid_tempfile = Tempfile.new("FReCon")
25
33
 
@@ -10,13 +10,46 @@
10
10
  require "frecon/base"
11
11
 
12
12
  module FReCon
13
+ # Public: A wrapper to handle converting match numbers and storing them.
13
14
  class MatchNumber
15
+ # Public: All of the possible match types for a MatchNumber to have.
14
16
  POSSIBLE_TYPES = [:practice, :qualification, :quarterfinal, :semifinal, :final]
15
- ELIMINATION_TYPES = [:quarterfinal, :semifinal, :final]
16
17
 
17
- attr_reader :number, :round
18
+ # Public: All of the elimination types for a MatchNumber to have.
19
+ ELIMINATION_TYPES = [:quarterfinal, :semifinal, :final]
18
20
 
19
- # MongoDB compatibility methods.
21
+ # Public: The numerical part of the match number
22
+ #
23
+ # Examples
24
+ #
25
+ # match_number = MatchNumber.new('qm2')
26
+ # match_number.number
27
+ # # => 2
28
+ attr_reader :number
29
+
30
+ # Public: The round part of the match number
31
+ #
32
+ # Examples
33
+ #
34
+ # match_number = MatchNumber.new('qf1m2r3')
35
+ # match_number.round
36
+ # # => 2
37
+ attr_reader :round
38
+
39
+ # Public: The type of the match.
40
+ #
41
+ # Examples
42
+ #
43
+ # match_number = MatchNumber.new('qf1m2r3')
44
+ # match_number.type
45
+ # # => :quarterfinal
46
+ attr_reader :type
47
+
48
+ # Public: Convert a stored match number to a MatchNumber object.
49
+ #
50
+ # object - String representation of a match number (mongoized)
51
+ #
52
+ # Returns MatchNumber parsed from object.
20
53
  def self.demongoize(object)
21
54
  # `object' should *always* be a string (since MatchNumber#mongoize returns a
22
55
  # String which is what is stored in the database)
@@ -25,6 +58,15 @@ module FReCon
25
58
  MatchNumber.new(object)
26
59
  end
27
60
 
61
+ # Public: Convert a MatchNumber object to a storable string representation.
62
+ #
63
+ # object - A MatchNumber, String, or Hash. If MatchNumber, run #mongoize on
64
+ # it. If String, create a new MatchNumber object for it, then run
65
+ # #mongoize on it. If Hash, convert its keys to symbols, then
66
+ # pull out the :alliance and :number keys to generate a
67
+ # MatchNumber.
68
+ #
69
+ # Returns String containing the mongo-ready value for the representation.
28
70
  def self.mongoize(object)
29
71
  case object
30
72
  when MatchNumber
@@ -36,6 +78,16 @@ module FReCon
36
78
  end
37
79
  end
38
80
 
81
+ # Public: Convert a MatchNumber object to a storable string representation
82
+ # for queries.
83
+ #
84
+ # object - A MatchNumber, String, or Hash. If MatchNumber, run #mongoize on
85
+ # it. If String, create a new MatchNumber object for it, then run
86
+ # #mongoize on it. If Hash, convert its keys to symbols, then
87
+ # pull out the :alliance and :number keys to generate a
88
+ # MatchNumber.
89
+ #
90
+ # Returns String containing the mongo-ready value for the representation.
39
91
  def self.evolve(object)
40
92
  case object
41
93
  when MatchNumber
@@ -47,6 +99,9 @@ module FReCon
47
99
  end
48
100
  end
49
101
 
102
+ # Public: Convert to a storable string representation.
103
+ #
104
+ # Returns String representing the MatchNumber's data.
50
105
  def mongoize
51
106
  to_s
52
107
  end
@@ -156,6 +211,9 @@ module FReCon
156
211
  end
157
212
  end
158
213
 
214
+ # Public: Convert to a String.
215
+ #
216
+ # Returns String representing the match number data.
159
217
  def to_s
160
218
  type_string = case @type
161
219
  when :practice
@@ -175,30 +233,38 @@ module FReCon
175
233
  "#{type_string}#{@round}#{match_string}#{replay_string}"
176
234
  end
177
235
 
236
+ # Public: Determine if MatchNumber represents a replay.
178
237
  def replay?
179
238
  !@replay_number.nil? && @replay_number > 0
180
239
  end
181
240
 
241
+ # Public: Determine if MatchNumber represents a practice match.
182
242
  def practice?
183
243
  @type == :practice
184
244
  end
185
245
 
246
+ # Public: Determine if MatchNumber represents a qualification match.
186
247
  def qualification?
187
248
  @type == :qualification
188
249
  end
189
250
 
251
+ # Public: Determine if MatchNumber represents a quarterfinal match.
190
252
  def quarterfinal?
191
253
  @type == :quarterfinal
192
254
  end
193
255
 
256
+ # Public: Determine if MatchNumber represents a semifinal match.
194
257
  def semifinal?
195
258
  @type == :semifinal
196
259
  end
197
260
 
261
+ # Public: Determine if MatchNumber represents a final match.
198
262
  def final?
199
263
  @type == :final
200
264
  end
201
265
 
266
+ # Public: Determine if MatchNumber represents a match of any elimination
267
+ # type.
202
268
  def elimination?
203
269
  ELIMINATION_TYPES.include?(@type)
204
270
  end
@@ -11,46 +11,101 @@ require "mongoid"
11
11
  require "frecon/mongoid/criteria"
12
12
 
13
13
  module FReCon
14
+ # Public: A base class designed to assist with creating MongoDB Models
15
+ # elsewhere in the project.
14
16
  class Model
17
+ # Public: Bootstraps inheritors of this class as working
18
+ # Models, also providing class methods for them to use.
19
+ #
20
+ # child - The class that is inheriting this class.
21
+ #
22
+ # Returns the result of bootstrapping the child.
15
23
  def self.inherited(child)
16
24
  child.class_eval do
25
+ # Include the various Mongoid modules that we want to use.
17
26
  include Mongoid::Document
18
27
  include Mongoid::Timestamps
19
28
  include Mongoid::Attributes::Dynamic
20
29
 
30
+ # Ensure that no invalid relations exist.
21
31
  validate :no_invalid_relations
22
32
 
23
33
  self.class_variable_set(:@@attributes, [])
24
34
 
35
+ # Public: Register a method as a routable relation method.
36
+ #
37
+ # Models can register relation methods that they have defined
38
+ # (e.g. team.robots) as routable methods. The Routes module reads
39
+ # these routable relations, and generates routes for them.
40
+ #
41
+ # method - A Symbol containing the name of the relation method.
42
+ # attribute - A String representing the attribute that the Routes
43
+ # module should route this method under.
44
+ #
45
+ # Examples
46
+ #
47
+ # # (Taken from the Team model)
48
+ # register_routable_relation :matches, "matches"
49
+ #
50
+ # Returns the result of pushing an object to class's attributes
51
+ # class variable.
25
52
  def self.register_routable_relation(method, attribute)
26
53
  self.class_variable_get(:@@attributes) << {method: method, type: :relation, attribute: attribute}
27
54
  end
28
55
 
56
+ # Public: Register a method as a routable attribute method.
57
+ #
58
+ # Models can register attribute methods that they have defined
59
+ # (e.g. team.number) as attribute methods. The Routes module reads
60
+ # these routable attributes, and generates routes for them.
61
+ #
62
+ # method - A Symbol containing the name of the attribute method.
63
+ # attribute - A String representing the attribute that the Routes
64
+ # module should route this method under.
65
+ #
66
+ # Returns the result of pushing an object to class's attributes
67
+ # class variable.
29
68
  def self.register_routable_attribute(method, attribute)
30
69
  self.class_variable_get(:@@attributes) << {method: method, type: :attribute, attribute: attribute}
31
70
  end
32
71
  end
33
72
  end
34
73
 
74
+ # Public: Gets the descendants for the Model class.
75
+ #
76
+ # Returns all of the descendants for the Model class.
35
77
  def self.descendants
36
- # Basically lists all of the models in this database.
37
- ObjectSpace.each_object(Class).select { |possibleChild| possibleChild < self }
78
+ ObjectSpace.each_object(Class).select { |possible_child| possible_child < self }
38
79
  end
39
80
 
81
+ # Public: Converts this Model to its associated Controller.
82
+ #
83
+ # Returns the associated Controller if it exists, else nil.
40
84
  def self.controller
41
85
  (self.name.pluralize + "Controller").constantize
42
86
  end
43
87
 
88
+ # Public: Validate that no invalid relations exist within this Model
44
89
  def no_invalid_relations
45
90
  # Get all of the belongs_to fields (ends with "_id" and not "_id" because that is the id).
46
- attributes.keys.select { |attribute| attribute.end_with?("_id") && attribute != "_id" }.each do |relation|
91
+ attributes.keys.select do |attribute|
92
+ attribute.end_with?("_id") && attribute != "_id"
93
+ end.each do |relation|
47
94
  # Get the model for the belongs_to association.
48
95
  model = "FReCon::".concat(relation.gsub(/_id\Z/, "").capitalize).constantize
49
- errors.add(relation.to_sym, "is invalid") if relation_invalid(model, send(relation))
96
+ errors.add(relation.to_sym, "is invalid") if relation_invalid?(model, send(relation))
50
97
  end
51
98
  end
52
99
 
53
- def relation_invalid(class_constant, id)
100
+ protected
101
+
102
+ # Internal: Determine if a relation is invalid.
103
+ #
104
+ # class_constant - The Model Class to test.
105
+ # id - The ID to check for extance.
106
+ #
107
+ # Returns true if the relation is invalid, false if not.
108
+ def relation_invalid?(class_constant, id)
54
109
  class_constant.find_by(id: id).nil?
55
110
  end
56
111
  end
@@ -10,6 +10,7 @@
10
10
  require "frecon/model"
11
11
 
12
12
  module FReCon
13
+ # Public: The Competition model.
13
14
  class Competition < Model
14
15
  field :location, type: String
15
16
  field :name, type: String
@@ -20,14 +21,17 @@ module FReCon
20
21
  validates :location, :name, presence: true
21
22
  validates :name, uniqueness: true
22
23
 
24
+ # Public: Get this Competition's Matches' Records
23
25
  def records
24
26
  Record.in match_id: matches.map(&:id)
25
27
  end
26
28
 
29
+ # Public: Get this Competition's Participations' Robots
27
30
  def robots
28
31
  Robot.in id: participations.map(&:robot_id)
29
32
  end
30
33
 
34
+ # Public: Get this Competition's Participations' Robots' Teams
31
35
  def teams
32
36
  Team.in id: robots.map(&:team_id)
33
37
  end
@@ -11,6 +11,7 @@ require "frecon/model"
11
11
  require "frecon/match_number"
12
12
 
13
13
  module FReCon
14
+ # Public: The Match model.
14
15
  class Match < Model
15
16
  field :number, type: MatchNumber
16
17
 
@@ -22,14 +23,17 @@ module FReCon
22
23
 
23
24
  validates :number, :competition_id, presence: true
24
25
 
26
+ # Public: Get this Match's Participations
25
27
  def participations
26
28
  Participation.in id: records.map(&:participation_id)
27
29
  end
28
30
 
31
+ # Public: Get this Match's Participations' Robots
29
32
  def robots
30
33
  Robot.in id: participations.map(&:robot_id)
31
34
  end
32
35
 
36
+ # Public: Get this Match's Participations' Robots' Teams
33
37
  def teams
34
38
  Team.in id: robots.map(&:team_id)
35
39
  end
@@ -10,6 +10,7 @@
10
10
  require "frecon/model"
11
11
 
12
12
  module FReCon
13
+ # Public: The Participation model.
13
14
  class Participation < Model
14
15
  belongs_to :robot
15
16
  belongs_to :competition
@@ -17,10 +18,12 @@ module FReCon
17
18
 
18
19
  validates :robot_id, :competition_id, presence: true
19
20
 
21
+ # Public: Get this Participation's Robot's Team
20
22
  def team
21
23
  robot.team
22
24
  end
23
25
 
26
+ # Public: Get this Participation's Competition's Matches
24
27
  def matches
25
28
  competition.matches
26
29
  end
@@ -11,6 +11,7 @@ require "frecon/model"
11
11
  require "frecon/position"
12
12
 
13
13
  module FReCon
14
+ # Public: The Record model.
14
15
  class Record < Model
15
16
  field :notes, type: String
16
17
  field :position, type: Position
@@ -20,14 +21,17 @@ module FReCon
20
21
 
21
22
  validates :position, :match_id, :participation_id, presence: true
22
23
 
24
+ # Public: Get this Record's Match's Competition
23
25
  def competition
24
26
  match.competition
25
27
  end
26
28
 
29
+ # Public: Get this Record's Participation's Robot
27
30
  def robot
28
31
  participation.robot
29
32
  end
30
33
 
34
+ # Public: Get this Record's Participation's Robot's Team
31
35
  def team
32
36
  participation.robot.team
33
37
  end
@@ -10,6 +10,7 @@
10
10
  require "frecon/model"
11
11
 
12
12
  module FReCon
13
+ # Public: The Robot model.
13
14
  class Robot < Model
14
15
  # This is an optional field we included for organization.
15
16
  field :name, type: String
@@ -19,14 +20,17 @@ module FReCon
19
20
 
20
21
  validates :team_id, presence: true
21
22
 
23
+ # Public: Get this Robot's Participations' Competitions
22
24
  def competitions
23
25
  Competition.in id: participations.map(&:competition_id)
24
26
  end
25
27
 
28
+ # Public: Get this Robot's Participations' Records
26
29
  def records
27
30
  Record.in participation_id: participations.map(&:id)
28
31
  end
29
32
 
33
+ # Public: Get this Robot's Participations' Records' Matches
30
34
  def matches
31
35
  Match.in id: records.map(&:match_id).uniq
32
36
  end
@@ -10,6 +10,7 @@
10
10
  require "frecon/model"
11
11
 
12
12
  module FReCon
13
+ # Public: The Team model.
13
14
  class Team < Model
14
15
  field :number, type: Integer
15
16
 
@@ -21,23 +22,31 @@ module FReCon
21
22
 
22
23
  validates :number, presence: true, uniqueness: true, numericality: { greater_than: 0 }
23
24
 
25
+ # Public: Find a team by number.
26
+ #
27
+ # team_number - An Integer to be used to compare.
28
+ #
29
+ # Returns a Team if one exists with the given number, otherwise nil.
24
30
  def self.number(team_number)
25
- # Team.find_by number: team_number
26
31
  find_by number: team_number
27
32
  end
28
33
 
34
+ # Public: Get this Team's Robots' Participations
29
35
  def participations
30
36
  Participation.in robot_id: robots.map(&:id)
31
37
  end
32
38
 
39
+ # Public: Get this Team's Robots' Participations' Competitions
33
40
  def competitions
34
41
  Competition.in id: participations.map(&:competition_id)
35
42
  end
36
43
 
44
+ # Public: Get this Team's Robots' Participations' Records
37
45
  def records
38
46
  Record.in participation_id: participations.map(&:id)
39
47
  end
40
48
 
49
+ # Public: Get this Team's Robots' Participations' Competitions' Matches
41
50
  def matches
42
51
  Match.in competition_id: competitions.map(&:id)
43
52
  end
@@ -9,24 +9,63 @@
9
9
 
10
10
  require "mongoid"
11
11
 
12
+ # Public: An extension for the Mongoid module.
12
13
  module Mongoid
14
+ # Public: A monkey-patch for the Mongoid::Criteria class which introduces
15
+ # a #psv_filter method.
13
16
  class Criteria
17
+ # Public: Filter by given PSV parameters.
18
+ #
19
+ # PSV is an introduced system that can be used within query strings to
20
+ # narrow a query. Since HTTP query strings can use "+" to act as spaces
21
+ # within a key-value pair, one can use these pluses to define nested
22
+ # query parameters when querying the database as in an indexing or
23
+ # showing request.
24
+ #
25
+ # psv_parameters - A Hash of PSV strings to comparison values.
26
+ #
27
+ # Examples
28
+ #
29
+ # Record.all.psv_filter({"participation robot team number" => "2503"})
30
+ # => #<Mongoid::Criteria ...>
31
+ #
32
+ # # Since each instance of Record has a :team shortcut method,
33
+ # # we can just filter it like so.
34
+ # Record.all.psv_filter({"team number" => "2503"})
35
+ # => #<Mongoid::Criteria ...>
36
+ #
37
+ # Returns a filtered version of self.
14
38
  def psv_filter(psv_parameters = {})
15
39
  collection = self
16
40
 
41
+ # Iterate through the Hash of query Strings to values, filtering using
42
+ # each pairing where the key is the query specifier and the value is the
43
+ # value that the last word in the query specifier is equal to. For
44
+ # multiple key-value pairs, just keep adding specifiers to the chain.
17
45
  psv_parameters.each do |psv_string, comparison_value|
18
- psv_keys = psv_string.split(" ").map do |psv_key|
46
+ # Split the query String, and convert each subsequent String
47
+ # to a symbol. Then, reverse the array to make it easy to perform
48
+ # an inside-out operation.
49
+ psv_keys = psv_string.split(/\W/).map do |psv_key|
19
50
  psv_key.to_sym
20
51
  end.reverse
21
52
 
53
+ # Get the final key in the query string.
22
54
  comparison_key = psv_keys.shift
23
55
 
56
+ # Create a comparison hash to be used to compare <attribute> to
57
+ # <expected value>.
24
58
  if comparison_value.length == 0 || comparison_value == "__nil__"
25
59
  comparison_hash = {comparison_key => nil}
26
60
  else
27
61
  comparison_hash = {comparison_key => comparison_value}
28
62
  end
29
63
 
64
+ # Each of the subsequent keys should be a model name. Generate a string
65
+ # corresponding to the "<model>" + "_id" for use as the comparison key,
66
+ # and find the model class by generating a constant.
67
+ #
68
+ # Then, nest a comparison around the current comparison hash.
30
69
  psv_keys.each do |model|
31
70
  model_id = (model.to_s + '_id').to_sym
32
71
  model_class = ("FReCon::" + model.to_s.capitalize).constantize
@@ -34,9 +73,11 @@ module Mongoid
34
73
  comparison_hash = {model_id => model_class.in(comparison_hash).map(&:id)}
35
74
  end
36
75
 
76
+ # Finally, complete this nested comparison.
37
77
  collection = collection.in(comparison_hash)
38
78
  end
39
79
 
80
+ # Return the fully-filtered collection.
40
81
  collection
41
82
  end
42
83
  end
@@ -10,20 +10,47 @@
10
10
  require "frecon/base"
11
11
 
12
12
  module FReCon
13
+ # Public: A wrapper to handle converting team positions and storing them.
13
14
  class Position
14
- attr_reader :alliance, :number
15
-
16
- # MongoDB compatibility methods.
15
+ # Public: The alliance part of the position
16
+ #
17
+ # Examples
18
+ #
19
+ # position = Position.new('r2')
20
+ # position.alliance
21
+ # # => :red
22
+ attr_reader :alliance
23
+
24
+ # Public: The slot part of the position.
25
+ #
26
+ # Examples
27
+ #
28
+ # position = Position.new('r2')
29
+ # position.number
30
+ # # => 2
31
+ attr_reader :number
32
+
33
+ # Public: Convert a stored position to a Position object.
34
+ #
35
+ # object - String representation of a position (mongoized)
36
+ #
37
+ # Returns Position parsed from object.
17
38
  def self.demongoize(object)
18
- # `object' should *always* be a string (since MatchNumber#mongoize returns a
39
+ # `object' should *always* be a String (since MatchNumber#mongoize returns a
19
40
  # String which is what is stored in the database)
20
41
  raise ArgumentError, "`object' must be a String" unless object.is_a?(String)
21
42
 
22
43
  Position.new(object)
23
44
  end
24
45
 
25
- # Allows passing a String or Hash instead of a Position.
26
- # i.e. record.position = "r3"
46
+ # Public: Convert a Position object to a storable string representation.
47
+ #
48
+ # object - A Position, String, or Hash. If Position, run #mongoize on it.
49
+ # If String, create a new Position object for it, then run
50
+ # #mongoize on it. If Hash, convert its keys to symbols, then
51
+ # pull out the :alliance and :number keys to generate a Position.
52
+ #
53
+ # Returns String containing the mongo-ready value for the representation.
27
54
  def self.mongoize(object)
28
55
  case object
29
56
  when Position
@@ -39,7 +66,15 @@ module FReCon
39
66
  end
40
67
  end
41
68
 
42
- # Used for queries.
69
+ # Public: Convert a Position object to a storable string representation for
70
+ # queries.
71
+ #
72
+ # object - A Position, String, or Hash. If Position, run #mongoize on it.
73
+ # If String, create a new Position object for it, then run
74
+ # #mongoize on it. If Hash, convert its keys to symbols, then
75
+ # pull out the :alliance and :number keys to generate a Position.
76
+ #
77
+ # Returns String containing the mongo-ready value for the representation.
43
78
  def self.evolve(object)
44
79
  case object
45
80
  when Position
@@ -55,6 +90,9 @@ module FReCon
55
90
  end
56
91
  end
57
92
 
93
+ # Public: Convert to a storable string representation.
94
+ #
95
+ # Returns String representing the Position's data.
58
96
  def mongoize
59
97
  to_s
60
98
  end
@@ -112,14 +150,19 @@ module FReCon
112
150
  end
113
151
  end
114
152
 
153
+ # Public: Convert to a String.
154
+ #
155
+ # Returns String representing the position data.
115
156
  def to_s
116
157
  "#{@alliance[0]}#{@number}"
117
158
  end
118
159
 
160
+ # Public: Determine if Position is on blue alliance.
119
161
  def is_blue?
120
162
  @alliance == :blue
121
163
  end
122
164
 
165
+ # Public: Determine if Position is on red alliance.
123
166
  def is_red?
124
167
  @alliance == :red
125
168
  end
@@ -9,9 +9,17 @@
9
9
 
10
10
  require "json"
11
11
 
12
+ # Public: A class representing errors that emanate from request handling.
12
13
  class RequestError < StandardError
14
+ # Public: The Array or Integer representing what can be returned from the
15
+ # request handler.
13
16
  attr_reader :return_value
14
17
 
18
+ # Public: Initialize a RequestError.
19
+ #
20
+ # code - An Integer representing the HTTP status code.
21
+ # message - A String representing the error message.
22
+ # context - An Object containing any necessary debugging values.
15
23
  def initialize(code, message = nil, context = nil)
16
24
  @code = code
17
25
  @message = message
@@ -10,7 +10,13 @@
10
10
  require "frecon/controllers"
11
11
 
12
12
  module FReCon
13
+ # Public: A module containing all of the routes.
13
14
  module Routes
15
+ # Public: Set up basic resource route handlers.
16
+ #
17
+ # base - Sinatra::Application to register the routes under.
18
+ # name - String containing the model name.
19
+ # controller - Controller-like object that contains key methods.
14
20
  def self.resource_routes(base, name, controller)
15
21
  base.post "/#{name}" do
16
22
  begin
@@ -53,6 +59,11 @@ module FReCon
53
59
  end
54
60
  end
55
61
 
62
+ # Public: Set up basic attribute route handlers.
63
+ #
64
+ # base - Sinatra::Application to register the routes under.
65
+ # name - String containing the model name.
66
+ # controller - Controller-like object.
56
67
  def self.attribute_routes(base, name, controller)
57
68
  model = controller.model
58
69
 
@@ -82,6 +93,9 @@ module FReCon
82
93
  end
83
94
  end
84
95
 
96
+ # Public: Bootstrap the routes into inclusors of this module.
97
+ #
98
+ # base - The child that included this module (should be a Sinatra App)
85
99
  def self.included(base)
86
100
  resource_routes base, "teams", TeamsController
87
101
  resource_routes base, "competitions", CompetitionsController
@@ -16,6 +16,7 @@ require "frecon/routes"
16
16
  require "frecon/controllers"
17
17
 
18
18
  module FReCon
19
+ # Public: The Sinatra web server.
19
20
  class Server < Sinatra::Base
20
21
  include Routes
21
22
 
@@ -23,23 +24,45 @@ module FReCon
23
24
  content_type "application/json"
24
25
  end
25
26
 
27
+ # Public: Start the Server.
28
+ #
29
+ # keyword_arguments - The Hash of arguments to use.
30
+ # :configuration - The Configuration to use when
31
+ # setting up the server.
32
+ #
33
+ # Returns the result of starting the server.
26
34
  def self.start(**keyword_arguments)
27
35
  run!(**keyword_arguments)
28
36
  end
29
37
 
30
38
  protected
31
39
 
40
+ # Internal: Set up the server.
41
+ #
42
+ # Sets the various Thin and Sinatra options, and sets up the database.
43
+ #
44
+ # :configuration - The Configuration to use when starting the server.
45
+ #
46
+ # Returns the result of setting up the database.
32
47
  def self.setup!(configuration: Configuration.construct!)
48
+ # Set the Thin and Sinatra options.
33
49
  set :server, %w[thin HTTP webrick]
34
50
  set :bind, configuration["frecon"]["server"]["host"]
35
51
  set :port, configuration["frecon"]["server"]["port"]
36
52
  set :environment, configuration["frecon"]["server"]["environment"]
37
53
 
54
+ # Grab out the mongoid configuration.
38
55
  mongoid = configuration["frecon"]["database"]["mongoid"]
39
56
 
40
- Database.setup(environment: environment, mongoid: mongoid)
57
+ # Set up the database.
58
+ Database.setup(environment, mongoid)
41
59
  end
42
60
 
61
+ # Internal: Set up the server and start it.
62
+ #
63
+ # keyword_arguments - The Hash of arguments to use.
64
+ # :configuration - The Configuration to use when
65
+ # setting up the server.
43
66
  def self.run!(**keyword_arguments)
44
67
  setup!(**keyword_arguments)
45
68
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: frecon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Craig
@@ -71,6 +71,48 @@ dependencies:
71
71
  - - "~>"
72
72
  - !ruby/object:Gem::Version
73
73
  version: '0.13'
74
+ - !ruby/object:Gem::Dependency
75
+ name: yard
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '0.8'
81
+ type: :development
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '0.8'
88
+ - !ruby/object:Gem::Dependency
89
+ name: yard-tomdoc
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '0.7'
95
+ type: :development
96
+ prerelease: false
97
+ version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '0.7'
102
+ - !ruby/object:Gem::Dependency
103
+ name: yard-mongoid
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '0.0'
109
+ type: :development
110
+ prerelease: false
111
+ version_requirements: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '0.0'
74
116
  description: A JSON API for scouting FRC competitions that manages the database for
75
117
  the user.
76
118
  email: frc-frecon@googlegroups.com
@@ -79,6 +121,8 @@ executables:
79
121
  extensions: []
80
122
  extra_rdoc_files: []
81
123
  files:
124
+ - Gemfile
125
+ - Rakefile
82
126
  - bin/frecon
83
127
  - config/default.yml
84
128
  - lib/frecon.rb