googletastic 0.0.1 → 0.0.3
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 +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
|