lib/hyper_resource.rb in hyperresource-0.1.9.2 vs lib/hyper_resource.rb in hyperresource-0.1.9.3

- old
+ new

@@ -1,25 +1,10 @@ -require 'hyper_resource/version' -require 'hyper_resource/attributes' -require 'hyper_resource/links' -require 'hyper_resource/link' -require 'hyper_resource/objects' -require 'hyper_resource/response' -require 'hyper_resource/exceptions' +this_dir = File.dirname(File.absolute_path(__FILE__)) +Dir.glob(this_dir + '/hyper_resource/**/*.rb') {|f| require f} -require 'hyper_resource/modules/utils' -require 'hyper_resource/modules/http' - -require 'hyper_resource/adapter' -require 'hyper_resource/adapter/hal_json' - require 'pp' -## TODO: -## as_json, to_json (in adapter?) -## save, update, create, delete - class HyperResource include HyperResource::Modules::Utils include HyperResource::Modules::HTTP private @@ -115,41 +100,19 @@ self.adapter = opts[:adapter] || self.class.adapter || HyperResource::Adapter::HAL_JSON end -private - def init_from_resource(resource) - (self.class._hr_attributes - [:attributes, :links, :objects]).each do |attr| - self.send("#{attr}=".to_sym, resource.send(attr)) - end - self.adapter.apply(self.response_object, self) - end - public + ## Returns true if one or more of this object's attributes has been + ## reassigned. def changed?(*args) attributes.changed?(*args) end - ## Returns a new HyperResource based on the given link href. - def _new_from_link(href) - self.class.new(:root => self.root, - :auth => self.auth, - :headers => self.headers, - :namespace => self.namespace, - :href => href) - end - - def to_response_class - response_class = self.get_response_class - return self if self.class == response_class - response_class.new(self) - end - - ## +incoming_body_filter+ filters a hash of attribute keys and values ## on their way from a response body to a HyperResource. Override this ## in a subclass of HyperResource to implement filters on incoming data. def incoming_body_filter(attr_hash) attr_hash @@ -169,34 +132,87 @@ def outgoing_uri_filter(attr_hash) attr_hash end - def get_response_class # :nodoc: - self.namespace ||= self.class.to_s unless self.class.to_s=='HyperResource' - self.class.get_response_class(self.response, self.namespace) + ## Returns the first object in the first collection of objects embedded + ## in this resource. Equivalent to +self.objects.first+. + def first; self.objects.first end + + ## Returns the *i*th object in the first collection of objects embedded + ## in this resource. Equivalent to +self.objects[i]+. + def [](i); self.objects.ith(i) end + + ## method_missing will load this resource if not yet loaded, then + ## attempt to delegate to +attributes+, then +objects+, then +links+. + ## Override with care. + def method_missing(method, *args) + self.get unless self.loaded + + method = method.to_s + if method[-1] == '=' + return attributes[method[0..-2]] = args.first if attributes[method[0..-2]] + else + return attributes[method] if attributes && attributes[method] + return objects[method] if objects && objects[method] + if links && links[method] + if args.count > 0 + return links[method].where(*args) + else + return links[method] + end + end + end + + raise NoMethodError, "undefined method `#{method}' for #{self.inspect}" end + + def inspect # :nodoc: + "#<#{self.class}:0x#{"%x" % self.object_id} @root=#{self.root.inspect} "+ + "@href=#{self.href.inspect} @loaded=#{self.loaded} "+ + "@namespace=#{self.namespace.inspect} ...>" + end + + ## +response_body+ is deprecated in favor of +response_object+. + def response_body; response_object end # :nodoc: + + + ####################################################################### + #### Underscored functions are not meant to be used outside of #### + #### HyperResource machinery. You have been warned. #### + + + ## Return a new HyperResource based on this object and a given href. + def _new_from_link(href) # :nodoc: + self.class.new(:root => self.root, + :auth => self.auth, + :headers => self.headers, + :namespace => self.namespace, + :href => href) + end + + ## Returns the class into which the given response should be cast. ## If the object is not loaded yet, or if +opts[:namespace]+ is ## not set, returns +self+. ## - ## Otherwise, +get_response_class+ uses +get_response_data_type+ to + ## Otherwise, +_get_response_class+ uses +_get_response_data_type+ to ## determine subclass name, glues it to the given namespace, and ## creates the class if it's not there yet. E.g., given a namespace of ## +FooAPI+ and a response content-type of ## "application/vnd.foocorp.fooapi.v1+json;type=User", this should ## return +FooAPI::User+ (even if +FooAPI::User+ hadn't existed yet). - def self.get_response_class(response, namespace) + def self._get_response_class(response, namespace) # :nodoc: if self.to_s == 'HyperResource' return self unless namespace end namespace ||= self.to_s - type_name = self.get_response_data_type(response) + type_name = self._get_response_data_type(response) return self unless type_name class_name = "#{namespace}::#{type_name}" class_name.gsub!(/[^_0-9A-Za-z:]/, '') ## sanitize class_name @@ -214,63 +230,47 @@ end Object.module_eval "class #{class_name} < #{namespace}; end" eval(class_name) end - - def get_response_data_type - self.class.get_response_data_type(self.response) + def _get_response_class # :nodoc: + self.namespace ||= self.class.to_s unless self.class.to_s=='HyperResource' + self.class._get_response_class(self.response, self.namespace) end + ## Inspects the given response, and returns a string describing this ## resource's data type. ## ## By default, this method looks for a +type=...+ modifier in the ## response's +Content-type+ and returns that value, capitalized. ## ## Override this method in a subclass to alter HyperResource's behavior. - def self.get_response_data_type(response) + def self._get_response_data_type(response) # :nodoc: return nil unless response return nil unless content_type = response['content-type'] return nil unless m=content_type.match(/;\s* type=(?<type> [0-9A-Za-z:]+)/x) m[:type][0].upcase + m[:type][1..-1] end - - - ## Returns the first object in the first collection of objects embedded - ## in this resource. Equivalent to +self.objects.first+. - def first; self.objects.first end - - ## Returns the *i*th object in the first collection of objects embedded - ## in this resource. Equivalent to +self.objects[i]+. - def [](i); self.objects.ith(i) end - - ## method_missing will load this resource if not yet loaded, then - ## attempt to delegate to +attributes+, then +objects+, then +links+. - def method_missing(method, *args) - self.get unless self.loaded - - method = method.to_s - if method[-1] == '=' - return attributes[method[0..-2]] = args.first if attributes[method[0..-2]] - else - return attributes[method] if attributes && attributes[method] - return objects[method] if objects && objects[method] - return links[method] if links && links[method] - end - - raise NoMethodError, "undefined method `#{method}' for #{self.inspect}" + def _get_response_data_type # :nodoc: + self.class._get_response_data_type(self.response) end +private - def inspect # :nodoc: - "#<#{self.class}:0x#{"%x" % self.object_id} @root=#{self.root.inspect} "+ - "@href=#{self.href.inspect} @loaded=#{self.loaded} "+ - "@namespace=#{self.namespace.inspect} ...>" + ## Return this object, "cast" into its proper response class. + def to_response_class # :nodoc: + response_class = self._get_response_class + return self if self.class == response_class + response_class.new(self) end - ## +response_body+ is deprecated in favor of +response_object+. - def response_body; response_object end + def init_from_resource(resource) + (self.class._hr_attributes - [:attributes, :links, :objects]).each do |attr| + self.send("#{attr}=".to_sym, resource.send(attr)) + end + self.adapter.apply(self.response_object, self) + end end