tzispa_data 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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