googletastic 0.0.1
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.textile +47 -0
- data/Rakefile +103 -0
- data/lib/googletastic/access_rule.rb +45 -0
- data/lib/googletastic/album.rb +22 -0
- data/lib/googletastic/app_engine.rb +103 -0
- data/lib/googletastic/attendee.rb +7 -0
- data/lib/googletastic/base.rb +46 -0
- data/lib/googletastic/calendar.rb +60 -0
- data/lib/googletastic/comment.rb +3 -0
- data/lib/googletastic/document.rb +219 -0
- data/lib/googletastic/event.rb +149 -0
- data/lib/googletastic/ext/file.rb +219 -0
- data/lib/googletastic/ext/pretty_print.xsl +47 -0
- data/lib/googletastic/ext/xml.rb +10 -0
- data/lib/googletastic/ext.rb +1 -0
- data/lib/googletastic/form.rb +131 -0
- data/lib/googletastic/group.rb +44 -0
- data/lib/googletastic/helpers/document.rb +70 -0
- data/lib/googletastic/helpers/event.rb +26 -0
- data/lib/googletastic/helpers/form.rb +102 -0
- data/lib/googletastic/helpers.rb +18 -0
- data/lib/googletastic/image.rb +121 -0
- data/lib/googletastic/mixins/actions.rb +113 -0
- data/lib/googletastic/mixins/attributes.rb +27 -0
- data/lib/googletastic/mixins/finders.rb +78 -0
- data/lib/googletastic/mixins/namespaces.rb +42 -0
- data/lib/googletastic/mixins/parsing.rb +68 -0
- data/lib/googletastic/mixins/requesting.rb +84 -0
- data/lib/googletastic/mixins.rb +5 -0
- data/lib/googletastic/person.rb +41 -0
- data/lib/googletastic/spreadsheet.rb +35 -0
- data/lib/googletastic/sync/document.rb +120 -0
- data/lib/googletastic/sync/form.rb +106 -0
- data/lib/googletastic/sync.rb +46 -0
- data/lib/googletastic/thumbnail.rb +7 -0
- data/lib/googletastic/youtube.rb +25 -0
- data/lib/googletastic.rb +115 -0
- data/spec/benchmarks/document_benchmark.rb +40 -0
- data/spec/config.yml +6 -0
- data/spec/fixtures/data/Doing business in the eMarketPlace.doc +0 -0
- data/spec/fixtures/data/basic.txt +1 -0
- data/spec/fixtures/data/calendar.list.xml +64 -0
- data/spec/fixtures/data/doc_as_html.html +3097 -0
- data/spec/fixtures/data/doc_as_html_html.html +0 -0
- data/spec/fixtures/data/doclist.xml +76 -0
- data/spec/fixtures/data/document.single.xml +30 -0
- data/spec/fixtures/data/end.xml +446 -0
- data/spec/fixtures/data/event.list.xml +62 -0
- data/spec/fixtures/data/form.html +66 -0
- data/spec/fixtures/data/group.list.xml +58 -0
- data/spec/fixtures/data/person.list.xml +53 -0
- data/spec/fixtures/data/photo.list.xml +111 -0
- data/spec/fixtures/data/sample_upload.mp4 +0 -0
- data/spec/fixtures/data/spreadsheet.list.xml +43 -0
- data/spec/fixtures/models/document.rb +7 -0
- data/spec/fixtures/models/event.rb +3 -0
- data/spec/fixtures/models/form.rb +6 -0
- data/spec/fixtures/models/test_model.rb +3 -0
- data/spec/fixtures/results/test.txt +185 -0
- data/spec/googletastic/access_rule_spec.rb +13 -0
- data/spec/googletastic/album_spec.rb +9 -0
- data/spec/googletastic/app_engine_spec.rb +12 -0
- data/spec/googletastic/base_spec.rb +49 -0
- data/spec/googletastic/calendar_spec.rb +11 -0
- data/spec/googletastic/document_spec.rb +163 -0
- data/spec/googletastic/event_spec.rb +34 -0
- data/spec/googletastic/form_spec.rb +43 -0
- data/spec/googletastic/group_spec.rb +9 -0
- data/spec/googletastic/image_spec.rb +9 -0
- data/spec/googletastic/person_spec.rb +9 -0
- data/spec/googletastic/post_spec.rb +46 -0
- data/spec/googletastic/spreadsheet_spec.rb +9 -0
- data/spec/googletastic/youtube_spec.rb +58 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +25 -0
- metadata +187 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module Googletastic::Helpers::EventModelHelper
|
2
|
+
|
3
|
+
def self.included(base, &block)
|
4
|
+
base.extend ClassMethods
|
5
|
+
base.class_eval do
|
6
|
+
include InstanceMethods
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
attr_accessor :google_doc
|
12
|
+
|
13
|
+
def hello
|
14
|
+
# puts _googletastic_options.inspect + " OPTIONS!"
|
15
|
+
end
|
16
|
+
|
17
|
+
def google_doc
|
18
|
+
@google_doc ||= Googletastic::DocList.find(self.remote_id)
|
19
|
+
@google_doc
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# was Googletastic::Helpers::Form, but rails through fits with namespacing problems
|
2
|
+
module Googletastic::Helpers::FormModelHelper
|
3
|
+
|
4
|
+
# REQUIREMENTS:
|
5
|
+
# DEFINE 'formkey' property on model
|
6
|
+
|
7
|
+
# class options for Form:
|
8
|
+
# as: property name for the googletastic form
|
9
|
+
# foreign_key: foreign key to reference it, defaults to "as"_id
|
10
|
+
# sync: the properties you want to sync with the form
|
11
|
+
# redirect_to: where to redirect the forms submission
|
12
|
+
# form_only: don't include title and header that google gives you (defaults to false)
|
13
|
+
def self.included(base)
|
14
|
+
# defaults
|
15
|
+
options = Googletastic[base]
|
16
|
+
options[:as] ||= "google_form"
|
17
|
+
options[:foreign_key] ||= "#{options[:as]}_id"
|
18
|
+
options[:form_key] ||= "form_key"
|
19
|
+
options[:form_only] = false unless options.has_key?(:form_only)
|
20
|
+
if options.has_key?(:sync)
|
21
|
+
options[:sync] = case options[:sync].class.to_s
|
22
|
+
when "Symbol"
|
23
|
+
{options[:sync] => options[:sync]}
|
24
|
+
when "String"
|
25
|
+
{options[:sync].to_sym => options[:sync].to_sym}
|
26
|
+
when "Array"
|
27
|
+
options[:sync].collect { |v| {v.to_sym => v.to_sym} }
|
28
|
+
else
|
29
|
+
options[:sync].symbolize_keys
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# fast access
|
34
|
+
as = options[:as]
|
35
|
+
foreign_key = options[:foreign_key]
|
36
|
+
sync = options[:sync]
|
37
|
+
form_key = options[:form_key]
|
38
|
+
|
39
|
+
# eval
|
40
|
+
base.class_eval <<-end_eval, __FILE__, __LINE__
|
41
|
+
attr_accessor :#{as}
|
42
|
+
attr_accessor :content
|
43
|
+
|
44
|
+
def self.find_with_#{as}(*args)
|
45
|
+
google_records = Googletastic::Form.all
|
46
|
+
foreign_keys = google_records.collect { |record| record.id }
|
47
|
+
records = find_all_by_#{foreign_key}(foreign_keys) || []
|
48
|
+
record_keys = records.collect { |record| record.#{foreign_key} }
|
49
|
+
google_records.each do |google_record|
|
50
|
+
if !record_keys.include?(google_record.id)
|
51
|
+
record = self.new(
|
52
|
+
:#{foreign_key} => google_record.id,
|
53
|
+
:updated_at => google_record.updated_at,
|
54
|
+
:#{as} => google_record,
|
55
|
+
:title => google_record.title
|
56
|
+
)
|
57
|
+
record.save
|
58
|
+
records << record
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
records.each do |record|
|
63
|
+
record.#{as} = google_records.select { |r| r.id == record.#{foreign_key} }.first
|
64
|
+
end
|
65
|
+
records
|
66
|
+
end
|
67
|
+
|
68
|
+
# get form content
|
69
|
+
def get
|
70
|
+
self.#{as}.form_key ||= self.#{form_key}
|
71
|
+
self.#{as}.get
|
72
|
+
end
|
73
|
+
|
74
|
+
if base.is_a?(ActiveRecord::Base)
|
75
|
+
|
76
|
+
before_validation :clean_form_key
|
77
|
+
validates_presence_of :#{as}
|
78
|
+
validates_uniqueness_of :#{as}
|
79
|
+
validate :validate_formkey_is_valid
|
80
|
+
before_save :sync_with_google
|
81
|
+
|
82
|
+
def sync_with_google
|
83
|
+
Hash(#{sync}).each do |mine, theirs|
|
84
|
+
self[mine] = self.#{as}[theirs]
|
85
|
+
end if Googletastic[self].has_key?(:sync)
|
86
|
+
end
|
87
|
+
|
88
|
+
def validate_form_key_is_valid
|
89
|
+
case self.#{as}.get
|
90
|
+
when Net::HTTPSuccess
|
91
|
+
true
|
92
|
+
else
|
93
|
+
errors.add(:form_key, "is not a valid Google Forms key or URL or error connecting to Google")
|
94
|
+
false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end_eval
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Googletastic::Helpers
|
2
|
+
def self.included(base)
|
3
|
+
base.extend(ClassMethods)
|
4
|
+
end
|
5
|
+
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def googletastic(model, options = {})
|
10
|
+
Googletastic.options_for(self, options)
|
11
|
+
|
12
|
+
include ("Googletastic::Helpers::#{model.to_s.camelize}ModelHelper").constantize
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require_local "helpers/*", __FILE__
|
@@ -0,0 +1,121 @@
|
|
1
|
+
class Googletastic::Image < Googletastic::Base
|
2
|
+
|
3
|
+
attr_accessor :url, :width, :height, :size, :thumbnails, :title, :summary, :content_type
|
4
|
+
attr_accessor :album_id, :access, :commentable, :id, :created_at, :updated_at, :kind
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
# http://code.google.com/apis/picasaweb/docs/2.0/reference.html#Parameters
|
9
|
+
def valid_queries
|
10
|
+
{
|
11
|
+
:access => "access",
|
12
|
+
:geo_coordinates => "bbox",
|
13
|
+
:geo_location => "l",
|
14
|
+
:image_max_size => "imgmax",
|
15
|
+
:thumb_size => "thumbsize",
|
16
|
+
:kind => "kind",
|
17
|
+
:tag => "tag",
|
18
|
+
}.merge(super)
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid_access?(value)
|
22
|
+
%w(all private public visible).include?(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid_image_max_size?(value)
|
26
|
+
[94, 110, 128, 200, 220, 288, 320, 400, 512, 576, 640, 720, 800, 912, 1024, 1152, 1280, 1440, 1600].include?(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
def valid_kind?(value)
|
30
|
+
%w(album photo comment tag user).include?(value)
|
31
|
+
end
|
32
|
+
|
33
|
+
def client_class
|
34
|
+
"Photos"
|
35
|
+
end
|
36
|
+
|
37
|
+
def index_url
|
38
|
+
"http://picasaweb.google.com/data/feed/api/user/#{Googletastic.credentials[:username]}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_url(options)
|
42
|
+
base = options.has_key?(:url) ? options[:url] : self.index_url
|
43
|
+
options[:url] = base
|
44
|
+
options[:kind] = "photo" unless options.has_key?(:kind)
|
45
|
+
options[:access] ||= "all"
|
46
|
+
#options[:image_max_size] = 720 unless options.has_key?(:image_max_size)
|
47
|
+
super(options)
|
48
|
+
end
|
49
|
+
|
50
|
+
def unmarshall(xml)
|
51
|
+
records = xml.xpath("//atom:entry", ns_tag("atom")).collect do |record|
|
52
|
+
# thumbnails
|
53
|
+
thumbnails = record.xpath("media:group/media:thumbnail").collect do |thumbnail|
|
54
|
+
url = thumbnail["url"]
|
55
|
+
width = thumbnail["width"].to_i
|
56
|
+
height = thumbnail["height"].to_i
|
57
|
+
Googletastic::Thumbnail.new(
|
58
|
+
:url => url,
|
59
|
+
:width => width,
|
60
|
+
:height => height
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
id = record.xpath("atom:id", ns_tag("atom")).first.text
|
65
|
+
created_at = record.xpath("atom:published", ns_tag("atom")).first.text
|
66
|
+
updated_at = record.xpath("atom:updated", ns_tag("atom")).first.text
|
67
|
+
kind = "photo"
|
68
|
+
title = record.xpath("atom:title", ns_tag("atom")).first.text
|
69
|
+
summary = record.xpath("atom:summary", ns_tag("atom")).first.text
|
70
|
+
|
71
|
+
album_id = record.xpath("gphoto:albumid", ns_tag("gphoto")).first.text
|
72
|
+
access = record.xpath("gphoto:access", ns_tag("gphoto")).first.text
|
73
|
+
width = record.xpath("gphoto:width", ns_tag("gphoto")).first.text.to_i
|
74
|
+
height = record.xpath("gphoto:height", ns_tag("gphoto")).first.text.to_i
|
75
|
+
size = record.xpath("gphoto:size", ns_tag("gphoto")).first.text.to_i
|
76
|
+
|
77
|
+
commentable = record.xpath("gphoto:commentingEnabled", ns_tag("gphoto")).first.text == "true" ? true : false
|
78
|
+
|
79
|
+
content = record.xpath("media:group/media:content").first
|
80
|
+
url = content["url"].to_s
|
81
|
+
content_type = content["type"].to_s
|
82
|
+
|
83
|
+
Googletastic::Image.new(
|
84
|
+
:id => id,
|
85
|
+
:created_at => created_at,
|
86
|
+
:kind => kind,
|
87
|
+
:title => title,
|
88
|
+
:summary => summary,
|
89
|
+
:album_id => album_id,
|
90
|
+
:access => access,
|
91
|
+
:width => width,
|
92
|
+
:height => height,
|
93
|
+
:size => size,
|
94
|
+
:commentable => commentable,
|
95
|
+
:url => url,
|
96
|
+
:content_type => content_type,
|
97
|
+
:thumbnails => thumbnails
|
98
|
+
)
|
99
|
+
end
|
100
|
+
records
|
101
|
+
end
|
102
|
+
|
103
|
+
# not implemented
|
104
|
+
def marshall(record)
|
105
|
+
Nokogiri::XML::Builder.new { |xml|
|
106
|
+
xml.entry('xmlns' => Googletastic::ATOM_NS, 'xmlns:docs' => ns("docs")) {
|
107
|
+
if record.id
|
108
|
+
xml.id {
|
109
|
+
xml.text show_url(record.id)
|
110
|
+
}
|
111
|
+
end
|
112
|
+
xml.title {
|
113
|
+
xml.text record.title
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}.to_xml
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Googletastic::Mixins::Actions
|
2
|
+
include_class_and_instance_modules
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
# see the data live on google docs
|
7
|
+
def show_url(id)
|
8
|
+
raise "Implement in Sublass"
|
9
|
+
end
|
10
|
+
|
11
|
+
# edit the data on google docs
|
12
|
+
def edit_url(id)
|
13
|
+
raise "Implement in Subclass"
|
14
|
+
end
|
15
|
+
|
16
|
+
# update the data via xml
|
17
|
+
def update_url(id)
|
18
|
+
raise "Implement in Subclass"
|
19
|
+
end
|
20
|
+
|
21
|
+
# xml feed
|
22
|
+
def index_url(ids = nil)
|
23
|
+
raise "Implement in Subclass"
|
24
|
+
end
|
25
|
+
|
26
|
+
# xml entry feed
|
27
|
+
def get_url(id)
|
28
|
+
raise "Implement 'get' in subclass"
|
29
|
+
end
|
30
|
+
|
31
|
+
def upload_url(id = nil)
|
32
|
+
raise "Implement in Subclass"
|
33
|
+
end
|
34
|
+
|
35
|
+
def download_url(id)
|
36
|
+
raise "Implement in Subclass"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module InstanceMethods
|
41
|
+
def show_url
|
42
|
+
self.class.show_url(self.id)
|
43
|
+
end
|
44
|
+
|
45
|
+
def edit_url
|
46
|
+
self.class.edit_url(self.id)
|
47
|
+
end
|
48
|
+
|
49
|
+
def index_url(ids = nil)
|
50
|
+
self.class.index_url(ids)
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_url
|
54
|
+
self.class.get_url(self.id)
|
55
|
+
end
|
56
|
+
|
57
|
+
def update_url
|
58
|
+
self.class.update_url(self.id)
|
59
|
+
end
|
60
|
+
|
61
|
+
def download_url
|
62
|
+
self.class.download_url(self.id)
|
63
|
+
end
|
64
|
+
|
65
|
+
def upload_url
|
66
|
+
puts "UPLOADURL"
|
67
|
+
self.class.upload_url(self.id)
|
68
|
+
puts "POST"
|
69
|
+
end
|
70
|
+
|
71
|
+
def save
|
72
|
+
create_or_update
|
73
|
+
end
|
74
|
+
|
75
|
+
def destroy
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
def clone
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
def reload(options = nil)
|
84
|
+
end
|
85
|
+
|
86
|
+
def new_record?
|
87
|
+
self.id.nil?
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
def create_or_update
|
92
|
+
result = new_record? ? create : update
|
93
|
+
result != false
|
94
|
+
end
|
95
|
+
|
96
|
+
def create
|
97
|
+
if has_attachment?
|
98
|
+
self.class.client.post_file(self.class.index_url, self.attachment_path, mime_type, self.to_xml)
|
99
|
+
else
|
100
|
+
self.class.client.post(self.class.index_url, self.to_xml)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def update(attribute_names = @attributes.keys)
|
105
|
+
if has_attachment?
|
106
|
+
self.class.client.put_file(self.class.index_url, self.attachment_path, mime_type, self.to_xml)
|
107
|
+
else
|
108
|
+
self.class.client.put(self.edit_url || self.class.index_url, self.to_xml)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Googletastic::Mixins::Attributes
|
2
|
+
attr_accessor :attributes
|
3
|
+
|
4
|
+
def attributes=(value)
|
5
|
+
return if value.nil?
|
6
|
+
attributes = value.dup
|
7
|
+
attributes.stringify_keys!
|
8
|
+
@attributes = attributes
|
9
|
+
attributes.each do |k, v|
|
10
|
+
respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise("unknown attribute: #{k}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def attribute_names
|
15
|
+
@attributes.keys.sort
|
16
|
+
end
|
17
|
+
|
18
|
+
def has_attribute?(attr_name)
|
19
|
+
@attributes.has_key?(attr_name.to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
hash = ""
|
24
|
+
attributes.each { |k,v| hash << " @#{k.to_s}=#{v.inspect}" }
|
25
|
+
"#<#{self.class.to_s}#{hash}/>"
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Googletastic::Mixins::Finders
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def first(*args)
|
9
|
+
find(:first, *args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def last(*args)
|
13
|
+
find(:last, *args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def all(*args)
|
17
|
+
find(:all, *args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def find(*args)
|
21
|
+
options = args.extract_options!
|
22
|
+
|
23
|
+
case args.first
|
24
|
+
when :first then find_initial(options)
|
25
|
+
when :last then find_last(options)
|
26
|
+
when :all then find_every(options)
|
27
|
+
else find_from_ids(args, options)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_initial(options)
|
32
|
+
find_by_api(options.merge({:limit => 1})).first
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_last(options)
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_every(options)
|
40
|
+
find_by_api(options)
|
41
|
+
end
|
42
|
+
|
43
|
+
def find_from_ids(ids, options)
|
44
|
+
expects_array = ids.first.kind_of?(Array)
|
45
|
+
return ids.first if expects_array && ids.first.empty?
|
46
|
+
|
47
|
+
ids = ids.flatten.compact.uniq
|
48
|
+
|
49
|
+
case ids.size
|
50
|
+
when 0
|
51
|
+
raise "Couldn't find #{self.to_s} without an ID"
|
52
|
+
when 1
|
53
|
+
result = find_one(ids.first, options)
|
54
|
+
expects_array ? [ result ] : result
|
55
|
+
else
|
56
|
+
find_some(ids, options)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def find_one(id, options)
|
61
|
+
options[:id] = id
|
62
|
+
find_by_api(options).first
|
63
|
+
end
|
64
|
+
|
65
|
+
def find_some(ids, options)
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
def find_by_api(options)
|
70
|
+
url = build_url(options)
|
71
|
+
agent = options.has_key?(:client) ? options[:client] : client
|
72
|
+
data = agent.get(url)
|
73
|
+
return data if options.has_key?(:raw) and options[:raw] == true
|
74
|
+
records = unmarshall(Nokogiri::XML(data.body)).collect {|r| r.response = data; r}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Googletastic::Mixins::Namespaces
|
2
|
+
|
3
|
+
NAMESPACES = {
|
4
|
+
"gphoto" => "http://schemas.google.com/photos/2007",
|
5
|
+
"media" => "http://search.yahoo.com/mrss",
|
6
|
+
"openSearch" => "http://a9.com/-/spec/opensearchrss/1.0/",
|
7
|
+
"docs" => "http://schemas.google.com/docs/2007",
|
8
|
+
"atom" => "http://www.w3.org/2005/Atom",
|
9
|
+
"gd" => "http://schemas.google.com/g/2005",
|
10
|
+
"gAcl" => "http://schemas.google.com/acl/2007",
|
11
|
+
"exif" => "http://schemas.google.com/photos/exif/2007",
|
12
|
+
"app" => "http://www.w3.org/2007/app",
|
13
|
+
"gCal" => "http://schemas.google.com/gCal/2005",
|
14
|
+
"gContact" => "http://schemas.google.com/contact/2008",
|
15
|
+
"batch" => "http://schemas.google.com/gdata/batch"
|
16
|
+
} unless defined?(NAMESPACES)
|
17
|
+
|
18
|
+
def self.included(base)
|
19
|
+
base.extend ClassMethods
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
def ns(name)
|
24
|
+
NAMESPACES[name]
|
25
|
+
end
|
26
|
+
|
27
|
+
def ns_tag(name)
|
28
|
+
{name => ns(name)}
|
29
|
+
end
|
30
|
+
|
31
|
+
def ns_xml(*names)
|
32
|
+
first = names[0]
|
33
|
+
names.inject({}) do |hash, name|
|
34
|
+
xmlns = "xmlns"
|
35
|
+
xmlns << ":#{name}" unless name == first
|
36
|
+
hash[xmlns] = ns(name)
|
37
|
+
hash
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Googletastic::Mixins::Parsing
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
base.class_eval do
|
6
|
+
include InstanceMethods
|
7
|
+
|
8
|
+
# if you want to hook into these processes easily,
|
9
|
+
# without having to override/copy-paste/alias-method
|
10
|
+
# they're placed at convenient locations
|
11
|
+
# define them as a block
|
12
|
+
# base.define_callbacks :before_unmarshall, :during_unmarshall, :after_unmarshall
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
|
18
|
+
# cached nokogiri xml, false by default
|
19
|
+
attr_accessor :cache_parsed
|
20
|
+
|
21
|
+
def cache_parsed?
|
22
|
+
@cache_parsed == true
|
23
|
+
end
|
24
|
+
|
25
|
+
# cache xml as string?
|
26
|
+
attr_accessor :cache_result
|
27
|
+
|
28
|
+
def cache_result?
|
29
|
+
@cache_result == true
|
30
|
+
end
|
31
|
+
|
32
|
+
# implement in subclasses
|
33
|
+
def unmarshall(xml_records)
|
34
|
+
raise "Implemnent in subclasses"
|
35
|
+
end
|
36
|
+
|
37
|
+
# implement in subclasses
|
38
|
+
def marshall(records)
|
39
|
+
raise "Implemnent in subclasses"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module InstanceMethods
|
44
|
+
# cached nokogiri xml, false by default
|
45
|
+
attr_accessor :cache_parsed
|
46
|
+
|
47
|
+
def cache_parsed?
|
48
|
+
@cache_parsed != false || self.class.cache_result?
|
49
|
+
end
|
50
|
+
|
51
|
+
# cache xml as string?
|
52
|
+
attr_accessor :cache_result
|
53
|
+
|
54
|
+
def cache_result?
|
55
|
+
@cache_result != false || self.class.cache_result?
|
56
|
+
end
|
57
|
+
|
58
|
+
def from_xml(xml)
|
59
|
+
self.class.unmarshall(xml)
|
60
|
+
end
|
61
|
+
|
62
|
+
def after_parse(parsed)
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Googletastic::Mixins::Requesting
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
base.class_eval do
|
6
|
+
include InstanceMethods
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def valid_queries
|
13
|
+
{
|
14
|
+
:limit => "max-result",
|
15
|
+
:offset => "start-index",
|
16
|
+
:start => "start-index",
|
17
|
+
:end => "end-index",
|
18
|
+
:categories => "category",
|
19
|
+
:with => "q"
|
20
|
+
# :only => "fields", # only the fields we want!
|
21
|
+
# :fields => "fields"
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def urlify(url, params = {})
|
26
|
+
if params && !params.empty?
|
27
|
+
query = params.collect do |k,v|
|
28
|
+
v = v.to_json if %w{key startkey endkey}.include?(k.to_s)
|
29
|
+
"#{k}=#{CGI.escape(v.to_s)}"
|
30
|
+
end.join("&")
|
31
|
+
url = "#{url}?#{query}"
|
32
|
+
end
|
33
|
+
url
|
34
|
+
end
|
35
|
+
|
36
|
+
# helper method for google client
|
37
|
+
def client(name = self.client_class)
|
38
|
+
Googletastic.client_for(name.to_s.underscore)
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_url(options)
|
42
|
+
base = options.has_key?(:url) ? options[:url] : self.index_url
|
43
|
+
options[:url] = base
|
44
|
+
urlify(base, extract_params(options))
|
45
|
+
end
|
46
|
+
|
47
|
+
# http://code.google.com/apis/gdata/docs/2.0/reference.html#Queries
|
48
|
+
def extract_params(options)
|
49
|
+
queries = self.valid_queries
|
50
|
+
options.inject({}) do |converted, (key, value)|
|
51
|
+
real_key = queries[key]
|
52
|
+
if queries.has_key?(key)
|
53
|
+
next if self.respond_to?("valid_#{real_key}?") and !self["valid_#{real_key}?"]
|
54
|
+
value = self.send("convert_#{real_key}", value) if self.respond_to?("convert_#{real_key}")
|
55
|
+
converted[real_key] = value
|
56
|
+
end
|
57
|
+
converted
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# http://code.google.com/apis/gdata/docs/2.0/reference.html#PartialResponse
|
62
|
+
# link,entry(@gd:etag,id,updated,link[@rel='edit']))
|
63
|
+
# fields=entry[author/name='Lance']
|
64
|
+
def convert_fields(value)
|
65
|
+
value
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module InstanceMethods
|
70
|
+
|
71
|
+
def has_attachment?
|
72
|
+
!self.attachment_path.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
def client
|
76
|
+
self.class.client
|
77
|
+
end
|
78
|
+
|
79
|
+
def mime_type
|
80
|
+
return "" if has_attachment?
|
81
|
+
return "" #TODO
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|