google-cells 0.3.0

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