sugarcrm 0.8.2 → 0.9.0
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.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
|
+
|