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 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: