Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 13 additions & 36 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ These are the features that have been added to HyRes.
* Active Record-like #load that doesn't #dup attributes (stores direct reference)
* reload that does a full clear/fetch to reload (also clears associations
cache!)
* now not only extract xml child but also xml attribute


== Callbacks
Expand All @@ -61,6 +62,7 @@ These are the features that have been added to HyRes.
* no 404-explosion for collection-finders that don't return anything They
just return nil (just like Active Record). This includes finders for
associations eg User.posts will return nil if the user hasn't any.
* add :post, :put and :delete options at find


=== Validations
Expand Down Expand Up @@ -118,6 +120,11 @@ Currently:
because (obviously) we're not using SQL...
* it's possible to still pass in :params => {:conditions => ...} (ie old
code shouldn't break) but it's not essential anymore (too complicated).

if your api not restful enough you can custom find by option :post, :put and :delete
Brands.find(:all, :from => '/brands/search.xml', :post => '<?xml version="1.0" encoding="UTF-8"?><search_params><region>EUR</region></search_params>')
will send '<?xml version="1.0" encoding="UTF-8"?><search_params><region>EUR</region></search_params>' with method post to yoursite+'/brands/search.xml'
and parse xml result



Expand Down Expand Up @@ -396,51 +403,21 @@ Currently in progress (ie awaiting discussion on RailsCore):

0) Testing!

1) proper callbacks for before/after save/create/validate etc rather than
bodgied-up functions called directly in the code
1) more customizable

2) MyModel.with_scope
2) if method post or put and using methods 'find_by' automatically create xml data to send to API

3) find(:include => ...)

4) attr_protected/attr_accessible

5) MyModel.calculate/average/minimum/maximum etc

6) reflections. There should be no reason why we can't re-use
Active Record-style reflections for our associations. They are not
SQL-specific. This will also allow a lot more code to automatically Just
Work (eg an Active Record could use "has_many :through" a HyRes)

7) Split HyRes into Base and other grouped functions as per AR

8) default_scope (as per AR)

9) validates_associated (as per AR)

10) write_attribute that actually hits the remoteAPI ???

11) a default format for when it doesn't understand how to deal with a given
mime-formats? One which will just pass back the raw data and let you play
with it?

12) cache the raw (un-decoded) data onto the object so we don't have to do a
second fetch? Or at least allow a universal attribute to be set that turns
on cacheing

13) HABTM - and the reverse (ie AR HABTM HyRes and HyRes HABTM AR)

14) has_many :through - and allowing AR's "through" to work as well

N) merge this stuff back into the real Active Resource (currently underway for Rails 3.0)



== Copyright and Authorship

Author:: Ibnul Qoyyim

== Copyright and Authorship
Forked from :

Author:: Taryn East
Taryn East
Copyright (c) 2009:: White Label Dating [http://whitelabeldating.com]

Based on Work Done by Medical Decision Logic
Expand Down
27 changes: 23 additions & 4 deletions lib/hyperactive_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class ResourceNotFound < AbstractRecordError; end
def self.human_name(options = {})
self.name.humanize
end
# Quick overloading of the ActvieRecord-style naming functions for
# Quick overloading of the ActiveRecord-style naming functions for
# attributes in error messages.
# This will be updated when associations are complete
def self.human_attribute_name(attribute_key_name, options = {})
Expand Down Expand Up @@ -1026,7 +1026,7 @@ def self.method_missing(method_name, *args )

# the user asked for find! and we found nothing - so raise a
# descriptive exception
raise ResourceNotFound, "Couldn't find #{self.name} with #{attr_conds.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(', ')}" if match.bang? && results.blank?
raise ResourceNotFound, "Couldn't find #{self.name} with " + attr_conds.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(', ') if match.bang? && results.blank?

# otherwise return whatever we found
return results
Expand Down Expand Up @@ -1081,12 +1081,31 @@ def self.method_missing(method_name, *args )
def self.find_every(options)
begin
from_value = options.respond_to?(:has_key?) && options.has_key?(:from) ? options.delete(:from) : nil
post_value = options.respond_to?(:has_key?) && options.has_key?(:post) ? options.delete(:post) : nil
put_value = options.respond_to?(:has_key?) && options.has_key?(:put) ? options.delete(:put) : nil
is_delete = options.respond_to?(:has_key?) && options.has_key?(:delete)
case from_value
when Symbol
instantiate_collection(get(from_value, options[:params]))
if !post_value.nil?
instantiate_collection(post(from_value, options[:params], post_value))
elsif !put_value.nil?
instantiate_collection(put(from_value, options[:params], put_value))
elsif is_delete
instantiate_collection(delete(from_value, options[:params]))
else
instantiate_collection(get(from_value, options[:params]))
end
when String
path = "#{from_value}#{query_string(options[:params])}"
instantiate_collection(connection.get(path, headers).arrayify)
if !post_value.nil?
instantiate_collection(format.decode(connection.post(path, post_value, headers).body).arrayify)
elsif !put_value.nil?
instantiate_collection(format.decode(connection.put(path, put_value, headers).body).arrayify)
elsif is_delete
instantiate_collection(connection.delete(path, headers).arrayify)
else
instantiate_collection(connection.get(path, headers).arrayify)
end
else
prefix_options, query_options = split_options(options)
suffix_options = query_options.delete(:suffix_options)
Expand Down
79 changes: 79 additions & 0 deletions lib/parse_xml_attributes_too.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# USAGE: Hash.from_xml:(YOUR_XML_STRING)
require 'nokogiri'
# modified from http://stackoverflow.com/questions/1230741/convert-a-nokogiri-document-to-a-ruby-hash/1231297#1231297
# EDITED from http://gist.github.com/335286

class Hash
class << self
alias :from_xml_without_attributes :from_xml
def from_xml(xml_io, options = {})
begin
return from_xml_without_attributes(xml_io) unless options.has_key?(:with_attributes)
result = Nokogiri::XML(xml_io)
return { result.root.name.to_sym => xml_node_to_hash(result.root)}
rescue Exception => e
# raise your custom exception here
end
end

def xml_node_to_hash(node)
# If we are at the root of the document, start the hash
if node.element?
result_hash = {}
if node.attributes != {}
result_hash[:attributes] = {}
node.attributes.keys.each do |key|
result_hash[:attributes][node.attributes[key].name.to_sym] = prepare(node.attributes[key].value)
end
end
if node.children.size > 0
node.children.each do |child|
result = xml_node_to_hash(child)

if child.name == "text"
unless child.next_sibling || child.previous_sibling
return prepare(result)
end
elsif result_hash[child.name.to_sym]
if result_hash[child.name.to_sym].is_a?(Object::Array)
result_hash[child.name.to_sym] << prepare(result)
else
result_hash[child.name.to_sym] = [result_hash[child.name.to_sym]] << prepare(result)
end
else
result_hash[child.name.to_sym] = prepare(result)
end
end

return result_hash
else
return result_hash
end
else
return prepare(node.content.to_s)
end
end

def prepare(data)
(data.class == String && data.to_i.to_s == data) ? data.to_i : data
end
end

def to_struct(struct_name)
Struct.new(struct_name,*keys).new(*values)
end
end

module ActiveResource
module Formats
module XmlFormat
extend self

def decode(xml)
from_xml_data(Hash.from_xml(xml, :with_attributes))
end
end
end
end