goodsheet 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 857c86e4b9ce1ab63a12d69a98a89194374731c8
4
- data.tar.gz: 6bc6c520ab684b0933288c25db1d389c1aa221de
3
+ metadata.gz: ba007929170fe7d6e80af781d6a20132018238f0
4
+ data.tar.gz: 97ae8e3fd6e1c83656d138a532cfa8177486da57
5
5
  SHA512:
6
- metadata.gz: 7e93e30871d1548a6f71fd969f1cde562ab99564045f29a79108ebfc191e73f53a23b830769d9c29a286028ba6c6d27c88cb3485f7846f86ddb3b61c709ce867
7
- data.tar.gz: 8cbded3dfb2890d91799a872d7d3b0489aec4bfaa55abe549a89321c331f30c83ed7c11a8db6b5422958d2318c08db1dcee92aacb0a476538f0443d519cece94
6
+ metadata.gz: d10c109430d18557c4fbd823755290ab1fc6361f2e9305de87e10896aa9bda9c5322e954969ec0991243e7921a45dc46d44f34485811ccc87506888921f006fd
7
+ data.tar.gz: 9ec851827a5c6a67e83dfadeb5036626b4f29aef8e5953a841860f5216275a453e690a81196d37e3dc10fe6eeb26e49a993e71bd43fe02aa0387c67e4a545e47
data/README.md CHANGED
@@ -24,24 +24,56 @@ Or install it yourself as:
24
24
 
25
25
  ### Getting started
26
26
 
27
+ Given a spreadsheet:
28
+ ![Spreadsheet](https://raw.github.com/iwan/goodsheet/master/img/img_01.png)
29
+
27
30
  ```ruby
28
- ss = Goodsheet::Spreadsheet.new("my_data.xlsx")
31
+ ss = Goodsheet::Spreadsheet.new("example.xls")
29
32
  res = ss.read do
30
- column_names :a => 0, :b => 1
31
- validates :a, :presence => true, :numericality => { :greater_than_or_equal_to => 0.0, :less_than_or_equal_to => 10 }
32
- validates :b, :presence => true, :numericality => { :greater_than_or_equal_to => 0.0, :less_than_or_equal_to => 100 }
33
+ column_names :filename => 0, :size => 1, :created_at => 3, :updated_at => 4 # ignore 'description' column
34
+ validates :filename, :presence => true
35
+ validates :size, :presence => true, :numericality => { :greater_than_or_equal_to => 0.0 }
36
+ validate :order_of_dates
37
+
38
+ def order_of_dates
39
+ if created_at > updated_at
40
+ errors.add(:updated_at, "cannot be before creation date")
41
+ end
42
+ end
33
43
  end
44
+ ```
34
45
 
46
+ If validation is successfull the spreadsheet will be readed and his content retrieved:
47
+ ```ruby
35
48
  res.valid? # => true
36
- res.values # => {:a => [1.0, 1.0, 1.4], :b => [0.0, 3.7, 10.9]}
49
+ res.values # => {:filename=>["img_01.jpg", "img_17.jpg", "img_56.jpg"], :size=>[123854.0, 278333.0, 529639.0], :created_at=>[#<Date: 2013-03-03 ((2456355j,0s,0n),+0s,2299161j)>, ...], ...}
50
+ ```
51
+
52
+ Alternatively you can get the result values by rows:
53
+ ```ruby
54
+ res.values(:rows_array) # => [["img_01.jpg", 123854.0, #<Date: 2013-03-03 ((2456355j,0s,0n),+0s,2299161j)>, #<Date: 2013-03-31 ((2456383j,0s,0n),+0s,2299161j)>], ["img_17.jpg", 278333.0, #<Date: 2013-05-03 ...], ...]
55
+
56
+ res.values(:rows_hash) # => [{:filename=>"img_01.jpg", :size=>123854.0, :created_at=>#<Date: 2013-03-03 ((2456355j,0s,0n),+0s,2299161j)>, :updated_at=>#<Date: 2013-03-31 ((2456383j,0s,0n),+0s,2299161j)>}, {:filename=>"img_17.jpg", :size=>278333.0, ...}, ... ]
57
+ ```
58
+
59
+ If validation fails the spreadsheet will not be readed:
60
+ ```ruby
61
+ res.valid? # => false
62
+ res.errors.size # => 1
63
+ res.errors.to_a.first # => "Row 3 is invalid: Filename can't be blank"
64
+ res.values # => {}
37
65
  ```
38
66
 
39
67
  By default:
40
68
  * the first sheet is selected
41
69
  * one line (the first) is skipped (i'm expeting that is the header line)
42
- * the content is returned as an hash of columns
43
70
 
44
- Pass your validation rules into the block passed to the read method, together with the column_names method that define the position (or index) and the name of the columns you want to read.
71
+ You can also invoke `validate` instead of `read` method (see below).
72
+ A validation will be always executed before the extraction (reading) of data. If validation fails the reading will not be performed.
73
+
74
+ The definition of columns you want to read (through the `column_names` method) and the rules for validation are defined inside the block you pass to `read` or `validate` method.
75
+
76
+
45
77
 
46
78
  ### More on usage
47
79
 
@@ -58,16 +90,17 @@ ss.size # => 4
58
90
  ss.sheets # => ["Sheet1", "Sheet2", "Sheet3", "Sheet4"]
59
91
  ```
60
92
 
61
- When you init a new spreadsheet, you select a sheet or you invoke `validate` or `read` method, you can pass an hash of options.
93
+ An hash of options can be passed to spreadsheet initializer `Goodsheet::Spreadsheet.new`, to `sheet` selection, to `validate` or `read` method. The first one define validation/reading rules for all sheets, but these can be overwritten by sheet and `validate`/`read`.
94
+
62
95
  ```ruby
63
96
  ss = Goodsheet::Spreadsheet.new("my_data.xlsx", :skip => 1, :header_row => 0, :max_errors => 0, :row_limit => 0, :force_nil => nil )
64
97
  ```
65
98
  These are the valid options with their default values:
66
- - `:skip` allow to skip a desired number of lines when you read or validate a sheet
67
- - with `:header_row` you define the index of the header row
68
- - with `:max_errors` you define the maximum number of errors after the validation break
69
- - with `:row_limit` you define the maximum number of row you wanto to read or validate
70
- - with `:force_nil` you can specify the value to set when a cell hold a nil value (is empty)
99
+ - `:skip` allow to skip a desired number of lines when you read or validate a sheet (1)
100
+ - with `:header_row` you define the index of the header row (0)
101
+ - with `:max_errors` you define the maximum number of errors after the validation break (0: no limit)
102
+ - with `:row_limit` you define the maximum number of row you wanto to read or validate (0: no limit)
103
+ - with `:force_nil` you can specify the value to set when a cell hold a nil value (is empty) (still nil)
71
104
 
72
105
  As said you can use the same option when selecting a sheet
73
106
  ```ruby
@@ -95,7 +128,7 @@ ss.rows_wo_skipped # => except the skipped ones, aliased by `rows` method
95
128
 
96
129
  Use the `validate` and `read` methods to perform validation and reading. Note that the reading function include a validation call.
97
130
  Pass the previously seen `options` hash and a block to `validate`/`read` method.
98
- Inside the block you define columns names and indexes you want to validate/read using the `column_names` method. You can use one of these 4 forms:
131
+ Inside the block you define columns names and indexes you want to validate/read using the `column_names` method. You can use one of these 4 forms (and their effect is the same):
99
132
  - `column_names :a => 0, :b => 1, :c => 3`
100
133
  - `column_names 0 => :a, 1 => :b, 3 => :c`
101
134
  - `column_names [:a, :b, nil, :c]`
data/img/img_01.png ADDED
Binary file
data/lib/goodsheet.rb CHANGED
@@ -4,9 +4,9 @@ module Goodsheet
4
4
  autoload :Row, 'goodsheet/row'
5
5
  autoload :SheetNotFoundError, 'goodsheet/exceptions'
6
6
  autoload :ReadResult, 'goodsheet/read_result'
7
- autoload :Row, 'goodsheet/row'
8
7
  autoload :Spreadsheet, 'goodsheet/spreadsheet'
9
8
  autoload :ValidationError, 'goodsheet/validation_error'
10
9
  autoload :ValidationErrors, 'goodsheet/validation_errors'
11
10
  autoload :Version, 'goodsheet/version'
12
11
  end
12
+
@@ -16,13 +16,14 @@ module Goodsheet
16
16
  !valid?
17
17
  end
18
18
 
19
- def add(attribute, row, force_nil=nil)
19
+ def add(attribute, row)
20
20
  attribute = attribute.to_sym
21
- (@vv[attribute] ||= []) << (row.send(attribute) || force_nil)
21
+ (@vv[attribute] ||= []) << row.send(attribute)
22
22
  end
23
23
 
24
24
  def values(format=:columns)
25
- values_size = @vv.values.first.size
25
+ values_size = 0
26
+ values_size = @vv.values.first.size if @vv.values.first
26
27
 
27
28
  case format
28
29
  when :columns
data/lib/goodsheet/row.rb CHANGED
@@ -12,10 +12,10 @@ module Goodsheet
12
12
  end
13
13
  @keys = {} # idx => key
14
14
 
15
- def initialize(arr)
15
+ def initialize(arr, nil_value=nil)
16
16
  arr.each_with_index do |v, idx|
17
17
  if k = self.class.keys[idx]
18
- send("#{k}=", v)
18
+ send("#{k}=", v || nil_value)
19
19
  end
20
20
  end
21
21
  super()
@@ -110,20 +110,18 @@ module Goodsheet
110
110
  # @yield Column settings and validation rules
111
111
  # @return [ValidationErrors] Validation errors
112
112
  def validate(options={}, &block)
113
- # set_current_sheet_options(options)
114
113
  skip = options[:skip] || @s_opts[index][:skip]
115
114
  header_row = options[:header_row] || @s_opts[index][:header_row]
116
115
  max_errors = options[:max_errors] || @s_opts[index][:max_errors]
117
116
  row_limit = options[:row_limit] || @s_opts[index][:row_limit]
118
117
  force_nil = options[:force_nil] || @s_opts[index][:force_nil]
119
-
120
118
  validation_errors = ValidationErrors.new
121
119
 
122
120
  my_class = options[:my_custom_row_class] || build_my_class(block)
123
121
 
124
122
  line = @s_opts[index][:skip] # 0-based, from the top
125
123
  @ss.parse[@s_opts[index][:skip]..-1].each do |row| # row is an array of elements
126
- validation_errors.add(line, my_class.new(row))
124
+ validation_errors.add(line, my_class.new(row, force_nil))
127
125
  break if max_errors>0 && validation_errors.size >= max_errors
128
126
  break if row_limit && row_limit>0 && line>=(row_limit+@s_opts[index][:skip]-1)
129
127
  line +=1
@@ -157,7 +155,7 @@ module Goodsheet
157
155
  line = skip # 0-based, from the top
158
156
  @ss.parse[skip..-1].each do |row| # row is an array of elements
159
157
  my_class.row_attributes.each do |attribute|
160
- read_result.add(attribute, my_class.new(row), force_nil)
158
+ read_result.add(attribute, my_class.new(row, force_nil))
161
159
  end
162
160
  break if row_limit && row_limit>0 && line>=(row_limit + skip - 1)
163
161
  line +=1
@@ -169,7 +167,8 @@ module Goodsheet
169
167
  private
170
168
 
171
169
  def build_my_class(block)
172
- Object.const_set get_custom_row_class_name, Row.inherit(block)
170
+ n = get_custom_row_class_name
171
+ Object.const_set n, Row.inherit(block)
173
172
  end
174
173
 
175
174
  def select_sheet_options(idx)
@@ -194,10 +193,6 @@ module Goodsheet
194
193
  "CustRow_#{(Time.now.to_f*(10**10)).to_i}"
195
194
  end
196
195
 
197
- def set_current_sheet_options(options)
198
- set_sheet_options(index, options)
199
- end
200
-
201
196
  def set_sheet_options(idx, options)
202
197
  i = idx.is_a?(Integer) ? idx : @ss.sheets.index(idx)
203
198
  @s_opts[i] = {
@@ -7,7 +7,8 @@ module Goodsheet
7
7
  end
8
8
 
9
9
  def to_s
10
- "Row #{@line} is invalid for the following reason(s): #{@val_err.full_messages.join(', ')}"
10
+ # "Row #{@line} is invalid for the following reason(s): #{@val_err.full_messages.join(', ')}"
11
+ "Row #{@line} is invalid: #{@val_err.full_messages.join(', ')}"
11
12
  end
12
13
  end
13
14
  end
@@ -36,5 +36,9 @@ module Goodsheet
36
36
  yield(i)
37
37
  end
38
38
  end
39
+
40
+ def valid?
41
+ empty?
42
+ end
39
43
  end
40
44
  end
@@ -1,3 +1,3 @@
1
1
  module Goodsheet
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
Binary file
Binary file
@@ -9,12 +9,12 @@ class TestSpreadsheet_01 < Test::Unit::TestCase
9
9
  end
10
10
 
11
11
  def test_sheets
12
- assert_equal(%w(Sheet1 Sheet2 Sheet3 Sheet4), @ss.sheets)
12
+ assert_equal(%w(Sheet1 Sheet2 Sheet3 Sheet4 Sheet5), @ss.sheets)
13
13
  end
14
14
 
15
15
  def test_failed_sheet_selection
16
16
  assert_raise Goodsheet::SheetNotFoundError do
17
- @ss.sheet(4)
17
+ @ss.sheet(14)
18
18
  end
19
19
  assert_raise Goodsheet::SheetNotFoundError do
20
20
  @ss.sheet("Sheet999")
@@ -36,6 +36,9 @@ class TestSpreadsheet_01 < Test::Unit::TestCase
36
36
 
37
37
  @ss.sheet(3)
38
38
  assert_equal("Sheet4", @ss.name)
39
+
40
+ @ss.sheet(4)
41
+ assert_equal("Sheet5", @ss.name)
39
42
  end
40
43
 
41
44
  def test_get_header_wo_options
@@ -105,7 +108,7 @@ class TestSpreadsheet_01 < Test::Unit::TestCase
105
108
  end
106
109
 
107
110
  def test_size
108
- assert_equal(4, @ss.size)
111
+ assert_equal(5, @ss.size)
109
112
  end
110
113
 
111
114
  def test_options_precedence
@@ -126,6 +129,8 @@ class TestSpreadsheet_01 < Test::Unit::TestCase
126
129
  assert_equal({:skip=>1, :header_row=>0, :max_errors=>0, :row_limit=>0, :force_nil=>nil}, @ss.options)
127
130
  end
128
131
 
132
+
133
+
129
134
  def test_read_sheet4
130
135
  @ss.sheet(3)
131
136
 
@@ -135,11 +140,11 @@ class TestSpreadsheet_01 < Test::Unit::TestCase
135
140
  validates :price, :presence => true, :numericality => { :greater_than_or_equal_to => 0.0 }
136
141
  validates :tot, :presence => true, :numericality => { :greater_than_or_equal_to => 0.0 }
137
142
  end
143
+ assert_equal([:qty, :price, :tot], result.values.keys)
138
144
  assert_equal(3, result.values.size)
139
145
  result.values.each do |k, vv|
140
146
  assert_equal(5, vv.size)
141
147
  end
142
- assert_equal([:qty, :price, :tot], result.values.keys)
143
148
 
144
149
 
145
150
 
@@ -1,9 +1,45 @@
1
- # test_spreadsheet_01.rb
1
+ # test_spreadsheet_03.rb
2
+
3
+ # ruby -I lib test/test_spreadsheet_03.rb
2
4
 
3
5
  require 'test/unit'
4
6
  require 'goodsheet'
5
7
 
6
8
  class TestSpreadsheet_03 < Test::Unit::TestCase
7
9
 
10
+ def setup
11
+ filepath = File.dirname(__FILE__) + "/fixtures/ss_01.xls"
12
+ @ss = Goodsheet::Spreadsheet.new(filepath)
13
+ end
14
+
15
+ def test_validation_with_nil_values
16
+ @ss.sheet(4) # "Sheet5"
17
+ result = @ss.validate do
18
+ column_names 0 => :qty, 1 => :price, 2 => :tot
19
+ validates :qty, :price, :tot, numericality: true
20
+ end
21
+ assert(!result.valid?)
22
+
23
+ result = @ss.validate(:force_nil => 0.0) do
24
+ column_names 0 => :qty, 1 => :price, 2 => :tot
25
+ validates :qty, :price, :tot, numericality: true
26
+ end
27
+ assert(result.valid?)
28
+ end
29
+
30
+ def test_reading_with_nil_values
31
+ @ss.sheet(4) # "Sheet5"
32
+ result = @ss.read do
33
+ column_names 0 => :qty, 1 => :price, 2 => :tot
34
+ validates :qty, :price, :tot, numericality: true
35
+ end
36
+ assert(!result.valid?)
37
+
38
+ result = @ss.read(:force_nil => 0.0) do
39
+ column_names 0 => :qty, 1 => :price, 2 => :tot
40
+ validates :qty, :price, :tot, numericality: true
41
+ end
42
+ assert(result.valid?)
43
+ end
8
44
 
9
45
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goodsheet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Iwan Buetti
@@ -94,6 +94,7 @@ files:
94
94
  - README.md
95
95
  - Rakefile
96
96
  - goodsheet.gemspec
97
+ - img/img_01.png
97
98
  - lib/goodsheet.rb
98
99
  - lib/goodsheet/exceptions.rb
99
100
  - lib/goodsheet/read_result.rb
@@ -103,6 +104,7 @@ files:
103
104
  - lib/goodsheet/validation_errors.rb
104
105
  - lib/goodsheet/version.rb
105
106
  - notes.txt
107
+ - test/fixtures/example.xls
106
108
  - test/fixtures/fixtures_template.xlsx
107
109
  - test/fixtures/ss_01.xls
108
110
  - test/fixtures/ss_02.csv
@@ -139,6 +141,7 @@ signing_key:
139
141
  specification_version: 4
140
142
  summary: Extract and validate data from a spreadsheet
141
143
  test_files:
144
+ - test/fixtures/example.xls
142
145
  - test/fixtures/fixtures_template.xlsx
143
146
  - test/fixtures/ss_01.xls
144
147
  - test/fixtures/ss_02.csv