remote_api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
File without changes
data/Manifest.txt ADDED
@@ -0,0 +1,20 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+
5
+ Rakefile
6
+ setup.rb
7
+
8
+ lib/dsl_accessor.rb
9
+ lib/remote_api.rb
10
+
11
+ lib/remote_api/base.rb
12
+ lib/remote_api/xml.rb
13
+ lib/remote_api/xml_response.rb
14
+
15
+ lib/remote_api/version.rb
16
+
17
+ test/test_helper.rb
18
+ test/base_test.rb
19
+ test/xml_test.rb
20
+ test/xml_response_test.rb
data/README.txt ADDED
@@ -0,0 +1,3 @@
1
+ README for remote_api
2
+ =====================
3
+
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+ require 'rake/packagetask'
6
+ require 'rake/gempackagetask'
7
+ require 'rake/rdoctask'
8
+ require 'rake/contrib/rubyforgepublisher'
9
+ require 'fileutils'
10
+ require 'hoe'
11
+ include FileUtils
12
+ require File.join(File.dirname(__FILE__), 'lib', 'remote_api', 'version')
13
+
14
+ AUTHOR = "Alex Wayne"
15
+ EMAIL = "rubyonrails@beautifulpixel.com"
16
+ DESCRIPTION = "Provides a basic framework for easily creating classes that access remote APIs."
17
+ GEM_NAME = "remote_api" # what ppl will type to install your gem
18
+ RUBYFORGE_PROJECT = "remote-api" # The unix name for your project
19
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
20
+ RELEASE_TYPES = %w( gem ) # can use: gem, tar, zip
21
+
22
+
23
+ NAME = "remote_api"
24
+ REV = nil # UNCOMMENT IF REQUIRED: File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
25
+ VERS = ENV['VERSION'] || (RemoteAPI::VERSION::STRING + (REV ? ".#{REV}" : ""))
26
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config']
27
+ RDOC_OPTS = ['--quiet', '--title', "remote_api documentation",
28
+ "--opname", "index.html",
29
+ "--line-numbers",
30
+ "--main", "README",
31
+ "--inline-source"]
32
+
33
+ # Generate all the Rake tasks
34
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
35
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
36
+ p.author = AUTHOR
37
+ p.description = DESCRIPTION
38
+ p.email = EMAIL
39
+ p.summary = DESCRIPTION
40
+ p.url = HOMEPATH
41
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
42
+ p.test_globs = ["test/**/*_test.rb"]
43
+ p.clean_globs = CLEAN #An array of file patterns to delete on clean.
44
+
45
+ # == Optional
46
+ #p.changes - A description of the release's latest changes.
47
+ p.extra_deps = %w( activesupport )
48
+ #p.spec_extras - A hash of extra values to set in the gemspec.
49
+ end
@@ -0,0 +1,50 @@
1
+ class Class
2
+ def dsl_accessor(name, options = {})
3
+ raise TypeError, "DSL Error: options should be a hash. but got `#{options.class}'" unless options.is_a?(Hash)
4
+ writer = options[:writer] || options[:setter]
5
+ writer =
6
+ case writer
7
+ when NilClass then Proc.new{|value| value}
8
+ when Symbol then Proc.new{|value| __send__(writer, value)}
9
+ when Proc then writer
10
+ else raise TypeError, "DSL Error: writer should be a symbol or proc. but got `#{options[:writer].class}'"
11
+ end
12
+ write_inheritable_attribute(:"#{name}_writer", writer)
13
+
14
+ default =
15
+ case options[:default]
16
+ when NilClass then nil
17
+ when [] then Proc.new{[]}
18
+ when {} then Proc.new{{}}
19
+ when Symbol then Proc.new{__send__(options[:default])}
20
+ when Proc then options[:default]
21
+ else Proc.new{options[:default]}
22
+ end
23
+ write_inheritable_attribute(:"#{name}_default", default)
24
+
25
+ self.class.class_eval do
26
+ define_method("#{name}=") do |value|
27
+ writer = read_inheritable_attribute(:"#{name}_writer")
28
+ value = writer.call(value) if writer
29
+ write_inheritable_attribute(:"#{name}", value)
30
+ end
31
+
32
+ define_method(name) do |*values|
33
+ if values.empty?
34
+ # getter method
35
+ key = :"#{name}"
36
+ if !inheritable_attributes.has_key?(key)
37
+ default = read_inheritable_attribute(:"#{name}_default")
38
+ value = default ? default.call(self) : nil
39
+ __send__("#{name}=", value)
40
+ end
41
+ read_inheritable_attribute(key)
42
+ else
43
+ # setter method
44
+ __send__("#{name}=", *values)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
@@ -0,0 +1,120 @@
1
+ class RemoteAPI
2
+ class ResponseFailure < RuntimeError; end
3
+
4
+ dsl_accessor :debug
5
+ dsl_accessor :url
6
+ dsl_accessor :content_type
7
+
8
+ class ResponseFailure < RuntimeError; end
9
+
10
+ # Eeverything happens on instantiation. Pass in a hash, and each key in the hash will
11
+ # become an instance variable for use in the rest of the instance methods.
12
+ #
13
+ # Then we send the request, assert that is the response is successful, and process the
14
+ # response.
15
+ def initialize(params = {})
16
+ params.each do |k, v|
17
+ instance_variable_set "@#{k}", v
18
+ end
19
+
20
+ send_request
21
+ assert_success
22
+ process
23
+ end
24
+
25
+ private
26
+
27
+ # Send the request to the UPS servers
28
+ def send_request(request_body = nil)
29
+ request_body ||= request
30
+ debug_request(request_body)
31
+
32
+ # Setup HTTP objects
33
+ url = URI.parse(server_url)
34
+ http = Net::HTTP.new(url.host, url.port)
35
+
36
+ if url.is_a?(URI::HTTPS)
37
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
38
+ http.use_ssl = true
39
+ end
40
+
41
+ # Setup request options
42
+ options = {}
43
+ options['ContentType'] = self.class.content_type if self.class.content_type
44
+
45
+ # Send Request
46
+ response = http.post(url.path, request_body, options)
47
+ debug_response(response.body, url)
48
+
49
+ # Convert response to proper format
50
+ @response = prepare_response(response)
51
+
52
+ # Code hook to raise execption if error conditions are met
53
+ assert_success
54
+ end
55
+
56
+ def assert_success
57
+ true
58
+ end
59
+
60
+ def process(*args)
61
+ raise 'You must define a "process" method! This should should parse the @response and' +
62
+ 'extract relevant data.'
63
+ end
64
+
65
+ def request(*args)
66
+ raise 'You must define a "request" method! This should create the body of your API request'
67
+ end
68
+
69
+ def prepare_response(response)
70
+ response.body
71
+ end
72
+
73
+ def server_url
74
+ url = self.class.url
75
+
76
+ case url.class.to_s
77
+ when 'String'
78
+ url
79
+ when 'Hash'
80
+ url.symbolize_keys[ENV['RAILS_ENV'].to_sym]
81
+ when 'Proc'
82
+ url.call
83
+ else
84
+ raise "url attribute must be a String, Proc or Hash. Got #{url.class}"
85
+ end
86
+ end
87
+
88
+ def format_response(response)
89
+ response
90
+ end
91
+
92
+ def debug_request(request_body)
93
+ return unless self.class.debug
94
+ puts <<DEBUG
95
+
96
+ -----------
97
+ - REQUEST -
98
+ -----------
99
+
100
+ #{request_body}
101
+ DEBUG
102
+ end
103
+
104
+ def debug_response(response, url)
105
+ return unless self.class.debug
106
+
107
+ parsed_response = format_response(response)
108
+
109
+ puts <<DEBUG
110
+
111
+ ------------"
112
+ - RESPONSE -"
113
+ ------------"
114
+ --- From: #{url}"
115
+
116
+ #{parsed_response}
117
+
118
+ DEBUG
119
+ end
120
+ end
@@ -0,0 +1,9 @@
1
+ class RemoteAPI #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ class RemoteAPI
2
+ class XML < RemoteAPI
3
+
4
+ include REXML
5
+
6
+ content_type 'application/xml'
7
+
8
+ private
9
+
10
+ def prepare_response(response)
11
+ Response.new(response.body)
12
+ end
13
+
14
+ def format_response(response)
15
+ Response.new(response).to_formatted_s
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ class RemoteAPI
2
+ class XML
3
+ class Response
4
+
5
+ def initialize(data)
6
+ @data = REXML::Document.new(data.to_s).root
7
+ end
8
+
9
+ # equivalent to:
10
+ # xml.elements[xpath].text
11
+ def [](xpath)
12
+ if element = @data.elements[xpath]
13
+ element.text
14
+ else
15
+ nil
16
+ end
17
+ end
18
+
19
+ # equivalent to:
20
+ # xml.each_element(xpath) { |node| ... }
21
+ def each(xpath)
22
+ raise ArgumentError, 'You must supply a block to the "each" method' unless block_given?
23
+ @data.each_element(xpath) do |node|
24
+ yield self.class.new(node)
25
+ end
26
+ end
27
+
28
+ def to_formatted_s
29
+ output = ''
30
+ @data.write(output, 0)
31
+ end
32
+ end # Response
33
+ end # XML
34
+ end # RemoteAPI
data/lib/remote_api.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'net/https'
2
+ require 'rexml/document'
3
+ require 'uri'
4
+
5
+ require 'active_support'
6
+
7
+ # Why isn't this a gem?
8
+ require File.join(File.dirname(__FILE__), 'dsl_accessor')
9
+
10
+ # remote_api files
11
+ require 'remote_api/base'
12
+ require 'remote_api/xml'
13
+ require 'remote_api/xml_response'