suds 0.1.0

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