goodsheet 0.2.1.1 → 0.2.2

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: 4c5b7ba40c72803a5496d2c8138e45c0d12b48e0
4
- data.tar.gz: 2fe40921757862d1c4e1920bb88f91716d4294cf
3
+ metadata.gz: 9a61094f40016b3c32594b97b5d3e134c4b59016
4
+ data.tar.gz: 5fc1f3d8bbbdeaf1efafbceeb2610296004bce86
5
5
  SHA512:
6
- metadata.gz: 823e26db17139b66e83446d6ad5df5b527bd743cebedf68d87e78039738d7f217ff06226c7992fc2239682e7d7395d37dba3ecb0cdc67d8264db7ce30b3c665f
7
- data.tar.gz: a2e4a6b75ccf0d84fdb243e5c47c795ecdcc171186d8d0f8ab8b93d19dd19982b9f5bc4351f5b35b5b6a508aa2192de64ce4c39008672d26256644e2dfa2c153
6
+ metadata.gz: 0ff578441474684622d97d9e7481abb007783c6ee0fb302689377c081eff0662dda889766140dbe0d51e7b99d0e9ba0e0846deb7463c0df85994fe6a8c5a3d07
7
+ data.tar.gz: 50dcf983da1e79799d313366c47369d0b8569aad10ff6a9312ceae7c4eeae8d5b0229ef0b49dc30dda941434e315214450349a8c2723feb699272e5eb98c21dc
data/README.md CHANGED
@@ -42,24 +42,87 @@ By default:
42
42
 
43
43
  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.
44
44
 
45
- ### More information on usage
45
+ ### More on usage
46
46
 
47
47
  You can select the desired sheet by index (starting from zero) or by name:
48
48
  ```ruby
49
49
  ss = Goodsheet::Spreadsheet.new("my_data.xlsx")
50
50
  ss.sheet(2) # select the third sheet
51
- ss.sheet("holydays")
52
- ss.sheets # get the sheet names array
51
+ ss.sheet("Sheet4") # select the sheet named "Sheet4"
53
52
  ```
54
- Get the
55
53
 
54
+ Get the number of sheets, and their names:
55
+ ```ruby
56
+ ss.size # => 4
57
+ ss.sheets # => ["Sheet1", "Sheet2", "Sheet3", "Sheet4"]
58
+ ```
56
59
 
60
+ When you init a new spreadsheet, you select a sheet or you invoke `validate` or `read` method, you can pass an hash of options.
61
+ ```ruby
62
+ ss = Goodsheet::Spreadsheet.new("my_data.xlsx", :skip => 1, :header_row => 0, :max_errors => 0, :row_limit => 0, :force_nil => nil )
63
+ ```
64
+ These are the valid options with their default values:
65
+ - `:skip` allow to skip a desired number of lines when you read or validate a sheet
66
+ - with `:header_row` you define the index of the header row
67
+ - with `:max_errors` you define the maximum number of errors after the validation break
68
+ - with `:row_limit` you define the maximum number of row you wanto to read or validate
69
+ - with `:force_nil` you can specify the value to set when a cell hold a nil value (is empty)
70
+
71
+ As said you can use the same option when selecting a sheet
72
+ ```ruby
73
+ ss.sheet(0, :skip => 2)
74
+ ```
75
+ or read or validate a sheet:
57
76
  ```ruby
77
+ ss.validate(0, :force_nil => 0.0) do
78
+ # ...
79
+ end
80
+ ```
58
81
 
82
+ Get the content of header row:
83
+ ```ruby
84
+ ss.header_row # => ["year", "month", "day"]
85
+ ```
59
86
 
87
+ Get the number of rows:
88
+ ```ruby
89
+ ss.total_rows # => all instanced rows
90
+ ss.rows_wo_skipped # => except the skipped ones, aliased by `rows` method
91
+ ```
92
+
93
+ #### Reading and validate
94
+
95
+ Use the `validate` and `read` methods to perform validation and reading. Note that the reading function include a validation call.
96
+ Pass the previously seen `options` hash and a block to `validate`/`read` method.
97
+ 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 3 forms:
98
+ - `column_names :a => 0, :b => 1, :c => 3`
99
+ - `column_names 0 => :a, 1 => :b, 3 => :c`
100
+ - `column_names [:a, :b, nil, :c]`
101
+
102
+ Aside from define the columns settings, into block you define the validation rules.
103
+ Refer to the [official guide](http://guides.rubyonrails.org/active_record_validations.html) and [ROR Api](http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html)
104
+
105
+
106
+ Another example:
107
+ ```ruby
108
+ ss = Goodsheet::Spreadsheet.new("my_data.xlsx")
109
+ ss.sheet(1, :max_errors => 50, :force_nil => 0.0)
110
+ res = ss.read do
111
+ column_names :item => 0, :qty => 1, :price => 2, :prod => 3
112
+ # same as: [:item, :qty, :price, :prod]
113
+ validates :item, :presence => true
114
+ validates :qty, :presence => true, :numericality => { :greater_than => 0.0}
115
+ validates :price, :presence => true, :numericality => { :greater_than => 0.0}
116
+ validate :product
117
+
118
+ def :product
119
+ if qty * price != prod
120
+ errors.add(:prod, "must be the product of qty and price")
121
+ end
122
+ end
123
+ end
60
124
  ```
61
125
 
62
- The +read+ method include the +validate+ method, that can be invoke
63
126
 
64
127
 
65
128
 
@@ -3,86 +3,151 @@ require 'roo'
3
3
  module Goodsheet
4
4
 
5
5
  class Spreadsheet < Roo::Spreadsheet
6
- attr_reader :time_zone, :skip, :header_row, :max_errors, :row_limit
6
+ attr_reader :skip, :header_row, :max_errors, :row_limit
7
+ attr_reader :s_opts
7
8
 
8
- # Initialize a Goodsheet object
9
+ # Initialize a Goodsheet object. The first sheet will be selected.
9
10
  #
10
- # @param filename [String] the spreadsheet filename you want to read
11
- # @param [String] filename the spreadsheet filename you want to read
12
- # Valid options:
13
- # :skip : number of rows to skip (default: 1)
14
- # :header_row : header's row index (0 based, default: 0)
15
- # :time_zone : time zone string
11
+ # @param filename [String] The spreadsheet filename you want to read
12
+ # @param [Hash] options Options to define the behaviour on reading and validation the sheets. These options are applied to all sheets, but can be overwritten by similar options when selecting the sheet or calling +read+ or +validate+ method
13
+ # @option options [Fixnum] :skip (1) Number of rows to skip
14
+ # @option options [Fixnum] :header_row (0) The header row index (0-based)
15
+ # @option options [Fixnum] :max_errors (0) Max number of error until stop validation
16
+ # @option options [Fixnum] :row_limit (0) Max number of row to read
17
+ # @option options [Object] :force_nil (nil) Force nils found to this value
16
18
  def initialize(filename, options={})
17
- set_options(options)
19
+ # set_book_options(options)
18
20
  @filename = filename
19
21
  @ss = Roo::Spreadsheet.open(filename, options)
22
+ @s_opts = Array.new(size, {})
23
+ size.times do |i|
24
+ set_sheet_options(i, options)
25
+ end
20
26
  end
21
27
 
22
28
 
23
- # idx can be a number or a string
29
+ # Select the desidered sheet.
30
+ #
31
+ # @param idx [Fixnum, String] The index (0-based) or the name of the sheet to select
32
+ # @param [Hash] options Options to define the behaviour on reading and validation the sheet. These options are applied only to the current sheet, but can be overwritten by similar options when calling +read+ or +validate+ method
33
+ # @option options [Fixnum] :skip (1) Number of rows to skip
34
+ # @option options [Fixnum] :header_row (0) The header row index (0-based)
35
+ # @option options [Fixnum] :max_errors (0) Max number of error until stop validation
36
+ # @option options [Fixnum] :row_limit (0) Max number of row to read
37
+ # @option options [Object] :force_nil (nil) Force nils found to this value
24
38
  def sheet(idx, options={})
25
- set_options(options)
39
+ check_sheet_exists(idx)
26
40
  @ss.sheet(idx)
27
- check_sheet_exists
41
+ set_sheet_options(idx, options)
28
42
  end
29
43
 
44
+ # Get the sheet names list
45
+ #
46
+ # @return [Array<String>] An array with sheet names.
30
47
  def sheets
31
48
  @ss.sheets
32
49
  end
33
50
 
51
+ # Get the number of sheets
52
+ #
53
+ # @return [Fixnum] Number of sheets.
54
+ def size
55
+ @ss.sheets.size
56
+ end
34
57
 
58
+ # Get the options of current sheet
59
+ #
60
+ # @return [Fixnum] Number of sheets.
61
+ def options
62
+ @s_opts[index]
63
+ end
64
+
65
+
66
+ # Get the header row of the currently selected sheet
67
+ #
68
+ # @return [Array<Object>] An array cell content objects (String, Float, ...)
35
69
  def get_header
36
- @ss.row(@header_row+1) # because roo in 1-based
70
+ @ss.row(@s_opts[index][:header_row]+1) # because roo in 1-based
37
71
  end
38
72
 
39
- # Get the currently selected sheet's name
73
+ # Get the name of current (default) sheet
74
+ #
75
+ # @return [String] The sheet name
40
76
  def name
41
77
  @ss.default_sheet
42
78
  end
43
79
 
80
+ # Get the index of current (default) sheet
81
+ #
82
+ # @return [Fixnum] The sheet index
83
+ def index
84
+ @ss.sheets.index(@ss.default_sheet)
85
+ end
86
+
87
+ # Get the total number of rows (of the currently selected sheet)
88
+ #
89
+ # @return [Fixnum] The number of rows
44
90
  def total_rows
45
91
  @ss.parse.size
46
92
  end
47
93
 
48
- def rows_wo_header
49
- @ss.parse.size - @skip
94
+ # Get the number of all rows minus the skipped ones (of the currently selected sheet)
95
+ #
96
+ # @return [Fixnum] The number of rows
97
+ def rows_wo_skipped
98
+ @ss.parse.size - @s_opts[index][:skip]
50
99
  end
51
- alias :rows :rows_wo_header
52
-
53
- # Valid options:
54
- # :max_errors : The validation will be stopped if the number of errors exceed max_errors (default: 0 or don't stop)
55
- # :limit : Max number of rows to validate (default: 0 or validate all rows)
100
+ alias :rows :rows_wo_skipped
56
101
 
57
- #
102
+ # Validate the current sheet.
103
+ #
104
+ # @param [Hash] options Validation options for the current sheet.
105
+ # @option options [Fixnum] :skip (1) Number of rows to skip
106
+ # @option options [Fixnum] :header_row (0) The header row index (0-based)
107
+ # @option options [Fixnum] :max_errors (0) Max number of error until stop validation
108
+ # @option options [Fixnum] :row_limit (0) Max number of row to read
109
+ # @option options [Object] :force_nil (nil) Force nils found to this value
110
+ # @yield Column settings and validation rules
111
+ # @return [ValidationErrors] Validation errors
58
112
  def validate(options={}, &block)
59
- skip = options[:skip] || @skip
60
- header_row = options[:header_row] || @header_row
61
- max_errors = options[:max_errors] || @max_errors
62
- row_limit = options[:row_limit] || @row_limit
113
+ # set_current_sheet_options(options)
114
+ skip = options[:skip] || @s_opts[index][:skip]
115
+ header_row = options[:header_row] || @s_opts[index][:header_row]
116
+ max_errors = options[:max_errors] || @s_opts[index][:max_errors]
117
+ row_limit = options[:row_limit] || @s_opts[index][:row_limit]
118
+ force_nil = options[:force_nil] || @s_opts[index][:force_nil]
63
119
 
64
120
  validation_errors = ValidationErrors.new
65
121
 
66
122
  my_class = options[:my_custom_row_class] || build_my_class(block)
67
123
 
68
- line = skip # 0-based, from the top
69
- @ss.parse[skip..-1].each do |row| # row is an array of elements
124
+ line = @s_opts[index][:skip] # 0-based, from the top
125
+ @ss.parse[@s_opts[index][:skip]..-1].each do |row| # row is an array of elements
70
126
  validation_errors.add(line, my_class.new(row))
71
127
  break if max_errors>0 && validation_errors.size >= max_errors
72
- break if row_limit && row_limit>0 && line>=(row_limit+skip-1)
128
+ break if row_limit && row_limit>0 && line>=(row_limit+@s_opts[index][:skip]-1)
73
129
  line +=1
74
130
  end
75
131
  validation_errors
76
132
  end
77
133
 
78
134
 
79
- # Columns must be an hash: labe for values and the column index like {:price => 5}
135
+ # Validate and, if successful, read the current sheet.
136
+ #
137
+ # @param [Hash] options Reading and validation options for the current sheet.
138
+ # @option options [Fixnum] :skip (1) Number of rows to skip
139
+ # @option options [Fixnum] :header_row (0) The header row index (0-based)
140
+ # @option options [Fixnum] :max_errors (0) Max number of error until stop validation
141
+ # @option options [Fixnum] :row_limit (0) Max number of row to read
142
+ # @option options [Object] :force_nil (nil) Force nils found to this value
143
+ # @yield Column settings and validation rules
144
+ # @return [ReadResult] The result
80
145
  def read(options={}, &block)
81
- skip = options[:skip] || @skip
82
- header_row = options[:header_row] || @header_row
83
- max_errors = options[:max_errors] || @max_errors
84
- row_limit = options[:row_limit] || @row_limit
85
- force_nil = options[:force_nil]
146
+ skip = options[:skip] || @s_opts[index][:skip]
147
+ header_row = options[:header_row] || @s_opts[index][:header_row]
148
+ max_errors = options[:max_errors] || @s_opts[index][:max_errors]
149
+ row_limit = options[:row_limit] || @s_opts[index][:row_limit]
150
+ force_nil = options[:force_nil] || @s_opts[index][:force_nil]
86
151
 
87
152
  my_class = build_my_class(block)
88
153
  options[:my_custom_row_class] = my_class
@@ -107,13 +172,21 @@ module Goodsheet
107
172
  Object.const_set get_custom_row_class_name, Row.inherit(block)
108
173
  end
109
174
 
110
- def check_sheet_exists
111
- begin
112
- @ss.cell(1,1)
113
- rescue ArgumentError => e
114
- raise Goodsheet::SheetNotFoundError
115
- rescue RangeError => e
116
- raise Goodsheet::SheetNotFoundError
175
+ def select_sheet_options(idx)
176
+ if idx.is_a? Integer
177
+ @s_opts[idx]
178
+ elsif idx.is_a? String
179
+ @s_opts[@ss.sheets.index(idx)]
180
+ end
181
+ end
182
+
183
+ def check_sheet_exists(idx)
184
+ if idx.is_a? Integer
185
+ raise Goodsheet::SheetNotFoundError if idx < 0 || idx > (size-1)
186
+ elsif idx.is_a? String
187
+ raise Goodsheet::SheetNotFoundError if !@ss.sheets.include?(idx)
188
+ else
189
+ raise ArgumentError, "idx must be an Integer or a String"
117
190
  end
118
191
  end
119
192
 
@@ -121,12 +194,19 @@ module Goodsheet
121
194
  "CustRow_#{(Time.now.to_f*(10**10)).to_i}"
122
195
  end
123
196
 
124
- def set_options(options)
125
- @time_zone = options.delete(:zone) || "Rome"
126
- @skip = options.delete(:skip) || 1
127
- @header_row = options.delete(:header_row) || 0
128
- @max_errors = options.delete(:max_errors) || 0
129
- @row_limit = options.delete(:row_limit) || 0
197
+ def set_current_sheet_options(options)
198
+ set_sheet_options(index, options)
199
+ end
200
+
201
+ def set_sheet_options(idx, options)
202
+ i = idx.is_a?(Integer) ? idx : @ss.sheets.index(idx)
203
+ @s_opts[i] = {
204
+ :skip => options[:skip] || @s_opts[i][:skip] || 1,
205
+ :header_row => options[:header_row] || @s_opts[i][:header_row] || 0,
206
+ :max_errors => options[:max_errors] || @s_opts[i][:max_errors] || 0,
207
+ :row_limit => options[:row_limit] || @s_opts[i][:row_limit] || 0,
208
+ :force_nil => options[:force_nil] || @s_opts[i][:force_nil] || nil
209
+ }
130
210
  end
131
211
  end
132
212
  end
@@ -1,3 +1,3 @@
1
1
  module Goodsheet
2
- VERSION = "0.2.1.1"
2
+ VERSION = "0.2.2"
3
3
  end
Binary file
@@ -104,6 +104,27 @@ class TestSpreadsheet_01 < Test::Unit::TestCase
104
104
  assert_equal(3, validation_errors.size)
105
105
  end
106
106
 
107
+ def test_size
108
+ assert_equal(4, @ss.size)
109
+ end
110
+
111
+ def test_options_precedence
112
+ assert_equal({:skip=>1, :header_row=>0, :max_errors=>0, :row_limit=>0, :force_nil=>nil}, @ss.options)
113
+
114
+ # select "Sheet4" and change option
115
+ @ss.sheet(3, :force_nil => 0.0)
116
+ assert_equal({:skip=>1, :header_row=>0, :max_errors=>0, :row_limit=>0, :force_nil=>0.0}, @ss.options)
117
+
118
+ # reselect "Sheet1" and change option
119
+ @ss.sheet(0)
120
+ assert_equal({:skip=>1, :header_row=>0, :max_errors=>0, :row_limit=>0, :force_nil=>nil}, @ss.options)
121
+
122
+ # now validate with new options, but the sheet option must be unchanged
123
+ result = @ss.validate(:force_nil => 0.0, :skip => 6) do
124
+ # none
125
+ end
126
+ assert_equal({:skip=>1, :header_row=>0, :max_errors=>0, :row_limit=>0, :force_nil=>nil}, @ss.options)
127
+ end
107
128
 
108
129
  def test_read_sheet4
109
130
  @ss.sheet(3)
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.2.1.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Iwan Buetti