roo-google 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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