sheety 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4d5c92ed5ae6bea6927c5e677aefe279464cf55f
4
+ data.tar.gz: d8dccac10a9bf260a14ab11f10f00c5f0b786297
5
+ SHA512:
6
+ metadata.gz: 47e44ab93c4dc032e79e8512622280da7aa9a233ef8387e3a425fc904c305d8508702ec0583ba957ea173ac837eb7f7a8fc0b6e539d6d5b13178a909e8a481e0
7
+ data.tar.gz: f0cc54c3d2a1c611c960af5b72d8ca0714d3c082bba4934d1a42a80a11bbf637c1d27d3ccf0dec1b2a947de8f9a77262a6a0797ad8fbce34625b0c1b863a29fb
@@ -0,0 +1,49 @@
1
+ module Sheety
2
+ NEWLINE = "\n"
3
+
4
+ def self.length_s(val)
5
+ if val.respond_to?(:length) then val.length.to_s else "??" end
6
+ end
7
+
8
+ end
9
+
10
+ require_relative 'sheety/api'
11
+ require_relative 'sheety/cell'
12
+ require_relative 'sheety/row'
13
+ require_relative 'sheety/worksheet'
14
+ require_relative 'sheety/spreadsheet'
15
+
16
+ module Sheety
17
+ ## Convenience Methods
18
+ def self.auth
19
+ return Api.inst.auth
20
+ end
21
+
22
+ def self.sheets(force_refetch=false)
23
+ return Api.inst.sheets(force_refetch)
24
+ end
25
+
26
+ def self.sheets_where(constraints)
27
+ return Api.inst.sheets_where(constraints)
28
+ end
29
+
30
+ def self.find_sheet(constraints)
31
+ return Api.inst.find_sheet(constraints)
32
+ end
33
+
34
+ def self.sheets_except(constraints)
35
+ return Api.inst.sheets_except(constraints)
36
+ end
37
+
38
+ def self.sheets_except_any(constraints)
39
+ return Api.inst.sheets_except_any(constraints)
40
+ end
41
+
42
+ def self.list_sheets
43
+ sheets.each_with_index do |sheet, index|
44
+ puts "Sheet #{index} - #{sheet}"
45
+ end
46
+
47
+ return nil
48
+ end
49
+ end
@@ -0,0 +1,99 @@
1
+ require "google/api_client"
2
+ require 'xmlsimple'
3
+
4
+ require_relative 'children'
5
+ require_relative 'spreadsheet'
6
+
7
+ module Sheety
8
+
9
+ private
10
+
11
+ class Api
12
+ include Sheety::Children
13
+
14
+ URI_LIST = 'https://spreadsheets.google.com/feeds/spreadsheets/private/full'
15
+ URI_AUTH = 'https://www.googleapis.com/oauth2/v3/token'
16
+
17
+ def link(key) # for compat with ChildBearing
18
+ return key
19
+ end
20
+
21
+ atom_children :sheets, klass: Sheety::Spreadsheet, link: URI_LIST
22
+
23
+ def self.inst
24
+ if @@instance.nil?
25
+ @@instance = Sheety::Api.new
26
+ end
27
+ return @@instance
28
+ end
29
+
30
+ def auth(force=false)
31
+ if @access_token.blank? || force
32
+ data = { :grant_type => 'urn:ietf:params:oauth:grant-type:jwt-bearer', :assertion => @auth.to_jwt.to_s }
33
+ resp = HTTParty.post(URI_AUTH, { :body => data })
34
+ if Net::HTTPOK === resp.response
35
+ @access_token = resp['access_token']
36
+ end
37
+ end
38
+
39
+ return (@access_token ? self : nil)
40
+ end
41
+
42
+ def get_feed(uri)
43
+ return parse_response(HTTParty.get(uri, headers: get_headers))
44
+ end
45
+
46
+ def post_feed(uri, data)
47
+ return parse_response(HTTParty.post(uri, body: data, headers: post_headers))
48
+ end
49
+
50
+ def put_feed(uri, data)
51
+ return parse_response(HTTParty.put(uri, body: data, headers: put_headers))
52
+ end
53
+
54
+ def delete_feed(uri)
55
+ return parse_response(HTTParty.delete(uri, headers: delete_headers))
56
+ end
57
+
58
+ private
59
+
60
+ def parse_response(resp)
61
+ begin
62
+ return XmlSimple.xml_in(resp.body, { 'KeyAttr' => 'name', 'keepnamespace' => true })
63
+ rescue
64
+ return resp
65
+ end
66
+ end
67
+
68
+ def get_headers
69
+ return auth_headers
70
+ end
71
+
72
+ def post_headers
73
+ return put_headers
74
+ end
75
+
76
+ def put_headers
77
+ return auth_headers.merge('Content-Type' => 'application/atom+xml')
78
+ end
79
+
80
+ def delete_headers
81
+ return auth_headers
82
+ end
83
+
84
+ def auth_headers
85
+ return { 'Authorization' => "Bearer #{@access_token}" }
86
+ end
87
+
88
+ @@instance = nil
89
+
90
+ def initialize
91
+ @client = Google::APIClient.new(:application_name => 'Sheety', :application_version => "0.1.0")
92
+ @client.authorization= :google_app_default
93
+ @auth = @client.authorization
94
+ @auth.scope = 'https://spreadsheets.google.com/feeds'
95
+
96
+ return self
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,92 @@
1
+ require_relative 'feed'
2
+
3
+ class Sheety::Cell < Sheety::Feed
4
+ COL_LETTERS = [nil, *('A'..'Z')]
5
+
6
+ attr_reader :row, :col, :input_value, :numeric_value, :display_value
7
+
8
+ def parse(entry)
9
+ super(entry)
10
+ cell = entry['gs:cell'][0]
11
+
12
+ @row = cell['row'].to_i
13
+ @col = cell['col'].to_i
14
+ @input_value = cell['inputValue']
15
+ @numeric_value = cell['numericValue'].to_f
16
+ @display_value = cell['content']
17
+ end
18
+
19
+ def value=(new_value)
20
+ if new_value != @input_value
21
+ @input_value = new_value
22
+ @numeric_value = nil
23
+ @display_value = nil
24
+ end
25
+ end
26
+
27
+ def col=(c)
28
+ if @col.nil?
29
+ @col = c
30
+ end
31
+ end
32
+
33
+ def row=(r)
34
+ if @row.nil?
35
+ @row = r
36
+ end
37
+ end
38
+
39
+ def rc
40
+ return "R#{row}C#{col}"
41
+ end
42
+
43
+ def as_xml
44
+ return [
45
+ "<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:gs=\"http://schemas.google.com/spreadsheets/2006\">",
46
+ "<id>#{
47
+ if @id then
48
+ @id
49
+ else
50
+ @parent.link(Sheety::Worksheet::LINK_CELLS) + "/#{rc}"
51
+ end}</id>",
52
+ "<link rel=\"#{Sheety::Feed::LINK_EDIT}\" type=\"application/atom+xml\" href=\"#{
53
+ if link(LINK_EDIT) then
54
+ link(LINK_EDIT)
55
+ else
56
+ @parent.link(Sheety::Worksheet::LINK_CELLS) + "/#{rc}"
57
+ end}\"/>",
58
+ "<gs:cell row=\"#{row}\" col=\"#{col}\" inputValue=\"#{input_value}\"/>",
59
+ "</entry>",
60
+ ].join(Sheety::NEWLINE)
61
+ end
62
+
63
+ def as_batch_xml
64
+ return [
65
+ "<entry>",
66
+ "<batch:id>#{title}</batch:id>",
67
+ "<batch:operation type=\"update\" />",
68
+ "<id>#{id}</id>",
69
+ "<link rel=\"#{LINK_EDIT}\" type=\"application/atom+xml\"",
70
+ "href=\"#{link(LINK_EDIT)}\"/>",
71
+ "<gs:cell row=\"#{@row}\" col=\"#{@col}\" inputValue=\"#{@input_value}\"/>",
72
+ "</entry>",
73
+ ].join(Sheety::NEWLINE)
74
+ end
75
+
76
+ def save
77
+ uri = link(LINK_EDIT)
78
+ if uri
79
+ return Sheety::Api.inst.put_feed(uri, as_xml)
80
+ else
81
+ return Sheety::Api.inst.post_feed(@parent.link(Sheety::Worksheet::LINK_CELLS), as_xml)
82
+ end
83
+ end
84
+
85
+ def to_s
86
+ "<#{self.class}::#{object_id} #{rc} input='#{input_value}' numeric='#{numeric_value}' display='#{display_value}'>"
87
+ end
88
+
89
+ def inspect
90
+ to_s
91
+ end
92
+ end
@@ -0,0 +1,115 @@
1
+ module Sheety::Children
2
+ def self.included base
3
+ base.extend ClassMethods
4
+ end
5
+
6
+ def _passes_constraint(i_val, c_val)
7
+ case c_val # Good Read: http://ruby.about.com/od/beginningruby/qt/On-Case-And-Class.htm
8
+ when Range
9
+ c_val.include? i_val
10
+ when Array
11
+ c_val.include? i_val
12
+ when Regexp
13
+ c_val =~ i_val
14
+ when String
15
+ c_val == i_val.to_s
16
+ else
17
+ c_val == i_val
18
+ end
19
+ end
20
+
21
+ def _get_i_val(item, c_key, accessor=nil)
22
+ if accessor && item.respond_to?(accessor)
23
+ return item.send(accessor, c_key)
24
+ else
25
+ return item.try(c_key)
26
+ end
27
+ end
28
+
29
+ module ClassMethods
30
+ def atom_children(symbol, options)
31
+
32
+ if options.blank?
33
+ raise ArgumentError.new("blank options #{options} are not valid options for bear_children")
34
+ end
35
+
36
+ if options[:klass].blank? || options[:klass].class != Class
37
+ raise ArgumentError.new("#{options} must have a :klass that is a Class, not a #{options[:klass].class}")
38
+ end
39
+
40
+ if options[:link].blank?
41
+ raise ArgumentError.new("#{options} must have a non-blank :link")
42
+ end
43
+
44
+ unless method_defined?(:link)
45
+ raise TypeError.new("#{self} must respond to :link to bear_children")
46
+ end
47
+
48
+ if options[:merge_links] && method_defined?(:add_links) == false
49
+ raise TypeError.new("#{self} must respond to :add_links to bear_children with mrege_links=true")
50
+ end
51
+
52
+ plural = symbol.to_s
53
+ singular = plural.singularize
54
+
55
+ inst_var_sym = :"@#{symbol}"
56
+
57
+ get_method = :"#{plural}"
58
+ new_method = :"new_#{singular}"
59
+ enum_method = :"_enumerate_#{plural}_by_method"
60
+ where_method = :"#{plural}_where"
61
+ except_method = :"#{plural}_except"
62
+ except_any_method = :"#{plural}_except_any"
63
+ find_first_method = :"find_#{singular}"
64
+
65
+ define_method(new_method) do |entry=nil|
66
+ options[:klass].new(self, entry)
67
+ end
68
+
69
+ define_method(get_method) do |force_refetch=false|
70
+ if instance_variable_get(inst_var_sym).nil? || force_refetch
71
+ list = Sheety::Api.inst.get_feed(link(options[:link])) # sort of a cyclic dependency, suboptimal
72
+
73
+ # TODO: Create a ListFeed Object so the links we get here don't need to be worried about collisions on link ids
74
+ add_links(list['link']) if !list['link'].blank? && options[:merge_links]
75
+
76
+ list = (list['entry'] || []).map do |entry|
77
+ method(new_method).call(entry)
78
+ end
79
+
80
+ instance_variable_set(inst_var_sym, list)
81
+ end
82
+
83
+ return instance_variable_get(inst_var_sym)
84
+ end
85
+
86
+ define_method(enum_method) do |constraints, method|
87
+ return method(get_method).call().send(method) do |item|
88
+ constraints.all? do |constraint|
89
+ _passes_constraint(_get_i_val(item, constraint[0], options[:accessor]), constraint[1])
90
+ end
91
+ end
92
+ end
93
+
94
+ define_method(where_method) do |constraints|
95
+ return method(enum_method).call(constraints, :find_all)
96
+ end
97
+
98
+ define_method(find_first_method) do |constraints|
99
+ return method(enum_method).call(constraints, :detect)
100
+ end
101
+
102
+ define_method(except_method) do |constraints|
103
+ return method(enum_method).call(constraints, :reject)
104
+ end
105
+
106
+ define_method(except_any_method) do |constraints|
107
+ return method(get_method).call().reject do |item|
108
+ constraints.any? do |constraint|
109
+ _passes_constraint(_get_i_val(item, constraint[0], options[:accessor]), constraint[1])
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,86 @@
1
+ require_relative 'children'
2
+
3
+ class Sheety::Feed
4
+ include Sheety::Children
5
+
6
+ LINK_SELF = 'self'
7
+ LINK_EDIT = 'edit'
8
+ LINK_ALT = 'alternate'
9
+
10
+ attr_reader :id, :content, :updated, :parent
11
+
12
+ attr_accessor :title
13
+
14
+ def initialize(parent, entry=nil)
15
+ @parent = parent
16
+ @links = {}
17
+ unless entry.nil?
18
+ parse(entry)
19
+ end
20
+ end
21
+
22
+ def parse(entry)
23
+ return if entry.blank? || entry.length == 0
24
+ @title = Sheety::Feed.deref(entry, 'title', 0, 'content')
25
+ @id = Sheety::Feed.deref(entry, 'id', 0)
26
+ @content = Sheety::Feed.deref(entry, 'content', 'content')
27
+ @updated = Sheety::Feed.deref(entry, 'updated', 0)
28
+ @updated = DateTime.iso8601(@updated) if !@updated.blank?
29
+ add_links(Sheety::Feed.deref(entry ,'link'))
30
+ end
31
+
32
+ def link(key)
33
+ @links[key]
34
+ end
35
+
36
+ def as_xml
37
+ return "<entry></entry>"
38
+ end
39
+
40
+ protected
41
+
42
+ def add_links(links)
43
+ return if links.blank?
44
+ # Conflict prevention is here to preserve original values parsed from the entities.
45
+ # This mainly comes into play with the List Feed (a.k.a.: Rows) because we need the
46
+ # ability to add new ones.
47
+ links.each { |link_obj| @links[link_obj['rel']] = link_obj['href'] }
48
+ end
49
+
50
+ def link_edit
51
+ link(LINK_EDIT)
52
+ end
53
+
54
+ def link_add
55
+ parent.link(parent.class::LINK_ADD)
56
+ end
57
+
58
+ def save
59
+ result = if link_edit
60
+ Sheety::Api.inst.put_feed(link_edit, as_xml)
61
+ else
62
+ Sheety::Api.inst.post_feed(link_add, as_xml)
63
+ end
64
+ parse(result)
65
+ return result
66
+ end
67
+
68
+ def to_s
69
+ "<#{self.class}::#{object_id}>"
70
+ end
71
+
72
+ def inspect
73
+ to_s
74
+ end
75
+
76
+ private
77
+
78
+ def self.deref(data, *keys)
79
+ found = data
80
+ keys.each do |k|
81
+ next if found.blank?
82
+ found = found[k]
83
+ end
84
+ return found
85
+ end
86
+ end
@@ -0,0 +1,87 @@
1
+ require_relative 'feed'
2
+
3
+ class Sheety::Row < Sheety::Feed
4
+ def initialize(parent, entry=nil)
5
+ @attrs = {}
6
+ super(parent, entry)
7
+ end
8
+
9
+ def parse(entry)
10
+ super(entry)
11
+ entry.keys.each do |k|
12
+ if /\Agsx:/i =~ k
13
+ @attrs[k] = entry[k][0]
14
+ end
15
+ end
16
+ end
17
+
18
+ def value(key)
19
+ return @attrs['gsx:' + key.to_s]
20
+ end
21
+
22
+ alias_method :[], :value
23
+
24
+ def []=(key, val)
25
+ @attrs['gsx:' + key.to_s]=val
26
+ end
27
+
28
+ def put(hash)
29
+ hash.each { |kv| self[Sheety::Row.normalize_key(kv[0])] = kv[1] }
30
+ end
31
+
32
+ def as_xml
33
+ return [
34
+ '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">',
35
+ if !@id then
36
+ ''
37
+ else
38
+ "<id>#{@id}</id>"
39
+ end,
40
+ if !@id then
41
+ ''
42
+ else
43
+ "<updated>#{DateTime.now}</updated>"
44
+ end,
45
+ if !@id then
46
+ ''
47
+ else
48
+ '<category scheme="http://schemas.google.com/spreadsheets/2006" term="http://schemas.google.com/spreadsheets/2006#list"/>'
49
+ end,
50
+ if !@id then
51
+ ''
52
+ else
53
+ "<link rel=\"edit\" type=\"application/atom+xml\" href=\"#{@links[LINK_EDIT]}\" />"
54
+ end,
55
+ *(@attrs.map { |pair| "<#{pair[0]}>#{pair[1]}</#{pair[0]}>" }),
56
+ '</entry>',
57
+ ].join(Sheety::NEWLINE)
58
+ end
59
+
60
+ def save
61
+ uri = link(LINK_EDIT)
62
+ if uri
63
+ return Sheety::Api.inst.put_feed(uri, as_xml)
64
+ else
65
+ return Sheety::Api.inst.post_feed(@parent.link(Sheety::Worksheet::LINK_POST), as_xml)
66
+ end
67
+ end
68
+
69
+ alias_method :update, :save
70
+
71
+ # ProTip: Delete Rows in Reverse, otherwise the shift of deletion will cause unexpected behaviors
72
+ def delete
73
+ return Sheety::Api.inst.delete_feed(link(LINK_EDIT))
74
+ end
75
+
76
+ def to_s
77
+ "<#{self.class}::#{object_id} #{(@attrs.map { |kv| "#{kv[0].gsub('gsx:', '')}:#{kv[1]}" }).join(', ')}>"
78
+ end
79
+
80
+ def inspect
81
+ to_s
82
+ end
83
+
84
+ def self.normalize_key(key)
85
+ key.to_s.gsub(/[^a-zA-Z0-9]/, '')
86
+ end
87
+ end
@@ -0,0 +1,87 @@
1
+ require_relative 'feed'
2
+ require_relative 'cell'
3
+ require_relative 'row'
4
+ require_relative 'worksheet'
5
+
6
+ class Sheety::Spreadsheet < Sheety::Feed
7
+ LINK_WORKSHEETS = 'http://schemas.google.com/spreadsheets/2006#worksheetsfeed'
8
+ LINK_ADD = LINK_WORKSHEETS
9
+
10
+ atom_children :worksheets, klass: Sheety::Worksheet, link: LINK_WORKSHEETS
11
+
12
+ attr_reader :author_name, :author_email
13
+
14
+ def parse(entry)
15
+ super(entry)
16
+ @author_name = entry['author'][0]['name'][0]
17
+ @author_email = entry['author'][0]['email'][0]
18
+ end
19
+
20
+ def export_to_worksheet(data, options={})
21
+ if data.respond_to?(:as_json)
22
+ data = data.as_json
23
+ end
24
+
25
+ unless !data.blank? && Array === data
26
+ raise ArgumentError.new("data must be a non-blank array (or convertible by :as_json), got #{data}")
27
+ end
28
+
29
+ options = {title: 'worksheet', timestamp: true}.merge(options)
30
+
31
+ headers = data[0].keys
32
+
33
+ worksheet = new_worksheet
34
+ worksheet.col_count = headers.length
35
+ worksheet.row_count = 1 # we only need to have access to the header row for cells, other rows are inserted
36
+ worksheet.title = if options[:timestamp] then
37
+ "#{options[:title]}_#{Time.now.to_i.to_s}"
38
+ else
39
+ "#{options[:title]}"
40
+ end
41
+ worksheet.save # save that to create the ws
42
+
43
+ worksheet.cells # we need to fetch the link to save new cells to
44
+
45
+ headers.each_with_index do |key, index|
46
+ cell = worksheet.new_cell
47
+ cell.row = 1
48
+ cell.col = index + 1
49
+ cell.value = key.to_s
50
+ cell.save
51
+ end
52
+
53
+ worksheet.rows # Fetches the link we can save rows to
54
+
55
+ # We have to normalize the keys we display to a version without underscores or spaces or other bollocks
56
+ header_keys = Hash[headers.map { |k| [k, Row.normalize_key(k)] }]
57
+
58
+ # TODO: Finish this if I end up caring....
59
+ # dupes = (header_keys.values - header_keys.values.uniq)
60
+ #
61
+ # if dupes.length
62
+ #
63
+ # end
64
+
65
+ data.each do |datum|
66
+ row = worksheet.new_row
67
+ datum.each do |kv|
68
+ key, val = kv
69
+ header_key = header_keys[key]
70
+ row[header_key] = REXML::Text.normalize(val.to_s) unless val.blank?
71
+ end
72
+ row.save
73
+ end
74
+ end
75
+
76
+ ## Serialization Methods
77
+
78
+ # TODO: as_xml
79
+
80
+ def to_s
81
+ "<Spreadsheet::#{object_id} '#{title}' by #{author_name} w/ #{Sheety.length_s(@worksheets)} Worksheets>"
82
+ end
83
+
84
+ def inspect
85
+ to_s
86
+ end
87
+ end
@@ -0,0 +1,94 @@
1
+ require_relative 'feed'
2
+ require_relative 'cell'
3
+ require_relative 'row'
4
+
5
+ class Sheety::Worksheet < Sheety::Feed
6
+ LINK_ROWS = 'http://schemas.google.com/spreadsheets/2006#listfeed'
7
+ LINK_CELLS = 'http://schemas.google.com/spreadsheets/2006#cellsfeed'
8
+ LINK_VIZ = 'http://schemas.google.com/visualization/2008#visualizationApi'
9
+ LINK_CSV = 'http://schemas.google.com/spreadsheets/2006#exportcsv'
10
+ NATIVE_LINKS = [LINK_ROWS, LINK_CELLS, LINK_VIZ, LINK_CSV, LINK_SELF, LINK_EDIT]
11
+ # TODO: Put these in a ListFeed Object, because that's where they come from
12
+ LINK_POST = 'http://schemas.google.com/g/2005#post'
13
+ LINK_FEED = 'http://schemas.google.com/g/2005#feed'
14
+
15
+ attr_accessor :col_count, :row_count
16
+
17
+ atom_children :rows, klass: Sheety::Row, link: LINK_ROWS, merge_links: true, accessor: :value
18
+ atom_children :cells, klass: Sheety::Cell, link: LINK_CELLS
19
+
20
+ def parse(entry)
21
+ super(entry)
22
+
23
+ @col_count = Sheety::Feed.deref(entry, 'gs:colCount', 0).to_i
24
+ @row_count = Sheety::Feed.deref(entry, 'gs:rowCount', 0).to_i
25
+ end
26
+
27
+ def update_rows(data, key_key='key', value_key='value')
28
+ logs = {}
29
+ row_key_key = Sheety::Row.normalize_key(key_key)
30
+ row_value_key = Sheety::Row.normalize_key(value_key)
31
+
32
+ data.each do |kv|
33
+ case kv
34
+ when Array
35
+ key = kv[0].to_s
36
+ val = kv[1]
37
+ when Hash
38
+ key = kv[key_key].to_s
39
+ val = kv
40
+ else
41
+ raise ArgumentError("Unknown argument type: #{kv.class}")
42
+ end
43
+
44
+ row = find_row(row_key_key => key)
45
+
46
+ if row.nil?
47
+ row = new_row
48
+ row[row_key_key] = key
49
+ end
50
+
51
+ if Hash === val
52
+ row.put val
53
+ else
54
+ row[row_value_key] = val
55
+ end
56
+
57
+ resp = row.save
58
+
59
+ logs[key] = resp unless resp['id']
60
+ end
61
+ return logs
62
+ end
63
+
64
+ ## Serialization Methods
65
+
66
+ def as_xml
67
+ return [
68
+ '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gs="http://schemas.google.com/spreadsheets/2006">',
69
+ if @id then
70
+ "<id>#{@id}</id>"
71
+ else
72
+ ''
73
+ end,
74
+ "<category scheme=\"http://schemas.google.com/spreadsheets/2006\" term=\"http://schemas.google.com/spreadsheets/2006#worksheet\"/>",
75
+ "<title type=\"text\">#{title}</title>",
76
+ if link(LINK_EDIT) then
77
+ "<link rel=\"#{LINK_EDIT}\" type=\"application/atom+xml\" href=\"#{link(LINK_EDIT)}\"/>"
78
+ else
79
+ ''
80
+ end,
81
+ "<gs:rowCount>#{row_count}</gs:rowCount>",
82
+ "<gs:colCount>#{col_count}</gs:colCount>",
83
+ "</entry>",
84
+ ].join(Sheety::NEWLINE)
85
+ end
86
+
87
+ def to_s
88
+ "<Worksheet::#{object_id} '#{title}' (#{col_count} x #{row_count}) w/ #{Sheety.length_s(@rows)} Rows & #{Sheety.length_s(@cells)} Cells>"
89
+ end
90
+
91
+ def inspect
92
+ to_s
93
+ end
94
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sheety
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Blake Israel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: xml-simple
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.1.5.pre.bisrael
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.1.5.pre.bisrael
27
+ - !ruby/object:Gem::Dependency
28
+ name: google-api-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.8'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 0.8.6
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '0.8'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 0.8.6
47
+ description: An interface for manipulating Google Sheets in Ruby on Rails
48
+ email: blake@honkforhelp.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - lib/sheety.rb
54
+ - lib/sheety/api.rb
55
+ - lib/sheety/cell.rb
56
+ - lib/sheety/children.rb
57
+ - lib/sheety/feed.rb
58
+ - lib/sheety/row.rb
59
+ - lib/sheety/spreadsheet.rb
60
+ - lib/sheety/worksheet.rb
61
+ homepage: https://github.com/honkforhelp/sheety
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.2.2
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: A Google Spreadsheets Gem
85
+ test_files: []