fight_csv 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.
@@ -0,0 +1,2 @@
1
+ "Should be Cooked?","Should be baked?","Baking Time","Name","Ingredients","Cooking time"
2
+ "No","No",,Gin Tonic,"Gin, Tonic, Crushed Ice",
@@ -0,0 +1,13 @@
1
+ field 'Name', {
2
+ identifier: :name,
3
+ }
4
+
5
+ field 'Paradigms', {
6
+ identifier: :paradigms,
7
+ validator: /([^,]*,)*[^,]*/,
8
+ converter: ->(string) { string.split(',') },
9
+ }
10
+
11
+ field 'Creator' , {
12
+ identifier: :creator,
13
+ }
@@ -0,0 +1,4 @@
1
+ Name,Paradigms,Creator
2
+ Ruby,"object oriented,imperative,reflective,functional",Yukihiro Matsumoto
3
+ Scheme,"Functional, Procedural, Meta",Guy L. Steele
4
+ Brainfuck,"being lol",'Some random guy'
@@ -0,0 +1,3 @@
1
+ Name,Ingredients,Cooking time,Should be Cooked?,Should be baked?,Baking Time
2
+ Apple Pie,"2 apples, 500 pound flour, eggs",,No,Yes,20
3
+ Soup,"vegetables, vegetable stack or meat stock, water",30,Yes,No,
data/test/helper.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'bundler'
2
+ begin
3
+ Bundler.setup(:default, :development)
4
+ rescue Bundler::BundlerError => e
5
+ $stderr.puts e.message
6
+ $stderr.puts "Run `bundle install` to install missing gems"
7
+ exit e.status_code
8
+ end
9
+
10
+ require 'simplecov'
11
+ SimpleCov.start do
12
+ add_filter 'test'
13
+ add_filter 'help'
14
+ end
15
+
16
+ require 'minitest/autorun'
17
+
18
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
19
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
20
+ require 'fight_csv'
21
+
22
+ class MiniTest::Unit::TestCase
23
+ include FightCSV
24
+ def fixture(filename)
25
+ File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', filename)
26
+ end
27
+
28
+ def refute_raises
29
+ test = -> do
30
+ begin
31
+ yield
32
+ rescue => e
33
+ return false, e
34
+ else
35
+ true
36
+ end
37
+ end
38
+ boolean, exception = test.call
39
+ assert boolean, "Expected no exception, but got #{exception}"
40
+ end
41
+ end
@@ -0,0 +1,119 @@
1
+ require 'helper'
2
+ require 'set'
3
+
4
+ describe 'Field' do
5
+ describe 'initialize' do
6
+ it 'sets the matcher for matching csv fields' do
7
+ field = FightCSV::Field.new(/Field/, identifier: :field)
8
+ assert_equal /Field/, field.matcher
9
+ end
10
+ end
11
+
12
+ describe 'identifier' do
13
+ it 'defaults to the matcher downcase' do
14
+ field = FightCSV::Field.new("Field")
15
+ assert_equal field.identifier, :field
16
+ end
17
+
18
+ it 'raises an error if nor a identifier is present neither the matcher is a string' do
19
+ field = FightCSV::Field.new(5)
20
+ assert_raises ArgumentError do
21
+ field.identifier
22
+ end
23
+ end
24
+ end
25
+
26
+ describe 'ivar_symbol' do
27
+ it 'returns the identifier as an ivar symbol' do
28
+ field = FightCSV::Field.new(/Field/, identifier: :field)
29
+ assert_equal :@field, field.ivar_symbol
30
+ end
31
+ end
32
+
33
+ describe 'validate' do
34
+ before do
35
+ @field = FightCSV::Field.new(/(Git-Hash)|(Commit-Hash)/,required: true, validator: /[a-z0-9]{5}/, identifier: :git_hash)
36
+ end
37
+
38
+ it 'checks for every property, returns {valid: true} if valid' do
39
+ assert_equal({valid: true}, @field.validate(['12abc'], ['Git-Hash']))
40
+ end
41
+
42
+ it 'returns detailed error information if invalid(validate)' do
43
+ assert_equal({valid: false, error: ":git_hash must match (?-mix:[a-z0-9]{5}), but was \"foo\""},
44
+ @field.validate(['foo'], ['Git-Hash']))
45
+ end
46
+
47
+ it 'also accepts a proc as a validator(not valid)' do
48
+ @field.validator = ->(git_hash) { git_hash === /[a-z0-9]{5}/ }
49
+ assert_equal({valid: false, error: ":git_hash must pass #{@field.validator}, but was \"foo\""},
50
+ @field.validate(['foo'],['Git-Hash']))
51
+ end
52
+
53
+ it 'also accepts a proc as a validator(not valid)' do
54
+ @field.validator = ->(git_hash) { /[a-z0-9]{5}/ === git_hash }
55
+ assert_equal({valid: true},
56
+ @field.validate(['5oa9c'],['Git-Hash']))
57
+ end
58
+ end
59
+
60
+ describe 'match' do
61
+ before do
62
+ @row = ['6','7']
63
+ end
64
+ describe 'not an integer' do
65
+ before do
66
+ @field = FightCSV::Field.new('Foo', identifier: :foo)
67
+ end
68
+
69
+ it 'returns an element, whose header matches the matcher' do
70
+ header = ['Foo', 'Bar']
71
+ assert_equal '6', @field.match(@row, header)
72
+ end
73
+
74
+ it 'raises an ArgumentError, if no header is provided' do
75
+ assert_raises ArgumentError do
76
+ @field.match(@row)
77
+ end
78
+ end
79
+ end
80
+
81
+ describe 'an integer' do
82
+ it 'returns the element, with the index == matcher - 1' do
83
+ field = FightCSV::Field.new(1, identifier: :foo)
84
+ assert_equal '6', field.match(@row)
85
+ end
86
+ end
87
+ end
88
+
89
+ describe 'process' do
90
+ before do
91
+ @field = FightCSV::Field.new('Foo', identifier: :foo)
92
+ end
93
+
94
+ it 'raises an ArgumentError if no header is present, and the matcher isn\'t an Integer' do
95
+ assert_raises ArgumentError do
96
+ @field.process(['bla'])
97
+ end
98
+ end
99
+
100
+ it 'returns a match if one is present' do
101
+ row = ['6','7']
102
+ header = ['Bar','Foo']
103
+ assert_equal '7', @field.process(row, header)
104
+ end
105
+
106
+ it 'returns "" if it doesnt find a match' do
107
+ row = ['6']
108
+ header = ['Bar']
109
+ assert_equal nil, @field.process(row, header)
110
+ end
111
+
112
+ it 'converts a value if necessary' do
113
+ @field.converter = proc { |value| value.to_i ** 2 }
114
+ row = ['6']
115
+ header = ['Foo']
116
+ assert_equal 36, @field.process(row, header)
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,23 @@
1
+ require 'helper'
2
+
3
+ describe 'Integration' do
4
+ before do
5
+
6
+ schema_file = fixture('prog_lang_schema.rb')
7
+ ProgrammingLanguage ||= Class.new do
8
+ include FightCSV::Record
9
+ schema schema_file
10
+ end
11
+
12
+ @programming_languages = ProgrammingLanguage.records File.open(fixture('programming_languages.csv'))
13
+ end
14
+
15
+ it 'can validate a csv document' do
16
+ assert_equal true, @programming_languages.all? { |programming_language| programming_language.valid? }
17
+ end
18
+
19
+ it 'converts fields of a csv document and provides dynamic attribut readers' do
20
+ ruby = @programming_languages.find { |prog_lang| prog_lang.name == 'Ruby' }
21
+ assert_equal ['object oriented', 'imperative', 'reflective', 'functional'], ruby.paradigms
22
+ end
23
+ end
@@ -0,0 +1,132 @@
1
+ require 'helper'
2
+ describe 'Record' do
3
+ before do
4
+ @klass = Class.new do
5
+ include FightCSV::Record
6
+ end
7
+ @schema = FightCSV::Schema.new do
8
+ field('Foo', identifier: :foo)
9
+ field('Foz', identifier: :foz)
10
+ end
11
+ @klass.schema = @schema
12
+ end
13
+
14
+ describe 'schema=' do
15
+ it 'automatically refreshes the row' do
16
+ instance = @klass.new(%w{bar baz}, header: %w{Bar Foz})
17
+ instance.schema = FightCSV::Schema.new do
18
+ field('Bar', identifier: :bar)
19
+ end
20
+ assert_equal({ bar: 'bar' }, instance.row)
21
+ end
22
+ end
23
+
24
+ describe 'records' do
25
+ it 'maps each row to a record object' do
26
+ records = @klass.records("Foo,Foz\nbar,baz\nfoo,foz")
27
+ assert records.all? { |r| Record === r }
28
+ assert_equal 'bar', records.first.foo
29
+ end
30
+ end
31
+
32
+ describe 'import' do
33
+ it 'provides an enumerator for iterating over the csv reusing the same record object' do
34
+ enum = @klass.import("Foo,Foz\nbar,baz\nfoo,foz")
35
+ assert enum.map(&:object_id).each_cons(2).all? { |a,b| a == b }, 'Expected all records to be the same object'
36
+ assert_equal({foo: 'bar', foz: 'baz'}, enum.first.row)
37
+ end
38
+ end
39
+
40
+ describe 'fields' do
41
+ it 'aggregates fields using the dynamic attribute readers or hard coded readers' do
42
+ @klass.class_eval { def foo; 1;end }
43
+ record = @klass.new(['Bar','Baz'], schema: @schema, header: ['Foo', 'Foz'])
44
+ assert_equal({foo: 1, foz: 'Baz'}, record.fields)
45
+ end
46
+ end
47
+
48
+ describe 'dynamic attributes' do
49
+ describe 'readers' do
50
+ it 'works' do
51
+ record = @klass.new(['Bar','Baz'], schema: @schema, header: ['Foo', 'Foz'])
52
+ assert_equal 'Bar', record.foo
53
+ assert_equal 'Baz', record.foz
54
+ end
55
+
56
+ it 'returns "" if the attribute is not defined' do
57
+ record = @klass.new(['Bar'], schema: @schema, header: ['Foo'])
58
+ assert_equal 'Bar', record.foo
59
+ assert_equal nil, record.foz
60
+ end
61
+
62
+ it 'converts values if necessary' do
63
+ @schema.fields.find { |f| f.matcher == 'Foo' }.converter = proc { |value| value.downcase.to_sym }
64
+ record = @klass.new(['Bar'], schema: @schema,header: ['Foo'])
65
+ assert_equal :bar, record.foo
66
+ end
67
+ end
68
+ describe 'writers' do
69
+ it 'allow write access to attributes in the row' do
70
+ record = @klass.new(['Bar'], schema: @schema, header: ['Foo'])
71
+ record.foo = 4
72
+ assert_equal 4, record.foo
73
+ end
74
+
75
+ it 'allows to write to fields defined in the schema but not provided through the csv document' do
76
+ record = @klass.new([], schema: @schema,header: [])
77
+ record.foo = 4
78
+ assert_equal 4, record.foo
79
+ end
80
+ end
81
+ end
82
+
83
+ describe 'shared test data' do
84
+ before do
85
+ @class = Class.new { include FightCSV::Record }
86
+ @class.schema do
87
+ converter = ->(value) { value.to_i }
88
+ field 'a', identifier: :a, converter: converter
89
+ field 'b', identifier: :b, converter: converter
90
+ field 'c', identifier: :c, converter: converter
91
+ end
92
+ @record = @class.new %w{1 2 3}, header: ['a','b','c']
93
+ end
94
+
95
+ describe 'row' do
96
+ it 'returns a hash with identifiers as keys and values as values' do
97
+ assert_equal Hash[[[:a, 1],[:b,2],[:c,3]]], @record.row
98
+ end
99
+ end
100
+ end
101
+
102
+ describe 'schema validation' do
103
+ before do
104
+ prog_lang_schema = fixture('prog_lang_schema.rb')
105
+ @prog_lang = Class.new do
106
+ include FightCSV::Record
107
+ schema prog_lang_schema
108
+ end
109
+ @prog_langs = @prog_lang.records(File.open(fixture('programming_languages.csv')))
110
+ end
111
+
112
+ describe 'valid?' do
113
+ it 'returns true if a record is valid' do
114
+ assert_equal true, @prog_langs.all?(&:valid?)
115
+ end
116
+ end
117
+
118
+ describe 'errors' do
119
+ it 'returns a hash includind valid: true if the record is valid' do
120
+ assert_equal({valid: true, errors: []}, @prog_langs.first.validate)
121
+ end
122
+
123
+ it 'returns a hash inlcuding valid: false and detailed error report' do
124
+ @prog_lang.schema.fields.find { |f| f.identifier == :creator }.validator = /.+/
125
+ errors = [ ':creator must match (?-mix:.+), but was ""']
126
+ instance = @prog_lang.new(['LOLCODE','lolfulness',nil], header: ['Name','Paradigms'])
127
+ assert_equal false, instance.valid?
128
+ assert_equal errors, instance.errors
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,34 @@
1
+ require 'helper'
2
+ describe 'Schema' do
3
+ describe 'DSL' do
4
+ it 'allows to easily create a field' do
5
+ schema = FightCSV::Schema.new
6
+ converter = ->(value) { value.upcase }
7
+
8
+ schema.field "Name", {
9
+ converter: converter,
10
+ validator: /\w*/,
11
+ identifier: :name
12
+ }
13
+
14
+ field = schema.fields.first
15
+ assert_equal converter, field.converter
16
+ assert_equal /\w*/, field.validator
17
+ end
18
+
19
+ it 'accepts a file name' do
20
+ schema = Schema.new fixture('prog_lang_schema.rb')
21
+ assert_equal FightCSV::Schema, schema.class
22
+ assert_equal 'Name', schema.fields.first.matcher
23
+ assert_equal 'Creator', schema.fields.last.matcher
24
+ end
25
+
26
+ it 'also responds to a block' do
27
+ schema = Schema.new do
28
+ field 'Foo', identifier: :foo
29
+ end
30
+
31
+ assert_equal 'Foo', schema.fields.first.matcher
32
+ end
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,172 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fight_csv
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Manuel Korfmann
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-08-18 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: constructable
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ prerelease: false
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: activesupport
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ version: "0"
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: jeweler
48
+ requirement: &id003 !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 1
55
+ - 6
56
+ - 0
57
+ version: 1.6.0
58
+ type: :development
59
+ prerelease: false
60
+ version_requirements: *id003
61
+ - !ruby/object:Gem::Dependency
62
+ name: awesome_print
63
+ requirement: &id004 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: *id004
74
+ - !ruby/object:Gem::Dependency
75
+ name: simplecov
76
+ requirement: &id005 !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ segments:
82
+ - 0
83
+ version: "0"
84
+ type: :development
85
+ prerelease: false
86
+ version_requirements: *id005
87
+ - !ruby/object:Gem::Dependency
88
+ name: rake
89
+ requirement: &id006 !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ segments:
95
+ - 0
96
+ version: "0"
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: *id006
100
+ description: "\n\
101
+ Provides a nice DSL to describe your CSV document.\n\
102
+ CSV documents can be validated against this description.\n\
103
+ You can easily define types like Integer or Array for CSV through converters.\n "
104
+ email: manu@korfmann.info
105
+ executables: []
106
+
107
+ extensions: []
108
+
109
+ extra_rdoc_files:
110
+ - LICENSE.txt
111
+ - README.md
112
+ files:
113
+ - .document
114
+ - .rvmrc
115
+ - Gemfile
116
+ - Gemfile.lock
117
+ - LICENSE.txt
118
+ - README.md
119
+ - Rakefile
120
+ - VERSION
121
+ - examples/timetracking.rb
122
+ - fight_csv.gemspec
123
+ - lib/fight_csv.rb
124
+ - lib/fight_csv/data_source.rb
125
+ - lib/fight_csv/field.rb
126
+ - lib/fight_csv/record.rb
127
+ - lib/fight_csv/schema.rb
128
+ - tags
129
+ - test/fixtures/cocktails.csv
130
+ - test/fixtures/prog_lang_schema.rb
131
+ - test/fixtures/programming_languages.csv
132
+ - test/fixtures/recipes.csv
133
+ - test/helper.rb
134
+ - test/test_field.rb
135
+ - test/test_fight_csv.rb
136
+ - test/test_record.rb
137
+ - test/test_schema.rb
138
+ has_rdoc: true
139
+ homepage: http://github.com/mkorfmann/fight_csv
140
+ licenses:
141
+ - MIT
142
+ post_install_message:
143
+ rdoc_options: []
144
+
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ none: false
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ hash: 729338381106442456
153
+ segments:
154
+ - 0
155
+ version: "0"
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ none: false
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ segments:
162
+ - 0
163
+ version: "0"
164
+ requirements: []
165
+
166
+ rubyforge_project:
167
+ rubygems_version: 1.3.7
168
+ signing_key:
169
+ specification_version: 3
170
+ summary: JSON-Schema + ActiveModel for CSV
171
+ test_files: []
172
+