roo-google 1.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bca53fffea8b64bf864b796d233da2c2c260130b
4
+ data.tar.gz: 66bd40f914969abf1a445fa49f28909a044df058
5
+ SHA512:
6
+ metadata.gz: dc20c65584614eb7615be9e2904fa7c5b16935f21cdc8d7f8c479e3f6c0bfb80004e85123d0e7fd3db40e0894622f742adf959f69af25c40f46ade90bb15535f
7
+ data.tar.gz: 9f997dde0080302a1b846add27a1d183b45dba007ea5f9203de04bebf59aa72564be5839addb593841f9b8661a7a34b635a99c342f31044fffd981e012ffc5b4
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ .idea
@@ -0,0 +1,17 @@
1
+ AllCops:
2
+ Include:
3
+ - '**/Rakefile'
4
+ Exclude:
5
+ - 'spec/**/*'
6
+ Metrics/LineLength:
7
+ Max: 99
8
+ Style/FileName:
9
+ Enabled: false
10
+ Style/ModuleFunction:
11
+ Enabled: false
12
+ Style/Encoding:
13
+ Enabled: false
14
+ Documentation:
15
+ Enabled: false
16
+ Metrics/MethodLength:
17
+ Max: 15
@@ -0,0 +1,4 @@
1
+ SimpleCov.start do
2
+ add_filter 'spec'
3
+ add_filter 'test'
4
+ end
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1
5
+ - 2.2
6
+ - ruby-head
7
+ - jruby-19mode # JRuby in 1.9 mode
8
+ - rbx-2
9
+ matrix:
10
+ allow_failures:
11
+ - rvm: ruby-head
12
+ bundler_args: --without local_development
data/Gemfile ADDED
@@ -0,0 +1,32 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in roo-xls.gemspec
4
+ gemspec
5
+
6
+ if ENV['TRAVIS']
7
+ gem 'roo', '>= 2.0.0beta1', github: 'roo-rb/roo'
8
+ else
9
+ gem 'roo', '>= 2.0.0beta1', path: ::File.expand_path('../../roo', __FILE__)
10
+ end
11
+
12
+ group :test do
13
+ # additional testing libs
14
+ gem 'webmock'
15
+ gem 'shoulda'
16
+ gem 'rspec', '>= 3.0.0'
17
+ gem 'vcr'
18
+ gem 'simplecov', '>= 0.9.0', :require => false
19
+ gem 'coveralls', :require => false
20
+ end
21
+
22
+ group :local_development do
23
+ gem 'terminal-notifier-guard', require: false if RUBY_PLATFORM.downcase.include?('darwin')
24
+ gem 'guard-rspec', '>= 4.3.1' ,require: false
25
+ gem 'guard-bundler', require: false
26
+ gem 'guard-preek', require: false
27
+ gem 'guard-rubocop', require: false
28
+ gem 'guard-reek', github: 'pericles/guard-reek', require: false
29
+ gem 'pry'
30
+ gem 'appraisal'
31
+ gem 'transpec'
32
+ end
@@ -0,0 +1,20 @@
1
+ guard 'rspec', cmd: 'rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
6
+
7
+ guard 'bundler' do
8
+ watch('Gemfile')
9
+ watch('roo-google.gemspec')
10
+ end
11
+
12
+ guard :rubocop do
13
+ watch(%r{.+\.rb$})
14
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
15
+ end
16
+
17
+ guard :preek, run_all_dir: 'lib' do
18
+ watch(/^lib\/(.*).rb/)
19
+ end
20
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2008-2014 Thomas Preymesser, Ben Woosley
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,33 @@
1
+ # Roo::Google [![BuildStatus](https://travis-ci.org/roo-rb/roo-google.svg)](https://travis-ci.org/roo-rb/roo-google)[![Code Climate](https://codeclimate.com/github/roo-rb/roo-google/badges/gpa.svg)](https://codeclimate.com/github/roo-rb/roo-google)[![Coverage Status](https://coveralls.io/repos/roo-rb/roo-google/badge.png)](https://coveralls.io/r/roo-rb/roo-google)
2
+
3
+ ### Google Spreadsheet
4
+
5
+ Using Roo to access Google spreadsheets requires you install the 'google-drive-ruby' gem separately.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'roo-google'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install roo-google
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Contributing
28
+
29
+ 1. Fork it ( https://github.com/roo-rb/roo-google/fork )
30
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
31
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
32
+ 4. Push to the branch (`git push origin my-new-feature`)
33
+ 5. Create a new Pull Request
@@ -0,0 +1,23 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ # require 'rake/testtask'
4
+ require 'rspec/core/rake_task'
5
+ require 'coveralls/rake/task'
6
+
7
+ # Test unit
8
+ # Rake::TestTask.new do |t|
9
+ # t.libs << 'test'
10
+ # t.test_files = FileList['test/test*.rb']
11
+ # t.verbose = true
12
+ # end
13
+
14
+ # RSpec
15
+ RSpec::Core::RakeTask.new(:spec)
16
+
17
+ # Coveralls
18
+ Coveralls::RakeTask.new
19
+
20
+ default_task = [:spec]
21
+ default_task << 'coveralls:push' if ENV['TRAVIS']
22
+
23
+ task default: default_task
@@ -0,0 +1,11 @@
1
+ ---
2
+ NestedIterators:
3
+ max_allowed_nesting: 2
4
+ UtilityFunction:
5
+ enabled: false
6
+ IrresponsibleModule:
7
+ enabled: false
8
+ DuplicateMethodCall:
9
+ max_calls: 5
10
+ FeatureEnvy:
11
+ enabled: false
@@ -0,0 +1,6 @@
1
+ require 'roo'
2
+ require 'roo/google'
3
+
4
+ module Roo
5
+ CLASS_FOR_EXTENSION.merge! google: ::Roo::Google
6
+ end
@@ -0,0 +1,244 @@
1
+ require 'roo/base'
2
+ require 'google_drive'
3
+
4
+ class Roo::Google < Roo::Base
5
+ attr_accessor :date_format, :datetime_format, :time_format
6
+ # returns an array of sheet names in the spreadsheet
7
+ attr_reader :sheets
8
+ DATE_FORMAT = '%d/%m/%Y'.freeze
9
+ DATETIME_FORMAT = '%d/%m/%Y %H:%M:%S'.freeze
10
+ TIME_FORMAT = '%H:%M:%S'.freeze
11
+
12
+ # Creates a new Google Drive object.
13
+ def initialize(spreadsheet_key, options = {})
14
+ @filename = spreadsheet_key
15
+ @access_token = options[:access_token] || ENV['GOOGLE_TOKEN']
16
+ super
17
+ @cell = Hash.new { |h, k| h[k] = Hash.new }
18
+ @cell_type = Hash.new { |h, k| h[k] = Hash.new }
19
+ @formula = {}
20
+ %w(date time datetime).each do |key|
21
+ __send__("#{key}_format=", self.class.const_get("#{key.upcase}_FORMAT"))
22
+ end
23
+ end
24
+
25
+ def worksheets
26
+ @worksheets ||= session.spreadsheet_by_key(@filename).worksheets
27
+ end
28
+
29
+ def sheets
30
+ @sheets ||= worksheets.map(&:title)
31
+ end
32
+
33
+ %w(date time datetime).each do |key|
34
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
35
+ def #{key}?(string)
36
+ ::DateTime.strptime(string, @#{key}_format || #{key.upcase}_FORMAT)
37
+ true
38
+ rescue
39
+ false
40
+ end
41
+ EOS
42
+ end
43
+
44
+ def numeric?(string)
45
+ string =~ /\A[0-9]+[\.]*[0-9]*\z/
46
+ end
47
+
48
+ def timestring_to_seconds(value)
49
+ hms = value.split(':')
50
+ hms[0].to_i * 3600 + hms[1].to_i * 60 + hms[2].to_i
51
+ end
52
+
53
+ # Returns the content of a spreadsheet-cell.
54
+ # (1,1) is the upper left corner.
55
+ # (1,1), (1,'A'), ('A',1), ('a',1) all refers to the
56
+ # cell at the first line and first row.
57
+ def cell(row, col, sheet = default_sheet)
58
+ validate_sheet!(sheet) # TODO: 2007-12-16
59
+ read_cells(sheet)
60
+ row, col = normalize(row, col)
61
+ value = @cell[sheet]["#{row},#{col}"]
62
+ type = celltype(row, col, sheet)
63
+ return value unless [:date, :datetime].include?(type)
64
+ klass, format = if type == :date
65
+ [::Date, @date_format]
66
+ else
67
+ [::DateTime, @datetime_format]
68
+ end
69
+ begin
70
+ return klass.strptime(value, format)
71
+ rescue ArgumentError
72
+ raise "Invalid #{klass} #{sheet}[#{row},#{col}] #{value} using format '#{format}'"
73
+ end
74
+ end
75
+
76
+ # returns the type of a cell:
77
+ # * :float
78
+ # * :string
79
+ # * :date
80
+ # * :percentage
81
+ # * :formula
82
+ # * :time
83
+ # * :datetime
84
+ def celltype(row, col, sheet = default_sheet)
85
+ read_cells(sheet)
86
+ row, col = normalize(row, col)
87
+ if @formula.size > 0 && @formula[sheet]["#{row},#{col}"]
88
+ :formula
89
+ else
90
+ @cell_type[sheet]["#{row},#{col}"]
91
+ end
92
+ end
93
+
94
+ # Returns the formula at (row,col).
95
+ # Returns nil if there is no formula.
96
+ # The method #formula? checks if there is a formula.
97
+ def formula(row, col, sheet = default_sheet)
98
+ read_cells(sheet)
99
+ row, col = normalize(row, col)
100
+ @formula[sheet]["#{row},#{col}"] && @formula[sheet]["#{row},#{col}"]
101
+ end
102
+
103
+ alias_method :formula?, :formula
104
+
105
+ # true, if the cell is empty
106
+ def empty?(row, col, sheet = default_sheet)
107
+ value = cell(row, col, sheet)
108
+ return true unless value
109
+ return false if value.class == Date # a date is never empty
110
+ return false if value.class == Float
111
+ return false if celltype(row, col, sheet) == :time
112
+ value.empty?
113
+ end
114
+
115
+ # sets the cell to the content of 'value'
116
+ # a formula can be set in the form of '=SUM(...)'
117
+ def set(row, col, value, sheet = default_sheet)
118
+ validate_sheet!(sheet)
119
+
120
+ sheet_no = sheets.index(sheet) + 1
121
+ row, col = normalize(row, col)
122
+ add_to_cell_roo(row, col, value, sheet_no)
123
+ # re-read the portion of the document that has changed
124
+ if @cells_read[sheet]
125
+ value, value_type = determine_datatype(value.to_s)
126
+
127
+ _set_value(row, col, value, sheet)
128
+ set_type(row, col, value_type, sheet)
129
+ end
130
+ end
131
+
132
+ %w(first_row last_row first_column last_column).each do |key|
133
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
134
+ def #{key}(sheet = default_sheet)
135
+ unless @#{key}[sheet]
136
+ set_first_last_row_column(sheet)
137
+ end
138
+ @#{key}[sheet]
139
+ end
140
+ EOS
141
+ end
142
+
143
+ private
144
+
145
+ def set_first_last_row_column(sheet)
146
+ sheet_no = sheets.index(sheet) + 1
147
+ @first_row[sheet], @last_row[sheet], @first_column[sheet], @last_column[sheet] =
148
+ rows_and_cols_min_max(sheet_no)
149
+ end
150
+
151
+ def _set_value(row, col, value, sheet = default_sheet)
152
+ @cell[sheet]["#{row},#{col}"] = value
153
+ end
154
+
155
+ def set_type(row, col, type, sheet = default_sheet)
156
+ @cell_type[sheet]["#{row},#{col}"] = type
157
+ end
158
+
159
+ # read all cells in a sheet.
160
+ def read_cells(sheet = default_sheet)
161
+ validate_sheet!(sheet)
162
+ return if @cells_read[sheet]
163
+
164
+ ws = worksheets[sheets.index(sheet)]
165
+ for row in 1..ws.num_rows
166
+ for col in 1..ws.num_cols
167
+ read_cell_row(sheet, ws, row, col)
168
+ end
169
+ end
170
+ @cells_read[sheet] = true
171
+ end
172
+
173
+ def read_cell_row(sheet, ws, row, col)
174
+ key = "#{row},#{col}"
175
+ string_value = ws.input_value(row, col) # item['inputvalue'] || item['inputValue']
176
+ numeric_value = ws[row, col] # item['numericvalue'] || item['numericValue']
177
+ (value, value_type) = determine_datatype(string_value, numeric_value)
178
+ @cell[sheet][key] = value unless value == '' || value.nil?
179
+ @cell_type[sheet][key] = value_type
180
+ @formula[sheet] = {} unless @formula[sheet]
181
+ @formula[sheet][key] = string_value if value_type == :formula
182
+ end
183
+
184
+ def determine_datatype(val, numval = nil)
185
+ if val.nil? || val[0, 1] == '='
186
+ ty = :formula
187
+ val = numeric?(numval) ? numval.to_f : numval
188
+ else
189
+ case
190
+ when datetime?(val)
191
+ ty = :datetime
192
+ when date?(val)
193
+ ty = :date
194
+ when numeric?(val)
195
+ ty = :float
196
+ val = val.to_f
197
+ when time?(val)
198
+ ty = :time
199
+ val = timestring_to_seconds(val)
200
+ else
201
+ ty = :string
202
+ end
203
+ end
204
+ [val, ty]
205
+ end
206
+
207
+ def add_to_cell_roo(row, col, value, sheet_no = 1)
208
+ sheet_no -= 1
209
+ worksheets[sheet_no][row, col] = value
210
+ worksheets[sheet_no].save
211
+ end
212
+
213
+ def entry_roo(value, row, col)
214
+ [value, row, col]
215
+ end
216
+
217
+ def rows_and_cols_min_max(sheet_no)
218
+ ws = worksheets[sheet_no - 1]
219
+ rows = []
220
+ cols = []
221
+ for row in 1..ws.num_rows
222
+ for col in 1..ws.num_cols
223
+ if ws[row, col] && !ws[row, col].empty?
224
+ rows << row
225
+ cols << col
226
+ end
227
+ end
228
+ end
229
+ [rows.min, rows.max, cols.min, cols.max]
230
+ end
231
+
232
+ def reinitialize
233
+ @session = nil
234
+ initialize(@filename, access_token: @access_token)
235
+ end
236
+
237
+ def session
238
+ @session ||= if @access_token
239
+ ::GoogleDrive.login_with_oauth(@access_token)
240
+ else
241
+ warn 'set access token'
242
+ end
243
+ end
244
+ end