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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d93f8550dc521d79171ed9b7a38ea5b3cd130260
4
- data.tar.gz: f984c6b25aa8f4dd2b6bc663a5099e98eaac68ef
3
+ metadata.gz: d986faff8dbc3e2fd759f03fe06790ad61e9b8f4
4
+ data.tar.gz: 8db044d027d97790e03b3300b00180d195f86c85
5
5
  SHA512:
6
- metadata.gz: 4ef78463937d73e27ddf3cfa42546d243f4833e572161c52c437867517ec128720451570aa1336f821ca3178cb5898a8ad24ecb277ae0bd2fd75e82003755def
7
- data.tar.gz: af14bd698ae4086476728169758bc7a4627e4bbe488deced716c3adcfea43f3376446bd59f342c3457ac20251bcbf7e7560a52a95e1ea0dfaa5df8ed5f745587
6
+ metadata.gz: 5238d861f3add98d6ee943384b306d811d9b5f1bd8ef1b712b38627aa9cb5835654db09bfd0df0caad6955cd9708db3473253dfb4d539b2332ef7a7d48f61562
7
+ data.tar.gz: 0d267d340bf788b6c1d923237976473de5ff49742ef02f5f8a49cd284e67278abc62316d0daa41abbc5d4da55e1c2f5a71ee76f4e0602da2d9013ae83fd61d0e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  Tzispa Data
2
2
 
3
+ ## v0.4.0
4
+ - new repository configuration schema
5
+ - code separation between models and entities into independent namespaces
6
+
3
7
  ## v0.3.0
4
8
  - local repository helpers preloading
5
9
  - fix local repository loader
@@ -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
- @default = default || config.first[0].to_sym
16
- @pool = Hash.new.tap { |hash|
17
- config.each { |key, value|
18
- hash[key.to_sym] = Sequel.connect value.adapter
19
- }
20
- }
21
- #@adapter_keys = @adapters.keys
22
- Sequel.default_timezone = :utc
23
- Sequel.datetime_class = DateTime
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
@@ -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?(:@@__entity_class) ?
22
- class_variable_get(:@@__entity_class) :
23
- class_variable_set(:@@__entity_class, "#{self}Entity".constantize )
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
- alias_method :entity, :entity_class
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 Repository
18
+ class UnknownModel < DataError; end
16
19
 
17
- using Tzispa::Utils
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 = LOCAL_REPO_ROOT)
27
+ def initialize(config, root = nil)
24
28
  @config = config
25
- @root = root
26
- @pool = Hash.new
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 || @adapters.default
32
- raise UnknownModel.new("The '#{model}' model does not exists in the adapter '#{selected_repo}'") unless @pool.has_key?(selected_repo) && @pool[selected_repo].has_key?(model.to_sym)
33
- @pool[selected_repo][model.to_sym]
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
- @pool[repo_id || @adapters.default].values
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 || @adapters.default
42
- @pool[selected_repo][:'__repository_module'] ||= repository_module(selected_repo)
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 { |id, cfg|
47
- Mutex.new.synchronize {
48
- @pool[id] = Hash.new
49
- Sequel::Model.db = @adapters[id]
50
- if cfg.local
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.split(',').each { |ext|
68
- model_class.db.extension ext.to_sym
69
- } if config.respond_to? :extensions
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 { |file|
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 load_local_helpers(repo_id, config)
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 { |file|
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, :line_size, :strip, :check_count, :lines, :errors
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 = Array.new
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
- buffer = Array.new
34
- File.open(filename, "rb:#{encoding}") { |fh|
35
- while line = read_line(fh, lines)
36
- lines += 1
37
- values = block_given? ? yield(line) : line.split(data_separator)
38
- raise TransporterBadFormat.new("Bad file format at line #{lines}: columns number does not match with values") unless values.count == columns.count
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}") { |fh|
52
- lock(fh, File::LOCK_EX) { |lfh|
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 += 1
46
+ count += 1
56
47
  else
57
- data.each { |row|
48
+ data.each do |row|
58
49
  lfh << build_line(row, &block)
59
- count += 1
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 build_line(data, &block)
70
- String.new(block_given? ? yield(data) : data.values.join(data_separator)).tap { |line|
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 { |record|
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
- begin
90
- dataset.import(columns, buffer)
91
- buffer.clear
92
- rescue
93
- insert_by_row(dataset, columns, buffer)
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
- begin
99
- buffer.each { |row|
100
- begin
101
- dataset.insert columns, row
102
- rescue => err
103
- errors << "#{err} in #{row.inspect}\n#{err.backtrace&.join("\n")}"
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
- if file.flock(mode)
113
- begin
114
- yield file
115
- ensure
116
- file.flock(File::LOCK_UN)
117
- end
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
@@ -3,7 +3,7 @@
3
3
  module Tzispa
4
4
  module Data
5
5
 
6
- VERSION = '0.3.0'
6
+ VERSION = '0.4.0'
7
7
  NAME = 'Tzispa Data'
8
8
  GEM_NAME = 'tzispa_data'
9
9
 
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Tzispa
2
4
  module Data
3
5
 
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.3.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-02-13 00:00:00.000000000 Z
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
@@ -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