Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

tried to add the ability to customize the form redirect, but because …

…marshalling was class methods, i tried doing tons of callbacks and dynamic stuff but it got too crazy and messing, so i just am parsing twice :/. REFACTOR LATER!
  • Loading branch information...
commit 085d12b3e30029ca0611d2b6df794fd8104e680e 1 parent 31622d9
@lancejpollard authored
View
65 Manifest
@@ -0,0 +1,65 @@
+README.textile
+Rakefile
+lib/googletastic
+lib/googletastic/access_rule.rb
+lib/googletastic/album.rb
+lib/googletastic/attendee.rb
+lib/googletastic/base.rb
+lib/googletastic/calendar.rb
+lib/googletastic/comment.rb
+lib/googletastic/document.rb
+lib/googletastic/event.rb
+lib/googletastic/form.rb
+lib/googletastic/group.rb
+lib/googletastic/helpers
+lib/googletastic/helpers/document.rb
+lib/googletastic/helpers/event.rb
+lib/googletastic/helpers/form.rb
+lib/googletastic/helpers.rb
+lib/googletastic/image.rb
+lib/googletastic/mixins
+lib/googletastic/mixins/attributes.rb
+lib/googletastic/mixins/finders.rb
+lib/googletastic/mixins/namespaces.rb
+lib/googletastic/mixins/service.rb
+lib/googletastic/mixins.rb
+lib/googletastic/person.rb
+lib/googletastic/thumbnail.rb
+lib/googletastic/youtube.rb
+lib/googletastic.rb
+spec/fixtures
+spec/fixtures/data
+spec/fixtures/data/basic.txt
+spec/fixtures/data/calendar.list.xml
+spec/fixtures/data/doc_as_html.html
+spec/fixtures/data/doc_as_html_html.html
+spec/fixtures/data/doclist.xml
+spec/fixtures/data/document.single.xml
+spec/fixtures/data/Doing business in the eMarketPlace.doc
+spec/fixtures/data/end.xml
+spec/fixtures/data/event.list.xml
+spec/fixtures/data/form.html
+spec/fixtures/data/group.list.xml
+spec/fixtures/data/person.list.xml
+spec/fixtures/data/photo.list.xml
+spec/fixtures/data/sample_upload.mp4
+spec/fixtures/models
+spec/fixtures/models/document.rb
+spec/fixtures/models/event.rb
+spec/fixtures/models/form.rb
+spec/fixtures/models/test_model.rb
+spec/googletastic
+spec/googletastic/access_rule_spec.rb
+spec/googletastic/album_spec.rb
+spec/googletastic/base_spec.rb
+spec/googletastic/calendar_spec.rb
+spec/googletastic/document_spec.rb
+spec/googletastic/event_spec.rb
+spec/googletastic/form_spec.rb
+spec/googletastic/group_spec.rb
+spec/googletastic/image_spec.rb
+spec/googletastic/person_spec.rb
+spec/googletastic/post_spec.rb
+spec/googletastic/youtube_spec.rb
+spec/spec.opts
+spec/spec_helper.rb
View
4 README.textile
@@ -10,8 +10,8 @@ sudo gem install googletastic
h3. Installing from Source
-<code>
+<pre><code>
git clone git@github.com:viatropos/googletastic.git
cd googletastic
rake install
-</code>
+</code></pre>
View
26 Rakefile
@@ -15,16 +15,19 @@ end
APP_ROOT = File.dirname(__FILE__)
+# http://docs.rubygems.org/read/chapter/20
spec = Gem::Specification.new do |s|
s.name = "googletastic"
+ s.author = "Lance Pollard"
s.version = Googletastic::VERSION
s.date = "Mon Mar 22 20:12:47 -0700 2010"
s.summary = "More than Syncing Rails Apps with the Google Data API"
- s.email = "lancejpollard@gmail.com"
s.homepage = "http://github.com/viatropos/googletastic"
+ s.email = "lancejpollard@gmail.com"
s.description = "Googletastic: A New Way of Googling"
s.has_rdoc = true
- s.authors = ["Lance Pollard"]
+ s.rubyforge_project = "googletastic"
+ s.platform = Gem::Platform::RUBY
s.files = %w(README.textile Rakefile) +
Dir["{googletastic,lib,spec}/**/*"] -
Dir["spec/tmp"]
@@ -38,12 +41,25 @@ end
desc "Create .gemspec file (useful for github)"
task :gemspec do
- filename = "#{spec.name}.gemspec"
- File.open(filename, "w") do |f|
+ File.open("#{spec.name}.gemspec", "w") do |f|
f.puts spec.to_ruby
end
end
+desc "Build the gem into the current directory"
+task :gem => :gemspec do
+ `gem build #{spec.name}.gemspec`
+end
+
+desc "Print a list of the files to be put into the gem"
+task :manifest => :clean do
+ File.open("Manifest", "w") do |f|
+ spec.files.each do |file|
+ f.puts file
+ end
+ end
+end
+
Rake::GemPackageTask.new(spec) do |pkg|
pkg.gem_spec = spec
end
@@ -70,7 +86,7 @@ Rake::RDocTask.new do |rdoc|
files = ["README.textile", "lib/**/*.rb"]
rdoc.rdoc_files.add(files)
rdoc.main = "README.textile"
- rdoc.title = "Googletastic: A Ruby Gem"
+ rdoc.title = "Googletastic: A New Way of Googling"
end
desc "Run the rspec"
View
40 lib/googletastic.rb
@@ -9,6 +9,19 @@
GOOGLETASTIC_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(GOOGLETASTIC_ROOT)
+class Module
+ def include_class_and_instance_modules
+ self.module_eval <<-eos
+ def self.included(base)
+ base.extend(ClassMethods)
+ base.class_eval do
+ include InstanceMethods
+ end
+ end
+ eos
+ end
+end
+
def require_local(path, from = __FILE__)
files(path, from) {|file| require file}
end
@@ -25,7 +38,7 @@ module Googletastic
# :stopdoc:
VERSION = '0.0.1'
# :startdoc
- class << self; attr_accessor :keys, :clients end
+ class << self; attr_accessor :keys, :clients, :options; end
def self.credentials
return self.keys if self.keys
@@ -43,18 +56,35 @@ def self.client_for(model)
client.clientlogin(credentials[:username], credentials[:password])
self.clients[model] = client
end
+
+ def self.options_for(klass, value = {})
+ self.options ||= {}
+ klass = klass.is_a?(Class) ? klass : klass.class
+ name = klass.to_s.underscore.downcase.to_sym
+ if value and !value.blank?
+ value.symbolize_keys!
+ self.options[name] = value
+ else
+ self.options[name] ||= {}
+ end
+ self.options[name]
+ end
+
+ def self.[](value)
+ self.options_for(value)
+ end
end
# main includes
require File.dirname(__FILE__) + '/googletastic/mixins'
require File.dirname(__FILE__) + '/googletastic/base'
-files("googletastic/mixins/*") do |file|
- Googletastic::Base.send(:include, "Googletastic::Mixins::#{File.basename(file, '.rb').camelize}".constantize)
-end
-
require_local "googletastic/*"
+#files("googletastic/mixins/*") do |file|
+# Googletastic::Base.send(:include, "Googletastic::Mixins::#{File.basename(file, '.rb').camelize}".constantize)
+#end
+
if defined?(ActiveRecord::Base)
ActiveRecord::Base.send(:include, Googletastic::Helpers)
end
View
102 lib/googletastic/base.rb
@@ -1,88 +1,40 @@
class Googletastic::Base < Hash
- class << self # Class methods
-
- # override if you name classes differently
- def client_class
- self.to_s.split("::").last
- end
-
- def valid_queries
- {
- :limit => "max-result",
- :offset => "start-index",
- :start => "start-index",
- :end => "end-index",
- :categories => "category",
- :with => "q"
- }
- end
- end
-
- attr_accessor :id, :acl
+ include Googletastic::Mixins::Namespaces
+ include Googletastic::Mixins::Attributes
+ include Googletastic::Mixins::Requesting
+ include Googletastic::Mixins::Parsing
+ include Googletastic::Mixins::Finders
+ include Googletastic::Mixins::Actions
+
+ attr_accessor :id
+ attr_accessor :acl
attr_accessor :attachment_path # for docs, images...
+ # classes/records it is synced with
+ attr_accessor :synced_with
- def save
- create_or_update
- end
-
- def destroy
-
- end
-
- def clone
-
- end
-
- def reload(options = nil)
- end
-
- def new_record?
- self.id.nil?
+ def synced_with
+ @synced_with ||= []
+ @synced_with
end
- def to_xml
- self.class.marshall(self)
- end
-
- def view_url
- end
-
- def download_url(format = "pdf")
- end
-
- def edit_url
- end
-
- def has_attachment?
- !self.attachment_path.nil?
- end
-
- def mime_type
- return "" if has_attachment?
- return "" #TODO
+ def initialize(attributes = {})
+ return if attributes.nil?
+ if (attributes.has_key?(:parsed))
+ # haven't figured out how to do callbacks correctly
+ after_parse(attributes[:parsed])
+ attributes.delete(:parsed)
+ end
+ self.attributes = attributes
end
- private
- def create_or_update
- result = new_record? ? create : update
- result != false
- end
+ class << self # Class methods
- def create
- if has_attachment?
- self.class.client.post_file(self.class.feed_url, self.attachment_path, mime_type, self.to_xml)
- else
- self.class.client.post(self.class.feed_url, self.to_xml)
- end
+ # override if you name classes differently
+ def client_class
+ self.to_s.split("::").last
end
- def update(attribute_names = @attributes.keys)
- if has_attachment?
- self.class.client.put_file(self.class.feed_url, self.attachment_path, mime_type, self.to_xml)
- else
- self.class.client.put(self.edit_url || self.class.feed_url, self.to_xml)
- end
- end
+ end
end
View
86 lib/googletastic/form.rb
@@ -1,10 +1,16 @@
# from http://github.com/mocra/custom_google_forms
class Googletastic::Form < Googletastic::Base
- attr_accessor :title, :body
+ attr_accessor :title, :body, :raw, :redirect_to
+
+ def redirect_to=(value)
+ self.class.add_redirect(raw, value)
+ end
class << self
+ attr_accessor :redirect_key
+
def form_url(id)
"http://spreadsheets.google.com/viewform?formkey=#{id}"
end
@@ -18,13 +24,17 @@ def unmarshall(html)
id = html.xpath("//form").first["action"].to_s.gsub("http://spreadsheets.google.com/formResponse?formkey=", "")
title = html.xpath("//h1[@class='ss-form-title']").first.text
body = content(html)
- Googletastic::Form.new(
+ form = Googletastic::Form.new(
:id => id,
- :title => title,
+ :title => title,
:body => body.to_html
)
end
+ def marshall(record)
+ return record.inspect
+ end
+
def fetch_form_page(id)
uri = URI.parse(form_url(id))
req = Net::HTTP::Get.new("#{uri.path}?#{uri.query}")
@@ -32,15 +42,38 @@ def fetch_form_page(id)
return response.is_a?(Net::HTTPSuccess) ? unmarshall(Nokogiri::HTML(response.body)) : response
end
- def content(form_html, &block)
- doc = form_html
- #google_form = form.xpath("//form").first.unlink
- doc.xpath("//*[@class='ss-footer']").first.unlink
- doc.xpath("//div[@class='ss-form-container']").first.unlink
+ def content(doc, &block)
+ doc.xpath("//form").first.unlink
end
end
+ def redirect=(value)
+ doc = Nokogiri::HTML(body)
+ form = doc.xpath("//form").first
+
+ original_action = form["action"]
+ # don't have time to build this correctly
+ new_action = "#{value}"
+ form["action"] = new_action
+
+ hidden_node = doc.create_element('input')
+ hidden_node["name"] = "google_form"
+ hidden_node["type"] = "hidden"
+ hidden_node["value"] = original_action
+ form.children.first.add_previous_sibling(hidden_node)
+
+ put_node = doc.create_element('input')
+ put_node["name"] = "_method"
+ put_node["type"] = "hidden"
+ put_node["value"] = "put"
+ form.children.first.add_previous_sibling(put_node)
+
+ self.body = form.to_html
+
+ new_action
+ end
+
def submit(google_form_action, params)
uri = URI.parse(google_form_action)
req = Net::HTTP::Post.new("#{uri.path}?#{uri.query}")
@@ -49,35 +82,14 @@ def submit(google_form_action, params)
response
end
- def to_rails_form
- form = Nokogiri::HTML(self.body)
- put_node = form.create_element('input')
- put_node["name"] = "_method"
- put_node["type"] = "hidden"
- put_node["value"] = "put"
- form.children.first.add_previous_sibling(put_node)
-
- css_node = form.create_element('link')
- css_node["href"] = "/stylesheets/reset.css"
- css_node["rel"] = "stylesheet"
- css_node["type"] = "text/css"
- form.xpath("//head").first.add_child(css_node)
-
- css_node = doc.create_element('link')
- css_node["href"] = "/stylesheets/style.css"
- css_node["rel"] = "stylesheet"
- css_node["type"] = "text/css"
- doc.xpath("//head").first.add_child(css_node)
-
- footer = doc.create_element('div')
- footer["id"] = "footer"
- doc.xpath("//body").first.add_child(footer)
-
- analytics = doc.create_element('div')
- #analytics.inner_html = render_to_string :partial => 'shared/google_analytics'
- doc.xpath("//body").first.add_child(analytics)
- doc = doc.xpath("//div[@class='ss-form-container']").first
- doc
+ def validate_formkey_is_valid
+ case fetch_form_page
+ when Net::HTTPSuccess
+ true
+ else
+ errors.add(:formkey, "is not a valid Google Forms key or URL or error connecting to Google")
+ false
+ end
end
def view_url
View
7 lib/googletastic/helpers.rb
@@ -2,10 +2,13 @@ module Googletastic::Helpers
def self.included(base)
base.extend(ClassMethods)
end
-
+
+
module ClassMethods
-
+
def googletastic(model, options = {})
+ Googletastic.options_for(self, options)
+
include ("Googletastic::Helpers::#{model.to_s.camelize}").constantize
end
View
1  lib/googletastic/helpers/event.rb
@@ -11,6 +11,7 @@ module InstanceMethods
attr_accessor :google_doc
def hello
+# puts _googletastic_options.inspect + " OPTIONS!"
end
def google_doc
View
89 lib/googletastic/helpers/form.rb
@@ -1,29 +1,78 @@
module Googletastic::Helpers::Form
+ # class options for Form:
+ # as: property name for the googletastic form
+ # foreign_key: foreign key to reference it, defaults to "as"_id
+ # sync: the properties you want to sync with the form
+ # redirect_to: where to redirect the forms submission
def self.included(base)
- base.extend ClassMethods
- base.class_eval do
- include InstanceMethods
- end
- end
-
- module InstanceMethods
- attr_accessor :g_form
-
- def hello
- end
-
- def google_doc
- @google_doc ||= Googletastic::DocList.find(self.remote_id)
- @google_doc
+ # defaults
+ options = Googletastic[base]
+ options[:as] ||= "google_form"
+ options[:foreign_key] ||= "#{options[:as]}_id"
+ if options.has_key?(:sync)
+ if options[:sync].is_a?(Symbol)
+ options[:sync] == {options[:sync] => options[:sync]}
+ elsif options[:sync].is_a?(String)
+ options[:sync] = {options[:sync].to_sym => options[:sync].to_sym}
+ elsif options[:sync].is_a?(Array)
+ options[:sync] = options[:sync].collect { |v| {v.to_sym => v.to_sym} }
+ else
+ options[:sync].symbolize_keys!
+ end
end
- end
-
- module ClassMethods
+
+ # eval
+ base.class_eval <<-"end_eval", __FILE__, __LINE__
+ def find_via_#{Googletastic[self][:as]}(*args)
+ google_records = Googletastic::Form.find(*args)
+ foreign_keys = google_records.collect { |record| record.id }
+ records = find(foreign_keys)
+ record_keys = records.collect do |record|
+ record["\#\{Googletastic[self][:foreign_key]\}"]
+ end
+ foreign_keys.each do |key|
+ if !record_keys.include?(key)
+ records << self.class.new(:"\#\{Googletastic[self][:foreign_key]\}" => key)
+ end
+ end
+ records
+ end
+ end_eval
- def g_form
+ base.class_eval do
+ attr_accessor :"#{Googletastic[self][:as]}"
+ if base.is_a?(ActiveRecord::Base)
+ before_validation :clean_form_key
+ validates_presence_of :form_key
+ validates_uniqueness_of :form_key
+ validate :validate_formkey_is_valid
+ before_save :sync_with_google
+
+ def sync_with_google
+ Googletastic[self].each do |mine, theirs|
+ self[mine] = self["#{Googletastic[self][:method_name]}"][theirs]
+ end if Googletastic[self].has_key?(:sync)
+ end
+
+ def clean_form_key
+ if self["#{Googletastic[self][:method_name]}"].form_key =~ /=(.*)$/
+ #{Googletastic[self][:method_name]}.form_key = $1
+ end
+ end
+
+ def validate_form_key_is_valid
+ case fetch_form_page
+ when Net::HTTPSuccess
+ true
+ else
+ errors.add(:form_key, "is not a valid Google Forms key or URL or error connecting to Google")
+ false
+ end
+ end
+ end
end
-
end
+
end
View
51 lib/googletastic/mixins/actions.rb
@@ -0,0 +1,51 @@
+module Googletastic::Mixins::Actions
+ include_class_and_instance_modules
+
+ module ClassMethods
+
+ end
+
+ module InstanceMethods
+ def save
+ create_or_update
+ end
+
+ def destroy
+
+ end
+
+ def clone
+
+ end
+
+ def reload(options = nil)
+ end
+
+ def new_record?
+ self.id.nil?
+ end
+
+ private
+ def create_or_update
+ result = new_record? ? create : update
+ result != false
+ end
+
+ def create
+ if has_attachment?
+ self.class.client.post_file(self.class.feed_url, self.attachment_path, mime_type, self.to_xml)
+ else
+ self.class.client.post(self.class.feed_url, self.to_xml)
+ end
+ end
+
+ def update(attribute_names = @attributes.keys)
+ if has_attachment?
+ self.class.client.put_file(self.class.feed_url, self.attachment_path, mime_type, self.to_xml)
+ else
+ self.class.client.put(self.edit_url || self.class.feed_url, self.to_xml)
+ end
+ end
+ end
+
+end
View
4 lib/googletastic/mixins/attributes.rb
@@ -1,10 +1,6 @@
module Googletastic::Mixins::Attributes
attr_accessor :attributes
- def initialize(attributes = {})
- self.attributes = attributes unless attributes.nil?
- end
-
def attributes=(value)
return if value.nil?
attributes = value.dup
View
72 lib/googletastic/mixins/parsing.rb
@@ -0,0 +1,72 @@
+module Googletastic::Mixins::Parsing
+
+ def self.included(base)
+ base.extend(ClassMethods)
+ base.class_eval do
+ include InstanceMethods
+
+ # if you want to hook into these processes easily,
+ # without having to override/copy-paste/alias-method
+ # they're placed at convenient locations
+ # define them as a block
+ # base.define_callbacks :before_unmarshall, :during_unmarshall, :after_unmarshall
+ end
+ end
+
+ module ClassMethods
+
+ # cached nokogiri xml, false by default
+ attr_accessor :cache_parsed
+
+ def cache_parsed?
+ @cache_parsed == true
+ end
+
+ # cache xml as string?
+ attr_accessor :cache_result
+
+ def cache_result?
+ @cache_result == true
+ end
+
+ # implement in subclasses
+ def unmarshall(xml_records)
+ raise "Implemnent in subclasses"
+ end
+
+ # implement in subclasses
+ def marshall(records)
+ raise "Implemnent in subclasses"
+ end
+ end
+
+ module InstanceMethods
+ # cached nokogiri xml, false by default
+ attr_accessor :cache_parsed
+
+ def cache_parsed?
+ @cache_parsed != false || self.class.cache_result?
+ end
+
+ # cache xml as string?
+ attr_accessor :cache_result
+
+ def cache_result?
+ @cache_result != false || self.class.cache_result?
+ end
+
+ def to_xml
+ self.class.marshall(self)
+ end
+
+ def from_xml(xml)
+ self.class.unmarshall(xml)
+ end
+
+ def after_parse(parsed)
+
+ end
+
+ end
+
+end
View
46 lib/googletastic/mixins/service.rb → lib/googletastic/mixins/requesting.rb
@@ -1,10 +1,24 @@
-module Googletastic::Mixins::Service
+module Googletastic::Mixins::Requesting
def self.included(base)
base.extend(ClassMethods)
+ base.class_eval do
+ include InstanceMethods
+ end
end
module ClassMethods
+
+ def valid_queries
+ {
+ :limit => "max-result",
+ :offset => "start-index",
+ :start => "start-index",
+ :end => "end-index",
+ :categories => "category",
+ :with => "q"
+ }
+ end
def urlify(url, params = {})
if params && !params.empty?
@@ -32,16 +46,6 @@ def build_url(options)
urlify(base, extract_params(options))
end
- # implement in subclasses
- def unmarshall(xml_records)
- raise "Implemnent in subclasses"
- end
-
- # implement in subclasses
- def marshall(records)
- raise "Implemnent in subclasses"
- end
-
# http://code.google.com/apis/gdata/docs/2.0/reference.html#Queries
def extract_params(options)
options.inject({}) do |converted, (key, value)|
@@ -50,4 +54,24 @@ def extract_params(options)
end
end
end
+
+ module InstanceMethods
+ def view_url
+ end
+
+ def download_url(format = "pdf")
+ end
+
+ def edit_url
+ end
+
+ def has_attachment?
+ !self.attachment_path.nil?
+ end
+
+ def mime_type
+ return "" if has_attachment?
+ return "" #TODO
+ end
+ end
end
View
5 spec/fixtures/models/form.rb
@@ -1,3 +1,6 @@
class Form < Googletastic::TestModel
- googletastic :form, :form_only => true, :action => /asdf/
+ googletastic :form,
+ :remote_id => :google_form_id,
+ :sync => :title,
+ :redirect_to => "/forms/:id"
end
View
2  spec/googletastic/event_spec.rb
@@ -28,7 +28,7 @@
end
it "should successfully add to a model via 'googletastic :event'" do
- Event.new
+ Event.new.hello
end
end
View
8 spec/googletastic/form_spec.rb
@@ -6,14 +6,10 @@
@form = Form.new
end
- it "should include the modules properly" do
- @form.hello
- end
-=begin
it "should strip the html page down to a form (unmarshall)" do
html = Nokogiri::HTML(IO.read(File.join(FIXTURES_DIR, "data/form.html")))
form = Googletastic::Form.unmarshall(html)
- puts form.to_xml
+ form.redirect = "/forms/my-custom-id"
+ puts form.body
end
-=end
end
Please sign in to comment.
Something went wrong with that request. Please try again.