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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/football/butler.rb +10 -0
  3. data/lib/football/butler/api.rb +8 -16
  4. data/lib/football/butler/apifootball/base_apifootball.rb +17 -0
  5. data/lib/football/butler/apifootball/competitions.rb +30 -0
  6. data/lib/football/butler/apifootball/countries.rb +20 -0
  7. data/lib/football/butler/apifootball/events.rb +61 -0
  8. data/lib/football/butler/apifootball/head_to_head.rb +30 -0
  9. data/lib/football/butler/apifootball/lineups.rb +22 -0
  10. data/lib/football/butler/apifootball/odds.rb +25 -0
  11. data/lib/football/butler/apifootball/players.rb +27 -0
  12. data/lib/football/butler/apifootball/predictions.rb +28 -0
  13. data/lib/football/butler/apifootball/standings.rb +23 -0
  14. data/lib/football/butler/apifootball/statistics.rb +22 -0
  15. data/lib/football/butler/apifootball/teams.rb +29 -0
  16. data/lib/football/butler/apifootball/top_scorers.rb +22 -0
  17. data/lib/football/butler/areas.rb +15 -24
  18. data/lib/football/butler/base.rb +47 -17
  19. data/lib/football/butler/competitions.rb +31 -44
  20. data/lib/football/butler/configuration.rb +166 -11
  21. data/lib/football/butler/countries.rb +10 -0
  22. data/lib/football/butler/events.rb +10 -0
  23. data/lib/football/butler/football_data/areas.rb +40 -0
  24. data/lib/football/butler/football_data/competitions.rb +72 -0
  25. data/lib/football/butler/football_data/head_to_head.rb +27 -0
  26. data/lib/football/butler/football_data/lineups.rb +32 -0
  27. data/lib/football/butler/football_data/matches.rb +110 -0
  28. data/lib/football/butler/football_data/odds.rb +27 -0
  29. data/lib/football/butler/football_data/players.rb +20 -0
  30. data/lib/football/butler/football_data/scorers.rb +23 -0
  31. data/lib/football/butler/football_data/standings.rb +47 -0
  32. data/lib/football/butler/football_data/teams.rb +39 -0
  33. data/lib/football/butler/head_to_head.rb +27 -0
  34. data/lib/football/butler/lineups.rb +17 -0
  35. data/lib/football/butler/matches.rb +47 -83
  36. data/lib/football/butler/odds.rb +17 -0
  37. data/lib/football/butler/players.rb +21 -0
  38. data/lib/football/butler/predictions.rb +16 -0
  39. data/lib/football/butler/scorers.rb +17 -0
  40. data/lib/football/butler/standings.rb +15 -29
  41. data/lib/football/butler/statistics.rb +16 -0
  42. data/lib/football/butler/teams.rb +14 -23
  43. data/lib/football/butler/top_scorers.rb +10 -0
  44. data/lib/football/butler/version.rb +5 -2
  45. 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
- ## AREA
9
- # v2/areas/{id}
10
- # returns area object directly as a hash
11
- def self.by_id(id:)
12
- path = "#{PATH}/#{id}"
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
- ## ADDITIONAL
23
- # v2/areas
24
- # v2/areas/{id}
25
- # returns area object directly as a hash
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
- by_id(id: area['id'])
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
@@ -3,30 +3,19 @@
3
3
  module Football
4
4
  module Butler
5
5
  class Base
6
- MSG_INVALID_TOKEN = 'Your API token is invalid.' # 400
7
- MSG_NOT_EXIST = 'The resource you are looking for does not exist' # 404
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
- ## COMPETITIONS
17
- #
18
- # areas={AREAS}
19
- # plan={PLAN}
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
- # v2/competitions?plan={plan}
27
- def self.by_plan(plan:, result: PATH, filters: {})
28
- filters.merge!({ plan: plan })
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
- # v2/competitions?areas={id1, id2, ...}
33
- def self.by_areas(ids:, result: PATH, filters: {})
34
- filters.merge!({ areas: ids.join(',') })
35
- Api.get(path: PATH, result: result, filters: filters)
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
- ## ADDITIONAL
39
- # v2/competitions/{id}
40
- def self.current_match_day(id:)
41
- response = by_id(id:id)
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
- if response.is_a?(Hash) && response.dig('message')
44
- response
45
- else
46
- response['currentSeason']['currentMatchday']
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
- # v2/competitions/{id}
51
- def self.seasons(id:)
52
- response = by_id(id:id)
37
+ ## ADDITIONAL
38
+ def current_match_day(id:)
39
+ api_switch_method(__method__, { id: id })
40
+ end
53
41
 
54
- if response.is_a?(Hash) && response.dig('message')
55
- response
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
- DEFAULT_API_URL = "https://api.football-data.org"
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 = 2
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 ||= DEFAULT_API_ENDPOINT
40
+ @api_endpoint ||= set_api_endpoint(@api_name, @api_version)
28
41
  @tier_plan ||= DEFAULT_TIER_PLAN
29
- @wait_on_limit ||= DEFAULT_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
- true
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 = "#{DEFAULT_API_URL}/v#{api_version}" if api_endpoint.nil?
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
- @api_endpoint = api_endpoint unless api_endpoint.nil?
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
- true
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