fight_csv 0.1.0

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