google-cells 0.3.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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +26 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +2 -0
  5. data/CHANGELOG.md +28 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +73 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +75 -0
  10. data/Rakefile +4 -0
  11. data/examples/oauth2_service_account.rb +17 -0
  12. data/examples/oauth2_web_flow.rb +66 -0
  13. data/examples/sinatra/routes.rb +21 -0
  14. data/google_cells.gemspec +30 -0
  15. data/lib/google_cells.rb +80 -0
  16. data/lib/google_cells/author.rb +7 -0
  17. data/lib/google_cells/cell.rb +32 -0
  18. data/lib/google_cells/cell_selector.rb +18 -0
  19. data/lib/google_cells/cell_selector/row_selector.rb +70 -0
  20. data/lib/google_cells/fetcher.rb +27 -0
  21. data/lib/google_cells/folder.rb +7 -0
  22. data/lib/google_cells/google_object.rb +31 -0
  23. data/lib/google_cells/reader.rb +19 -0
  24. data/lib/google_cells/row.rb +7 -0
  25. data/lib/google_cells/spreadsheet.rb +179 -0
  26. data/lib/google_cells/url_helper.rb +37 -0
  27. data/lib/google_cells/util.rb +17 -0
  28. data/lib/google_cells/version.rb +3 -0
  29. data/lib/google_cells/worksheet.rb +63 -0
  30. data/spec/google_cells/cell_selector/row_selector_spec.rb +104 -0
  31. data/spec/google_cells/cell_selector_spec.rb +26 -0
  32. data/spec/google_cells/fetcher_spec.rb +21 -0
  33. data/spec/google_cells/google_object_spec.rb +18 -0
  34. data/spec/google_cells/reader_spec.rb +24 -0
  35. data/spec/google_cells/spreadsheet_spec.rb +171 -0
  36. data/spec/google_cells/url_helper_spec.rb +15 -0
  37. data/spec/google_cells/worksheet_spec.rb +69 -0
  38. data/spec/google_cells_spec.rb +13 -0
  39. data/spec/spec_helper.rb +30 -0
  40. data/spec/vcr_cassettes/google_cells/cell_selector/each.yml +9147 -0
  41. data/spec/vcr_cassettes/google_cells/cell_selector/find_each.yml +10272 -0
  42. data/spec/vcr_cassettes/google_cells/cell_selector/find_each/selection.yml +947 -0
  43. data/spec/vcr_cassettes/google_cells/fetcher.yml +144 -0
  44. data/spec/vcr_cassettes/google_cells/reader.yml +144 -0
  45. data/spec/vcr_cassettes/google_cells/spreadsheet/cell_selector/worksheet.yml +276 -0
  46. data/spec/vcr_cassettes/google_cells/spreadsheet/copy.yml +447 -0
  47. data/spec/vcr_cassettes/google_cells/spreadsheet/copy/content.yml +18555 -0
  48. data/spec/vcr_cassettes/google_cells/spreadsheet/enfold.yml +259 -0
  49. data/spec/vcr_cassettes/google_cells/spreadsheet/folders.yml +319 -0
  50. data/spec/vcr_cassettes/google_cells/spreadsheet/get.yml +135 -0
  51. data/spec/vcr_cassettes/google_cells/spreadsheet/list.yml +276 -0
  52. data/spec/vcr_cassettes/google_cells/spreadsheet/worksheets.yml +276 -0
  53. data/spec/vcr_cassettes/google_cells/worksheet/save.yml +9555 -0
  54. data/spec/vcr_cassettes/google_cells/worksheets.yml +145 -0
  55. metadata +250 -0
@@ -0,0 +1,17 @@
1
+ module GoogleCells
2
+ module Util
3
+
4
+ def e(str)
5
+ CGI.escapeHTML(str.to_s()).gsub(/\n/, '
')
6
+ end
7
+
8
+ def concat_url(url, piece)
9
+ (url_base, url_query) = url.split(/\?/, 2)
10
+ (piece_base, piece_query) = piece.split(/\?/, 2)
11
+ result_query = [url_query, piece_query].select(){ |s| s && !s.empty? }.join("&")
12
+ return (url_base || "") +
13
+ (piece_base || "") +
14
+ (result_query.empty? ? "" : "?#{result_query}")
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module GoogleCells
2
+ VERSION = "0.3.0"
3
+ end
@@ -0,0 +1,63 @@
1
+ require 'cgi'
2
+
3
+ module GoogleCells
4
+ class Worksheet < GoogleCells::GoogleObject
5
+ include Fetcher
6
+ include Util
7
+
8
+ @permanent_attributes = [ :etag, :title, :updated_at, :cells_uri,
9
+ :lists_uri, :spreadsheet, :row_count, :col_count ]
10
+ define_accessors
11
+
12
+ def rows
13
+ GoogleCells::CellSelector::RowSelector.new(self)
14
+ end
15
+
16
+ class UpdateError < StandardError ; end
17
+
18
+ def save!
19
+ return if @changed_cells.nil? || @changed_cells.empty?
20
+ batch_url = concat_url(cells_uri, "/batch")
21
+ response = request(:post, batch_url, body: to_xml, headers:{
22
+ "Content-Type" => "application/atom+xml", "If-Match" => "*"})
23
+ doc = Nokogiri.parse(response.body)
24
+
25
+ for entry in doc.css("atom|entry")
26
+ interrupted = entry.css("batch|interrupted")[0]
27
+ if interrupted
28
+ raise(UpdateError, "Update failed: %s" % interrupted["reason"])
29
+ end
30
+ if !(entry.css("batch|status").first["code"] =~ /^2/)
31
+ raise(UpdateError, "Update failed for cell %s: %s" %
32
+ [entry.css("atom|id").text, entry.css("batch|status")[0]["reason"]])
33
+ end
34
+ end
35
+ @changed_cells = {}
36
+ true
37
+ end
38
+
39
+ def track_changes(cell)
40
+ @changed_cells ||= {}
41
+ @changed_cells[cell.title] = cell # track only most recent change
42
+ nil
43
+ end
44
+
45
+ private
46
+
47
+ def to_xml
48
+ xml = <<-EOS
49
+ <feed xmlns="http://www.w3.org/2005/Atom"
50
+ xmlns:batch="http://schemas.google.com/gdata/batch"
51
+ xmlns:gs="http://schemas.google.com/spreadsheets/2006">
52
+ <id>#{e(self.cells_uri)}</id>
53
+ EOS
54
+ @changed_cells.each do |title, cell|
55
+ xml << cell.to_xml
56
+ end
57
+ xml << <<-"EOS"
58
+ </feed>
59
+ EOS
60
+ end
61
+ end
62
+ end
63
+
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoogleCells::CellSelector::RowSelector do
4
+
5
+ let(:subject) do
6
+ w = nil
7
+ VCR.use_cassette('google_cells/spreadsheet/cell_selector/worksheet') do
8
+ s = GoogleCells::Spreadsheet.list.first
9
+ w = s.worksheets.first
10
+ end
11
+ GoogleCells::CellSelector::RowSelector.new(w)
12
+ end
13
+
14
+ describe "#find_each" do
15
+
16
+ it "iterates over all the rows" do
17
+ count = 0
18
+
19
+ VCR.use_cassette('google_cells/cell_selector/find_each') do
20
+ subject.find_each do |r|
21
+ count += 1
22
+ r.class.should eq GoogleCells::Row
23
+ end
24
+ end
25
+ count.should eq 100
26
+ end
27
+
28
+ it "iterates over rows specified in cell selector" do
29
+ count = 0
30
+
31
+ VCR.use_cassette('google_cells/cell_selector/find_each/selection') do
32
+ s = GoogleCells::Spreadsheet.list.first
33
+ w = s.worksheets.first
34
+ rs = subject.from(5).to(10)
35
+ rs.find_each do |r|
36
+ count += 1
37
+ r.class.should eq GoogleCells::Row
38
+ end
39
+ end
40
+ count.should eq 6
41
+ end
42
+
43
+ context "optional batch sizes" do
44
+ it "fetches in batches 10" do
45
+ batch = 10
46
+ subject.should_receive(:get_cells).exactly(10).times.and_return((1..batch).to_a)
47
+ subject.find_each(batch_size:batch){|r| r}
48
+ end
49
+ it "fetches in batches 3" do
50
+ batch = 3
51
+ subject.should_receive(:get_cells).exactly(34).times.and_return((1..batch).to_a)
52
+ subject.find_each(batch_size:batch){|r| r}
53
+ end
54
+ end
55
+ end
56
+
57
+ describe "#each" do
58
+
59
+ it "iterates over rows specified in cell selector" do
60
+ count = 0
61
+
62
+ VCR.use_cassette('google_cells/cell_selector/each') do
63
+ subject.each do |r|
64
+ count += 1
65
+ r.class.should eq GoogleCells::Row
66
+ end
67
+ end
68
+ count.should eq 100
69
+ end
70
+
71
+ it "fetches in a single request" do
72
+ subject.should_receive(:get_cells).and_return((1..subject.worksheet.
73
+ row_count).to_a)
74
+ subject.each{|r| r}
75
+ end
76
+ end
77
+
78
+ describe "#from" do
79
+ it "sets its min row" do
80
+ subject.from(5)
81
+ subject.min_row.should eq 5
82
+ subject.from(10)
83
+ subject.min_row.should eq 10
84
+ end
85
+
86
+ it "returns itself" do
87
+ subject.from(subject.min_row).should eq subject
88
+ end
89
+ end
90
+
91
+ describe "#to" do
92
+ it "sets its max row" do
93
+ subject.to(5)
94
+ subject.max_row.should eq 5
95
+ subject.to(10)
96
+ subject.max_row.should eq 10
97
+ end
98
+
99
+ it "returns itself" do
100
+ subject.from(subject.max_row).should eq subject
101
+ end
102
+ end
103
+ end
104
+
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoogleCells::CellSelector do
4
+
5
+ let(:subject) do
6
+ w = nil
7
+ VCR.use_cassette('google_cells/spreadsheet/list') do
8
+ s = GoogleCells::Spreadsheet.list.first
9
+ w = s.worksheets.first
10
+ end
11
+ GoogleCells::CellSelector.new(w)
12
+ end
13
+
14
+ it { should respond_to(:min_row) }
15
+ it { should respond_to(:max_row) }
16
+ it { should respond_to(:min_col) }
17
+ it { should respond_to(:max_col) }
18
+ it { should respond_to(:worksheet) }
19
+
20
+ context "default values should be derived from worksheet" do
21
+ its(:min_row){ should eq 1 }
22
+ its(:max_row){ should eq 100 }
23
+ its(:min_col){ should eq 1 }
24
+ its(:max_col){ should eq 18 }
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoogleCells::Fetcher do
4
+
5
+ let(:object) do
6
+ obj = Object.new
7
+ obj.extend(GoogleCells::Fetcher)
8
+ obj
9
+ end
10
+
11
+ describe "#raw" do
12
+
13
+ it "returns the raw xml body of an http GET response" do
14
+ VCR.use_cassette('google_cells/fetcher') do |c|
15
+ doc = object.raw
16
+ doc.should_not be_empty
17
+ Nokogiri::XML(doc).errors.should be_empty
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoogleCells::GoogleObject do
4
+
5
+ context "subclass declaration" do
6
+
7
+ class TestObject < GoogleCells::GoogleObject
8
+ @permanent_attributes = %w{ name email }
9
+ define_accessors
10
+ end
11
+
12
+ it "automatically generates accessors for perm attribs" do
13
+ o = TestObject.new
14
+ o.should respond_to :name
15
+ o.should respond_to :email
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoogleCells::Reader do
4
+
5
+ let(:object) do
6
+ obj = Object.new
7
+ obj.extend(GoogleCells::Reader)
8
+ obj
9
+ end
10
+
11
+ describe "#each_entry" do
12
+
13
+ it "iterates over raw xml entries" do
14
+ VCR.use_cassette('google_cells/reader') do |c|
15
+ count = 0
16
+ object.each_entry do |e|
17
+ e.class.should eq Nokogiri::XML::Document
18
+ count +=1
19
+ end
20
+ count.should eq 3
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,171 @@
1
+ require 'spec_helper'
2
+
3
+ describe GoogleCells::Spreadsheet do
4
+
5
+ let(:klass){GoogleCells::Spreadsheet}
6
+
7
+ it { should respond_to(:title) }
8
+ it { should respond_to(:key) }
9
+ it { should respond_to(:updated_at) }
10
+ it { should respond_to(:author) }
11
+
12
+ describe ".share" do
13
+
14
+ let(:body){{role:'owner',type:'user',value:'me@me.com'}}
15
+
16
+ it "creates a new permission for the passed args" do
17
+ klass.should_receive(:request).with(:post, "https://www.googleapis.com" +
18
+ "/drive/v2/files/0AsfWTr-e4bf1dFVFRWtuSFJWTm1XeWhRdUt2MWwtQnc/"+
19
+ "permissions", {:body=>"{\"role\":\"" + "owner\",\"type\":\"user\"" +
20
+ ",\"value\":\"me@me.com\"}", :url_params=>{},
21
+ :headers=>{"Content-Type"=>"application/json"}})
22
+ klass.share('0AsfWTr-e4bf1dFVFRWtuSFJWTm1XeWhRdUt2MWwtQnc', body)
23
+ end
24
+ end
25
+
26
+ describe ".copy" do
27
+
28
+ before(:each) do
29
+ @s, @c = nil
30
+ VCR.use_cassette('google_cells/spreadsheet/copy') do
31
+ @s = GoogleCells::Spreadsheet.list.first
32
+ @c = klass.copy(s.key)
33
+ end
34
+ end
35
+ let(:s){ @s }
36
+
37
+ context "new copy" do
38
+ subject{ @c }
39
+
40
+ its(:key){ should_not eq s.key }
41
+ its(:title){ should eq s.title }
42
+
43
+ it "should have same content" do
44
+ VCR.use_cassette('google_cells/spreadsheet/copy/content') do
45
+ svals = s.worksheets[0].rows.first.cells.map(&:value)
46
+ cvals = @c.worksheets[0].rows.first.cells.map(&:value)
47
+ svals.should eq cvals
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ describe ".list" do
54
+
55
+ before(:each) do
56
+ @list = []
57
+ VCR.use_cassette('google_cells/spreadsheet/list') do |c|
58
+ @list = klass.list
59
+ end
60
+ end
61
+ let(:list){ @list }
62
+
63
+ it "returns a list" do
64
+ list.count.should eq 3
65
+ end
66
+
67
+ it "returns spreadsheet objects" do
68
+ list.each{|s| s.class.should eq subject.class }
69
+ end
70
+
71
+ context "loading new spreadsheets correcty" do
72
+ subject {list.first}
73
+
74
+ its(:title){ should eq "My Spreadsheet" }
75
+ its(:updated_at){ should eq '2014-02-23T04:52:51.908Z' }
76
+ its(:key){ should eq '0ApTxW-6l0Ch_dHFyNHNwX0NjTHVIVk9ZS2duQ2ptUlE' }
77
+
78
+ context "author" do
79
+ subject {list.first.author}
80
+
81
+ its(:name){ should eq '194578754295' }
82
+ its(:email){ should eq '194578754295@developer.gserviceaccount.com' }
83
+ end
84
+ end
85
+ end
86
+
87
+ describe ".get" do
88
+
89
+ before(:each) do
90
+ @s = nil
91
+ VCR.use_cassette('google_cells/spreadsheet/get') do
92
+ @s = klass.get('0AsfWTr-e4bf1dFVFRWtuSFJWTm1XeWhRdUt2MWwtQnc')
93
+ end
94
+ end
95
+ subject{ @s }
96
+
97
+ its(:class){ should eq klass }
98
+ its(:title){ should eq 'My Spreadsheet' }
99
+ its(:updated_at){ should eq '2014-02-23T02:25:18.152Z' }
100
+
101
+ context "author" do
102
+ subject{ @s.author }
103
+ its(:name){ should eq '194578754295' }
104
+ # its(:email){ should eq '194578754295@developer.gserviceaccount.com' }
105
+ end
106
+ end
107
+
108
+ describe "#enfold" do
109
+
110
+ before(:each) do
111
+ @s = nil
112
+ VCR.use_cassette('google_cells/spreadsheet/enfold') do
113
+ @s = GoogleCells::Spreadsheet.list.first
114
+ @s.instance_variable_set(:@folders, [])
115
+ @s.enfold('0B5TxW-6l0Ch_elJwQlVMaGcwTjA')
116
+ end
117
+ end
118
+
119
+ context "folders" do
120
+ subject{ @s.instance_variable_get(:@folders) }
121
+ its(:count){ should eq 1 }
122
+
123
+ it "sets key" do
124
+ subject.first.key.should eq '0B5TxW-6l0Ch_elJwQlVMaGcwTjA'
125
+ end
126
+ end
127
+ end
128
+
129
+ describe "#folders" do
130
+
131
+ before(:each) do
132
+ @s = nil
133
+ VCR.use_cassette('google_cells/spreadsheet/folders') do
134
+ @s = GoogleCells::Spreadsheet.list.first
135
+ @s.folders
136
+ end
137
+ end
138
+ it{ @s.folders.count.should eq 1 }
139
+
140
+ context "folders" do
141
+ subject{ @s.folders.first }
142
+
143
+ its(:class){ should eq GoogleCells::Folder }
144
+ its(:key){ should eq '0AJTxW-6l0Ch_Uk9PVA' }
145
+ end
146
+ end
147
+
148
+ describe "#worksheets" do
149
+
150
+ before(:each) do
151
+ @s = nil
152
+ VCR.use_cassette('google_cells/spreadsheet/worksheets') do
153
+ @s = klass.list.first
154
+ @s.worksheets.count.should eq 1
155
+ end
156
+ end
157
+ context "worksheets" do
158
+ subject{ @s.worksheets.first }
159
+
160
+ its(:title){ should eq 'Sheet1' }
161
+ its(:updated_at){ should eq '2014-02-23T02:25:18.152Z' }
162
+ its(:cells_uri){ should eq 'https://spreadsheets.google.com/feeds/'+
163
+ 'cells/0ApTxW-6l0Ch_dHFyNHNwX0NjTHVIVk9ZS2duQ2ptUlE/od6/private/full' }
164
+ its(:lists_uri){ should eq 'https://spreadsheets.google.com/feeds/'+
165
+ 'list/0ApTxW-6l0Ch_dHFyNHNwX0NjTHVIVk9ZS2duQ2ptUlE/od6/private/full' }
166
+ its(:row_count){ should eq 100 }
167
+ its(:col_count){ should eq 18 }
168
+ its(:spreadsheet){ should eq @s }
169
+ end
170
+ end
171
+ end