activeresource_csi 2.3.5.p6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +292 -0
- data/README +165 -0
- data/Rakefile +139 -0
- data/lib/active_resource/base.rb +1157 -0
- data/lib/active_resource/connection.rb +283 -0
- data/lib/active_resource/custom_methods.rb +120 -0
- data/lib/active_resource/exceptions.rb +66 -0
- data/lib/active_resource/formats/json_format.rb +23 -0
- data/lib/active_resource/formats/xml_format.rb +34 -0
- data/lib/active_resource/formats.rb +14 -0
- data/lib/active_resource/http_mock.rb +207 -0
- data/lib/active_resource/validations.rb +290 -0
- data/lib/active_resource/version.rb +9 -0
- data/lib/active_resource.rb +44 -0
- data/lib/activeresource.rb +2 -0
- data/test/abstract_unit.rb +21 -0
- data/test/authorization_test.rb +122 -0
- data/test/base/custom_methods_test.rb +100 -0
- data/test/base/equality_test.rb +52 -0
- data/test/base/load_test.rb +161 -0
- data/test/base_errors_test.rb +85 -0
- data/test/base_test.rb +1038 -0
- data/test/connection_test.rb +238 -0
- data/test/debug.log +5335 -0
- data/test/fixtures/beast.rb +14 -0
- data/test/fixtures/customer.rb +3 -0
- data/test/fixtures/person.rb +3 -0
- data/test/fixtures/proxy.rb +4 -0
- data/test/fixtures/street_address.rb +4 -0
- data/test/format_test.rb +112 -0
- data/test/setter_trap.rb +26 -0
- metadata +119 -0
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'active_resource/connection'
|
2
|
+
|
3
|
+
module ActiveResource
|
4
|
+
class InvalidRequestError < StandardError; end #:nodoc:
|
5
|
+
|
6
|
+
# One thing that has always been a pain with remote web services is testing. The HttpMock
|
7
|
+
# class makes it easy to test your Active Resource models by creating a set of mock responses to specific
|
8
|
+
# requests.
|
9
|
+
#
|
10
|
+
# To test your Active Resource model, you simply call the ActiveResource::HttpMock.respond_to
|
11
|
+
# method with an attached block. The block declares a set of URIs with expected input, and the output
|
12
|
+
# each request should return. The passed in block has any number of entries in the following generalized
|
13
|
+
# format:
|
14
|
+
#
|
15
|
+
# mock.http_method(path, request_headers = {}, body = nil, status = 200, response_headers = {})
|
16
|
+
#
|
17
|
+
# * <tt>http_method</tt> - The HTTP method to listen for. This can be +get+, +post+, +put+, +delete+ or
|
18
|
+
# +head+.
|
19
|
+
# * <tt>path</tt> - A string, starting with a "/", defining the URI that is expected to be
|
20
|
+
# called.
|
21
|
+
# * <tt>request_headers</tt> - Headers that are expected along with the request. This argument uses a
|
22
|
+
# hash format, such as <tt>{ "Content-Type" => "application/xml" }</tt>. This mock will only trigger
|
23
|
+
# if your tests sends a request with identical headers.
|
24
|
+
# * <tt>body</tt> - The data to be returned. This should be a string of Active Resource parseable content,
|
25
|
+
# such as XML.
|
26
|
+
# * <tt>status</tt> - The HTTP response code, as an integer, to return with the response.
|
27
|
+
# * <tt>response_headers</tt> - Headers to be returned with the response. Uses the same hash format as
|
28
|
+
# <tt>request_headers</tt> listed above.
|
29
|
+
#
|
30
|
+
# In order for a mock to deliver its content, the incoming request must match by the <tt>http_method</tt>,
|
31
|
+
# +path+ and <tt>request_headers</tt>. If no match is found an InvalidRequestError exception
|
32
|
+
# will be raised letting you know you need to create a new mock for that request.
|
33
|
+
#
|
34
|
+
# ==== Example
|
35
|
+
# def setup
|
36
|
+
# @matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person")
|
37
|
+
# ActiveResource::HttpMock.respond_to do |mock|
|
38
|
+
# mock.post "/people.xml", {}, @matz, 201, "Location" => "/people/1.xml"
|
39
|
+
# mock.get "/people/1.xml", {}, @matz
|
40
|
+
# mock.put "/people/1.xml", {}, nil, 204
|
41
|
+
# mock.delete "/people/1.xml", {}, nil, 200
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# def test_get_matz
|
46
|
+
# person = Person.find(1)
|
47
|
+
# assert_equal "Matz", person.name
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
class HttpMock
|
51
|
+
class Responder #:nodoc:
|
52
|
+
def initialize(responses)
|
53
|
+
@responses = responses
|
54
|
+
end
|
55
|
+
|
56
|
+
for method in [ :post, :put, :get, :delete, :head ]
|
57
|
+
# def post(path, request_headers = {}, body = nil, status = 200, response_headers = {})
|
58
|
+
# @responses[Request.new(:post, path, nil, request_headers)] = Response.new(body || "", status, response_headers)
|
59
|
+
# end
|
60
|
+
module_eval <<-EOE, __FILE__, __LINE__
|
61
|
+
def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {})
|
62
|
+
@responses << [Request.new(:#{method}, path, nil, request_headers), Response.new(body || "", status, response_headers)]
|
63
|
+
end
|
64
|
+
EOE
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class << self
|
69
|
+
|
70
|
+
# Returns an array of all request objects that have been sent to the mock. You can use this to check
|
71
|
+
# if your model actually sent an HTTP request.
|
72
|
+
#
|
73
|
+
# ==== Example
|
74
|
+
# def setup
|
75
|
+
# @matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person")
|
76
|
+
# ActiveResource::HttpMock.respond_to do |mock|
|
77
|
+
# mock.get "/people/1.xml", {}, @matz
|
78
|
+
# end
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# def test_should_request_remote_service
|
82
|
+
# person = Person.find(1) # Call the remote service
|
83
|
+
#
|
84
|
+
# # This request object has the same HTTP method and path as declared by the mock
|
85
|
+
# expected_request = ActiveResource::Request.new(:get, "/people/1.xml")
|
86
|
+
#
|
87
|
+
# # Assert that the mock received, and responded to, the expected request from the model
|
88
|
+
# assert ActiveResource::HttpMock.requests.include?(expected_request)
|
89
|
+
# end
|
90
|
+
def requests
|
91
|
+
@@requests ||= []
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns the list of requests and their mocked responses. Look up a
|
95
|
+
# response for a request using responses.assoc(request).
|
96
|
+
def responses
|
97
|
+
@@responses ||= []
|
98
|
+
end
|
99
|
+
|
100
|
+
# Accepts a block which declares a set of requests and responses for the HttpMock to respond to. See the main
|
101
|
+
# ActiveResource::HttpMock description for a more detailed explanation.
|
102
|
+
def respond_to(pairs = {}) #:yields: mock
|
103
|
+
reset!
|
104
|
+
responses.concat pairs.to_a
|
105
|
+
if block_given?
|
106
|
+
yield Responder.new(responses)
|
107
|
+
else
|
108
|
+
Responder.new(responses)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Deletes all logged requests and responses.
|
113
|
+
def reset!
|
114
|
+
requests.clear
|
115
|
+
responses.clear
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# body? methods
|
120
|
+
{ true => %w(post put),
|
121
|
+
false => %w(get delete head) }.each do |has_body, methods|
|
122
|
+
methods.each do |method|
|
123
|
+
# def post(path, body, headers)
|
124
|
+
# request = ActiveResource::Request.new(:post, path, body, headers)
|
125
|
+
# self.class.requests << request
|
126
|
+
# self.class.responses.assoc(request).try(:second) || raise(InvalidRequestError.new("No response recorded for #{request}"))
|
127
|
+
# end
|
128
|
+
module_eval <<-EOE, __FILE__, __LINE__
|
129
|
+
def #{method}(path, #{'body, ' if has_body}headers)
|
130
|
+
request = ActiveResource::Request.new(:#{method}, path, #{has_body ? 'body, ' : 'nil, '}headers)
|
131
|
+
self.class.requests << request
|
132
|
+
self.class.responses.assoc(request).try(:second) || raise(InvalidRequestError.new("No response recorded for \#{request}"))
|
133
|
+
end
|
134
|
+
EOE
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def initialize(site) #:nodoc:
|
139
|
+
@site = site
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class Request
|
144
|
+
attr_accessor :path, :method, :body, :headers
|
145
|
+
|
146
|
+
def initialize(method, path, body = nil, headers = {})
|
147
|
+
@method, @path, @body, @headers = method, path, body, headers.merge(ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[method] => 'application/xml')
|
148
|
+
end
|
149
|
+
|
150
|
+
def ==(req)
|
151
|
+
path == req.path && method == req.method && headers == req.headers
|
152
|
+
end
|
153
|
+
|
154
|
+
def to_s
|
155
|
+
"<#{method.to_s.upcase}: #{path} [#{headers}] (#{body})>"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class Response
|
160
|
+
attr_accessor :body, :message, :code, :headers
|
161
|
+
|
162
|
+
def initialize(body, message = 200, headers = {})
|
163
|
+
@body, @message, @headers = body, message.to_s, headers
|
164
|
+
@code = @message[0,3].to_i
|
165
|
+
|
166
|
+
resp_cls = Net::HTTPResponse::CODE_TO_OBJ[@code.to_s]
|
167
|
+
if resp_cls && !resp_cls.body_permitted?
|
168
|
+
@body = nil
|
169
|
+
end
|
170
|
+
|
171
|
+
if @body.nil?
|
172
|
+
self['Content-Length'] = "0"
|
173
|
+
else
|
174
|
+
self['Content-Length'] = body.size.to_s
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def success?
|
179
|
+
(200..299).include?(code)
|
180
|
+
end
|
181
|
+
|
182
|
+
def [](key)
|
183
|
+
headers[key]
|
184
|
+
end
|
185
|
+
|
186
|
+
def []=(key, value)
|
187
|
+
headers[key] = value
|
188
|
+
end
|
189
|
+
|
190
|
+
def ==(other)
|
191
|
+
if (other.is_a?(Response))
|
192
|
+
other.body == body && other.message == message && other.headers == headers
|
193
|
+
else
|
194
|
+
false
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
class Connection
|
200
|
+
private
|
201
|
+
silence_warnings do
|
202
|
+
def http
|
203
|
+
@http ||= HttpMock.new(@site)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,290 @@
|
|
1
|
+
module ActiveResource
|
2
|
+
class ResourceInvalid < ClientError #:nodoc:
|
3
|
+
end
|
4
|
+
|
5
|
+
# Active Resource validation is reported to and from this object, which is used by Base#save
|
6
|
+
# to determine whether the object in a valid state to be saved. See usage example in Validations.
|
7
|
+
class Errors
|
8
|
+
include Enumerable
|
9
|
+
attr_reader :errors
|
10
|
+
|
11
|
+
delegate :empty?, :to => :errors
|
12
|
+
|
13
|
+
def initialize(base) # :nodoc:
|
14
|
+
@base, @errors = base, {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Add an error to the base Active Resource object rather than an attribute.
|
18
|
+
#
|
19
|
+
# ==== Examples
|
20
|
+
# my_folder = Folder.find(1)
|
21
|
+
# my_folder.errors.add_to_base("You can't edit an existing folder")
|
22
|
+
# my_folder.errors.on_base
|
23
|
+
# # => "You can't edit an existing folder"
|
24
|
+
#
|
25
|
+
# my_folder.errors.add_to_base("This folder has been tagged as frozen")
|
26
|
+
# my_folder.valid?
|
27
|
+
# # => false
|
28
|
+
# my_folder.errors.on_base
|
29
|
+
# # => ["You can't edit an existing folder", "This folder has been tagged as frozen"]
|
30
|
+
#
|
31
|
+
def add_to_base(msg)
|
32
|
+
add(:base, msg)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Adds an error to an Active Resource object's attribute (named for the +attribute+ parameter)
|
36
|
+
# with the error message in +msg+.
|
37
|
+
#
|
38
|
+
# ==== Examples
|
39
|
+
# my_resource = Node.find(1)
|
40
|
+
# my_resource.errors.add('name', 'can not be "base"') if my_resource.name == 'base'
|
41
|
+
# my_resource.errors.on('name')
|
42
|
+
# # => 'can not be "base"!'
|
43
|
+
#
|
44
|
+
# my_resource.errors.add('desc', 'can not be blank') if my_resource.desc == ''
|
45
|
+
# my_resource.valid?
|
46
|
+
# # => false
|
47
|
+
# my_resource.errors.on('desc')
|
48
|
+
# # => 'can not be blank!'
|
49
|
+
#
|
50
|
+
def add(attribute, msg)
|
51
|
+
@errors[attribute.to_s] = [] if @errors[attribute.to_s].nil?
|
52
|
+
@errors[attribute.to_s] << msg
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns true if the specified +attribute+ has errors associated with it.
|
56
|
+
#
|
57
|
+
# ==== Examples
|
58
|
+
# my_resource = Disk.find(1)
|
59
|
+
# my_resource.errors.add('location', 'must be Main') unless my_resource.location == 'Main'
|
60
|
+
# my_resource.errors.on('location')
|
61
|
+
# # => 'must be Main!'
|
62
|
+
#
|
63
|
+
# my_resource.errors.invalid?('location')
|
64
|
+
# # => true
|
65
|
+
# my_resource.errors.invalid?('name')
|
66
|
+
# # => false
|
67
|
+
def invalid?(attribute)
|
68
|
+
!@errors[attribute.to_s].nil?
|
69
|
+
end
|
70
|
+
|
71
|
+
# A method to return the errors associated with +attribute+, which returns nil, if no errors are
|
72
|
+
# associated with the specified +attribute+, the error message if one error is associated with the specified +attribute+,
|
73
|
+
# or an array of error messages if more than one error is associated with the specified +attribute+.
|
74
|
+
#
|
75
|
+
# ==== Examples
|
76
|
+
# my_person = Person.new(params[:person])
|
77
|
+
# my_person.errors.on('login')
|
78
|
+
# # => nil
|
79
|
+
#
|
80
|
+
# my_person.errors.add('login', 'can not be empty') if my_person.login == ''
|
81
|
+
# my_person.errors.on('login')
|
82
|
+
# # => 'can not be empty'
|
83
|
+
#
|
84
|
+
# my_person.errors.add('login', 'can not be longer than 10 characters') if my_person.login.length > 10
|
85
|
+
# my_person.errors.on('login')
|
86
|
+
# # => ['can not be empty', 'can not be longer than 10 characters']
|
87
|
+
def on(attribute)
|
88
|
+
errors = @errors[attribute.to_s]
|
89
|
+
return nil if errors.nil?
|
90
|
+
errors.size == 1 ? errors.first : errors
|
91
|
+
end
|
92
|
+
|
93
|
+
alias :[] :on
|
94
|
+
|
95
|
+
# A method to return errors assigned to +base+ object through add_to_base, which returns nil, if no errors are
|
96
|
+
# associated with the specified +attribute+, the error message if one error is associated with the specified +attribute+,
|
97
|
+
# or an array of error messages if more than one error is associated with the specified +attribute+.
|
98
|
+
#
|
99
|
+
# ==== Examples
|
100
|
+
# my_account = Account.find(1)
|
101
|
+
# my_account.errors.on_base
|
102
|
+
# # => nil
|
103
|
+
#
|
104
|
+
# my_account.errors.add_to_base("This account is frozen")
|
105
|
+
# my_account.errors.on_base
|
106
|
+
# # => "This account is frozen"
|
107
|
+
#
|
108
|
+
# my_account.errors.add_to_base("This account has been closed")
|
109
|
+
# my_account.errors.on_base
|
110
|
+
# # => ["This account is frozen", "This account has been closed"]
|
111
|
+
#
|
112
|
+
def on_base
|
113
|
+
on(:base)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Yields each attribute and associated message per error added.
|
117
|
+
#
|
118
|
+
# ==== Examples
|
119
|
+
# my_person = Person.new(params[:person])
|
120
|
+
#
|
121
|
+
# my_person.errors.add('login', 'can not be empty') if my_person.login == ''
|
122
|
+
# my_person.errors.add('password', 'can not be empty') if my_person.password == ''
|
123
|
+
# messages = ''
|
124
|
+
# my_person.errors.each {|attr, msg| messages += attr.humanize + " " + msg + "<br />"}
|
125
|
+
# messages
|
126
|
+
# # => "Login can not be empty<br />Password can not be empty<br />"
|
127
|
+
#
|
128
|
+
def each
|
129
|
+
@errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } }
|
130
|
+
end
|
131
|
+
|
132
|
+
# Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned
|
133
|
+
# through iteration as "First name can't be empty".
|
134
|
+
#
|
135
|
+
# ==== Examples
|
136
|
+
# my_person = Person.new(params[:person])
|
137
|
+
#
|
138
|
+
# my_person.errors.add('login', 'can not be empty') if my_person.login == ''
|
139
|
+
# my_person.errors.add('password', 'can not be empty') if my_person.password == ''
|
140
|
+
# messages = ''
|
141
|
+
# my_person.errors.each_full {|msg| messages += msg + "<br/>"}
|
142
|
+
# messages
|
143
|
+
# # => "Login can not be empty<br />Password can not be empty<br />"
|
144
|
+
#
|
145
|
+
def each_full
|
146
|
+
full_messages.each { |msg| yield msg }
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns all the full error messages in an array.
|
150
|
+
#
|
151
|
+
# ==== Examples
|
152
|
+
# my_person = Person.new(params[:person])
|
153
|
+
#
|
154
|
+
# my_person.errors.add('login', 'can not be empty') if my_person.login == ''
|
155
|
+
# my_person.errors.add('password', 'can not be empty') if my_person.password == ''
|
156
|
+
# messages = ''
|
157
|
+
# my_person.errors.full_messages.each {|msg| messages += msg + "<br/>"}
|
158
|
+
# messages
|
159
|
+
# # => "Login can not be empty<br />Password can not be empty<br />"
|
160
|
+
#
|
161
|
+
def full_messages
|
162
|
+
full_messages = []
|
163
|
+
|
164
|
+
@errors.each_key do |attr|
|
165
|
+
@errors[attr].each do |msg|
|
166
|
+
next if msg.nil?
|
167
|
+
|
168
|
+
if attr == "base"
|
169
|
+
full_messages << msg
|
170
|
+
else
|
171
|
+
full_messages << [attr.humanize, msg].join(' ')
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
full_messages
|
176
|
+
end
|
177
|
+
|
178
|
+
def clear
|
179
|
+
@errors = {}
|
180
|
+
end
|
181
|
+
|
182
|
+
# Returns the total number of errors added. Two errors added to the same attribute will be counted as such
|
183
|
+
# with this as well.
|
184
|
+
#
|
185
|
+
# ==== Examples
|
186
|
+
# my_person = Person.new(params[:person])
|
187
|
+
# my_person.errors.size
|
188
|
+
# # => 0
|
189
|
+
#
|
190
|
+
# my_person.errors.add('login', 'can not be empty') if my_person.login == ''
|
191
|
+
# my_person.errors.add('password', 'can not be empty') if my_person.password == ''
|
192
|
+
# my_person.error.size
|
193
|
+
# # => 2
|
194
|
+
#
|
195
|
+
def size
|
196
|
+
@errors.values.inject(0) { |error_count, attribute| error_count + attribute.size }
|
197
|
+
end
|
198
|
+
|
199
|
+
alias_method :count, :size
|
200
|
+
alias_method :length, :size
|
201
|
+
|
202
|
+
# Grabs errors from an array of messages (like ActiveRecord::Validations)
|
203
|
+
def from_array(messages)
|
204
|
+
clear
|
205
|
+
humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.humanize => attr_name) }
|
206
|
+
messages.each do |message|
|
207
|
+
attr_message = humanized_attributes.keys.detect do |attr_name|
|
208
|
+
if message[0, attr_name.size + 1] == "#{attr_name} "
|
209
|
+
add humanized_attributes[attr_name], message[(attr_name.size + 1)..-1]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
add_to_base message if attr_message.nil?
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Grabs errors from the json response.
|
218
|
+
def from_json(json)
|
219
|
+
array = ActiveSupport::JSON.decode(json)['errors'] rescue []
|
220
|
+
from_array array
|
221
|
+
end
|
222
|
+
|
223
|
+
# Grabs errors from the XML response.
|
224
|
+
def from_xml(xml)
|
225
|
+
array = Array.wrap(Hash.from_xml(xml)['errors']['error']) rescue []
|
226
|
+
from_array array
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Module to support validation and errors with Active Resource objects. The module overrides
|
231
|
+
# Base#save to rescue ActiveResource::ResourceInvalid exceptions and parse the errors returned
|
232
|
+
# in the web service response. The module also adds an +errors+ collection that mimics the interface
|
233
|
+
# of the errors provided by ActiveRecord::Errors.
|
234
|
+
#
|
235
|
+
# ==== Example
|
236
|
+
#
|
237
|
+
# Consider a Person resource on the server requiring both a +first_name+ and a +last_name+ with a
|
238
|
+
# <tt>validates_presence_of :first_name, :last_name</tt> declaration in the model:
|
239
|
+
#
|
240
|
+
# person = Person.new(:first_name => "Jim", :last_name => "")
|
241
|
+
# person.save # => false (server returns an HTTP 422 status code and errors)
|
242
|
+
# person.valid? # => false
|
243
|
+
# person.errors.empty? # => false
|
244
|
+
# person.errors.count # => 1
|
245
|
+
# person.errors.full_messages # => ["Last name can't be empty"]
|
246
|
+
# person.errors.on(:last_name) # => "can't be empty"
|
247
|
+
# person.last_name = "Halpert"
|
248
|
+
# person.save # => true (and person is now saved to the remote service)
|
249
|
+
#
|
250
|
+
module Validations
|
251
|
+
def self.included(base) # :nodoc:
|
252
|
+
base.class_eval do
|
253
|
+
alias_method_chain :save, :validation
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
# Validate a resource and save (POST) it to the remote web service.
|
258
|
+
def save_with_validation
|
259
|
+
save_without_validation
|
260
|
+
true
|
261
|
+
rescue ResourceInvalid => error
|
262
|
+
case error.response['Content-Type']
|
263
|
+
when /xml/
|
264
|
+
errors.from_xml(error.response.body)
|
265
|
+
when /json/
|
266
|
+
errors.from_json(error.response.body)
|
267
|
+
end
|
268
|
+
false
|
269
|
+
end
|
270
|
+
|
271
|
+
# Checks for errors on an object (i.e., is resource.errors empty?).
|
272
|
+
#
|
273
|
+
# ==== Examples
|
274
|
+
# my_person = Person.create(params[:person])
|
275
|
+
# my_person.valid?
|
276
|
+
# # => true
|
277
|
+
#
|
278
|
+
# my_person.errors.add('login', 'can not be empty') if my_person.login == ''
|
279
|
+
# my_person.valid?
|
280
|
+
# # => false
|
281
|
+
def valid?
|
282
|
+
errors.empty?
|
283
|
+
end
|
284
|
+
|
285
|
+
# Returns the Errors object that holds all information about attribute error messages.
|
286
|
+
def errors
|
287
|
+
@errors ||= Errors.new(self)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 David Heinemeier Hansson
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
begin
|
25
|
+
require 'active_support'
|
26
|
+
rescue LoadError
|
27
|
+
activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
|
28
|
+
if File.directory?(activesupport_path)
|
29
|
+
$:.unshift activesupport_path
|
30
|
+
require 'active_support'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'active_resource/formats'
|
35
|
+
require 'active_resource/base'
|
36
|
+
require 'active_resource/validations'
|
37
|
+
require 'active_resource/custom_methods'
|
38
|
+
|
39
|
+
module ActiveResource
|
40
|
+
Base.class_eval do
|
41
|
+
include Validations
|
42
|
+
include CustomMethods
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'active_support/test_case'
|
4
|
+
|
5
|
+
$:.unshift "#{File.dirname(__FILE__)}/../lib"
|
6
|
+
$:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib"
|
7
|
+
require 'active_resource'
|
8
|
+
require 'active_resource/http_mock'
|
9
|
+
|
10
|
+
$:.unshift "#{File.dirname(__FILE__)}/../test"
|
11
|
+
require 'setter_trap'
|
12
|
+
|
13
|
+
ActiveResource::Base.logger = Logger.new("#{File.dirname(__FILE__)}/debug.log")
|
14
|
+
|
15
|
+
def uses_gem(gem_name, test_name, version = '> 0')
|
16
|
+
gem gem_name.to_s, version
|
17
|
+
require gem_name.to_s
|
18
|
+
yield
|
19
|
+
rescue LoadError
|
20
|
+
$stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again."
|
21
|
+
end
|