levene 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest.txt ADDED
@@ -0,0 +1,18 @@
1
+ Manifest.txt
2
+ README.markdown
3
+ Rakefile
4
+ levene.gemspec
5
+ lib/levene.rb
6
+ lib/levene/version.rb
7
+ lib/levene/connection.rb
8
+ lib/levene/models/account.rb
9
+ lib/levene/models/base.rb
10
+ lib/levene/models/campaign.rb
11
+ lib/levene/models/campaign_member.rb
12
+ lib/levene/models/contact.rb
13
+ lib/levene/models/lead.rb
14
+ lib/levene/models/opportunity.rb
15
+ lib/levene/extensions/binding.rb
16
+ test/helper.rb
17
+ test/test_login.rb
18
+ test/test_lead_model.rb
data/README.markdown ADDED
@@ -0,0 +1,46 @@
1
+ # Levene
2
+
3
+ * https://github.com/robrasmussen/levene
4
+
5
+
6
+ ## DESCRIPTION
7
+
8
+ A light wrapper around RForce to make working with Salesforce a little
9
+ easier.
10
+
11
+ ## USAGE
12
+
13
+ Levene allows you to easily create models that map to their Salesforce
14
+ API counterpart:
15
+
16
+ class Contact < Levene::Models::Base
17
+ end
18
+
19
+ Levene depends on a few environment variables being defined:
20
+
21
+ SALESFORCE_DEV_USER: The Salesforce account you're using
22
+ SALESFORCE_DEV_PASS: Your Salesforce password + security token
23
+
24
+ (If you don't have a Salesforce dev account or an account with API
25
+ access, you'll have to take care of that first.)
26
+
27
+
28
+ Creating a subclass of Levene::Models::Base will cause the fields of the
29
+ relevant Salesforce object to be looked up, and will create accessors
30
+ and ActiveModel validations based on the field definititions.
31
+
32
+ contact = Contact.new
33
+ contact.valid? #=> false
34
+ contact.errors.full_messages #=> ["Last name can't be blank"]
35
+ contact.first_name = "Ricky"
36
+ contact.last_name = "Roma"
37
+ contact.description = "The guy is a closer."
38
+ contact.save #=> true
39
+ contact.id #=> "00AA000006636drIAA"
40
+
41
+
42
+ This is not a replacement for the ActiveRecord connection adapter for
43
+ Salesforce. Levene is mostly for people who need to push data up to
44
+ Salesforce in the background. *It is not thread-safe*.
45
+
46
+
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require "rubygems"
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+
5
+ Hoe.plugins.delete :rubyforge
6
+ Hoe.plugin :minitest #gem install hoe-seattlerb
7
+ Hoe.plugin :gemspec #gem install hoe-gemspec
8
+
9
+ Hoe.spec 'levene' do
10
+ developer('Robert Rasmussen', 'rob.rasmussen@gmail.com')
11
+
12
+ self.readme_file = 'README.markdown'
13
+ self.extra_rdoc_files = FileList['README.markdown']
14
+
15
+ self.extra_deps << [ 'activemodel', '>=3.0.3' ]
16
+ self.extra_deps << [ 'activesupport', '>=3.0.3' ]
17
+ self.extra_deps << [ 'rforce', '~>0.6.0' ]
18
+
19
+ self.extra_dev_deps << [ 'nokogiri', '>=1.4.4' ]
20
+ end
21
+
data/levene.gemspec ADDED
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{levene}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Robert Rasmussen"]
9
+ s.date = %q{2011-01-20}
10
+ s.description = %q{A light wrapper around RForce to make working with Salesforce a little
11
+ easier.}
12
+ s.email = ["rob.rasmussen@gmail.com"]
13
+ s.extra_rdoc_files = ["Manifest.txt", "README.markdown"]
14
+ s.files = ["Manifest.txt", "README.markdown", "Rakefile", "levene.gemspec", "lib/levene.rb", "lib/levene/version.rb", "lib/levene/connection.rb", "lib/levene/models/account.rb", "lib/levene/models/base.rb", "lib/levene/models/campaign.rb", "lib/levene/models/campaign_member.rb", "lib/levene/models/contact.rb", "lib/levene/models/lead.rb", "lib/levene/models/opportunity.rb", "lib/levene/extensions/binding.rb", "test/helper.rb", "test/test_login.rb", "test/test_lead_model.rb", "test/test_base_model.rb"]
15
+ s.homepage = %q{https://github.com/robrasmussen/levene}
16
+ s.rdoc_options = ["--main", "README.markdown"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{levene}
19
+ s.rubygems_version = %q{1.3.7}
20
+ s.summary = %q{A light wrapper around RForce to make working with Salesforce a little easier.}
21
+ s.test_files = ["test/test_base_model.rb", "test/test_lead_model.rb", "test/test_login.rb"]
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
28
+ s.add_runtime_dependency(%q<activemodel>, [">= 3.0.3"])
29
+ s.add_runtime_dependency(%q<activesupport>, [">= 3.0.3"])
30
+ s.add_runtime_dependency(%q<rforce>, ["~> 0.6.0"])
31
+ s.add_development_dependency(%q<minitest>, [">= 1.6.0"])
32
+ s.add_development_dependency(%q<nokogiri>, [">= 1.4.4"])
33
+ s.add_development_dependency(%q<hoe>, [">= 2.8.0"])
34
+ else
35
+ s.add_dependency(%q<activemodel>, [">= 3.0.3"])
36
+ s.add_dependency(%q<activesupport>, [">= 3.0.3"])
37
+ s.add_dependency(%q<rforce>, ["~> 0.6.0"])
38
+ s.add_dependency(%q<minitest>, [">= 1.6.0"])
39
+ s.add_dependency(%q<nokogiri>, [">= 1.4.4"])
40
+ s.add_dependency(%q<hoe>, [">= 2.8.0"])
41
+ end
42
+ else
43
+ s.add_dependency(%q<activemodel>, [">= 3.0.3"])
44
+ s.add_dependency(%q<activesupport>, [">= 3.0.3"])
45
+ s.add_dependency(%q<rforce>, ["~> 0.6.0"])
46
+ s.add_dependency(%q<minitest>, [">= 1.6.0"])
47
+ s.add_dependency(%q<nokogiri>, [">= 1.4.4"])
48
+ s.add_dependency(%q<hoe>, [">= 2.8.0"])
49
+ end
50
+ end
@@ -0,0 +1,30 @@
1
+ module Levene
2
+ class Connection
3
+
4
+ def login(username, password)
5
+ login_result = self.binding.login username, password
6
+ true
7
+ rescue
8
+ false
9
+ end
10
+
11
+ def binding
12
+ @binding ||= ::RForce::Binding.new 'https://login.salesforce.com/services/Soap/u/20.0'
13
+ end
14
+
15
+ def start
16
+ yield binding
17
+ logout
18
+ end
19
+
20
+ def logout
21
+ self.binding.logout
22
+ @binding = nil
23
+ end
24
+
25
+ def has_session?
26
+ !self.binding.instance_variable_get("@session_id").nil?
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,46 @@
1
+ module Levene
2
+ module Extensions
3
+ module Binding
4
+
5
+ def logout
6
+ response = call_remote(:logout, [])
7
+ @session_id = nil
8
+ end
9
+
10
+ def describe(model)
11
+ result = call_remote(:describeSObject, :sObjectType => model.name.demodulize)
12
+ if result[:Fault]
13
+ raise result[:Fault][:faultstring]
14
+ else
15
+ return result[:describeSObjectResponse][:result][:fields]
16
+ end
17
+ end
18
+
19
+ def query(model, conditions)
20
+ full_query = "SELECT #{model.columns.collect {|c| c[:name]}.join(", ")} FROM #{model.model_name.demodulize} #{conditions || ""}"
21
+ result = call_remote(:query, :queryString => full_query)
22
+ if result[:Fault]
23
+ raise result[:Fault][:faultstring]
24
+ else
25
+ result[:queryResponse][:result].nil? ? [] : [result[:queryResponse][:result][:records]].flatten
26
+ end
27
+ end
28
+
29
+ def create(model)
30
+ return false unless model.valid?
31
+ result = call_remote(:create, model.to_s_object)
32
+ if result[:Fault]
33
+ raise result[:Fault][:faultstring]
34
+ else
35
+ unless result[:createResponse][:result][:id].nil?
36
+ model.id = result[:createResponse][:result][:id]
37
+ return true
38
+ else
39
+ model.errors.add_to_base(result[:createResponse][:result][:errors][:message])
40
+ return false
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,6 @@
1
+ module Levene
2
+ module Models
3
+ class Account < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,140 @@
1
+ require 'active_model'
2
+ require 'active_support/core_ext/class/attribute'
3
+
4
+ module Levene
5
+ module Models
6
+ class Base
7
+ extend ActiveModel::Naming
8
+ include ActiveModel::Validations
9
+
10
+ class_attribute :connection
11
+
12
+ def attributes
13
+ self.class.attr_names.inject({}) do |memo, name|
14
+ memo[name] = self.send(name)
15
+ memo
16
+ end
17
+ end
18
+
19
+ def attributes=(attrs)
20
+ attrs.each do |attr, val|
21
+ if attr[0] == attr[0].upcase
22
+ attr = attr.to_s.underscore
23
+ attr = attr[0].downcase + attr[1..-1]
24
+ end
25
+
26
+ if attr == "id" && Array === val
27
+ val = val.first
28
+ end
29
+
30
+ self.send "#{attr}=", val if self.respond_to?("#{attr}=".to_sym)
31
+ end
32
+ end
33
+
34
+ def save
35
+ if self.id.nil?
36
+ self.connection.binding.create(self)
37
+ else
38
+ self.connection.binding.update(self)
39
+ end
40
+ end
41
+
42
+ def to_s_object
43
+ attributeMap = self.attributes.inject({}) do |memo, attr|
44
+ next memo if attr.last.nil? || attr.last.blank?
45
+ levene_attr_name = attr.first.to_s.camelize
46
+ if attr.first.to_s.ends_with?("__c")
47
+ levene_attr_name.gsub!(/_c$/, "__c")
48
+ end
49
+ memo[levene_attr_name] = attr.last
50
+ memo
51
+ end
52
+
53
+ fieldsToNull = self.attributes.select {|k,v| self.class.column_hash[k][:createable] == "true" && (v.nil? || v.blank?)}.collect {|k,v| k.to_s.camelize}
54
+ fieldsToNull = fieldsToNull.collect {|k| k[0].downcase + k[1..-1]}
55
+
56
+ {:sObject => {"xsi:type" => self.class.model_name.demodulize}.merge(attributeMap)}
57
+ end
58
+
59
+ class << self
60
+ attr_accessor :abstract
61
+ alias_method :abstract?, :abstract
62
+ attr_accessor :columns
63
+ attr_accessor :attr_names
64
+ attr_accessor :column_hash
65
+
66
+ def abstract!
67
+ self.abstract = true
68
+ end
69
+
70
+ def inherited(klazz)
71
+ klazz.abstract = false
72
+ klazz.get_column_definitions
73
+ end
74
+
75
+ def find(*ids)
76
+ results = self.connection.find(self,ids)
77
+ end
78
+
79
+ def find_by_soql(query)
80
+ result = self.connection.binding.query(self, query)
81
+ return [] if result == [nil]
82
+ result.collect {|r| obj = self.new; obj.attributes = r; obj}
83
+ end
84
+
85
+ def get_column_definitions
86
+ self.columns = self.connection.binding.describe(self)
87
+ self.columns.each do |col|
88
+ next if col[:deprecatedAndHidden] == "true"
89
+
90
+ attribute = (col[:name][0].downcase + col[:name][1..-1]).underscore
91
+ self.attr_names ||= []
92
+ self.attr_names << attribute.to_sym
93
+ self.column_hash ||= {}
94
+ self.column_hash[attribute.to_sym] = col
95
+
96
+ attr_accessor attribute
97
+
98
+ if col[:nillable] == "false" && col[:createable] == "true" && col[:defaultedOnCreate] != "true"
99
+ validates_presence_of attribute
100
+ end
101
+
102
+ if col[:type] == "string" && col[:length] && col[:length].to_i > 0
103
+ validates_length_of attribute, :maximum => col[:length].to_i
104
+ end
105
+
106
+ if col[:picklistValues]
107
+ valid_values = col[:picklistValues].select {|h| h[:active] == "true"}.collect {|h| h[:value]}
108
+ default = col[:picklistValues].find {|h| h[:defaultValue] == "true"}
109
+ if col[:nillable] == "true" || col[:createable] == "false"
110
+ validates_inclusion_of attribute, :in => valid_values, :allow_nil => true
111
+ else
112
+ validates_inclusion_of attribute, :in => valid_values
113
+ end
114
+
115
+ if default
116
+ class_eval """def #{attribute}
117
+ @#{attribute} ||= \"#{default[:value]}\"
118
+ end"""
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ def connection_established?
125
+ !connection.nil? && connection.has_session?
126
+ end
127
+
128
+ def establish_connection
129
+ self.connection = Levene::Connection.new
130
+ self.connection.login ENV["SALESFORCE_DEV_USER"], ENV["SALESFORCE_DEV_PASS"]
131
+ end
132
+
133
+ end
134
+
135
+ abstract!
136
+ establish_connection
137
+
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,6 @@
1
+ module Levene
2
+ module Models
3
+ class Campaign < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Levene
2
+ module Models
3
+ class CampaignMember < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Levene
2
+ module Models
3
+ class Contact < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Levene
2
+ module Models
3
+ class Lead < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Levene
2
+ module Models
3
+ class Opportunity < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module Levene
2
+ VERSION = "0.0.1"
3
+ end
data/lib/levene.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'rforce'
2
+ require 'active_support'
3
+
4
+ module Levene
5
+ autoload :Connection, "levene/connection.rb"
6
+ module Models
7
+ autoload :Base, "levene/models/base.rb"
8
+ autoload :Account, "levene/models/account.rb"
9
+ autoload :Campaign, "levene/models/campaign.rb"
10
+ autoload :CampaignMember, "levene/models/campaign_member.rb"
11
+ autoload :Contact, "levene/models/contact.rb"
12
+ autoload :Lead, "levene/models/lead.rb"
13
+ autoload :Opportunity, "levene/models/opportunity.rb"
14
+
15
+ end
16
+ module Extensions
17
+ autoload :Binding, "levene/extensions/binding.rb"
18
+ end
19
+ end
20
+
21
+ RForce::Binding.send(:include, Levene::Extensions::Binding)
data/test/helper.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'minitest/autorun'
3
+ require 'levene'
4
+
5
+ class Object
6
+ def must_be_true
7
+ must_equal true
8
+ end
9
+
10
+ def must_be_false
11
+ must_equal false
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ require 'helper'
2
+
3
+ describe Levene::Models::Base do
4
+
5
+ it "should be abstract" do
6
+ Levene::Models::Base.abstract?.must_be_true
7
+ end
8
+ end
@@ -0,0 +1,29 @@
1
+ require 'helper'
2
+
3
+ describe Levene::Models::Lead do
4
+ before do
5
+ @lead = Levene::Models::Lead.new
6
+ end
7
+
8
+ it "should conform to ActiveModel::Naming" do
9
+ @lead.class.model_name.must_equal "Levene::Models::Lead"
10
+ @lead.class.model_name.human.must_equal "Lead"
11
+ end
12
+
13
+ it "should not be abstract" do
14
+ @lead.class.abstract?.must_be_false
15
+ end
16
+
17
+ it "should have a list of columns" do
18
+ @lead.class.columns.count.must_be :>, 0
19
+ end
20
+
21
+ it "should have a connection attribute" do
22
+ @lead.class.must_respond_to :connection
23
+ end
24
+
25
+ it "should respond to #save" do
26
+ @lead.must_respond_to :save
27
+ end
28
+
29
+ end
@@ -0,0 +1,66 @@
1
+ require 'helper'
2
+
3
+ describe Levene::Connection do
4
+ before do
5
+ @connection = Levene::Connection.new
6
+ end
7
+
8
+ it "must respond to #login" do
9
+ @connection.must_respond_to :login
10
+ end
11
+
12
+ it "must respond to #binding" do
13
+ @connection.must_respond_to :binding
14
+ end
15
+
16
+ describe "with valid user credentials" do
17
+ before do
18
+ @username = ENV["SALESFORCE_DEV_USER"]
19
+ @password = ENV["SALESFORCE_DEV_PASS"]
20
+ end
21
+
22
+ it "should return a truthy value for a successful login" do
23
+ assert @connection.login(@username, @password), "Successful login should return something truthy"
24
+ end
25
+
26
+ it "should report is has a ssession after a successful login" do
27
+ @connection.login(@username, @password)
28
+ @connection.has_session?.must_be_true
29
+ end
30
+ end
31
+
32
+ describe "with invalid user credentials" do
33
+ before do
34
+ @username= "foo"
35
+ @password = "bar"
36
+ end
37
+
38
+ it "should return false for an unsuccessful login" do
39
+ assert !@connection.login(@username, @password), "Unsuccessful login should return someing falsy"
40
+ end
41
+
42
+ it "should not have a session after an unsuccessful login" do
43
+ @connection.login(@username, @password)
44
+ assert @connection.has_session?.must_be_false
45
+ end
46
+ end
47
+
48
+
49
+ describe "a logged in connection" do
50
+ before do
51
+ @connection.login(ENV["SALESFORCE_DEV_USER"], ENV["SALESFORCE_DEV_PASS"])
52
+ end
53
+
54
+ it "should yield a levene binding from start" do
55
+ @connection.start do |binding|
56
+ binding.must_be_kind_of RForce::Binding
57
+ @connection.has_session?.must_be_true
58
+ end
59
+ end
60
+
61
+ it "should not have an active binding after the block finished" do
62
+ @connection.start {|binding| }
63
+ @connection.has_session?.must_be_false
64
+ end
65
+ end
66
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: levene
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Robert Rasmussen
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-01-20 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activemodel
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 3
30
+ - 0
31
+ - 3
32
+ version: 3.0.3
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: activesupport
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 3
45
+ - 0
46
+ - 3
47
+ version: 3.0.3
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: rforce
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 0
60
+ - 6
61
+ - 0
62
+ version: 0.6.0
63
+ type: :runtime
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: minitest
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 1
75
+ - 6
76
+ - 0
77
+ version: 1.6.0
78
+ type: :development
79
+ version_requirements: *id004
80
+ - !ruby/object:Gem::Dependency
81
+ name: nokogiri
82
+ prerelease: false
83
+ requirement: &id005 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ segments:
89
+ - 1
90
+ - 4
91
+ - 4
92
+ version: 1.4.4
93
+ type: :development
94
+ version_requirements: *id005
95
+ - !ruby/object:Gem::Dependency
96
+ name: hoe
97
+ prerelease: false
98
+ requirement: &id006 !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ segments:
104
+ - 2
105
+ - 8
106
+ - 0
107
+ version: 2.8.0
108
+ type: :development
109
+ version_requirements: *id006
110
+ description: |-
111
+ A light wrapper around RForce to make working with Salesforce a little
112
+ easier.
113
+ email:
114
+ - rob.rasmussen@gmail.com
115
+ executables: []
116
+
117
+ extensions: []
118
+
119
+ extra_rdoc_files:
120
+ - Manifest.txt
121
+ - README.markdown
122
+ files:
123
+ - Manifest.txt
124
+ - README.markdown
125
+ - Rakefile
126
+ - levene.gemspec
127
+ - lib/levene.rb
128
+ - lib/levene/version.rb
129
+ - lib/levene/connection.rb
130
+ - lib/levene/models/account.rb
131
+ - lib/levene/models/base.rb
132
+ - lib/levene/models/campaign.rb
133
+ - lib/levene/models/campaign_member.rb
134
+ - lib/levene/models/contact.rb
135
+ - lib/levene/models/lead.rb
136
+ - lib/levene/models/opportunity.rb
137
+ - lib/levene/extensions/binding.rb
138
+ - test/helper.rb
139
+ - test/test_login.rb
140
+ - test/test_lead_model.rb
141
+ - test/test_base_model.rb
142
+ has_rdoc: true
143
+ homepage: https://github.com/robrasmussen/levene
144
+ licenses: []
145
+
146
+ post_install_message:
147
+ rdoc_options:
148
+ - --main
149
+ - README.markdown
150
+ require_paths:
151
+ - lib
152
+ required_ruby_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ segments:
158
+ - 0
159
+ version: "0"
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ segments:
166
+ - 0
167
+ version: "0"
168
+ requirements: []
169
+
170
+ rubyforge_project: levene
171
+ rubygems_version: 1.3.7
172
+ signing_key:
173
+ specification_version: 3
174
+ summary: A light wrapper around RForce to make working with Salesforce a little easier.
175
+ test_files:
176
+ - test/test_base_model.rb
177
+ - test/test_lead_model.rb
178
+ - test/test_login.rb