goodsheet 0.3.1 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba007929170fe7d6e80af781d6a20132018238f0
4
- data.tar.gz: 97ae8e3fd6e1c83656d138a532cfa8177486da57
3
+ metadata.gz: aef27ae57a44343de4b43b8118c3a4dba7940021
4
+ data.tar.gz: ccd478aad49271a61e1e4278cbfd95ea9a226b17
5
5
  SHA512:
6
- metadata.gz: d10c109430d18557c4fbd823755290ab1fc6361f2e9305de87e10896aa9bda9c5322e954969ec0991243e7921a45dc46d44f34485811ccc87506888921f006fd
7
- data.tar.gz: 9ec851827a5c6a67e83dfadeb5036626b4f29aef8e5953a841860f5216275a453e690a81196d37e3dc10fe6eeb26e49a993e71bd43fe02aa0387c67e4a545e47
6
+ metadata.gz: cf0b9d857e4eb5a66cbf72a6c205e1ab4f517fd93dbd40e42382e1b1f34d0b0e74813bdf8bb5e3e48c554933297ccc31160d39e83e2d4b311825ddf73c32d897
7
+ data.tar.gz: f866ccb2f33ffa9618f12c597a1ab4c9d88282eaf6ffd0485d0e262f78cc506c424a58cc97f2de569e385aa88e2f517b3ee85fd443a955c35e9fe4c808bd7e6e
data/lib/goodsheet.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "goodsheet/version"
2
+ require "goodsheet/aux"
2
3
 
3
4
  module Goodsheet
4
5
  autoload :Row, 'goodsheet/row'
@@ -1,11 +1,30 @@
1
1
  module Goodsheet
2
-
3
2
  class ReadResult
4
- attr_reader :vv, :errors
3
+ attr_reader :errors
5
4
 
6
- def initialize(errors=ValidationErrors.new)
7
- @errors = errors
8
- @vv = {}
5
+ def initialize(row_attributes, max_errors, collector=:a_arr)
6
+ @row_attributes = row_attributes
7
+ case collector
8
+ when :hash
9
+ @hash = {}
10
+ when :a_arr
11
+ @a_arr = []
12
+ when :h_arr
13
+ @h_arr = []
14
+ end
15
+ @errors = ValidationErrors.new(max_errors)
16
+ end
17
+
18
+
19
+ def add(row_number, row)
20
+ if defined? @hash
21
+ row.to_hash.each_pair do |k, v|
22
+ (@hash[k]||=[]) << v # @hash = {:a => ["dog, "cat", ...], :b => [3, 7, ...]}
23
+ end
24
+ end
25
+ @a_arr << row.to_a if defined? @a_arr # @a_arr = [["dog", 3], ["cat", 7], ...]
26
+ @h_arr << row.to_hash if defined? @h_arr # @h_arr = [{:a => "dog", :b => 3}, {:a => "cat", :b => 7}, ...]
27
+ @errors.add(row_number, row)
9
28
  end
10
29
 
11
30
  def valid?
@@ -16,33 +35,81 @@ module Goodsheet
16
35
  !valid?
17
36
  end
18
37
 
19
- def add(attribute, row)
20
- attribute = attribute.to_sym
21
- (@vv[attribute] ||= []) << row.send(attribute)
22
- end
23
-
24
38
  def values(format=:columns)
25
- values_size = 0
26
- values_size = @vv.values.first.size if @vv.values.first
39
+ if defined? @hash
40
+ return [] if @hash.empty?
41
+ values_for_hash(format)
42
+
43
+ elsif defined? @h_arr
44
+ return [] if @h_arr.empty?
45
+ values_for_h_arr(format)
27
46
 
47
+ elsif defined? @a_arr # @a_arr = [["dog", 3], ["cat", 7], ...]
48
+ return [] if @a_arr.empty?
49
+ values_for_a_arr(format)
50
+ end
51
+ end
52
+
53
+ def values_for_hash(format)
28
54
  case format
29
55
  when :columns
30
- @vv
56
+ @hash
31
57
 
32
58
  when :rows_array
33
- Array.new(values_size) do |i1|
34
- Array.new(@vv.size) do |i2|
35
- @vv[@vv.keys[i2]][i1]
36
- end
59
+ a1 = []
60
+ @hash.values.first.size.times do |i|
61
+ a1 << @row_attributes.collect{|a| @hash[a][i]}
62
+ end
63
+ a1
64
+
65
+ when :rows_hash
66
+ a1 = []
67
+ @hash.values.first.size.times do |i|
68
+ a2 = {}
69
+ @row_attributes.each{|a| a2[a] = @hash[a][i]}
70
+ a1 << a2
71
+ end
72
+ a1
73
+ end
74
+ end
75
+
76
+ def values_for_a_arr(format)
77
+ case format
78
+ when :columns
79
+ h = {}
80
+ @row_attributes.each_with_index do |attrib, i|
81
+ h[attrib] = @a_arr.map{|e| e[i]}
37
82
  end
83
+ h
84
+
85
+ when :rows_array
86
+ @a_arr
38
87
 
39
88
  when :rows_hash
40
- Array.new(values_size) do |i1|
41
- hh = {}
42
- @vv.keys.each{|k| hh[k] = @vv[k][i1] }
43
- hh
89
+ @a_arr.map do |arr|
90
+ Hash[@row_attributes.map.with_index{|attrib,i| [attrib, arr[i]]}]
44
91
  end
45
92
  end
46
93
  end
94
+
95
+ def values_for_h_arr(format)
96
+ case format
97
+ when :columns
98
+ keys = @h_arr.first.keys
99
+ h = {}
100
+ @h_arr.each do |e|
101
+ keys.each do |key|
102
+ (h[key] ||= []) << e[key]
103
+ end
104
+ end
105
+ h
106
+
107
+ when :rows_array
108
+ @h_arr.map(&:values)
109
+
110
+ when :rows_hash
111
+ @h_arr
112
+ end
113
+ end
47
114
  end
48
115
  end
data/lib/goodsheet/row.rb CHANGED
@@ -4,18 +4,21 @@ module Goodsheet
4
4
 
5
5
  class Row
6
6
  include ActiveModel::Validations
7
- include ActiveModel::Conversion
8
- extend ActiveModel::Naming
9
7
 
10
8
  class << self
11
- attr_accessor :keys
9
+ attr_accessor :keys, :defaults
12
10
  end
13
- @keys = {} # idx => key
11
+ # @keys = {} # idx => key: {0=>:name, 1=>:quantity, 2=>:price, 3=>:total, 6=>:password}
12
+ # @defaults = {} # name => default_value
14
13
 
15
14
  def initialize(arr, nil_value=nil)
15
+ # puts "--- arr: #{arr.inspect}"
16
+ if (diff=self.class.keys.size-arr.size)>0
17
+ arr = arr + Array.new(diff, nil)
18
+ end
16
19
  arr.each_with_index do |v, idx|
17
20
  if k = self.class.keys[idx]
18
- send("#{k}=", v || nil_value)
21
+ send "#{k}=", v || self.class.defaults[k] || nil_value
19
22
  end
20
23
  end
21
24
  super()
@@ -23,12 +26,30 @@ module Goodsheet
23
26
 
24
27
  def self.inherit(block)
25
28
  c = Class.new(self) do
26
- @keys = {} # idx => key
29
+ @keys = {} # idx => key: {0=>:name, 1=>:quantity, 2=>:price, 3=>:total, 6=>:password}
30
+ @defaults = {} # name => default_value
27
31
  end
28
32
  c.class_eval(&block)
29
33
  c
30
34
  end
31
35
 
36
+ # using indexes: defaults 1 => 0.0, 2 => ""
37
+ # using names: defaults :qty => 0.0, :name => ""
38
+
39
+ def self.column_defaults(*attr)
40
+ raise ArgumentError, 'You have to pass at least one attribute' if attr.empty?
41
+ if attr[0].is_a? Array
42
+ @defaults = Hash[attr[0].map.with_index{|v, i| [self.keys[i], v]}]
43
+
44
+ elsif attr[0].is_a? Hash
45
+ @defaults = Hash[attr[0].to_a.collect{|a| [(a[0].is_a?(Fixnum)) ? (self.keys[a[0]]) : a[0], a[1]]}]
46
+
47
+ else
48
+ @defaults = Hash[attr.map.with_index{|v, i| [self.keys[i], v]}]
49
+ end
50
+ end
51
+
52
+
32
53
  # Define the position (or index) and the name of columns.
33
54
  # You have four ways to define them:
34
55
  # using an hash index-to-name (like { 0 => :year, 2 => :day })
@@ -42,22 +63,25 @@ module Goodsheet
42
63
  raise ArgumentError, 'You have to pass at least one attribute' if attr.empty?
43
64
  if attr[0].is_a? Array
44
65
  attr[0].each_with_index do |name, idx|
45
- if name
46
- self.keys[idx] = name
47
- attr_accessor name
48
- end
66
+ self.set_key_pair(idx, name) if name
67
+ # if name
68
+ # self.keys[idx] = name
69
+ # attr_accessor name
70
+ # end
49
71
  end
50
72
 
51
73
  elsif attr[0].is_a? Hash
52
74
  if attr[0].first[0].is_a? Integer
53
75
  attr[0].each do |idx, name|
54
- self.keys[idx] = name
55
- attr_accessor name
76
+ self.set_key_pair(idx, name)
77
+ # self.keys[idx] = name
78
+ # attr_accessor name
56
79
  end
57
80
  else
58
81
  attr[0].each do |name, idx|
59
- self.keys[idx] = name
60
- attr_accessor name
82
+ self.set_key_pair(idx, name)
83
+ # self.keys[idx] = name
84
+ # attr_accessor name
61
85
  end
62
86
  end
63
87
 
@@ -65,8 +89,9 @@ module Goodsheet
65
89
  attr.each_with_index do |name, idx|
66
90
  if name
67
91
  name = name.to_s.gsub(" ", "_").to_sym unless name.is_a? Symbol
68
- self.keys[idx] = name
69
- attr_accessor name
92
+ self.set_key_pair(idx, name)
93
+ # self.keys[idx] = name
94
+ # attr_accessor name
70
95
  end
71
96
  end
72
97
  end
@@ -74,13 +99,47 @@ module Goodsheet
74
99
  end
75
100
 
76
101
 
77
- def persisted?
78
- false
102
+ def self.set_key_pair(idx, name)
103
+ self.keys[idx] = name
104
+ attr_accessor name
79
105
  end
80
106
 
107
+
108
+ # def persisted?
109
+ # false
110
+ # end
111
+
81
112
  # Get the list of attributes (the columns to import)
82
- def self.row_attributes
113
+ def Row.attributes
83
114
  @keys.values
84
115
  end
116
+
117
+ def attributes
118
+ self.class.attributes
119
+ end
120
+
121
+ def Row.extend_with(block)
122
+ class_name = "CustRow_#{(Time.now.to_f*(10**10)).to_i}"
123
+ Object.const_set class_name, Row.inherit(block)
124
+ end
125
+
126
+ def to_hash
127
+ Hash[self.class.attributes.map{|a| [a, self.send(a)]}]
128
+ end
129
+
130
+ def to_a
131
+ self.class.attributes.map{|a| self.send(a)}
132
+ end
85
133
  end
86
134
  end
135
+
136
+ # class Row01 < Goodsheet::Row
137
+ # column_names :filename => 0, :size => 1
138
+ # validates :size, :numericality => true
139
+ # end
140
+
141
+ # r = Row01.new(["pippo", "e"])
142
+ # p r.valid?
143
+ # puts r.class.attributes.inspect
144
+ # puts r.to_hash.inspect
145
+ # puts r.to_a.inspect
@@ -110,23 +110,15 @@ module Goodsheet
110
110
  # @yield Column settings and validation rules
111
111
  # @return [ValidationErrors] Validation errors
112
112
  def validate(options={}, &block)
113
- skip = options[:skip] || @s_opts[index][:skip]
114
- header_row = options[:header_row] || @s_opts[index][:header_row]
115
- max_errors = options[:max_errors] || @s_opts[index][:max_errors]
116
- row_limit = options[:row_limit] || @s_opts[index][:row_limit]
117
- force_nil = options[:force_nil] || @s_opts[index][:force_nil]
118
- validation_errors = ValidationErrors.new
119
-
120
- my_class = options[:my_custom_row_class] || build_my_class(block)
121
-
122
- line = @s_opts[index][:skip] # 0-based, from the top
123
- @ss.parse[@s_opts[index][:skip]..-1].each do |row| # row is an array of elements
124
- validation_errors.add(line, my_class.new(row, force_nil))
125
- break if max_errors>0 && validation_errors.size >= max_errors
126
- break if row_limit && row_limit>0 && line>=(row_limit+@s_opts[index][:skip]-1)
127
- line +=1
113
+ set_variables(options)
114
+ errors = ValidationErrors.new(@max_errors)
115
+ row_class = Row.extend_with(block)
116
+
117
+ last_row = @row_limit.zero? ? @ss.last_row : min(@ss.last_row, @row_limit+@skip)
118
+ (@skip+1).upto(last_row) do |r|
119
+ break unless errors.add(r, row_class.new(@ss.row(r), @force_nil))
128
120
  end
129
- validation_errors
121
+ errors
130
122
  end
131
123
 
132
124
 
@@ -141,24 +133,13 @@ module Goodsheet
141
133
  # @yield Column settings and validation rules
142
134
  # @return [ReadResult] The result
143
135
  def read(options={}, &block)
144
- skip = options[:skip] || @s_opts[index][:skip]
145
- header_row = options[:header_row] || @s_opts[index][:header_row]
146
- max_errors = options[:max_errors] || @s_opts[index][:max_errors]
147
- row_limit = options[:row_limit] || @s_opts[index][:row_limit]
148
- force_nil = options[:force_nil] || @s_opts[index][:force_nil]
149
-
150
- my_class = build_my_class(block)
151
- options[:my_custom_row_class] = my_class
152
- read_result = ReadResult.new(validate(options){ block })
153
- return read_result if read_result.invalid?
154
-
155
- line = skip # 0-based, from the top
156
- @ss.parse[skip..-1].each do |row| # row is an array of elements
157
- my_class.row_attributes.each do |attribute|
158
- read_result.add(attribute, my_class.new(row, force_nil))
159
- end
160
- break if row_limit && row_limit>0 && line>=(row_limit + skip - 1)
161
- line +=1
136
+ set_variables(options)
137
+ row_class = Row.extend_with(block)
138
+ read_result = ReadResult.new(row_class.attributes, @max_errors, options[:collector]||:a_arr)
139
+
140
+ last_row = @row_limit.zero? ? @ss.last_row : min(@ss.last_row, @row_limit+@skip)
141
+ (@skip+1).upto(last_row) do |r|
142
+ break unless read_result.add(r, row_class.new(@ss.row(r), @force_nil))
162
143
  end
163
144
  read_result
164
145
  end
@@ -166,9 +147,14 @@ module Goodsheet
166
147
 
167
148
  private
168
149
 
169
- def build_my_class(block)
170
- n = get_custom_row_class_name
171
- Object.const_set n, Row.inherit(block)
150
+
151
+
152
+ def set_variables(options)
153
+ @skip = options[:skip] || @s_opts[index][:skip]
154
+ @header_row = options[:header_row] || @s_opts[index][:header_row]
155
+ @max_errors = options[:max_errors] || @s_opts[index][:max_errors]
156
+ @row_limit = options[:row_limit] || @s_opts[index][:row_limit] || 0
157
+ @force_nil = options[:force_nil] || @s_opts[index][:force_nil]
172
158
  end
173
159
 
174
160
  def select_sheet_options(idx)
@@ -189,9 +175,6 @@ module Goodsheet
189
175
  end
190
176
  end
191
177
 
192
- def get_custom_row_class_name
193
- "CustRow_#{(Time.now.to_f*(10**10)).to_i}"
194
- end
195
178
 
196
179
  def set_sheet_options(idx, options)
197
180
  i = idx.is_a?(Integer) ? idx : @ss.sheets.index(idx)
@@ -203,6 +186,10 @@ module Goodsheet
203
186
  :force_nil => options[:force_nil] || @s_opts[i][:force_nil] || nil
204
187
  }
205
188
  end
189
+
190
+ def min(a,b)
191
+ a<b ? a : b
192
+ end
206
193
  end
207
194
  end
208
195
 
@@ -1,14 +1,14 @@
1
1
  module Goodsheet
2
2
 
3
3
  class ValidationError
4
+
4
5
  def initialize(line, val_err)
5
6
  @line = line
6
- @val_err = val_err
7
+ @val_err = val_err.full_messages.join(', ')
7
8
  end
8
9
 
9
10
  def to_s
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
+ "Row #{@line} is not valid: #{@val_err}"
12
12
  end
13
13
  end
14
14
  end
@@ -1,40 +1,18 @@
1
1
  module Goodsheet
2
2
 
3
- class ValidationErrors
4
- attr_reader :array
3
+ class ValidationErrors < Array
5
4
 
6
- def initialize
7
- @array = []
5
+ def initialize(limit=0)
6
+ @max_size = (limit==0 || limit.nil?) ? Float::INFINITY : limit
8
7
  end
9
8
 
9
+ # Add a potential error (will be added only if the row is not valid)
10
+ #
11
+ # @param line_number [Fixnum] Line number (0-based).
12
+ # @return [boolean] Return false if the limit has been reached, true otherwise.
10
13
  def add(line_number, row)
11
- @array << ValidationError.new(line_number+1, row.errors) if row.invalid?
12
- end
13
-
14
- def empty?
15
- @array.empty?
16
- end
17
-
18
- def size
19
- @array.size
20
- end
21
-
22
- def to_s
23
- @array.to_s
24
- end
25
-
26
- def [](i)
27
- @array[i]
28
- end
29
-
30
- def to_a
31
- @array
32
- end
33
-
34
- def each(&block)
35
- @array.each do |i|
36
- yield(i)
37
- end
14
+ self << ValidationError.new(line_number+1, row.errors) if row.invalid?
15
+ self.size < @max_size
38
16
  end
39
17
 
40
18
  def valid?
@@ -1,3 +1,3 @@
1
1
  module Goodsheet
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.0"
3
3
  end
data/notes.txt CHANGED
@@ -66,3 +66,16 @@ values = @ss.read(:force_nil => 0.0, :skip => 6) do |row|
66
66
  MyRow.new(:year => row[0])
67
67
  end
68
68
 
69
+
70
+ Test a single test file:
71
+ rake test TEST=test/test_defaults.rb
72
+ L3lIrj
73
+
74
+
75
+ Tests:
76
+ rake test TEST=test/test_defaults.rb # OOOKK! committa
77
+ rake test TEST=test/test_enel.rb # OOOKK!
78
+ rake test TEST=test/test_example_2.rb # OOOKK!
79
+ rake test TEST=test/test_roo.rb # OOOKK!
80
+
81
+
Binary file
data/test/test_row.rb CHANGED
@@ -3,6 +3,7 @@ require 'goodsheet'
3
3
 
4
4
  class TestRow < Test::Unit::TestCase
5
5
 
6
+ # Test various style of column_names option
6
7
  def test_column_names
7
8
  assert_raise ArgumentError do
8
9
  Goodsheet::Row.column_names
@@ -11,17 +12,18 @@ class TestRow < Test::Unit::TestCase
11
12
  Goodsheet::Row.column_names(6)
12
13
  end
13
14
 
15
+ result = {0 => :a, 2 => :b, 3 => :c}
14
16
  Goodsheet::Row.column_names(:a, nil, :b, :c)
15
- assert_equal(Goodsheet::Row.keys, {0 => :a, 2 => :b, 3 => :c})
17
+ assert_equal(Goodsheet::Row.keys, result)
16
18
 
17
19
  Goodsheet::Row.column_names([:a, nil, :b, :c])
18
- assert_equal(Goodsheet::Row.keys, {0 => :a, 2 => :b, 3 => :c})
20
+ assert_equal(Goodsheet::Row.keys, result)
19
21
 
20
22
  Goodsheet::Row.column_names(:a => 0, :b => 2, :c => 3)
21
- assert_equal(Goodsheet::Row.keys, {0 => :a, 2 => :b, 3 => :c})
23
+ assert_equal(Goodsheet::Row.keys, result)
22
24
 
23
25
  Goodsheet::Row.column_names(0 => :a, 2 => :b, 3 => :c)
24
- assert_equal(Goodsheet::Row.keys, {0 => :a, 2 => :b, 3 => :c})
26
+ assert_equal(Goodsheet::Row.keys, result)
25
27
  end
26
28
 
27
29
 
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.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Iwan Buetti