cloudsheet 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|