parxer 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +14 -0
- data/.hound.yml +4 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1038 -0
- data/.ruby-version +1 -0
- data/.travis.yml +12 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +4 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +140 -0
- data/Rakefile +5 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/docs/images/parxer-response.png +0 -0
- data/docs/images/superheroes-xls.png +0 -0
- data/lib/parxer.rb +10 -0
- data/lib/parxer/collections/attributes.rb +16 -0
- data/lib/parxer/collections/callbacks.rb +22 -0
- data/lib/parxer/collections/row_errors.rb +19 -0
- data/lib/parxer/collections/validators.rb +34 -0
- data/lib/parxer/dsl/dsl.rb +92 -0
- data/lib/parxer/formatters/base_formatter.rb +39 -0
- data/lib/parxer/formatters/boolean_formatter.rb +14 -0
- data/lib/parxer/formatters/custom_formatter.rb +13 -0
- data/lib/parxer/formatters/number_formatter.rb +25 -0
- data/lib/parxer/formatters/rut_formatter.rb +33 -0
- data/lib/parxer/formatters/string_formatter.rb +9 -0
- data/lib/parxer/parsers/base_parser.rb +80 -0
- data/lib/parxer/parsers/concerns/attributes.rb +25 -0
- data/lib/parxer/parsers/concerns/callback.rb +24 -0
- data/lib/parxer/parsers/concerns/config.rb +23 -0
- data/lib/parxer/parsers/concerns/formatter.rb +14 -0
- data/lib/parxer/parsers/concerns/validator.rb +75 -0
- data/lib/parxer/parsers/csv_parser.rb +13 -0
- data/lib/parxer/parsers/xls_parser.rb +21 -0
- data/lib/parxer/utils/context.rb +26 -0
- data/lib/parxer/utils/errors.rb +10 -0
- data/lib/parxer/utils/formatter_builder.rb +16 -0
- data/lib/parxer/utils/inherited_resource.rb +35 -0
- data/lib/parxer/utils/row_builder.rb +22 -0
- data/lib/parxer/validators/base_validator.rb +22 -0
- data/lib/parxer/validators/boolean_validator.rb +13 -0
- data/lib/parxer/validators/columns_validator.rb +19 -0
- data/lib/parxer/validators/custom_validator.rb +17 -0
- data/lib/parxer/validators/datetime_validator.rb +54 -0
- data/lib/parxer/validators/email_validator.rb +14 -0
- data/lib/parxer/validators/file_format_validator.rb +18 -0
- data/lib/parxer/validators/file_presence_validator.rb +9 -0
- data/lib/parxer/validators/inclusion_validator.rb +26 -0
- data/lib/parxer/validators/number_validator.rb +63 -0
- data/lib/parxer/validators/presence_validator.rb +9 -0
- data/lib/parxer/validators/rows_count_validator.rb +9 -0
- data/lib/parxer/validators/rut_validator.rb +32 -0
- data/lib/parxer/validators/url_validator.rb +11 -0
- data/lib/parxer/values/attribute.rb +19 -0
- data/lib/parxer/values/callback.rb +22 -0
- data/lib/parxer/values/row.rb +17 -0
- data/lib/parxer/version.rb +3 -0
- data/parxer.gemspec +32 -0
- metadata +247 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
module Parxer
|
2
|
+
module RowBuilder
|
3
|
+
def self.build(attributes)
|
4
|
+
Class.new(Parxer::Row) do
|
5
|
+
def self.column_accessor(attribute)
|
6
|
+
attr_accessor(attribute) unless method_defined?(attribute)
|
7
|
+
rescue NameError
|
8
|
+
raise Parxer::RowError.new("invalid '#{attribute}' method name")
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_attribute(attribute, value = nil)
|
12
|
+
self.class.column_accessor(attribute)
|
13
|
+
send("#{attribute}=", value) if value
|
14
|
+
end
|
15
|
+
|
16
|
+
attributes.each do |attribute|
|
17
|
+
column_accessor(attribute)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Parxer
|
2
|
+
module Validator
|
3
|
+
class Base
|
4
|
+
include Parxer::Context
|
5
|
+
|
6
|
+
attr_reader :config
|
7
|
+
|
8
|
+
def initialize(config = {})
|
9
|
+
@context = config.delete(:context)
|
10
|
+
@config = config
|
11
|
+
end
|
12
|
+
|
13
|
+
def id
|
14
|
+
self.class.name.demodulize.tableize.singularize.to_sym
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate
|
18
|
+
raise Parxer::ValidatorError.new("'validate' method not implemented")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Parxer
|
2
|
+
module Validator
|
3
|
+
class HeaderOrder < Base
|
4
|
+
def validate
|
5
|
+
context.attributes.each_with_index do |attribute, idx|
|
6
|
+
return false unless valid_header?(attribute, idx)
|
7
|
+
end
|
8
|
+
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def valid_header?(attribute, attribute_idx)
|
15
|
+
!!context.header[attribute_idx] && context.header[attribute_idx] == attribute.name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Parxer
|
2
|
+
module Validator
|
3
|
+
class Custom < Base
|
4
|
+
def id
|
5
|
+
config[:id].to_sym
|
6
|
+
end
|
7
|
+
|
8
|
+
def validate
|
9
|
+
if !config[:condition_proc].is_a?(Proc)
|
10
|
+
raise Parxer::ValidatorError.new("'condition_proc' needs to be a Proc")
|
11
|
+
end
|
12
|
+
|
13
|
+
instance_eval(&config[:condition_proc])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Parxer
|
2
|
+
module Validator
|
3
|
+
class Datetime < Base
|
4
|
+
def validate
|
5
|
+
v = context.value.to_s
|
6
|
+
return true if v.blank?
|
7
|
+
return false unless valid_datetime?(v)
|
8
|
+
return false unless valid_format?(v)
|
9
|
+
validate_range(v)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def valid_datetime?(v)
|
15
|
+
!!DateTime.parse(v)
|
16
|
+
rescue ArgumentError
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def date_format
|
21
|
+
config[:format]
|
22
|
+
end
|
23
|
+
|
24
|
+
def valid_format?(v)
|
25
|
+
return true unless date_format
|
26
|
+
!!DateTime.strptime(v, date_format)
|
27
|
+
rescue ArgumentError
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate_range(v)
|
32
|
+
v = v.to_datetime
|
33
|
+
return false unless validate_limit(:gt, ">", v)
|
34
|
+
return false unless validate_limit(:gteq, ">=", v)
|
35
|
+
return false unless validate_limit(:lt, "<", v)
|
36
|
+
return false unless validate_limit(:lteq, "<=", v)
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate_limit(limit_name, method, v)
|
41
|
+
r = valid_limit(limit_name)
|
42
|
+
return true if r.blank?
|
43
|
+
v.public_send(method, r)
|
44
|
+
end
|
45
|
+
|
46
|
+
def valid_limit(name)
|
47
|
+
limit = config[name].to_s
|
48
|
+
return if limit.blank?
|
49
|
+
return limit.to_datetime if valid_datetime?(limit)
|
50
|
+
raise Parxer::ValidatorError.new("'#{name}' has not a valid value for given datetime")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# rubocop:disable Metrics/LineLength
|
2
|
+
module Parxer
|
3
|
+
module Validator
|
4
|
+
class Email < Base
|
5
|
+
EMAIL_REGEXP = %r{\A[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\z}
|
6
|
+
|
7
|
+
def validate
|
8
|
+
v = context.value.to_s
|
9
|
+
return true if v.blank?
|
10
|
+
!!(v =~ EMAIL_REGEXP)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Parxer
|
2
|
+
module Validator
|
3
|
+
class FileFormat < Base
|
4
|
+
def validate
|
5
|
+
return false unless allowed_extensions.include?(context.file_extension)
|
6
|
+
!!context.raw_rows
|
7
|
+
rescue
|
8
|
+
false
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def allowed_extensions
|
14
|
+
config[:allowed_extensions].map(&:to_sym)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Parxer
|
2
|
+
module Validator
|
3
|
+
class Inclusion < Base
|
4
|
+
def validate
|
5
|
+
v = context.value.to_s
|
6
|
+
return true if v.blank?
|
7
|
+
options.include?(v)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def options
|
13
|
+
@options ||= begin
|
14
|
+
opts = config[:in]
|
15
|
+
raise Parxer::ValidatorError.new("'in' config option is required") if opts.blank?
|
16
|
+
|
17
|
+
if !opts.is_a?(Array)
|
18
|
+
raise Parxer::ValidatorError.new("'in' config option needs to be Array")
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.map(&:to_s)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Parxer
|
2
|
+
module Validator
|
3
|
+
class Number < Base
|
4
|
+
def validate
|
5
|
+
v = context.value.to_s
|
6
|
+
return true if v.blank?
|
7
|
+
return false unless validate_number(v)
|
8
|
+
return false unless validate_negative(v)
|
9
|
+
validate_range(v)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def allow_negative?
|
15
|
+
!!config[:allow_negative]
|
16
|
+
end
|
17
|
+
|
18
|
+
def only_integer?
|
19
|
+
!!config[:only_integer]
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate_number(v)
|
23
|
+
only_integer? ? valid_integer?(v) : valid_float?(v)
|
24
|
+
end
|
25
|
+
|
26
|
+
def valid_integer?(v)
|
27
|
+
!!(v.to_s =~ /\A[-+]?[0-9]+\z/)
|
28
|
+
end
|
29
|
+
|
30
|
+
def valid_float?(v)
|
31
|
+
!!Float(v)
|
32
|
+
rescue ArgumentError
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate_negative(v)
|
37
|
+
!(!allow_negative? && v.to_i.negative?)
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate_range(v)
|
41
|
+
v = only_integer? ? v.to_i : v.to_f
|
42
|
+
return false unless validate_limit(:gt, ">", v)
|
43
|
+
return false unless validate_limit(:gteq, ">=", v)
|
44
|
+
return false unless validate_limit(:lt, "<", v)
|
45
|
+
return false unless validate_limit(:lteq, "<=", v)
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_limit(limit_name, method, v)
|
50
|
+
r = valid_limit(limit_name)
|
51
|
+
return true if r.blank?
|
52
|
+
v.public_send(method, r)
|
53
|
+
end
|
54
|
+
|
55
|
+
def valid_limit(name)
|
56
|
+
limit = config[name]
|
57
|
+
return if limit.blank?
|
58
|
+
return (only_integer? ? limit.to_i : limit.to_f) if validate_number(limit)
|
59
|
+
raise Parxer::ValidatorError.new("'#{name}' has not a valid value for given number type")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Parxer
|
2
|
+
module Validator
|
3
|
+
class Rut < Base
|
4
|
+
def validate
|
5
|
+
rut = clean_rut
|
6
|
+
return true if rut.blank?
|
7
|
+
t = rut[0...-1].to_i
|
8
|
+
m = 0
|
9
|
+
s = 1
|
10
|
+
while t.positive?
|
11
|
+
s = (s + t % 10 * (9 - m % 6)) % 11
|
12
|
+
m += 1
|
13
|
+
t /= 10
|
14
|
+
end
|
15
|
+
v = if s.positive? then (s - 1).to_s else 'K' end
|
16
|
+
(v == rut.last.upcase)
|
17
|
+
rescue
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def clean_rut
|
24
|
+
rut = context.value.to_s
|
25
|
+
rut.to_s.upcase!
|
26
|
+
rut.delete!(".")
|
27
|
+
rut.delete!("-")
|
28
|
+
rut
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Parxer
|
2
|
+
class Attribute
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
attr_reader :id, :name, :validators, :formatter
|
6
|
+
|
7
|
+
def_delegators :validators, :add_validator
|
8
|
+
|
9
|
+
def initialize(id, name: nil)
|
10
|
+
@id = id.to_sym
|
11
|
+
@name = name
|
12
|
+
@validators = Parxer::Validators.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_formatter(formatter_name, config = {}, &block)
|
16
|
+
@formatter = Parxer::FormatterBuilder.build(formatter_name, config, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Parxer
|
2
|
+
class Callback
|
3
|
+
include Parxer::Context
|
4
|
+
|
5
|
+
attr_reader :type, :action, :config
|
6
|
+
|
7
|
+
def initialize(type: nil, action: nil, config: {})
|
8
|
+
@type = type.to_sym
|
9
|
+
@context = config.delete(:context)
|
10
|
+
@config = config
|
11
|
+
@action = action
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
if action.is_a?(Proc)
|
16
|
+
instance_eval(&action)
|
17
|
+
else
|
18
|
+
context.send(action)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Parxer
|
2
|
+
class Row
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
attr_reader :idx
|
6
|
+
|
7
|
+
def_delegators :errors, :add_error, :attribute_error, :attribute_error?, :errors?
|
8
|
+
|
9
|
+
def initialize(idx: nil)
|
10
|
+
@idx = idx
|
11
|
+
end
|
12
|
+
|
13
|
+
def errors
|
14
|
+
@errors ||= Parxer::RowErrors.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/parxer.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "parxer/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "parxer"
|
9
|
+
spec.version = Parxer::VERSION
|
10
|
+
spec.authors = ["Platanus", "Leandro Segovia"]
|
11
|
+
spec.email = ["rubygems@platan.us", "ldlsegovia@gmail.com"]
|
12
|
+
spec.homepage = "https://github.com/platanus/parxer/master"
|
13
|
+
spec.summary = "ruby gem to parse data"
|
14
|
+
spec.description = "ruby gem to parse data from different source types"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "require_all"
|
23
|
+
spec.add_dependency "activesupport"
|
24
|
+
spec.add_dependency "roo"
|
25
|
+
spec.add_dependency "roo-xls"
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.4"
|
29
|
+
spec.add_development_dependency "guard-rspec", "~> 4.7"
|
30
|
+
spec.add_development_dependency "pry"
|
31
|
+
spec.add_development_dependency "coveralls"
|
32
|
+
end
|