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