football-butler 1.1.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/football/butler.rb +10 -0
- data/lib/football/butler/api.rb +8 -16
- data/lib/football/butler/apifootball/base_apifootball.rb +17 -0
- data/lib/football/butler/apifootball/competitions.rb +30 -0
- data/lib/football/butler/apifootball/countries.rb +20 -0
- data/lib/football/butler/apifootball/events.rb +61 -0
- data/lib/football/butler/apifootball/head_to_head.rb +30 -0
- data/lib/football/butler/apifootball/lineups.rb +22 -0
- data/lib/football/butler/apifootball/odds.rb +25 -0
- data/lib/football/butler/apifootball/players.rb +27 -0
- data/lib/football/butler/apifootball/predictions.rb +28 -0
- data/lib/football/butler/apifootball/standings.rb +23 -0
- data/lib/football/butler/apifootball/statistics.rb +22 -0
- data/lib/football/butler/apifootball/teams.rb +29 -0
- data/lib/football/butler/apifootball/top_scorers.rb +22 -0
- data/lib/football/butler/areas.rb +15 -24
- data/lib/football/butler/base.rb +47 -17
- data/lib/football/butler/competitions.rb +31 -44
- data/lib/football/butler/configuration.rb +166 -11
- data/lib/football/butler/countries.rb +10 -0
- data/lib/football/butler/events.rb +10 -0
- data/lib/football/butler/football_data/areas.rb +40 -0
- data/lib/football/butler/football_data/competitions.rb +72 -0
- data/lib/football/butler/football_data/head_to_head.rb +27 -0
- data/lib/football/butler/football_data/lineups.rb +32 -0
- data/lib/football/butler/football_data/matches.rb +110 -0
- data/lib/football/butler/football_data/odds.rb +27 -0
- data/lib/football/butler/football_data/players.rb +20 -0
- data/lib/football/butler/football_data/scorers.rb +23 -0
- data/lib/football/butler/football_data/standings.rb +47 -0
- data/lib/football/butler/football_data/teams.rb +39 -0
- data/lib/football/butler/head_to_head.rb +27 -0
- data/lib/football/butler/lineups.rb +17 -0
- data/lib/football/butler/matches.rb +47 -83
- data/lib/football/butler/odds.rb +17 -0
- data/lib/football/butler/players.rb +21 -0
- data/lib/football/butler/predictions.rb +16 -0
- data/lib/football/butler/scorers.rb +17 -0
- data/lib/football/butler/standings.rb +15 -29
- data/lib/football/butler/statistics.rb +16 -0
- data/lib/football/butler/teams.rb +14 -23
- data/lib/football/butler/top_scorers.rb +10 -0
- data/lib/football/butler/version.rb +5 -2
- metadata +40 -7
@@ -1,35 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'football/butler/apifootball/countries'
|
3
|
+
require 'football/butler/football_data/areas'
|
2
4
|
|
3
5
|
module Football
|
4
6
|
module Butler
|
5
7
|
class Areas < Base
|
6
|
-
PATH = :areas
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
Api.get(path: path)
|
14
|
-
end
|
15
|
-
|
16
|
-
## AREAS
|
17
|
-
# v2/areas
|
18
|
-
def self.all(result: PATH)
|
19
|
-
Api.get(path: PATH, result: result)
|
20
|
-
end
|
9
|
+
class << self
|
10
|
+
## AREA / COUNTRY
|
11
|
+
def by_id(id:)
|
12
|
+
api_switch_method(__method__, { id: id })
|
13
|
+
end
|
21
14
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
def self.by_name(name:)
|
27
|
-
areas = all
|
28
|
-
return areas if areas.is_a?(Hash) && areas.with_indifferent_access.dig('message')
|
29
|
-
area = areas&.detect { |area| area['name'] == name }
|
30
|
-
return not_found_result(name) unless area
|
15
|
+
## AREAS / COUNTRIES
|
16
|
+
def all(result: api_switch_result)
|
17
|
+
api_switch_method(__method__, { result: result })
|
18
|
+
end
|
31
19
|
|
32
|
-
|
20
|
+
## ADDITIONAL
|
21
|
+
def by_name(name:)
|
22
|
+
api_switch_method(__method__, { name: name })
|
23
|
+
end
|
33
24
|
end
|
34
25
|
end
|
35
26
|
end
|
data/lib/football/butler/base.rb
CHANGED
@@ -3,30 +3,19 @@
|
|
3
3
|
module Football
|
4
4
|
module Butler
|
5
5
|
class Base
|
6
|
-
|
7
|
-
|
8
|
-
MSG_REACHED_LIMIT = 'You reached your request limit.' # 429
|
9
|
-
MSG_INVALID_CONFIG = 'Invalid Configuration, check empty api_token / api_endpoint!'
|
6
|
+
MSG_REACHED_LIMIT = 'You reached your request limit.' # code: 429
|
7
|
+
MSG_INVALID_CONFIG = 'Invalid Configuration, check empty api_token or empty / invalid api_endpoint!'
|
10
8
|
|
11
9
|
class << self
|
12
|
-
# MESSAGES
|
13
|
-
def invalid_token?(response)
|
14
|
-
response.dig('message') ? response['message'] == MSG_INVALID_TOKEN : false
|
15
|
-
end
|
16
|
-
|
17
|
-
def resource_not_found?(response)
|
18
|
-
response.dig('message') ? response['message'] == MSG_NOT_EXIST : false
|
19
|
-
end
|
20
10
|
|
11
|
+
# MESSAGES
|
21
12
|
def reached_limit?(response)
|
13
|
+
return false if !response.is_a?(Hash) &&
|
14
|
+
(response.respond_to?(:parsed_response) &&
|
15
|
+
!response.parsed_response.is_a?(Hash))
|
22
16
|
response.dig('message') ? response['message'].start_with?(MSG_REACHED_LIMIT) : false
|
23
17
|
end
|
24
18
|
|
25
|
-
# CODES
|
26
|
-
def bad_request?(response)
|
27
|
-
response.dig('errorCode') ? response['errorCode'] == 400 : false
|
28
|
-
end
|
29
|
-
|
30
19
|
# RESULT MESSAGES
|
31
20
|
def not_found_result(*params)
|
32
21
|
error_message("#{params.join(', ')} could not be found.")
|
@@ -36,9 +25,50 @@ module Football
|
|
36
25
|
error_message(MSG_INVALID_CONFIG)
|
37
26
|
end
|
38
27
|
|
28
|
+
def unsupported_api_call(klass, method)
|
29
|
+
message = "Method '#{method}' is not supported for the endpoint '#{klass}' by this API: "
|
30
|
+
message += "#{Configuration.api_name}"
|
31
|
+
error_message(message)
|
32
|
+
end
|
33
|
+
|
34
|
+
def unsupported_api_endpoint(klass)
|
35
|
+
error_message("The Endpoint '#{klass}' is not supported by this API: #{Configuration.api_name}")
|
36
|
+
end
|
37
|
+
|
39
38
|
def error_message(error)
|
40
39
|
{ message: error }.with_indifferent_access
|
41
40
|
end
|
41
|
+
|
42
|
+
# MULTI-API
|
43
|
+
def api_switch_method(method, named_params)
|
44
|
+
klass = api_switch
|
45
|
+
if klass
|
46
|
+
klass.respond_to?(method) ?
|
47
|
+
klass.send(method, **named_params) :
|
48
|
+
unsupported_api_call(this_class, method)
|
49
|
+
else
|
50
|
+
unsupported_api_endpoint(this_class)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def api_switch_result
|
55
|
+
Configuration.api_result(api_switch)
|
56
|
+
end
|
57
|
+
|
58
|
+
def api_switch
|
59
|
+
"Football::Butler::#{api_class}::#{this_class}".constantize
|
60
|
+
rescue
|
61
|
+
return nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def api_class
|
65
|
+
Configuration.api_class
|
66
|
+
end
|
67
|
+
|
68
|
+
def this_class
|
69
|
+
klass = self.to_s.split('::').last
|
70
|
+
Configuration.class_converter(klass)
|
71
|
+
end
|
42
72
|
end
|
43
73
|
end
|
44
74
|
end
|
@@ -1,62 +1,49 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'football/butler/apifootball/competitions'
|
3
|
+
require 'football/butler/football_data/competitions'
|
2
4
|
|
3
5
|
module Football
|
4
6
|
module Butler
|
5
7
|
class Competitions < Base
|
6
|
-
PATH = :competitions
|
7
|
-
|
8
|
-
## COMPETITION
|
9
|
-
# v2/competitions/{id}
|
10
|
-
# returns competition object directly as a hash
|
11
|
-
def self.by_id(id:)
|
12
|
-
path = "#{PATH}/#{id}"
|
13
|
-
Api.get(path: path)
|
14
|
-
end
|
15
8
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# v2/competitions
|
22
|
-
def self.all(result: PATH, filters: Configuration.tier_plan_filter)
|
23
|
-
Api.get(path: PATH, result: result, filters: filters)
|
24
|
-
end
|
9
|
+
class << self
|
10
|
+
## COMPETITION
|
11
|
+
def by_id(id:)
|
12
|
+
api_switch_method(__method__, { id: id })
|
13
|
+
end
|
25
14
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
Api.get(path: PATH, result: result, filters: filters)
|
30
|
-
end
|
15
|
+
def by_country(id:)
|
16
|
+
api_switch_method(__method__, { id: id })
|
17
|
+
end
|
31
18
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
19
|
+
## COMPETITIONS
|
20
|
+
def all(result: api_switch_result, filters: {})
|
21
|
+
api_switch_method(__method__, { result: result, filters: filters })
|
22
|
+
end
|
37
23
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
24
|
+
def all_tier_plan_filter(result: api_switch_result, filters: {})
|
25
|
+
filters.merge!(Configuration.tier_plan_filter)
|
26
|
+
api_switch_method(__method__, { result: result, filters: filters })
|
27
|
+
end
|
42
28
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
29
|
+
def by_plan(plan:, result: api_switch_result, filters: {})
|
30
|
+
api_switch_method(__method__, { plan: plan, result: result, filters: filters })
|
31
|
+
end
|
32
|
+
|
33
|
+
def by_areas(ids:, result: api_switch_result, filters: {})
|
34
|
+
api_switch_method(__method__, { ids: ids, result: result, filters: filters })
|
47
35
|
end
|
48
|
-
end
|
49
36
|
|
50
|
-
|
51
|
-
|
52
|
-
|
37
|
+
## ADDITIONAL
|
38
|
+
def current_match_day(id:)
|
39
|
+
api_switch_method(__method__, { id: id })
|
40
|
+
end
|
53
41
|
|
54
|
-
|
55
|
-
|
56
|
-
else
|
57
|
-
response['seasons']
|
42
|
+
def seasons(id:)
|
43
|
+
api_switch_method(__method__, { id: id })
|
58
44
|
end
|
59
45
|
end
|
46
|
+
|
60
47
|
end
|
61
48
|
end
|
62
49
|
end
|
@@ -3,11 +3,20 @@
|
|
3
3
|
module Football
|
4
4
|
module Butler
|
5
5
|
module Configuration
|
6
|
+
# MULTI-API
|
7
|
+
API_URL_FOOTBALL_DATA = 'https://api.football-data.org'
|
8
|
+
API_URL_APIFOOTBALL = 'https://apiv2.apifootball.com/?'
|
9
|
+
|
10
|
+
API_VERSION_FOOTBALL_DATA = 2
|
11
|
+
API_VERSION_APIFOOTBALL = 2
|
12
|
+
|
6
13
|
# API
|
7
|
-
|
14
|
+
AVAILABLE_APIS = [:football_data_org, :apifootball_com]
|
15
|
+
DEFAULT_API_NAME = :football_data_org
|
16
|
+
DEFAULT_API_URL = API_URL_FOOTBALL_DATA
|
8
17
|
|
9
18
|
DEFAULT_API_TOKEN = nil
|
10
|
-
DEFAULT_API_VERSION =
|
19
|
+
DEFAULT_API_VERSION = API_VERSION_FOOTBALL_DATA
|
11
20
|
DEFAULT_API_ENDPOINT = "#{DEFAULT_API_URL}/v#{DEFAULT_API_VERSION}"
|
12
21
|
|
13
22
|
# ADDITIONAL
|
@@ -15,43 +24,61 @@ module Football
|
|
15
24
|
DEFAULT_WAIT_ON_LIMIT = false
|
16
25
|
|
17
26
|
class << self
|
18
|
-
attr_accessor :api_version, :api_token, :api_endpoint, :tier_plan, :wait_on_limit, :init_done
|
27
|
+
attr_accessor :api_version, :api_token, :api_endpoint, :tier_plan, :wait_on_limit, :init_done,
|
28
|
+
:api_name
|
19
29
|
|
20
30
|
def configure
|
21
31
|
raise "You need to configure football-butler first, see readme." unless block_given?
|
22
32
|
|
23
33
|
yield self
|
24
34
|
|
35
|
+
# api_name: AVAILABLE_APIS
|
36
|
+
@api_name = set_api_name(self.api_name)
|
37
|
+
|
25
38
|
@api_token ||= DEFAULT_API_TOKEN
|
26
39
|
@api_version ||= DEFAULT_API_VERSION
|
27
|
-
@api_endpoint ||=
|
40
|
+
@api_endpoint ||= set_api_endpoint(@api_name, @api_version)
|
28
41
|
@tier_plan ||= DEFAULT_TIER_PLAN
|
29
|
-
@wait_on_limit ||=
|
42
|
+
@wait_on_limit ||= set_wait_on_limit(self.wait_on_limit, @api_name)
|
30
43
|
|
31
44
|
@init_done = true
|
32
45
|
|
33
|
-
|
46
|
+
api_name_valid?(self.api_name)
|
34
47
|
end
|
35
48
|
|
36
49
|
def reconfigure(
|
37
|
-
api_token: nil, api_version: nil, api_endpoint: nil, tier_plan: nil, wait_on_limit: nil
|
50
|
+
api_token: nil, api_version: nil, api_endpoint: nil, tier_plan: nil, wait_on_limit: nil,
|
51
|
+
api_name: nil
|
38
52
|
)
|
39
53
|
|
40
54
|
reset unless @init_done
|
41
55
|
|
56
|
+
@api_name = set_api_name(api_name) unless api_name.nil?
|
42
57
|
@api_token = api_token unless api_token.nil?
|
58
|
+
|
43
59
|
unless api_version.nil?
|
44
60
|
@api_version = api_version
|
45
|
-
@api_endpoint =
|
61
|
+
@api_endpoint = set_api_endpoint(@api_name, @api_version) if api_endpoint.nil?
|
62
|
+
end
|
63
|
+
|
64
|
+
if api_endpoint.nil?
|
65
|
+
@api_endpoint = set_api_endpoint(@api_name, @api_version) if api_name
|
66
|
+
else
|
67
|
+
@api_endpoint = api_endpoint
|
46
68
|
end
|
47
|
-
|
69
|
+
|
48
70
|
@tier_plan = tier_plan unless tier_plan.nil?
|
49
|
-
@wait_on_limit = wait_on_limit unless wait_on_limit.nil?
|
71
|
+
@wait_on_limit = set_wait_on_limit(wait_on_limit, @api_name) unless wait_on_limit.nil?
|
50
72
|
|
51
|
-
|
73
|
+
api_name_valid?(api_name ? api_name : @api_name)
|
74
|
+
end
|
75
|
+
|
76
|
+
def api_name_valid?(api_name)
|
77
|
+
AVAILABLE_APIS.include?(api_name)
|
52
78
|
end
|
53
79
|
|
54
80
|
def reset
|
81
|
+
@api_name = DEFAULT_API_NAME
|
55
82
|
@api_version = DEFAULT_API_VERSION
|
56
83
|
@api_endpoint = DEFAULT_API_ENDPOINT
|
57
84
|
@tier_plan = DEFAULT_TIER_PLAN
|
@@ -66,6 +93,134 @@ module Football
|
|
66
93
|
def tier_plan_filter
|
67
94
|
tier_plan.nil? ? {} : { plan: tier_plan }
|
68
95
|
end
|
96
|
+
|
97
|
+
# [:football_data_org, :apifootball_com]
|
98
|
+
# - https://www.football-data.org/documentation/api
|
99
|
+
# - https://apifootball.com/documentation
|
100
|
+
def api_class
|
101
|
+
case api_name
|
102
|
+
when :apifootball_com
|
103
|
+
'Apifootball'
|
104
|
+
when :football_data_org
|
105
|
+
'FootballData'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def set_api_name(api_name)
|
110
|
+
api_name_valid?(api_name) ? api_name : DEFAULT_API_NAME
|
111
|
+
end
|
112
|
+
|
113
|
+
def set_api_endpoint(api_name, api_version = DEFAULT_API_VERSION)
|
114
|
+
return if api_name.nil?
|
115
|
+
|
116
|
+
case api_name
|
117
|
+
when :apifootball_com
|
118
|
+
API_URL_APIFOOTBALL
|
119
|
+
when :football_data_org
|
120
|
+
"#{API_URL_FOOTBALL_DATA}/v#{api_version}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def set_wait_on_limit(wait_on_limit, api_name)
|
125
|
+
case api_name
|
126
|
+
when :apifootball_com
|
127
|
+
false
|
128
|
+
when :football_data_org
|
129
|
+
wait_on_limit
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def http_party_headers
|
134
|
+
case api_name
|
135
|
+
when :apifootball_com
|
136
|
+
{}
|
137
|
+
when :football_data_org
|
138
|
+
{ "X-Auth-Token": Configuration.api_token }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def http_party_url(path)
|
143
|
+
case api_name
|
144
|
+
when :apifootball_com
|
145
|
+
"#{Configuration.api_endpoint}#{path}&APIkey=#{Configuration.api_token}"
|
146
|
+
when :football_data_org
|
147
|
+
"#{Configuration.api_endpoint}/#{path}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def http_party_response(response, result)
|
152
|
+
case api_name
|
153
|
+
when :apifootball_com
|
154
|
+
response_apifootball_com(response, result)
|
155
|
+
when :football_data_org
|
156
|
+
response_football_data_org(response, result)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def response_apifootball_com(response, result)
|
161
|
+
case result
|
162
|
+
when :default
|
163
|
+
response
|
164
|
+
else
|
165
|
+
response.parsed_response
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def response_football_data_org(response, result)
|
170
|
+
case result
|
171
|
+
when :default
|
172
|
+
response
|
173
|
+
when :parsed_response
|
174
|
+
response.parsed_response
|
175
|
+
else
|
176
|
+
response&.keys&.include?(result.to_s) ? response[result.to_s] : nil
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def tier_from_response(response)
|
181
|
+
case api_name
|
182
|
+
when :apifootball_com
|
183
|
+
# n/a
|
184
|
+
when :football_data_org
|
185
|
+
Tier.set_from_response_headers(response)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def api_result(klass)
|
190
|
+
case api_name
|
191
|
+
when :apifootball_com
|
192
|
+
:parsed_response
|
193
|
+
when :football_data_org
|
194
|
+
klass::PATH
|
195
|
+
end
|
196
|
+
rescue
|
197
|
+
return nil
|
198
|
+
end
|
199
|
+
|
200
|
+
def class_converter(klass)
|
201
|
+
case api_name
|
202
|
+
when :apifootball_com
|
203
|
+
case klass
|
204
|
+
when 'Areas'
|
205
|
+
return 'Countries'
|
206
|
+
when 'Matches'
|
207
|
+
return 'Events'
|
208
|
+
when 'Scorers'
|
209
|
+
return 'TopScorers'
|
210
|
+
end
|
211
|
+
when :football_data_org
|
212
|
+
case klass
|
213
|
+
when 'Countries'
|
214
|
+
return 'Areas'
|
215
|
+
when 'Events'
|
216
|
+
return 'Matches'
|
217
|
+
when 'TopScorers'
|
218
|
+
return 'Scorers'
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
klass
|
223
|
+
end
|
69
224
|
end
|
70
225
|
end
|
71
226
|
end
|