diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG deleted file mode 100644 index 43ab43c6cb5a3..0000000000000 --- a/activeresource/CHANGELOG +++ /dev/null @@ -1,67 +0,0 @@ -*SVN* - -* Add basic logging support for logging outgoing requests. [Jamis Buck] - -* Add Base.delete for deleting resources without having to instantiate them first. [Jamis Buck] - -* Make #save behavior mimic AR::Base#save (true on success, false on failure). [Jamis Buck] - -* Add Basic HTTP Authentication to ActiveResource (closes #6305). [jonathan] - -* Extracted #id_from_response as an entry point for customizing how a created resource gets its own ID. - By default, it extracts from the Location response header. - -* Optimistic locking: raise ActiveResource::ResourceConflict on 409 Conflict response. [Jeremy Kemper] - - # Example controller action - def update - @person.save! - rescue ActiveRecord::StaleObjectError - render :xml => @person.reload.to_xml, :status => '409 Conflict' - end - -* Basic validation support [Rick Olson] - - Parses the xml response of ActiveRecord::Errors#to_xml with a similar interface to ActiveRecord::Errors. - - render :xml => @person.errors.to_xml, :status => '400 Validation Error' - -* Deep hashes are converted into collections of resources. [Jeremy Kemper] - Person.new :name => 'Bob', - :address => { :id => 1, :city => 'Portland' }, - :contacts => [{ :id => 1 }, { :id => 2 }] - Looks for Address and Contact resources and creates them if unavailable. - So clients can fetch a complex resource in a single request if you e.g. - render :xml => @person.to_xml(:include => [:address, :contacts]) - in your controller action. - -* Major updates [Rick Olson] - - * Add full support for find/create/update/destroy - * Add support for specifying prefixes. - * Allow overriding of element_name, collection_name, and primary key - * Provide simpler HTTP mock interface for testing - - # rails routing code - map.resources :posts do |post| - post.resources :comments - end - - # ActiveResources - class Post < ActiveResource::Base - self.site = "http://37s.sunrise.i:3000/" - end - - class Comment < ActiveResource::Base - self.site = "http://37s.sunrise.i:3000/posts/:post_id/" - end - - @post = Post.find 5 - @comments = Comment.find :all, :post_id => @post.id - - @comment = Comment.new({:body => 'hello world'}, {:post_id => @post.id}) - @comment.save - -* Base.site= accepts URIs. 200...400 are valid response codes. PUT and POST request bodies default to ''. [Jeremy Kemper] - -* Initial checkin: object-oriented client for restful HTTP resources which follow the Rails convention. [DHH] diff --git a/activeresource/MIT-LICENSE b/activeresource/MIT-LICENSE deleted file mode 100644 index 7f65dd1989cbb..0000000000000 --- a/activeresource/MIT-LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2006 David Heinemeier Hansson - -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. \ No newline at end of file diff --git a/activeresource/README b/activeresource/README deleted file mode 100644 index f1013b54e0ea7..0000000000000 --- a/activeresource/README +++ /dev/null @@ -1 +0,0 @@ -= Active Resource -- Object-oriented REST services \ No newline at end of file diff --git a/activeresource/Rakefile b/activeresource/Rakefile deleted file mode 100644 index c56c33a56a8be..0000000000000 --- a/activeresource/Rakefile +++ /dev/null @@ -1,138 +0,0 @@ -require 'rubygems' -require 'rake' -require 'rake/testtask' -require 'rake/rdoctask' -require 'rake/packagetask' -require 'rake/gempackagetask' -require 'rake/contrib/rubyforgepublisher' -require File.join(File.dirname(__FILE__), 'lib', 'active_resource', 'version') - -PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : '' -PKG_NAME = 'activeresource' -PKG_VERSION = ActiveResource::VERSION::STRING + PKG_BUILD -PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}" - -RELEASE_NAME = "REL #{PKG_VERSION}" - -RUBY_FORGE_PROJECT = "activerecord" -RUBY_FORGE_USER = "webster132" - -PKG_FILES = FileList[ - "lib/**/*", "test/**/*", "examples/**/*", "doc/**/*", "[A-Z]*", "install.rb", "Rakefile" -].exclude(/\bCVS\b|~$/) - -desc "Default Task" -task :default => [ :test ] - -# Run the unit tests - -Rake::TestTask.new { |t| - t.libs << "test" - t.pattern = 'test/**/*_test.rb' - t.verbose = true - t.warning = true -} - - -# Generate the RDoc documentation - -Rake::RDocTask.new { |rdoc| - rdoc.rdoc_dir = 'doc' - rdoc.title = "Active Resource -- Object-oriented REST services" - rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object' - rdoc.template = "#{ENV['template']}.rb" if ENV['template'] - rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG') - rdoc.rdoc_files.include('lib/**/*.rb') - rdoc.rdoc_files.exclude('lib/active_record/vendor/*') - rdoc.rdoc_files.include('dev-utils/*.rb') -} - - -# Create compressed packages - -dist_dirs = [ "lib", "test", "examples", "dev-utils" ] - -spec = Gem::Specification.new do |s| - s.name = PKG_NAME - s.version = PKG_VERSION - s.summary = "Implements the ActiveRecord pattern for ORM." - s.description = %q{Implements the ActiveRecord pattern (Fowler, PoEAA) for ORM. It ties database tables and classes together for business objects, like Customer or Subscription, that can find, save, and destroy themselves without resorting to manual SQL.} - - s.files = [ "Rakefile", "install.rb", "README", "RUNNING_UNIT_TESTS", "CHANGELOG" ] - dist_dirs.each do |dir| - s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } - end - - s.add_dependency('activesupport', '= 1.3.1' + PKG_BUILD) - - s.files.delete "test/fixtures/fixture_database.sqlite" - s.files.delete "test/fixtures/fixture_database_2.sqlite" - s.files.delete "test/fixtures/fixture_database.sqlite3" - s.files.delete "test/fixtures/fixture_database_2.sqlite3" - s.require_path = 'lib' - s.autorequire = 'active_record' - - s.has_rdoc = true - s.extra_rdoc_files = %w( README ) - s.rdoc_options.concat ['--main', 'README'] - - s.author = "David Heinemeier Hansson" - s.email = "david@loudthinking.com" - s.homepage = "http://www.rubyonrails.org" - s.rubyforge_project = "activerecord" -end - -Rake::GemPackageTask.new(spec) do |p| - p.gem_spec = spec - p.need_tar = true - p.need_zip = true -end - -task :lines do - lines, codelines, total_lines, total_codelines = 0, 0, 0, 0 - - for file_name in FileList["lib/active_record/**/*.rb"] - next if file_name =~ /vendor/ - f = File.open(file_name) - - while line = f.gets - lines += 1 - next if line =~ /^\s*$/ - next if line =~ /^\s*#/ - codelines += 1 - end - puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}" - - total_lines += lines - total_codelines += codelines - - lines, codelines = 0, 0 - end - - puts "Total: Lines #{total_lines}, LOC #{total_codelines}" -end - - -# Publishing ------------------------------------------------------ - -desc "Publish the beta gem" -task :pgem => [:package] do - Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload - `ssh davidhh@wrath.rubyonrails.org './gemupdate.sh'` -end - -desc "Publish the API documentation" -task :pdoc => [:rdoc] do - Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.org", "public_html/ar", "doc").upload -end - -desc "Publish the release files to RubyForge." -task :release => [ :package ] do - `rubyforge login` - - for ext in %w( gem tgz zip ) - release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" - puts release_command - system(release_command) - end -end diff --git a/activeresource/lib/active_resource.rb b/activeresource/lib/active_resource.rb deleted file mode 100644 index 89fd6f7722ed4..0000000000000 --- a/activeresource/lib/active_resource.rb +++ /dev/null @@ -1,45 +0,0 @@ -#-- -# Copyright (c) 2006 David Heinemeier Hansson -# -# 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. -#++ - -$:.unshift(File.dirname(__FILE__)) unless - $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__))) - -unless defined?(ActiveSupport) - begin - $:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib") - require 'active_support' - rescue LoadError - require 'rubygems' - require_gem 'activesupport' - end -end - -require 'active_resource/base' -require 'active_resource/struct' -require 'active_resource/validations' - -module ActiveResource - Base.class_eval do - include Validations - end -end \ No newline at end of file diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb deleted file mode 100644 index 9b5432ed37ef3..0000000000000 --- a/activeresource/lib/active_resource/base.rb +++ /dev/null @@ -1,222 +0,0 @@ -require 'active_resource/connection' - -module ActiveResource - class Base - # The logger for logging diagnostic and trace information during ARes - # calls. - cattr_accessor :logger - - class << self - attr_reader :site - - def site=(site) - @site = site.is_a?(URI) ? site : URI.parse(site) - @connection = nil - @site - end - - def connection(refresh = false) - @connection = Connection.new(site) if refresh || @connection.nil? - @connection - end - - def element_name - self.to_s.underscore - end - - def collection_name - element_name.pluralize - end - - def prefix(options={}) - default = site.path - default << '/' unless default[-1..-1] == '/' - self.prefix = default - prefix(options) - end - - def prefix=(value = '/') - prefix_call = value.gsub(/:\w+/) { |s| "\#{options[#{s}]}" } - method_decl = %(def self.prefix(options={}) "#{prefix_call}" end) - eval method_decl - end - alias_method :set_prefix, :prefix= - - def element_name=(value) - class << self ; attr_reader :element_name ; end - @element_name = value - end - alias_method :set_element_name, :element_name= - - def collection_name=(value) - class << self ; attr_reader :collection_name ; end - @collection_name = value - end - alias_method :set_collection_name, :collection_name= - - def element_path(id, options = {}) - "#{prefix(options)}#{collection_name}/#{id}.xml" - end - - def collection_path(options = {}) - "#{prefix(options)}#{collection_name}.xml" - end - - def primary_key - self.primary_key = 'id' - end - - def primary_key=(value) - class << self ; attr_reader :primary_key ; end - @primary_key = value - end - alias_method :set_primary_key, :primary_key= - - # Person.find(1) # => GET /people/1.xml - # StreetAddress.find(1, :person_id => 1) # => GET /people/1/street_addresses/1.xml - def find(*arguments) - scope = arguments.slice!(0) - options = arguments.slice!(0) || {} - - case scope - when :all then find_every(options) - when :first then find_every(options).first - else find_single(scope, options) - end - end - - def delete(id) - connection.delete(element_path(id)) - end - - private - # { :people => { :person => [ person1, person2 ] } } - def find_every(options) - connection.get(collection_path(options)).values.first.values.first.collect { |element| new(element, options) } - end - - # { :person => person1 } - def find_single(scope, options) - new(connection.get(element_path(scope, options)).values.first, options) - end - end - - attr_accessor :attributes - attr_accessor :prefix_options - - def initialize(attributes = {}, prefix_options = {}) - @attributes = {} - self.load attributes - @prefix_options = prefix_options - end - - def new? - id.nil? - end - - def id - attributes[self.class.primary_key] - end - - def id=(id) - attributes[self.class.primary_key] = id - end - - def save - new? ? create : update - end - - def destroy - connection.delete(self.class.element_path(id, prefix_options)) - end - - def to_xml(options={}) - attributes.to_xml({:root => self.class.element_name}.merge(options)) - end - - # Reloads the attributes of this object from the remote web service. - def reload - self.load self.class.find(id, @prefix_options).attributes - end - - # Manually load attributes from a hash. Recursively loads collections of - # resources. - def load(attributes) - raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash) - attributes.each do |key, value| - @attributes[key.to_s] = - case value - when Array - resource = find_or_create_resource_for_collection(key) - value.map { |attrs| resource.new(attrs) } - when Hash - # Workaround collections loaded as Hash - # :persons => { :person => [ - # { :id => 1, :name => 'a' }, - # { :id => 2, :name => 'b' } ]} - if value.keys.size == 1 and value.values.first.is_a?(Array) - resource = find_or_create_resource_for(value.keys.first) - value.values.first.map { |attrs| resource.new(attrs) } - else - resource = find_or_create_resource_for(key) - resource.new(value) - end - when ActiveResource::Base - value.class.new(value.attributes) - else - value.dup rescue value - end - end - self - end - - protected - def connection(refresh = false) - self.class.connection(refresh) - end - - def update - connection.put(self.class.element_path(id, prefix_options), to_xml) - true - end - - def create - resp = connection.post(self.class.collection_path(prefix_options), to_xml) - self.id = id_from_response(resp) - true - end - - # takes a response from a typical create post and pulls the ID out - def id_from_response(response) - response['Location'][/\/([^\/]*?)(\.\w+)?$/, 1] - end - - private - def find_or_create_resource_for_collection(name) - find_or_create_resource_for(name.to_s.singularize) - end - - def find_or_create_resource_for(name) - resource_name = name.to_s.camelize - resource_name.constantize - rescue NameError - resource = self.class.const_set(resource_name, Class.new(ActiveResource::Base)) - resource.prefix = self.class.prefix - resource.site = self.class.site - resource - end - - def method_missing(method_symbol, *arguments) - method_name = method_symbol.to_s - - case method_name.last - when "=" - attributes[method_name.first(-1)] = arguments.first - when "?" - attributes[method_name.first(-1)] == true - else - attributes.has_key?(method_name) ? attributes[method_name] : super - end - end - end -end diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb deleted file mode 100644 index 1a1b376d3af8c..0000000000000 --- a/activeresource/lib/active_resource/connection.rb +++ /dev/null @@ -1,116 +0,0 @@ -require 'net/https' -require 'date' -require 'time' -require 'uri' -require 'benchmark' - -module ActiveResource - class ConnectionError < StandardError - attr_reader :response - - def initialize(response, message = nil) - @response = response - @message = message - end - - def to_s - "Failed with #{response.code}" - end - end - - class ClientError < ConnectionError; end # 4xx Client Error - class ResourceNotFound < ClientError; end # 404 Not Found - class ResourceConflict < ClientError; end # 409 Conflict - - class ServerError < ConnectionError; end # 5xx Server Error - - - class Connection - attr_reader :site - - class << self - def requests - @@requests ||= [] - end - - def default_header - class << self ; attr_reader :default_header end - @default_header = { 'Content-Type' => 'application/xml' } - end - end - - def initialize(site) - self.site = site.is_a?(URI) ? site : URI.parse(site) - end - - def site=(site) - @site = site.is_a?(URI) ? site : URI.parse(site) - end - - def get(path) - Hash.from_xml(request(:get, path, build_request_headers).body) - end - - def delete(path) - request(:delete, path, build_request_headers) - end - - def put(path, body = '') - request(:put, path, body, build_request_headers) - end - - def post(path, body = '') - request(:post, path, body, build_request_headers) - end - - private - def request(method, path, *arguments) - logger.info "requesting #{method.to_s.upcase} #{site.scheme}://#{site.host}:#{site.port}#{path}" if logger - result = nil - time = Benchmark.realtime { result = http.send(method, path, *arguments) } - logger.info "--> #{result.code} #{result.message} (#{result.body.length}b %.2fs)" % time if logger - handle_response(result) - end - - def handle_response(response) - case response.code.to_i - when 200...400 - response - when 404 - raise(ResourceNotFound.new(response)) - when 400 - raise(ResourceInvalid.new(response)) - when 409 - raise(ResourceConflict.new(response)) - when 401...500 - raise(ClientError.new(response)) - when 500...600 - raise(ServerError.new(response)) - else - raise(ConnectionError.new(response, "Unknown response code: #{response.code}")) - end - end - - def http - unless @http - @http = Net::HTTP.new(@site.host, @site.port) - @http.use_ssl = @site.is_a?(URI::HTTPS) - @http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @http.use_ssl - end - - @http - end - - def build_request_headers - authorization_header.update(self.class.default_header) - end - - def authorization_header - (@site.user || @site.password ? { 'Authorization' => 'Basic ' + ["#{@site.user}:#{ @site.password}"].pack('m').delete("\r\n") } : {}) - end - - def logger - ActiveResource::Base.logger - end - end -end diff --git a/activeresource/lib/active_resource/struct.rb b/activeresource/lib/active_resource/struct.rb deleted file mode 100644 index 6f4ffecc20d55..0000000000000 --- a/activeresource/lib/active_resource/struct.rb +++ /dev/null @@ -1,7 +0,0 @@ -module ActiveResource - class Struct - def self.create - Class.new(Base) - end - end -end \ No newline at end of file diff --git a/activeresource/lib/active_resource/validations.rb b/activeresource/lib/active_resource/validations.rb deleted file mode 100644 index 3011ba3295ddb..0000000000000 --- a/activeresource/lib/active_resource/validations.rb +++ /dev/null @@ -1,126 +0,0 @@ -module ActiveResource - class ResourceInvalid < ClientError - end - - class Errors - include Enumerable - attr_reader :errors - - delegate :empty?, :to => :errors - - def initialize(base) # :nodoc: - @base, @errors = base, {} - end - - def add_to_base(msg) - add(:base, msg) - end - - def add(attribute, msg) - @errors[attribute.to_s] = [] if @errors[attribute.to_s].nil? - @errors[attribute.to_s] << msg - end - - # Returns true if the specified +attribute+ has errors associated with it. - def invalid?(attribute) - !@errors[attribute.to_s].nil? - end - - # * Returns nil, if no errors are associated with the specified +attribute+. - # * Returns the error message, if one error is associated with the specified +attribute+. - # * Returns an array of error messages, if more than one error is associated with the specified +attribute+. - def on(attribute) - errors = @errors[attribute.to_s] - return nil if errors.nil? - errors.size == 1 ? errors.first : errors - end - - alias :[] :on - - # Returns errors assigned to base object through add_to_base according to the normal rules of on(attribute). - def on_base - on(:base) - end - - # Yields each attribute and associated message per error added. - def each - @errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } } - end - - # Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned - # through iteration as "First name can't be empty". - def each_full - full_messages.each { |msg| yield msg } - end - - # Returns all the full error messages in an array. - def full_messages - full_messages = [] - - @errors.each_key do |attr| - @errors[attr].each do |msg| - next if msg.nil? - - if attr == "base" - full_messages << msg - else - full_messages << [attr.humanize, msg].join(' ') - end - end - end - full_messages - end - - def clear - @errors = {} - end - - # Returns the total number of errors added. Two errors added to the same attribute will be counted as such - # with this as well. - def size - @errors.values.inject(0) { |error_count, attribute| error_count + attribute.size } - end - - alias_method :count, :size - alias_method :length, :size - - def from_xml(xml) - clear - humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.humanize => attr_name) } - messages = Hash.from_xml(xml)['errors']['error'] rescue [] - messages.each do |message| - attr_message = humanized_attributes.keys.detect do |attr_name| - if message[0, attr_name.size + 1] == "#{attr_name} " - add humanized_attributes[attr_name], message[(attr_name.size + 1)..-1] - end - end - - add_to_base message if attr_message.nil? - end - end - end - - module Validations - def self.included(base) # :nodoc: - base.class_eval do - alias_method_chain :save, :validation - end - end - - def save_with_validation - save_without_validation - rescue ResourceInvalid => error - errors.from_xml(error.response.body) - return false - end - - def valid? - errors.empty? - end - - # Returns the Errors object that holds all information about attribute error messages. - def errors - @errors ||= Errors.new(self) - end - end -end \ No newline at end of file diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb deleted file mode 100644 index f4e322093240f..0000000000000 --- a/activeresource/lib/active_resource/version.rb +++ /dev/null @@ -1,9 +0,0 @@ -module ActiveResource - module VERSION #:nodoc: - MAJOR = 0 - MINOR = 5 - TINY = 0 - - STRING = [MAJOR, MINOR, TINY].join('.') - end -end diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb deleted file mode 100644 index a20b8a23ab055..0000000000000 --- a/activeresource/test/abstract_unit.rb +++ /dev/null @@ -1,7 +0,0 @@ -$:.unshift(File.dirname(__FILE__) + '/../lib') -require 'active_resource' -require 'test/unit' -require 'active_support/breakpoint' - -$:.unshift(File.dirname(__FILE__) + '/.') -require 'http_mock' diff --git a/activeresource/test/authorization_test.rb b/activeresource/test/authorization_test.rb deleted file mode 100644 index bfe51ce1e3f88..0000000000000 --- a/activeresource/test/authorization_test.rb +++ /dev/null @@ -1,82 +0,0 @@ -require "#{File.dirname(__FILE__)}/abstract_unit" -require 'base64' - -class AuthorizationTest < Test::Unit::TestCase - Response = Struct.new(:code) - - def setup - @conn = ActiveResource::Connection.new('http://localhost') - @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person') - @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person') - @authenticated_conn = ActiveResource::Connection.new("http://david:test123@localhost") - @authorization_request_header = { 'Authorization' => 'Basic ZGF2aWQ6dGVzdDEyMw==' } - - ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/2.xml", @authorization_request_header, @david - mock.put "/people/2.xml", @authorization_request_header, nil, 204 - mock.delete "/people/2.xml", @authorization_request_header, nil, 200 - mock.post "/people/2/addresses.xml", @authorization_request_header, nil, 201, 'Location' => '/people/1/addresses/5' - end - end - - def test_authorization_header - authorization_header = @authenticated_conn.send(:authorization_header) - assert_equal @authorization_request_header['Authorization'], authorization_header['Authorization'] - authorization = authorization_header["Authorization"].to_s.split - - assert_equal "Basic", authorization[0] - assert_equal ["david", "test123"], Base64.decode64(authorization[1]).split(":")[0..1] - end - - def test_authorization_header_with_username_but_no_password - @conn = ActiveResource::Connection.new("http://david:@localhost") - authorization_header = @conn.send(:authorization_header) - authorization = authorization_header["Authorization"].to_s.split - - assert_equal "Basic", authorization[0] - assert_equal ["david"], Base64.decode64(authorization[1]).split(":")[0..1] - end - - def test_authorization_header_with_password_but_no_username - @conn = ActiveResource::Connection.new("http://:test123@localhost") - authorization_header = @conn.send(:authorization_header) - authorization = authorization_header["Authorization"].to_s.split - - assert_equal "Basic", authorization[0] - assert_equal ["", "test123"], Base64.decode64(authorization[1]).split(":")[0..1] - end - - def test_get - david = @authenticated_conn.get("/people/2.xml") - assert_equal "David", david["person"]["name"] - end - - def test_post - response = @authenticated_conn.post("/people/2/addresses.xml") - assert_equal "/people/1/addresses/5", response["Location"] - end - - def test_put - response = @authenticated_conn.put("/people/2.xml") - assert_equal 204, response.code - end - - def test_delete - response = @authenticated_conn.delete("/people/2.xml") - assert_equal 200, response.code - end - - def test_raises_invalid_request_on_unauthorized_requests - assert_raises(ActiveResource::InvalidRequestError) { @conn.post("/people/2.xml") } - assert_raises(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.xml") } - assert_raises(ActiveResource::InvalidRequestError) { @conn.put("/people/2.xml") } - assert_raises(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.xml") } - end - - protected - def assert_response_raises(klass, code) - assert_raise(klass, "Expected response code #{code} to raise #{klass}") do - @conn.send(:handle_response, Response.new(code)) - end - end -end diff --git a/activeresource/test/base/load_test.rb b/activeresource/test/base/load_test.rb deleted file mode 100644 index ca8d924693522..0000000000000 --- a/activeresource/test/base/load_test.rb +++ /dev/null @@ -1,77 +0,0 @@ -require "#{File.dirname(__FILE__)}/../abstract_unit" -require "fixtures/person" -require "fixtures/street_address" - -class BaseLoadTest < Test::Unit::TestCase - def setup - @matz = { :id => 1, :name => 'Matz' } - - @first_address = { :id => 1, :street => '12345 Street' } - @addresses = [@first_address, { :id => 2, :street => '67890 Street' }] - @addresses_from_xml = { :street_addresses => { :street_address => @addresses }} - - @deep = { :id => 1, :street => { - :id => 1, :state => { :id => 1, :name => 'Oregon', - :notable_rivers => { :notable_river => [ - { :id => 1, :name => 'Willamette' }, - { :id => 2, :name => 'Columbia', :rafted_by => @matz }] }}}} - - @person = Person.new - end - - def test_load_expects_hash - assert_raise(ArgumentError) { @person.load nil } - assert_raise(ArgumentError) { @person.load '' } - end - - def test_load_simple_hash - assert_equal Hash.new, @person.attributes - assert_equal @matz.stringify_keys, @person.load(@matz).attributes - end - - def test_load_one_with_existing_resource - address = @person.load(:street_address => @first_address).street_address - assert_kind_of StreetAddress, address - assert_equal @first_address.stringify_keys, address.attributes - end - - def test_load_one_with_unknown_resource - address = silence_warnings { @person.load(:address => @first_address).address } - assert_kind_of Person::Address, address - assert_equal @first_address.stringify_keys, address.attributes - end - - def test_load_collection_with_existing_resource - addresses = @person.load(@addresses_from_xml).street_addresses - assert_kind_of Array, addresses - addresses.each { |address| assert_kind_of StreetAddress, address } - assert_equal @addresses.map(&:stringify_keys), addresses.map(&:attributes) - end - - def test_load_collection_with_unknown_resource - assert !Person.const_defined?(:Address), "Address shouldn't exist until autocreated" - addresses = silence_warnings { @person.load(:addresses => @addresses).addresses } - assert Person.const_defined?(:Address), "Address should have been autocreated" - addresses.each { |address| assert_kind_of Person::Address, address } - assert_equal @addresses.map(&:stringify_keys), addresses.map(&:attributes) - end - - def test_recursively_loaded_collections - person = @person.load(@deep) - assert_equal @deep[:id], person.id - - street = person.street - assert_kind_of Person::Street, street - assert_equal @deep[:street][:id], street.id - - state = street.state - assert_kind_of Person::Street::State, state - assert_equal @deep[:street][:state][:id], state.id - - rivers = state.notable_rivers - assert_kind_of Array, rivers - assert_kind_of Person::Street::State::NotableRiver, rivers.first - assert_equal @deep[:street][:state][:notable_rivers][:notable_river].first[:id], rivers.first.id - assert_equal @matz[:id], rivers.last.rafted_by.id - end -end diff --git a/activeresource/test/base_errors_test.rb b/activeresource/test/base_errors_test.rb deleted file mode 100644 index c6b840ebe1b4a..0000000000000 --- a/activeresource/test/base_errors_test.rb +++ /dev/null @@ -1,35 +0,0 @@ -require "#{File.dirname(__FILE__)}/abstract_unit" -require "fixtures/person" - -class BaseErrorsTest < Test::Unit::TestCase - def setup - ActiveResource::HttpMock.respond_to do |mock| - mock.post "/people.xml", {}, "Age can't be blankName can't be blankName must start with a letterPerson quota full for today.", 400 - end - @person = Person.new(:name => '', :age => '') - assert_equal @person.save, false - end - - def test_should_mark_as_invalid - assert !@person.valid? - end - - def test_should_parse_xml_errors - assert_kind_of ActiveResource::Errors, @person.errors - assert_equal 4, @person.errors.size - end - - def test_should_parse_errors_to_individual_attributes - assert_equal "can't be blank", @person.errors.on(:age) - assert_equal ["can't be blank", "must start with a letter"], @person.errors[:name] - assert_equal "Person quota full for today.", @person.errors.on_base - end - - def test_should_format_full_errors - full = @person.errors.full_messages - assert full.include?("Age can't be blank") - assert full.include?("Name can't be blank") - assert full.include?("Name must start with a letter") - assert full.include?("Person quota full for today.") - end -end \ No newline at end of file diff --git a/activeresource/test/base_test.rb b/activeresource/test/base_test.rb deleted file mode 100644 index 685f9aa994aca..0000000000000 --- a/activeresource/test/base_test.rb +++ /dev/null @@ -1,174 +0,0 @@ -require "#{File.dirname(__FILE__)}/abstract_unit" -require "fixtures/person" -require "fixtures/street_address" - -class BaseTest < Test::Unit::TestCase - def setup - @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person') - @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person') - @addy = { :id => 1, :street => '12345 Street' }.to_xml(:root => 'address') - @default_request_headers = { 'Content-Type' => 'application/xml' } - - ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1.xml", {}, @matz - mock.get "/people/2.xml", {}, @david - mock.put "/people/1.xml", {}, nil, 204 - mock.delete "/people/1.xml", {}, nil, 200 - mock.delete "/people/2.xml", {}, nil, 400 - mock.post "/people.xml", {}, nil, 201, 'Location' => '/people/5.xml' - mock.get "/people/99.xml", {}, nil, 404 - mock.get "/people.xml", {}, "#{@matz}#{@david}" - mock.get "/people/1/addresses.xml", {}, "#{@addy}" - mock.get "/people/1/addresses/1.xml", {}, @addy - mock.put "/people/1/addresses/1.xml", {}, nil, 204 - mock.delete "/people/1/addresses/1.xml", {}, nil, 200 - mock.post "/people/1/addresses.xml", {}, nil, 201, 'Location' => '/people/1/addresses/5' - mock.get "/people//addresses.xml", {}, nil, 404 - mock.get "/people//addresses/1.xml", {}, nil, 404 - mock.put "/people//addresses/1.xml", {}, nil, 404 - mock.delete "/people//addresses/1.xml", {}, nil, 404 - mock.post "/people//addresses.xml", {}, nil, 404 - end - end - - - def test_site_accessor_accepts_uri_or_string_argument - site = URI.parse('http://localhost') - - assert_nothing_raised { Person.site = 'http://localhost' } - assert_equal site, Person.site - - assert_nothing_raised { Person.site = site } - assert_equal site, Person.site - end - - - def test_collection_name - assert_equal "people", Person.collection_name - end - - def test_collection_path - assert_equal '/people.xml', Person.collection_path - end - - def test_custom_element_path - assert_equal '/people/1/addresses/1.xml', StreetAddress.element_path(1, :person_id => 1) - end - - def test_custom_collection_path - assert_equal '/people/1/addresses.xml', StreetAddress.collection_path(:person_id => 1) - end - - def test_custom_element_name - assert_equal 'address', StreetAddress.element_name - end - - def test_custom_collection_name - assert_equal 'addresses', StreetAddress.collection_name - end - - def test_prefix - assert_equal "/", Person.prefix - end - - def test_custom_prefix - assert_equal '/people//', StreetAddress.prefix - assert_equal '/people/1/', StreetAddress.prefix(:person_id => 1) - end - - def test_find_by_id - matz = Person.find(1) - assert_kind_of Person, matz - assert_equal "Matz", matz.name - end - - def test_find_by_id_with_custom_prefix - addy = StreetAddress.find(1, :person_id => 1) - assert_kind_of StreetAddress, addy - assert_equal '12345 Street', addy.street - end - - def test_find_all - all = Person.find(:all) - assert_equal 2, all.size - assert_kind_of Person, all.first - assert_equal "Matz", all.first.name - assert_equal "David", all.last.name - end - - def test_find_first - matz = Person.find(:first) - assert_kind_of Person, matz - assert_equal "Matz", matz.name - end - - def test_find_by_id_not_found - assert_raises(ActiveResource::ResourceNotFound) { Person.find(99) } - assert_raises(ActiveResource::ResourceNotFound) { StreetAddress.find(1) } - end - - def test_create - rick = Person.new - assert_equal true, rick.save - assert_equal '5', rick.id - end - - def test_id_from_response - p = Person.new - resp = {'Location' => '/foo/bar/1'} - assert_equal '1', p.send(:id_from_response, resp) - - resp['Location'] << '.xml' - assert_equal '1', p.send(:id_from_response, resp) - end - - def test_create_with_custom_prefix - matzs_house = StreetAddress.new({}, {:person_id => 1}) - matzs_house.save - assert_equal '5', matzs_house.id - end - - def test_update - matz = Person.find(:first) - matz.name = "David" - assert_kind_of Person, matz - assert_equal "David", matz.name - assert_equal true, matz.save - end - - def test_update_with_custom_prefix - addy = StreetAddress.find(1, :person_id => 1) - addy.street = "54321 Street" - assert_kind_of StreetAddress, addy - assert_equal "54321 Street", addy.street - addy.save - end - - def test_update_conflict - ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/2.xml", {}, @david - mock.put "/people/2.xml", @default_request_headers, nil, 409 - end - assert_raises(ActiveResource::ResourceConflict) { Person.find(2).save } - end - - def test_destroy - assert Person.find(1).destroy - ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1.xml", {}, nil, 404 - end - assert_raises(ActiveResource::ResourceNotFound) { Person.find(1).destroy } - end - - def test_destroy_with_custom_prefix - assert StreetAddress.find(1, :person_id => 1).destroy - ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1/addresses/1.xml", {}, nil, 404 - end - assert_raises(ActiveResource::ResourceNotFound) { StreetAddress.find(1, :person_id => 1).destroy } - end - - def test_delete - assert Person.delete(1) - end -end diff --git a/activeresource/test/connection_test.rb b/activeresource/test/connection_test.rb deleted file mode 100644 index efde099c90455..0000000000000 --- a/activeresource/test/connection_test.rb +++ /dev/null @@ -1,88 +0,0 @@ -require "#{File.dirname(__FILE__)}/abstract_unit" -require 'base64' - -class ConnectionTest < Test::Unit::TestCase - Response = Struct.new(:code) - - def setup - @conn = ActiveResource::Connection.new('http://localhost') - @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person') - @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person') - @default_request_headers = { 'Content-Type' => 'application/xml' } - ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1.xml", {}, @matz - mock.put "/people/1.xml", {}, nil, 204 - mock.delete "/people/1.xml", {}, nil, 200 - mock.post "/people.xml", {}, nil, 201, 'Location' => '/people/5.xml' - end - end - - def test_handle_response - # 2xx and 3xx are valid responses. - [200, 299, 300, 399].each do |code| - expected = Response.new(code) - assert_equal expected, @conn.send(:handle_response, expected) - end - - # 404 is a missing resource. - assert_response_raises ActiveResource::ResourceNotFound, 404 - - # 400 is a validation error - assert_response_raises ActiveResource::ResourceInvalid, 400 - - # 409 is an optimistic locking error - assert_response_raises ActiveResource::ResourceConflict, 409 - - # 4xx are client errors. - [401, 499].each do |code| - assert_response_raises ActiveResource::ClientError, code - end - - # 5xx are server errors. - [500, 599].each do |code| - assert_response_raises ActiveResource::ServerError, code - end - - # Others are unknown. - [199, 600].each do |code| - assert_response_raises ActiveResource::ConnectionError, code - end - end - - def test_site_accessor_accepts_uri_or_string_argument - site = URI.parse("http://localhost") - - assert_nothing_raised { @conn.site = "http://localhost" } - assert_equal site, @conn.site - - assert_nothing_raised { @conn.site = site } - assert_equal site, @conn.site - end - - def test_get - matz = @conn.get("/people/1.xml") - assert_equal "Matz", matz["person"]["name"] - end - - def test_post - response = @conn.post("/people.xml") - assert_equal "/people/5.xml", response["Location"] - end - - def test_put - response = @conn.put("/people/1.xml") - assert_equal 204, response.code - end - - def test_delete - response = @conn.delete("/people/1.xml") - assert_equal 200, response.code - end - - protected - def assert_response_raises(klass, code) - assert_raise(klass, "Expected response code #{code} to raise #{klass}") do - @conn.send(:handle_response, Response.new(code)) - end - end -end diff --git a/activeresource/test/fixtures/person.rb b/activeresource/test/fixtures/person.rb deleted file mode 100644 index 8e5768586a1b1..0000000000000 --- a/activeresource/test/fixtures/person.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Person < ActiveResource::Base - self.site = "http://37s.sunrise.i:3000" -end \ No newline at end of file diff --git a/activeresource/test/fixtures/street_address.rb b/activeresource/test/fixtures/street_address.rb deleted file mode 100644 index 94a86702b02ea..0000000000000 --- a/activeresource/test/fixtures/street_address.rb +++ /dev/null @@ -1,4 +0,0 @@ -class StreetAddress < ActiveResource::Base - self.site = "http://37s.sunrise.i:3000/people/:person_id/" - self.element_name = 'address' -end diff --git a/activeresource/test/http_mock.rb b/activeresource/test/http_mock.rb deleted file mode 100644 index 1166a56af7a1d..0000000000000 --- a/activeresource/test/http_mock.rb +++ /dev/null @@ -1,123 +0,0 @@ -require 'active_resource/connection' - -module ActiveResource - class InvalidRequestError < StandardError; end - - class HttpMock - class Responder - def initialize(responses) - @responses = responses - end - - for method in [ :post, :put, :get, :delete ] - module_eval <<-EOE - def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {}) - @responses[Request.new(:#{method}, path, nil, request_headers)] = Response.new(body || {}, status, response_headers) - end - EOE - end - end - - class << self - def requests - @@requests ||= [] - end - - def responses - @@responses ||= {} - end - - def respond_to(pairs = {}) - reset! - pairs.each do |(path, response)| - responses[path] = response - end - yield Responder.new(responses) if block_given? - end - - def reset! - requests.clear - responses.clear - end - end - - for method in [ :post, :put ] - module_eval <<-EOE - def #{method}(path, body, headers) - request = ActiveResource::Request.new(:#{method}, path, body, headers) - self.class.requests << request - self.class.responses[request] || raise(InvalidRequestError.new("No response recorded for: \#{request}")) - end - EOE - end - - for method in [ :get, :delete ] - module_eval <<-EOE - def #{method}(path, headers) - request = ActiveResource::Request.new(:#{method}, path, nil, headers) - self.class.requests << request - self.class.responses[request] || raise(InvalidRequestError.new("No response recorded for: \#{request}")) - end - EOE - end - - def initialize(site) - @site = site - end - end - - class Request - attr_accessor :path, :method, :body, :headers - - def initialize(method, path, body = nil, headers = {}) - @method, @path, @body, @headers = method, path, body, headers - @headers.update('Content-Type' => 'application/xml') - end - - def ==(other_request) - other_request.hash == hash - end - - def eql?(other_request) - self == other_request - end - - def to_s - "<#{method.to_s.upcase}: #{path} [#{headers}] (#{body})>" - end - - def hash - "#{path}#{method}#{headers}".hash - end - end - - class Response - attr_accessor :body, :code, :headers - - def initialize(body, code = 200, headers = {}) - @body, @code, @headers = body, code, headers - end - - def success? - (200..299).include?(code) - end - - def [](key) - headers[key] - end - - def []=(key, value) - headers[key] = value - end - - end - - class Connection - private - silence_warnings do - def http - @http ||= HttpMock.new(@site) - end - end - end -end