sugarcrm_emp 0.10.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.
Files changed (94) hide show
  1. data/.document +5 -0
  2. data/.gitignore +29 -0
  3. data/Gemfile +14 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +275 -0
  6. data/Rakefile +44 -0
  7. data/VERSION +1 -0
  8. data/WATCHLIST.rdoc +7 -0
  9. data/bin/sugarcrm +26 -0
  10. data/lib/rails/generators/sugarcrm/config/config_generator.rb +22 -0
  11. data/lib/rails/generators/sugarcrm/config/templates/initializer.rb +4 -0
  12. data/lib/rails/generators/sugarcrm/config/templates/sugarcrm.yml +19 -0
  13. data/lib/sugarcrm/associations/association.rb +170 -0
  14. data/lib/sugarcrm/associations/association_cache.rb +36 -0
  15. data/lib/sugarcrm/associations/association_collection.rb +141 -0
  16. data/lib/sugarcrm/associations/association_methods.rb +91 -0
  17. data/lib/sugarcrm/associations/associations.rb +61 -0
  18. data/lib/sugarcrm/associations.rb +5 -0
  19. data/lib/sugarcrm/attributes/attribute_methods.rb +203 -0
  20. data/lib/sugarcrm/attributes/attribute_serializers.rb +55 -0
  21. data/lib/sugarcrm/attributes/attribute_typecast.rb +44 -0
  22. data/lib/sugarcrm/attributes/attribute_validations.rb +62 -0
  23. data/lib/sugarcrm/attributes.rb +4 -0
  24. data/lib/sugarcrm/base.rb +355 -0
  25. data/lib/sugarcrm/config/sugarcrm.yaml +10 -0
  26. data/lib/sugarcrm/connection/api/get_available_modules.rb +22 -0
  27. data/lib/sugarcrm/connection/api/get_document_revision.rb +14 -0
  28. data/lib/sugarcrm/connection/api/get_entries.rb +23 -0
  29. data/lib/sugarcrm/connection/api/get_entries_count.rb +20 -0
  30. data/lib/sugarcrm/connection/api/get_entry.rb +23 -0
  31. data/lib/sugarcrm/connection/api/get_entry_list.rb +31 -0
  32. data/lib/sugarcrm/connection/api/get_module_fields.rb +15 -0
  33. data/lib/sugarcrm/connection/api/get_note_attachment.rb +14 -0
  34. data/lib/sugarcrm/connection/api/get_relationships.rb +30 -0
  35. data/lib/sugarcrm/connection/api/get_report_entries.rb +17 -0
  36. data/lib/sugarcrm/connection/api/get_server_info.rb +7 -0
  37. data/lib/sugarcrm/connection/api/get_user_id.rb +13 -0
  38. data/lib/sugarcrm/connection/api/get_user_team_id.rb +14 -0
  39. data/lib/sugarcrm/connection/api/login.rb +18 -0
  40. data/lib/sugarcrm/connection/api/logout.rb +15 -0
  41. data/lib/sugarcrm/connection/api/seamless_login.rb +13 -0
  42. data/lib/sugarcrm/connection/api/search_by_module.rb +25 -0
  43. data/lib/sugarcrm/connection/api/set_campaign_merge.rb +15 -0
  44. data/lib/sugarcrm/connection/api/set_document_revision.rb +35 -0
  45. data/lib/sugarcrm/connection/api/set_entries.rb +15 -0
  46. data/lib/sugarcrm/connection/api/set_entry.rb +15 -0
  47. data/lib/sugarcrm/connection/api/set_note_attachment.rb +25 -0
  48. data/lib/sugarcrm/connection/api/set_relationship.rb +27 -0
  49. data/lib/sugarcrm/connection/api/set_relationships.rb +22 -0
  50. data/lib/sugarcrm/connection/connection.rb +201 -0
  51. data/lib/sugarcrm/connection/helper.rb +50 -0
  52. data/lib/sugarcrm/connection/request.rb +61 -0
  53. data/lib/sugarcrm/connection/response.rb +91 -0
  54. data/lib/sugarcrm/connection.rb +5 -0
  55. data/lib/sugarcrm/connection_pool.rb +163 -0
  56. data/lib/sugarcrm/exceptions.rb +23 -0
  57. data/lib/sugarcrm/extensions/README.txt +23 -0
  58. data/lib/sugarcrm/finders/dynamic_finder_match.rb +41 -0
  59. data/lib/sugarcrm/finders/finder_methods.rb +243 -0
  60. data/lib/sugarcrm/finders.rb +2 -0
  61. data/lib/sugarcrm/module.rb +174 -0
  62. data/lib/sugarcrm/module_methods.rb +91 -0
  63. data/lib/sugarcrm/session.rb +218 -0
  64. data/lib/sugarcrm.rb +22 -0
  65. data/sugarcrm.gemspec +178 -0
  66. data/test/config_test.yaml +15 -0
  67. data/test/connection/test_get_available_modules.rb +9 -0
  68. data/test/connection/test_get_entries.rb +15 -0
  69. data/test/connection/test_get_entry.rb +22 -0
  70. data/test/connection/test_get_entry_list.rb +23 -0
  71. data/test/connection/test_get_module_fields.rb +11 -0
  72. data/test/connection/test_get_relationships.rb +12 -0
  73. data/test/connection/test_get_server_info.rb +9 -0
  74. data/test/connection/test_get_user_id.rb +9 -0
  75. data/test/connection/test_get_user_team_id.rb +9 -0
  76. data/test/connection/test_login.rb +9 -0
  77. data/test/connection/test_logout.rb +9 -0
  78. data/test/connection/test_set_document_revision.rb +28 -0
  79. data/test/connection/test_set_entry.rb +15 -0
  80. data/test/connection/test_set_note_attachment.rb +16 -0
  81. data/test/connection/test_set_relationship.rb +18 -0
  82. data/test/extensions_test/patch.rb +9 -0
  83. data/test/helper.rb +17 -0
  84. data/test/test_association_collection.rb +11 -0
  85. data/test/test_associations.rb +156 -0
  86. data/test/test_connection.rb +13 -0
  87. data/test/test_connection_pool.rb +40 -0
  88. data/test/test_finders.rb +201 -0
  89. data/test/test_module.rb +51 -0
  90. data/test/test_request.rb +35 -0
  91. data/test/test_response.rb +26 -0
  92. data/test/test_session.rb +136 -0
  93. data/test/test_sugarcrm.rb +213 -0
  94. metadata +266 -0
@@ -0,0 +1,25 @@
1
+ module SugarCRM; class Connection
2
+ # Returns the ID, module name and fields for specified modules.
3
+ # Supported modules are Accounts, Bugs, Calls, Cases, Contacts,
4
+ # Leads, Opportunities, Projects, Project Tasks, and Quotes.
5
+ def search_by_module(search_string, modules, opts={})
6
+ login! unless logged_in?
7
+
8
+ options = {
9
+ :offset => nil,
10
+ :limit => nil,
11
+ }.merge! opts
12
+
13
+ json = <<-EOF
14
+ {
15
+ "session": "#{@sugar_session_id}",
16
+ "search_string": "#{search_string}",
17
+ "modules": "#{modules}",
18
+ "offset": #{options[:offset]},
19
+ "max_results": #{options[:limit]}
20
+ }
21
+ EOF
22
+ json.gsub!(/^\s{6}/,'')
23
+ send!(:search_by_module, json)
24
+ end
25
+ end; end
@@ -0,0 +1,15 @@
1
+ module SugarCRM; class Connection
2
+ # Performs a mail merge for the specified campaign.
3
+ def set_campaign_merge(targets, campaign_id)
4
+ login! unless logged_in?
5
+ json = <<-EOF
6
+ {
7
+ "session": "#{@sugar_session_id}",
8
+ "targets": #{targets.to_json},
9
+ "campaign-id": "#{campaign_id}"
10
+ }
11
+ EOF
12
+ json.gsub!(/^\s{6}/,'')
13
+ send!(:set_campaign_merge, json)
14
+ end
15
+ end; end
@@ -0,0 +1,35 @@
1
+ module SugarCRM; class Connection
2
+ # Sets a new revision for a document.
3
+ def set_document_revision(document_id, revision_number, opts={})
4
+ options = {
5
+ :file => '',
6
+ :file_name => '',
7
+ :document_name => nil
8
+ }.merge! opts
9
+
10
+ # Raise an exception of we try to pass :file, but not :file_name
11
+ if (!options[:file].empty? && options[:file_name].empty?)
12
+ raise ArgumentException, ":file_name must be specified if :file is specified"
13
+ end
14
+
15
+ # If no document_name is given, use the file_name
16
+ options[:document_name] ||= options[:file_name]
17
+
18
+ login! unless logged_in?
19
+
20
+ json = <<-EOF
21
+ {
22
+ "session": "#{@sugar_session_id}",
23
+ "document_revision": {
24
+ "id": "#{document_id}",
25
+ "document_name": "#{options[:document_name]}",
26
+ "revision": "#{revision_number}",
27
+ "filename": "#{options[:file_name]}",
28
+ "file": "#{b64_encode(options[:file])}"
29
+ }
30
+ }
31
+ EOF
32
+ json.gsub!(/^\s{6}/,'')
33
+ send!(:set_document_revision, json)
34
+ end
35
+ end; end
@@ -0,0 +1,15 @@
1
+ module SugarCRM; class Connection
2
+ # Creates or updates a list of SugarBeans.
3
+ def set_entries(module_name, name_value_lists)
4
+ login! unless logged_in?
5
+ json = <<-EOF
6
+ {
7
+ "session": "#{@sugar_session_id}",
8
+ "module_name": "#{module_name}",
9
+ "name_value_list": #{name_value_lists.to_json}
10
+ }
11
+ EOF
12
+ json.gsub!(/^\s{6}/,'')
13
+ send!(:set_entries, json)
14
+ end
15
+ end; end
@@ -0,0 +1,15 @@
1
+ module SugarCRM; class Connection
2
+ # Creates or updates a single SugarBean.
3
+ def set_entry(module_name, name_value_list)
4
+ login! unless logged_in?
5
+ json = <<-EOF
6
+ {
7
+ "session": "#{@sugar_session_id}",
8
+ "module_name": "#{module_name}",
9
+ "name_value_list": #{name_value_list.to_json}
10
+ }
11
+ EOF
12
+ json.gsub!(/^\s{6}/,'')
13
+ send!(:set_entry, json)
14
+ end
15
+ end; end
@@ -0,0 +1,25 @@
1
+ module SugarCRM; class Connection
2
+ # Creates or updates an attachment on a note
3
+ def set_note_attachment(id, filename, file, opts={})
4
+ options = {
5
+ :module_id => '',
6
+ :module_name => ''
7
+ }.merge! opts
8
+
9
+ login! unless logged_in?
10
+ json = <<-EOF
11
+ {
12
+ "session": "#{@sugar_session_id}",
13
+ "note": {
14
+ "id": "#{id}",
15
+ "filename": "#{filename}",
16
+ "file": "#{b64_encode(file)}",
17
+ "related_module_id": "#{options[:module_id]}",
18
+ "related_module_name": "#{options[:module_name]}"
19
+ }
20
+ }
21
+ EOF
22
+ json.gsub!(/^\s{6}/,'')
23
+ send!(:set_note_attachment, json)
24
+ end
25
+ end; end
@@ -0,0 +1,27 @@
1
+ module SugarCRM; class Connection
2
+ # Sets a single relationship between two SugarBeans.
3
+ def set_relationship(module_name, module_id, link_field_name, related_ids, opts={})
4
+ login! unless logged_in?
5
+ options = {
6
+ :name_value_list => [],
7
+ :delete => 0,
8
+ }.merge! opts
9
+ raise ArgumentError, "related_ids must be an Array" unless related_ids.class == Array
10
+ json = <<-EOF
11
+ {
12
+ "session": "#{@sugar_session_id}",
13
+ "module_name": "#{module_name}",
14
+ "module_id": "#{module_id}",
15
+ "link_field_name": "#{link_field_name}",
16
+ "related_ids": #{related_ids.to_json},
17
+ "name_value_list": #{options[:name_value_list].to_json},
18
+ "delete": #{options[:delete]}
19
+ }
20
+ EOF
21
+ json.gsub!(/^\s{6}/,'')
22
+ # TODO: Add a handler for the response. By default it returns a hash like:
23
+ # {failed => 0, deleted => 0, created => 1}. We should add this to the
24
+ # Response.handle method and return true/false depending on the outcome.
25
+ send!(:set_relationship, json)
26
+ end
27
+ end; end
@@ -0,0 +1,22 @@
1
+ module SugarCRM; class Connection
2
+ # Sets multiple relationships between two SugarBeans.
3
+ def set_relationships(module_names, module_ids, link_field_names, related_ids)
4
+ login! unless logged_in?
5
+
6
+ [module_names, module_ids, link_field_names, related_ids].each do |arg|
7
+ raise ArgumentError, "argument must be an Array" unless arg.class == Array
8
+ end
9
+
10
+ json = <<-EOF
11
+ {
12
+ "session": "#{@sugar_session_id}",
13
+ "module_names": "#{module_names.to_json}",
14
+ "module_ids": #{module_ids.to_json},
15
+ "link_field_names": #{link_field_names.to_json},
16
+ "related_ids": #{related_ids.to_json}
17
+ }
18
+ EOF
19
+ json.gsub!(/^\s{6}/,'')
20
+ send!(:set_relationships, json)
21
+ end
22
+ end; end
@@ -0,0 +1,201 @@
1
+ module SugarCRM; class Connection
2
+
3
+ URL = "/service/v2/rest.php"
4
+ # Set this to filter out debug output on a certain method (i.e. get_modules, or get_fields)
5
+ DONT_SHOW_DEBUG_FOR = []
6
+ RESPONSE_IS_NOT_JSON = [:get_user_id, :get_user_team_id]
7
+
8
+ attr :url, true
9
+ attr :user, false
10
+ attr :pass, false
11
+ attr :session, true
12
+ attr :sugar_session_id, true
13
+ attr :connection, true
14
+ attr :options, true
15
+ attr :request, true
16
+ attr :response, true
17
+ attr :errors, true
18
+
19
+ # This is the singleton connection class.
20
+ def initialize(url, user, pass, options={})
21
+ @options = {
22
+ :debug => false,
23
+ :register_modules => true,
24
+ :load_environment => true
25
+ }.merge(options)
26
+ @errors = []
27
+ @url = URI.parse(url)
28
+ @user = user
29
+ @pass = pass
30
+ @request = ""
31
+ @response = ""
32
+ resolve_url
33
+ login!
34
+ self
35
+ end
36
+
37
+ # Check to see if we are logged in
38
+ def logged_in?
39
+ connect! unless connected?
40
+ @sugar_session_id ? true : false
41
+ end
42
+
43
+ # Login
44
+ def login!
45
+ @sugar_session_id = login["id"]
46
+ raise SugarCRM::LoginError, "Invalid Login" unless logged_in?
47
+ end
48
+
49
+ def logout
50
+ logout
51
+ @sugar_session_id = nil
52
+ end
53
+
54
+ # Check to see if we are connected
55
+ def connected?
56
+ return false unless @connection
57
+ true
58
+ end
59
+
60
+ # Connect
61
+ def connect!
62
+ @connection = HTTPClient.new
63
+ end
64
+ alias :reconnect! :connect!
65
+
66
+ # Send a request to the Sugar Instance
67
+ def send!(method, json, max_retry=3)
68
+ if max_retry == 0
69
+ raise SugarCRM::RetryLimitExceeded, "SugarCRM::Connection Errors: \n#{@errors.reverse.join "\n\s\s"}"
70
+ end
71
+ @request = SugarCRM::Request.new(@url, method, json, @options[:debug])
72
+ # Send Ze Reques
73
+ begin
74
+ if @request.length > 3900
75
+ @response = @connection.post(@url, @request)
76
+ else
77
+ @response = @connection.get(@url, @request)
78
+ end
79
+ return handle_response
80
+ # Timeouts are usually a server side issue
81
+ rescue Timeout::Error => error
82
+ @errors << error
83
+ send!(method, json, max_retry.pred)
84
+ # Lower level errors requiring a reconnect
85
+ rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE, EOFError => error
86
+ @errors << error
87
+ reconnect!
88
+ send!(method, json, max_retry.pred)
89
+ # Handle invalid sessions
90
+ rescue SugarCRM::InvalidSession => error
91
+ @errors << error
92
+ old_session = @sugar_session_id.dup
93
+ login!
94
+ # Update the session id in the request that we want to retry.
95
+ json.gsub!(old_session, @sugar_session_id)
96
+ send!(method, json, max_retry.pred)
97
+ end
98
+ end
99
+ alias :retry! :send!
100
+
101
+ def debug=(debug)
102
+ options[:debug] = debug
103
+ end
104
+
105
+ def debug?
106
+ options[:debug]
107
+ end
108
+
109
+ private
110
+
111
+ def handle_response
112
+ case @response.status
113
+ when 200
114
+ return process_response
115
+ when 404
116
+ raise SugarCRM::InvalidSugarCRMUrl, "#{@url} is invalid"
117
+ when 500
118
+ raise SugarCRM::InvalidRequest, "#{@request} is invalid"
119
+ else
120
+ if @options[:debug]
121
+ puts "#{@request.method}: Raw Response:"
122
+ puts @response.body
123
+ puts "\n"
124
+ end
125
+ raise SugarCRM::UnhandledResponse, "Can't handle response #{@response}"
126
+ end
127
+ end
128
+
129
+ def process_response
130
+ empty_body?
131
+ if response_contains_json?
132
+ return parse_response
133
+ else
134
+ return @response.body
135
+ end
136
+ end
137
+
138
+ def resolve_url
139
+ # Appends the rest.php path onto the end of the URL if it's not included
140
+ if @url.path !~ /rest.php$/
141
+ @url.path += URL
142
+ end
143
+ end
144
+
145
+ # Complain if our body is empty.
146
+ def empty_body?
147
+ raise SugarCRM::EmptyResponse unless @response.body
148
+ end
149
+
150
+ # Some methods are dumb and don't return a JSON Response
151
+ def response_contains_json?
152
+ if RESPONSE_IS_NOT_JSON.include? @request.method
153
+ return false
154
+ end
155
+ true
156
+ end
157
+
158
+ def parse_response
159
+ begin
160
+ # Push it through the old meat grinder.
161
+ json = JSON.parse(@response.body)
162
+ rescue StandardError => e
163
+ # Complain if we can't parse
164
+ raise UnhandledResponse, @response.body
165
+ end
166
+ # Do ze debugs!
167
+ nice_debugging_for json
168
+ # Check for an invalid session
169
+ invalid_session? json
170
+ # Check for an empty result set
171
+ if zero_results? json
172
+ return nil
173
+ end
174
+ json
175
+ end
176
+
177
+ # Check if we got an invalid session error back
178
+ # something like:
179
+ # {"name"=>"Invalid Session ID",
180
+ # "number"=>11,
181
+ # "description"=>"The session ID is invalid"}
182
+ def invalid_session?(json)
183
+ return false unless json["name"]
184
+ return false if @request.method == :logout
185
+ raise SugarCRM::InvalidSession if json["name"] == "Invalid Session ID"
186
+ end
187
+
188
+ def zero_results?(json)
189
+ json["result_count"] == 0
190
+ end
191
+
192
+ # Filter debugging on REALLY BIG responses
193
+ def nice_debugging_for(json)
194
+ if @options[:debug] && !(DONT_SHOW_DEBUG_FOR.include? @request.method)
195
+ puts "#{@request.method}: JSON Response:"
196
+ pp json
197
+ puts "\n"
198
+ end
199
+ end
200
+
201
+ end; end
@@ -0,0 +1,50 @@
1
+ module SugarCRM; class Connection
2
+ # Attempts to return a list of fields for the target of the association.
3
+ # i.e. if we are associating Contact -> Account, using the "contacts" link
4
+ # field name - this will lookup the contacts association and try to determine
5
+ # the target object type (Contact). It will then pull the fields for that object
6
+ # and shove them in the related_fields portion of the get_relationship request.
7
+ def resolve_related_fields(module_name, link_field)
8
+ a = Association.new(class_for(module_name), link_field)
9
+ if a.target
10
+ fields = a.target.new.attributes.keys
11
+ else
12
+ fields = ["id"]
13
+ end
14
+ fields.to_json
15
+ end
16
+
17
+ def resolve_fields(module_name, fields)
18
+ # FIXME: This is to work around a bug in SugarCRM 6.0
19
+ # where no fields are returned if no fields are specified
20
+ if fields.length == 0
21
+ mod = Module.find(module_name.classify, @session)
22
+ if mod
23
+ fields = mod.fields.keys
24
+ else
25
+ fields = ["id"]
26
+ end
27
+ end
28
+ return fields.to_json
29
+ end
30
+
31
+ # Returns an instance of class for the provided module name
32
+ def class_for(module_name)
33
+ begin
34
+ class_const = @session.namespace_const.const_get(module_name.classify)
35
+ klass = class_const.new
36
+ rescue NameError
37
+ raise InvalidModule, "Module: #{module_name} is not registered"
38
+ end
39
+ end
40
+
41
+ # We need to strip newlines from Base64 encoding for JSON validation purposes.
42
+ def b64_encode(file)
43
+ Base64.encode64(file).gsub(/\n/, '')
44
+ end
45
+
46
+ def b64_decode(file)
47
+ Base64.decode64(file)
48
+ end
49
+
50
+ end; end
@@ -0,0 +1,61 @@
1
+ module SugarCRM; class Request
2
+ attr :request, true
3
+ attr :url, true
4
+ attr :method, true
5
+ attr :json, true
6
+ attr :http_method
7
+
8
+ def initialize(url, method, json, debug=false)
9
+ @url = url
10
+ @method = method
11
+ @json = escape(json)
12
+ @request = 'method=' << @method.to_s
13
+ @request << '&input_type=JSON'
14
+ @request << '&response_type=JSON'
15
+ @request << '&rest_data=' << @json
16
+ if debug
17
+ puts "#{method}: Request:"
18
+ puts json
19
+ puts "\n"
20
+ end
21
+ self
22
+ end
23
+
24
+ def escape(json)
25
+ # BUG: SugarCRM doesn't properly handle '&quot;' inside of JSON for some reason. Let's unescape any html elements.
26
+ j = convert_reserved_characters(json)
27
+ # Now we escape the resulting string.
28
+ j = CGI.escape(j)
29
+ j
30
+ end
31
+
32
+ # TODO: Fix this so that it JSON.parse will consume it.
33
+ def unescape
34
+ j = CGI.unescape(@json)
35
+ j.gsub!(/\n/, '')
36
+ end
37
+
38
+ def bytesize
39
+ self.to_s.bytesize
40
+ end
41
+
42
+ def length
43
+ self.to_s.length
44
+ end
45
+
46
+ def to_s
47
+ @request
48
+ end
49
+ alias :to_str :to_s
50
+
51
+ # A tiny helper for converting reserved characters for html encoding
52
+ def convert_reserved_characters(string)
53
+ string.gsub!(/&quot;/, '\"')
54
+ string.gsub!(/&apos;/, '\'')
55
+ string.gsub!(/&amp;/, '\&')
56
+ string.gsub!(/&lt;/, '\<')
57
+ string.gsub!(/&lt;/, '\>')
58
+ string
59
+ end
60
+
61
+ end; end
@@ -0,0 +1,91 @@
1
+ module SugarCRM; class Response
2
+ class << self
3
+ # This class handles the response from the server.
4
+ # It tries to convert the response into an object such as User
5
+ # or an object collection. If it fails, it just returns the response hash
6
+ def handle(json, session)
7
+ r = new(json, session)
8
+ begin
9
+ return r.to_obj
10
+ rescue UninitializedModule => e
11
+ raise e
12
+ rescue InvalidAttribute => e
13
+ raise e
14
+ rescue InvalidAttributeType => e
15
+ raise e
16
+ rescue => e
17
+ if session.connection.debug?
18
+ puts "Failed to process JSON:"
19
+ pp json
20
+ end
21
+ raise e
22
+ end
23
+ end
24
+ end
25
+
26
+ attr :response, false
27
+
28
+ def initialize(json, session, opts={})
29
+ @options = { :always_return_array => false }.merge! opts
30
+ @response = json
31
+ @response = json.with_indifferent_access if json.is_a? Hash
32
+ @session = session
33
+ end
34
+
35
+ # Tries to instantiate and return an object with the values
36
+ # populated from the response
37
+ def to_obj
38
+ # If this is not a "entry_list" response, just return
39
+ return @response unless @response && @response["entry_list"]
40
+
41
+ objects = []
42
+ @response["entry_list"].each do |object|
43
+ attributes = []
44
+ _module = resolve_module(object)
45
+ attributes = flatten_name_value_list(object)
46
+ namespace = @session.namespace_const
47
+ if namespace.const_get(_module)
48
+ if attributes.length == 0
49
+ raise AttributeParsingError, "response contains objects without attributes!"
50
+ end
51
+ objects << namespace.const_get(_module).new(attributes)
52
+ else
53
+ raise InvalidModule, "#{_module} does not exist, or is not accessible"
54
+ end
55
+ end
56
+ # If we only have one result, just return the object
57
+ if objects.length == 1 && !@options[:always_return_array]
58
+ return objects[0]
59
+ else
60
+ return objects
61
+ end
62
+ end
63
+
64
+ def to_json
65
+ @response.to_json
66
+ end
67
+
68
+ def resolve_module(list)
69
+ list["module_name"].classify
70
+ end
71
+
72
+ def flatten_name_value_list(list)
73
+ if list["name_value_list"]
74
+ return flatten(list["name_value_list"])
75
+ else
76
+ return false
77
+ end
78
+ end
79
+
80
+ # Takes a hash like { "first_name" => {"name" => "first_name", "value" => "John"}}
81
+ # And flattens it into {"first_name" => "John"}
82
+ def flatten(list)
83
+ raise ArgumentError, list[0]['value'] if list[0] && list[0]['name'] == 'warning'
84
+ raise ArgumentError, 'method parameter must respond to #each_pair' unless list.respond_to? :each_pair
85
+ flat_list = {}
86
+ list.each_pair do |k,v|
87
+ flat_list[k.to_sym] = v["value"]
88
+ end
89
+ flat_list
90
+ end
91
+ end; end
@@ -0,0 +1,5 @@
1
+ require 'sugarcrm/connection/helper'
2
+ require 'sugarcrm/connection/connection'
3
+ require 'sugarcrm/connection/request'
4
+ require 'sugarcrm/connection/response'
5
+ Dir["#{File.dirname(__FILE__)}/connection/api/*.rb"].each { |f| load(f) }