cascade-rb 0.1.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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.hound.yml +2 -0
  4. data/.ruby-style.yml +1063 -0
  5. data/Gemfile +4 -0
  6. data/README.md +25 -0
  7. data/Rakefile +9 -0
  8. data/cascade.gemspec +33 -0
  9. data/lib/cascade.rb +11 -0
  10. data/lib/cascade/cascade_csv.rb +13 -0
  11. data/lib/cascade/columns_matching.rb +41 -0
  12. data/lib/cascade/complex_fields.rb +3 -0
  13. data/lib/cascade/complex_fields/boolean.rb +11 -0
  14. data/lib/cascade/complex_fields/country_iso.rb +12 -0
  15. data/lib/cascade/complex_fields/currency.rb +23 -0
  16. data/lib/cascade/concerns/statistics_collectible.rb +17 -0
  17. data/lib/cascade/data_parser.rb +28 -0
  18. data/lib/cascade/error_handler.rb +23 -0
  19. data/lib/cascade/exceptions.rb +2 -0
  20. data/lib/cascade/exceptions/unknown_presenter_type.rb +3 -0
  21. data/lib/cascade/exceptions/wrong_mapping_format.rb +3 -0
  22. data/lib/cascade/helpers/configuration.rb +32 -0
  23. data/lib/cascade/helpers/hash.rb +5 -0
  24. data/lib/cascade/row_processor.rb +51 -0
  25. data/lib/cascade/statistics.rb +58 -0
  26. data/lib/cascade/statistics_stores.rb +3 -0
  27. data/lib/cascade/statistics_stores/abstract_store.rb +21 -0
  28. data/lib/cascade/statistics_stores/array_store.rb +15 -0
  29. data/lib/cascade/statistics_stores/counter_store.rb +16 -0
  30. data/lib/cascade/version.rb +4 -0
  31. data/spec/lib/cascade_csv_spec.rb +21 -0
  32. data/spec/lib/columns_matching_spec.rb +52 -0
  33. data/spec/lib/complex_fields/boolean_spec.rb +22 -0
  34. data/spec/lib/complex_fields/country_iso_spec.rb +24 -0
  35. data/spec/lib/complex_fields/currency_spec.rb +26 -0
  36. data/spec/lib/concerns/statistics_collectible_spec.rb +15 -0
  37. data/spec/lib/data_parser_spec.rb +37 -0
  38. data/spec/lib/error_handler_spec.rb +32 -0
  39. data/spec/lib/exceptions/unknown_presenter_type_spec.rb +9 -0
  40. data/spec/lib/exceptions/wrong_mapping_format_spec.rb +9 -0
  41. data/spec/lib/helpers/configuration_spec.rb +36 -0
  42. data/spec/lib/helpers/hash.rb +14 -0
  43. data/spec/lib/row_processor_spec.rb +71 -0
  44. data/spec/lib/statistics_spec.rb +39 -0
  45. data/spec/lib/statistics_stores/abstract_store_spec.rb +22 -0
  46. data/spec/lib/statistics_stores/array_store_spec.rb +18 -0
  47. data/spec/lib/statistics_stores/counter_store_spec.rb +19 -0
  48. data/spec/spec_helper.rb +16 -0
  49. metadata +264 -0
@@ -0,0 +1,21 @@
1
+ module Cascade
2
+ module StatisticsStores
3
+ class AbstractStore
4
+ def initialize(default_value = nil)
5
+ @store = default_value || initialize_value
6
+ end
7
+
8
+ attr_reader :store
9
+
10
+ def update(*)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ private
15
+
16
+ def initialize_value(*)
17
+ raise NotImplementedError
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ module Cascade
2
+ module StatisticsStores
3
+ class ArrayStore < AbstractStore
4
+ def update(value)
5
+ @store << value
6
+ end
7
+
8
+ private
9
+
10
+ def initialize_value
11
+ []
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module Cascade
2
+ module StatisticsStores
3
+ class CounterStore < AbstractStore
4
+ def update(value = nil)
5
+ value ||= 1
6
+ @store += value
7
+ end
8
+
9
+ private
10
+
11
+ def initialize_value
12
+ 0
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ module Cascade
2
+ # current gem version
3
+ VERSION = "0.1.0"
4
+ end
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+ require "cascade/cascade_csv"
3
+
4
+ describe Cascade::CascadeCsv do
5
+ def described_class
6
+ Cascade::CascadeCsv
7
+ end
8
+
9
+ let(:filename) { "filename.csv" }
10
+
11
+ it "pass default col_sep and quote_char if in not setted" do
12
+ mock(CSV).open(filename, col_sep: "\t", quote_char: "\0")
13
+ described_class.open(filename)
14
+ end
15
+
16
+ it "pass specified col_sep and quote_char" do
17
+ mock(CSV).open(filename, col_sep: "custom_sep", quote_char: "custom_quote")
18
+ described_class.open(filename, col_sep: "custom_sep",
19
+ quote_char: "custom_quote")
20
+ end
21
+ end
@@ -0,0 +1,52 @@
1
+ require "spec_helper"
2
+ require "cascade/columns_matching"
3
+
4
+ describe Cascade::ColumnsMatching do
5
+ def described_class
6
+ Cascade::ColumnsMatching
7
+ end
8
+ subject { described_class.new }
9
+
10
+ context "on settings file parsing" do
11
+ subject { described_class.new(filepath: "config/columns_match.yml") }
12
+
13
+ it "raise error if columns matching file doesnt contain mapping key" do
14
+ mock(YAML).load_file("config/columns_match.yml") { {} }
15
+ assert_raises(Cascade::WrongMappingFormat) { subject }
16
+ end
17
+
18
+ it "raise error if columns matching file doesnt contain any info" do
19
+ mock(YAML).load_file("config/columns_match.yml") { nil }
20
+ assert_raises(Cascade::WrongMappingFormat) { subject }
21
+ end
22
+ end
23
+
24
+ context "after file parsed" do
25
+ before do
26
+ stub(YAML).load_file do
27
+ {
28
+ "mapping" => {
29
+ "name" => "string",
30
+ "class" => "string"
31
+ }
32
+ }
33
+ end
34
+ end
35
+
36
+ context "#supported_keys" do
37
+ it { subject.must_respond_to(:supported_keys) }
38
+ it "return array" do
39
+ subject.supported_keys.must_be_kind_of Array
40
+ end
41
+ end
42
+
43
+ context "#column_type" do
44
+ it "return curresponding value for passed column value" do
45
+ assert_equal :string,
46
+ described_class.new(content: { name: "string" }).column_type(:name)
47
+ end
48
+ end
49
+ end
50
+
51
+ it { delegate_method(:index).to(:supported_keys) }
52
+ end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+ require "cascade/complex_fields/boolean"
3
+
4
+ describe Cascade::ComplexFields::Boolean do
5
+ def described_class
6
+ Cascade::ComplexFields::Boolean
7
+ end
8
+
9
+ let(:subject) { described_class.new }
10
+
11
+ it "return true value for values that seems like true" do
12
+ ["True", "true", "x", "+", true].each do |value|
13
+ assert subject.call(value)
14
+ end
15
+ end
16
+
17
+ it "return false value for values that cant be true" do
18
+ ["false", false, nil, "", "some value"].each do |value|
19
+ refute subject.call(value)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+ require "cascade/complex_fields/country_iso"
3
+
4
+ describe Cascade::ComplexFields::CountryIso do
5
+ def described_class
6
+ Cascade::ComplexFields::CountryIso
7
+ end
8
+
9
+ let(:subject) { described_class.new }
10
+
11
+ it "translate country name to alpha-2 code" do
12
+ assert_equal subject.call("France"), "FR"
13
+ end
14
+
15
+ it "raise error if there is no such country" do
16
+ assert_raises IsoCountryCodes::UnknownCodeError do
17
+ subject.call("some_blank_value")
18
+ end
19
+ end
20
+
21
+ it "return nil if nil value passed" do
22
+ assert_nil subject.call(nil)
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ require "spec_helper"
2
+ require "cascade/complex_fields/currency"
3
+
4
+ describe Cascade::ComplexFields::Currency do
5
+ def described_class
6
+ Cascade::ComplexFields::Currency
7
+ end
8
+
9
+ subject { described_class.new }
10
+
11
+ describe "parse" do
12
+ it "return nil if input string isn't number" do
13
+ assert_nil subject.call("0o")
14
+ end
15
+
16
+ it "prepare string to use it as bignum" do
17
+ assert_kind_of BigDecimal, subject.call("1 123 123 45")
18
+ assert_kind_of BigDecimal, subject.call("1 123, 123 45")
19
+ end
20
+
21
+ it "return zero if field is empty" do
22
+ assert_nil subject.call("")
23
+ assert_nil subject.call(nil)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ require "spec_helper"
2
+ require "cascade/concerns/statistics_collectible"
3
+
4
+ describe Cascade::StatisticsCollectible do
5
+ class ExtendableClass; include Cascade::StatisticsCollectible; end
6
+ subject { ExtendableClass.new }
7
+
8
+ it "defines helper for access to statics class" do
9
+ assert_instance_of Cascade::Statistics, subject.statistics
10
+ end
11
+
12
+ it "delegates register_action method to statistics" do
13
+ assert_respond_to subject, :register_action
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ require "spec_helper"
2
+ require "cascade/data_parser"
3
+
4
+ describe Cascade::DataParser do
5
+ def described_class
6
+ Cascade::DataParser
7
+ end
8
+
9
+ class FakeDataProvider
10
+ def open(_)
11
+ [
12
+ ["Sally Whittaker", "2018", "McCarren House", "312",
13
+ "3.75", "France", "+", "123.123"],
14
+ ["Jeff Smith", "2018", "Prescott House", "17-D", "3.20",
15
+ "Austria", "45.12"]
16
+ ]
17
+ end
18
+ end
19
+
20
+ let(:filename) { "spec/examples/data_test.txt" }
21
+ let(:data_provider) { FakeDataProvider.new }
22
+
23
+ before do
24
+ @processor_calls_count = 0
25
+ @data_saves_count = 0
26
+ row_processor = ->(_row) { @processor_calls_count += 1 }
27
+ data_saver = ->(*) { @data_saves_count += 1 }
28
+ @parser = described_class.new(filename, row_processor: row_processor,
29
+ data_saver: data_saver, data_provider: data_provider)
30
+ end
31
+
32
+ it "calls row processor for each file line" do
33
+ @parser.call
34
+ assert_equal @processor_calls_count, 2
35
+ assert_equal @data_saves_count, 2
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ require "spec_helper"
2
+ require "cascade/error_handler"
3
+
4
+ describe Cascade::ErrorHandler do
5
+ def described_class
6
+ Cascade::ErrorHandler
7
+ end
8
+
9
+ let(:error_store) do
10
+ ->(row, reason) do
11
+ @errors ||= []
12
+ @errors << [row, reason]
13
+ end
14
+ end
15
+ let(:row) { Struct.new(:fields) }
16
+
17
+ subject { described_class.new(error_store: error_store) }
18
+
19
+ Cascade::ErrorHandler::HANDLING_EXCEPTIONS.each do |exception|
20
+ it "catch #{exception} and send info to error store" do
21
+ subject.with_errors_handling(row) { raise exception }
22
+ assert_includes @errors, [row, exception.to_s]
23
+ end
24
+ end
25
+
26
+ describe "DEFAULT_ERROR_STORE" do
27
+ it "create new array and push row with reason" do
28
+ result = Cascade::ErrorHandler::DEFAULT_ERROR_STORE.call(:row, :reason)
29
+ assert_includes result, %i(row reason)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,9 @@
1
+ require "spec_helper"
2
+ require "cascade/exceptions/unknown_presenter_type"
3
+
4
+ describe Cascade::UnknownPresenterType do
5
+ subject { Cascade::UnknownPresenterType.new }
6
+ it "exception raisable" do
7
+ assert_respond_to subject, :exception
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require "spec_helper"
2
+ require "cascade/exceptions/wrong_mapping_format"
3
+
4
+ describe Cascade::WrongMappingFormat do
5
+ subject { Cascade::WrongMappingFormat.new }
6
+ it "exception raisable" do
7
+ assert_respond_to subject, :exception
8
+ end
9
+ end
@@ -0,0 +1,36 @@
1
+ require "spec_helper"
2
+ require "cascade/helpers/configuration"
3
+
4
+ describe Configuration do
5
+ class ExtendableClass
6
+ extend Configuration
7
+
8
+ define_setting :simple_setting
9
+ define_setting :setting_with_default, 42
10
+ end
11
+
12
+ it "defines class variables with curresponding default values" do
13
+ assert_equal ExtendableClass.class_variables,
14
+ [:@@simple_setting, :@@setting_with_default]
15
+ end
16
+
17
+ it "defines reader methods for extended class" do
18
+ assert_respond_to ExtendableClass, :simple_setting
19
+ assert_respond_to ExtendableClass, :setting_with_default
20
+ end
21
+
22
+ it "defines write accessor for extended class" do
23
+ assert_respond_to ExtendableClass, :simple_setting=
24
+ assert_respond_to ExtendableClass, :setting_with_default=
25
+ end
26
+
27
+ it "allows to set class variables in configuration block" do
28
+ ExtendableClass.configuration do |config|
29
+ config.simple_setting = :value
30
+ config.setting_with_default = 29
31
+ end
32
+
33
+ assert_equal ExtendableClass.setting_with_default, 29
34
+ assert_equal ExtendableClass.simple_setting, :value
35
+ end
36
+ end
@@ -0,0 +1,14 @@
1
+ require "spec_helper"
2
+ require "cascade/helpers/hash"
3
+
4
+ describe Hash do
5
+ describe "#reverse_merge" do
6
+ let(:another_hash) { { a: 1, b: 2 } }
7
+ let(:hash) { { a: 4, c: 3 } }
8
+
9
+ it "merge self into anothe hash" do
10
+ assert_equal hash.reverse_merge(another_hash),
11
+ a: 4, b: 2, c: 3
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,71 @@
1
+ require "spec_helper"
2
+ require "cascade/row_processor"
3
+
4
+ describe Cascade::RowProcessor do
5
+ def described_class
6
+ Cascade::RowProcessor
7
+ end
8
+
9
+ it "allows to set settings" do
10
+ assert_respond_to described_class, :use_default_presenter=
11
+ assert_respond_to described_class, :deafult_presenter=
12
+ end
13
+
14
+ context "when parsing row" do
15
+ let(:row) { [:a_value, :b_value, :c_value] }
16
+ let(:keys) { [:a, :b, :c] }
17
+ let(:columns_matching) { OpenStruct.new(supported_keys: keys) }
18
+
19
+ before do
20
+ keys.each_with_index do |key, index|
21
+ stub(columns_matching).index(key) { index }
22
+ stub(columns_matching).column_type(key) { :string }
23
+ end
24
+ end
25
+
26
+ it "collect row values with corresponding keys" do
27
+ processed_row = described_class.new(columns_matching:
28
+ columns_matching).call(row)
29
+ assert_equal(processed_row, a: :a_value, b: :b_value, c: :c_value)
30
+ end
31
+ end
32
+
33
+ context "when presenting row values" do
34
+ let(:row) { ["raw_value"] }
35
+ let(:columns_matching) { OpenStruct.new(supported_keys: [:a]) }
36
+ let(:value_presenter) { ->(_raw_value) { "parsed_value" } }
37
+
38
+ before do
39
+ stub(columns_matching).index(:a) { 0 }
40
+ stub(columns_matching).column_type(:a) { :presenter }
41
+ end
42
+
43
+ context "when curresponding presenter passed" do
44
+ it "process field with specified presenter" do
45
+ parsed_value = described_class.new(columns_matching: columns_matching,
46
+ presenter: value_presenter).call(row)[:a]
47
+ assert_equal "parsed_value", parsed_value
48
+ end
49
+ end
50
+
51
+ context "when curresponding presenter not passed" do
52
+ it "process field with default presenter if use_default_presenter true" do
53
+ described_class.stub(:use_default_presenter, true) do
54
+ described_class.stub(:deafult_presenter, -> { value_presenter }) do
55
+ parsed_value = described_class.new(columns_matching:
56
+ columns_matching).call(row)[:a]
57
+ assert_equal "parsed_value", parsed_value
58
+ end
59
+ end
60
+ end
61
+
62
+ it "process field and rise error if use_default_presenter false" do
63
+ described_class.stub(:use_default_presenter, false) do
64
+ assert_raises(Cascade::UnknownPresenterType) do
65
+ described_class.new(columns_matching: columns_matching).call(row)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end