goodsheet 0.3.0 → 0.3.1

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.
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