sports_data_api 0.2.1 → 0.2.2
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 +4 -4
- data/.travis.yml +2 -1
- data/lib/sports_data_api.rb +1 -0
- data/lib/sports_data_api/ncaafb.rb +84 -0
- data/lib/sports_data_api/ncaafb/broadcast.rb +14 -0
- data/lib/sports_data_api/ncaafb/division.rb +14 -0
- data/lib/sports_data_api/ncaafb/game.rb +78 -0
- data/lib/sports_data_api/ncaafb/games.rb +28 -0
- data/lib/sports_data_api/ncaafb/injuries.rb +9 -0
- data/lib/sports_data_api/ncaafb/player.rb +13 -0
- data/lib/sports_data_api/ncaafb/season.rb +25 -0
- data/lib/sports_data_api/ncaafb/team.rb +86 -0
- data/lib/sports_data_api/ncaafb/teams.rb +101 -0
- data/lib/sports_data_api/ncaafb/venue.rb +21 -0
- data/lib/sports_data_api/ncaafb/weather.rb +16 -0
- data/lib/sports_data_api/ncaafb/week.rb +18 -0
- data/lib/sports_data_api/version.rb +1 -1
- data/spec/cassettes/sports_data_api_ncaafb.yml +117 -0
- data/spec/cassettes/sports_data_api_ncaafb_broadcast.yml +1293 -0
- data/spec/cassettes/sports_data_api_ncaafb_game.yml +21491 -0
- data/spec/cassettes/sports_data_api_ncaafb_games.yml +1452 -0
- data/spec/cassettes/sports_data_api_ncaafb_season.yml +18150 -0
- data/spec/cassettes/sports_data_api_ncaafb_team.yml +884 -0
- data/spec/cassettes/sports_data_api_ncaafb_team_hierarchy.yml +1819 -0
- data/spec/cassettes/sports_data_api_ncaafb_venue.yml +1293 -0
- data/spec/cassettes/sports_data_api_ncaafb_weather.yml +1293 -0
- data/spec/cassettes/sports_data_api_nfl_week.yml +18148 -1
- data/spec/lib/sports_data_api/ncaafb/broadcast_spec.rb +19 -0
- data/spec/lib/sports_data_api/ncaafb/game_spec.rb +157 -0
- data/spec/lib/sports_data_api/ncaafb/games_spec.rb +21 -0
- data/spec/lib/sports_data_api/ncaafb/season_spec.rb +31 -0
- data/spec/lib/sports_data_api/ncaafb/team_spec.rb +74 -0
- data/spec/lib/sports_data_api/ncaafb/teams_spec.rb +76 -0
- data/spec/lib/sports_data_api/ncaafb/venue_spec.rb +27 -0
- data/spec/lib/sports_data_api/ncaafb/weather_spec.rb +21 -0
- data/spec/lib/sports_data_api/ncaafb/week_spec.rb +21 -0
- data/spec/lib/sports_data_api/ncaafb_spec.rb +42 -0
- data/spec/spec_helper.rb +2 -0
- data/sports_data_api.gemspec +1 -1
- metadata +56 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 955753065636d3442bf1601d37c3281fd20b3c8b
|
4
|
+
data.tar.gz: 591bae0973f4af66eba5a8449163383839eb841f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa67111339cc2fceec855ed582f39888f04cffb0ecd8d5af4aa68be62e0197726de446f69e299e99d073c83adfc70a8db6f56a27e886f704933c8c45c938da07
|
7
|
+
data.tar.gz: 7b20aa4149a074f663926b5447c48a7f4d82ac81f500df04b842888362b3124c95391dd0a2988fcb763cab1c9e69fc00ce6bab7d7c0192c0784f372ec8a5bb5a
|
data/.travis.yml
CHANGED
data/lib/sports_data_api.rb
CHANGED
@@ -48,6 +48,7 @@ module SportsDataApi
|
|
48
48
|
|
49
49
|
autoload :Stats, File.join(LIBRARY_PATH, 'stats')
|
50
50
|
autoload :Nfl, File.join(LIBRARY_PATH, 'nfl')
|
51
|
+
autoload :Ncaafb, File.join(LIBRARY_PATH, 'ncaafb')
|
51
52
|
autoload :Nba, File.join(LIBRARY_PATH, 'nba')
|
52
53
|
autoload :Mlb, File.join(LIBRARY_PATH, 'mlb')
|
53
54
|
autoload :Nhl, File.join(LIBRARY_PATH, 'nhl')
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module SportsDataApi
|
2
|
+
module Ncaafb
|
3
|
+
class Exception < ::Exception
|
4
|
+
end
|
5
|
+
|
6
|
+
DIR = File.join(File.dirname(__FILE__), 'ncaafb')
|
7
|
+
BASE_URL = 'http://api.sportsdatallc.org/ncaafb-%{access_level}%{version}'
|
8
|
+
DEFAULT_VERSION = 1
|
9
|
+
SPORT = :ncaafb
|
10
|
+
|
11
|
+
autoload :Division, File.join(DIR, 'division')
|
12
|
+
autoload :Team, File.join(DIR, 'team')
|
13
|
+
autoload :Teams, File.join(DIR, 'teams')
|
14
|
+
autoload :Player, File.join(DIR, 'player')
|
15
|
+
autoload :Game, File.join(DIR, 'game')
|
16
|
+
autoload :Games, File.join(DIR, 'games')
|
17
|
+
autoload :Week, File.join(DIR, 'week')
|
18
|
+
autoload :Season, File.join(DIR, 'season')
|
19
|
+
autoload :Venue, File.join(DIR, 'venue')
|
20
|
+
autoload :Broadcast, File.join(DIR, 'broadcast')
|
21
|
+
autoload :Weather, File.join(DIR, 'weather')
|
22
|
+
|
23
|
+
##
|
24
|
+
# Fetches Ncaafb season schedule for a given year and season
|
25
|
+
def self.schedule(year, season, version = DEFAULT_VERSION)
|
26
|
+
season = season.to_s.upcase.to_sym
|
27
|
+
raise SportsDataApi::Ncaafb::Exception.new("#{season} is not a valid season") unless Season.valid?(season)
|
28
|
+
|
29
|
+
response = self.response_json(version, "/#{year}/#{season}/schedule.json")
|
30
|
+
|
31
|
+
return Season.new(response)
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Fetches Ncaafb boxscore for a given game
|
36
|
+
def self.boxscore(year, season, week, home, away, version = DEFAULT_VERSION)
|
37
|
+
season = season.to_s.upcase.to_sym
|
38
|
+
raise SportsDataApi::Ncaafb::Exception.new("#{season} is not a valid season") unless Season.valid?(season)
|
39
|
+
|
40
|
+
response = self.response_json(version, "/#{year}/#{season}/#{week}/#{away}/#{home}/boxscore.json")
|
41
|
+
|
42
|
+
return Game.new(year, season, week, response)
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Fetches statistics for a given Ncaafb game
|
47
|
+
def self.game_statistics(year, season, week, home, away, version = DEFAULT_VERSION)
|
48
|
+
season = season.to_s.upcase.to_sym
|
49
|
+
raise SportsDataApi::Ncaafb::Exception.new("#{season} is not a valid season") unless Season.valid?(season)
|
50
|
+
|
51
|
+
response = self.response_json(version, "/#{year}/#{season}/#{week}/#{away}/#{home}/statistics.json")
|
52
|
+
|
53
|
+
return Game.new(year, season, week, response)
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Fetches all Ncaafb teams
|
58
|
+
def self.teams(division, version = DEFAULT_VERSION)
|
59
|
+
raise SportsDataApi::Ncaafb::Exception.new("#{division} is not a valid season") unless Division.valid?(division)
|
60
|
+
response = self.response_json(version, "/teams/#{division}/hierarchy.json")
|
61
|
+
|
62
|
+
return Teams.new(response)
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Fetches Ncaafb weekly schedule for a given year, season and week
|
67
|
+
def self.weekly(year, season, week, version = DEFAULT_VERSION)
|
68
|
+
season = season.to_s.upcase.to_sym
|
69
|
+
raise SportsDataApi::Ncaafb::Exception.new("#{season} is not a valid season") unless Season.valid?(season)
|
70
|
+
|
71
|
+
response = self.response_json(version, "/#{year}/#{season}/#{week}/schedule.json")
|
72
|
+
|
73
|
+
return Games.new(year, season, week, response)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def self.response_json(version, url)
|
79
|
+
base_url = BASE_URL % { access_level: SportsDataApi.access_level(SPORT), version: version }
|
80
|
+
response = SportsDataApi.generic_request("#{base_url}#{url}", SPORT)
|
81
|
+
MultiJson.load(response.to_s)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SportsDataApi
|
2
|
+
module Ncaafb
|
3
|
+
class Broadcast
|
4
|
+
attr_reader :network, :satellite, :internet
|
5
|
+
def initialize(broadcast_hash)
|
6
|
+
if broadcast_hash
|
7
|
+
@network = broadcast_hash['network']
|
8
|
+
@satellite = broadcast_hash['satellite'] || ''
|
9
|
+
@internet = broadcast_hash['internet']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SportsDataApi
|
2
|
+
module Ncaafb
|
3
|
+
class Division
|
4
|
+
##
|
5
|
+
# Check if the requested division is a valid
|
6
|
+
# Ncaafb division type.
|
7
|
+
#
|
8
|
+
# The only valid types are: :REG
|
9
|
+
def self.valid?(division)
|
10
|
+
[:FBS,:FCS, :D2, :D3, :NAIA, :USCAA].include?(division)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module SportsDataApi
|
2
|
+
module Ncaafb
|
3
|
+
class Game
|
4
|
+
attr_reader :id, :scheduled, :home, :home_team, :away,
|
5
|
+
:away_team, :status, :quarter, :clock, :venue, :broadcast, :weather,
|
6
|
+
:year, :season, :week
|
7
|
+
|
8
|
+
def initialize(year, season, week, game_hash)
|
9
|
+
@year = year
|
10
|
+
@season = season
|
11
|
+
@week = week
|
12
|
+
|
13
|
+
@id = game_hash['id']
|
14
|
+
@scheduled = Time.parse game_hash['scheduled']
|
15
|
+
@home = game_hash['home'] || game_hash['home_team']['id']
|
16
|
+
@away = game_hash['away'] || game_hash['away_team']['id']
|
17
|
+
@status = game_hash['status']
|
18
|
+
@quarter = game_hash['quarter'].to_i
|
19
|
+
@clock = game_hash['clock']
|
20
|
+
|
21
|
+
@home_team = Team.new(game_hash['home_team'])
|
22
|
+
@away_team = Team.new(game_hash['away_team'])
|
23
|
+
@venue = Venue.new(game_hash['venue'])
|
24
|
+
@broadcast = Broadcast.new(game_hash['broadcast'])
|
25
|
+
@weather = Weather.new(game_hash['weather'])
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Wrapper for Ncaafb.statistics
|
30
|
+
def statistics
|
31
|
+
Ncaafb.game_statistics(year, season, week, home, away, 1)
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Wrapper for Ncaafb.summary
|
36
|
+
# TODO
|
37
|
+
def summary
|
38
|
+
raise NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Wrapper for Ncaafb.pbp (Ncaafb.play_by_play)
|
43
|
+
# TODO
|
44
|
+
def pbp
|
45
|
+
raise NotImplementedError
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Wrapper for Ncaafb.boxscore these helper methods are used
|
50
|
+
# to provide similar functionality as the links attribute
|
51
|
+
# found in the weekly schedule example.
|
52
|
+
def boxscore
|
53
|
+
Ncaafb.boxscore(year, season, week, home, away, 1)
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Wrapper for Ncaafb.roster
|
58
|
+
# TODO
|
59
|
+
def roster
|
60
|
+
raise NotImplementedError
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Wrapper for Ncaafb.injuries
|
65
|
+
# TODO
|
66
|
+
def injuries
|
67
|
+
raise NotImplementedError
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Wrapper for Ncaafb.depthchart
|
72
|
+
# TODO
|
73
|
+
def depthchart
|
74
|
+
raise NotImplementedError
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module SportsDataApi
|
2
|
+
module Ncaafb
|
3
|
+
class Games
|
4
|
+
include Enumerable
|
5
|
+
attr_reader :games, :year, :season, :week
|
6
|
+
|
7
|
+
def initialize(year, season, week, games_hash)
|
8
|
+
@year = year
|
9
|
+
@season = season
|
10
|
+
@week = week
|
11
|
+
|
12
|
+
@games = games_hash['games'].map do |game_hash|
|
13
|
+
Game.new(@year, @season, @week, game_hash)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def each &block
|
18
|
+
@games.each do |game|
|
19
|
+
if block_given?
|
20
|
+
block.call game
|
21
|
+
else
|
22
|
+
yield game
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SportsDataApi
|
2
|
+
module Ncaafb
|
3
|
+
class Player
|
4
|
+
def initialize(json)
|
5
|
+
player_ivar = self.instance_variable_set("@player", {})
|
6
|
+
self.class.class_eval { attr_reader :"player" }
|
7
|
+
json.each_pair do | attr_name, attr_value|
|
8
|
+
player_ivar[attr_name.to_sym] = attr_value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module SportsDataApi
|
2
|
+
module Ncaafb
|
3
|
+
class Season
|
4
|
+
attr_reader :year, :type, :weeks
|
5
|
+
|
6
|
+
def initialize(season_hash)
|
7
|
+
@weeks = []
|
8
|
+
@year = season_hash['season']
|
9
|
+
@type = season_hash['type'].to_sym
|
10
|
+
@weeks = season_hash['weeks'].map do |week_hash|
|
11
|
+
Week.new(@year, @type, week_hash)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Check if the requested season is a valid
|
17
|
+
# Ncaafb season type.
|
18
|
+
#
|
19
|
+
# The only valid types are: :REG
|
20
|
+
def self.valid?(season)
|
21
|
+
[:REG].include?(season)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module SportsDataApi
|
2
|
+
module Ncaafb
|
3
|
+
class Team
|
4
|
+
attr_reader :id, :name, :conference, :division, :market, :remaining_challenges,
|
5
|
+
:remaining_timeouts, :score, :quarters, :venue, :points, :statistics, :players, :subdivision
|
6
|
+
|
7
|
+
def initialize(team_hash, conference = nil, division = nil, subdivision = nil)
|
8
|
+
if team_hash
|
9
|
+
@id = team_hash['id']
|
10
|
+
@name = team_hash['name']
|
11
|
+
@conference = conference
|
12
|
+
@division = division
|
13
|
+
@subdivision = subdivision
|
14
|
+
@market = team_hash['market']
|
15
|
+
@remaining_challenges = team_hash['remaining_challenges']
|
16
|
+
@remaining_timeouts = team_hash['remaining_timeouts']
|
17
|
+
@quarters = []
|
18
|
+
if team_hash['scoring']
|
19
|
+
team_hash['scoring'].each do |scoring_hash|
|
20
|
+
@quarters[scoring_hash['quarter']-1] = scoring_hash['points']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@quarters = @quarters.fill(0, @quarters.size, 4 - @quarters.size)
|
24
|
+
|
25
|
+
# Parse the Venue data if it exists
|
26
|
+
if team_hash.key?('venue')
|
27
|
+
@venue = Venue.new(team_hash['venue'])
|
28
|
+
end
|
29
|
+
|
30
|
+
if team_hash['statistics']
|
31
|
+
@statistics = parse_team_statistics(team_hash['statistics'])
|
32
|
+
@players = parse_player_statistics(team_hash['statistics'])
|
33
|
+
end
|
34
|
+
@points = team_hash['points'] || score
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sum the score of each quarter
|
39
|
+
def score
|
40
|
+
@quarters.inject(:+)
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Compare the Team with another team
|
45
|
+
def ==(other)
|
46
|
+
# Must have an id to compare
|
47
|
+
return false if self.id.nil?
|
48
|
+
|
49
|
+
if other.is_a? SportsDataApi::Ncaafb::Team
|
50
|
+
return false if other.id.nil?
|
51
|
+
self.id === other.id
|
52
|
+
elsif other.is_a? Symbol
|
53
|
+
self.id.to_sym === other
|
54
|
+
else
|
55
|
+
super(other)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def parse_team_statistics(stats_hash)
|
62
|
+
stats = {}
|
63
|
+
stats_hash.keys.each do |key|
|
64
|
+
stats[key] = stats_hash[key]['team']
|
65
|
+
end
|
66
|
+
stats
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_player_statistics(stats_hash)
|
70
|
+
players = []
|
71
|
+
stats_hash.keys.each do |key|
|
72
|
+
next if !stats_hash[key]['players']
|
73
|
+
stats_hash[key]['players'].each do |p|
|
74
|
+
player = players.select{|a| a['id'] == p['id']}.first || {}
|
75
|
+
players << player if !players.select{|a| a['id'] == p['id']}.first
|
76
|
+
['id', 'name', 'jersey', 'position'].each do |k|
|
77
|
+
player[k] = p.delete(k)
|
78
|
+
end
|
79
|
+
player[key] = p
|
80
|
+
end
|
81
|
+
end
|
82
|
+
players
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module SportsDataApi
|
2
|
+
module Ncaafb
|
3
|
+
class Teams
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :conferences, :subdivisions, :divisions
|
7
|
+
|
8
|
+
def initialize(teams_hash)
|
9
|
+
@teams = []
|
10
|
+
@allowed_divisions = []
|
11
|
+
@allowed_conferences = []
|
12
|
+
@allowed_subdivisions = []
|
13
|
+
did = teams_hash['id']
|
14
|
+
teams_hash['conferences'].each do |conference_hash|
|
15
|
+
# Conference ID. e.g ACC
|
16
|
+
cid = conference_hash['id']
|
17
|
+
@teams << Array(conference_hash['teams']).map { |team_hash| Team.new(team_hash, cid) }
|
18
|
+
|
19
|
+
# If Sub Division
|
20
|
+
Array(conference_hash['subdivisions']).each do |subdivision|
|
21
|
+
# SUB Division ID, e.g., ACC-ATLANTIC
|
22
|
+
sdid = subdivision['id']
|
23
|
+
@teams << subdivision['teams'].map { |team_hash| Team.new(team_hash, cid, did, sdid) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
@teams.flatten!
|
28
|
+
|
29
|
+
uniq_conferences = @teams.map { |team| team.conference.upcase }.uniq
|
30
|
+
@allowed_conferences = concat_allowed_methods(uniq_conferences)
|
31
|
+
@conferences = uniq_conferences.map(&:to_sym)
|
32
|
+
|
33
|
+
@divisions = select_divisions_by_type(:division)
|
34
|
+
@subdivisions = select_divisions_by_type(:subdivision)
|
35
|
+
|
36
|
+
@allowed_divisions.concat(concat_allowed_methods(@divisions))
|
37
|
+
@allowed_subdivisions.concat(concat_allowed_methods(@subdivisions))
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
def [](search_index)
|
43
|
+
found_index = @teams.index(search_index)
|
44
|
+
unless found_index.nil?
|
45
|
+
@teams[found_index]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
#
|
51
|
+
def respond_to?(method)
|
52
|
+
@allowed_conferences.include?(method) || @allowed_divisions.include?(method) || @allowed_subdivisions
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
#
|
57
|
+
def method_missing(method, *args, &block)
|
58
|
+
case true
|
59
|
+
when @allowed_conferences.include?(method)
|
60
|
+
select_by(:conference, method)
|
61
|
+
when @allowed_divisions.include?(method)
|
62
|
+
select_by(:division, method)
|
63
|
+
when @allowed_subdivisions.include?(method)
|
64
|
+
select_by(:subdivision, method)
|
65
|
+
else
|
66
|
+
super(method, *args, &block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
##
|
73
|
+
# Make the class Enumerable
|
74
|
+
def each(&block)
|
75
|
+
@teams.each do |team|
|
76
|
+
if block_given?
|
77
|
+
block.call team
|
78
|
+
else
|
79
|
+
yield team
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
private
|
84
|
+
|
85
|
+
def select_by(type, method)
|
86
|
+
normalize_method = method.downcase.to_sym
|
87
|
+
@teams.select do |team|
|
88
|
+
normalize_method == team.send(type).to_s.downcase.to_sym
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def select_divisions_by_type(division_type)
|
93
|
+
(@teams.map { |team| team.send(division_type).to_s.upcase.to_sym }.uniq - [:"", nil])
|
94
|
+
end
|
95
|
+
|
96
|
+
def concat_allowed_methods(list)
|
97
|
+
list.map { |str| str.to_sym }.concat(list.map { |str| str.downcase.to_sym })
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|