cloudsheet 0.1.0 → 0.2.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.
- data/README.rdoc +9 -7
- data/VERSION +1 -1
- data/cloudsheet.gemspec +7 -2
- data/lib/cloudsheet.rb +1 -33
- data/lib/cloudsheet/drive.rb +34 -0
- data/lib/cloudsheet/map.rb +29 -0
- data/lib/cloudsheet/mapping.rb +42 -17
- data/lib/cloudsheet/row.rb +8 -11
- data/lib/cloudsheet/sheet.rb +29 -0
- data/spec/drive_spec.rb +18 -10
- data/spec/mapping_spec.rb +62 -0
- data/spec/row_spec.rb +32 -0
- data/spec/spec_helper.rb +3 -1
- metadata +8 -3
data/README.rdoc
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
= Cloudsheet
|
2
2
|
Load Google Drive Spreadsheets sanely.
|
3
3
|
|
4
|
-
Setup
|
4
|
+
Setup the Cloudsheet. sheet is an optional worksheet ID.
|
5
|
+
cloudsheet = Cloudsheet::Drive.new(user: "me@example.com", password: "secret", sheet_key: "SPREADSHEETID")
|
5
6
|
|
6
|
-
|
7
|
-
cloudsheet = Cloudsheet::Drive.new(drive, "SPREADSHEETID")
|
8
|
-
|
9
|
-
Setup an optional mapping of column #s to names and lambdas.
|
7
|
+
Setup an optional mapping of column #s to names and lambdas. The default operation is to no-op.
|
10
8
|
|
11
9
|
m = Cloudsheet::Map.new
|
12
10
|
m[0] = Cloudsheet::Mapping.new(:name)
|
13
|
-
m[1] = Cloudsheet::Mapping.new(:
|
11
|
+
m[1] = Cloudsheet::Mapping.new(:score).type(Float)
|
12
|
+
m[2] = Cloudsheet::Mapping.new(:cash).type("money")
|
13
|
+
m[3] = Cloudsheet::Mapping.new(:start_date).map(->(d) {DateTime.strptime(d, "%m/%d/%Y")})
|
14
|
+
|
15
|
+
The map will try to do the right thing and if nil is passed in, it will skip the lambda and output nil.
|
14
16
|
|
15
17
|
Select a worksheet if not the first one and pass in an optional map
|
16
18
|
|
17
|
-
cloudsheet.
|
19
|
+
cloudsheet.sheet(0).map(m)
|
18
20
|
cloudsheet.each do |row|
|
19
21
|
row["name"] # Column 0
|
20
22
|
row["start_date"] # DateTime object created from column 1
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/cloudsheet.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "cloudsheet"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Kurt Nelson"]
|
12
|
-
s.date = "2012-10-
|
12
|
+
s.date = "2012-10-19"
|
13
13
|
s.description = "Pull google drive spreadsheets with your column names and datatypes"
|
14
14
|
s.email = "kurtisnelson@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -26,9 +26,14 @@ Gem::Specification.new do |s|
|
|
26
26
|
"VERSION",
|
27
27
|
"cloudsheet.gemspec",
|
28
28
|
"lib/cloudsheet.rb",
|
29
|
+
"lib/cloudsheet/drive.rb",
|
30
|
+
"lib/cloudsheet/map.rb",
|
29
31
|
"lib/cloudsheet/mapping.rb",
|
30
32
|
"lib/cloudsheet/row.rb",
|
33
|
+
"lib/cloudsheet/sheet.rb",
|
31
34
|
"spec/drive_spec.rb",
|
35
|
+
"spec/mapping_spec.rb",
|
36
|
+
"spec/row_spec.rb",
|
32
37
|
"spec/spec_helper.rb"
|
33
38
|
]
|
34
39
|
s.homepage = "http://github.com/kurtisnelson/cloudsheet"
|
data/lib/cloudsheet.rb
CHANGED
@@ -1,33 +1 @@
|
|
1
|
-
require_relative "cloudsheet/
|
2
|
-
require_relative "cloudsheet/mapping"
|
3
|
-
|
4
|
-
module Cloudsheet
|
5
|
-
class Drive
|
6
|
-
attr_reader :sheet, :spreadsheet
|
7
|
-
def initialize(drive_session, spreadsheet)
|
8
|
-
@spreadsheet = drive_session.spreadsheet_by_key(spreadsheet)
|
9
|
-
@sheet = @spreadsheet.worksheets[0]
|
10
|
-
end
|
11
|
-
|
12
|
-
def worksheet(num)
|
13
|
-
@sheet = @spreadsheet.worksheets[num]
|
14
|
-
self
|
15
|
-
end
|
16
|
-
|
17
|
-
def map(m)
|
18
|
-
@map = m
|
19
|
-
self
|
20
|
-
end
|
21
|
-
|
22
|
-
def each
|
23
|
-
rower = Cloudsheet::Row.new(@map)
|
24
|
-
rows.each do |r|
|
25
|
-
yield rower.parse(r)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def rows
|
30
|
-
@sheet.rows.drop(1)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
1
|
+
require_relative "cloudsheet/drive"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "cloudsheet/sheet"
|
2
|
+
|
3
|
+
module Cloudsheet
|
4
|
+
class Drive < Sheet
|
5
|
+
def initialize(hash)
|
6
|
+
if hash[:user] and hash[:password]
|
7
|
+
drive = GoogleDrive.login(hash[:user], hash[:password])
|
8
|
+
else
|
9
|
+
raise "Missing Google Credentials"
|
10
|
+
end
|
11
|
+
|
12
|
+
if hash[:sheet_key]
|
13
|
+
@workbook = drive.spreadsheet_by_key(hash[:sheet_key])
|
14
|
+
else
|
15
|
+
raise "Missing sheet key"
|
16
|
+
end
|
17
|
+
|
18
|
+
if hash[:sheet]
|
19
|
+
@raw_sheet = @workbook.worksheets[hash[:sheet]]
|
20
|
+
else
|
21
|
+
@raw_sheet = @workbook.worksheets[0]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def sheet(num)
|
26
|
+
@raw_sheet = @workbook.worksheets[num]
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def rows
|
31
|
+
super.drop(1)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Cloudsheet
|
2
|
+
class Map
|
3
|
+
def initialize
|
4
|
+
@data = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def []=(i,val)
|
8
|
+
@data[i] = val
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](i)
|
12
|
+
if @data[i]
|
13
|
+
@data[i]
|
14
|
+
else
|
15
|
+
Mapping.new(i)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def read(i, d)
|
20
|
+
return nil unless d
|
21
|
+
self[i].read.call d
|
22
|
+
end
|
23
|
+
|
24
|
+
def write(i, d)
|
25
|
+
return "" unless d
|
26
|
+
self[i].write.call d
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/cloudsheet/mapping.rb
CHANGED
@@ -1,26 +1,51 @@
|
|
1
1
|
module Cloudsheet
|
2
|
-
class
|
3
|
-
|
4
|
-
|
2
|
+
class Mapping
|
3
|
+
attr_reader :name, :read
|
4
|
+
TO_S = ->(d) {d.to_s}
|
5
|
+
NOOP = ->(d) {d}
|
6
|
+
def initialize(name)
|
7
|
+
@name = name
|
8
|
+
@read = NOOP
|
9
|
+
@write = NOOP
|
5
10
|
end
|
6
|
-
|
7
|
-
def
|
8
|
-
@
|
11
|
+
|
12
|
+
def map(read, write = nil)
|
13
|
+
@read = read
|
14
|
+
@write = write if write
|
15
|
+
self
|
9
16
|
end
|
10
17
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
18
|
+
def type(klass)
|
19
|
+
klass = klass.to_s
|
20
|
+
klass.downcase!
|
21
|
+
case klass #Because case uses ===
|
22
|
+
when ""
|
23
|
+
when "noop"
|
24
|
+
when "string"
|
25
|
+
@read = ->(d) {String.new(d) if d}
|
26
|
+
@write = TO_S
|
27
|
+
when "date"
|
28
|
+
@read = ->(d) {Date.parse(d) if not d.empty?}
|
29
|
+
@write = TO_S
|
30
|
+
when "float"
|
31
|
+
@read = ->(d) {d.to_f if not d.empty?}
|
32
|
+
@write = TO_S
|
33
|
+
when "integer"
|
34
|
+
@read = ->(d) {d.to_i if not d.empty?}
|
35
|
+
@write = TO_S
|
36
|
+
when "datetime"
|
37
|
+
@read = ->(d) {DateTime.parse(d) if not d.empty?}
|
38
|
+
@write = TO_S
|
39
|
+
when "money"
|
40
|
+
@read = ->(d) {d.gsub(',','').gsub('$','').to_f}
|
41
|
+
@write = ->(d) {"$"+d.to_s}
|
42
|
+
when "month"
|
43
|
+
@read = ->(d) {Date.strptime(d, '%m/%Y') if not d.empty?}
|
44
|
+
@write = ->(d) {d.strftime("%m/%Y")}
|
14
45
|
else
|
15
|
-
|
46
|
+
raise "No mapping for that type is built in"
|
16
47
|
end
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
class Mapping < Struct.new :name, :function
|
21
|
-
def initialize(name, func = ->(d) {String.new(d)})
|
22
|
-
self.name = name
|
23
|
-
self.function = func
|
48
|
+
self
|
24
49
|
end
|
25
50
|
end
|
26
51
|
end
|
data/lib/cloudsheet/row.rb
CHANGED
@@ -1,19 +1,16 @@
|
|
1
1
|
module Cloudsheet
|
2
|
-
class Row
|
3
|
-
def initialize(map
|
2
|
+
class Row < Hash
|
3
|
+
def initialize(map)
|
4
4
|
@map = map
|
5
5
|
end
|
6
6
|
|
7
|
-
def parse(data)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
return out
|
14
|
-
else
|
15
|
-
return data.to_a
|
7
|
+
def self.parse(data, map = nil)
|
8
|
+
map = Cloudsheet::Map.new unless map
|
9
|
+
row = self.new(map)
|
10
|
+
data.each_with_index do |d, i|
|
11
|
+
row[map[i].name] = map.read(i, d)
|
16
12
|
end
|
13
|
+
row
|
17
14
|
end
|
18
15
|
end
|
19
16
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "cloudsheet/row"
|
2
|
+
require "cloudsheet/map"
|
3
|
+
require "cloudsheet/mapping"
|
4
|
+
|
5
|
+
module Cloudsheet
|
6
|
+
class Sheet
|
7
|
+
attr_reader :raw_sheet, :workbook
|
8
|
+
|
9
|
+
def map(m)
|
10
|
+
@map = m
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def each
|
15
|
+
rows.each do |r|
|
16
|
+
yield Row.parse(r, @map)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def rows
|
21
|
+
@raw_sheet.rows
|
22
|
+
end
|
23
|
+
|
24
|
+
def sheet(num)
|
25
|
+
@raw_sheet = @workbook[num]
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/drive_spec.rb
CHANGED
@@ -2,19 +2,19 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
2
|
require "google_drive"
|
3
3
|
|
4
4
|
describe Cloudsheet::Drive do
|
5
|
-
before :
|
6
|
-
@
|
5
|
+
before :all do
|
6
|
+
@google_spreadsheet = GoogleDrive.login(ENV['G_USERNAME'], ENV['G_PASSWORD']).spreadsheet_by_key(ENV['G_SPREADSHEET'])
|
7
7
|
end
|
8
|
-
|
9
|
-
|
10
|
-
subject{Cloudsheet::Drive.new(
|
8
|
+
|
9
|
+
context "properly initialized" do
|
10
|
+
subject{Cloudsheet::Drive.new(user: ENV['G_USERNAME'], password: ENV['G_PASSWORD'], sheet_key: ENV['G_SPREADSHEET'])}
|
11
11
|
|
12
12
|
it "initializes" do
|
13
|
-
subject.
|
13
|
+
subject.raw_sheet.worksheet_feed_url.should eq(@google_spreadsheet.worksheets[0].worksheet_feed_url)
|
14
14
|
end
|
15
15
|
|
16
16
|
it "selects a worksheet" do
|
17
|
-
subject.
|
17
|
+
subject.sheet(0).raw_sheet.worksheet_feed_url.should eq(@google_spreadsheet.worksheets[0].worksheet_feed_url)
|
18
18
|
end
|
19
19
|
|
20
20
|
describe "rows" do
|
@@ -24,7 +24,7 @@ describe Cloudsheet::Drive do
|
|
24
24
|
|
25
25
|
it "is enumerated without a map" do
|
26
26
|
i = 2
|
27
|
-
ws = google_spreadsheet.worksheets[0]
|
27
|
+
ws = @google_spreadsheet.worksheets[0]
|
28
28
|
subject.each do |r|
|
29
29
|
r[0].should eq(ws[i, 1])
|
30
30
|
i += 1
|
@@ -34,17 +34,25 @@ describe Cloudsheet::Drive do
|
|
34
34
|
it "enumerates with a map" do
|
35
35
|
m = Cloudsheet::Map.new
|
36
36
|
m[0] = Cloudsheet::Mapping.new(:name)
|
37
|
-
m[1] = Cloudsheet::Mapping.new(:start_date
|
37
|
+
m[1] = Cloudsheet::Mapping.new(:start_date).map(->(d) {DateTime.strptime(d, "%m/%d/%Y")})
|
38
|
+
m[3] = Cloudsheet::Mapping.new(3)
|
38
39
|
subject.map(m)
|
39
40
|
|
40
|
-
ws = google_spreadsheet.worksheets[0]
|
41
|
+
ws = @google_spreadsheet.worksheets[0]
|
41
42
|
i = 2
|
42
43
|
subject.each do |r|
|
43
44
|
r[:name].should eq(ws[i, 1])
|
44
45
|
r[:start_date].should eq(DateTime.strptime(ws[i, 2], "%m/%d/%Y"))
|
45
46
|
r[2].should eq(ws[i, 3])
|
47
|
+
r[3].should eq(ws[i, 4])
|
46
48
|
i += 1
|
47
49
|
end
|
48
50
|
end
|
49
51
|
end
|
52
|
+
end
|
53
|
+
context "missing parameters" do
|
54
|
+
it "fails without sheet_key" do
|
55
|
+
->{Cloudsheet::Drive.new(user: ENV['G_USERNAME'], password: ENV['G_PASSWORD'])}.should raise_error
|
56
|
+
end
|
57
|
+
end
|
50
58
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cloudsheet::Mapping do
|
4
|
+
subject { Cloudsheet::Mapping.new(:test) }
|
5
|
+
describe "#map" do
|
6
|
+
it "always returns self" do
|
7
|
+
subject.map(nil).should be subject
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#type" do
|
12
|
+
specify{ expect{subject.type(Object)}.to raise_error}
|
13
|
+
it "defaults to no-op" do
|
14
|
+
subject.read.call(nil).should be_nil
|
15
|
+
obj = Object.new
|
16
|
+
subject.read.call(obj).should be obj
|
17
|
+
end
|
18
|
+
|
19
|
+
it "always returns self" do
|
20
|
+
subject.type(nil).should be subject
|
21
|
+
end
|
22
|
+
|
23
|
+
it "supports String" do
|
24
|
+
subject.type(String)
|
25
|
+
subject.read.call("Hello").should eq(String.new("Hello"))
|
26
|
+
subject.read.call("").should eq("")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "supports Date" do
|
30
|
+
subject.type(Date)
|
31
|
+
subject.read.call(Date.today.to_s).should eq(Date.today)
|
32
|
+
subject.read.call("").should be_nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it "supports DateTime" do
|
36
|
+
subject.type(DateTime)
|
37
|
+
now = DateTime.now
|
38
|
+
subject.read.call(now.to_s).should eq(DateTime.parse(now.to_s))
|
39
|
+
end
|
40
|
+
|
41
|
+
it "supports Integer" do
|
42
|
+
subject.type(Integer)
|
43
|
+
num = Random.rand(0...100)
|
44
|
+
subject.read.call(num.to_s).should eq(num)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "supports Float" do
|
48
|
+
subject.type(Float)
|
49
|
+
subject.read.call("100.5").should eq(100.5)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "supports money" do
|
53
|
+
subject.type("money")
|
54
|
+
subject.read.call("$100,981.02").should eq(100981.02)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "supports month" do
|
58
|
+
subject.type("month")
|
59
|
+
subject.read.call("12/2012").should be_an_instance_of Date
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/spec/row_spec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cloudsheet::Row do
|
4
|
+
let(:example) {["0", "hi", Date.today.to_s]}
|
5
|
+
context "no map" do
|
6
|
+
subject{Cloudsheet::Row.parse(example)}
|
7
|
+
|
8
|
+
it "constructs" do
|
9
|
+
subject[0].should eq(example[0])
|
10
|
+
subject[1].should eq(example[1])
|
11
|
+
subject[2].should eq(example[2])
|
12
|
+
end
|
13
|
+
|
14
|
+
specify{subject.count.should eq(example.count)}
|
15
|
+
end
|
16
|
+
|
17
|
+
context "map" do
|
18
|
+
let(:map) {
|
19
|
+
map = double("map")
|
20
|
+
map.stub_chain(:[], :name){"name"}
|
21
|
+
map.stub(:read){"data"}
|
22
|
+
map
|
23
|
+
}
|
24
|
+
|
25
|
+
subject{Cloudsheet::Row.parse([0], map)}
|
26
|
+
it "constructs" do
|
27
|
+
subject["name"].should eq("data")
|
28
|
+
end
|
29
|
+
|
30
|
+
specify{subject.count.should eq(1)}
|
31
|
+
end
|
32
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
1
3
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
4
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
5
|
require 'rspec'
|
4
6
|
require 'pry'
|
5
|
-
require '
|
7
|
+
require 'cloudsheet'
|
6
8
|
|
7
9
|
# Requires supporting files with custom matchers and macros, etc,
|
8
10
|
# in ./support/ and its subdirectories.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloudsheet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: google_drive
|
@@ -140,9 +140,14 @@ files:
|
|
140
140
|
- VERSION
|
141
141
|
- cloudsheet.gemspec
|
142
142
|
- lib/cloudsheet.rb
|
143
|
+
- lib/cloudsheet/drive.rb
|
144
|
+
- lib/cloudsheet/map.rb
|
143
145
|
- lib/cloudsheet/mapping.rb
|
144
146
|
- lib/cloudsheet/row.rb
|
147
|
+
- lib/cloudsheet/sheet.rb
|
145
148
|
- spec/drive_spec.rb
|
149
|
+
- spec/mapping_spec.rb
|
150
|
+
- spec/row_spec.rb
|
146
151
|
- spec/spec_helper.rb
|
147
152
|
homepage: http://github.com/kurtisnelson/cloudsheet
|
148
153
|
licenses:
|
@@ -159,7 +164,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
159
164
|
version: '0'
|
160
165
|
segments:
|
161
166
|
- 0
|
162
|
-
hash:
|
167
|
+
hash: 2552302486075153280
|
163
168
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
169
|
none: false
|
165
170
|
requirements:
|