Permalink
Browse files

Initial check-in of Active Resourse

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4492 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent a552651 commit 99d268c8534ad398c6c60a4978ef94699cbb8ada @dhh dhh committed Jun 25, 2006
View
@@ -0,0 +1 @@
+= Active Resource -- Object-oriented REST services
View
@@ -0,0 +1,137 @@
+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
+}
+
+
+# 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
@@ -0,0 +1,38 @@
+#--
+# 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'
@@ -0,0 +1,99 @@
+require 'active_resource/connection'
+
+module ActiveResource
+ class Base
+ class << self
+ def site=(site)
+ @@site = URI.parse(site)
+ end
+
+ def site
+ @@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 element_path(id)
+ "/#{collection_name}/#{id}.xml"
+ end
+
+ def collection_path
+ "/#{collection_name}.xml"
+ end
+
+ def find(*arguments)
+ scope = arguments.slice!(0)
+
+ case scope
+ when Fixnum
+ # { :person => person1 }
+ new(connection.get(element_path(scope)).values.first)
+ when :all
+ # { :people => { :person => [ person1, person2 ] } }
+ connection.get(collection_path).values.first.values.first.collect { |element| new(element) }
+ when :first
+ find(:all, *arguments).first
+ end
+ end
+ end
+
+ attr_accessor :attributes
+
+ def initialize(attributes = {})
+ @attributes = attributes
+ end
+
+ def id
+ attributes["id"]
+ end
+
+ def id=(id)
+ attributes["id"] = id
+ end
+
+ def save
+ update
+ end
+
+ def destroy
+ connection.delete(self.class.element_path(id))
+ end
+
+ def to_xml
+ attributes.to_xml(:root => self.class.element_name)
+ end
+
+ protected
+ def connection(refresh = false)
+ self.class.connection(refresh)
+ end
+
+ def update
+ connection.put(self.class.element_path(id), to_xml)
+ 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 "?"
+ # TODO
+ else
+ attributes[method_name] || super
+ end
+ end
+ end
+end
@@ -0,0 +1,86 @@
+require 'net/https'
+require 'date'
+require 'time'
+require 'uri'
+
+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
+
+ class ServerError < ConnectionError
+ end
+
+ class ResourceNotFound < ClientError
+ end
+
+ class Connection
+ attr_accessor :uri
+
+ class << self
+ def requests
+ @@requests ||= []
+ end
+ end
+
+ def initialize(site)
+ @site = site
+ end
+
+ def get(path)
+ Hash.create_from_xml(request(:get, path).body)
+ end
+
+ def delete(path)
+ request(:delete, path)
+ end
+
+ def put(path, body)
+ request(:put, path, body)
+ end
+
+ def post(path, body)
+ request(:post, path, body)
+ end
+
+ private
+ def request(method, *arguments)
+ response = http.send(method, *arguments)
+
+ case response.code.to_i
+ when 200...300
+ response
+ when 404
+ raise(ResourceNotFound.new(response))
+ when 400...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
+ end
+end
@@ -0,0 +1,7 @@
+module ActiveResource
+ class Struct
+ def self.create
+ Class.new(Base)
+ end
+ end
+end
@@ -0,0 +1,9 @@
+module ActiveResource
+ module VERSION #:nodoc:
+ MAJOR = 0
+ MINOR = 5
+ TINY = 0
+
+ STRING = [MAJOR, MINOR, TINY].join('.')
+ end
+end
@@ -0,0 +1,8 @@
+$:.unshift(File.dirname(__FILE__) + '/../lib')
+$:.unshift(File.dirname(__FILE__) + '/.')
+
+require 'active_resource'
+require 'test/unit'
+require 'active_support/breakpoint'
+
+require "#{File.dirname(__FILE__)}/http_mock"
Oops, something went wrong.

0 comments on commit 99d268c

Please sign in to comment.