sugarcrm 0.9.8 → 0.9.9
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/Gemfile +1 -1
- data/README.rdoc +44 -0
- data/Rakefile +6 -6
- data/VERSION +1 -1
- data/lib/sugarcrm.rb +3 -0
- data/lib/sugarcrm/associations/association.rb +14 -24
- data/lib/sugarcrm/associations/association_cache.rb +12 -6
- data/lib/sugarcrm/associations/association_collection.rb +4 -1
- data/lib/sugarcrm/associations/association_methods.rb +17 -2
- data/lib/sugarcrm/base.rb +54 -4
- data/lib/sugarcrm/config/sugarcrm.yaml +10 -0
- data/lib/sugarcrm/connection/api/get_document_revision.rb +0 -1
- data/lib/sugarcrm/connection/api/get_entries.rb +1 -1
- data/lib/sugarcrm/connection/api/get_entry.rb +1 -1
- data/lib/sugarcrm/connection/api/login.rb +6 -6
- data/lib/sugarcrm/connection/connection.rb +6 -3
- data/lib/sugarcrm/connection/request.rb +3 -3
- data/lib/sugarcrm/connection/response.rb +2 -3
- data/lib/sugarcrm/environment.rb +48 -0
- data/lib/sugarcrm/extensions/README.txt +23 -0
- data/lib/sugarcrm/module.rb +10 -3
- data/lib/sugarcrm/module_methods.rb +18 -1
- data/test/config_test.yaml +15 -0
- data/test/extensions_test/patch.rb +9 -0
- data/test/test_association.rb +18 -1
- data/test/test_environment.rb +38 -0
- data/test/test_sugarcrm.rb +103 -12
- metadata +48 -32
data/Gemfile
CHANGED
@@ -2,6 +2,7 @@ source "http://rubygems.org"
|
|
2
2
|
|
3
3
|
gem "activesupport", ">= 3.0.0", :require => "active_support"
|
4
4
|
gem "i18n"
|
5
|
+
gem "json"
|
5
6
|
|
6
7
|
# Add dependencies to develop your gem here.
|
7
8
|
# Include everything needed to run rake, tests, features, etc.
|
@@ -9,5 +10,4 @@ group :development do
|
|
9
10
|
gem "shoulda", ">= 0"
|
10
11
|
gem "bundler", "~> 1.0.0"
|
11
12
|
gem "jeweler", "~> 1.5.2"
|
12
|
-
gem "rcov", ">= 0"
|
13
13
|
end
|
data/README.rdoc
CHANGED
@@ -143,6 +143,46 @@ Instead of SugarCRM.connection.get_entry("Users", "1") you could use SugarCRM::U
|
|
143
143
|
}
|
144
144
|
)
|
145
145
|
|
146
|
+
== USING A CONFIGURATION FILE
|
147
|
+
|
148
|
+
If you want to use a configuration file instead of always specifying the url, username, and password to connect to SugarCRM, you can either
|
149
|
+
|
150
|
+
* add your credentials to `/etc/sugarcrm.yaml`
|
151
|
+
* add your credentials to `~/.sugarcrm.yaml`
|
152
|
+
* add your credentials to `config/sugarcrm.yaml` (will need to be copied each time you upgrade or reinstall the gem)
|
153
|
+
* add your credentials to a YAML file and call `SugarCRM::Environment.load_config` followed by the absolute path to your configuration file
|
154
|
+
|
155
|
+
If there are several configuration files, they are loaded sequentially in the order above and will overwrite previous values (if present). This allows you to (e.g.) have a config file in `/etc/sugarcrm.yaml` with system-wide configuration information (such as the url where SugarCRM is located) and/or defaults. Each developer/user can then have his personal configuration file in `~/.sugarcrm.yaml` with his own username and password. A developer could also specify a different location for the SugarCRM instance (e.g. a local testing instance) in his configuration file, which will take precedence over the value in `/etc/sugarcrm.yaml`.
|
156
|
+
|
157
|
+
Your configuration should be in YAML format:
|
158
|
+
|
159
|
+
config:
|
160
|
+
base_url: http://127.0.0.1/sugarcrm
|
161
|
+
username: admin
|
162
|
+
password: letmein
|
163
|
+
|
164
|
+
An example, accompanied by instructions, can be found in the `config/sugarcrm.yaml` file. In addition, a working example used for testing can be found in `test/config_test.yaml`
|
165
|
+
|
166
|
+
== USING THE GEM IN A CONSOLE
|
167
|
+
|
168
|
+
1. Type `irb` in your command prompt
|
169
|
+
|
170
|
+
2. Require the gem with `require 'sugarcrm'`
|
171
|
+
|
172
|
+
3. * if your login credentials are stored in the `config/sugarcrm.yaml` file, you have been automagically logged in already ;
|
173
|
+
* if your login credentials are stored in a different config file, just call `SugarCRM::Environment.load_config` followed by the absolute path to your config file. This will log you in automatically ;
|
174
|
+
* if you don't have a configuration file, you can still call the basic `SugarCRM.connect` and give it the proper arguments (see documentation above)
|
175
|
+
|
176
|
+
4. You now have full access to the gem's functionality, e.g. `puts SugarCRM::Account.first.name`
|
177
|
+
|
178
|
+
== EXPANDING THE GEM
|
179
|
+
|
180
|
+
If you want to expand the gem's capabilities (e.g. to add methods specific to your environment), you can either
|
181
|
+
|
182
|
+
* drop your `*.rb` files in `lib/sugarcrm/extensions/` (see the README in that folder)
|
183
|
+
|
184
|
+
* drop your `*.rb` files in any other folder and call `SugarCRM::Environment.extensions_folder = ` followed by the absolute path to the folder containing your extensions
|
185
|
+
|
146
186
|
== REQUIREMENTS:
|
147
187
|
|
148
188
|
* >= activesupport 3.0.0 gem
|
@@ -151,6 +191,10 @@ Instead of SugarCRM.connection.get_entry("Users", "1") you could use SugarCRM::U
|
|
151
191
|
|
152
192
|
* sudo gem install sugarcrm
|
153
193
|
|
194
|
+
== TEST:
|
195
|
+
|
196
|
+
Set the values in `test/helper.rb` to point to a SugarCRM instance with demo data
|
197
|
+
|
154
198
|
== Note on Patches/Pull Requests
|
155
199
|
|
156
200
|
* Fork the project.
|
data/Rakefile
CHANGED
@@ -28,12 +28,12 @@ Rake::TestTask.new(:test) do |test|
|
|
28
28
|
test.verbose = true
|
29
29
|
end
|
30
30
|
|
31
|
-
require 'rcov/rcovtask'
|
32
|
-
Rcov::RcovTask.new do |test|
|
33
|
-
test.libs << 'test'
|
34
|
-
test.pattern = 'test/**/test_*.rb'
|
35
|
-
test.verbose = true
|
36
|
-
end
|
31
|
+
#require 'rcov/rcovtask'
|
32
|
+
#Rcov::RcovTask.new do |test|
|
33
|
+
# test.libs << 'test'
|
34
|
+
# test.pattern = 'test/**/test_*.rb'
|
35
|
+
# test.verbose = true
|
36
|
+
#end
|
37
37
|
|
38
38
|
task :default => :test
|
39
39
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
1
|
+
0.9.9
|
data/lib/sugarcrm.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'net/https'
|
2
2
|
require 'pp'
|
3
3
|
require 'set'
|
4
|
+
require 'cgi'
|
4
5
|
require 'uri'
|
5
6
|
require 'rubygems'
|
6
7
|
require 'active_support/core_ext'
|
8
|
+
require 'json'
|
7
9
|
|
10
|
+
require 'sugarcrm/environment'
|
8
11
|
require 'sugarcrm/module_methods'
|
9
12
|
require 'sugarcrm/connection'
|
10
13
|
require 'sugarcrm/exceptions'
|
@@ -1,12 +1,13 @@
|
|
1
1
|
module SugarCRM
|
2
2
|
# Represents an association and it's metadata
|
3
3
|
class Association
|
4
|
-
attr :owner
|
5
|
-
attr :target
|
6
|
-
attr :link_field
|
7
|
-
attr :attributes
|
8
|
-
attr :methods
|
4
|
+
attr :owner, true
|
5
|
+
attr :target, true
|
6
|
+
attr :link_field, true
|
7
|
+
attr :attributes, true
|
8
|
+
attr :methods, true
|
9
9
|
|
10
|
+
# TODO: Describe this.
|
10
11
|
def initialize(owner,link_field,opts={})
|
11
12
|
@options = { :define_methods? => true }.merge! opts
|
12
13
|
@owner = owner
|
@@ -35,11 +36,11 @@ module SugarCRM
|
|
35
36
|
end
|
36
37
|
|
37
38
|
# Attempts to determine the class of the target in the association
|
39
|
+
# TODO: Write tests for this.
|
38
40
|
def resolve_target
|
39
41
|
# Use the link_field name first
|
40
42
|
klass = @link_field.singularize.camelize
|
41
43
|
return "SugarCRM::#{klass}".constantize if SugarCRM.const_defined? klass
|
42
|
-
|
43
44
|
# Use the link_field attribute "module"
|
44
45
|
if @attributes["module"].length > 0
|
45
46
|
module_name = SugarCRM::Module.find(@attributes["module"])
|
@@ -54,14 +55,12 @@ module SugarCRM
|
|
54
55
|
end
|
55
56
|
|
56
57
|
# Generates the association proxy method for related module
|
57
|
-
def define_method(link_field
|
58
|
-
pretty_name ||= link_field
|
58
|
+
def define_method(link_field)
|
59
59
|
@owner.class.module_eval %Q?
|
60
|
-
def #{
|
60
|
+
def #{link_field}
|
61
61
|
query_association :#{link_field}
|
62
62
|
end
|
63
63
|
?
|
64
|
-
pretty_name
|
65
64
|
end
|
66
65
|
|
67
66
|
# Defines methods for accessing the association target on the owner class.
|
@@ -71,10 +70,10 @@ module SugarCRM
|
|
71
70
|
def define_methods
|
72
71
|
methods = []
|
73
72
|
pretty_name = humanized_link_name(@link_field)
|
74
|
-
methods << define_method(
|
73
|
+
methods << define_method(@link_field)
|
75
74
|
if pretty_name != @link_field
|
76
75
|
@owner.class.module_eval %Q?
|
77
|
-
alias :#{
|
76
|
+
alias :#{pretty_name} #{@link_field}
|
78
77
|
?
|
79
78
|
methods << @link_field
|
80
79
|
end
|
@@ -85,18 +84,9 @@ module SugarCRM
|
|
85
84
|
# e.g. if a custom relationship is defined in Studio between Tasks and Documents,
|
86
85
|
# the link_field will be `tasks_documents` but a human would call the relationship `documents`
|
87
86
|
def humanized_link_name(link_field)
|
88
|
-
#
|
89
|
-
|
90
|
-
|
91
|
-
# Determine the parts we don't want
|
92
|
-
# SugarCRM::Contact => ["contacts", "contact"]
|
93
|
-
o = @owner.class._module.table_name
|
94
|
-
# Use array subtraction to remove parts representing the owner side of the relationship
|
95
|
-
# ["contact", "accounts"] - ["contacts", "contact"] => ["accounts"]
|
96
|
-
t = m - [o, o.singularize]
|
97
|
-
# Reassemble whatever's left
|
98
|
-
# "accounts"
|
99
|
-
t.join('_')
|
87
|
+
# the module name is used to function properly with modules containing '_' (e.g. a custom module abc_sale : custom modules need a prefix (abc here) so they will always have a '_' in their table name)
|
88
|
+
return link_field unless link_field.to_s =~ /((.*)_)?#{Regexp.quote(@owner.class._module.name.downcase)}(_(.*))?/
|
89
|
+
[$2, $4].compact.join('_')
|
100
90
|
end
|
101
91
|
|
102
92
|
end
|
@@ -7,7 +7,17 @@ module SugarCRM; module AssociationCache
|
|
7
7
|
@association_cache.symbolize_keys.include? association.to_sym
|
8
8
|
end
|
9
9
|
|
10
|
-
|
10
|
+
# Updates an association cache entry if it's been initialized
|
11
|
+
def update_association_cache_for(association, target, action=:add)
|
12
|
+
return unless association_cached? association
|
13
|
+
case action
|
14
|
+
when :add
|
15
|
+
return if @association_cache[association].collection.include? target
|
16
|
+
@association_cache[association].push(target) # don't use `<<` because overriden method in AssociationCollection gets called instead
|
17
|
+
when :delete
|
18
|
+
@association_cache[association].delete target
|
19
|
+
end
|
20
|
+
end
|
11
21
|
|
12
22
|
# Returns true if an association collection has changed
|
13
23
|
def associations_changed?
|
@@ -17,11 +27,7 @@ module SugarCRM; module AssociationCache
|
|
17
27
|
false
|
18
28
|
end
|
19
29
|
|
20
|
-
|
21
|
-
def update_association_cache_for(association, target)
|
22
|
-
# only add to the cache if the relationship has been queried
|
23
|
-
@association_cache[association] << target if association_cached? association
|
24
|
-
end
|
30
|
+
protected
|
25
31
|
|
26
32
|
# Resets the association cache
|
27
33
|
def clear_association_cache
|
@@ -2,6 +2,8 @@ module SugarCRM
|
|
2
2
|
# A class for handling association collections. Basically just an extension of Array
|
3
3
|
# doesn't actually load the records from Sugar until you invoke one of the public methods
|
4
4
|
class AssociationCollection
|
5
|
+
|
6
|
+
attr_reader :collection
|
5
7
|
|
6
8
|
# creates a new instance of an AssociationCollection
|
7
9
|
# Owner is the parent object, and association is the target
|
@@ -66,7 +68,8 @@ module SugarCRM
|
|
66
68
|
record.save! if record.new?
|
67
69
|
result = true
|
68
70
|
result = false if include?(record)
|
69
|
-
@
|
71
|
+
@owner.update_association_cache_for(@association, record, :add)
|
72
|
+
record.update_association_cache_for(record.associations.find!(@owner).link_field, @owner, :add)
|
70
73
|
result && self
|
71
74
|
end
|
72
75
|
alias :add :<<
|
@@ -24,7 +24,7 @@ module SugarCRM; module AssociationMethods
|
|
24
24
|
# In contrast to using account.contacts << contact, this method doesn't load the relationships
|
25
25
|
# before setting the new relationship.
|
26
26
|
# This method is useful when certain modules have many links to other modules: not loading the
|
27
|
-
# relationships allows one
|
27
|
+
# relationships allows one to avoid a Timeout::Error
|
28
28
|
def associate!(target,opts={})
|
29
29
|
targets = Array.wrap(target)
|
30
30
|
targets.each do |t|
|
@@ -37,10 +37,25 @@ module SugarCRM; module AssociationMethods
|
|
37
37
|
raise AssociationFailed,
|
38
38
|
"Couldn't associate #{self.class._module.name}: #{self.id} -> #{t}: #{t.id}!"
|
39
39
|
end
|
40
|
-
|
40
|
+
# We need to update the association cache for any changes we make.
|
41
|
+
if opts[:delete]
|
42
|
+
update_association_cache_for(association.link_field, t, :delete)
|
43
|
+
t.update_association_cache_for(association.link_field, self, :delete)
|
44
|
+
else
|
45
|
+
update_association_cache_for(association.link_field, t, :add)
|
46
|
+
t.update_association_cache_for(t.associations.find!(self).link_field, self, :add)
|
47
|
+
end
|
41
48
|
end
|
42
49
|
true
|
43
50
|
end
|
51
|
+
alias :relate! :associate!
|
52
|
+
|
53
|
+
# Removes a relationship between the current object and the target object
|
54
|
+
# TODO: Write a test for this.
|
55
|
+
def disassociate!(target)
|
56
|
+
associate!(target,{:delete => 1})
|
57
|
+
end
|
58
|
+
alias :unrelate! :disassociate!
|
44
59
|
|
45
60
|
protected
|
46
61
|
|
data/lib/sugarcrm/base.rb
CHANGED
@@ -27,7 +27,7 @@ module SugarCRM; class Base
|
|
27
27
|
def establish_connection(url, user, pass, opts={})
|
28
28
|
options = {
|
29
29
|
:debug => false,
|
30
|
-
:register_modules => true
|
30
|
+
:register_modules => true
|
31
31
|
}.merge(opts)
|
32
32
|
@debug = options[:debug]
|
33
33
|
@@connection = SugarCRM::Connection.new(url, user, pass, options)
|
@@ -96,7 +96,9 @@ module SugarCRM; class Base
|
|
96
96
|
|
97
97
|
def find_initial(options)
|
98
98
|
options.update(:limit => 1)
|
99
|
-
|
99
|
+
result = find_by_sql(options)
|
100
|
+
return result.first if result.instance_of? Array # find_by_sql will return an Array if result are found
|
101
|
+
result
|
100
102
|
end
|
101
103
|
|
102
104
|
def find_from_ids(ids, options)
|
@@ -152,8 +154,56 @@ module SugarCRM; class Base
|
|
152
154
|
end
|
153
155
|
|
154
156
|
def find_by_sql(options)
|
155
|
-
|
156
|
-
|
157
|
+
# SugarCRM REST API has a bug where, when :limit and :offset options are passed simultaneously, :limit is considered to be the smallest of the two, and :offset is the larger
|
158
|
+
# in addition to allowing querying of large datasets while avoiding timeouts,
|
159
|
+
# this implementation fixes the :limit - :offset bug so that it behaves correctly
|
160
|
+
local_options = {}
|
161
|
+
options.keys.each{|k|
|
162
|
+
local_options[k] = options[k]
|
163
|
+
}
|
164
|
+
local_options.delete(:offset) if local_options[:offset] == 0
|
165
|
+
|
166
|
+
# store the number of records wanted by user, as we'll overwrite :limit option to obtain several slices of records (to avoid timeout issues)
|
167
|
+
nb_to_fetch = local_options[:limit]
|
168
|
+
nb_to_fetch = nb_to_fetch.to_i if nb_to_fetch
|
169
|
+
offset_value = local_options[:offset] || 10 # arbitrary value, must be bigger than :limit used (see comment above)
|
170
|
+
offset_value = offset_value.to_i
|
171
|
+
offset_value.freeze
|
172
|
+
initial_limit = nb_to_fetch.nil? ? offset_value : [offset_value, nb_to_fetch].min # how many records should be fetched on first pass
|
173
|
+
# ensure results are ordered so :limit and :offset option behave in a deterministic fashion
|
174
|
+
local_options = { :order_by => :id }.merge(local_options)
|
175
|
+
local_options.update(:limit => initial_limit) # override original argument
|
176
|
+
|
177
|
+
# get first slice of results
|
178
|
+
# note: to work around a SugarCRM REST API bug, the :limit option must always be smaller than the :offset option
|
179
|
+
# this is the reason this first query is separate (not in the loop): the initial query has a larger limit, so that we can then use the loop
|
180
|
+
# with :limit always smaller than :offset
|
181
|
+
results = SugarCRM.connection.get_entry_list(self._module.name, query_from_options(local_options), local_options)
|
182
|
+
return nil unless results
|
183
|
+
results = Array.wrap(results)
|
184
|
+
|
185
|
+
limit_value = [5, offset_value].min # arbitrary value, must be smaller than :offset used (see comment above)
|
186
|
+
limit_value.freeze
|
187
|
+
local_options = { :order_by => :id }.merge(local_options)
|
188
|
+
local_options.update(:limit => limit_value)
|
189
|
+
|
190
|
+
# a portion of the results has already been queried
|
191
|
+
# update or set the :offset value to reflect this
|
192
|
+
local_options[:offset] ||= results.size
|
193
|
+
local_options[:offset] += offset_value
|
194
|
+
|
195
|
+
# continue fetching results until we either
|
196
|
+
# a) have as many results as the user wants (specified via the original :limit option)
|
197
|
+
# b) there are no more results matching the criteria
|
198
|
+
while result_slice = SugarCRM.connection.get_entry_list(self._module.name, query_from_options(local_options), local_options)
|
199
|
+
results.concat(Array.wrap(result_slice))
|
200
|
+
# make sure we don't return more results than the user requested (via original :limit option)
|
201
|
+
if nb_to_fetch && results.size >= nb_to_fetch
|
202
|
+
return results.slice(0, nb_to_fetch)
|
203
|
+
end
|
204
|
+
local_options[:offset] += local_options[:limit] # update :offset as we get more records
|
205
|
+
end
|
206
|
+
results
|
157
207
|
end
|
158
208
|
|
159
209
|
def query_from_options(options)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# below is an example configuration file
|
2
|
+
#
|
3
|
+
# to create you own configuration file, simply copy and adapt the text below, removing the '#' in front of the key-value pairs
|
4
|
+
#
|
5
|
+
# you'll find an example of a configuration file (used for tests) in test/config_test.yaml
|
6
|
+
#
|
7
|
+
# config:
|
8
|
+
# base_url: http://127.0.0.1/sugarcrm # where your SugarCRM instance is located
|
9
|
+
# username: admin
|
10
|
+
# password: letmein
|
@@ -14,7 +14,7 @@ module SugarCRM; class Connection
|
|
14
14
|
\"module_name\": \"#{module_name}\"\,
|
15
15
|
\"ids\": #{ids.to_json}\,
|
16
16
|
\"select_fields\": #{resolve_fields(module_name, options[:fields])}\,
|
17
|
-
\"link_name_to_fields_array\": #{options[:link_fields].to_json}
|
17
|
+
\"link_name_to_fields_array\": #{options[:link_fields].to_json}
|
18
18
|
}
|
19
19
|
EOF
|
20
20
|
json.gsub!(/^\s{6}/,'')
|
@@ -13,7 +13,7 @@ module SugarCRM; class Connection
|
|
13
13
|
\"module_name\": \"#{module_name}\"\,
|
14
14
|
\"id\": \"#{id}\"\,
|
15
15
|
\"select_fields\": #{resolve_fields(module_name, options[:fields])}\,
|
16
|
-
\"link_name_to_fields_array\": #{options[:link_fields]}
|
16
|
+
\"link_name_to_fields_array\": #{options[:link_fields]}
|
17
17
|
}
|
18
18
|
EOF
|
19
19
|
|
@@ -1,15 +1,15 @@
|
|
1
1
|
module SugarCRM; class Connection
|
2
2
|
# Logs the user into the Sugar application.
|
3
3
|
def login
|
4
|
-
connect! unless connected?
|
4
|
+
connect! unless connected?
|
5
5
|
json = <<-EOF
|
6
6
|
{
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
"user_auth": {
|
8
|
+
"user_name": "#{@user}",
|
9
|
+
"password": "#{OpenSSL::Digest::MD5.new(@pass)}",
|
10
|
+
"version": 2
|
11
11
|
},
|
12
|
-
|
12
|
+
"application": "sugarcrm_rubygem"
|
13
13
|
}
|
14
14
|
EOF
|
15
15
|
json.gsub!(/^\s{6}/,'')
|
@@ -18,7 +18,8 @@ module SugarCRM; class Connection
|
|
18
18
|
def initialize(url, user, pass, options={})
|
19
19
|
@options = {
|
20
20
|
:debug => false,
|
21
|
-
:register_modules => true
|
21
|
+
:register_modules => true,
|
22
|
+
:load_environment => true
|
22
23
|
}.merge(options)
|
23
24
|
@url = URI.parse(url)
|
24
25
|
@user = user
|
@@ -27,6 +28,8 @@ module SugarCRM; class Connection
|
|
27
28
|
@response = ""
|
28
29
|
resolve_url
|
29
30
|
login!
|
31
|
+
# make sure the environment singleton gets loaded
|
32
|
+
SugarCRM::Environment.instance if @options[:load_environment]
|
30
33
|
self
|
31
34
|
end
|
32
35
|
|
@@ -123,12 +126,12 @@ module SugarCRM; class Connection
|
|
123
126
|
return @response.body if RESPONSE_IS_NOT_JSON.include? @request.method
|
124
127
|
begin
|
125
128
|
# Push it through the old meat grinder.
|
126
|
-
response_json =
|
129
|
+
response_json = JSON.parse(@response.body)
|
127
130
|
rescue StandardError => e
|
128
131
|
raise UnhandledResponse, @response.body
|
129
132
|
end
|
130
133
|
# Empty result. Is this wise?
|
131
|
-
return
|
134
|
+
return nil if response_json["result_count"] == 0
|
132
135
|
# Filter debugging on REALLY BIG responses
|
133
136
|
if @options[:debug] && !(DONT_SHOW_DEBUG_FOR.include? @request.method)
|
134
137
|
puts "#{@request.method}: JSON Response:"
|
@@ -8,14 +8,14 @@ module SugarCRM; class Request
|
|
8
8
|
def initialize(url, method, json, debug=false)
|
9
9
|
@url = url
|
10
10
|
@method = method
|
11
|
-
@json = json
|
11
|
+
@json = CGI.escape(json)
|
12
12
|
@request = 'method=' << @method.to_s
|
13
13
|
@request << '&input_type=JSON'
|
14
14
|
@request << '&response_type=JSON'
|
15
15
|
@request << '&rest_data=' << @json
|
16
16
|
if debug
|
17
17
|
puts "#{method}: Request:"
|
18
|
-
puts
|
18
|
+
puts json
|
19
19
|
puts "\n"
|
20
20
|
end
|
21
21
|
self
|
@@ -26,6 +26,6 @@ module SugarCRM; class Request
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def to_s
|
29
|
-
|
29
|
+
@request
|
30
30
|
end
|
31
31
|
end; end
|
@@ -17,9 +17,8 @@ module SugarCRM; class Response
|
|
17
17
|
if SugarCRM.connection.debug?
|
18
18
|
puts "Failed to process JSON:"
|
19
19
|
pp json
|
20
|
-
raise e
|
21
20
|
end
|
22
|
-
|
21
|
+
raise e
|
23
22
|
end
|
24
23
|
end
|
25
24
|
end
|
@@ -36,7 +35,7 @@ module SugarCRM; class Response
|
|
36
35
|
# populated from the response
|
37
36
|
def to_obj
|
38
37
|
# If this is not a "entry_list" response, just return
|
39
|
-
return @response unless @response["entry_list"]
|
38
|
+
return @response unless @response && @response["entry_list"]
|
40
39
|
|
41
40
|
objects = []
|
42
41
|
@response["entry_list"].each do |object|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module SugarCRM; class Environment
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
attr_reader :config
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@config = {}
|
10
|
+
|
11
|
+
# see README for reasoning behind the priorization
|
12
|
+
['/etc/sugarcrm.yaml', File.expand_path('~/.sugarcrm.yaml'), File.join(File.dirname(__FILE__), 'config', 'sugarcrm.yaml')].each{|path|
|
13
|
+
load_config path if File.exists? path
|
14
|
+
}
|
15
|
+
extensions_folder = File.join(File.dirname(__FILE__), 'extensions')
|
16
|
+
SugarCRM::Base.establish_connection(@config[:base_url], @config[:username], @config[:password], {:load_environment => false}) if SugarCRM.connection.nil? && connection_info_loaded?
|
17
|
+
end
|
18
|
+
|
19
|
+
def connection_info_loaded?
|
20
|
+
@config[:base_url] && @config[:username] && @config[:password]
|
21
|
+
end
|
22
|
+
|
23
|
+
def load_config(path)
|
24
|
+
validate_path path
|
25
|
+
config = YAML.load_file(path)
|
26
|
+
if config && config["config"]
|
27
|
+
config["config"].each{|k,v|
|
28
|
+
@config[k.to_sym] = v
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# load all the monkey patch extension files in the provided folder
|
34
|
+
def extensions_folder=(folder, dirstring=nil)
|
35
|
+
validate_path folder
|
36
|
+
path = File.expand_path(folder, dirstring)
|
37
|
+
Dir[File.join(path, '**', '*.rb').to_s].each { |f| load(f) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.method_missing(method_id, *args, &block)
|
41
|
+
self.instance.send(method_id, *args, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def validate_path(path)
|
46
|
+
raise "Invalid path: #{path}" unless File.exists? path
|
47
|
+
end
|
48
|
+
end; end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Include your extension files here, as simple *.rb files. Here is an example of an extension:
|
2
|
+
|
3
|
+
SugarCRM::Contact.class_eval do
|
4
|
+
def self.ten_oldest
|
5
|
+
self.find(:order_by => 'date_entered', :limit => 10)
|
6
|
+
end
|
7
|
+
|
8
|
+
def vip?
|
9
|
+
self.opportunities.size > 100
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# This will enable you to call
|
14
|
+
|
15
|
+
SugarCRM::Contact.ten_oldest
|
16
|
+
|
17
|
+
# to get the 10 oldest contacts entered in CRM .
|
18
|
+
#
|
19
|
+
# You will also be able to call
|
20
|
+
|
21
|
+
SugarCRM::Contact.first.vip?
|
22
|
+
|
23
|
+
# to see whether a contact is VIP or not.
|
data/lib/sugarcrm/module.rb
CHANGED
@@ -15,7 +15,11 @@ module SugarCRM
|
|
15
15
|
def initialize(name)
|
16
16
|
@name = name
|
17
17
|
@klass = name.classify
|
18
|
-
|
18
|
+
unless custom_module?
|
19
|
+
@table_name = name.tableize
|
20
|
+
else
|
21
|
+
@table_name = @name
|
22
|
+
end
|
19
23
|
@custom_table_name = resolve_custom_table_name
|
20
24
|
@fields = {}
|
21
25
|
@link_fields = {}
|
@@ -36,8 +40,11 @@ module SugarCRM
|
|
36
40
|
# For custom modules (created in the Studio), table name don't need to be tableized since
|
37
41
|
# the name passed to the constructor is already tableized
|
38
42
|
def resolve_custom_table_name
|
39
|
-
|
40
|
-
|
43
|
+
if custom_module?
|
44
|
+
@custom_table_name = @name + "_cstm"
|
45
|
+
else
|
46
|
+
@custom_table_name = @table_name + "_cstm"
|
47
|
+
end
|
41
48
|
end
|
42
49
|
|
43
50
|
# Returns the fields associated with the module
|
@@ -7,7 +7,7 @@ module SugarCRM
|
|
7
7
|
def self.connection=(connection)
|
8
8
|
@@connection = connection
|
9
9
|
end
|
10
|
-
def self.connect(url, user, pass, options={})
|
10
|
+
def self.connect(url=SugarCRM::Environment.config[:base_url], user=SugarCRM::Environment.config[:username], pass=SugarCRM::Environment.config[:password], options={})
|
11
11
|
SugarCRM::Base.establish_connection(url, user, pass, options)
|
12
12
|
end
|
13
13
|
class << self
|
@@ -26,4 +26,21 @@ module SugarCRM
|
|
26
26
|
SugarCRM::User.find_by_user_name(connection.user)
|
27
27
|
end
|
28
28
|
|
29
|
+
# If a user tries to access a SugarCRM class before they're logged in,
|
30
|
+
# try to log in using credentials from config file.
|
31
|
+
# This will trigger module loading,
|
32
|
+
# and we can then attempt to return the requested class automagically
|
33
|
+
def self.const_missing(sym)
|
34
|
+
# if we're logged in, modules should be loaded and available
|
35
|
+
if SugarCRM.connection && SugarCRM.connection.logged_in?
|
36
|
+
super
|
37
|
+
else
|
38
|
+
# here, we initialize the environment (which happens on any method call, if singleton hasn't already been initialized)
|
39
|
+
# initializing the environment will log user in if credentials present in config file
|
40
|
+
# if it isn't possible to log in and access modules, pass the exception on
|
41
|
+
super unless SugarCRM::Environment.connection_info_loaded?
|
42
|
+
# try and return the requested module
|
43
|
+
SugarCRM.const_get(sym)
|
44
|
+
end
|
45
|
+
end
|
29
46
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# below is an example configuration file
|
2
|
+
#
|
3
|
+
# to create you own configuration file, simply copy and adapt the text below, removing the '#' in front of the key-value pairs
|
4
|
+
#
|
5
|
+
# you'll find an example of a configuration file (used for tests) in test/config_test.yaml
|
6
|
+
#
|
7
|
+
# config:
|
8
|
+
# base_url: http://127.0.0.1/sugarcrm # where your SugarCRM instance is located
|
9
|
+
# username: admin
|
10
|
+
# password: letmein
|
11
|
+
|
12
|
+
config:
|
13
|
+
base_url: http://127.0.0.1/sugarcrm # where your SugarCRM instance is located
|
14
|
+
username: admin
|
15
|
+
password: letmein
|
data/test/test_association.rb
CHANGED
@@ -1 +1,18 @@
|
|
1
|
-
#TODO: Figure out how to test this.
|
1
|
+
#TODO: Figure out how to test this.
|
2
|
+
|
3
|
+
|
4
|
+
# That code works, but what didn't work:
|
5
|
+
# document.tasks.size
|
6
|
+
# document.associate!(task)
|
7
|
+
# document.tasks.size
|
8
|
+
# This seems to be fixed in HEAD in my repo. I haven't pushed the changes to your repo, because I don't really see why commit https://github.com/davidsulc/sugarcrm/commit/228375348c9113324370afa0aca4120eb117d3e1 fixes the issue...
|
9
|
+
# Also, I fixed this case
|
10
|
+
# document.tasks.size
|
11
|
+
# document.associate!(task)
|
12
|
+
# document.tasks.size # => 1 (correct)
|
13
|
+
# document.associate!(task)
|
14
|
+
# document.tasks.size # => 2 (incorrect: should remain 1)
|
15
|
+
# One thing we should look into is
|
16
|
+
# document.associate!(task)
|
17
|
+
# document.tasks.size # => 1
|
18
|
+
# task.documents.size # => 0 (should be 1)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestEnvironment < Test::Unit::TestCase
|
4
|
+
context "A SugarCRM::Environment singleton" do
|
5
|
+
|
6
|
+
should "delegate missing methods to singleton instance" do
|
7
|
+
assert_equal SugarCRM::Environment.instance.config, SugarCRM::Environment.config
|
8
|
+
end
|
9
|
+
|
10
|
+
should "load monkey patch extensions" do
|
11
|
+
SugarCRM::Environment.extensions_folder = File.join(File.dirname(__FILE__), 'extensions_test')
|
12
|
+
assert SugarCRM::Contact.is_extended?
|
13
|
+
assert SugarCRM::Contact.is_extended?
|
14
|
+
end
|
15
|
+
|
16
|
+
should "load config file" do
|
17
|
+
SugarCRM::Environment.load_config File.join(File.dirname(__FILE__), 'config_test.yaml')
|
18
|
+
|
19
|
+
config_contents = {
|
20
|
+
:config => {
|
21
|
+
:base_url => 'http://127.0.0.1/sugarcrm',
|
22
|
+
:username => 'admin',
|
23
|
+
:password => 'letmein'
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
config_contents[:config].each{|k,v|
|
28
|
+
assert_equal v, SugarCRM::Environment.config[k]
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
should "log in to Sugar automatically if credentials are present in config file" do
|
33
|
+
SugarCRM::Environment.load_config File.join(File.dirname(__FILE__), 'config_test.yaml')
|
34
|
+
assert SugarCRM.connection.logged_in?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/test/test_sugarcrm.rb
CHANGED
@@ -52,28 +52,26 @@ class TestSugarCRM < Test::Unit::TestCase
|
|
52
52
|
end
|
53
53
|
|
54
54
|
should "not save a record that is missing required attributes" do
|
55
|
-
SugarCRM.connection.debug = false
|
56
55
|
u = SugarCRM::User.new
|
57
56
|
u.last_name = "Test"
|
58
57
|
assert !u.save
|
59
|
-
SugarCRM.connection.debug = false
|
60
58
|
assert_raise SugarCRM::InvalidRecord do
|
61
59
|
u.save!
|
62
60
|
end
|
63
61
|
end
|
64
62
|
|
65
63
|
should "always return an Array when :all" do
|
66
|
-
users = SugarCRM::User.all
|
64
|
+
users = SugarCRM::User.all(:limit => 10)
|
67
65
|
assert_instance_of Array, users
|
68
66
|
users = SugarCRM::User.find(:all, :conditions => {:user_name => '= admin'})
|
69
67
|
assert_instance_of Array, users
|
68
|
+
assert users.length == 1
|
70
69
|
users = SugarCRM::User.find(:all, :conditions => {:user_name => '= invalid_user_123'})
|
71
70
|
assert_instance_of Array, users
|
72
71
|
assert users.length == 0
|
73
72
|
end
|
74
73
|
|
75
74
|
should "create, modify, and delete a record" do
|
76
|
-
#SugarCRM.connection.debug = true
|
77
75
|
u = SugarCRM::User.new
|
78
76
|
u.email1 = "abc@abc.com"
|
79
77
|
u.first_name = "Test"
|
@@ -85,10 +83,10 @@ class TestSugarCRM < Test::Unit::TestCase
|
|
85
83
|
assert u.save!
|
86
84
|
assert !u.new?
|
87
85
|
m = SugarCRM::User.find_by_first_name_and_last_name("Test", "User")
|
86
|
+
assert m.user_name != "admin"
|
88
87
|
m.title = "Test User"
|
89
88
|
assert m.save!
|
90
89
|
assert m.delete
|
91
|
-
#SugarCRM.connection.debug = false
|
92
90
|
end
|
93
91
|
|
94
92
|
should "support finding first instance (sorted by attribute)" do
|
@@ -126,7 +124,48 @@ class TestSugarCRM < Test::Unit::TestCase
|
|
126
124
|
|
127
125
|
should "return an array of records when sent #find([id1, id2, id3])" do
|
128
126
|
users = SugarCRM::User.find(["seed_sarah_id", 1])
|
129
|
-
assert_equal "
|
127
|
+
assert_equal "admin", users.last.user_name
|
128
|
+
end
|
129
|
+
|
130
|
+
# test Base#find_by_sql edge case
|
131
|
+
should "return an array of records with small limit and an offset of 0" do
|
132
|
+
accounts = SugarCRM::Account.all(:limit => 3, :offset => 0)
|
133
|
+
assert_equal 3, accounts.size
|
134
|
+
end
|
135
|
+
|
136
|
+
# test Base#find_by_sql standard case
|
137
|
+
should "return an array of records with high limit" do
|
138
|
+
accounts = SugarCRM::Account.all(:limit => 12)
|
139
|
+
assert_equal 12, accounts.size
|
140
|
+
end
|
141
|
+
|
142
|
+
should "return an array of records when using :order_by, :limit, and :offset options" do
|
143
|
+
accounts = SugarCRM::Account.all(:order_by => 'name', :limit => 3, :offset => 10)
|
144
|
+
accounts_api = SugarCRM.connection.get_entry_list('Accounts', '1=1', :order_by => 'name', :limit => 3, :offset => 10)
|
145
|
+
assert_equal accounts_api, accounts
|
146
|
+
end
|
147
|
+
|
148
|
+
should "return an array of records working around a SugarCRM bug when :limit > :offset" do
|
149
|
+
accounts = SugarCRM::Account.all(:order_by => 'name', :limit => 10, :offset => 2)
|
150
|
+
assert_equal 10, accounts.size
|
151
|
+
end
|
152
|
+
|
153
|
+
should "return an array of 1 record with :limit => 1, :offset => 1" do
|
154
|
+
accounts = SugarCRM::Account.all(:order_by => 'name', :limit => 1, :offset => 1)
|
155
|
+
assert_equal 1, accounts.size
|
156
|
+
end
|
157
|
+
|
158
|
+
should "ignore :offset => 0" do
|
159
|
+
accounts = SugarCRM::Account.all(:order_by => 'name', :limit => 3)
|
160
|
+
accounts_offset = SugarCRM::Account.all(:order_by => 'name', :limit => 3, :offset => 0)
|
161
|
+
assert_equal accounts, accounts_offset
|
162
|
+
end
|
163
|
+
|
164
|
+
should "compute offsets correctly" do
|
165
|
+
accounts = SugarCRM::Account.all(:order_by => 'name', :limit => 10, :offset => 3)
|
166
|
+
accounts_first_slice = SugarCRM::Account.all(:order_by => 'name', :limit => 5, :offset => 3)
|
167
|
+
accounts_second_slice = SugarCRM::Account.all(:order_by => 'name', :limit => 5, :offset => 8)
|
168
|
+
assert_equal accounts, accounts_first_slice.concat(accounts_second_slice)
|
130
169
|
end
|
131
170
|
|
132
171
|
should "return an instance of User when sent User#find_by_username" do
|
@@ -143,12 +182,64 @@ class TestSugarCRM < Test::Unit::TestCase
|
|
143
182
|
assert a.delete
|
144
183
|
end
|
145
184
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
185
|
+
should "update association cache on associate! only if association changes" do
|
186
|
+
a = SugarCRM::Account.first
|
187
|
+
c = SugarCRM::Contact.create(:last_name => 'Doe')
|
188
|
+
|
189
|
+
nb_contacts = a.contacts.size
|
190
|
+
a.associate!(c)
|
191
|
+
assert_equal nb_contacts + 1, a.contacts.size
|
192
|
+
a.associate!(c)
|
193
|
+
assert_equal nb_contacts + 1, a.contacts.size # should not change: already associated
|
194
|
+
|
195
|
+
c.delete
|
196
|
+
end
|
197
|
+
|
198
|
+
should "update association cache on << only if association changes" do
|
199
|
+
a = SugarCRM::Account.first
|
200
|
+
c = SugarCRM::Contact.create(:last_name => 'Doe')
|
201
|
+
|
202
|
+
nb_contacts = a.contacts.size
|
203
|
+
a.contacts << c
|
204
|
+
assert_equal nb_contacts + 1, a.contacts.size
|
205
|
+
a.contacts << c
|
206
|
+
assert_equal nb_contacts + 1, a.contacts.size # should not change: already associated
|
207
|
+
|
208
|
+
c.delete
|
209
|
+
end
|
210
|
+
|
211
|
+
should "update association cache for both sides of the relationship when calling associate!" do
|
212
|
+
a = SugarCRM::Account.first
|
213
|
+
c = SugarCRM::Contact.create(:last_name => 'Doe')
|
214
|
+
|
215
|
+
nb_contacts = a.contacts.size
|
216
|
+
nb_accounts = c.accounts.size
|
217
|
+
a.associate!(c)
|
218
|
+
assert_equal nb_contacts + 1, a.contacts.size
|
219
|
+
assert_equal nb_accounts + 1, c.accounts.size
|
220
|
+
|
221
|
+
c.delete
|
222
|
+
end
|
223
|
+
|
224
|
+
should "update association cache for both sides of the relationship when calling <<" do
|
225
|
+
a = SugarCRM::Account.first
|
226
|
+
c = SugarCRM::Contact.create(:last_name => 'Doe')
|
227
|
+
|
228
|
+
nb_contacts = a.contacts.size
|
229
|
+
nb_accounts = c.accounts.size
|
230
|
+
a.contacts << c
|
231
|
+
assert_equal nb_contacts + 1, a.contacts.size
|
232
|
+
assert_equal nb_accounts + 1, c.accounts.size
|
233
|
+
|
234
|
+
c.delete
|
235
|
+
end
|
236
|
+
|
237
|
+
should "support saving of records with special characters in them" do
|
238
|
+
a = SugarCRM::Account.new
|
239
|
+
a.name = "COHEN, WEISS & SIMON LLP"
|
240
|
+
assert a.save!
|
241
|
+
assert a.delete
|
242
|
+
end
|
152
243
|
end
|
153
244
|
|
154
245
|
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sugarcrm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 41
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 9
|
8
|
-
-
|
9
|
-
version: 0.9.
|
9
|
+
- 9
|
10
|
+
version: 0.9.9
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Carl Hicks
|
@@ -14,93 +15,99 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2011-01-
|
18
|
+
date: 2011-01-30 00:00:00 -08:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
21
24
|
name: activesupport
|
22
|
-
|
25
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
23
26
|
none: false
|
24
27
|
requirements:
|
25
28
|
- - ">="
|
26
29
|
- !ruby/object:Gem::Version
|
30
|
+
hash: 7
|
27
31
|
segments:
|
28
32
|
- 3
|
29
33
|
- 0
|
30
34
|
- 0
|
31
35
|
version: 3.0.0
|
36
|
+
requirement: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
32
38
|
type: :runtime
|
33
39
|
prerelease: false
|
34
|
-
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
40
|
name: i18n
|
37
|
-
|
41
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
38
42
|
none: false
|
39
43
|
requirements:
|
40
44
|
- - ">="
|
41
45
|
- !ruby/object:Gem::Version
|
46
|
+
hash: 3
|
42
47
|
segments:
|
43
48
|
- 0
|
44
49
|
version: "0"
|
50
|
+
requirement: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
45
52
|
type: :runtime
|
46
53
|
prerelease: false
|
47
|
-
|
54
|
+
name: json
|
55
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
requirement: *id003
|
48
65
|
- !ruby/object:Gem::Dependency
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
49
68
|
name: shoulda
|
50
|
-
|
69
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
51
70
|
none: false
|
52
71
|
requirements:
|
53
72
|
- - ">="
|
54
73
|
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
55
75
|
segments:
|
56
76
|
- 0
|
57
77
|
version: "0"
|
78
|
+
requirement: *id004
|
79
|
+
- !ruby/object:Gem::Dependency
|
58
80
|
type: :development
|
59
81
|
prerelease: false
|
60
|
-
version_requirements: *id003
|
61
|
-
- !ruby/object:Gem::Dependency
|
62
82
|
name: bundler
|
63
|
-
|
83
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
64
84
|
none: false
|
65
85
|
requirements:
|
66
86
|
- - ~>
|
67
87
|
- !ruby/object:Gem::Version
|
88
|
+
hash: 23
|
68
89
|
segments:
|
69
90
|
- 1
|
70
91
|
- 0
|
71
92
|
- 0
|
72
93
|
version: 1.0.0
|
94
|
+
requirement: *id005
|
95
|
+
- !ruby/object:Gem::Dependency
|
73
96
|
type: :development
|
74
97
|
prerelease: false
|
75
|
-
version_requirements: *id004
|
76
|
-
- !ruby/object:Gem::Dependency
|
77
98
|
name: jeweler
|
78
|
-
|
99
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
79
100
|
none: false
|
80
101
|
requirements:
|
81
102
|
- - ~>
|
82
103
|
- !ruby/object:Gem::Version
|
104
|
+
hash: 7
|
83
105
|
segments:
|
84
106
|
- 1
|
85
107
|
- 5
|
86
108
|
- 2
|
87
109
|
version: 1.5.2
|
88
|
-
|
89
|
-
prerelease: false
|
90
|
-
version_requirements: *id005
|
91
|
-
- !ruby/object:Gem::Dependency
|
92
|
-
name: rcov
|
93
|
-
requirement: &id006 !ruby/object:Gem::Requirement
|
94
|
-
none: false
|
95
|
-
requirements:
|
96
|
-
- - ">="
|
97
|
-
- !ruby/object:Gem::Version
|
98
|
-
segments:
|
99
|
-
- 0
|
100
|
-
version: "0"
|
101
|
-
type: :development
|
102
|
-
prerelease: false
|
103
|
-
version_requirements: *id006
|
110
|
+
requirement: *id006
|
104
111
|
description: 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 a 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.
|
105
112
|
email: carl.hicks@gmail.com
|
106
113
|
executables: []
|
@@ -130,6 +137,7 @@ files:
|
|
130
137
|
- lib/sugarcrm/attributes/attribute_typecast.rb
|
131
138
|
- lib/sugarcrm/attributes/attribute_validations.rb
|
132
139
|
- lib/sugarcrm/base.rb
|
140
|
+
- lib/sugarcrm/config/sugarcrm.yaml
|
133
141
|
- lib/sugarcrm/connection.rb
|
134
142
|
- lib/sugarcrm/connection/api/get_available_modules.rb
|
135
143
|
- lib/sugarcrm/connection/api/get_document_revision.rb
|
@@ -160,9 +168,12 @@ files:
|
|
160
168
|
- lib/sugarcrm/connection/request.rb
|
161
169
|
- lib/sugarcrm/connection/response.rb
|
162
170
|
- lib/sugarcrm/dynamic_finder_match.rb
|
171
|
+
- lib/sugarcrm/environment.rb
|
163
172
|
- lib/sugarcrm/exceptions.rb
|
173
|
+
- lib/sugarcrm/extensions/README.txt
|
164
174
|
- lib/sugarcrm/module.rb
|
165
175
|
- lib/sugarcrm/module_methods.rb
|
176
|
+
- test/config_test.yaml
|
166
177
|
- test/connection/test_get_available_modules.rb
|
167
178
|
- test/connection/test_get_entries.rb
|
168
179
|
- test/connection/test_get_entry.rb
|
@@ -175,11 +186,13 @@ files:
|
|
175
186
|
- test/connection/test_login.rb
|
176
187
|
- test/connection/test_logout.rb
|
177
188
|
- test/connection/test_set_relationship.rb
|
189
|
+
- test/extensions_test/patch.rb
|
178
190
|
- test/helper.rb
|
179
191
|
- test/test_association.rb
|
180
192
|
- test/test_association_collection.rb
|
181
193
|
- test/test_associations.rb
|
182
194
|
- test/test_connection.rb
|
195
|
+
- test/test_environment.rb
|
183
196
|
- test/test_module.rb
|
184
197
|
- test/test_response.rb
|
185
198
|
- test/test_sugarcrm.rb
|
@@ -197,7 +210,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
197
210
|
requirements:
|
198
211
|
- - ">="
|
199
212
|
- !ruby/object:Gem::Version
|
200
|
-
hash:
|
213
|
+
hash: 3
|
201
214
|
segments:
|
202
215
|
- 0
|
203
216
|
version: "0"
|
@@ -206,6 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
206
219
|
requirements:
|
207
220
|
- - ">="
|
208
221
|
- !ruby/object:Gem::Version
|
222
|
+
hash: 3
|
209
223
|
segments:
|
210
224
|
- 0
|
211
225
|
version: "0"
|
@@ -229,11 +243,13 @@ test_files:
|
|
229
243
|
- test/connection/test_login.rb
|
230
244
|
- test/connection/test_logout.rb
|
231
245
|
- test/connection/test_set_relationship.rb
|
246
|
+
- test/extensions_test/patch.rb
|
232
247
|
- test/helper.rb
|
233
248
|
- test/test_association.rb
|
234
249
|
- test/test_association_collection.rb
|
235
250
|
- test/test_associations.rb
|
236
251
|
- test/test_connection.rb
|
252
|
+
- test/test_environment.rb
|
237
253
|
- test/test_module.rb
|
238
254
|
- test/test_response.rb
|
239
255
|
- test/test_sugarcrm.rb
|