cascade-rb 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +1 -1
  3. data/.rubocop.yml +11 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +3 -1
  6. data/README.md +2 -25
  7. data/Rakefile +7 -4
  8. data/cascade.gemspec +22 -21
  9. data/lib/cascade.rb +6 -7
  10. data/lib/cascade/columns_matching.rb +14 -13
  11. data/lib/cascade/complex_fields.rb +5 -3
  12. data/lib/cascade/complex_fields/array_processor.rb +2 -0
  13. data/lib/cascade/complex_fields/boolean.rb +3 -1
  14. data/lib/cascade/complex_fields/currency.rb +8 -4
  15. data/lib/cascade/concerns/statistics_collectible.rb +3 -1
  16. data/lib/cascade/data_parser.rb +4 -2
  17. data/lib/cascade/error_handler.rb +4 -7
  18. data/lib/cascade/exceptions.rb +5 -3
  19. data/lib/cascade/exceptions/unknown_presenter_type.rb +2 -0
  20. data/lib/cascade/exceptions/unsupported_component.rb +3 -1
  21. data/lib/cascade/exceptions/wrong_mapping_format.rb +2 -0
  22. data/lib/cascade/helpers/hash.rb +2 -0
  23. data/lib/cascade/registry.rb +6 -3
  24. data/lib/cascade/row_processor.rb +23 -22
  25. data/lib/cascade/statistics.rb +4 -2
  26. data/lib/cascade/statistics_stores.rb +5 -3
  27. data/lib/cascade/statistics_stores/abstract_store.rb +2 -0
  28. data/lib/cascade/statistics_stores/array_store.rb +2 -0
  29. data/lib/cascade/statistics_stores/counter_store.rb +2 -0
  30. data/lib/cascade/version.rb +3 -1
  31. data/spec/integration_spec.rb +70 -0
  32. data/spec/lib/columns_matching_spec.rb +18 -26
  33. data/spec/lib/complex_fields/array_processor_spec.rb +6 -4
  34. data/spec/lib/complex_fields/boolean_spec.rb +8 -6
  35. data/spec/lib/complex_fields/currency_spec.rb +11 -9
  36. data/spec/lib/concerns/statistics_collectible_spec.rb +6 -4
  37. data/spec/lib/data_parser_spec.rb +14 -11
  38. data/spec/lib/error_handler_spec.rb +21 -15
  39. data/spec/lib/exceptions/unknown_presenter_type_spec.rb +5 -3
  40. data/spec/lib/exceptions/unsupported_component_spec.rb +10 -8
  41. data/spec/lib/exceptions/wrong_mapping_format_spec.rb +5 -3
  42. data/spec/lib/helpers/hash_spec.rb +7 -5
  43. data/spec/lib/registry_spec.rb +9 -8
  44. data/spec/lib/row_processor_spec.rb +33 -33
  45. data/spec/lib/statistics_spec.rb +14 -12
  46. data/spec/lib/statistics_stores/abstract_store_spec.rb +7 -5
  47. data/spec/lib/statistics_stores/array_store_spec.rb +8 -6
  48. data/spec/lib/statistics_stores/counter_store_spec.rb +8 -6
  49. data/spec/spec_helper.rb +16 -15
  50. metadata +28 -28
  51. data/.ruby-style.yml +0 -1063
  52. data/lib/cascade/helpers/configuration.rb +0 -32
  53. data/spec/lib/helpers/configuration_spec.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a7c6aadcd330b397cf46bd735ec764579c5a221d
4
- data.tar.gz: a3e28fcdd18696b60c7ac067f3cbbfc87fa5acca
3
+ metadata.gz: 1fed47a0100266f90223d01c1bb3d44b0051793c
4
+ data.tar.gz: 8eef3a7b5d3ecd9f4da62a863d0b7c980ab1b828
5
5
  SHA512:
6
- metadata.gz: 3a4a9bd0fb79bbe8e23f7ef0255c5ea0b650018325189ac2204aa2281de0cf75933ab475a18cc7c0c310dc0748c7dd7a84c84f37453a3dec5c9c38a6badd005b
7
- data.tar.gz: 556e36c7e5c1d20f2f17fe69c36e9596297820d2c92b6bf8e5c9664d949b9065dea650fbdde1baaf2e1d1e1a816713b0b036c2027d4746db2a1003b16cf875f8
6
+ metadata.gz: 2a61fe4e44402f142faa7b00dbd8c1dcde6296c75232668eb96fe09ed1de6d8f71bee6c87140febf0a969defbfff24dbcdaf833362c19e79a849599f3f98c395
7
+ data.tar.gz: 8505f5e2f46df476fc9f75bb31aaffd8307a018ffdd6b9658ee89b591cde724a64e67beb2d44d49a159174a62d655d17a9a2df89e1bcbb62c158d6cf4988428c
data/.hound.yml CHANGED
@@ -1,2 +1,2 @@
1
1
  ruby:
2
- config_file: .ruby-style.yml
2
+ config_file: .rubocop.yml
@@ -0,0 +1,11 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ UseCache: false
4
+ Exclude:
5
+ - 'vendor/**/*'
6
+
7
+ Metrics/BlockLength:
8
+ Exclude:
9
+ - 'spec/**/*.rb'
10
+ Style/Documentation:
11
+ Enabled: false
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.8
4
+ - 2.4.5
5
+ - 2.5.3
6
+ before_install:
7
+ - gem update --system
8
+ - gem --version
9
+ - gem install bundler
10
+ script:
11
+ - bundle exec rake
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in elasticquery.gemspec
4
6
  gemspec
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # [Cascade]
2
2
 
3
- [![Codeship Status for ignat-zakrevsky/cascade](https://codeship.com/projects/d7590880-9943-0132-4aa6-1e41bc68e178/status?branch=master)](https://codeship.com/projects/63625) [![Code Climate](https://codeclimate.com/github/ignat-zakrevsky/cascade/badges/gpa.svg)](https://codeclimate.com/github/ignat-zakrevsky/cascade) [![Test Coverage](https://codeclimate.com/github/ignat-zakrevsky/cascade/badges/coverage.svg)](https://codeclimate.com/github/ignat-zakrevsky/cascade) [![Gem Version](https://badge.fury.io/rb/cascade-rb.svg)](http://badge.fury.io/rb/cascade-rb) [![Codacy Badge](https://api.codacy.com/project/badge/grade/3d27b4bc418341dcb4f704cafebffd4d)](https://www.codacy.com/app/iezakrevsky/cascade)
3
+ [![Build Status](https://travis-ci.com/ignat-z/cascade.svg?branch=master)](https://travis-ci.com/ignat-z/cascade) [![Gem Version](https://badge.fury.io/rb/cascade-rb.svg)](http://badge.fury.io/rb/cascade-rb)
4
4
 
5
5
  The main goal of this gem is to provide some kind of template for parsing files.
6
6
  Usually, file parsing process consists of the following steps:
@@ -15,12 +15,6 @@ Usually, file parsing process consists of the following steps:
15
15
 
16
16
  Cascade pretends to simplify main part of this step to save your time.
17
17
 
18
- ## Examples
19
-
20
- [Minimal working example](https://github.com/ignat-zakrevsky/cascade-example)
21
-
22
- [More complicated example](https://github.com/ignat-zakrevsky/cascade-example/tree/json-example)
23
-
24
18
  ## Installation
25
19
  Install the cascade-rb package from Rubygems:
26
20
  ```
@@ -38,20 +32,6 @@ Require gem files
38
32
  require 'cascade'
39
33
  ```
40
34
 
41
- Configure it!
42
- ```ruby
43
- Cascade.configuration do
44
- # [Object#call] will be used for undefined fields types
45
- Cascade::RowProcessor.deafult_presenter
46
- # [Boolean] will throw exception in case of unavailable presenter
47
- Cascade::RowProcessor.use_default_presenter
48
- # [String] filepath with columns mapping, see below
49
- Cascade::ColumnsMatching.mapping_file
50
- # [Boolean] will raise fields parsing exceptions
51
- Cascade::ErrorHandler.raise_parse_errors
52
- end
53
- ```
54
-
55
35
  Provide enumerable object for parsing and run it!
56
36
  ```ruby
57
37
  Cascade::DataParser.new(data_provider: Csv.open("data_test.csv")).call
@@ -111,11 +91,8 @@ Provide all this stuff into data parser
111
91
  ```ruby
112
92
  Cascade::DataParser.new(
113
93
  data_provider: ParserJSON.new.open("data_test.csv"),
114
- row_processor: Cascade::RowProcessor.new(date: DateParser.new),
94
+ row_processor: Cascade::RowProcessor.new(ext_parsers: { date: DateParser.new }),
115
95
  data_saver: PERSON_SAVER
116
96
  ).call
117
97
  ```
118
98
  And that's all!
119
- [Example](https://github.com/ignat-zakrevsky/cascade-example/blob/json-example/main.rb)
120
- ## Conventions
121
- I'm fan of callable object as consequence I prefer #call methods for classes with one responsibility. [Nice video](http://www.rubytapas.com/episodes/35-Callable) that describes benefits of such approach
data/Rakefile CHANGED
@@ -1,9 +1,12 @@
1
- require "rake/testtask"
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake/testtask'
4
+ require 'bundler/gem_tasks'
2
5
 
3
6
  Rake::TestTask.new do |task|
4
- task.libs << "lib"
5
- task.libs << "spec"
6
- task.pattern = "spec/**/*_spec.rb"
7
+ task.libs << 'lib'
8
+ task.libs << 'spec'
9
+ task.pattern = 'spec/**/*_spec.rb'
7
10
  end
8
11
 
9
12
  task default: [:test]
@@ -1,31 +1,32 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "cascade/version"
5
+ require 'cascade/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "cascade-rb"
8
+ spec.name = 'cascade-rb'
8
9
  spec.version = Cascade::VERSION
9
- spec.authors = ["Ignat Zakrevsky"]
10
- spec.email = %w(iezakrevsky@gmail.com)
11
- spec.summary = "Ruby data parser gem."
12
- spec.description = "Highly customizable data parser with a lot of DI"
13
- spec.homepage = "https://github.com/ignat-zakrevsky/cascade"
14
- spec.license = "MIT"
10
+ spec.authors = ['Ignat Zakrevsky']
11
+ spec.email = %w[iezakrevsky@gmail.com]
12
+ spec.summary = 'Ruby data parser gem.'
13
+ spec.description = 'Highly customizable data parser with a lot of DI'
14
+ spec.homepage = 'https://github.com/ignat-zakrevsky/cascade'
15
+ spec.license = 'MIT'
15
16
 
16
17
  spec.files = `git ls-files -z`.split("\x0")
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
20
+ spec.require_paths = ['lib']
20
21
 
21
- spec.add_development_dependency "bundler", "~> 1.6"
22
- spec.add_development_dependency "yard"
23
- spec.add_development_dependency "rake"
24
- spec.add_development_dependency "minitest"
25
- spec.add_development_dependency "rr"
26
- spec.add_development_dependency "shoulda-matchers", "2.8.0"
27
- spec.add_development_dependency "simplecov"
28
- spec.add_development_dependency "pry"
29
- spec.add_development_dependency "rubocop"
30
- spec.add_development_dependency "codeclimate-test-reporter"
22
+ spec.add_development_dependency 'bundler'
23
+ spec.add_development_dependency 'codeclimate-test-reporter'
24
+ spec.add_development_dependency 'minitest'
25
+ spec.add_development_dependency 'pry'
26
+ spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'rr'
28
+ spec.add_development_dependency 'rubocop'
29
+ spec.add_development_dependency 'shoulda-matchers', '2.8.0'
30
+ spec.add_development_dependency 'simplecov'
31
+ spec.add_development_dependency 'yard'
31
32
  end
@@ -1,11 +1,10 @@
1
- require "cascade/version"
2
- require "cascade/columns_matching"
3
- require "cascade/row_processor"
4
- require "cascade/helpers/configuration"
1
+ # frozen_string_literal: true
2
+
3
+ require 'cascade/version'
4
+ require 'cascade/columns_matching'
5
+ require 'cascade/row_processor'
5
6
 
6
7
  # Base gem module
7
8
  module Cascade
8
- extend Configuration
9
-
10
- autoload :DataParser, "cascade/data_parser"
9
+ autoload :DataParser, 'cascade/data_parser'
11
10
  end
@@ -1,42 +1,43 @@
1
- require "yaml"
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
2
4
  require 'forwardable'
3
- require "cascade/exceptions"
4
- require "cascade/helpers/configuration"
5
+ require 'cascade/exceptions'
5
6
 
6
7
  module Cascade
7
8
  class ColumnsMatching
8
9
  extend Forwardable
9
- extend Configuration
10
10
 
11
- define_setting :mapping_file
11
+ ROOT_KEY = 'mapping'
12
12
 
13
13
  def_delegator :supported_keys, :index
14
14
 
15
15
  def initialize(options = {})
16
- @filepath = options[:filepath]
17
- @content = options.fetch(:content) { parse_content_file }
16
+ @content = options.fetch(:content) do
17
+ parse_content_file(options.fetch(:filepath))
18
+ end
18
19
  end
19
20
 
20
21
  # Defines set of possible keys that can be used for iterating through
21
- # parsed line
22
+ # the parsed line
22
23
  #
23
- # @return [Array] of supported keys
24
+ # @return [Array] of the supported keys
24
25
  def supported_keys
25
26
  @supported_keys ||= @content.keys
26
27
  end
27
28
 
28
29
  # Presenter for passed key
29
30
  #
30
- # @return [Symbol] with curresponding value
31
+ # @return [Symbol] with the corresponding value
31
32
  def column_type(key)
32
33
  @content[key].to_sym
33
34
  end
34
35
 
35
36
  private
36
37
 
37
- def parse_content_file
38
- content = YAML.load_file(@filepath || self.class.mapping_file)
39
- (content && content["mapping"]) || raise(Cascade::WrongMappingFormat.new)
38
+ def parse_content_file(filepath)
39
+ content = YAML.load_file(filepath)
40
+ (content && content[ROOT_KEY]) || raise(Cascade::WrongMappingFormat)
40
41
  end
41
42
  end
42
43
  end
@@ -1,3 +1,5 @@
1
- require "cascade/complex_fields/currency"
2
- require "cascade/complex_fields/boolean"
3
- require "cascade/complex_fields/array_processor"
1
+ # frozen_string_literal: true
2
+
3
+ require 'cascade/complex_fields/currency'
4
+ require 'cascade/complex_fields/boolean'
5
+ require 'cascade/complex_fields/array_processor'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Cascade
2
4
  module ComplexFields
3
5
  class ArrayProcessor
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Cascade
2
4
  module ComplexFields
3
5
  class Boolean
4
- TRUE_VALUES = ["True", "true", "x", "+", true]
6
+ TRUE_VALUES = ['True', 'true', 'x', '+', true].freeze
5
7
 
6
8
  def call(value)
7
9
  TRUE_VALUES.include?(value)
@@ -1,5 +1,7 @@
1
- require "bigdecimal"
2
- require "bigdecimal/util"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bigdecimal'
4
+ require 'bigdecimal/util'
3
5
 
4
6
  module Cascade
5
7
  module ComplexFields
@@ -12,11 +14,13 @@ module Cascade
12
14
  private
13
15
 
14
16
  def normalized_value(value)
15
- String(value).tr(",", ".").tr(" ", "")
17
+ String(value).tr(',', '.').tr(' ', '')
16
18
  end
17
19
 
18
20
  def valid?(value)
19
- true if Float(value) rescue false
21
+ true if Float(value)
22
+ rescue StandardError
23
+ false
20
24
  end
21
25
  end
22
26
  end
@@ -1,4 +1,6 @@
1
- require "cascade/statistics"
1
+ # frozen_string_literal: true
2
+
3
+ require 'cascade/statistics'
2
4
 
3
5
  module Cascade
4
6
  module StatisticsCollectible
@@ -1,5 +1,7 @@
1
- require "cascade/columns_matching"
2
- require "cascade/registry"
1
+ # frozen_string_literal: true
2
+
3
+ require 'cascade/columns_matching'
4
+ require 'cascade/registry'
3
5
 
4
6
  module Cascade
5
7
  class DataParser
@@ -1,12 +1,8 @@
1
- require "cascade/helpers/configuration"
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Cascade
4
4
  class ErrorHandler
5
- extend Configuration
6
-
7
- define_setting :raise_parse_errors, false
8
-
9
- HANDLING_EXCEPTIONS = [IndexError]
5
+ HANDLING_EXCEPTIONS = [IndexError].freeze
10
6
  DEFAULT_ERROR_STORE = lambda do |row, exception|
11
7
  @errors ||= []
12
8
  @errors << [row, exception.to_s]
@@ -14,6 +10,7 @@ module Cascade
14
10
 
15
11
  def initialize(options = {})
16
12
  @error_store = options.fetch(:error_store) { DEFAULT_ERROR_STORE }
13
+ @raise_parse_errors = options.fetch(:raise_parse_errors, false)
17
14
  @handling_exceptions = options.fetch(:handling_exceptions) do
18
15
  HANDLING_EXCEPTIONS
19
16
  end
@@ -27,7 +24,7 @@ module Cascade
27
24
  yield
28
25
  rescue *@handling_exceptions => exception
29
26
  @error_store.call(row, exception)
30
- raise exception if self.class.raise_parse_errors
27
+ raise exception if @raise_parse_errors
31
28
  end
32
29
  end
33
30
  end
@@ -1,3 +1,5 @@
1
- require "cascade/exceptions/wrong_mapping_format"
2
- require "cascade/exceptions/unknown_presenter_type"
3
- require "cascade/exceptions/unsupported_component"
1
+ # frozen_string_literal: true
2
+
3
+ require 'cascade/exceptions/wrong_mapping_format'
4
+ require 'cascade/exceptions/unknown_presenter_type'
5
+ require 'cascade/exceptions/unsupported_component'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Cascade
2
4
  class UnknownPresenterType < ::StandardError; end
3
5
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Cascade
2
4
  class UnsupportedComponent < ::StandardError
3
- MESSAGE = "You should provide data provider that respond to `each` method"
5
+ MESSAGE = 'You should provide data provider that respond to `each` method'
4
6
 
5
7
  def initialize(msg = MESSAGE)
6
8
  super
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Cascade
2
4
  class WrongMappingFormat < ::StandardError; end
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HashRefinements
2
4
  refine Hash do
3
5
  # Merges the caller into +other_hash+
@@ -1,12 +1,15 @@
1
- require "cascade/error_handler"
2
- require "cascade/row_processor"
1
+ # frozen_string_literal: true
2
+
3
+ require 'cascade/error_handler'
4
+ require 'cascade/row_processor'
3
5
 
4
6
  module Cascade
5
7
  module Registry
6
8
  PUTS_DATA_SAVER = ->(*args) { p args }
9
+ SELF_PROCESSOR = ->(row) { row }
7
10
 
8
11
  def self.row_processor
9
- RowProcessor.new
12
+ SELF_PROCESSOR
10
13
  end
11
14
 
12
15
  def self.error_handler
@@ -1,27 +1,27 @@
1
- require "cascade/complex_fields"
2
- require "cascade/exceptions"
3
- require "cascade/helpers/configuration"
4
- require "cascade/helpers/hash"
1
+ # frozen_string_literal: true
2
+
3
+ require 'cascade/complex_fields'
4
+ require 'cascade/exceptions'
5
+ require 'cascade/helpers/hash'
5
6
 
6
7
  module Cascade
7
8
  class RowProcessor
8
- extend Configuration
9
9
  using HashRefinements
10
10
 
11
- DEFAULT_PROCESSOR = ->(value) { value && value.to_s.strip }
12
-
13
- define_setting :use_default_presenter, false
14
- define_setting :deafult_presenter, -> { DEFAULT_PROCESSOR }
11
+ DEFAULT_PROCESSOR = ->(value) { value&.to_s&.strip }
15
12
 
16
13
  def initialize(options = {})
17
- @options = options
18
- @columns_matching = options[:columns_matching] || ColumnsMatching.new
14
+ @options = options
15
+ @ext_presenters = options[:ext_presenters].to_h
16
+ @columns_matching = options.fetch(:columns_matching)
17
+ @use_default_presenter = options.fetch(:use_default_presenter, false)
18
+ @deafult_presenter = options.fetch(:deafult_presenter, DEFAULT_PROCESSOR)
19
19
  end
20
20
 
21
21
  # Iterates through object using columns values supported keys as keys for
22
- # iterating, then parse it by curresponding parser.
22
+ # iterating, then parse it by the corresponding parser.
23
23
  #
24
- # @param row [Hash] the object retrieved from CSV
24
+ # @param row [Hash] the object retrieved from data parser
25
25
  # @return [Hash] the object with parsed columns
26
26
  def call(row)
27
27
  @columns_matching.supported_keys.inject({}) do |result, key|
@@ -37,27 +37,28 @@ module Cascade
37
37
 
38
38
  def receive_presenter(column_name)
39
39
  presenter = presenters[@columns_matching.column_type(column_name)]
40
- if presenter.nil? && !self.class.use_default_presenter
41
- raise Cascade::UnknownPresenterType.new
40
+ if presenter.nil? && !@use_default_presenter
41
+ raise Cascade::UnknownPresenterType
42
42
  end
43
- presenter || self.class.deafult_presenter
43
+
44
+ presenter || @deafult_presenter
44
45
  end
45
46
 
46
47
  def presenters
47
- @presenters ||= options.reverse_merge(defined_presenters)
48
+ @presenters ||= @ext_presenters.reverse_merge(predefined_presenters)
48
49
  end
49
50
 
50
51
  def self_copy
51
52
  self.class.new(options)
52
53
  end
53
54
 
54
- def defined_presenters
55
+ def predefined_presenters
55
56
  {
56
- string: DEFAULT_PROCESSOR,
57
- currency: ComplexFields::Currency.new,
58
- boolean: ComplexFields::Boolean.new,
57
+ string: DEFAULT_PROCESSOR,
58
+ currency: ComplexFields::Currency.new,
59
+ boolean: ComplexFields::Boolean.new,
59
60
  iterable_recursive: ComplexFields::ArrayProcessor.new(self_copy),
60
- recursive: self_copy
61
+ recursive: self_copy
61
62
  }
62
63
  end
63
64
  end