Permalink
Browse files

switch to activeresource, read working for projects

  • Loading branch information...
1 parent 168bae8 commit 3b4cbd897e3ce482392c8af20bdbc4c518460e50 @jnewland committed Jan 17, 2010
View
@@ -11,9 +11,7 @@ begin
gem.homepage = "http://github.com/jnewland/agile_zen"
gem.authors = ["Jesse Newland"]
gem.add_development_dependency "rspec", "= 1.2.9"
- gem.add_development_dependency "fakeweb"
- gem.add_dependency "hashie", "~> 0.1.8"
- gem.add_dependency "httparty", "~> 0.5.0"
+ gem.add_dependency "activeresource", "~> 2.3.5"
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
View
@@ -1,3 +1,8 @@
-require 'hashie'
-require 'httparty'
-require 'agile_zen/version'
+module AgileZen; end
+
+require 'active_resource'
+require 'agile_zen/format'
+require 'agile_zen/version'
+require 'agile_zen/base'
+require 'agile_zen/story'
+require 'agile_zen/project'
View
@@ -0,0 +1,52 @@
+class AgileZen::Base < ActiveResource::Base
+ class << self
+
+ # Set this to true if you'd like your API traffic sent over SSL
+ def ssl=(ssl = false)
+ self.site = 'https://agilezen.com/api/v1/' if ssl
+ end
+
+ # Turn on connection logging to STDOUT and monkey patch ActiveResource
+ # to include headers when logging
+ def debug=(debug = false)
+ if debug
+ require 'logger'
+ self.logger = Logger.new(STDOUT)
+ require 'agile_zen/debug'
+ end
+ end
+
+ def key=(key)
+ write_inheritable_attribute(:key, key)
+ end
+
+ def key
+ AgileZen::Base.read_inheritable_attribute(:key)
+ end
+
+ def headers
+ { 'X-Zen-ApiKey' => AgileZen::Base.key }
+ end
+
+ def all(*args)
+ find(:all, *args)
+ end
+
+ # override to remove the period since AgileZen's API doesn't accept
+ # extensions to specify content type
+ def element_path(id, prefix_options = {}, query_options = nil)
+ prefix_options, query_options = split_options(prefix_options) if query_options.nil?
+ "#{prefix(prefix_options)}#{collection_name.singularize}/#{id}#{format.extension}#{query_string(query_options)}"
+ end
+
+ # override to remove the period since AgileZen's API doesn't accept
+ # extensions to specify content type
+ def collection_path(prefix_options = {}, query_options = nil)
+ prefix_options, query_options = split_options(prefix_options) if query_options.nil?
+ "#{prefix(prefix_options)}#{collection_name}#{format.extension}#{query_string(query_options)}"
+ end
+ end
+end
+
+AgileZen::Base.format = ActiveResource::Formats::AgileZenXmlFormat
+AgileZen::Base.site = 'http://agilezen.com/api/v1/'
View
@@ -0,0 +1,20 @@
+module ActiveResource
+ class Connection
+ private
+ # Override request to include headers if debug mode is turned on
+ def request(method, path, *arguments)
+ logger.info "#{method.to_s.upcase} #{site.scheme}://#{site.host}:#{site.port}#{path}" if logger
+ logger.info arguments.inspect if logger
+ result = nil
+ ms = Benchmark.ms { result = http.send(method, path, *arguments) }
+ logger.info "--> %d %s (%d %.0fms)" % [result.code, result.message, result.body ? result.body.length : 0, ms] if logger
+ handle_response(result)
+ rescue Timeout::Error => e
+ raise TimeoutError.new(e.message)
+ rescue OpenSSL::SSL::SSLError => e
+ raise SSLError.new(e.message)
+ end
+ end
+end
+
+ActiveResource::Formats::AgileZenXmlFormat.logger = AgileZen::Base.logger
View
@@ -0,0 +1,51 @@
+module ActiveResource
+ module Formats
+ # Modify ActiveResource::Formats::XmlFormat for AgileZen as follows:
+ #
+ # * Remove the extension from all requests
+ # * Handle AgileZen's paginated XML format
+ module AgileZenXmlFormat
+ extend self
+
+ def extension
+ ""
+ end
+
+ def mime_type
+ "application/xml"
+ end
+
+ def encode(hash, options={})
+ hash.to_xml(options)
+ end
+
+ def decode(xml)
+ hash = Hash.from_xml(xml)
+ @logger.info hash.inspect if @logger
+ data = from_xml_data(hash)
+ @logger.info data.inspect if @logger
+ data
+ end
+
+ def logger=(logger)
+ @logger = logger
+ end
+
+ private
+ # Manipulate from_xml Hash, because xml_simple is not exactly what we
+ # want for Active Resource.
+ def from_xml_data(data)
+ if data.is_a?(Hash) && data.keys.size == 1
+ if data.values.first['items']
+ data = data.values.first['items'].values.first
+ data.is_a?(Hash) ? [data] : data
+ else
+ data.values.first
+ end
+ else
+ data
+ end
+ end
+ end
+ end
+end
View
@@ -0,0 +1,2 @@
+class AgileZen::Project < AgileZen::Base
+end
View
@@ -0,0 +1,2 @@
+class AgileZen::Story < AgileZen::Base
+end
@@ -0,0 +1,26 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe 'projects' do
+
+ it "should be accessible easily" do
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/api/v1/projects", AgileZen::Base.headers, fixture('projects.xml')
+ end
+ projects = AgileZen::Project.all
+ projects.should be_instance_of(Array)
+ projects.length.should == 2
+ projects.first.should be_instance_of(AgileZen::Project)
+ projects.first.name.should == 'World Peace'
+ end
+
+ it "should work if there's only one in the collection" do
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/api/v1/projects", AgileZen::Base.headers, fixture('projects_with_only_one.xml')
+ end
+ projects = AgileZen::Project.all
+ projects.should be_instance_of(Array)
+ projects.length.should == 1
+ projects.first.should be_instance_of(AgileZen::Project)
+ projects.first.name.should == 'World Peace'
+ end
+end
View
@@ -4,4 +4,22 @@
it "should provide a version constant" do
AgileZen::VERSION.should be_instance_of(String)
end
+
+ it "should accept an API key" do
+ AgileZen::Base.key = 'key'
+ AgileZen::Base.headers['X-Zen-ApiKey'].should == 'key'
+ end
+
+ it "should be configured to not send an extension" do
+ AgileZen::Base.format.extension.should == ''
+ end
+
+ it "should default to non-ssl" do
+ AgileZen::Base.site.to_s.should == 'http://agilezen.com/api/v1/'
+ end
+
+ it "should allow ssl access" do
+ AgileZen::Base.ssl = true
+ AgileZen::Base.site.to_s.should == 'https://agilezen.com/api/v1/'
+ end
end
View
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<projects>
+ <page>1</page>
+ <pageSize>10</pageSize>
+ <totalPages>1</totalPages>
+ <totalItems>2</totalItems>
+ <items>
+ <project>
+ <id>123</id>
+ <name>World Peace</name>
+ <description>Working towards world peace</description>
+ <owner>
+ <id>1</id>
+ <name>John Doe</name>
+ </owner>
+ </project>
+ <project>
+ <id>124</id>
+ <name>World Domination</name>
+ <description>Secret plan to conquer the world</description>
+ <owner>
+ <id>1</id>
+ <name>John Doe</name>
+ </owner>
+ </project>
+ </items>
+</projects>
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<projects>
+ <page>1</page>
+ <pageSize>10</pageSize>
+ <totalPages>1</totalPages>
+ <totalItems>1</totalItems>
+ <items>
+ <project>
+ <id>123</id>
+ <name>World Peace</name>
+ <description>Working towards world peace</description>
+ <owner>
+ <id>1</id>
+ <name>John Doe</name>
+ </owner>
+ </project>
+ </items>
+</projects>
View
@@ -1,19 +1,8 @@
require 'agile_zen'
require 'spec'
require 'spec/autorun'
-require 'fakeweb'
+require 'active_resource/http_mock'
-FakeWeb.allow_net_connect = false
-
-def stub_http_response_with(filename)
- format = filename.split('.').last.intern
- data = file_fixture(filename)
-
- response = Net::HTTPOK.new("1.1", 200, "Content for you")
- response.stub!(:body).and_return(data)
-
- http_request = HTTParty::Request.new(Net::HTTP::Get, 'http://localhost', :format => format)
- http_request.stub!(:perform_actual_request).and_return(response)
-
- HTTParty::Request.should_receive(:new).and_return(http_request)
+def fixture(filename)
+ open(File.join(File.dirname(__FILE__), 'fixtures', "#{filename.to_s}")).read
end

0 comments on commit 3b4cbd8

Please sign in to comment.