suds 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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +62 -0
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +7 -0
  7. data/Gemfile.lock +77 -0
  8. data/Guardfile +7 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +68 -0
  11. data/lib/suds.rb +1 -0
  12. data/lib/suds/all.rb +12 -0
  13. data/lib/suds/cleaner.rb +32 -0
  14. data/lib/suds/cleaner/column_converter_cleaner.rb +26 -0
  15. data/lib/suds/cleaner/column_filter_cleaner.rb +26 -0
  16. data/lib/suds/cleaner/downcase_cleaner.rb +21 -0
  17. data/lib/suds/cleaner/regex_cleaner.rb +41 -0
  18. data/lib/suds/cleaner/whitespace_cleaner.rb +9 -0
  19. data/lib/suds/converter.rb +27 -0
  20. data/lib/suds/converter/json_converter.rb +8 -0
  21. data/lib/suds/interpreter.rb +19 -0
  22. data/lib/suds/interpreter/csv_interpreter.rb +28 -0
  23. data/lib/suds/interpreter/file_interpreter.rb +11 -0
  24. data/lib/suds/suds.rb +46 -0
  25. data/lib/suds/version.rb +4 -0
  26. data/spec/lib/suds/all_spec.rb +13 -0
  27. data/spec/lib/suds/cleaner/column_converter_cleaner_spec.rb +29 -0
  28. data/spec/lib/suds/cleaner/column_filter_cleaner_spec.rb +56 -0
  29. data/spec/lib/suds/cleaner/downcase_cleaner_spec.rb +15 -0
  30. data/spec/lib/suds/cleaner/regex_cleaner_spec.rb +52 -0
  31. data/spec/lib/suds/cleaner/whitespace_cleaner_spec.rb +15 -0
  32. data/spec/lib/suds/cleaner_spec.rb +28 -0
  33. data/spec/lib/suds/converter/json_converter_spec.rb +12 -0
  34. data/spec/lib/suds/converter_spec.rb +12 -0
  35. data/spec/lib/suds/interpreter/csv_interpreter_spec.rb +45 -0
  36. data/spec/lib/suds/interpreter_spec.rb +11 -0
  37. data/spec/lib/suds/suds_spec.rb +121 -0
  38. data/spec/spec_helper.rb +87 -0
  39. data/spec/support/cleaner_context.rb +5 -0
  40. data/suds.gemspec +15 -0
  41. metadata +83 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e0fc3f6bfeb9132463f70ae8bfb07e1ec04c0fa4
4
+ data.tar.gz: 770b6fe86d994af04989b28d7fd22c4b1d335053
5
+ SHA512:
6
+ metadata.gz: 116913488fb79c62e8f75ba6bd0adc6232ca1124b23bc0b2598b3709c9419f55d831c948bf2b8585f8857228812e95fe3df499cd5f8ed92bc7035781f39281c0
7
+ data.tar.gz: c8601f3759c37aa3150ed440e9af5bd0d8d37bbfc428441dfa7e66d73b47b06ddd61df653c70722944bb6cf0df1a4ca6be99d0130427fb16398d947ca9917094
@@ -0,0 +1 @@
1
+ repo_token: ULz8tz46aXDE4AbHGI2sh3SwgqjJvHLQs
@@ -0,0 +1,62 @@
1
+ data/*
2
+
3
+ ### Ruby
4
+
5
+ *.gem
6
+ *.rbc
7
+ /.config
8
+ /coverage/
9
+ /InstalledFiles
10
+ /pkg/
11
+ /spec/reports/
12
+ /test/tmp/
13
+ /test/version_tmp/
14
+ /tmp/
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+
21
+ ## Documentation cache and generated files:
22
+ /.yardoc/
23
+ /_yardoc/
24
+ /doc/
25
+ /rdoc/
26
+
27
+ ## Environment normalisation:
28
+ /.bundle/
29
+ /lib/bundler/man/
30
+
31
+ # for a library or gem, you might want to ignore these files since the code is
32
+ # intended to run in multiple environments; otherwise, check them in:
33
+ # Gemfile.lock
34
+ # .ruby-version
35
+ .ruby-gemset
36
+
37
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
38
+ .rvmrc
39
+
40
+ ### Mac
41
+
42
+ .DS_Store
43
+ .AppleDouble
44
+ .LSOverride
45
+
46
+ # Icon must end with two \r
47
+ Icon
48
+
49
+
50
+ # Thumbnails
51
+ ._*
52
+
53
+ # Files that might appear on external disk
54
+ .Spotlight-V100
55
+ .Trashes
56
+
57
+ # Directories potentially created on remote AFP share
58
+ .AppleDB
59
+ .AppleDesktop
60
+ Network Trash Folder
61
+ Temporary Items
62
+ .apdisk
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ #--warnings
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1 @@
1
+ 2.1.3
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+ group :development, :test do
3
+ gem 'guard-rspec', require: false
4
+ gem 'terminal-notifier-guard'
5
+ gem 'coveralls', require: false
6
+ gem "codeclimate-test-reporter", group: :test, require: nil
7
+ end
@@ -0,0 +1,77 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ celluloid (0.15.2)
5
+ timers (~> 1.1.0)
6
+ codeclimate-test-reporter (0.3.0)
7
+ simplecov (>= 0.7.1, < 1.0.0)
8
+ coderay (1.1.0)
9
+ coveralls (0.7.0)
10
+ multi_json (~> 1.3)
11
+ rest-client
12
+ simplecov (>= 0.7)
13
+ term-ansicolor
14
+ thor
15
+ diff-lcs (1.2.5)
16
+ docile (1.1.5)
17
+ ffi (1.9.3)
18
+ formatador (0.2.5)
19
+ guard (2.6.1)
20
+ formatador (>= 0.2.4)
21
+ listen (~> 2.7)
22
+ lumberjack (~> 1.0)
23
+ pry (>= 0.9.12)
24
+ thor (>= 0.18.1)
25
+ guard-rspec (4.2.10)
26
+ guard (~> 2.1)
27
+ rspec (>= 2.14, < 4.0)
28
+ listen (2.7.9)
29
+ celluloid (>= 0.15.2)
30
+ rb-fsevent (>= 0.9.3)
31
+ rb-inotify (>= 0.9)
32
+ lumberjack (1.0.7)
33
+ method_source (0.8.2)
34
+ mime-types (2.3)
35
+ multi_json (1.10.1)
36
+ pry (0.10.0)
37
+ coderay (~> 1.1.0)
38
+ method_source (~> 0.8.1)
39
+ slop (~> 3.4)
40
+ rb-fsevent (0.9.4)
41
+ rb-inotify (0.9.5)
42
+ ffi (>= 0.5.0)
43
+ rest-client (1.6.7)
44
+ mime-types (>= 1.16)
45
+ rspec (3.0.0)
46
+ rspec-core (~> 3.0.0)
47
+ rspec-expectations (~> 3.0.0)
48
+ rspec-mocks (~> 3.0.0)
49
+ rspec-core (3.0.2)
50
+ rspec-support (~> 3.0.0)
51
+ rspec-expectations (3.0.2)
52
+ diff-lcs (>= 1.2.0, < 2.0)
53
+ rspec-support (~> 3.0.0)
54
+ rspec-mocks (3.0.2)
55
+ rspec-support (~> 3.0.0)
56
+ rspec-support (3.0.2)
57
+ simplecov (0.8.2)
58
+ docile (~> 1.1.0)
59
+ multi_json
60
+ simplecov-html (~> 0.8.0)
61
+ simplecov-html (0.8.0)
62
+ slop (3.5.0)
63
+ term-ansicolor (1.3.0)
64
+ tins (~> 1.0)
65
+ terminal-notifier-guard (1.5.3)
66
+ thor (0.19.1)
67
+ timers (1.1.0)
68
+ tins (1.3.0)
69
+
70
+ PLATFORMS
71
+ ruby
72
+
73
+ DEPENDENCIES
74
+ codeclimate-test-reporter
75
+ coveralls
76
+ guard-rspec
77
+ terminal-notifier-guard
@@ -0,0 +1,7 @@
1
+ guard :rspec, cmd: "bundle exec rspec" do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+
6
+ notification :terminal_notifier
7
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 HealthWave.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,68 @@
1
+ # suds
2
+
3
+ Welcome to the suds project. This is very much a work in progress, so please use this at your own risk. I am changing the DSL constantly and rewriting the history.
4
+
5
+ This project will become stable fairly soon.
6
+
7
+ Suds is a essentially a tool for working with data files. Suds is broken up into three modular components: **interpreters**, **cleaners**, and **converters**.
8
+
9
+ **Interpreters** load the data into the `suds` object. This can be in the form of raw text, json, csv, etc. Interpreteres can be chained together, for instance a (fictional) `GoogleDriveInterpreter` can pass it's data to the `CSVInterpreter`.
10
+
11
+ **Cleaners** manipulate the data. You can modify/remove rows/columns (in the context of a CSV file).
12
+
13
+ **Converters** export the file either to disk or to a remote location.
14
+
15
+
16
+ ## Requirements
17
+
18
+ Ruby `2.0.x` is tested. If you need support for `1.9.3` use the `1.9.3` branch.
19
+
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ gem install suds
25
+ ```
26
+
27
+ Or if you're using Bundler, include the following line in your Gemfile:
28
+
29
+ ```ruby
30
+ gem 'suds', git: 'git@github.com:HealthWave/suds.git'
31
+ ```
32
+
33
+ ## Getting started starting
34
+
35
+ Initialize your interpreter(s):
36
+
37
+
38
+ ```ruby
39
+ f_interpreter = FileInterpreter.new('./path/to/my/file')
40
+ csv_interpreter = CSVInterpreter.new(f_interpreter.data)
41
+ ```
42
+
43
+ Create a suds list and add the interpreters to it:
44
+
45
+ ```ruby
46
+ list = Suds.new( csv_interpreter )
47
+ ```
48
+
49
+ Add some cleaners to it:
50
+
51
+ ```ruby
52
+ list.add_cleaner Cleaner.new {|_,v| v.strip! if v } # Inline initialization of a generic cleaner
53
+ # Configuring an existing cleaner
54
+ list.add_cleaner ColumnConverterCleaner.new({
55
+ company: :name,
56
+ nlacno: :partner_practitioner_id,
57
+ })
58
+ list.add_cleaner ColumnFilterCleaner.new(include_columns: [:name, :partner_practitioner_id, :email])
59
+ list.add_cleaner DowncaseCleaner.new()
60
+ ```
61
+
62
+ Run all the cleaners:
63
+
64
+ ```ruby
65
+ list.clean
66
+ ```
67
+
68
+
@@ -0,0 +1 @@
1
+ require 'suds/suds'
@@ -0,0 +1,12 @@
1
+
2
+ require 'suds/suds'
3
+ require 'suds/cleaner/column_converter_cleaner'
4
+ require 'suds/cleaner/column_filter_cleaner'
5
+ require 'suds/cleaner/downcase_cleaner'
6
+ require 'suds/cleaner/regex_cleaner'
7
+ require 'suds/cleaner/whitespace_cleaner'
8
+
9
+ require 'suds/interpreter/file_interpreter'
10
+ require 'suds/interpreter/csv_interpreter'
11
+
12
+ require 'suds/converter/json_converter'
@@ -0,0 +1,32 @@
1
+
2
+ class Cleaner
3
+ attr_accessor :data, :action
4
+
5
+ def initialize &b
6
+ block_given? ? @action = b : raise("The generic Cleaner must be provided a block.")
7
+ end
8
+
9
+ def self.clean_array array, &b
10
+ raise "Please provide either a hash or an array as the main parameter" unless Array === array
11
+ array.each do |row|
12
+ clean_hash row, &b
13
+ end
14
+ return array
15
+ end
16
+
17
+ def self.clean_hash hash, &b
18
+ raise "Please provide either a hash or an array as the main parameter" unless Hash === hash
19
+
20
+ hash.each do |k,v|
21
+ b.call(k,v)
22
+ end
23
+ return hash
24
+ end
25
+
26
+ def clean data
27
+ @data = data
28
+ self.class.clean_array(@data) do |k,v|
29
+ @action.call(k,v)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ require 'suds/cleaner'
2
+
3
+ class ColumnConverterCleaner < Cleaner
4
+ def initialize convert_hash, force_strings = false
5
+ @force_strings = force_strings
6
+ @convert_hash = convert_hash
7
+ end
8
+
9
+ def clean data
10
+ data.each do |row|
11
+ keys = row.keys
12
+ keys.each do |key|
13
+ if new_key = @convert_hash[key]
14
+ old_value = row[key]
15
+ row.delete key
16
+ if @force_strings
17
+ new_key = new_key.to_s
18
+ else
19
+ new_key = new_key.to_sym
20
+ end
21
+ row[new_key] = old_value
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ require 'suds/cleaner'
2
+
3
+ class ColumnFilterCleaner < Cleaner
4
+ attr_accessor :exclude_columns, :include_columns
5
+ def initialize exclude_columns:[], include_columns:[]
6
+ @exclude_columns = [exclude_columns].flatten.map(&:to_s)
7
+ @include_columns = [include_columns].flatten.map(&:to_s)
8
+
9
+ raise "You must provide include_columns or exclude_columns." if @exclude_columns.empty? and @include_columns.empty?
10
+ end
11
+
12
+ def clean data
13
+ if not @include_columns.empty?
14
+ exclude_columns = data.first.keys.map(&:to_s) - @include_columns
15
+ else
16
+ exclude_columns = @exclude_columns
17
+ end
18
+
19
+ data.each do |row|
20
+ exclude_columns.each do |col|
21
+ row.delete col
22
+ row.delete col.to_sym
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ require 'suds/cleaner'
2
+
3
+ class DowncaseCleaner < Cleaner
4
+ def initialize exclude_columns:[], include_columns:[]
5
+ @exclude_columns = [exclude_columns].flatten
6
+ @include_columns = [include_columns].flatten
7
+ end
8
+
9
+ def clean data
10
+ Cleaner.clean_array(data) do |k,v|
11
+ next if v.nil?
12
+ if !@include_columns.empty?
13
+ v.downcase if @include_columns.include?(k)
14
+ elsif !@exclude_columns.empty?
15
+ v.downcase! unless @exclude_columns.include?(k)
16
+ else
17
+ v.downcase!
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ require 'suds/cleaner'
2
+
3
+ class RegexCleaner < Cleaner
4
+ attr_accessor :regex_map
5
+ EMAIL_REGEX = /^[a-zA-Z0-9_.+\-]+@[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-.]+$/
6
+
7
+ def initialize regex_map, destroy_row: false
8
+ @regex_map = {}
9
+ regex_map.each do |column,regex|
10
+ if Array === column
11
+ column.each do |col|
12
+ @regex_map[col] = regex
13
+ end
14
+ else
15
+ @regex_map[column] = regex
16
+ end
17
+ end
18
+ @columns = @regex_map.keys.map(&:to_s)
19
+ @destroy_row = destroy_row
20
+ end
21
+
22
+ def clean data
23
+ ret_data = data.select do |row|
24
+ save_row = true
25
+ row.keys.each do |key|
26
+ if @regex_map[key]
27
+ if !(row[key].to_s =~ @regex_map[key])
28
+ if @destroy_row
29
+ save_row = false
30
+ else
31
+ row.delete key
32
+ end
33
+ end
34
+ end
35
+ end
36
+ save_row # for select
37
+ end
38
+ return ret_data
39
+ end
40
+
41
+ end
@@ -0,0 +1,9 @@
1
+ require 'suds/cleaner'
2
+
3
+ class WhitespaceCleaner
4
+ def self.clean data, options={}
5
+ Cleaner.clean_array(data) do |_,v|
6
+ v.strip! if v
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+ require 'fileutils'
2
+
3
+ class Converter
4
+ attr_accessor :outfile, :converter, :converted_data
5
+
6
+ def initialize outfile=nil, &b
7
+ @outfile = outfile
8
+ @converter = b
9
+ end
10
+
11
+ def convert data
12
+ raise "A generic Converter can only convert with a block" unless @converter
13
+ @converted_data = @converter.call(data)
14
+ end
15
+
16
+ def convert! data
17
+ raise "Cannot output to file if outfile is not set." unless @outfile
18
+ fname = File.expand_path(@outfile)
19
+ dir = File.dirname fname
20
+
21
+ if !File.directory?(dir)
22
+ FileUtils.mkdir_p dir
23
+ end
24
+
25
+ File.open(fname, 'w').write(convert(data))
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ require 'json'
2
+ require 'suds/converter'
3
+
4
+ class JSONConverter < Converter
5
+ def convert data
6
+ self.converted_data = data.to_json
7
+ end
8
+ end
@@ -0,0 +1,19 @@
1
+
2
+ class Interpreter
3
+ attr_accessor :headers, :data
4
+
5
+ def initialize
6
+ @headers = []
7
+ @data = []
8
+ end
9
+
10
+
11
+ def interpret
12
+ raise "No interpretation defined."
13
+ end
14
+
15
+ def data
16
+ interpret if @data.nil? || @data.empty?
17
+ @data
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ require 'csv'
2
+ require 'suds/interpreter'
3
+
4
+ class CSVInterpreter < Interpreter
5
+ attr_accessor :filepath
6
+
7
+ def initialize raw_data
8
+ @raw_data = raw_data
9
+ super()
10
+ end
11
+
12
+
13
+ def interpret
14
+ CSV.parse(@raw_data, headers: true, header_converters: :symbol).each do |row|
15
+ @headers = row.headers if @headers.nil? || @headers.empty?
16
+ interpret_unit row
17
+ end
18
+ @data
19
+ end
20
+
21
+ def interpret_unit unit
22
+ raise "Headers have not be set." if @headers.empty?
23
+ raise "Invalid data for current headers." if @headers.size != unit.size
24
+
25
+ @data << unit.to_h
26
+ end
27
+
28
+ end
@@ -0,0 +1,11 @@
1
+
2
+ class FileInterpreter < Interpreter
3
+ def initialize filepath
4
+ # TODO File validation
5
+ @raw_data = open(filepath, 'r').read
6
+ end
7
+
8
+ def interpret
9
+ @data = @raw_data
10
+ end
11
+ end
@@ -0,0 +1,46 @@
1
+ require 'suds/interpreter'
2
+ require 'suds/cleaner'
3
+ require 'suds/converter'
4
+
5
+ class Suds
6
+
7
+ attr_accessor :interpreter, :cleaners, :converters, :data
8
+
9
+ def initialize interpreter
10
+ @interpreter = interpreter
11
+ @converters = []
12
+ @cleaners = []
13
+ end
14
+
15
+ def add_converter converter
16
+ raise "#{converter.class} is not a valid Converter." unless converter.is_a?(Converter)
17
+ @converters << converter
18
+ end
19
+
20
+ def add_cleaner cleaner
21
+ raise "#{cleaner.class} is not a valid Cleaner." unless cleaner.is_a?(Cleaner)
22
+ @cleaners << cleaner
23
+ end
24
+
25
+ def clean
26
+ @data = @cleaners.inject(interpreter.data) do |data,cleaner|
27
+ data = cleaner.clean data
28
+ end
29
+ end
30
+
31
+ def convert!
32
+ @converters.each { |converter| converter.convert! data}
33
+ end
34
+
35
+ def convert
36
+ @converters.map { |converter| converter.convert data}
37
+ end
38
+
39
+ def raw_data
40
+ interpreter.data
41
+ end
42
+
43
+ def data
44
+ @data ||= interpreter.interpret
45
+ end
46
+ end
@@ -0,0 +1,4 @@
1
+
2
+ class Suds
3
+ VERSION = '0.1.0'
4
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'suds/all'
3
+
4
+ describe "all requires" do
5
+ it { JSONConverter }
6
+ it { CSVInterpreter }
7
+ it { FileInterpreter }
8
+ it { ColumnConverterCleaner }
9
+ it { ColumnFilterCleaner }
10
+ it { DowncaseCleaner }
11
+ it { RegexCleaner }
12
+ it { WhitespaceCleaner }
13
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'suds/cleaner/column_converter_cleaner'
3
+
4
+ describe ColumnConverterCleaner do
5
+ subject { ColumnConverterCleaner }
6
+ let(:convert_hash) { {company: :name} }
7
+
8
+ describe "#initialize" do
9
+ it "takes a hash of strings mapped to strings" do
10
+ subject.new convert_hash
11
+ end
12
+ end
13
+
14
+ describe "#clean" do
15
+ let(:data) { [{ company: "uri gorelik" }] }
16
+ let(:cleaner) { cleaner = subject.new convert_hash, false }
17
+ it "converts the keys of a hash" do
18
+ results = cleaner.clean data
19
+ expect(results.first.keys - convert_hash.values).to eq []
20
+ end
21
+
22
+ it 'retains the original key type' do
23
+ results = cleaner.clean data
24
+ expect(results.first.keys.first).to be_a(Symbol)
25
+ results = cleaner.clean([{ "company" => "test"}])
26
+ expect(results.first.keys.first).to be_a(String)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+ require 'suds/cleaner/column_filter_cleaner'
3
+
4
+ describe ColumnFilterCleaner do
5
+ subject { ColumnFilterCleaner }
6
+
7
+ describe '#initialize' do
8
+
9
+ it 'raises an error if both exclude and include columns are not present or empty' do
10
+ error_reg = /You must provide include_columns or exclude_columns./
11
+ expect{ subject.new }.to raise_error(error_reg)
12
+ expect{ subject.new include_columns: [] }.to raise_error(error_reg)
13
+ expect{ subject.new exclude_columns: [] }.to raise_error(error_reg)
14
+ expect{ subject.new include_columns: [], exclude_columns: [] }.to raise_error(error_reg)
15
+ end
16
+
17
+ it 'makes include columns publicly available' do
18
+ cleaner = subject.new include_columns: [{}]
19
+ expect(cleaner).to respond_to(:include_columns)
20
+ end
21
+
22
+ it 'makes exclude columns publicly available' do
23
+ cleaner = subject.new exclude_columns: [{}]
24
+ expect(cleaner).to respond_to(:exclude_columns)
25
+ end
26
+ end
27
+
28
+ describe '#clean' do
29
+ let(:data) { [{row1: 'a', row2: 'b'}] }
30
+ let(:original_data) { data.clone }
31
+
32
+ it 'excludes certain columns' do
33
+ cleaner = subject.new exclude_columns: :row2
34
+ result = cleaner.clean data
35
+ expect(result.first.keys).to include(:row1)
36
+ expect(result.first.keys).to_not include(:row2)
37
+ end
38
+
39
+ it 'only includes the listed columns' do
40
+ cleaner = subject.new include_columns: :row2
41
+ result = cleaner.clean data
42
+ expect(result.first.keys).to_not include(:row1)
43
+ expect(result.first.keys).to include(:row2)
44
+ end
45
+
46
+ it 'accepts both strings and symbol arrays' do
47
+ cleaner = subject.new include_columns: "row2"
48
+ result = cleaner.clean data
49
+ expect(result.first.keys).to_not include(:row1)
50
+ expect(result.first.keys).to include(:row2)
51
+
52
+ end
53
+ end
54
+
55
+
56
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ require 'cleaner/downcase_cleaner'
3
+
4
+ describe DowncaseCleaner do
5
+ include_context "shared cleaner"
6
+ subject { DowncaseCleaner }
7
+
8
+ let(:data) { {a: " Test", b: "Double test "} }
9
+ # let!(:original_value) { data.first.last.clone }
10
+
11
+ # it "downcases an array of objects" do
12
+ #expect( modified_value ).to match /[^A-Z]/
13
+ # end
14
+
15
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+ require 'suds/cleaner/regex_cleaner'
3
+
4
+ describe RegexCleaner do
5
+ subject { RegexCleaner }
6
+
7
+ context 'constants' do
8
+ it "has a regex constant for emails" do
9
+ expect(subject).to be_const_defined(:EMAIL_REGEX)
10
+ end
11
+ end
12
+
13
+
14
+ describe "#initialize" do
15
+ it 'accepts a hash that maps column names to regexes' do
16
+ subject.new({name: /uri/})
17
+ end
18
+
19
+ it 'accepts arrays mapped to regular expressions' do
20
+ cleaner = subject.new({[:first_name, :last_name] => /uri/})
21
+ expect(cleaner.instance_variable_get(:@columns)).to eq(%w[first_name last_name])
22
+ end
23
+
24
+ it 'accepts a hash that decides whether or not to delete the entire row' do
25
+ subject.new({name: /uri/}, destroy_row: true)
26
+ end
27
+ end
28
+
29
+ describe "#clean" do
30
+ let(:bad_data) { [{email: "uri"}] }
31
+ let(:good_data) { [{email: "uri@healthwave.co"}] }
32
+ let(:cleaner) { subject.new({email: subject::EMAIL_REGEX}) }
33
+
34
+
35
+ it 'keeps columns if they match the regex' do
36
+ results = cleaner.clean good_data
37
+ expect(results.first.keys).to include(:email)
38
+ end
39
+
40
+ it 'removes columns if they do not match the regex' do
41
+ results = cleaner.clean bad_data
42
+ expect(results.first.keys).to_not include(:email)
43
+ end
44
+
45
+ it "destroys rows if the regex doesn't match" do
46
+ cleaner = subject.new({email: subject::EMAIL_REGEX}, destroy_row: true)
47
+ results = cleaner.clean bad_data
48
+ expect(results).to be_empty
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ require 'cleaner/whitespace_cleaner'
3
+
4
+ describe WhitespaceCleaner do
5
+ include_context "shared cleaner"
6
+ subject { WhitespaceCleaner }
7
+
8
+ let(:data) { {a: " Test", b: "Double test "} }
9
+ # let!(:original_value) { data.first.last.clone }
10
+
11
+ # it "downcases an array of objects" do
12
+ #expect( modified_value ).to match /^[^\s].+[^\s]$/
13
+ # end
14
+
15
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+ require 'cleaner'
3
+
4
+ describe Cleaner do
5
+ subject { Cleaner.new(){} }
6
+
7
+ it { expect(Cleaner).to respond_to(:clean_array) }
8
+ it { expect(Cleaner).to respond_to(:clean_hash) }
9
+
10
+ describe "#clean" do
11
+ let(:data) { [{row1: "TEST"}] }
12
+
13
+ it "runs the provided block" do
14
+ cleaner = Cleaner.new() do |_,v|
15
+ v.downcase!
16
+ end
17
+
18
+ result = cleaner.clean data
19
+ expect( result.first.to_a.flatten.last ).to match(/[^A-Z]/)
20
+ end
21
+ end
22
+
23
+ context "failure" do
24
+ it "raises an error when an non hash or array is passed in as the data" do
25
+ expect {Cleaner.clean_array double("Fake")}.to raise_error(/Please provide either a hash or an array as the main parameter/)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'converter/json_converter'
3
+
4
+ describe JSONConverter do
5
+ subject { JSONConverter }
6
+ let(:unconverted_data) { [{row1: 1, row2:2}] * 5 }
7
+ it "converts an array to json" do
8
+ converter = subject.new(unconverted_data)
9
+ expect( converter.convert(unconverted_data).class ).to be String
10
+ expect( JSON.parse(converter.converted_data, symbolize_names: true) ).to eq unconverted_data
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'converter'
3
+
4
+ describe Converter do
5
+ subject { Converter }
6
+
7
+ describe "#convert!" do
8
+ it "it raises an error if no output file was specified" do
9
+ expect { Converter.new.convert!(double) }.to raise_error(/Cannot output to file if outfile is not set./)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+ require 'interpreter/csv_interpreter'
3
+
4
+ describe CSVInterpreter do
5
+ subject {CSVInterpreter.new("path")}
6
+
7
+ describe "#interpret_unit" do
8
+ let(:headers) { %w{foo} }
9
+ let(:row1) { {"foo" => "bar"} }
10
+ let(:csvint) do
11
+ csvint = CSVInterpreter.new("path")
12
+ csvint.headers = headers
13
+ csvint.interpret_unit row1
14
+ csvint
15
+ end
16
+
17
+ context 'success' do
18
+ subject { csvint.data }
19
+
20
+ it {expect( subject.size ).to eq 1 }
21
+
22
+ context "data" do
23
+ subject { csvint.data.first }
24
+ it {expect( subject.keys ).to include(*headers) }
25
+ it {expect( subject.values ).to include(*row1.values) }
26
+ end
27
+ end
28
+
29
+ context 'failure' do
30
+ subject { csvint }
31
+ let(:csvint) { CSVInterpreter.new("path") }
32
+
33
+ it "raises an error if the headers aren't set" do
34
+ expect{subject.interpret_unit %w{too many values}}.to raise_error(/Headers have not be set./)
35
+ end
36
+
37
+ it "raises an error if the header doesn't match the data" do
38
+ subject.headers = ["row1"]
39
+ expect{subject.interpret_unit %w{too many values}}.to raise_error(/Invalid data for current headers./)
40
+ end
41
+ end
42
+
43
+
44
+ end
45
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+ require 'interpreter'
3
+
4
+ describe Interpreter do
5
+ subject { Interpreter.new }
6
+
7
+ context "interface" do
8
+ it { should respond_to :data }
9
+ it { should respond_to :headers }
10
+ end
11
+ end
@@ -0,0 +1,121 @@
1
+ require 'spec_helper'
2
+ require 'suds'
3
+ require 'cleaner'
4
+ require 'converter'
5
+ require 'interpreter'
6
+
7
+ describe Suds do
8
+ subject { Suds }
9
+
10
+ describe '#initialize' do
11
+ subject { Suds.new double("Interpreter") }
12
+ it { should respond_to(:data) }
13
+ end
14
+
15
+ context 'plugins' do
16
+ let(:list) { subject.new double("Interpreter") }
17
+
18
+ describe '#add_cleaner' do
19
+ it "adds a cleaner" do
20
+ cleaner = Cleaner.new(){}
21
+ list.add_cleaner cleaner
22
+ expect(list.cleaners ).to_not be_empty
23
+ end
24
+
25
+ it 'raises an error if the added cleaner is not a Cleaner' do
26
+ cleaner = double
27
+ expect { list.add_cleaner cleaner }.to raise_error(Regexp.new("#{cleaner.class} is not a valid Cleaner."))
28
+ end
29
+ end
30
+
31
+ describe '#add_converter' do
32
+ it 'adds a converter' do
33
+ list.add_converter Converter.new([])
34
+ expect( list.converters ).to_not be_empty
35
+ end
36
+ it 'raises an error if the added converter is not a Converter' do
37
+ converter = double
38
+ expect { list.add_converter converter }.to raise_error(Regexp.new("#{converter.class} is not a valid Converter."))
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ context 'data manipulation' do
45
+ let(:data) { {row1: :a, row2: :b} }
46
+
47
+ let(:special_converter) do
48
+ converter = double("SpecialConverter")
49
+ allow(converter).to receive(:is_a?).with(Converter).and_return(true)
50
+ allow(converter).to receive(:convert)
51
+ allow(converter).to receive(:convert!)
52
+ allow(converter).to receive(:data).and_return([{a: '1'}])
53
+ converter
54
+ end
55
+
56
+ let(:special_cleaner) do
57
+ cleaner = double("SpecialCleaner")
58
+ allow(cleaner).to receive(:is_a?).with(Cleaner).and_return(true)
59
+ allow(cleaner).to receive(:clean).and_return({z: "9"})
60
+ cleaner
61
+ end
62
+
63
+ let(:special_interpreter) do
64
+ interpreter = double("SpecialInterpreter")
65
+ allow(interpreter).to receive(:is_a?).with(Interpreter).and_return(true)
66
+ allow(interpreter).to receive(:interpret)
67
+ allow(interpreter).to receive(:data).and_return(data)
68
+ interpreter
69
+ end
70
+
71
+ describe '#clean' do
72
+ let(:list) { Suds.new(special_interpreter) }
73
+ before do
74
+ list.add_cleaner(special_cleaner)
75
+ end
76
+
77
+ it 'cleans the data' do
78
+ expect( special_cleaner ).to receive(:clean)
79
+ list.clean
80
+ expect( list.interpreter.data ).to_not eq( list.data )
81
+ end
82
+
83
+ end
84
+
85
+ describe '#convert' do
86
+ let(:list) { Suds.new(special_interpreter) }
87
+ before do
88
+ list.add_converter special_converter
89
+ end
90
+
91
+ it 'converts the data into a portable type' do
92
+ expect( special_converter ).to receive(:convert)
93
+ list.convert
94
+ end
95
+
96
+ it 'can convert without cleaning first' do
97
+ allow( special_interpreter ).to receive(:interpret).and_return([])
98
+ list.convert
99
+ expect( list.data ).to_not be_nil
100
+ end
101
+
102
+ it 'returns a list of strings' do
103
+ allow( special_converter ).to receive(:convert).and_return("converted data")
104
+ convert = list.convert
105
+ expect( convert ).to be_an( Array )
106
+ expect( convert.first ).to be_a( String )
107
+ end
108
+ end
109
+
110
+ describe '#convert!' do
111
+ it 'writes out the data to a file' do
112
+ expect( special_converter ).to receive(:convert!)
113
+
114
+ list = Suds.new(special_interpreter)
115
+ list.add_converter special_converter
116
+ list.convert!
117
+ end
118
+ end
119
+ end
120
+
121
+ end
@@ -0,0 +1,87 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
4
+ # file to always be loaded, without a need to explicitly require it in any files.
5
+ #
6
+ # Given that it is always loaded, you are encouraged to keep this file as
7
+ # light-weight as possible. Requiring heavyweight dependencies from this file
8
+ # will add to the boot time of your test suite on EVERY test run, even for an
9
+ # individual file that may not need all of that loaded. Instead, make a
10
+ # separate helper file that requires this one and then use it only in the specs
11
+ # that actually need it.
12
+ #
13
+ # The `.rspec` file also contains a few flags that are not defaults but that
14
+ # users commonly want.
15
+ #
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+
18
+ $LOAD_PATH.unshift(File.dirname(__FILE__), *%w{.. lib/suds})
19
+ require 'coveralls'
20
+ require "codeclimate-test-reporter"
21
+ CodeClimate::TestReporter.start
22
+ Coveralls.wear!
23
+
24
+ Dir["./spec/support/**/*.rb"].sort.each { |f| require f}
25
+
26
+ RSpec.configure do |config|
27
+ # The settings below are suggested to provide a good initial experience
28
+ # with RSpec, but feel free to customize to your heart's content.
29
+ =begin
30
+ # These two settings work together to allow you to limit a spec run
31
+ # to individual examples or groups you care about by tagging them with
32
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
33
+ # get run.
34
+ config.filter_run :focus
35
+ config.run_all_when_everything_filtered = true
36
+
37
+ # Many RSpec users commonly either run the entire suite or an individual
38
+ # file, and it's useful to allow more verbose output when running an
39
+ # individual spec file.
40
+ if config.files_to_run.one?
41
+ # Use the documentation formatter for detailed output,
42
+ # unless a formatter has already been configured
43
+ # (e.g. via a command-line flag).
44
+ config.default_formatter = 'doc'
45
+ end
46
+
47
+ # Print the 10 slowest examples and example groups at the
48
+ # end of the spec run, to help surface which specs are running
49
+ # particularly slow.
50
+ config.profile_examples = 10
51
+
52
+ # Run specs in random order to surface order dependencies. If you find an
53
+ # order dependency and want to debug it, you can fix the order by providing
54
+ # the seed, which is printed after each run.
55
+ # --seed 1234
56
+ config.order = :random
57
+
58
+ # Seed global randomization in this process using the `--seed` CLI option.
59
+ # Setting this allows you to use `--seed` to deterministically reproduce
60
+ # test failures related to randomization by passing the same `--seed` value
61
+ # as the one that triggered the failure.
62
+ Kernel.srand config.seed
63
+
64
+ # rspec-expectations config goes here. You can use an alternate
65
+ # assertion/expectation library such as wrong or the stdlib/minitest
66
+ # assertions if you prefer.
67
+ config.expect_with :rspec do |expectations|
68
+ # Enable only the newer, non-monkey-patching expect syntax.
69
+ # For more details, see:
70
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
71
+ expectations.syntax = :expect
72
+ end
73
+
74
+ # rspec-mocks config goes here. You can use an alternate test double
75
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
76
+ config.mock_with :rspec do |mocks|
77
+ # Enable only the newer, non-monkey-patching expect syntax.
78
+ # For more details, see:
79
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
80
+ mocks.syntax = :expect
81
+
82
+ # Prevents you from mocking or stubbing a method that does not exist on
83
+ # a real object. This is generally recommended.
84
+ mocks.verify_partial_doubles = true
85
+ end
86
+ =end
87
+ end
@@ -0,0 +1,5 @@
1
+ shared_context "shared cleaner" do
2
+ let(:sample_array) { [data] }
3
+ let(:result) { subject.clean sample_array }
4
+ let(:modified_value) { result.first.first.last }
5
+ end
@@ -0,0 +1,15 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require 'suds/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'suds'
6
+ s.version = Suds::VERSION
7
+ s.licenses = ['MIT']
8
+ s.summary = "Interpret, clean, and convert lists."
9
+ s.description = "Suds is a versatile list manipulation library/dsl. It's meant to interpret various difference formats, apply mutations, and then export to various formats."
10
+ s.authors = ["Uri Gorelik"]
11
+ s.email = 'uri@healthwave.co'
12
+ s.files = `git ls-files`.split("\n")
13
+ s.homepage = 'https://healthwave.co/'
14
+ s.require_paths = ["lib"]
15
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: suds
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Uri Gorelik
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-27 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Suds is a versatile list manipulation library/dsl. It's meant to interpret
14
+ various difference formats, apply mutations, and then export to various formats.
15
+ email: uri@healthwave.co
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".coveralls.yml"
21
+ - ".gitignore"
22
+ - ".rspec"
23
+ - ".ruby-version"
24
+ - Gemfile
25
+ - Gemfile.lock
26
+ - Guardfile
27
+ - LICENSE.txt
28
+ - README.md
29
+ - lib/suds.rb
30
+ - lib/suds/all.rb
31
+ - lib/suds/cleaner.rb
32
+ - lib/suds/cleaner/column_converter_cleaner.rb
33
+ - lib/suds/cleaner/column_filter_cleaner.rb
34
+ - lib/suds/cleaner/downcase_cleaner.rb
35
+ - lib/suds/cleaner/regex_cleaner.rb
36
+ - lib/suds/cleaner/whitespace_cleaner.rb
37
+ - lib/suds/converter.rb
38
+ - lib/suds/converter/json_converter.rb
39
+ - lib/suds/interpreter.rb
40
+ - lib/suds/interpreter/csv_interpreter.rb
41
+ - lib/suds/interpreter/file_interpreter.rb
42
+ - lib/suds/suds.rb
43
+ - lib/suds/version.rb
44
+ - spec/lib/suds/all_spec.rb
45
+ - spec/lib/suds/cleaner/column_converter_cleaner_spec.rb
46
+ - spec/lib/suds/cleaner/column_filter_cleaner_spec.rb
47
+ - spec/lib/suds/cleaner/downcase_cleaner_spec.rb
48
+ - spec/lib/suds/cleaner/regex_cleaner_spec.rb
49
+ - spec/lib/suds/cleaner/whitespace_cleaner_spec.rb
50
+ - spec/lib/suds/cleaner_spec.rb
51
+ - spec/lib/suds/converter/json_converter_spec.rb
52
+ - spec/lib/suds/converter_spec.rb
53
+ - spec/lib/suds/interpreter/csv_interpreter_spec.rb
54
+ - spec/lib/suds/interpreter_spec.rb
55
+ - spec/lib/suds/suds_spec.rb
56
+ - spec/spec_helper.rb
57
+ - spec/support/cleaner_context.rb
58
+ - suds.gemspec
59
+ homepage: https://healthwave.co/
60
+ licenses:
61
+ - MIT
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 2.2.2
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: Interpret, clean, and convert lists.
83
+ test_files: []