Skip to content
taryneast edited this page Sep 13, 2010 · 8 revisions

This is a forked repository. Have a look at
humaneq’s hyperactiveresource wiki
for more information.

HyRes is aimed at Rails 2.3.X which does not have modular Validations (as Rails 3.0 does).
So for HyRes’s validations etc to work you must replace your copy of ActiveResource’s validations.rb with the (stripped down) code to follow.

For Use with Rails 3.0… stay tuned.

module ActiveResource

  class RecordInvalid < ClientError  #:nodoc:
    attr_reader :record
    def initialize(record)
      @record = record
      super("Validation failed: #{@record.errors.full_messages.join(", ")}")
    end
  end
  class ResourceInvalid < ClientError  #:nodoc:
  end

  # Module to support validation and errors with Active Resource objects. The module overrides
  # Base#save to rescue ActiveResource::ResourceInvalid exceptions and parse the errors returned 
  # in the web service response. The module also adds an +errors+ collection that mimics the interface 
  # of the errors provided by ActiveRecord::Errors.
  #
  # ==== Example
  #
  # Consider a Person resource on the server requiring both a +first_name+ and a +last_name+ with a 
  # <tt>validates_presence_of :first_name, :last_name</tt> declaration in the model:
  #
  #   person = Person.new(:first_name => "Jim", :last_name => "")
  #   person.save                   # => false (server returns an HTTP 422 status code and errors)
  #   person.valid?                 # => false
  #   person.errors.empty?          # => false
  #   person.errors.count           # => 1
  #   person.errors.full_messages   # => ["Last name can't be empty"]
  #   person.errors.on(:last_name)  # => "can't be empty"
  #   person.last_name = "Halpert"  
  #   person.save                   # => true (and person is now saved to the remote service)
  #
  module Validations
    def self.included(base) # :nodoc:
      base.class_eval do
        alias_method_chain :save, :validation
      end
    end

    # Validate a resource and save (POST) it to the remote web service.
    def save_with_validation(perform_validation = true)
      # clear the remote validations so they don't interfere with the local
      # ones. Otherwise we get an endless loop and can never change the
      # fields so as to make the resource valid
      @remote_errors = nil
      if perform_validation && valid? || !perform_validation 
        save_without_validation
        true
      else
        false
      end
    rescue ResourceInvalid => error
      @remote_errors = error.response.body
      errors.from_xml(@remote_errors)
      false
    end
    # overload the Active Record valid? method to reload any remote errors we
    # still have hanging around since the last time we touched the remote
    # system
    def valid?
      super
      errors.from_xml(@remote_errors, true) if defined?(@remote_errors) && @remote_errors
      errors.empty?
    end
  end
end

There’s a TODO list on the README file – I’ll update those as I go.

There’s one known bug(that I know):

steps to reproduce:


Site < HyperactiveResource
self.site = ‘http://localhost:3000/’
end

Site.create()

fails with a 500 internal server error (see backtrace at bottom) because the remote API receives: Parameters: {"site"=>"\n"}

So – creating a new thing with an empty set of attributes fails miserably.

Looking at ARes it encodes just the same xml as HyRes… but for some reason ARes never actually sends a create request for an empty object…
It looks like the issue is actually Hash::to_xml which can be shown thus:


>> my_hash = {}
=> {}
>> my_hash.to_xml
=> “<?xml version=\”1.0\" encoding=\“UTF-8\”?>\n\n\n"

The remote site interprets this as: Parameters: {"hash"=>"\n"}
and can’t interpret the “\n” back into a hash as it automatically tries to stringify_keys! (which, of course, a string doesn’t have). :P

so the workaround is to always make sure there is at least one field filled in… not so good if you need to get a valid id before entering stuff… but ok for most normal situations.

backtrace follows:


ActiveResource::ServerError: Failed with 500 Internal Server Error
from /usr/lib/ruby/gems/1.8/gems/activeresource-2.3.2/lib/active_resource/connection.rb:180:in `handle_response’
from /usr/lib/ruby/gems/1.8/gems/activeresource-2.3.2/lib/active_resource/connection.rb:151:in `request’
from /usr/lib/ruby/gems/1.8/gems/activeresource-2.3.2/lib/active_resource/connection.rb:134:in `post’
from /home/taryn/projects/partnerportal/vendor/plugins/hyperactiveresource/lib/hyperactive_resource.rb:340:in `create’
from /usr/lib/ruby/gems/1.8/gems/activeresource-2.3.2/lib/active_resource/base.rb:796:in `save_without_validation’
from /usr/lib/ruby/gems/1.8/gems/activeresource-2.3.2/lib/active_resource/validations.rb:43:in `save’
from /home/taryn/projects/partnerportal/vendor/plugins/hyperactiveresource/lib/hyperactive_resource.rb:209:in `save’
from /usr/lib/ruby/gems/1.8/gems/activeresource-2.3.2/lib/active_resource/base.rb:465:in `create’
from /usr/lib/ruby/gems/1.8/gems/activeresource-2.3.2/lib/active_resource/base.rb:465:in `tap’
from /usr/lib/ruby/gems/1.8/gems/activeresource-2.3.2/lib/active_resource/base.rb:465:in `create’
from (irb):1
Clone this wiki locally