diff --git a/README b/README index deabb73..0407694 100644 --- a/README +++ b/README @@ -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 @@ -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 @@ -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 => 'EUR') +will send 'EUR' with method post to yoursite+'/brands/search.xml' +and parse xml result @@ -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 diff --git a/lib/hyperactive_resource.rb b/lib/hyperactive_resource.rb index 285c0a9..c818723 100644 --- a/lib/hyperactive_resource.rb +++ b/lib/hyperactive_resource.rb @@ -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 = {}) @@ -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 @@ -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) diff --git a/lib/parse_xml_attributes_too.rb b/lib/parse_xml_attributes_too.rb new file mode 100644 index 0000000..fc2b0d6 --- /dev/null +++ b/lib/parse_xml_attributes_too.rb @@ -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 + +