scrap_cbf_record 0.1.0
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 +7 -0
- data/lib/scrap_cbf_record.rb +72 -0
- data/lib/scrap_cbf_record/active_record.rb +105 -0
- data/lib/scrap_cbf_record/active_records/base.rb +196 -0
- data/lib/scrap_cbf_record/active_records/matches.rb +80 -0
- data/lib/scrap_cbf_record/active_records/rankings.rb +70 -0
- data/lib/scrap_cbf_record/active_records/rounds.rb +60 -0
- data/lib/scrap_cbf_record/active_records/teams.rb +42 -0
- data/lib/scrap_cbf_record/config.rb +217 -0
- data/lib/scrap_cbf_record/configs/base.rb +258 -0
- data/lib/scrap_cbf_record/configs/championship.rb +97 -0
- data/lib/scrap_cbf_record/configs/match.rb +164 -0
- data/lib/scrap_cbf_record/configs/ranking.rb +152 -0
- data/lib/scrap_cbf_record/configs/round.rb +113 -0
- data/lib/scrap_cbf_record/configs/team.rb +99 -0
- data/lib/scrap_cbf_record/errors.rb +83 -0
- data/lib/scrap_cbf_record/logger.rb +20 -0
- data/lib/scrap_cbf_record/models/base.rb +16 -0
- data/lib/scrap_cbf_record/models/championship.rb +28 -0
- data/lib/scrap_cbf_record/models/concerns/active_record_relation.rb +37 -0
- data/lib/scrap_cbf_record/models/match.rb +71 -0
- data/lib/scrap_cbf_record/models/ranking.rb +58 -0
- data/lib/scrap_cbf_record/models/round.rb +54 -0
- data/lib/scrap_cbf_record/models/team.rb +22 -0
- data/lib/scrap_cbf_record/version.rb +5 -0
- metadata +170 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1689f847640b2f8bcfd5e9362b785d4b703bddd402097082f0c8f6bace631afe
|
4
|
+
data.tar.gz: 0fca0b02a69c838721a5f60fc1228e059bb35a2dd95f383b4d5cec94ca13b0d3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4600056001b3c311ee5a9ea08ec1a3e35b6a1aa3502002afa8fc54c693c00bbf7094126f8a835418ddbb6164ab4b50bcf82680f255d8adf850da84976006766d
|
7
|
+
data.tar.gz: 2857154aa9b4acff0221fa0ad6447d3d77eb685bbf61584ae795f15c85b55b1a24f5b0c677d49408163e928ae11bf8af8c3017eeae33ba41a1649c627cb21e20
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'scrap_cbf_record/version'
|
4
|
+
require_relative 'scrap_cbf_record/logger'
|
5
|
+
require_relative 'scrap_cbf_record/errors'
|
6
|
+
require_relative 'scrap_cbf_record/models/base'
|
7
|
+
require_relative 'scrap_cbf_record/models/championship'
|
8
|
+
require_relative 'scrap_cbf_record/models/match'
|
9
|
+
require_relative 'scrap_cbf_record/models/ranking'
|
10
|
+
require_relative 'scrap_cbf_record/models/round'
|
11
|
+
require_relative 'scrap_cbf_record/models/team'
|
12
|
+
require_relative 'scrap_cbf_record/models/concerns/active_record_relation'
|
13
|
+
require_relative 'scrap_cbf_record/config'
|
14
|
+
require_relative 'scrap_cbf_record/active_record'
|
15
|
+
|
16
|
+
# This module saves on database the output from the gem ScrapCbf
|
17
|
+
#
|
18
|
+
# It has two modules to accomplish that:
|
19
|
+
# - configs: holds the settings for how to save the data
|
20
|
+
# - records: responsible for saving the data on database.
|
21
|
+
#
|
22
|
+
# There configs are:
|
23
|
+
# - championship
|
24
|
+
# - match
|
25
|
+
# - ranking
|
26
|
+
# - round
|
27
|
+
# - team
|
28
|
+
#
|
29
|
+
# The records are:
|
30
|
+
# - matches: saves the matches for a specific championship
|
31
|
+
# - rankings: saves the rankings for a specific championship
|
32
|
+
# - rounds: saves the rounds for a specific championship
|
33
|
+
# - teams: saves the teams that participated on the specific champ.
|
34
|
+
class ScrapCbfRecord
|
35
|
+
class << self
|
36
|
+
# Returns the global configurations for these module
|
37
|
+
#
|
38
|
+
# @return [ScrapCbfRecord::Config]
|
39
|
+
def config
|
40
|
+
@config ||= settings
|
41
|
+
end
|
42
|
+
|
43
|
+
# Sets the global configurations
|
44
|
+
# We can set the configurations in the following way:
|
45
|
+
#
|
46
|
+
# ScrapCbfRecord.settings do |config|
|
47
|
+
# config.championship = {
|
48
|
+
# class_name: 'Championship'
|
49
|
+
# rename_attrs: {},
|
50
|
+
# exclude_attrs_on_create: %i[],
|
51
|
+
# exclude_attrs_on_update: %i[],
|
52
|
+
# associations: %i[]
|
53
|
+
# }
|
54
|
+
#
|
55
|
+
# config.match = { ... }
|
56
|
+
# config.ranking = { ... }
|
57
|
+
# config.round = { ... }
|
58
|
+
# config.team = { ... }
|
59
|
+
# end
|
60
|
+
# If a config or a config's attribute was not set,
|
61
|
+
# default setting will be used
|
62
|
+
#
|
63
|
+
# @return [ScrapCbfRecord::Config]
|
64
|
+
def settings
|
65
|
+
configuration = Config.instance
|
66
|
+
|
67
|
+
yield configuration if block_given?
|
68
|
+
|
69
|
+
@config = configuration
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
5
|
+
require 'active_support/core_ext/hash/except'
|
6
|
+
|
7
|
+
require_relative 'active_records/base'
|
8
|
+
require_relative 'active_records/matches'
|
9
|
+
require_relative 'active_records/rankings'
|
10
|
+
require_relative 'active_records/rounds'
|
11
|
+
require_relative 'active_records/teams'
|
12
|
+
|
13
|
+
class ScrapCbfRecord
|
14
|
+
# This module uses Active Record module to save data on database.
|
15
|
+
class ActiveRecord
|
16
|
+
class << self
|
17
|
+
def save(records)
|
18
|
+
new(records).save
|
19
|
+
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# The argument records is a hash (json or not) with the following look :
|
25
|
+
# - hash[:championship] the championship for a specific year and divison
|
26
|
+
# - hash[:matches] the matches for the specific championship
|
27
|
+
# - hash[:rankings] the rankings for the specific championship
|
28
|
+
# - hash[:rounds] the rounds for the specific championship
|
29
|
+
# - hash[:teams] the teams that participated on the specific championship
|
30
|
+
#
|
31
|
+
# @param [records] hash or json returned from ScrapCbf gem
|
32
|
+
# @return [nil]
|
33
|
+
def initialize(records)
|
34
|
+
records = parse_json!(records) if records.is_a?(String)
|
35
|
+
|
36
|
+
raise ::ArgumentError, invalid_type_message unless records.is_a?(Hash)
|
37
|
+
|
38
|
+
@records = records.with_indifferent_access
|
39
|
+
|
40
|
+
validate_record_key_presence!(@records)
|
41
|
+
|
42
|
+
@championship = @records[:championship]
|
43
|
+
@matches = @records[:matches]
|
44
|
+
@rankings = @records[:rankings]
|
45
|
+
@rounds = @records[:rounds]
|
46
|
+
@teams = @records[:teams]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Save records to the database.
|
50
|
+
# Note: Because of database relationships and dependencies between records
|
51
|
+
# there maybe exist a saving order.
|
52
|
+
# - Teams must be save before Rankings and Match.
|
53
|
+
# - Rounds must be save before Matches
|
54
|
+
#
|
55
|
+
# @raise [ActiveRecordValidationError] in case of failing while saving
|
56
|
+
#
|
57
|
+
# @return [Boolean] true in case of success
|
58
|
+
def save
|
59
|
+
save_teams(@teams)
|
60
|
+
save_rankings(@rankings, @championship)
|
61
|
+
save_rounds(@rounds, @championship)
|
62
|
+
save_matches(@matches, @championship)
|
63
|
+
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def save_teams(teams)
|
70
|
+
Teams.new(teams).create_unless_found
|
71
|
+
end
|
72
|
+
|
73
|
+
def save_rankings(rankings, championship)
|
74
|
+
Rankings.new(rankings).create_or_update(championship)
|
75
|
+
end
|
76
|
+
|
77
|
+
def save_rounds(rounds, championship)
|
78
|
+
Rounds.new(rounds).create_unless_found(championship)
|
79
|
+
end
|
80
|
+
|
81
|
+
def save_matches(matches, championship)
|
82
|
+
Matches.new(matches).create_or_update(championship)
|
83
|
+
end
|
84
|
+
|
85
|
+
def invalid_type_message
|
86
|
+
'must be a Hash or Json of a Hash'
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse_json!(records)
|
90
|
+
JSON.parse(records)
|
91
|
+
rescue JSON::ParserError => e
|
92
|
+
raise JsonDecodeError, e
|
93
|
+
end
|
94
|
+
|
95
|
+
def validate_record_key_presence!(records)
|
96
|
+
raise MissingKeyError, 'championship' unless records.key?(:championship)
|
97
|
+
raise MissingKeyError, 'matches' unless records.key?(:matches)
|
98
|
+
raise MissingKeyError, 'rankings' unless records.key?(:rankings)
|
99
|
+
raise MissingKeyError, 'rounds' unless records.key?(:rounds)
|
100
|
+
raise MissingKeyError, 'teams' unless records.key?(:teams)
|
101
|
+
|
102
|
+
true
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ScrapCbfRecord
|
4
|
+
class ActiveRecord
|
5
|
+
# Superclass for the classes lib/active_records/<record>.rb
|
6
|
+
class Base
|
7
|
+
def initialize(config)
|
8
|
+
# config is associated with the current record class
|
9
|
+
#
|
10
|
+
@config = config
|
11
|
+
|
12
|
+
# This reference the user class used to save on database
|
13
|
+
#
|
14
|
+
@model = config.klass
|
15
|
+
|
16
|
+
# current configs set by users
|
17
|
+
#
|
18
|
+
@associations = @config.associations
|
19
|
+
@exclude_attrs_on_create = @config.exclude_attrs_on_create
|
20
|
+
@exclude_attrs_on_update = @config.exclude_attrs_on_update
|
21
|
+
@rename_attrs = @config.rename_attrs
|
22
|
+
|
23
|
+
# current configs required by the system
|
24
|
+
#
|
25
|
+
@must_exclude_attrs = @config.must_exclude_attrs
|
26
|
+
end
|
27
|
+
|
28
|
+
# Normalize, for create and update, the new record with:
|
29
|
+
# Setting Associations
|
30
|
+
# Rename attributes
|
31
|
+
# Exclusion of attributes
|
32
|
+
#
|
33
|
+
# @param [Object, Nil] the record if exist
|
34
|
+
# @param [Hash] contaning the new record
|
35
|
+
# @param [Hash] contaning the existent record's associations
|
36
|
+
# @return [Object] normalized
|
37
|
+
def normalize_before_save(record, hash, associations = {})
|
38
|
+
if record
|
39
|
+
hash = normalize_before_update(hash, associations)
|
40
|
+
|
41
|
+
record.assign_attributes(hash)
|
42
|
+
|
43
|
+
record
|
44
|
+
else
|
45
|
+
hash = normalize_before_create(hash, associations)
|
46
|
+
|
47
|
+
@model.new(hash)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Normalize, on create, the new record with:
|
52
|
+
# Setting Associations
|
53
|
+
# Rename attributes
|
54
|
+
# Exclusion of attributes
|
55
|
+
#
|
56
|
+
# @param [Hash] contaning the new record
|
57
|
+
# @param [Hash] contaning the existent record's associations
|
58
|
+
# @return [Hash] normalized
|
59
|
+
def normalize_before_create(hash, assocs = {})
|
60
|
+
hash = include_associations(
|
61
|
+
hash,
|
62
|
+
@associations,
|
63
|
+
assocs
|
64
|
+
)
|
65
|
+
|
66
|
+
hash = exclude_attrs(
|
67
|
+
hash,
|
68
|
+
@exclude_attrs_on_create,
|
69
|
+
@must_exclude_attrs,
|
70
|
+
@associations.keys
|
71
|
+
)
|
72
|
+
|
73
|
+
hash = rename_attrs(hash, @rename_attrs)
|
74
|
+
|
75
|
+
hash
|
76
|
+
end
|
77
|
+
|
78
|
+
# Normalize, on update, the new record with:
|
79
|
+
# Setting Associations
|
80
|
+
# Rename attributes
|
81
|
+
# Exclusion of attributes
|
82
|
+
#
|
83
|
+
# @param [Hash] contaning the new record
|
84
|
+
# @param [Hash] contaning the existent record's associations
|
85
|
+
# @return [Hash] normalized
|
86
|
+
def normalize_before_update(hash, assocs = {})
|
87
|
+
hash = include_associations(
|
88
|
+
hash,
|
89
|
+
@associations,
|
90
|
+
assocs
|
91
|
+
)
|
92
|
+
|
93
|
+
hash = exclude_attrs(
|
94
|
+
hash,
|
95
|
+
@exclude_attrs_on_update,
|
96
|
+
@must_exclude_attrs,
|
97
|
+
@associations.keys
|
98
|
+
)
|
99
|
+
|
100
|
+
hash = rename_attrs(hash, @rename_attrs)
|
101
|
+
|
102
|
+
hash
|
103
|
+
end
|
104
|
+
|
105
|
+
# Save record instance or log the errors found
|
106
|
+
#
|
107
|
+
# @raise [ActiveRecordValidationError]
|
108
|
+
# @param record [ActiveRecord] instance to be saved
|
109
|
+
# @return [ActiveRecord] the instance saved
|
110
|
+
def save_or_log_error(record)
|
111
|
+
if record.valid?
|
112
|
+
record.save
|
113
|
+
else
|
114
|
+
log_record_errors(record)
|
115
|
+
# It raises custom error to display message warning about the logs.
|
116
|
+
raise ActiveRecordValidationError
|
117
|
+
end
|
118
|
+
record
|
119
|
+
end
|
120
|
+
|
121
|
+
protected
|
122
|
+
|
123
|
+
# Includes associations <attribute_id> and its value to the record hash
|
124
|
+
# only include if the associations was set by the user.
|
125
|
+
#
|
126
|
+
# @param hash [Hash] record hash to be modified
|
127
|
+
# @param associations [Hash] hash contaning associations details
|
128
|
+
# @param assocs [Hash] hash contaning the associations
|
129
|
+
# values returned by find_by
|
130
|
+
# @return [Hash] record hash with associations included
|
131
|
+
def include_associations(hash, associations, assocs)
|
132
|
+
associations.each do |name, attrs|
|
133
|
+
instance = assocs[name.to_sym]
|
134
|
+
|
135
|
+
foreign_key = attrs[:foreign_key]
|
136
|
+
|
137
|
+
# for cases where instance is:
|
138
|
+
# - association is empty (nil)
|
139
|
+
# - association is present
|
140
|
+
#
|
141
|
+
# update hash with the foreign_key
|
142
|
+
hash[foreign_key.to_sym] = (instance.id if instance.present?)
|
143
|
+
end
|
144
|
+
|
145
|
+
hash
|
146
|
+
end
|
147
|
+
|
148
|
+
# Exclude some keys from the record hash
|
149
|
+
#
|
150
|
+
# @param hash [Hash] record hash to be modified
|
151
|
+
# @param attrs [Array] has the keys to be exclude.
|
152
|
+
# These attrs are the union of exclude_attrs_on_create/update.
|
153
|
+
# @param must_exclude [Array] has the keys to be excluded.
|
154
|
+
# The keys are set by the lib, and is used to remove unwanted attrs.
|
155
|
+
# @param associations [Array] has the names of the associations.
|
156
|
+
# include_associations method does't remove the associations added.
|
157
|
+
# e.g if championship association exist, then:
|
158
|
+
# add championship_id to hash.
|
159
|
+
# But, it doesn't remove the championship key from the hash.
|
160
|
+
# It's remove here.
|
161
|
+
# @return [Hash] record hash modified
|
162
|
+
def exclude_attrs(hash, attrs, must_exclude, associations)
|
163
|
+
exclude = attrs + associations + must_exclude
|
164
|
+
hash.except(*exclude)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Rename keys from the record hash
|
168
|
+
#
|
169
|
+
# @param hash [Hash] record hash to be modified
|
170
|
+
# @param renames [Hash] has the keys to be renamed by the key's value.
|
171
|
+
# @return [Hash] record hash modified
|
172
|
+
def rename_attrs(hash, renames)
|
173
|
+
# rename attrs
|
174
|
+
renames.each do |key, val|
|
175
|
+
hash[val.to_sym] = hash.delete(key) if hash.key?(key)
|
176
|
+
end
|
177
|
+
|
178
|
+
hash
|
179
|
+
end
|
180
|
+
|
181
|
+
def raise_unless_respond_to_each(records, records_type)
|
182
|
+
return if records.respond_to?(:each)
|
183
|
+
|
184
|
+
raise ::ArgumentError, "#{records_type} must respond to method :each"
|
185
|
+
end
|
186
|
+
|
187
|
+
def log_record_errors(record)
|
188
|
+
TagLogger.with_context([Time.current], 'Errors found while saving')
|
189
|
+
record.errors.each do |attribute, message|
|
190
|
+
TagLogger.with_context(:info, record.inspect.to_s)
|
191
|
+
TagLogger.with_context(:error, "#{attribute}: #{message}")
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ScrapCbfRecord
|
4
|
+
class ActiveRecord
|
5
|
+
# Class responsible for saving matches to database
|
6
|
+
class Matches < Base
|
7
|
+
#
|
8
|
+
# @param [matches] hash contaning the matches
|
9
|
+
# @return [nil]
|
10
|
+
def initialize(matches)
|
11
|
+
raise_unless_respond_to_each(matches, :matches)
|
12
|
+
|
13
|
+
configurations = ScrapCbfRecord.config
|
14
|
+
|
15
|
+
super(configurations.match)
|
16
|
+
|
17
|
+
@matches = matches
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates/Updates the matches found on the instance variable matches
|
21
|
+
# Update if match already exist, otherwise create it
|
22
|
+
#
|
23
|
+
# @param [championship_hash] the championship associated with the matches
|
24
|
+
# @raise [ActiveRecordValidationError] if fail on saving
|
25
|
+
# @return [Boolean] true if not exception is raise
|
26
|
+
def create_or_update(championship_hash)
|
27
|
+
championship, serie = find_championship_by(championship_hash)
|
28
|
+
|
29
|
+
::ActiveRecord::Base.transaction do
|
30
|
+
@matches.each do |hash|
|
31
|
+
attrs = [hash, championship, serie]
|
32
|
+
match = find_match_by(*attrs)
|
33
|
+
round = find_round_by(*attrs)
|
34
|
+
team = Match.team.find_by(name: hash[:team])
|
35
|
+
opponent = Match.opponent.find_by(name: hash[:opponent])
|
36
|
+
|
37
|
+
associations = {
|
38
|
+
championship: championship,
|
39
|
+
round: round,
|
40
|
+
team: team,
|
41
|
+
opponent: opponent
|
42
|
+
}
|
43
|
+
|
44
|
+
match = normalize_before_save(match, hash, associations)
|
45
|
+
|
46
|
+
save_or_log_error(match)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def find_championship_by(championship_hash)
|
55
|
+
championship =
|
56
|
+
Match.championship.find_by!(year: championship_hash[:year])
|
57
|
+
|
58
|
+
serie = championship_hash[:serie]
|
59
|
+
|
60
|
+
[championship, serie]
|
61
|
+
end
|
62
|
+
|
63
|
+
def find_match_by(hash, championship, serie)
|
64
|
+
Match.find_by(
|
65
|
+
id_match: hash[:id_match],
|
66
|
+
championship: championship,
|
67
|
+
serie: serie
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def find_round_by(hash, championship, serie)
|
72
|
+
Match.round.find_by(
|
73
|
+
number: hash[:round],
|
74
|
+
championship: championship,
|
75
|
+
serie: serie
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|