Permalink
Browse files

first commit

  • Loading branch information...
lukegalea committed Jul 21, 2008
0 parents commit 7310672bcab616c667624f21e623a453a3ab9743
Showing with 1,019 additions and 0 deletions.
  1. +20 −0 MIT-LICENSE
  2. +48 −0 README
  3. +22 −0 Rakefile
  4. +1 −0 init.rb
  5. +1 −0 install.rb
  6. +50 −0 lib/cross_associated_model.rb
  7. +76 −0 lib/http_mock_mod.rb
  8. +374 −0 lib/hyperactive_resource.rb
  9. +394 −0 spec/hyperactive_resource_spec.rb
  10. +17 −0 spec/spec_helper.rb
  11. +15 −0 tasks/rspec.rake
  12. +1 −0 uninstall.rb
@@ -0,0 +1,20 @@
+Copyright (c) 2008 Medical Decision Logic
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48 README
@@ -0,0 +1,48 @@
+HyperactiveResource
+===================
+
+Many have said that ActiveResource is not really "complete". On the surface, this means that some features that are documented aren't implemented. Digging a little deeper, we find that some features that should exist don't.
+
+Arguably, a "complete" ActiveResource would behave like ActiveRecord or, as the rdoc for ActiveResource states "very similarly to Active Record".
+
+Hyperactive Resource is MDL's extension to ActiveResource::Base written to support our Patient Registry and goes a long way towards the goal of an ActiveResource that behaves like ActiveRecord.
+
+Features
+ * Client side validations
+ * Hooks for before_validate, before_save
+ * Dynamic finders: find_by_X
+ * save!
+ * Awareness of associations between resources: belongs_to, has_many, has_one & columns
+ * Patient.new.name returns nil instead of MethodMissing
+ * Patient.new.races returns [] instead of MethodMissing
+ * pat = Patient.new; pat.gender_id = 1; pat.gender #Will return find the gender obj
+ * Resources can be associated with records
+ * Records can be associated with records
+ * ActiveRecord-like attributes= (updates rather than replaces)
+ * ActiveRecord-like #load that doesn't #dup attributes (stores direct reference)
+ * Supports saving resources that :include other resources via:
+ * Nested resource saving (creating a patient will create their associated addresses)
+ * Mapping associations ([:gender].id will serialize as :gender_id)
+
+Example
+=======
+
+ 1. Install the plugin via: (insert svn commands here)
+
+ 2. Create a HyperactiveResource where you would normally use ActiveResource and define the meta-data/associations that drive the dynamic magic:
+
+ class Address < HyperactiveResource
+ self.columns = [ :street_address, :city, :zipcode, :home_phone_number ]
+ self.belong_tos = [ :country, :state ]
+ self.has_manys = [ :people ]
+ end
+
+ 3. Enjoy the magic
+
+ address = Address.new
+ address.country # nil instead of method_missing
+ address.country_id = 5
+ address.country #Returns Country.find(5)
+ etc..
+
+Copyright (c) 2008 Medical Decision Logic, released under the MIT license
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the hyperactive_resource plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the hyperactive_resource plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'HyperactiveResource'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
@@ -0,0 +1 @@
+# Include hook code here
@@ -0,0 +1 @@
+# Install hook code here
@@ -0,0 +1,50 @@
+module CrossAssociatedModel
+ def self.included(mod)
+ mod.extend(CrossAssociatedModelClassMethods)
+ end
+
+ module CrossAssociatedModelClassMethods
+
+ #The base implementation assumes a resource that references a record
+ def belongs_to_record( name )
+ #Assumes this record has a "name_id" and that a "Name" exists that is an active_record
+ instance_var = "@#{name}"
+
+ define_method( name ) do
+ association_id = send("#{name}_id")
+ resource_class = name.to_s.classify.constantize
+
+ if association_id == 0 or association_id.nil?
+ return nil
+ elsif instance_variable_get(instance_var).nil?
+ instance_variable_set( instance_var, resource_class.find(association_id) )
+ end
+ instance_variable_get(instance_var)
+ end
+
+ define_method( "#{name}=" ) do |new_associated_resource|
+ instance_variable_set( instance_var, new_associated_resource )
+ send("#{name}_id=", (new_associated_resource.nil? ? nil : new_associated_resource.id))
+ end
+ end
+
+ #The base implementation assumes a resource that references many record
+ def has_many_records( name )
+ #Assumes this record has a "name_ids" and that a "Name" exists that is an active_record
+ instance_var = "@#{name}"
+
+ define_method( name ) do
+ resource_class = name.to_s.classify.constantize
+ finder_method = "find_all_by_#{self.class.name.tableize.singularize}_id"
+
+ if instance_variable_get(instance_var).nil?
+ instance_variable_set( instance_var, resource_class.send( finder_method, self.id ) )
+ end
+ instance_variable_get(instance_var)
+ end
+ end
+
+ alias_method :belongs_to_resource, :belongs_to_record
+ alias_method :has_many_resources, :has_many_records
+ end
+end
@@ -0,0 +1,76 @@
+require 'active_resource/connection'
+
+module ActiveResource
+ class InvalidRequestError < StandardError; end #:nodoc:
+
+ class HttpMock
+ class ResponderWithLoc < Responder
+ for method in [ :post ]
+ module_eval <<-EOE
+ def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {}, response_body = nil, location = nil)
+ @responses[Request.new(:#{method}, path, nil, request_headers)] = ResponseWithLoc.new(response_body || body || "", status, response_headers, location)
+ end
+ EOE
+ end
+ end
+
+ class << self
+ def respond_to_with_location(pairs = {})
+ reset!
+ pairs.each do |(path, response)|
+ responses[path] = response
+ end
+
+ if block_given?
+ yield ResponderWithLoc.new(responses)
+ else
+ ResponderWithLoc.new(responses)
+ end
+ end
+ end
+
+ for method in [ :post, :put ] # try to remove the authorization info from @request.headers
+ module_eval <<-EOE
+ def #{method}(path, body, headers)
+ request = ActiveResource::Request.new(:#{method}, path, body, headers)
+ request.headers.delete('Authorization')
+ self.class.requests << request
+ self.class.responses[request] || raise(InvalidRequestError.new("No response recorded for: \#{request.inspect}"))
+ end
+ EOE
+ end
+
+ for method in [ :get, :delete ]
+ module_eval <<-EOE
+ def #{method}(path, headers)
+ request = ActiveResource::Request.new(:#{method}, path, nil, headers)
+ request.headers.delete('Authorization')
+ self.class.requests << request
+ self.class.responses[request] || raise(InvalidRequestError.new("No response recorded for: \#{request.inspect}"))
+ end
+ EOE
+ end
+
+ end
+
+ class ResponseWithLoc < Response
+ attr_accessor :location
+
+ def initialize(body, message = 200, headers = {}, location=nil)
+ @body, @message, @headers = body, message.to_s, headers
+ @code = @message[0,3].to_i
+ self['Location'] = location if location
+
+ resp_cls = Net::HTTPResponse::CODE_TO_OBJ[@code.to_s]
+ if resp_cls && !resp_cls.body_permitted?
+ @body = nil
+ end
+
+ if @body.nil?
+ self['Content-Length'] = "0"
+ else
+ self['Content-Length'] = body.size.to_s
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 7310672

Please sign in to comment.