Browse files

Shifting to http://github.com/halorgium/dm-salesforce/tree/master

  • Loading branch information...
1 parent 3ea4b8c commit 24c80dbb2d3515b12690b3d3f71ac01d791d91f7 @halorgium halorgium committed Dec 11, 2008
View
1 README
@@ -0,0 +1 @@
+Shifted to http://github.com/halorgium/dm-salesforce/tree/master
View
20 salesforce/LICENSE
@@ -1,20 +0,0 @@
-Copyright (c) 2008 Yehuda Katz
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
50 salesforce/README.markdown
@@ -1,50 +0,0 @@
-dm-salesforce
-=============
-
-A gem that provides a Salesforce Adapter for DataMapper.
-
-The URI for a Salesforce repository is:
-
- salesforce://username:password@salesforce.wsdl # relative wsdl path
- salesforce://username:password@/etc/salesforce.wsdl # absolute wsdl path
-
-The wsdl is converted into Ruby classes and stored in ~/.salesforce. This automatically
-happens the first time you use the salesforce adapter, so you don't need to worry about
-generating Ruby code. It just works.
-
-An example of using the adapter:
-
- class Account
- include DataMapper::Resource
-
- property :id, String, :key => true
- property :name, String
- property :description, String
- property :billing_city, String
- property :billing_postal_code, String
- property :billing_country, String
- property :billing_state, String
- property :billing_street, String
- property :fax, String
- property :phone, String
- property :type, String
- property :website, String
- has n, :contacts
- end
-
- class Contact
- include DataMapper::Resource
-
- property :id, String, :key => true
- property :title, String
- property :email, String
- property :first_name, String
- property :last_name, String
- property :home_phone, String
- property :mobile_phone, String
- property :phone, String
- belongs_to :account
- end
-
-You can then do `Contact.all("account.name.like" => "%ruby%")`, which will get a list of all contacts
-whose associated account's name is like `%ruby%`.
View
50 salesforce/Rakefile
@@ -1,50 +0,0 @@
-require 'rubygems'
-require 'rake/gempackagetask'
-require 'rubygems/specification'
-require 'date'
-
-GEM = "dm-salesforce"
-GEM_VERSION = "0.9.7.2"
-AUTHOR = "Yehuda Katz"
-EMAIL = "wycats@gmail.com"
-HOMEPAGE = "http://www.yehudakatz.com"
-SUMMARY = "A DataMapper adapter to the Salesforce API"
-
-spec = Gem::Specification.new do |s|
- s.name = GEM
- s.version = GEM_VERSION
- s.platform = Gem::Platform::RUBY
- s.has_rdoc = true
- s.extra_rdoc_files = ["README.markdown", "LICENSE"]
- s.summary = SUMMARY
- s.description = s.summary
- s.author = AUTHOR
- s.email = EMAIL
- s.homepage = HOMEPAGE
-
- # Uncomment this to add a dependency
- # s.add_dependency "foo"
- s.add_dependency "dm-core", "~> 0.9.4"
- s.add_dependency "extlib", "~> 0.9.4"
- s.add_dependency "soap4r", "~> 1.5.8"
-
- s.require_path = 'lib'
- s.autorequire = GEM
- s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("{lib,specs}/**/*")
-end
-
-Rake::GemPackageTask.new(spec) do |pkg|
- pkg.gem_spec = spec
-end
-
-desc "install the gem locally"
-task :install => [:package] do
- sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION} --no-ri --no-rdoc}
-end
-
-desc "create a gemspec file"
-task :make_spec do
- File.open("#{GEM}.gemspec", "w") do |file|
- file.puts spec.to_ruby
- end
-end
View
39 salesforce/dm-salesforce.gemspec
@@ -1,39 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-Gem::Specification.new do |s|
- s.name = %q{dm-salesforce}
- s.version = "0.9.7.2"
-
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
- s.authors = ["Yehuda Katz"]
- s.autorequire = %q{dm-salesforce}
- s.date = %q{2008-11-25}
- s.description = %q{A DataMapper adapter to the Salesforce API}
- s.email = %q{wycats@gmail.com}
- s.extra_rdoc_files = ["README.markdown", "LICENSE"]
- s.files = ["LICENSE", "README.markdown", "Rakefile", "lib/dm-salesforce.rb", "lib/salesforce_api.rb"]
- s.has_rdoc = true
- s.homepage = %q{http://www.yehudakatz.com}
- s.require_paths = ["lib"]
- s.rubygems_version = %q{1.3.1}
- s.summary = %q{A DataMapper adapter to the Salesforce API}
-
- if s.respond_to? :specification_version then
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
- s.specification_version = 2
-
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
- s.add_runtime_dependency(%q<dm-core>, ["~> 0.9.4"])
- s.add_runtime_dependency(%q<extlib>, ["~> 0.9.4"])
- s.add_runtime_dependency(%q<soap4r>, ["~> 1.5.8"])
- else
- s.add_dependency(%q<dm-core>, ["~> 0.9.4"])
- s.add_dependency(%q<extlib>, ["~> 0.9.4"])
- s.add_dependency(%q<soap4r>, ["~> 1.5.8"])
- end
- else
- s.add_dependency(%q<dm-core>, ["~> 0.9.4"])
- s.add_dependency(%q<extlib>, ["~> 0.9.4"])
- s.add_dependency(%q<soap4r>, ["~> 1.5.8"])
- end
-end
View
204 salesforce/lib/dm-salesforce.rb
@@ -1,204 +0,0 @@
-$:.push File.expand_path(File.dirname(__FILE__))
-require "fileutils"
-require "salesforce_api"
-
-module DataMapper
- module Adapters
- module SQL
- class << self
- def from_condition(condition, repository)
- op, prop, value = condition
- operator = case op
- when String then operator
- when :eql, :in then equality_operator(value)
- when :not then inequality_operator(value)
- when :like then "LIKE #{quote_value(value)}"
- when :gt then "> #{quote_value(value)}"
- when :gte then ">= #{quote_value(value)}"
- when :lt then "< #{quote_value(value)}"
- when :lte then "<= #{quote_value(value)}"
- else raise "CAN HAS CRASH?"
- end
- case prop
- when Property
- "#{prop.field} #{operator}"
- when Query::Path
- rels = prop.relationships
- names = rels.map {|r| storage_name(r, repository) }.join(".")
- "#{names}.#{prop.field} #{operator}"
- end
- end
-
- def storage_name(rel, repository)
- rel.parent_model.storage_name(repository.name)
- end
-
- def order(direction)
- "#{direction.property.field} #{direction.direction.to_s.upcase}"
- end
-
- private
- def equality_operator(value)
- case value
- when Array then "IN #{quote_value(value)}"
- else "= #{quote_value(value)}"
- end
- end
-
- def inequality_operator(value)
- case value
- when Array then "NOT IN #{quote_value(value)}"
- else "!= #{quote_value(value)}"
- end
- end
-
- def quote_value(value)
- case value
- when Array then "(#{value.map {|v| quote_value(v)}.join(", ")})"
- when NilClass then "NULL"
- when String then "'#{value.gsub(/'/, "\\'").gsub(/\\/, %{\\\\})}'"
- else "#{value}"
- end
- end
- end
- end
-
- class SalesforceAdapter < AbstractAdapter
- def initialize(name, uri_or_options)
- super
- @resource_naming_convention = proc do |value|
- klass = Extlib::Inflection.constantize(value)
- if klass.respond_to?(:salesforce_class)
- klass.salesforce_class
- else
- value.split("::").last
- end
- end
- @field_naming_convention = proc do |property|
- connection.field_name_for(property.model.storage_name(name), property.name.to_s)
- end
- end
-
- def normalize_uri(uri_or_options)
- if uri_or_options.kind_of?(Addressable::URI)
- return uri_or_options
- end
-
- if uri_or_options.kind_of?(String)
- uri_or_options = Addressable::URI.parse(uri_or_options)
- end
-
- adapter = uri_or_options.delete(:adapter).to_s
- user = uri_or_options.delete(:username)
- password = uri_or_options.delete(:password)
- host = uri_or_options.delete(:host) || "."
- path = uri_or_options.delete(:path)
- query = uri_or_options.to_a.map { |pair| pair * '=' } * '&'
- query = nil if query == ''
-
- return Addressable::URI.new({:adapter => adapter, :user => user, :password => password, :host => host, :path => path, :query => query})
- end
-
- def connection
- @connection ||= SalesforceAPI::Connection.new(@uri.user, @uri.password, @uri.host + @uri.path)
- end
-
- def read_many(query)
- Collection.new(query) do |set|
- read(query) do |result|
- set.load(result)
- end
- end
- end
-
- def read_one(query)
- read(query) do |result|
- return query.model.load(result, query)
- end
- end
-
- def create(resources)
- arr = resources.map do |resource|
- obj = make_salesforce_obj(resource, resource.dirty_attributes, nil)
- end
-
- result = connection.create(arr)
- result.each_with_index do |record, i|
- resource = resources[i]
- key = resource.class.key(repository.name).first
- resource.instance_variable_set(key.instance_variable_name, record.id)
- end
- result.size
- end
-
- def update(attributes, query)
- arr = if key_condition = query.conditions.find {|op,prop,val| prop.key?}
- [ make_salesforce_obj(query, attributes, key_condition.last) ]
- else
- read_many(query).map do |obj|
- obj = make_salesforce_obj(query, attributes, x.key)
- end
- end
- connection.update(arr).size
- end
-
- def delete(query)
- keys = if key_condition = query.conditions.find {|op,prop,val| prop.key?}
- [key_condition.last]
- else
- query.read_many.map {|r| r.key}
- end
-
- connection.delete(keys).size
- end
-
- # A dummy method to allow migrations without upsetting any data
- def destroy_model_storage(*args)
- true
- end
-
- # A dummy method to allow migrations without upsetting any data
- def create_model_storage(*args)
- true
- end
-
- private
- def read(query, &block)
- repository = query.repository
- properties = query.fields
- properties_with_indexes = Hash[*properties.zip((0...properties.size).to_a).flatten]
- conditions = query.conditions.map {|c| SQL.from_condition(c, repository)}.compact.join(") AND (")
-
- sql = "SELECT #{query.fields.map {|f| f.field(repository.name)}.join(", ")} from #{query.model.storage_name(repository.name)}"
- sql << " WHERE (#{conditions})" unless conditions.empty?
- sql << " ORDER BY #{SQL.order(query.order[0])}" unless query.order.empty?
- sql << " LIMIT #{query.limit}" if query.limit
-
- DataMapper.logger.debug sql
-
- result = connection.query(sql)
-
- return unless result.records
-
- result.records.each do |record|
- accum = []
- properties_with_indexes.each do |(property, idx)|
- meth = connection.field_name_for(property.model.storage_name(repository.name), property.field(repository.name))
- accum[idx] = record.send(meth)
- end
- yield accum
- end
- end
-
- def make_salesforce_obj(query, attrs, key)
- klass_name = query.model.storage_name(query.repository.name)
- values = {}
- values["id"] = query.conditions.find {|op,prop,val| prop.key?}.last if key
- attrs.each do |property,value|
- values[property.field(query.repository.name)] = value
- end
- connection.make_object(klass_name, values)
- end
- end
- end
-end
View
188 salesforce/lib/salesforce_api.rb
@@ -1,188 +0,0 @@
-gem "soap4r", ">= 1.5.8"
-require 'soap/wsdlDriver'
-require 'soap/header/simplehandler'
-require "rexml/element"
-
-module SalesforceAPI
- class Connection
- class Error < StandardError; end
- class FieldNotFound < Error; end
- class LoginFailed < Error; end
- class SessionTimeout < Error; end
-
- class SOAPError < Error
- def initialize(message, result)
- @result = result
- super("#{message}: #{result_message}")
- end
-
- def failed_records
- @result.reject {|r| r.success}
- end
-
- def result_message
- failed_records.map do |r|
- message_for_record(r)
- end.join("; ")
- end
-
- def message_for_record(record)
- record.errors.map {|e| "#{e.statusCode}: #{e.message}"}.join(", ")
- end
- end
- class CreateError < SOAPError; end
- class QueryError < SOAPError; end
- class DeleteError < SOAPError; end
- class UpdateError < SOAPError; end
-
- class HeaderHandler < SOAP::Header::SimpleHandler
- def initialize(tag, value)
- super(XSD::QName.new('urn:enterprise.soap.sforce.com', tag))
- @tag = tag
- @value = value
- end
- def on_simple_outbound
- @value
- end
- end
-
- def initialize(username, password, wsdl_path, organization_id = nil)
- @username, @password, @wsdl_path, @organization_id = URI.unescape(username), password, File.expand_path(wsdl_path), organization_id
- driver
- end
- attr_reader :wsdl_path, :user_id, :user_details
-
- def organization_id
- @user_details && @user_details.organizationId
- end
-
- def make_object(klass_name, values)
- klass = SalesforceAPI.const_get(klass_name)
- obj = klass.new
- values.each do |property,value|
- field = field_name_for(klass_name, property)
- obj.send("#{field}=", value)
- end
- obj
- end
-
- def field_name_for(klass_name, column)
- klass = SalesforceAPI.const_get(klass_name)
- fields = [column, column.camel_case, "#{column}__c".downcase]
- options = /^(#{fields.join("|")})$/i
- matches = klass.instance_methods(false).grep(options)
- if matches.any?
- matches.first
- else
- raise FieldNotFound,
- "You specified #{column} as a field, but neither #{fields.join(" or ")} exist. " \
- "Either manually specify the field name with :field, or check to make sure you have " \
- "provided a correct field name."
- end
- end
-
- def query(string)
- with_reconnection do
- driver.query(:queryString => string).result
- end
- rescue SOAP::FaultError => e
- raise QueryError.new(e.message, [])
- end
-
- def create(objects)
- call_api(:create, CreateError, "creating", objects)
- end
-
- def update(objects)
- call_api(:update, UpdateError, "updating", objects)
- end
-
- def delete(keys)
- call_api(:delete, DeleteError, "deleting", keys)
- end
-
- private
- def login
- generate_soap_classes
- driver = Soap.new
- if @organization_id
- driver.headerhandler << HeaderHandler.new("LoginScopeHeader", :organizationId => @organization_id)
- end
-
- begin
- result = driver.login(:username => @username, :password => @password).result
- rescue SOAP::FaultError => error
- if error.faultcode.to_obj == "sf:INVALID_LOGIN"
- raise LoginFailed, "Could not login to Salesforce; #{error.faultstring.text}"
- else
- raise
- end
- end
- driver.endpoint_url = result.serverUrl
- driver.headerhandler << HeaderHandler.new("SessionHeader", "sessionId" => result.sessionId)
- driver.headerhandler << HeaderHandler.new("CallOptions", "client" => "client")
- @user_id = result.userId
- @user_details = result.userInfo
- driver
- end
-
- def driver
- @driver ||= login
- end
-
- def call_api(method, exception_class, message, args)
- with_reconnection do
- result = driver.send(method, args)
- if result.all? {|r| r.success}
- result
- else
- raise exception_class.new("Got some errors while #{message} Salesforce objects", result)
- end
- end
- end
-
- # Generate Ruby files and move them into .salesforce for future use
- def generate_soap_classes
- unless File.directory?(wsdl_api_dir) && Dir["#{wsdl_api_dir}/SalesforceAPI*.rb"].size == 3
- old_args = ARGV.dup
- unless File.file?(@wsdl_path)
- raise Errno::ENOENT, "Could not find the Salesforce WSDL at #{@wsdl_path}"
- end
- ARGV.replace %W(--wsdl #{@wsdl_path} --module_path SalesforceAPI --classdef SalesforceAPI --type client)
- load `which wsdl2ruby.rb`.chomp
- ARGV.replace old_args
- FileUtils.mkdir_p wsdl_api_dir
- FileUtils.mv Dir["SalesforceAPI*"], wsdl_api_dir
- FileUtils.rm Dir["SforceServiceClient.rb"]
- end
-
- $:.push wsdl_api_dir
- require "SalesforceAPIDriver"
- end
-
- def wsdl_api_dir
- "#{ENV["HOME"]}/.salesforce/#{wsdl_basename}"
- end
-
- def wsdl_basename
- @wsdl_basename ||= File.basename(@wsdl_path)
- end
-
- def with_reconnection(&block)
- yield
- rescue SOAP::FaultError => error
- retry_count ||= 0
- if error.faultcode.text == "sf:INVALID_SESSION_ID"
- $stderr.puts "Got a invalid session id; reconnecting"
- @driver = nil
- login
- retry_count += 1
- retry unless retry_count > 5
- else
- raise
- end
-
- raise SessionTimeout, "The Salesforce session could not be established"
- end
- end
-end
View
2 salesforce/spec/spec_helper.rb
@@ -1,2 +0,0 @@
-$TESTING=true
-$:.push File.join(File.dirname(__FILE__), '..', 'lib')

0 comments on commit 24c80db

Please sign in to comment.