tzispa_data 0.3.0 → 0.4.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 +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/tzispa/data/adapter_pool.rb +23 -11
- data/lib/tzispa/data/entity.rb +8 -5
- data/lib/tzispa/data/repository.rb +62 -41
- data/lib/tzispa/data/transporter.rb +73 -62
- data/lib/tzispa/data/version.rb +1 -1
- data/lib/tzispa/data.rb +3 -3
- data/lib/tzispa_data.rb +2 -0
- metadata +2 -3
- data/lib/tzispa/data/adapter.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d986faff8dbc3e2fd759f03fe06790ad61e9b8f4
|
4
|
+
data.tar.gz: 8db044d027d97790e03b3300b00180d195f86c85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5238d861f3add98d6ee943384b306d811d9b5f1bd8ef1b712b38627aa9cb5835654db09bfd0df0caad6955cd9708db3473253dfb4d539b2332ef7a7d48f61562
|
7
|
+
data.tar.gz: 0d267d340bf788b6c1d923237976473de5ff49742ef02f5f8a49cd284e67278abc62316d0daa41abbc5d4da55e1c2f5a71ee76f4e0602da2d9013ae83fd61d0e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'sequel'
|
2
4
|
require 'forwardable'
|
3
5
|
|
@@ -11,22 +13,32 @@ module Tzispa
|
|
11
13
|
def_delegators :@pool, :has_key?, :keys
|
12
14
|
attr_reader :default
|
13
15
|
|
14
|
-
def initialize(config, default=nil)
|
15
|
-
|
16
|
-
@
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
def initialize(config, default = nil)
|
17
|
+
setup_sequel
|
18
|
+
@default = default || config.first[0]
|
19
|
+
@pool = {}.tap do |hsh|
|
20
|
+
config.each do |k, v|
|
21
|
+
conn = Sequel.connect "#{v.adapter}://#{v.database}"
|
22
|
+
if v.connection_validation
|
23
|
+
conn.extension(:connection_validator)
|
24
|
+
conn.pool.connection_validation_timeout = v.validation_timeout || DEFAULT_TIMEOUT
|
25
|
+
end
|
26
|
+
hsh[k.to_sym] = conn
|
27
|
+
end
|
28
|
+
end
|
24
29
|
end
|
25
30
|
|
26
|
-
def [](name=nil)
|
31
|
+
def [](name = nil)
|
27
32
|
@pool[name&.to_sym || default]
|
28
33
|
end
|
29
34
|
|
35
|
+
def setup_sequel
|
36
|
+
Sequel.extension :core_extensions
|
37
|
+
Sequel.default_timezone = :utc
|
38
|
+
Sequel.datetime_class = DateTime
|
39
|
+
end
|
40
|
+
|
41
|
+
DEFAULT_TIMEOUT = 3600
|
30
42
|
end
|
31
43
|
|
32
44
|
end
|
data/lib/tzispa/data/entity.rb
CHANGED
@@ -15,14 +15,17 @@ module Tzispa
|
|
15
15
|
end
|
16
16
|
|
17
17
|
module ClassMethods
|
18
|
-
using Tzispa::Utils
|
18
|
+
using Tzispa::Utils::TzString
|
19
19
|
|
20
20
|
def entity_class
|
21
|
-
class_variable_defined?
|
22
|
-
class_variable_get
|
23
|
-
|
21
|
+
if class_variable_defined? :@@__entity_class
|
22
|
+
class_variable_get :@@__entity_class
|
23
|
+
else
|
24
|
+
class_variable_set :@@__entity_class,
|
25
|
+
name.sub('::Model', '::Entity').constantize
|
26
|
+
end
|
24
27
|
end
|
25
|
-
|
28
|
+
alias entity entity_class
|
26
29
|
end
|
27
30
|
|
28
31
|
end
|
@@ -7,96 +7,117 @@ require 'tzispa/data/adapter_pool'
|
|
7
7
|
module Tzispa
|
8
8
|
module Data
|
9
9
|
|
10
|
-
class DataError < StandardError; end
|
11
|
-
class UnknownAdapter < DataError; end;
|
12
|
-
class UnknownModel < DataError; end;
|
10
|
+
class DataError < StandardError; end
|
13
11
|
|
12
|
+
class UnknownAdapter < DataError
|
13
|
+
def initialize(model, repo)
|
14
|
+
"The '#{model}' model does not exists in the adapter '#{repo}'"
|
15
|
+
end
|
16
|
+
end
|
14
17
|
|
15
|
-
class
|
18
|
+
class UnknownModel < DataError; end
|
16
19
|
|
17
|
-
|
20
|
+
class Repository
|
21
|
+
using Tzispa::Utils::TzString
|
18
22
|
|
19
23
|
attr_reader :root, :adapters
|
20
24
|
|
21
25
|
LOCAL_REPO_ROOT = :repository
|
22
26
|
|
23
|
-
def initialize(config, root =
|
27
|
+
def initialize(config, root = nil)
|
24
28
|
@config = config
|
25
|
-
@root = root
|
26
|
-
@pool =
|
29
|
+
@root = root || LOCAL_REPO_ROOT
|
30
|
+
@pool = {}
|
27
31
|
@adapters = AdapterPool.new config
|
28
32
|
end
|
29
33
|
|
30
|
-
def [](model, repo_id=nil)
|
31
|
-
selected_repo = repo_id ||
|
32
|
-
raise UnknownModel.new(
|
33
|
-
|
34
|
+
def [](model, repo_id = nil)
|
35
|
+
selected_repo = repo_id || adapters.default
|
36
|
+
raise UnknownModel.new(model, selected_repo) unless pool.key?(selected_repo) &&
|
37
|
+
pool[selected_repo].key?(model.to_sym)
|
38
|
+
pool[selected_repo][model.to_sym]
|
34
39
|
end
|
35
40
|
|
36
|
-
def models(repo_id=nil)
|
37
|
-
|
41
|
+
def models(repo_id = nil)
|
42
|
+
pool[repo_id || adapters.default].values
|
38
43
|
end
|
39
44
|
|
40
|
-
def module_const(repo_id=nil)
|
41
|
-
selected_repo = repo_id ||
|
42
|
-
|
45
|
+
def module_const(repo_id = nil)
|
46
|
+
selected_repo = repo_id || adapters.default
|
47
|
+
pool[selected_repo][:__repository_module] ||= repository_module(selected_repo)
|
43
48
|
end
|
44
49
|
|
45
50
|
def load!(domain)
|
46
|
-
@config.each
|
47
|
-
Mutex.new.synchronize
|
48
|
-
|
49
|
-
Sequel::Model.db =
|
50
|
-
|
51
|
-
load_local_helpers id, cfg
|
52
|
-
load_local_models id, cfg
|
53
|
-
else
|
54
|
-
require cfg.gem
|
55
|
-
repo_module = "#{id.to_s.camelize}".constantize
|
56
|
-
self.class.include repo_module
|
57
|
-
self.class.send "load_#{id}", self, id, cfg
|
58
|
-
end
|
51
|
+
@config.each do |id, cfg|
|
52
|
+
Mutex.new.synchronize do
|
53
|
+
pool[id] = {}
|
54
|
+
Sequel::Model.db = adapters[id]
|
55
|
+
load_config_repo(id, cfg)
|
59
56
|
domain.include module_const(id)
|
60
|
-
|
61
|
-
|
57
|
+
end
|
58
|
+
end
|
62
59
|
self
|
63
60
|
end
|
64
61
|
|
65
62
|
def register(model_id, model_class, repo_id, config)
|
66
63
|
model_class.db = @adapters[repo_id]
|
67
|
-
config.extensions
|
68
|
-
|
69
|
-
|
64
|
+
if config.respond_to? :extensions
|
65
|
+
config.extensions.split(',').each do |ext|
|
66
|
+
model_class.db.extension ext.to_sym
|
67
|
+
end
|
68
|
+
end
|
70
69
|
@pool[repo_id][model_id.to_sym] = model_class
|
71
70
|
end
|
72
71
|
|
73
72
|
private
|
74
73
|
|
74
|
+
attr_reader :pool
|
75
|
+
|
75
76
|
def repository_module(repo_id)
|
76
77
|
rm = @pool[repo_id].first[1].name.split('::')
|
77
78
|
rm.pop
|
78
79
|
rm.join('::').constantize
|
79
80
|
end
|
80
81
|
|
82
|
+
def load_config_repo(id, cfg)
|
83
|
+
if cfg.local
|
84
|
+
load_local_helpers id
|
85
|
+
load_local_models id, cfg
|
86
|
+
load_local_entities id
|
87
|
+
else
|
88
|
+
require cfg.gem
|
89
|
+
repo_module = id.to_s.camelize.constantize
|
90
|
+
self.class.include repo_module
|
91
|
+
self.class.send "load_#{id}", self, id, cfg
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
81
95
|
def load_local_models(repo_id, config)
|
82
96
|
models_path = "./#{root}/#{repo_id}/model"
|
83
97
|
repo_module = "#{repo_id.to_s.camelize}::Model".constantize
|
84
|
-
Dir["#{models_path}/*.rb"].each
|
98
|
+
Dir["#{models_path}/*.rb"].each do |file|
|
85
99
|
model_id = file.split('/').last.split('.').first
|
86
100
|
require "#{models_path}/#{model_id}"
|
87
101
|
model_class = "#{repo_module}::#{model_id.camelize}".constantize
|
88
102
|
register model_id, model_class, repo_id, config
|
89
|
-
|
103
|
+
end
|
90
104
|
end
|
91
105
|
|
92
|
-
def
|
106
|
+
def load_local_entities(repo_id)
|
107
|
+
entities_path = "./#{root}/#{repo_id}/entity"
|
108
|
+
Dir["#{entities_path}/*.rb"].each do |file|
|
109
|
+
entity_id = file.split('/').last.split('.').first
|
110
|
+
require "#{entities_path}/#{entity_id}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def load_local_helpers(repo_id)
|
93
115
|
helpers_path = "./#{root}/#{repo_id}/helpers"
|
94
|
-
Dir["#{helpers_path}/*.rb"].each
|
116
|
+
Dir["#{helpers_path}/*.rb"].each do |file|
|
95
117
|
helper_id = file.split('/').last.split('.').first
|
96
118
|
require "#{helpers_path}/#{helper_id}"
|
97
|
-
|
119
|
+
end
|
98
120
|
end
|
99
|
-
|
100
121
|
end
|
101
122
|
|
102
123
|
end
|
@@ -4,12 +4,12 @@ module Tzispa
|
|
4
4
|
module Data
|
5
5
|
|
6
6
|
class Transporter
|
7
|
-
|
8
7
|
DEFAULT_ENCODING = 'ASCII-8BIT'
|
9
8
|
DEFAULT_BUFFER_SIZE = 2048
|
10
9
|
DEFAULT_LINE_SEPARATOR = "\n"
|
11
10
|
|
12
|
-
attr_reader :filename, :buffer_size, :encoding, :line_separator, :data_separator,
|
11
|
+
attr_reader :filename, :buffer_size, :encoding, :line_separator, :data_separator,
|
12
|
+
:line_size, :strip, :check_count, :lines, :errors
|
13
13
|
|
14
14
|
def initialize(fn, options = {})
|
15
15
|
@filename = fn
|
@@ -20,7 +20,7 @@ module Tzispa
|
|
20
20
|
@data_separator = options[:data_separator]
|
21
21
|
@strip = options[:strip]
|
22
22
|
@check_count = options[:check_count]
|
23
|
-
@errors =
|
23
|
+
@errors = []
|
24
24
|
end
|
25
25
|
|
26
26
|
def exist?
|
@@ -28,103 +28,114 @@ module Tzispa
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def import(dataset, columns)
|
31
|
-
lines = 0
|
32
31
|
errors.clear
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
buffer << values
|
40
|
-
flush_data(dataset, columns, buffer) if lines % buffer_size == 0
|
41
|
-
end
|
42
|
-
flush_data dataset, columns, buffer
|
43
|
-
}
|
44
|
-
ds_count = dataset.count
|
45
|
-
raise TransporterRecordCount.new ("Lines count (#{lines}) and records count (#{ds_count}) does not match") if check_count && lines != ds_count
|
46
|
-
[lines, ds_count]
|
32
|
+
File.open(filename, "rb:#{encoding}") do |fh|
|
33
|
+
lines = process_file_import fh, dataset, columns
|
34
|
+
ds_count = dataset.count
|
35
|
+
raise TransporterRecordCount.new(lines, ds_count) if check_count && lines != ds_count
|
36
|
+
[lines, ds_count]
|
37
|
+
end
|
47
38
|
end
|
48
39
|
|
49
40
|
def export(data, append = false, &block)
|
50
41
|
count = 0
|
51
|
-
File.open(filename, append ? "ab:#{encoding}" : "wb:#{encoding}")
|
52
|
-
lock(fh, File::LOCK_EX)
|
42
|
+
File.open(filename, append ? "ab:#{encoding}" : "wb:#{encoding}") do |fh|
|
43
|
+
lock(fh, File::LOCK_EX) do |lfh|
|
53
44
|
if data.is_a? Hash
|
54
45
|
lfh << build_line(data, &block)
|
55
|
-
count +=
|
46
|
+
count += 1
|
56
47
|
else
|
57
|
-
data.each
|
48
|
+
data.each do |row|
|
58
49
|
lfh << build_line(row, &block)
|
59
|
-
count +=
|
60
|
-
|
50
|
+
count += 1
|
51
|
+
end
|
61
52
|
end
|
62
|
-
|
63
|
-
|
53
|
+
end
|
54
|
+
end
|
64
55
|
count
|
65
56
|
end
|
66
57
|
|
67
58
|
private
|
68
59
|
|
69
|
-
def
|
70
|
-
|
60
|
+
def process_file_import(fh, dataset, columns)
|
61
|
+
while (line = read_line(fh, lines))
|
62
|
+
lines = (lines || 0) + 1
|
63
|
+
values = block_given? ? yield(line) : line.split(data_separator)
|
64
|
+
columns? lines, values.count
|
65
|
+
(buffer ||= []) << values
|
66
|
+
flush_data(dataset, columns, buffer)
|
67
|
+
end
|
68
|
+
flush_data dataset, columns, buffer, true
|
69
|
+
lines
|
70
|
+
end
|
71
|
+
|
72
|
+
def build_line(data)
|
73
|
+
String.new(block_given? ? yield(data) : data.values.join(data_separator)).tap do |line|
|
71
74
|
line << line_separator
|
72
|
-
|
75
|
+
end
|
73
76
|
end
|
74
77
|
|
75
78
|
def read_line(fh, lines)
|
79
|
+
return if fh.eof?
|
76
80
|
if line_size
|
77
|
-
fh.read(line_size).tap {
|
78
|
-
res = fh.gets(line_separator) if strip && !fh.eof?
|
79
|
-
raise TransporterBadFormat.new("Bad file format at line #{lines+1}") unless res.nil? || res.strip.empty?
|
80
|
-
} unless fh.eof?
|
81
|
+
fh.read(line_size).tap { separator? fh, lines + 1 }
|
81
82
|
else
|
82
|
-
fh.gets(line_separator).tap { |line|
|
83
|
-
line.rstrip! if strip
|
84
|
-
} unless fh.eof?
|
83
|
+
fh.gets(line_separator).tap { |line| line.rstrip! if strip }
|
85
84
|
end
|
86
85
|
end
|
87
86
|
|
88
|
-
def flush_data(dataset, columns, buffer)
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
87
|
+
def flush_data(dataset, columns, buffer, force = false)
|
88
|
+
return unless (buffer.count % buffer_size).zero? || force
|
89
|
+
dataset.import(columns, buffer)
|
90
|
+
buffer.clear
|
91
|
+
rescue
|
92
|
+
insert_by_row(dataset, columns, buffer)
|
95
93
|
end
|
96
94
|
|
97
95
|
def insert_by_row(dataset, columns, buffer)
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end
|
105
|
-
}
|
106
|
-
ensure
|
107
|
-
buffer.clear
|
96
|
+
buffer.each do |row|
|
97
|
+
begin
|
98
|
+
dataset.insert columns, row
|
99
|
+
rescue => err
|
100
|
+
errors << "#{err} in #{row.inspect}\n#{err.backtrace&.join("\n")}"
|
101
|
+
end
|
108
102
|
end
|
103
|
+
ensure
|
104
|
+
buffer.clear
|
109
105
|
end
|
110
106
|
|
111
107
|
def lock(file, mode)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
end
|
108
|
+
return unless file.flock(mode)
|
109
|
+
begin
|
110
|
+
yield file
|
111
|
+
ensure
|
112
|
+
file.flock(File::LOCK_UN)
|
113
|
+
end
|
119
114
|
end
|
120
115
|
|
116
|
+
def separator?(fh, line)
|
117
|
+
res = fh.gets(line_separator) if strip && !fh.eof?
|
118
|
+
raise TransporterBadFormat.new(line) unless res.nil? || res.strip.empty?
|
119
|
+
end
|
121
120
|
|
121
|
+
def columns?(line, cols)
|
122
|
+
raise TransporterBadFormat.new(line) unless cols == columns.count
|
123
|
+
end
|
122
124
|
end
|
123
125
|
|
124
126
|
class TransporterError < StandardError; end
|
125
|
-
class TransporterBadFormat < TransporterError; end
|
126
|
-
class TransporterRecordCount < TransporterError; end
|
127
127
|
|
128
|
+
class TransporterBadFormat < TransporterError
|
129
|
+
def initialize(line)
|
130
|
+
super "Bad file format at line #{line}: columns number does not match"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class TransporterRecordCount < TransporterError
|
135
|
+
def initialize(lines, count)
|
136
|
+
super "Lines count (#{lines}) and records count (#{count}) does not match"
|
137
|
+
end
|
138
|
+
end
|
128
139
|
|
129
140
|
end
|
130
141
|
end
|
data/lib/tzispa/data/version.rb
CHANGED
data/lib/tzispa/data.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Tzispa
|
2
4
|
module Data
|
3
5
|
|
4
|
-
require 'sequel'
|
5
|
-
require 'tzispa/data/adapter'
|
6
6
|
require 'tzispa/data/adapter_pool'
|
7
7
|
require 'tzispa/data/repository'
|
8
|
+
require 'tzispa/data/transporter'
|
8
9
|
require 'tzispa/data/entity'
|
9
10
|
require 'tzispa/data/version'
|
10
11
|
|
11
|
-
|
12
12
|
end
|
13
13
|
end
|
data/lib/tzispa_data.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tzispa_data
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juan Antonio Piñero
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -48,7 +48,6 @@ files:
|
|
48
48
|
- CHANGELOG.md
|
49
49
|
- README.md
|
50
50
|
- lib/tzispa/data.rb
|
51
|
-
- lib/tzispa/data/adapter.rb
|
52
51
|
- lib/tzispa/data/adapter_pool.rb
|
53
52
|
- lib/tzispa/data/entity.rb
|
54
53
|
- lib/tzispa/data/repository.rb
|
data/lib/tzispa/data/adapter.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'sequel'
|
2
|
-
require 'sequel/connection_pool/threaded'
|
3
|
-
require 'forwardable'
|
4
|
-
|
5
|
-
module Tzispa
|
6
|
-
module Data
|
7
|
-
|
8
|
-
class AdapterPool
|
9
|
-
include Enumerable
|
10
|
-
extend Forwardable
|
11
|
-
|
12
|
-
def_delegators :@pool, :has_key?, :keys
|
13
|
-
attr_reader :default
|
14
|
-
|
15
|
-
def initialize(config, default=nil)
|
16
|
-
Sequel.default_timezone = :utc
|
17
|
-
Sequel.datetime_class = DateTime
|
18
|
-
Sequel.extension :core_extensions
|
19
|
-
@default = default || config.first[0].to_sym
|
20
|
-
@pool = Hash.new.tap { |hpool|
|
21
|
-
config.each { |key, value|
|
22
|
-
conn = Sequel.connect value.adapter, :pool_class => Sequel::ThreadedConnectionPool
|
23
|
-
if value.connection_validation
|
24
|
-
conn.extension(:connection_validator)
|
25
|
-
conn.pool.connection_validation_timeout = value.connection_validation_timeout
|
26
|
-
end
|
27
|
-
hpool[key.to_sym] = conn
|
28
|
-
}
|
29
|
-
}
|
30
|
-
end
|
31
|
-
|
32
|
-
def [](name=nil)
|
33
|
-
@pool[name&.to_sym || default]
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|