goodsheet 0.3.1 → 0.4.0

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