activeresource 2.3.18 → 3.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activeresource might be problematic. Click here for more details.

@@ -0,0 +1,21 @@
1
+ module ActiveResource
2
+ module Observing
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::Observing
5
+
6
+ included do
7
+ %w( create save update destroy ).each do |method|
8
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
9
+ def #{method}_with_notifications(*args, &block)
10
+ notify_observers(:before_#{method})
11
+ if result = #{method}_without_notifications(*args, &block)
12
+ notify_observers(:after_#{method})
13
+ end
14
+ result
15
+ end
16
+ EOS
17
+ alias_method_chain(method, :notifications)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ require "active_resource"
2
+ require "rails"
3
+
4
+ module ActiveResource
5
+ class Railtie < Rails::Railtie
6
+ railtie_name :active_resource
7
+
8
+ require "active_resource/railties/subscriber"
9
+ subscriber ActiveResource::Railties::Subscriber.new
10
+
11
+ initializer "active_resource.set_configs" do |app|
12
+ app.config.active_resource.each do |k,v|
13
+ ActiveResource::Base.send "#{k}=", v
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveResource
2
+ module Railties
3
+ class Subscriber < Rails::Subscriber
4
+ def request(event)
5
+ result = event.payload[:result]
6
+ info "#{event.payload[:method].to_s.upcase} #{event.payload[:request_uri]}"
7
+ info "--> %d %s %d (%.1fms)" % [result.code, result.message, result.body.to_s.length, event.duration]
8
+ end
9
+
10
+ def logger
11
+ ActiveResource::Base.logger
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,55 @@
1
+ require 'active_resource/exceptions'
2
+
3
+ module ActiveResource # :nodoc:
4
+ class Schema # :nodoc:
5
+ # attributes can be known to be one of these types. They are easy to
6
+ # cast to/from.
7
+ KNOWN_ATTRIBUTE_TYPES = %w( string integer float )
8
+
9
+ # An array of attribute definitions, representing the attributes that
10
+ # have been defined.
11
+ attr_accessor :attrs
12
+
13
+ # The internals of an Active Resource Schema are very simple -
14
+ # unlike an Active Record TableDefinition (on which it is based).
15
+ # It provides a set of convenience methods for people to define their
16
+ # schema using the syntax:
17
+ # schema do
18
+ # string :foo
19
+ # integer :bar
20
+ # end
21
+ #
22
+ # The schema stores the name and type of each attribute. That is then
23
+ # read out by the schema method to populate the actual
24
+ # Resource's schema
25
+ def initialize
26
+ @attrs = {}
27
+ end
28
+
29
+ def attribute(name, type, options = {})
30
+ raise ArgumentError, "Unknown Attribute type: #{type.inspect} for key: #{name.inspect}" unless type.nil? || Schema::KNOWN_ATTRIBUTE_TYPES.include?(type.to_s)
31
+
32
+ the_type = type.to_s
33
+ # TODO: add defaults
34
+ #the_attr = [type.to_s]
35
+ #the_attr << options[:default] if options.has_key? :default
36
+ @attrs[name.to_s] = the_type
37
+ self
38
+ end
39
+
40
+ # The following are the attribute types supported by Active Resource
41
+ # migrations.
42
+ # TODO: We should eventually support all of these:
43
+ # %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |attr_type|
44
+ KNOWN_ATTRIBUTE_TYPES.each do |attr_type|
45
+ class_eval <<-EOV
46
+ def #{attr_type.to_s}(*args)
47
+ options = args.extract_options!
48
+ attr_names = args
49
+
50
+ attr_names.each { |name| attribute(name, '#{attr_type}', options) }
51
+ end
52
+ EOV
53
+ end
54
+ end
55
+ end
@@ -1,207 +1,17 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
1
3
  module ActiveResource
2
4
  class ResourceInvalid < ClientError #:nodoc:
3
5
  end
4
6
 
5
7
  # Active Resource validation is reported to and from this object, which is used by Base#save
6
8
  # 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
-
9
+ class Errors < ActiveModel::Errors
202
10
  # Grabs errors from an array of messages (like ActiveRecord::Validations)
203
- def from_array(messages)
204
- clear
11
+ # The second parameter directs the errors cache to be cleared (default)
12
+ # or not (by passing true)
13
+ def from_array(messages, save_cache = false)
14
+ clear unless save_cache
205
15
  humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.humanize => attr_name) }
206
16
  messages.each do |message|
207
17
  attr_message = humanized_attributes.keys.detect do |attr_name|
@@ -210,20 +20,20 @@ module ActiveResource
210
20
  end
211
21
  end
212
22
 
213
- add_to_base message if attr_message.nil?
23
+ self[:base] << message if attr_message.nil?
214
24
  end
215
25
  end
216
26
 
217
- # Grabs errors from the json response.
218
- def from_json(json)
27
+ # Grabs errors from a json response.
28
+ def from_json(json, save_cache = false)
219
29
  array = ActiveSupport::JSON.decode(json)['errors'] rescue []
220
- from_array array
30
+ from_array array, save_cache
221
31
  end
222
32
 
223
- # Grabs errors from the XML response.
224
- def from_xml(xml)
33
+ # Grabs errors from an XML response.
34
+ def from_xml(xml, save_cache = false)
225
35
  array = Array.wrap(Hash.from_xml(xml)['errors']['error']) rescue []
226
- from_array array
36
+ from_array array, save_cache
227
37
  end
228
38
  end
229
39
 
@@ -243,32 +53,70 @@ module ActiveResource
243
53
  # person.errors.empty? # => false
244
54
  # person.errors.count # => 1
245
55
  # person.errors.full_messages # => ["Last name can't be empty"]
246
- # person.errors.on(:last_name) # => "can't be empty"
56
+ # person.errors[:last_name] # => ["can't be empty"]
247
57
  # person.last_name = "Halpert"
248
58
  # person.save # => true (and person is now saved to the remote service)
249
59
  #
250
60
  module Validations
251
- def self.included(base) # :nodoc:
252
- base.class_eval do
253
- alias_method_chain :save, :validation
254
- end
61
+ extend ActiveSupport::Concern
62
+ include ActiveModel::Validations
63
+
64
+ included do
65
+ alias_method_chain :save, :validation
255
66
  end
256
67
 
257
68
  # Validate a resource and save (POST) it to the remote web service.
258
- def save_with_validation
259
- save_without_validation
260
- true
69
+ # If any local validations fail - the save (POST) will not be attempted.
70
+ def save_with_validation(options=nil)
71
+ perform_validation = case options
72
+ when Hash
73
+ options[:validate] != false
74
+ when NilClass
75
+ true
76
+ else
77
+ ActiveSupport::Deprecation.warn "save(#{options}) is deprecated, please give save(:validate => #{options}) instead", caller
78
+ options
79
+ end
80
+
81
+ # clear the remote validations so they don't interfere with the local
82
+ # ones. Otherwise we get an endless loop and can never change the
83
+ # fields so as to make the resource valid
84
+ @remote_errors = nil
85
+ if perform_validation && valid? || !perform_validation
86
+ save_without_validation
87
+ true
88
+ else
89
+ false
90
+ end
261
91
  rescue ResourceInvalid => error
92
+ # cache the remote errors because every call to <tt>valid?</tt> clears
93
+ # all errors. We must keep a copy to add these back after local
94
+ # validations
95
+ @remote_errors = error
96
+ load_remote_errors(@remote_errors, true)
97
+ false
98
+ end
99
+
100
+
101
+ # Loads the set of remote errors into the object's Errors based on the
102
+ # content-type of the error-block received
103
+ def load_remote_errors(remote_errors, save_cache = false ) #:nodoc:
262
104
  case self.class.format
263
105
  when ActiveResource::Formats[:xml]
264
- errors.from_xml(error.response.body)
106
+ errors.from_xml(remote_errors.response.body, save_cache)
265
107
  when ActiveResource::Formats[:json]
266
- errors.from_json(error.response.body)
108
+ errors.from_json(remote_errors.response.body, save_cache)
267
109
  end
268
- false
269
110
  end
270
111
 
271
112
  # Checks for errors on an object (i.e., is resource.errors empty?).
113
+ #
114
+ # Runs all the specified local validations and returns true if no errors
115
+ # were added, otherwise false.
116
+ # Runs local validations (eg those on your Active Resource model), and
117
+ # also any errors returned from the remote system the last time we
118
+ # saved.
119
+ # Remote errors can only be cleared by trying to re-save the resource.
272
120
  #
273
121
  # ==== Examples
274
122
  # my_person = Person.create(params[:person])
@@ -278,7 +126,10 @@ module ActiveResource
278
126
  # my_person.errors.add('login', 'can not be empty') if my_person.login == ''
279
127
  # my_person.valid?
280
128
  # # => false
129
+ #
281
130
  def valid?
131
+ super
132
+ load_remote_errors(@remote_errors, true) if defined?(@remote_errors) && @remote_errors.present?
282
133
  errors.empty?
283
134
  end
284
135
 
@@ -1,8 +1,8 @@
1
1
  module ActiveResource
2
2
  module VERSION #:nodoc:
3
- MAJOR = 2
4
- MINOR = 3
5
- TINY = 18
3
+ MAJOR = 3
4
+ MINOR = 0
5
+ TINY = "0.beta"
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end