sugarcrm 0.8.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +58 -19
- data/Rakefile +1 -4
- data/VERSION +1 -1
- data/lib/sugarcrm.rb +7 -4
- data/lib/sugarcrm/associations.rb +2 -0
- data/lib/sugarcrm/associations/association_collection.rb +143 -0
- data/lib/sugarcrm/associations/association_methods.rb +92 -0
- data/lib/sugarcrm/attributes.rb +4 -0
- data/lib/sugarcrm/attributes/attribute_methods.rb +131 -0
- data/lib/sugarcrm/attributes/attribute_serializers.rb +55 -0
- data/lib/sugarcrm/attributes/attribute_typecast.rb +39 -0
- data/lib/sugarcrm/attributes/attribute_validations.rb +37 -0
- data/lib/sugarcrm/base.rb +77 -21
- data/lib/sugarcrm/connection.rb +3 -137
- data/lib/sugarcrm/connection/api/get_entry_list.rb +1 -1
- data/lib/sugarcrm/connection/api/get_note_attachment.rb +0 -1
- data/lib/sugarcrm/connection/api/set_relationship.rb +3 -0
- data/lib/sugarcrm/connection/connection.rb +144 -0
- data/lib/sugarcrm/{request.rb → connection/request.rb} +2 -10
- data/lib/sugarcrm/{response.rb → connection/response.rb} +10 -4
- data/lib/sugarcrm/exceptions.rb +14 -26
- data/lib/sugarcrm/module.rb +19 -18
- data/test/connection/test_get_entry.rb +5 -5
- data/test/connection/test_get_module_fields.rb +1 -1
- data/test/connection/test_set_relationship.rb +13 -21
- data/test/helper.rb +1 -1
- data/test/test_association_collection.rb +12 -0
- data/test/test_associations.rb +33 -0
- data/test/test_connection.rb +0 -7
- data/test/test_module.rb +1 -1
- data/test/test_sugarcrm.rb +16 -8
- metadata +22 -28
- data/lib/sugarcrm/association_methods.rb +0 -46
- data/lib/sugarcrm/attribute_methods.rb +0 -170
data/README.rdoc
CHANGED
@@ -12,17 +12,17 @@ RubyGem for interacting with SugarCRM via REST.
|
|
12
12
|
|
13
13
|
A less clunky way to interact with SugarCRM via REST.
|
14
14
|
|
15
|
-
|
16
|
-
call SugarCRM::User.find(1). There is also support for collections à la SugarCRM::User.find(1).email_addresses.
|
17
|
-
ActiveRecord style finders are in place, with limited support for conditions and joins
|
18
|
-
e.g. SugarCRM::Contacts.find_by_title("VP of Sales") will work, but SugarCRM::Contacts.find_by_title("VP of Sales", {:conditions => {:deleted => 0}}) will not.
|
15
|
+
Instead of SugarCRM.connection.get_entry("Users", "1"), you can use SugarCRM::User.find(1). There is also support for collections à la SugarCRM::User.find(1).email_addresses, or SugarCRM::Contact.first.meetings << new_meeting. ActiveRecord style finders are in place, with limited support for conditions and joins.
|
19
16
|
|
20
17
|
== FEATURES/PROBLEMS:
|
21
18
|
|
22
|
-
*
|
23
|
-
* Supports saving of
|
24
|
-
*
|
25
|
-
*
|
19
|
+
* Works with all v2 API calls
|
20
|
+
* Supports creation, saving, and deletion of SugarCRM specific objects.
|
21
|
+
* Validations, typecasting, and serialization of boolean, date, and integer fields
|
22
|
+
* Query, update and delete records from collections!
|
23
|
+
* ActiveRecord style finders!
|
24
|
+
* Auto-generation of SugarCRM specific objects. When a connection is established, get_available_modules is called and the resultant modules are turned into SugarCRM::Module classes.
|
25
|
+
* If you want to use the vanilla API, you can access the methods directly on the SugarCRM.connection object.
|
26
26
|
|
27
27
|
== SYNOPSIS:
|
28
28
|
|
@@ -50,32 +50,58 @@ e.g. SugarCRM::Contacts.find_by_title("VP of Sales") will work, but SugarCRM::Co
|
|
50
50
|
|
51
51
|
# Check if an object is valid (i.e. if it has the required fields to save)
|
52
52
|
u.valid?
|
53
|
-
|
53
|
+
|
54
54
|
# Access the errors collection
|
55
55
|
u.errors
|
56
56
|
|
57
|
+
# Show the fields required to save
|
58
|
+
u.required_attributes
|
59
|
+
|
57
60
|
# Delete an Account
|
58
61
|
a = SugarCRM::Account.find_by_name("JAB Funds Ltd.")
|
59
62
|
a.delete
|
60
63
|
|
61
|
-
# Retrieve all Email Addresses assigned to a particular
|
64
|
+
# Retrieve all Email Addresses assigned to a particular User.
|
62
65
|
SugarCRM::User.find_by_user_name('sarah').email_addresses
|
63
66
|
|
64
|
-
# Retrieve all
|
67
|
+
# Retrieve all Email Addresses on an Account
|
65
68
|
SugarCRM::Account.find_by_name("JAB Funds Ltd.").contacts.each do |contact|
|
66
69
|
contact.email_addresses.each do |email|
|
67
|
-
puts "#{email.email_address}" unless email.opt_out ==
|
70
|
+
puts "#{email.email_address}" unless email.opt_out == true
|
68
71
|
end
|
69
72
|
end
|
70
73
|
|
71
|
-
#
|
72
|
-
SugarCRM::
|
74
|
+
# Add a Meeting to a Contact
|
75
|
+
c = SugarCRM::Contact.first
|
76
|
+
c.meetings << SugarCRM::Meeting.new({
|
77
|
+
:name => "Product Introduction",
|
78
|
+
:date_start => DateTime.now,
|
79
|
+
:duration_hours => 1
|
80
|
+
})
|
81
|
+
c.save!
|
73
82
|
|
74
|
-
#
|
75
|
-
SugarCRM::
|
83
|
+
# Add a Contact to an Account
|
84
|
+
a = SugarCRM::Account.find_by_name("JAB Funds Ltd.")
|
85
|
+
c = SugarCRM::Contact.new
|
86
|
+
c.last_name = 'Doe'
|
87
|
+
a.contacts << c
|
88
|
+
a.save # or a.contacts.save
|
76
89
|
|
77
|
-
#
|
78
|
-
SugarCRM.
|
90
|
+
# Check if an Account has a specific Contact associated with it
|
91
|
+
c = SugarCRM::Contact.find_by_last_name("Doe")
|
92
|
+
a = SugarCRM::Account.find_by_name("JAB Funds Ltd.")
|
93
|
+
a.contacts.include?(c)
|
94
|
+
|
95
|
+
# Remove a Contact from an Account
|
96
|
+
c = SugarCRM::Contact.find_by_last_name("Doe")
|
97
|
+
a = SugarCRM::Account.find_by_name("JAB Funds Ltd.")
|
98
|
+
a.contacts.delete(c)
|
99
|
+
a.save # or a.contacts.save
|
100
|
+
|
101
|
+
# Look up the Case with the smallest case number
|
102
|
+
SugarCRM::Case.first({
|
103
|
+
:order_by => 'case_number'
|
104
|
+
})
|
79
105
|
|
80
106
|
# Retrieve the first 10 Accounts with a zip code between 10000 and 10500
|
81
107
|
SugarCRM::Account.all({
|
@@ -83,6 +109,20 @@ e.g. SugarCRM::Contacts.find_by_title("VP of Sales") will work, but SugarCRM::Co
|
|
83
109
|
:limit => '10',
|
84
110
|
:order_by => 'billing_address_postalcode'
|
85
111
|
})
|
112
|
+
|
113
|
+
# Retrieve all Accounts with a zip code
|
114
|
+
SugarCRM::Account.all({
|
115
|
+
:conditions => { :billing_address_postalcode => "<> NULL" }
|
116
|
+
})
|
117
|
+
|
118
|
+
# Look up the fields for a given module
|
119
|
+
SugarCRM::Module.find("Accounts").fields
|
120
|
+
|
121
|
+
# Look up the relationships for a given module
|
122
|
+
SugarCRM::Module.find("Accounts").link_fields
|
123
|
+
|
124
|
+
# Use the HTTP Connection and SugarCRM API to load the Admin user
|
125
|
+
SugarCRM.connection.get_entry("Users", 1)
|
86
126
|
|
87
127
|
# Retrieve all Accounts by user name (direct API method)
|
88
128
|
SugarCRM.connection.get_entry_list(
|
@@ -101,7 +141,6 @@ e.g. SugarCRM::Contacts.find_by_title("VP of Sales") will work, but SugarCRM::Co
|
|
101
141
|
== REQUIREMENTS:
|
102
142
|
|
103
143
|
* >= activesupport 3.0.0 gem
|
104
|
-
* json gem
|
105
144
|
|
106
145
|
== INSTALL:
|
107
146
|
|
data/Rakefile
CHANGED
@@ -6,14 +6,11 @@ begin
|
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "sugarcrm"
|
8
8
|
gem.summary = %Q{Ruby based REST client for SugarCRM}
|
9
|
-
gem.description = %Q{
|
10
|
-
on top of the basic API methods. The end result will be to provide ActiveRecord style finders and first class
|
11
|
-
objects. Some of this functionality is included today.}
|
9
|
+
gem.description = %Q{A less clunky way to interact with SugarCRM via REST. Instead of SugarCRM.connection.get_entry("Users", "1") you could use SugarCRM::User.find(1). There is support for collections à la SugarCRM::User.find(1).email_addresses, or SugarCRM::Contact.first.meetings << new_meeting. ActiveRecord style finders are in place, with limited support for conditions and joins.}
|
12
10
|
gem.email = "carl.hicks@gmail.com"
|
13
11
|
gem.homepage = "http://github.com/chicks/sugarcrm"
|
14
12
|
gem.authors = ["Carl Hicks"]
|
15
13
|
gem.add_development_dependency "shoulda", ">= 0"
|
16
|
-
gem.add_dependency "json", ">= 0"
|
17
14
|
gem.add_dependency "activesupport", ">= 3.0"
|
18
15
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
19
16
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.9.0
|
data/lib/sugarcrm.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
|
+
require 'net/https'
|
1
2
|
require 'pp'
|
2
3
|
require 'set'
|
4
|
+
require 'uri'
|
3
5
|
require 'rubygems'
|
4
6
|
require 'active_support/core_ext'
|
5
7
|
|
6
8
|
require 'sugarcrm/module_methods'
|
7
|
-
require 'sugarcrm/base'
|
8
9
|
require 'sugarcrm/connection'
|
9
|
-
require 'sugarcrm/dynamic_finder_match'
|
10
10
|
require 'sugarcrm/exceptions'
|
11
|
+
require 'sugarcrm/attributes'
|
12
|
+
require 'sugarcrm/associations'
|
13
|
+
require 'sugarcrm/dynamic_finder_match'
|
11
14
|
require 'sugarcrm/module'
|
12
|
-
require 'sugarcrm/
|
13
|
-
|
15
|
+
require 'sugarcrm/base'
|
16
|
+
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module SugarCRM
|
2
|
+
# A class for handling association collections. Basically just an extension of Array
|
3
|
+
# doesn't actually load the records from Sugar until you invoke one of the public methods
|
4
|
+
class AssociationCollection
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
# creates a new instance of an AssociationCollection
|
8
|
+
# Owner is the parent object, and association is the target
|
9
|
+
def initialize(owner, association, preload=false)
|
10
|
+
@loaded = false
|
11
|
+
@owner = owner
|
12
|
+
@association= association
|
13
|
+
load if preload
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def changed?
|
18
|
+
return false unless loaded?
|
19
|
+
return true if added.length > 0
|
20
|
+
return true if removed.length > 0
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
def loaded?
|
25
|
+
@loaded
|
26
|
+
end
|
27
|
+
|
28
|
+
def load
|
29
|
+
load_associated_records unless loaded?
|
30
|
+
end
|
31
|
+
|
32
|
+
def reload
|
33
|
+
load_associated_records
|
34
|
+
end
|
35
|
+
|
36
|
+
def each(&block)
|
37
|
+
load
|
38
|
+
@collection.each(&block)
|
39
|
+
end
|
40
|
+
|
41
|
+
# we should probably delegate this
|
42
|
+
def length
|
43
|
+
load
|
44
|
+
@collection.length
|
45
|
+
end
|
46
|
+
|
47
|
+
# return any added elements
|
48
|
+
def added
|
49
|
+
load
|
50
|
+
@collection - @original
|
51
|
+
end
|
52
|
+
|
53
|
+
# return any removed elements
|
54
|
+
def removed
|
55
|
+
load
|
56
|
+
@original - @collection
|
57
|
+
end
|
58
|
+
|
59
|
+
# Removes an record from the collection, uses the id of the record as a test for inclusion.
|
60
|
+
def delete(record)
|
61
|
+
load
|
62
|
+
raise InvalidRecord, "#{record.class} does not have a valid :id!" if record.id.empty?
|
63
|
+
@collection.delete record
|
64
|
+
end
|
65
|
+
|
66
|
+
# Checks if a record is included in the current collection. Uses id's as comparison
|
67
|
+
def include?(record)
|
68
|
+
load
|
69
|
+
@collection.include? record
|
70
|
+
end
|
71
|
+
|
72
|
+
# Add +records+ to this association, saving any unsaved records before adding them.
|
73
|
+
# Returns +self+ so method calls may be chained.
|
74
|
+
# Be sure to call save on the association to commit any association changes
|
75
|
+
def <<(record)
|
76
|
+
load
|
77
|
+
record.save! if record.new?
|
78
|
+
result = true
|
79
|
+
result = false if include?(record)
|
80
|
+
@collection << record
|
81
|
+
result && self
|
82
|
+
end
|
83
|
+
alias :add :<<
|
84
|
+
|
85
|
+
def save
|
86
|
+
begin
|
87
|
+
save!
|
88
|
+
rescue
|
89
|
+
return false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Pushes collection changes to SugarCRM, and updates the state of the collection
|
94
|
+
def save!
|
95
|
+
load
|
96
|
+
added.each do |record|
|
97
|
+
associate!(record)
|
98
|
+
end
|
99
|
+
removed.each do |record|
|
100
|
+
disassociate!(record)
|
101
|
+
end
|
102
|
+
@original = @collection.dup
|
103
|
+
@original.freeze
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
protected
|
108
|
+
|
109
|
+
# Loads related records for the given association
|
110
|
+
def load_associated_records
|
111
|
+
array = SugarCRM.connection.get_relationships(@owner.class._module.name, @owner.id, @association.to_s)
|
112
|
+
@loaded = true
|
113
|
+
# we use original to track the state of the collection at start
|
114
|
+
@collection = Array.wrap(array).dup
|
115
|
+
@original = Array.wrap(array).freeze
|
116
|
+
end
|
117
|
+
|
118
|
+
# Creates a relationship between the current object and the target
|
119
|
+
# Owner is the record the Collection is accessed from
|
120
|
+
# Target is the record we are adding to the collection
|
121
|
+
# i.e. user.email_addresses.associate!(EmailAddress.new(:email_address => "abc@abc.com"))
|
122
|
+
# user would be the owner, and EmailAddress.new() is the target
|
123
|
+
def associate!(target, opts={})
|
124
|
+
#target.save! if target.new?
|
125
|
+
response = SugarCRM.connection.set_relationship(
|
126
|
+
@owner.class._module.name, @owner.id,
|
127
|
+
target.class._module.table_name, [target.id],
|
128
|
+
opts
|
129
|
+
)
|
130
|
+
raise AssociationFailed,
|
131
|
+
"Couldn't associate #{@owner.class._module.name}: #{@owner.id} -> #{target.class._module.table_name}:#{target.id}!" if response["failed"] > 0
|
132
|
+
true
|
133
|
+
end
|
134
|
+
|
135
|
+
# Removes a relationship between the current object and the target
|
136
|
+
def disassociate!(target)
|
137
|
+
associate!(target,{:delete => 1})
|
138
|
+
end
|
139
|
+
|
140
|
+
alias :relate! :associate!
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module SugarCRM; module AssociationMethods
|
2
|
+
|
3
|
+
module ClassMethods
|
4
|
+
# Returns an array of the module link fields
|
5
|
+
def associations_from_module_link_fields
|
6
|
+
self._module.link_fields.keys
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
attr :association_cache, false
|
11
|
+
|
12
|
+
def association_cached?(association)
|
13
|
+
@association_cache.keys.include? association.to_sym
|
14
|
+
end
|
15
|
+
|
16
|
+
def associations_changed?
|
17
|
+
@association_cache.values.each do |collection|
|
18
|
+
return true if collection.changed?
|
19
|
+
end
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def save_modified_associations
|
26
|
+
@association_cache.values.each do |collection|
|
27
|
+
if collection.changed?
|
28
|
+
return false unless collection.save
|
29
|
+
end
|
30
|
+
end
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def clear_association_cache
|
35
|
+
@association_cache = {}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generates the association proxy methods for related modules
|
39
|
+
def define_association_methods
|
40
|
+
return if association_methods_generated?
|
41
|
+
@associations.each do |k|
|
42
|
+
self.class.module_eval %Q?
|
43
|
+
def #{k}
|
44
|
+
query_association :#{k}
|
45
|
+
end
|
46
|
+
?
|
47
|
+
#seed_association_cache(k.to_syn)
|
48
|
+
end
|
49
|
+
self.class.association_methods_generated = true
|
50
|
+
end
|
51
|
+
|
52
|
+
# def seed_association_cache(association)
|
53
|
+
# @association_cache[association] = AssociationCollection.new(self,association)
|
54
|
+
# end
|
55
|
+
|
56
|
+
# Returns the records from the associated module or returns the cached copy if we've already
|
57
|
+
# loaded it. Force a reload of the records with reload=true
|
58
|
+
#
|
59
|
+
# {"email_addresses"=>
|
60
|
+
# {"name"=>"email_addresses",
|
61
|
+
# "module"=>"EmailAddress",
|
62
|
+
# "bean_name"=>"EmailAddress",
|
63
|
+
# "relationship"=>"users_email_addresses",
|
64
|
+
# "type"=>"link"},
|
65
|
+
#
|
66
|
+
def query_association(assoc, reload=false)
|
67
|
+
association = assoc.to_sym
|
68
|
+
return @association_cache[association] if association_cached?(association) && !reload
|
69
|
+
# TODO: Some relationships aren't fetchable via get_relationship (i.e users.contacts)
|
70
|
+
# even though get_module_fields lists them on the related_fields array. This is most
|
71
|
+
# commonly seen with one-to-many relationships without a join table. We need to cook
|
72
|
+
# up some elegant way to handle this.
|
73
|
+
collection = AssociationCollection.new(self,association,true)
|
74
|
+
# add it to the cache
|
75
|
+
@association_cache[association] = collection
|
76
|
+
collection
|
77
|
+
end
|
78
|
+
|
79
|
+
# Loads related records for the given association
|
80
|
+
# def load_associations_for(association)
|
81
|
+
# SugarCRM.connection.get_relationships(self.class._module.name, self.id, association.to_s)
|
82
|
+
# end
|
83
|
+
|
84
|
+
# pushes an element to the association collection
|
85
|
+
def append_to_association(association, record)
|
86
|
+
collection = query_association(association)
|
87
|
+
collection << record
|
88
|
+
collection
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
end; end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module SugarCRM; module AttributeMethods
|
2
|
+
|
3
|
+
module ClassMethods
|
4
|
+
# Returns a hash of the module fields from the module
|
5
|
+
# merges matching keys if another attributes hash is provided
|
6
|
+
def attributes_from_module_fields
|
7
|
+
fields = {}.with_indifferent_access
|
8
|
+
self._module.fields.keys.sort.each do |k|
|
9
|
+
fields[k] = nil
|
10
|
+
end
|
11
|
+
fields
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# TODO: Object.id is not being updated properly. Figure out why...
|
16
|
+
alias :pk :id
|
17
|
+
alias :primary_key :id
|
18
|
+
|
19
|
+
# Determines if attributes or associations have been changed
|
20
|
+
def changed?
|
21
|
+
return true if attributes_changed?
|
22
|
+
return true if associations_changed?
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def attributes_changed?
|
27
|
+
@modified_attributes.length > 0
|
28
|
+
end
|
29
|
+
|
30
|
+
# Is this a new record?
|
31
|
+
def new?
|
32
|
+
@attributes[:id].blank?
|
33
|
+
end
|
34
|
+
|
35
|
+
# List the required attributes for save
|
36
|
+
def required_attributes
|
37
|
+
self.class._module.required_fields
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
# Merges attributes provided as an argument to initialize
|
43
|
+
# with attributes from the module.fields array. Skips any
|
44
|
+
# fields that aren't in the module.fields array
|
45
|
+
#
|
46
|
+
# BUG: SugarCRM likes to return fields you don't ask for, and
|
47
|
+
# aren't fields on a module (i.e. modified_user_name). This
|
48
|
+
# royally screws up our typecasting code, so we handle it here.
|
49
|
+
def merge_attributes(attrs={})
|
50
|
+
# copy attributes from the parent module fields array
|
51
|
+
@attributes = self.class.attributes_from_module_fields
|
52
|
+
# populate the attributes with values from the attrs provided to init.
|
53
|
+
@attributes.keys.each do |name|
|
54
|
+
write_attribute name, attrs[name] if attrs[name]
|
55
|
+
end
|
56
|
+
# If this is an existing record, blank out the modified_attributes hash
|
57
|
+
@modified_attributes = {} unless new?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Generates get/set methods for keys in the attributes hash
|
61
|
+
def define_attribute_methods
|
62
|
+
return if attribute_methods_generated?
|
63
|
+
@attributes.keys.sort.each do |k|
|
64
|
+
self.class.module_eval %Q?
|
65
|
+
def #{k}
|
66
|
+
read_attribute :#{k}
|
67
|
+
end
|
68
|
+
def #{k}=(value)
|
69
|
+
write_attribute :#{k},value
|
70
|
+
end
|
71
|
+
?
|
72
|
+
end
|
73
|
+
self.class.attribute_methods_generated = true
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns an <tt>#inspect</tt>-like string for the value of the
|
77
|
+
# attribute +attr_name+. String attributes are elided after 50
|
78
|
+
# characters, and Date and Time attributes are returned in the
|
79
|
+
# <tt>:db</tt> format. Other attributes return the value of
|
80
|
+
# <tt>#inspect</tt> without modification.
|
81
|
+
#
|
82
|
+
# person = Person.create!(:name => "David Heinemeier Hansson " * 3)
|
83
|
+
#
|
84
|
+
# person.attribute_for_inspect(:name)
|
85
|
+
# # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
|
86
|
+
#
|
87
|
+
# person.attribute_for_inspect(:created_at)
|
88
|
+
# # => '"2009-01-12 04:48:57"'
|
89
|
+
def attribute_for_inspect(attr_name)
|
90
|
+
value = read_attribute(attr_name)
|
91
|
+
if value.is_a?(String) && value.length > 50
|
92
|
+
"#{value[0..50]}...".inspect
|
93
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
94
|
+
%("#{value.to_s(:db)}")
|
95
|
+
else
|
96
|
+
value.inspect
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Wrapper for invoking save on modified_attributes
|
101
|
+
# sets the id if it's a new record
|
102
|
+
def save_modified_attributes
|
103
|
+
# Complain if we aren't valid
|
104
|
+
raise InvalidRecord, errors.to_a.join(", ") if !valid?
|
105
|
+
# Send the save request
|
106
|
+
response = SugarCRM.connection.set_entry(self.class._module.name, serialize_modified_attributes)
|
107
|
+
# Complain if we don't get a parseable response back
|
108
|
+
raise RecordsaveFailed, "Failed to save record: #{self}. Response was not a Hash" unless response.is_a? Hash
|
109
|
+
# Complain if we don't get a valid id back
|
110
|
+
raise RecordSaveFailed, "Failed to save record: #{self}. Response did not contain a valid 'id'." if response["id"].nil?
|
111
|
+
# Save the id to the record, if it's a new record
|
112
|
+
@attributes[:id] = response["id"] if new?
|
113
|
+
raise InvalidRecord, "Failed to update id for: #{self}." if id.nil?
|
114
|
+
# Clear the modified attributes Hash
|
115
|
+
@modified_attributes = {}
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
# Wrapper around attributes hash
|
120
|
+
def read_attribute(key)
|
121
|
+
@attributes[key]
|
122
|
+
end
|
123
|
+
|
124
|
+
# Wrapper around attributes hash
|
125
|
+
def write_attribute(key, value)
|
126
|
+
@modified_attributes[key] = { :old => @attributes[key].to_s, :new => value }
|
127
|
+
@attributes[key] = value
|
128
|
+
end
|
129
|
+
|
130
|
+
end; end
|
131
|
+
|