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 CHANGED
@@ -1,20 +1,22 @@
1
1
  = Cloudsheet
2
2
  Load Google Drive Spreadsheets sanely.
3
3
 
4
- Setup a Google Drive Session and pass it into Cloudsheet along with the Google Spreadsheet ID (seen in the url)
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
- drive = GoogleDrive.login("user@example.com", "mypassword")
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(:start_date, ->(d) {DateTime.strptime(d, "%m/%d/%Y")})
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.worksheet(0).map(m)
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
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.1.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-18"
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/row"
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
@@ -1,26 +1,51 @@
1
1
  module Cloudsheet
2
- class Map
3
- def initialize
4
- @data = {}
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 []=(i,val)
8
- @data[i] = val
11
+
12
+ def map(read, write = nil)
13
+ @read = read
14
+ @write = write if write
15
+ self
9
16
  end
10
17
 
11
- def [](i)
12
- if @data[i]
13
- @data[i]
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
- Mapping.new(i)
46
+ raise "No mapping for that type is built in"
16
47
  end
17
- end
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
@@ -1,19 +1,16 @@
1
1
  module Cloudsheet
2
- class Row
3
- def initialize(map = nil)
2
+ class Row < Hash
3
+ def initialize(map)
4
4
  @map = map
5
5
  end
6
6
 
7
- def parse(data)
8
- if @map
9
- out = { }
10
- data.each_with_index do |d, i|
11
- out[@map[i].name] = @map[i].function.call d
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 :each do
6
- @drive = GoogleDrive.login(ENV['G_USERNAME'], ENV['G_PASSWORD'])
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
- let(:google_spreadsheet) {@drive.spreadsheet_by_key(ENV['G_SPREADSHEET'])}
9
-
10
- subject{Cloudsheet::Drive.new(@drive, ENV['G_SPREADSHEET'])}
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.spreadsheet.worksheets_feed_url.should eq(google_spreadsheet.worksheets_feed_url)
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.worksheet(0).sheet.worksheet_feed_url.should eq(google_spreadsheet.worksheets[0].worksheet_feed_url)
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, ->(d) {DateTime.strptime(d, "%m/%d/%Y")})
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 'Cloudsheet'
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.1.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-18 00:00:00.000000000 Z
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: 1593865811557726438
167
+ hash: 2552302486075153280
163
168
  required_rubygems_version: !ruby/object:Gem::Requirement
164
169
  none: false
165
170
  requirements: