googletastic 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +2 -0
- data/Rakefile +0 -3
- data/lib/googletastic.rb +3 -2
- data/lib/googletastic/analytics.rb +34 -0
- data/lib/googletastic/app_engine.rb +4 -1
- data/lib/googletastic/apps.rb +62 -0
- data/lib/googletastic/ext/pretty_print.xsl +1 -1
- data/lib/googletastic/ext/xml.rb +29 -0
- data/lib/googletastic/form.rb +43 -6
- data/lib/googletastic/helpers.rb +0 -1
- data/lib/googletastic/spreadsheet.rb +6 -2
- data/spec/googletastic/form_spec.rb +17 -14
- metadata +4 -2
data/README.textile
CHANGED
@@ -38,6 +38,8 @@ If this is for Google Apps, you must go to Google Docs Settings in the Admin Pan
|
|
38
38
|
h2. Top 10 Things I Need the Google Data API to Have
|
39
39
|
|
40
40
|
"Submit Your Ideas to Google!!!":http://productideas.appspot.com/#15/e=220cb&t=2192a&f=22150
|
41
|
+
http://productideas.appspot.com/#15/e=220cb&t=cabc1
|
42
|
+
|
41
43
|
|
42
44
|
# Partial Responses for everything.
|
43
45
|
So we can get only the data we need. This way we can poll our google docs for any changes and run processes on them.
|
data/Rakefile
CHANGED
@@ -17,9 +17,6 @@ def files(path, from = __FILE__, &block)
|
|
17
17
|
Dir.glob(File.join(File.dirname(from), path)) {|file| yield file}
|
18
18
|
end
|
19
19
|
|
20
|
-
APP_ROOT = defined?(RAILS_ROOT) ? RAILS_ROOT : File.dirname(__FILE__)
|
21
|
-
RAILS_ROOT = File.dirname(__FILE__)
|
22
|
-
|
23
20
|
# http://docs.rubygems.org/read/chapter/20
|
24
21
|
spec = Gem::Specification.new do |s|
|
25
22
|
s.name = "googletastic"
|
data/lib/googletastic.rb
CHANGED
@@ -9,6 +9,7 @@ require 'active_support'
|
|
9
9
|
require 'active_record'
|
10
10
|
require 'gdata'
|
11
11
|
require 'liquid'
|
12
|
+
require 'mechanize'
|
12
13
|
|
13
14
|
GOOGLETASTIC_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(GOOGLETASTIC_ROOT)
|
14
15
|
|
@@ -58,7 +59,7 @@ end
|
|
58
59
|
|
59
60
|
module Googletastic
|
60
61
|
# :stopdoc:
|
61
|
-
VERSION = '0.0.
|
62
|
+
VERSION = '0.0.3'
|
62
63
|
# :startdoc
|
63
64
|
class << self; attr_accessor :keys, :clients, :options; end
|
64
65
|
|
@@ -69,7 +70,7 @@ module Googletastic
|
|
69
70
|
raise "Sorry, you must have #{config_file}" unless File.exists?(config_file)
|
70
71
|
self.keys = YAML.load_file(config_file).symbolize_keys
|
71
72
|
end
|
72
|
-
|
73
|
+
|
73
74
|
def self.client_for(model)
|
74
75
|
self.clients ||= {}
|
75
76
|
model = model.to_sym
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Googletastic::Analytics
|
2
|
+
# https://www.google.com/accounts/ServiceLoginBox?service=analytics&nui=1&hl=en-US&continue=https://www.google.com/analytics/settings/?et=reset&hl=en&et=reset&hl=en-US
|
3
|
+
|
4
|
+
def create(options = {})
|
5
|
+
agent = WWW::Mechanize.new
|
6
|
+
agent.cookie_jar.load(options[:cookies])
|
7
|
+
scid = "15601175" # one of the options options[:profile]
|
8
|
+
form.fields_with("scid").first.value = scid
|
9
|
+
next_page = agent.submit(form)
|
10
|
+
new_analytics = "https://www.google.com/analytics/settings/add_profile?scid=#{scid}"
|
11
|
+
# ucpr_protocol
|
12
|
+
form = next_page.forms_with(:name => "mform").first
|
13
|
+
form.ucpr_url = options[:domain]
|
14
|
+
tracking = agent.submit(form)
|
15
|
+
tracking_code = tracking.parser.xpath("//script").to_s.match(/getTracker\(["|']([^"|']+)["|']\)/).captures.first
|
16
|
+
end
|
17
|
+
|
18
|
+
# returns an array of account names you can use
|
19
|
+
def login(options = {})
|
20
|
+
agent = WWW::Mechanize.new
|
21
|
+
url = "https://www.google.com/accounts/ServiceLoginBox?service=analytics&nui=1&hl=en-US&continue=https://www.google.com/analytics/settings/?et=reset&hl=en&et=reset&hl=en-US"
|
22
|
+
page = agent.get(url)
|
23
|
+
login_form = page.forms.first
|
24
|
+
login_form.Email = options[:email]
|
25
|
+
login_form.Passwd = options[:password]
|
26
|
+
|
27
|
+
# agent.cookie_jar.save_as("something.yml")
|
28
|
+
|
29
|
+
next_page = agent.submit(login_form)
|
30
|
+
home = next_page.links.first.click
|
31
|
+
|
32
|
+
form = home.forms_with("account_selector").first
|
33
|
+
end
|
34
|
+
end
|
@@ -83,6 +83,9 @@ module GData
|
|
83
83
|
output.write("#{password}\n")
|
84
84
|
end
|
85
85
|
while ((str = input.gets) != nil)
|
86
|
+
if str =~ /Checking if new version is ready to serve/
|
87
|
+
|
88
|
+
end
|
86
89
|
puts str
|
87
90
|
end
|
88
91
|
rescue Exception => e
|
@@ -98,6 +101,6 @@ module GData
|
|
98
101
|
end
|
99
102
|
end
|
100
103
|
|
101
|
-
|
104
|
+
class Googletastic::AppEngine < Googletastic::Base
|
102
105
|
|
103
106
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class Googletastic::Apps < Googletastic::Base
|
2
|
+
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def new_url
|
6
|
+
"http://www.google.com/a/cpanel/domain/new"
|
7
|
+
end
|
8
|
+
|
9
|
+
# follow_meta_refresh for FORMS!!!
|
10
|
+
# might have to save cookiejar between requests as captcha is presented
|
11
|
+
# domain, first_name, last_name, email, phone, country, username, password, password_confirmation
|
12
|
+
def create(options = {})
|
13
|
+
# precaptcha and postcaptcha
|
14
|
+
agent = WWW::Mechanize.new
|
15
|
+
page = agent.get(new_url)
|
16
|
+
# fill out domain name (must already have)
|
17
|
+
form = page.forms_with("domainEntry").first
|
18
|
+
form.radiobuttons.first.check
|
19
|
+
form.fields_with("existingDomain").first.value = options[:domain]
|
20
|
+
second_page = agent.submit(form)
|
21
|
+
# fill out profile
|
22
|
+
form = second_page.forms.first
|
23
|
+
form.firstName = options[:first_name]
|
24
|
+
form.lastName = options[:last_name]
|
25
|
+
form.email = options[:email]
|
26
|
+
form.phone = options[:phone]
|
27
|
+
#form.jobTitle = ""
|
28
|
+
#form.orgName = ""
|
29
|
+
# default value is "US"
|
30
|
+
form.country = options[:country] || "US"
|
31
|
+
# form.orgType = ""
|
32
|
+
# form.orgSize
|
33
|
+
# DO YOU AGREE???
|
34
|
+
form.checkboxes_with("domainAdminCheck").first.check
|
35
|
+
# third page must be filled out within about a minute it seems
|
36
|
+
third_page = agent.submit(form)
|
37
|
+
form = last_page.forms.first
|
38
|
+
# sample CAPTCHA URL (or "format=audio")
|
39
|
+
# form.captchaToken
|
40
|
+
# https://www.google.com/a/cpanel/captcha?format=image&captchaToken=crazy-token
|
41
|
+
form.captchaAnswer = "progshi"
|
42
|
+
form.newUserName = options[:username]
|
43
|
+
# minimum 6 chars
|
44
|
+
form.fields_with("newPassword.alpha").first.value = options[:password]
|
45
|
+
form.fields_with("newPassword.beta").first.value = options[:password_confirmation]
|
46
|
+
last_page = agent.submit(form)
|
47
|
+
|
48
|
+
# now you have your account
|
49
|
+
form = last_page.forms_with(:action => "VerifyDomainOwnership").first
|
50
|
+
form.radiobuttons_with(:value => "htmlVer").first.check
|
51
|
+
|
52
|
+
verify_page = agent.submit(form)
|
53
|
+
form = verify_page.forms_with(:action => "VerifyDomainOwnership") # or "id => 'verificationPages'"
|
54
|
+
# upload file "googlehostedservice.html" with special text, and let google know it's okay
|
55
|
+
special_text = verify_page.parser.xpath("//span[@class='callout']").first.text
|
56
|
+
# upload to http://tinker.heroku.com/googlehostedservice.html
|
57
|
+
apps_home = agent.submit(form)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
data/lib/googletastic/ext/xml.rb
CHANGED
@@ -2,9 +2,38 @@
|
|
2
2
|
module Googletastic::PrettyPrint
|
3
3
|
class << self
|
4
4
|
def xml(xml)
|
5
|
+
return "" if xml.nil?
|
6
|
+
raise "will throw segmentation error if not an Nokogiri::XML::Document" unless xml.is_a?(Nokogiri::XML::Document)
|
5
7
|
pretty_printer = File.join(File.dirname(__FILE__), "pretty_print.xsl")
|
6
8
|
xsl = Nokogiri::XSLT(IO.read(pretty_printer))
|
7
9
|
xsl.transform(xml).children.to_xml.gsub(/\t/, "")
|
8
10
|
end
|
11
|
+
|
12
|
+
def pretty_html(html)
|
13
|
+
return "" if html.nil?
|
14
|
+
raise "will throw segmentation error if not an Nokogiri::HTML::Document" unless xml.is_a?(Nokogiri::HTML::Document)
|
15
|
+
pretty_printer = File.join(File.dirname(__FILE__), "pretty_print.xsl")
|
16
|
+
xsl = Nokogiri::XSLT(IO.read(pretty_printer))
|
17
|
+
# they get erased
|
18
|
+
top_level_attributes = []
|
19
|
+
elements = html.is_a?(Nokogiri::XML::NodeSet) ? html : [html]
|
20
|
+
puts "ELEMENTS: #{elements[0].class}"
|
21
|
+
elements.each do |element|
|
22
|
+
top_level_attributes.push(element.attributes)
|
23
|
+
end
|
24
|
+
top_level_attributes.reverse!
|
25
|
+
puts "??"
|
26
|
+
children = xsl.transform(html)
|
27
|
+
#.children
|
28
|
+
# reapply top-level attributes
|
29
|
+
puts "TOP ATTRIBUTES: #{top_level_attributes.inspect}"
|
30
|
+
children.each do |child|
|
31
|
+
attributes = top_level_attributes.pop
|
32
|
+
attributes.each do |k,v|
|
33
|
+
child[k] = v
|
34
|
+
end unless attributes.nil?
|
35
|
+
end
|
36
|
+
children.to_html.gsub(/\t/, "")
|
37
|
+
end
|
9
38
|
end
|
10
39
|
end
|
data/lib/googletastic/form.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# from http://github.com/mocra/custom_google_forms
|
2
2
|
class Googletastic::Form < Googletastic::Base
|
3
3
|
|
4
|
-
|
4
|
+
FORM_KEY_EXPRESSION = /formkey["|']\s*:["|']\s*([^"|']+)"/ unless defined?(FORM_KEY_EXPRESSION)
|
5
|
+
|
6
|
+
attr_accessor :title, :body, :redirect_to, :form_key, :form_only, :authenticity_token
|
5
7
|
|
6
8
|
def body(options = {}, &block)
|
7
9
|
@body ||= get(options, &block)
|
@@ -36,7 +38,11 @@ class Googletastic::Form < Googletastic::Base
|
|
36
38
|
|
37
39
|
def unmarshall(xml)
|
38
40
|
records = xml.xpath("//atom:entry", ns_tag("atom")).collect do |record|
|
39
|
-
id = record.xpath("atom:id", ns_tag("atom")).first.text.gsub("http://spreadsheets.google.com/feeds/spreadsheets/", "")
|
41
|
+
#id = record.xpath("atom:id", ns_tag("atom")).first.text.gsub("http://spreadsheets.google.com/feeds/spreadsheets/", "")
|
42
|
+
id = record.xpath("atom:link[@rel='alternate']", ns_tag("atom")).first
|
43
|
+
if id
|
44
|
+
id = id["href"].gsub("http://spreadsheets.google.com/ccc?key=", "")
|
45
|
+
end
|
40
46
|
title = record.xpath("atom:title", ns_tag("atom")).first.text
|
41
47
|
created_at = record.xpath("atom:published", ns_tag("atom")).text
|
42
48
|
updated_at = record.xpath("atom:updated", ns_tag("atom")).text
|
@@ -53,13 +59,44 @@ class Googletastic::Form < Googletastic::Base
|
|
53
59
|
|
54
60
|
end
|
55
61
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
62
|
+
def get_form_key
|
63
|
+
begin
|
64
|
+
agent = WWW::Mechanize.new
|
65
|
+
# google wants recent browsers!
|
66
|
+
# http://docs.google.com/support/bin/answer.py?answer=37560&hl=en
|
67
|
+
agent.user_agent = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; ru-ru) AppleWebKit/533.2+ (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10"
|
68
|
+
url = "http://spreadsheets.google.com/"
|
69
|
+
# for spreadsheet, we need the domain!
|
70
|
+
if Googletastic.credentials[:domain]
|
71
|
+
url << "a/#{Googletastic.credentials[:domain]}/"
|
72
|
+
end
|
73
|
+
url << "ccc?key=#{self.id}&hl=en&pli=1"
|
74
|
+
login_form = agent.get(url).forms.first
|
75
|
+
login_form.Email = Googletastic.credentials[:username].split("@").first # don't want emails
|
76
|
+
login_form.Passwd = Googletastic.credentials[:password]
|
77
|
+
page = agent.submit(login_form)
|
78
|
+
match = page.body.match(FORM_KEY_EXPRESSION)
|
79
|
+
if page.meta.first and (page.title == "Redirecting" || match.nil?)
|
80
|
+
page = page.meta.first.click
|
81
|
+
match = page.body.match(FORM_KEY_EXPRESSION)
|
82
|
+
end
|
83
|
+
formkey = match.captures.first if match
|
84
|
+
rescue Exception => e
|
85
|
+
puts "ERROR: #{e.to_s}"
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def submit(form_key, params, options)
|
91
|
+
uri = URI.parse(submit_url)
|
59
92
|
req = Net::HTTP::Post.new("#{uri.path}?#{uri.query}")
|
60
93
|
req.form_data = params
|
61
94
|
response = Net::HTTP.new(uri.host).start {|h| h.request(req)}
|
62
|
-
response
|
95
|
+
if response.is_a?(Net::HTTPSuccess) || response.is_a?(GData::HTTP::Response)
|
96
|
+
unmarshall(Nokogiri::HTML(response.body), options).to_html
|
97
|
+
else
|
98
|
+
response.body
|
99
|
+
end
|
63
100
|
end
|
64
101
|
|
65
102
|
# get(:redirect => {:url => "/our-forms", :params => {:one => "hello"}})
|
data/lib/googletastic/helpers.rb
CHANGED
@@ -15,12 +15,16 @@ class Googletastic::Spreadsheet < Googletastic::Base
|
|
15
15
|
|
16
16
|
def unmarshall(xml)
|
17
17
|
records = xml.xpath("//atom:entry", ns_tag("atom")).collect do |record|
|
18
|
-
id = record.xpath("atom:id", ns_tag("atom")).first.text.gsub("http://spreadsheets.google.com/feeds/spreadsheets/", "")
|
18
|
+
#id = record.xpath("atom:id", ns_tag("atom")).first.text.gsub("http://spreadsheets.google.com/feeds/spreadsheets/", "")
|
19
|
+
id = record.xpath("atom:link[@rel='alternate']", ns_tag("atom")).first
|
20
|
+
if id
|
21
|
+
id = id["href"].gsub("http://spreadsheets.google.com/ccc?key=", "")
|
22
|
+
end
|
19
23
|
title = record.xpath("atom:title", ns_tag("atom")).first.text
|
20
24
|
content = record.xpath("atom:content", ns_tag("atom")).first.text
|
21
25
|
created_at = record.xpath("atom:published", ns_tag("atom")).text
|
22
26
|
updated_at = record.xpath("atom:updated", ns_tag("atom")).text
|
23
|
-
|
27
|
+
|
24
28
|
Googletastic::Spreadsheet.new(
|
25
29
|
:id => id,
|
26
30
|
:title => title,
|
@@ -8,7 +8,9 @@ describe Googletastic::Form do
|
|
8
8
|
|
9
9
|
describe "find" do
|
10
10
|
it "should list all spreadsheets as forms" do
|
11
|
-
|
11
|
+
Googletastic::Form.all.each do |form|
|
12
|
+
form.should be_an_instance_of(Googletastic::Form)
|
13
|
+
end
|
12
14
|
end
|
13
15
|
end
|
14
16
|
|
@@ -23,21 +25,22 @@ describe Googletastic::Form do
|
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
|
-
describe "
|
27
|
-
it "should
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
describe "mechanize" do
|
29
|
+
it "it should get the formkey via mechanize" do
|
30
|
+
form = Googletastic::Form.first
|
31
|
+
formkey = form.get_form_key
|
32
|
+
puts "FORMKEY: #{formkey}"
|
33
|
+
formkey.should_not be_nil
|
31
34
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
Nokogiri::HTML(form.body).xpath("//form").first["action"].should == "/my-generic-redirect"
|
35
|
+
|
36
|
+
it "should remove unnecessary html from form" do
|
37
|
+
form = Googletastic::Form.first
|
38
|
+
form.form_key = form.get_form_key
|
39
|
+
body = form.body
|
40
|
+
puts "BODY!: #{body.to_s}"
|
41
|
+
body.should_not == nil
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
45
|
+
|
43
46
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Lance Pollard
|
@@ -82,7 +82,9 @@ files:
|
|
82
82
|
- Rakefile
|
83
83
|
- lib/googletastic/access_rule.rb
|
84
84
|
- lib/googletastic/album.rb
|
85
|
+
- lib/googletastic/analytics.rb
|
85
86
|
- lib/googletastic/app_engine.rb
|
87
|
+
- lib/googletastic/apps.rb
|
86
88
|
- lib/googletastic/attendee.rb
|
87
89
|
- lib/googletastic/base.rb
|
88
90
|
- lib/googletastic/calendar.rb
|