ecircle_soap_client 0.0.5
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/.document +5 -0
- data/.rvmrc +1 -0
- data/Gemfile +20 -0
- data/README.md +89 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/ecircle_soap_client.gemspec +100 -0
- data/lib/ecircle/base.rb +40 -0
- data/lib/ecircle/client.rb +145 -0
- data/lib/ecircle/configuration.rb +14 -0
- data/lib/ecircle/group.rb +73 -0
- data/lib/ecircle/member.rb +31 -0
- data/lib/ecircle/message.rb +93 -0
- data/lib/ecircle/user.rb +120 -0
- data/lib/ecircle_soap_client.rb +48 -0
- data/start_pry_with_ecircle +31 -0
- data/test/.login.yml.sample +4 -0
- data/test/helper.rb +116 -0
- data/test/test_ecircle_soap_client.rb +317 -0
- data/test/test_ecircle_soap_client_group.rb +61 -0
- data/test/test_ecircle_soap_client_member.rb +48 -0
- data/test/test_ecircle_soap_client_message.rb +174 -0
- data/test/test_ecircle_soap_client_user.rb +220 -0
- metadata +288 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
module Ecircle
|
3
|
+
class Configuration
|
4
|
+
attr_accessor :realm, :user, :password, :wsdl
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@session_token = nil
|
8
|
+
@wsdl = OpenStruct.new
|
9
|
+
wsdl.document = "http://webservices.ecircle-ag.com/soap/ecm.wsdl"
|
10
|
+
wsdl.endpoint = "http://webservices.ecircle-ag.com/rpc"
|
11
|
+
wsdl.namespace = "http://webservices.ecircleag.com/rpcns"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Ecircle
|
2
|
+
class Group < Ecircle::Base
|
3
|
+
class << self
|
4
|
+
def find_all_by_name(group_name)
|
5
|
+
ary = Ecircle.client.lookup_groups :lookupParams => { :groupName => group_name }
|
6
|
+
ary.is_a?(Array) ? ary.collect { |a| Ecircle::Group.new(a) } : []
|
7
|
+
end
|
8
|
+
|
9
|
+
def find_by_name(group_name)
|
10
|
+
hsh = Ecircle.client.lookup_groups :lookupParams => { :groupName => group_name }
|
11
|
+
hsh.is_a?(Hash) ? Group.new(hsh) : raise("Group name #{group_name} not unique")
|
12
|
+
end
|
13
|
+
|
14
|
+
def all
|
15
|
+
find_all_by_name("")
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_by_id(idstr)
|
19
|
+
## TODO there must be a better way of doing this?!
|
20
|
+
## TODO but there isn't a lookupGroupById in the wsdl specification.
|
21
|
+
all.reject { |a| a.id != idstr }.first
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(hsh = nil)
|
26
|
+
super()
|
27
|
+
@all_fields = hsh || {}
|
28
|
+
@id = self[:id]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Make a user a member of a group. Can be called multiple times for the same
|
32
|
+
# user, eCircle checks for duplicates based on the email or whatever.
|
33
|
+
# Returns a member object.
|
34
|
+
def add_member(user, send_invite = false, send_message = false)
|
35
|
+
member_id = Ecircle.client.
|
36
|
+
create_member(:userId => user.id, :groupId => @id,
|
37
|
+
:invite => send_invite, :sendMessage => send_message)
|
38
|
+
Ecircle::Member.find_by_id(member_id)
|
39
|
+
end
|
40
|
+
|
41
|
+
def remove_user(user, send_message = false)
|
42
|
+
user.leave_group(self, send_message)
|
43
|
+
end
|
44
|
+
|
45
|
+
def remove_member(member, send_message = false)
|
46
|
+
member.user.leave_group(self, send_message)
|
47
|
+
end
|
48
|
+
|
49
|
+
# clone this group at eCircle. For example,
|
50
|
+
# Ecircle::Group.
|
51
|
+
# find_by_name("fubar").
|
52
|
+
# clone( "snafu", "snafu@cmXX.ecircle-ag.com", false )
|
53
|
+
def clone(with_name, with_email, keep_owner = true)
|
54
|
+
Ecircle.client.
|
55
|
+
clone_group(:templateGroupId => @id, :newGroupEmail => with_email,
|
56
|
+
:newGroupName => with_name, :keepOwner => keep_owner)
|
57
|
+
end
|
58
|
+
|
59
|
+
def delete
|
60
|
+
Ecircle.client.delete_group :groupId => @id
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_xml
|
64
|
+
# prefer to use u.send(...) but that creates a new xml element called 'send'!
|
65
|
+
# hence this is using instance_eval with a string.
|
66
|
+
Savon::SOAP::XML.new.xml do |x|
|
67
|
+
x.group(:id => @id) do |u|
|
68
|
+
u.name(@id)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Ecircle
|
2
|
+
class Member < Ecircle::Base
|
3
|
+
class << self
|
4
|
+
def find_by_id(member_id)
|
5
|
+
Ecircle.client.lookup_member_by_id :memberid => member_id
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(xml_string)
|
10
|
+
super()
|
11
|
+
init_with_xml("member", xml_string)
|
12
|
+
end
|
13
|
+
|
14
|
+
def delete
|
15
|
+
Ecircle.client.delete_member :memberId => @id
|
16
|
+
end
|
17
|
+
|
18
|
+
## TODO with assume that the member id is the form of "<userid>g<groupid>", hence
|
19
|
+
## TODO split on 'g' should work!
|
20
|
+
def user_id ; @id.split(/g/).first ; end
|
21
|
+
def group_id ; @id.split(/g/).last ; end
|
22
|
+
|
23
|
+
def group
|
24
|
+
Ecircle::Group.find_by_id(group_id)
|
25
|
+
end
|
26
|
+
def user
|
27
|
+
Ecircle::User.find_by_id(user_id)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Ecircle
|
2
|
+
class Message < Ecircle::Base
|
3
|
+
|
4
|
+
MessageTypeUnknown = Class.new(RuntimeError)
|
5
|
+
MessageGroupNotDefined = Class.new(RuntimeError)
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def find_all_by_group_name(group_name)
|
9
|
+
ary = Ecircle.client.lookup_messages :lookupParams => { :groupName => group_name }
|
10
|
+
ary.is_a?(Array) ? ary.collect { |a| Ecircle::Message.new(a) } : []
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_by_id(idstr)
|
14
|
+
## TODO no lookupMessageById, hence this workaround.
|
15
|
+
all.reject { |msg| msg.id != idstr }.first
|
16
|
+
end
|
17
|
+
|
18
|
+
def all
|
19
|
+
find_all_by_group_name("")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(hsh)
|
24
|
+
super()
|
25
|
+
@all_fields = hsh
|
26
|
+
@id = self[:id]
|
27
|
+
end
|
28
|
+
|
29
|
+
def subject
|
30
|
+
self[:subject]
|
31
|
+
end
|
32
|
+
|
33
|
+
def group
|
34
|
+
# a single message could potentially have no group_id? Either case this
|
35
|
+
# should just return nil instead of raising an exception.
|
36
|
+
Ecircle::Group.find_by_id(self[:group_id]) if self[:group_id]
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete
|
40
|
+
Ecircle.client.delete_message :messageId => self.id
|
41
|
+
true
|
42
|
+
rescue Ecircle::Client::PermissionDenied => e
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
# This does one of two things, if the message is of type 'single', then
|
47
|
+
# it uses send_single_message_to_user, else if the type is 'normal' then
|
48
|
+
# it uses the send_group_message_to_user.
|
49
|
+
#
|
50
|
+
# If parameters are given and this is a single message, then a parameterized
|
51
|
+
# version of the message is sent.
|
52
|
+
#
|
53
|
+
# Return value is an array with the first value being boolean to indicate
|
54
|
+
# success status. The second value is the original result returned by the
|
55
|
+
# ecircle API. The thing is, that ecircle will return nil on success, so
|
56
|
+
# in that case, we return [true, nil].
|
57
|
+
def send_to_user(user, parameters = nil)
|
58
|
+
result = case self[:type]
|
59
|
+
|
60
|
+
when /single/
|
61
|
+
if parameters.nil?
|
62
|
+
Ecircle.client.
|
63
|
+
send_single_message_to_user(:singleMessageId => @id,
|
64
|
+
:userId => user.id)
|
65
|
+
else
|
66
|
+
paras = { :singleMessageId => @id,
|
67
|
+
:userId => user.id,
|
68
|
+
:names => parameters.keys,
|
69
|
+
:values => parameters.values,
|
70
|
+
}
|
71
|
+
Ecircle.client.send_parametrized_single_message_to_user(paras)
|
72
|
+
end
|
73
|
+
|
74
|
+
when /normal/
|
75
|
+
# raise an exception because this is inconsistent: a group message without
|
76
|
+
# group_id is not possible.
|
77
|
+
raise MessageGroupNotDefined, "MsgId: #{self.id}" unless self[:group_id]
|
78
|
+
|
79
|
+
Ecircle.client.
|
80
|
+
send_group_message_to_user(:userId => user.id,
|
81
|
+
:messageId => @id,
|
82
|
+
:groupid => self[:group_id])
|
83
|
+
else
|
84
|
+
raise(MessageTypeUnknown, "Type: #{self[:type]} unknown for "+
|
85
|
+
"MsgId: #{self.id}")
|
86
|
+
end
|
87
|
+
|
88
|
+
# strangely, if the message sending worked out, then ecircle sends nil, i.e.
|
89
|
+
# nothing back. Else we get some sort of strange error or exception.
|
90
|
+
result.nil? ? [true, nil] : [false, result]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/ecircle/user.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
module Ecircle
|
2
|
+
class User < Ecircle::Base
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def create_by_email(email)
|
6
|
+
u = User.new
|
7
|
+
u.email = email
|
8
|
+
## TODO why must the title be defined
|
9
|
+
u.title = "-1"
|
10
|
+
u.id = Ecircle.client.create_user :userXmlSpec => u.to_xml
|
11
|
+
u
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_by_email(email)
|
15
|
+
Ecircle.client.lookup_user_by_email :email => email
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_by_identifier(idstr)
|
19
|
+
Ecircle.client.lookup_user_by_identifier :identifier => idstr
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_by_id(idstr)
|
23
|
+
Ecircle.client.lookup_user_by_id :userId => idstr
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(xml_string = nil)
|
28
|
+
super()
|
29
|
+
initialize_with_xml(xml_string) if xml_string
|
30
|
+
end
|
31
|
+
|
32
|
+
def email
|
33
|
+
self[:email]
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete
|
37
|
+
Ecircle.client.delete_user :user_id => @id
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_or_update(send_message = false)
|
41
|
+
Ecircle.client.
|
42
|
+
create_or_update_user_by_email :userXml => to_xml, :sendMessage => send_message
|
43
|
+
end
|
44
|
+
|
45
|
+
def save
|
46
|
+
Ecircle.client.update_user :userXmlSpec => to_xml
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the group ids this user is signed up to as an Array of strings.
|
50
|
+
def group_ids
|
51
|
+
[Ecircle.client.find_memberships_by_email(:email => email)].flatten.compact
|
52
|
+
end
|
53
|
+
|
54
|
+
def groups
|
55
|
+
group_ids.collect { |grpid| Ecircle::Group.find_by_id(grpid) }
|
56
|
+
end
|
57
|
+
alias_method :memberships, :groups
|
58
|
+
|
59
|
+
# +group_or_id+ may be a Ecircle::Group
|
60
|
+
# object, containing the group's id, or the id directly.
|
61
|
+
def in_group?(group_or_id)
|
62
|
+
group_ids.include?(Ecircle::User.group_id(group_or_id))
|
63
|
+
end
|
64
|
+
|
65
|
+
def join_group(group, send_invite = false, send_message = false)
|
66
|
+
group.add_member self, send_invite, send_message
|
67
|
+
end
|
68
|
+
|
69
|
+
# Unsubscribe this user from the given group. group may be a Ecircle::Group
|
70
|
+
# object, containing the group's id, or the id directly.
|
71
|
+
# Always returns true.
|
72
|
+
def leave_group(group_or_id, send_message = false)
|
73
|
+
Ecircle.client.
|
74
|
+
unsubscribe_member_by_email(:groupId => Ecircle::User.group_id(group_or_id),
|
75
|
+
:email => email,
|
76
|
+
:sendMessage => send_message)
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_xml
|
80
|
+
obj = self # in instance_eval 'self' will be something else, so create new
|
81
|
+
# reference to te self containing all the data.
|
82
|
+
|
83
|
+
# prefer to use u.send(...) but that creates a new xml element called 'send'!
|
84
|
+
# hence this is using instance_eval with a string.
|
85
|
+
Savon::SOAP::XML.new.xml do |x|
|
86
|
+
x.user(:id => @id) do |u|
|
87
|
+
# email is special, so so so special.
|
88
|
+
u.email(self.email)
|
89
|
+
|
90
|
+
# base attributes of a user
|
91
|
+
[:title, :firstname, :lastname, :nickname, :dob_dd,
|
92
|
+
:dob_mm, :dob_yyyy,:countrycode,:languagecode].each do |field_name|
|
93
|
+
u.instance_eval "%s(obj[:%s])" % ([field_name]*2)
|
94
|
+
end
|
95
|
+
|
96
|
+
# cust_attr_X
|
97
|
+
9.times.collect { |idx| "cust_attr_#{idx+1}" }.
|
98
|
+
each do |field_name|
|
99
|
+
u.instance_eval "%s(obj[:%s])" % ([field_name]*2)
|
100
|
+
end
|
101
|
+
|
102
|
+
# named attributes, these are generic and defined by some guy in a suit.
|
103
|
+
named_attrs.each do |key,value|
|
104
|
+
u.namedattr({:name => key}, value)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def self.group_id(group_obj_or_id)
|
113
|
+
(group_obj_or_id.is_a?(Ecircle::Group) ? group_obj_or_id.id : group_obj_or_id).to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
def initialize_with_xml(xml_string)
|
117
|
+
init_with_xml("user", xml_string)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'savon'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'net/http/post/multipart'
|
4
|
+
require 'uuidtools'
|
5
|
+
require 'base64'
|
6
|
+
require 'ostruct'
|
7
|
+
|
8
|
+
require 'ecircle/configuration'
|
9
|
+
require 'ecircle/client'
|
10
|
+
require 'ecircle/base'
|
11
|
+
require 'ecircle/member'
|
12
|
+
require 'ecircle/group'
|
13
|
+
require 'ecircle/user'
|
14
|
+
require 'ecircle/message'
|
15
|
+
|
16
|
+
# Helper for converting times to ecircle expected format.
|
17
|
+
module EcircleTimeHelper
|
18
|
+
def ecircle_format(prefix)
|
19
|
+
# ecircle uses JS month indices
|
20
|
+
strftime("yyyy:%Y,dd:%d,hh:%H,min:%M,ss:%S,mm:#{'%02d' % (month - 1)}").split(',').
|
21
|
+
inject({}) do |hsh, postfix_and_value|
|
22
|
+
postfix, value = postfix_and_value.split(':')
|
23
|
+
hsh.merge( "#{prefix}_#{postfix}".to_sym => value )
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
[Date, Time].each do |clazz|
|
29
|
+
clazz.send(:include, EcircleTimeHelper)
|
30
|
+
end
|
31
|
+
|
32
|
+
module Ecircle
|
33
|
+
extend self
|
34
|
+
|
35
|
+
def configuration
|
36
|
+
@configuration ||= Configuration.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def client
|
40
|
+
@client ||= Client.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def configure
|
44
|
+
config = configuration
|
45
|
+
block_given? ? yield(config) : config
|
46
|
+
config
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- ruby -*-
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
|
13
|
+
original_load_path = $LOAD_PATH
|
14
|
+
dirs = ['ext', 'lib'].select { |dir| File.directory?(dir) }
|
15
|
+
$LOAD_PATH.unshift(*dirs)
|
16
|
+
|
17
|
+
require 'pry'
|
18
|
+
require 'ecircle_soap_client'
|
19
|
+
|
20
|
+
settings = YAML::load_file(File.join(File.dirname(__FILE__), 'test', '.login.yml'))
|
21
|
+
Ecircle.configure do |config|
|
22
|
+
config.user = settings["user"]
|
23
|
+
config.realm = settings["realm"]
|
24
|
+
config.password = settings["password"]
|
25
|
+
end
|
26
|
+
puts "*** Setup Ecircle"
|
27
|
+
|
28
|
+
Pry.editor = "emacs"
|
29
|
+
Pry.start
|
30
|
+
|
31
|
+
$LOAD_PATH.reject! { |path| !(original_load_path.include?(path)) }
|
data/test/helper.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
begin
|
5
|
+
Bundler.setup(:default, :development)
|
6
|
+
rescue Bundler::BundlerError => e
|
7
|
+
$stderr.puts e.message
|
8
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
9
|
+
exit e.status_code
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'test/unit'
|
13
|
+
require 'shoulda'
|
14
|
+
require 'yaml'
|
15
|
+
require 'rr'
|
16
|
+
|
17
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
18
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
19
|
+
|
20
|
+
require 'ecircle_soap_client'
|
21
|
+
|
22
|
+
class Test::Unit::TestCase
|
23
|
+
include RR::Adapters::TestUnit
|
24
|
+
|
25
|
+
def _soap_fault(msg)
|
26
|
+
os = OpenStruct.new
|
27
|
+
os.body = Savon::SOAP::XML.new.xml do |x|
|
28
|
+
x.envelope do |e|
|
29
|
+
e.body do |b|
|
30
|
+
b.Fault do |f|
|
31
|
+
f.faultcode("client")
|
32
|
+
f.faultstring(msg)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
Savon::SOAP::Fault.new(os)
|
38
|
+
end
|
39
|
+
|
40
|
+
def mock_ecircle_client(create_client_object = false)
|
41
|
+
if create_client_object
|
42
|
+
client, req_obj = Ecircle::Client.new, Object.new
|
43
|
+
mock(client).client { req_obj }
|
44
|
+
# don't mock client since this will be used directly.
|
45
|
+
yield(client, mock(req_obj))
|
46
|
+
else
|
47
|
+
req_object = Object.new
|
48
|
+
mock(Ecircle).client { req_object }
|
49
|
+
yield(mock(req_object))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def in_soap_body
|
54
|
+
<<-SOAP
|
55
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
56
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
57
|
+
<soapenv:Body>
|
58
|
+
#{yield}
|
59
|
+
</soapenv:Body>
|
60
|
+
</soapenv:Envelope>
|
61
|
+
SOAP
|
62
|
+
end
|
63
|
+
|
64
|
+
def mock_response(resp)
|
65
|
+
mock_ecircle_client(true) do |client, savon_client|
|
66
|
+
mock(Ecircle).client { client }
|
67
|
+
mock(client).logon { nil }
|
68
|
+
savon_client.request.with_any_args do
|
69
|
+
Savon::SOAP::Response.new(HTTPI::Response.new(200, {}, resp))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def config_soap_client
|
75
|
+
# keep login details separate from gem.
|
76
|
+
settings = begin
|
77
|
+
YAML::load_file(File.join(File.dirname(__FILE__), '.login.yml'))
|
78
|
+
rescue
|
79
|
+
puts "NO test/.login.yml --> copy the sample across to test"
|
80
|
+
exit 1
|
81
|
+
end
|
82
|
+
|
83
|
+
Ecircle.configure do |config|
|
84
|
+
config.user = settings["user"]
|
85
|
+
config.realm = settings["realm"]
|
86
|
+
config.password = settings["password"]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def assert_difference(expression, difference = 1, message = nil, &block)
|
91
|
+
b = block.send(:binding)
|
92
|
+
exps = Array.wrap(expression)
|
93
|
+
before = exps.map { |e| eval(e, b) }
|
94
|
+
|
95
|
+
yield
|
96
|
+
|
97
|
+
exps.each_with_index do |e, i|
|
98
|
+
error = "#{e.inspect} didn't change by #{difference}"
|
99
|
+
error = "#{message}.\n#{error}" if message
|
100
|
+
assert_equal(before[i] + difference, eval(e, b), error)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Rails extension required for assert_difference (another rails extension)
|
106
|
+
class Array
|
107
|
+
def self.wrap(object)
|
108
|
+
if object.nil?
|
109
|
+
[]
|
110
|
+
elsif object.respond_to?(:to_ary)
|
111
|
+
object.to_ary
|
112
|
+
else
|
113
|
+
[object]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|