sheety 0.1.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.
@@ -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: []