resto 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/resto.rb CHANGED
@@ -180,7 +180,7 @@ module Resto
180
180
  end
181
181
 
182
182
  def request
183
- Extra::Copy.request_base(@request)
183
+ Copy.request_base(@request)
184
184
  end
185
185
 
186
186
  def response(response)
@@ -188,7 +188,7 @@ module Resto
188
188
  end
189
189
 
190
190
  def base_response
191
- Extra::Copy.response_base(@response)
191
+ Copy.response_base(@response)
192
192
  end
193
193
 
194
194
  def property_handler
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ module Resto
4
+
5
+ # @note This class is only used internally.
6
+ #
7
+ # AssertHash validates that a Hash only contain valid keys. The purpose
8
+ # is to assert that methods are used with correct arguments.
9
+ class AssertHash
10
+
11
+ # Asserts that keys in the Hash is valid. It also converts
12
+ # String keys to Symbol keys.
13
+ #
14
+ # === Examples:
15
+ # hash = { :valid => 'I am valid' }
16
+ # AssertHash.keys(hash, :valid)
17
+ # # => { :valid => 'I am valid'}
18
+ #
19
+ # hash = { 'valid' => 'I am valid' }
20
+ # AssertHash.keys(hash, :valid)
21
+ # # => { :valid => 'I am valid'}
22
+ #
23
+ # hash = { :invalid => 'I am invalid' }
24
+ # AssertHash.keys(hash, :valid)
25
+ # # => raises ArgumentError
26
+ #
27
+ # @param hash [Hash]
28
+ # @param *valid_keys [Symbol, Symbol, ...]
29
+ #
30
+ # @return [Hash] string keys are converted to their corresponding Symbols.
31
+ #
32
+ # @raise [ArgumentError] if the Hash contains unknown key(s).
33
+ def self.keys(hash, *valid_keys)
34
+ hash ||= {}
35
+
36
+ hash.each { |key, value| hash[key.to_sym] = hash.delete(key) }
37
+
38
+ known_keys = [valid_keys].flatten
39
+ unknown_keys = hash.keys - known_keys
40
+ unless unknown_keys.empty?
41
+ unknown = "Invalid key(s): #{unknown_keys.join(", ")}"
42
+ known = "Valid key(s): #{known_keys.join(", ")}"
43
+ raise(ArgumentError, "#{unknown} \n #{known}")
44
+ end
45
+
46
+ hash
47
+ end
48
+ end
49
+ end
@@ -4,30 +4,55 @@ require 'resto/request/base'
4
4
  require 'resto/response/base'
5
5
 
6
6
  module Resto
7
- module Extra
8
- module Copy
9
7
 
10
- def self.request_base(request_base)
11
- Resto::Request::Base.new.tap do |copy|
12
- copy_instance_variables(request_base, copy, ["@request"])
8
+ # @note This module is used internally by these classes/modules:
9
+ # Resto::ClassMethods (see method {Resto::ClassMethods#request}, and
10
+ # {Resto::ClassMethods#base_response}). Resto::Request::Base
11
+ # (see method {Resto::Request::Base#construct_path}).
12
+ #
13
+ # This module has two helper methods that handles the process of copying the
14
+ # {Resto::Request::Base} object and {Resto::Response::Base} object correctly.
15
+ module Copy
13
16
 
14
- request_klass = request_base.instance_variable_get("@request_klass")
15
- copy.instance_variable_set("@request", request_klass.new(copy))
16
- end
17
+ # Returns a copy of the {Resto::Request::Base} object.
18
+ #
19
+ # === Examples:
20
+ # Copy.request_base(request)
21
+ # # => new_request
22
+ #
23
+ # @param request_base [Request::Base]
24
+ #
25
+ # @return [Request::Base]
26
+ def self.request_base(request_base)
27
+ Resto::Request::Base.new.tap do |copy|
28
+ copy_instance_variables(request_base, copy, ["@request"])
29
+
30
+ request_klass = request_base.instance_variable_get("@request_klass")
31
+ copy.instance_variable_set("@request", request_klass.new(copy))
17
32
  end
33
+ end
18
34
 
19
- def self.response_base(response_base)
20
- Resto::Response::Base.new.tap do |copy|
21
- copy_instance_variables(response_base, copy, ["@response"])
22
- end
35
+
36
+ # Returns a copy of the {Resto::Response::Base} object.
37
+ #
38
+ # === Examples:
39
+ # Copy.response_base(response)
40
+ # # => new_response
41
+ #
42
+ # @param response_base [Response::Base]
43
+ #
44
+ # @return [Response::Base]
45
+ def self.response_base(response_base)
46
+ Resto::Response::Base.new.tap do |copy|
47
+ copy_instance_variables(response_base, copy, ["@response"])
23
48
  end
49
+ end
24
50
 
25
- def self.copy_instance_variables(from, to, exclude = [])
26
- (from.instance_variables.map(&:to_s) - exclude).each do |name|
27
- instance_variable = from.instance_variable_get(name)
51
+ def self.copy_instance_variables(from, to, exclude = [])
52
+ (from.instance_variables.map(&:to_s) - exclude).each do |name|
53
+ instance_variable = from.instance_variable_get(name)
28
54
 
29
- to.instance_variable_set(name, instance_variable)
30
- end
55
+ to.instance_variable_set(name, instance_variable)
31
56
  end
32
57
  end
33
58
  end
data/lib/resto/format.rb CHANGED
@@ -6,15 +6,80 @@ require 'resto/format/json'
6
6
  require 'resto/format/xml'
7
7
 
8
8
  module Resto
9
+
10
+ # @note This module is only used internally by these classes/modules:
11
+ # Resto::Request::Base (see method {Resto::Request::Header#format}).
12
+ # Resto::Response::Base (see method {Resto::Response::Base#format}).
13
+ #
14
+ #== Add a Format
15
+ #
16
+ # If a user of Resto wants to handle a format different from whats
17
+ # available, she can create a class in the Format module. Then
18
+ # include the Format module into the meta class and override the methods
19
+ # that she wants to change. See example below.
20
+ #
21
+ # === Example:
22
+ # module Resto
23
+ # module Format
24
+ # class Foo; end
25
+ #
26
+ # class << Foo
27
+ # include Format
28
+ #
29
+ # def extension; 'foo' end
30
+ # def accept; 'foo'; end
31
+ # end
32
+ # end
33
+ # end
34
+ #
35
+ #
9
36
  module Format
37
+
38
+ # Returns the class that corresponds to the symbol argument.
39
+ #
40
+ # === Examples:
41
+ # Resto::Format.get(:json)
42
+ # # => Resto::Format::Json
43
+ #
44
+ # Resto::Format.get(:xml)
45
+ # # => Resto::Format:Xml
46
+ #
47
+ # @param symbol [Symbol]
48
+ # @return [Format, #accept, #content_type, #decode, #encode, #extension]
49
+ #
50
+ # @raise [NameError] if the class doesn't exist.
10
51
  def self.get(symbol=:default)
11
- format = Resto::Format.const_get("#{symbol.to_s.capitalize}")
52
+ Resto::Format.const_get("#{symbol.to_s.capitalize}")
12
53
  end
13
54
 
14
- def extension; end
15
- def accept; '*/*'; end
16
- def content_type; end
17
- def encode(*args); args.first end
18
- def decode(*args); args.first end
55
+ # The accept header. This method is overriden by the classes
56
+ # that includes this module if they want a different accept header.
57
+ #
58
+ # @return [String] "*/*"
59
+ def accept; '*/*'; end
60
+
61
+ # The content-type header. This method is overriden by the classes
62
+ # that includes this module if they want a different content-type header.
63
+ #
64
+ # @return [nil]
65
+ def content_type; end
66
+
67
+ # Returns the arguments as an Array. This method is overriden by classes
68
+ # that includes this module if they want a different behavior.
69
+ #
70
+ # @return [Array<Object>] the arguments as an Array.
71
+ def decode(*args); args end
72
+
73
+ # Returns the first argument. This method is overriden by classes that
74
+ # includes this module if they want a different behavior.
75
+ #
76
+ # @return [Object] the first argument.
77
+ def encode(*args); args.first end
78
+
79
+ # The extension. This method is overriden by the classes
80
+ # that includes this module if they want to have an extension.
81
+ #
82
+ # @return [nil]
83
+ def extension; end
19
84
  end
20
85
  end
@@ -2,6 +2,12 @@
2
2
 
3
3
  module Resto
4
4
  module Format
5
+
6
+ # @note When no format is selected this class is used by the
7
+ # {Request::Base} and {Response::Base} objects.
8
+ #
9
+ # This class includes the methods from the {Resto::Format} module without
10
+ # any change.
5
11
  class Default; end
6
12
 
7
13
  class << Default
@@ -1,30 +1,74 @@
1
1
  # encoding: utf-8
2
2
 
3
- # http://en.wikipedia.org/wiki/JSON
4
3
  require 'yajl'
5
4
 
6
5
  module Resto
7
6
  module Format
7
+ # @note This class is only used internally by these classes/modules:
8
+ # {Resto::Request::Base} (see method {Resto::Request::Header#formatter})
9
+ # uses this class to create a valid JSON request. {Resto::Response::Base}
10
+ # (see method {Resto::Response::Base#read_body})
11
+ # uses this class to convert a JSON formatted String to a Hash.
8
12
  class Json; end
9
13
 
10
14
  class << Json
11
15
  include Format
16
+ # The accept header when sending JSON data.
17
+ #
18
+ # === Example:
19
+ # headers["accept"] = Resto::Format::Json.accept
20
+ #
21
+ # @return [String] "application/json, */*"
22
+ def accept; 'application/json, */*'; end
12
23
 
13
- def extension; 'json'; end
14
- def accept; 'application/json, */*'; end
15
- def content_type; 'application/json'; end
24
+ # The content-type header when sending JSON data.
25
+ #
26
+ # === Example:
27
+ # headers["content-type"] = Resto::Format::Json.content_type
28
+ #
29
+ # @return [String] "application/json"
30
+ def content_type; 'application/json'; end
16
31
 
32
+ # Converts a JSON formatted String to an Array of a Hashes.
33
+ #
34
+ # === Example:
35
+ # Json.decode("{\"root\":{\"body\":\"I am a body\"}}")
36
+ # # => [{ 'root': { 'body': 'I am a body' } }]
37
+ #
38
+ # @param json [String]
39
+ # @param options is not used.
40
+ #
41
+ # @return [Array<Hash>]
42
+ def decode(json, options=nil)
43
+ raise ArgumentError unless json.is_a?(String)
44
+
45
+ result = Yajl::Parser.parse(json)
46
+ result.is_a?(Array) ? result : [result].compact
47
+ end
48
+
49
+ # Converts a Hash to a JSON formatted String.
50
+ #
51
+ # @param hash [Hash]
52
+ # @param options is not used.
53
+ #
54
+ # === Example:
55
+ # Json.encode({ root: { body: 'I am a body' } })
56
+ # # => "{\"root\":{\"body\":\"I am a body\"}}"
57
+ #
58
+ # @return [String]
17
59
  def encode(hash, options = nil)
18
60
  raise ArgumentError unless hash.is_a?(Hash)
19
61
 
20
62
  Yajl::Encoder.encode(hash)
21
63
  end
22
64
 
23
- def decode(json, options=nil)
24
- raise ArgumentError unless json.is_a?(String)
25
-
26
- Yajl::Parser.parse(json)
27
- end
65
+ # The extension name used (if required) as the URL suffix.
66
+ #
67
+ # === Example:
68
+ # http://myhost.com:8085/bamboo/rest/api/latest/plan.json
69
+ #
70
+ # @return [String] "json"
71
+ def extension; 'json'; end
28
72
  end
29
73
  end
30
74
  end
@@ -2,12 +2,28 @@
2
2
 
3
3
  module Resto
4
4
  module Format
5
+
6
+ # This class is used when sending plain text requests.
5
7
  class Plain; end
6
8
 
7
9
  class << Plain
8
10
  include Format
9
11
 
12
+ # The accept header when sending text data.
13
+ #
14
+ # === Example:
15
+ # headers["accept"] = Resto::Format::Plain.accept
16
+ #
17
+ # @return [String] "text/plain, */*"
10
18
  def accept; 'text/plain, */*'; end
19
+
20
+
21
+ # The content-type header when sending text data.
22
+ #
23
+ # === Example:
24
+ # headers["content-type"] = Resto::Format::Plain.content_type
25
+ #
26
+ # @return [String] "text/plain"
11
27
  def content_type; 'text/plain'; end
12
28
  end
13
29
  end
@@ -3,38 +3,87 @@
3
3
  # https://tools.ietf.org/html/rfc3023
4
4
  require 'resto/format'
5
5
  require 'nokogiri'
6
+ require 'resto/extra/assert_hash.rb'
6
7
 
7
8
  module Resto
8
9
  module Format
10
+
11
+ # Used when sending and receiveing XML data.
9
12
  class Xml; end
10
13
 
11
14
  class << Xml
12
15
  include Format
13
16
 
14
- def extension; 'xml'; end
15
- def accept; 'application/xml, */*'; end
16
- def content_type; 'application/xml;charset=utf-8'; end
17
+ # The accept header when sending XML data.
18
+ #
19
+ # === Example:
20
+ # headers["accept"] = Resto::Format::Xml.accept
21
+ #
22
+ # @return [String] "application/xml, */*"
23
+ def accept; 'application/xml, */*'; end
17
24
 
18
- def encode(hash, options = nil)
19
- Nokogiri::XML::Builder.new { |xml| to_xml(hash, xml) }.to_xml
20
- end
25
+ # The content-type header when sending XML data.
26
+ #
27
+ # === Example:
28
+ # headers["content-type"] = Resto::Format::Xml.content_type
29
+ #
30
+ # @return [String] "application/xml;charset=utf-8"
31
+ def content_type; 'application/xml;charset=utf-8'; end
21
32
 
22
- def decode(xml, options)
23
- xpath = options.fetch(:xpath)
33
+ # Converts an XML formatted String to an Array of Hashes.
34
+ #
35
+ # === Example:
36
+ # xml = '<?xml version="1.0"?>
37
+ # <user>
38
+ # <name>Anders Törnqvist</name>
39
+ # </user>'
40
+ # Xml.decode(xml, :xpath => 'user')
41
+ # # => [{ 'user': { 'name': 'Anders Törnqvist' } }]
42
+ #
43
+ # @param xml [String]
44
+ # @param [Hash] opts the options used when decoding the xml.
45
+ # @option opts [String] :xpath the xpath to where the elements are found.
46
+ #
47
+ # @return [Array<Hash>]
48
+ #
49
+ def decode(xml, opts)
50
+ xpath = AssertHash.keys(opts, :xpath).fetch(:xpath)
24
51
 
25
52
  doc = Nokogiri::XML(xml)
26
53
  nodes = doc.xpath(xpath)
27
54
 
28
55
  case nodes.size
29
56
  when 0
30
- {}
57
+ [{}]
31
58
  when 1
32
- elements_to_hash(nodes.first.children)
59
+ [elements_to_hash(nodes.first.children)]
33
60
  else
34
61
  nodes.map { |node| elements_to_hash(node.children) }
35
62
  end
36
63
  end
37
64
 
65
+ # Converts a Hash to a XML formatted String.
66
+ #
67
+ # @param hash [Hash]
68
+ # @param options is not used.
69
+ #
70
+ # === Example:
71
+ # Xml.encode({ root: { body: 'I am a body' } })
72
+ # # => "<?xml version=\"1.0\"?><root><body>I am a body</body></root>"
73
+ #
74
+ # @return [String]
75
+ def encode(hash, options = nil)
76
+ Nokogiri::XML::Builder.new { |xml| to_xml(hash, xml) }.to_xml
77
+ end
78
+
79
+ # The extension name used (if required) as the URL suffix.
80
+ #
81
+ # === Example:
82
+ # http://myhost.com:8085/bamboo/rest/api/latest/plan.xml
83
+ #
84
+ # @return [String] "xml"
85
+ def extension; 'xml'; end
86
+
38
87
  private
39
88
 
40
89
  def elements_to_hash(children)
@@ -1,23 +1,23 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'resto/extra/delegation'
3
+ require 'forwardable'
4
+ require 'resto/extra/copy'
4
5
  require 'resto/request/uri'
5
6
  require 'resto/request/header'
6
7
  require 'resto/request/option'
7
8
  require 'resto/request/factory'
8
9
  require 'resto/translator/request_factory'
9
- require 'resto/extra/copy'
10
10
 
11
11
  module Resto
12
12
  module Request
13
13
  class Base
14
- extend Resto::Extra::Delegation
14
+ extend Forwardable
15
15
  include Resto::Request::Header
16
16
  include Resto::Request::Uri
17
17
  include Resto::Request::Option
18
18
 
19
- delegate :head, :head!, :get, :get!, :post, :post!,
20
- :put, :put!, :delete, :delete!, :to => :@request
19
+ def_delegators :@request, :head, :head!, :get, :get!, :post, :post!,
20
+ :put, :put!, :delete, :delete!
21
21
 
22
22
  def initialize(request=Resto::Request::Factory)
23
23
  @request_klass = request
@@ -31,7 +31,7 @@ module Resto
31
31
  new_path.gsub!(/:#{substitute}/, options[substitute].to_s)
32
32
  end
33
33
 
34
- Extra::Copy.request_base(self).path(new_path)
34
+ Copy.request_base(self).path(new_path)
35
35
  end
36
36
 
37
37
  def url(url)
@@ -62,6 +62,11 @@ module Resto
62
62
  end
63
63
 
64
64
  def params(hash)
65
+ @params ||= {}
66
+ tap { @params.update(hash) }
67
+ end
68
+
69
+ def params!(hash)
65
70
  tap { @params = hash }
66
71
  end
67
72
 
@@ -1,16 +1,16 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'net/https'
4
- require 'resto/extra/delegation'
4
+ require 'forwardable'
5
5
 
6
6
  module Resto
7
7
  module Request
8
8
  class Factory
9
- extend Resto::Extra::Delegation
9
+ extend Forwardable
10
10
 
11
- delegate :read_host, :read_port, :options, :read_body, :composed_path,
12
- :composed_headers, :scheme, :use_ssl, :current_formatter,
13
- :to => :@request
11
+ def_delegators :@request, :read_host, :read_port, :options, :read_body,
12
+ :composed_path, :composed_headers, :scheme, :use_ssl,
13
+ :current_formatter
14
14
 
15
15
  def initialize(request)
16
16
  @request = request
@@ -1,15 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require 'resto/format'
3
-
4
- require 'resto/extra/hash_args'
5
- class BasicAuth < Resto::Extra::HashArgs
6
- key :username
7
- key :password
8
- end
9
-
10
- class FormatExtension < Resto::Extra::HashArgs
11
- key :extension
12
- end
3
+ require 'resto/extra/assert_hash'
13
4
 
14
5
  module Resto
15
6
  module Request
@@ -20,8 +11,8 @@ module Resto
20
11
  end
21
12
 
22
13
  def formatter(formatter, options=nil)
23
- @add_extension =
24
- FormatExtension.new(options).fetch(:extension) { false }
14
+ options = AssertHash.keys(options, :extension)
15
+ @add_extension = options.fetch(:extension) { false }
25
16
  @formatter = formatter
26
17
  accept(formatter.accept)
27
18
  content_type(formatter.content_type)
@@ -44,7 +35,7 @@ module Resto
44
35
  end
45
36
 
46
37
  def basic_auth(options)
47
- options = BasicAuth.new(options)
38
+ options = AssertHash.keys(options, :username, :password)
48
39
 
49
40
  username = options.fetch(:username)
50
41
  password = options.fetch(:password)
@@ -63,7 +63,9 @@ module Resto
63
63
  def to_object
64
64
  return self unless @translator
65
65
 
66
- @translator.call(@klass, read_body).tap do |instance|
66
+ body = read_body ? read_body.first : nil
67
+
68
+ @translator.call(@klass, body).tap do |instance|
67
69
  instance.response = self
68
70
  end
69
71
  end
@@ -71,9 +73,7 @@ module Resto
71
73
  def to_collection
72
74
  return self unless @translator
73
75
 
74
- body = read_body.is_a?(Hash) ? [read_body] : read_body
75
-
76
- (body || []).map do |hash|
76
+ (read_body || []).map do |hash|
77
77
  @translator.call(@klass, hash).tap do |instance|
78
78
  instance.response = self
79
79
  end
data/lib/resto/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Resto
4
- VERSION = "0.0.9"
4
+ VERSION = "0.1.0"
5
5
  end
data/resto.gemspec CHANGED
@@ -1,6 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
-
4
3
  require File.expand_path("../lib/resto/version", __FILE__)
5
4
 
6
5
  Gem::Specification.new do |s|
@@ -17,7 +16,7 @@ Gem::Specification.new do |s|
17
16
  s.required_rubygems_version = ">= 1.3.6"
18
17
  s.rubyforge_project = "resto"
19
18
 
20
- s.add_runtime_dependency "yajl-ruby", "0.8.2"
19
+ s.add_runtime_dependency "yajl-ruby", ">= 0.8.2"
21
20
  s.add_runtime_dependency "nokogiri", ">=1.4.4"
22
21
  # s.add_dependency "activesupport", "3.0.0" ???
23
22
  s.add_development_dependency "bundler", ">= 1.0.13"
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
  require 'resto/extra/copy'
5
- describe Resto::Extra::Copy do
5
+ describe Resto::Copy do
6
6
  describe ".request_base" do
7
7
  before do
8
8
  @request_base = Resto::Request::Base.new.port(40).
@@ -10,7 +10,7 @@ describe Resto::Extra::Copy do
10
10
  query('q=adam').
11
11
  path('contacts')
12
12
 
13
- @new_request_base = Resto::Extra::Copy.request_base(@request_base).
13
+ @new_request_base = Resto::Copy.request_base(@request_base).
14
14
  url('http://new.se:99/other').
15
15
  query('q=not-same').
16
16
  path('other-contacts/').
@@ -47,7 +47,7 @@ describe Resto::Extra::Copy do
47
47
  describe ".response_base" do
48
48
  before do
49
49
  @response_base = Resto::Response::Base.new.format(:json)
50
- @new_response_base = Resto::Extra::Copy.response_base(@response_base).
50
+ @new_response_base = Resto::Copy.response_base(@response_base).
51
51
  http_response('response')
52
52
  end
53
53
 
@@ -7,10 +7,16 @@ require 'yajl'
7
7
  describe "Resto::Format.get(:json)" do
8
8
  subject { Resto::Format.get(:json) }
9
9
 
10
- its(:extension) { should == 'json' }
11
10
  its(:accept) { should == 'application/json, */*' }
12
11
  its(:content_type) { should == 'application/json' }
13
12
 
13
+ describe '.decode(json)' do
14
+ json = Yajl::Encoder.encode( { :foo => 12425125, :bar => "some string" })
15
+ expected = [{ 'foo' => 12425125, 'bar' => "some string" }]
16
+
17
+ it { subject.decode(json).should == expected }
18
+ end
19
+
14
20
  describe ".encode(hash)" do
15
21
  before { @result = subject.encode({ :bar => "some string",
16
22
  :foo => 12425125}) }
@@ -19,11 +25,5 @@ describe "Resto::Format.get(:json)" do
19
25
  it { @result.should =~ /foo\":12425125/ }
20
26
  end
21
27
 
22
- describe '.decode(json)' do
23
- json = Yajl::Encoder.encode( { :foo => 12425125, :bar => "some string" })
24
- expected = { 'foo' => 12425125, 'bar' => "some string" }
25
-
26
- it { subject.decode(json).should == expected }
27
- end
28
-
28
+ its(:extension) { should == 'json' }
29
29
  end
@@ -87,11 +87,11 @@ describe "Resto::Format.get(:xml)" do
87
87
 
88
88
  describe '.decode(xml, xpath)' do
89
89
  context 'when one item' do
90
- it { subject.decode(xml, :xpath => '//zone').should == attributes }
90
+ it { subject.decode(xml, :xpath => '//zone').should == [attributes] }
91
91
  end
92
92
 
93
93
  context 'when 0 items' do
94
- it { subject.decode(xml, :xpath => '//xx0').should == {} }
94
+ it { subject.decode(xml, :xpath => '//xx0').should == [{}] }
95
95
  end
96
96
 
97
97
  context 'when a collection of two items' do
data/spec/resto_spec.rb CHANGED
@@ -531,14 +531,14 @@ describe Resto do
531
531
  it { Bitly.all.code.should == "200" }
532
532
  end
533
533
 
534
- describe ".all('shortUrl' => short)" do
534
+ describe ".all('short' => sh)" do
535
535
  before do
536
- stub_request(:get, "http://bit.ly/v3?format=json&shortUrl=short").
536
+ stub_request(:get, "http://bit.ly/v3?format=json&longUrl=ll&short=sh").
537
537
  with(:headers => headers("content-type" => "text/html")).
538
538
  to_return(:status => 200)
539
539
  end
540
540
 
541
- it { Bitly.all('shortUrl' => 'short').code.should == "200" }
541
+ it { Bitly.all('short' => 'sh').code.should == "200" }
542
542
  end
543
543
  end
544
544
 
metadata CHANGED
@@ -1,8 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resto
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 27
4
5
  prerelease:
5
- version: 0.0.9
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
6
11
  platform: ruby
7
12
  authors:
8
13
  - "Anders T\xC3\xB6rnqvist"
@@ -10,7 +15,7 @@ autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
17
 
13
- date: 2011-05-17 00:00:00 Z
18
+ date: 2011-06-15 00:00:00 Z
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
21
  name: yajl-ruby
@@ -18,8 +23,13 @@ dependencies:
18
23
  requirement: &id001 !ruby/object:Gem::Requirement
19
24
  none: false
20
25
  requirements:
21
- - - "="
26
+ - - ">="
22
27
  - !ruby/object:Gem::Version
28
+ hash: 59
29
+ segments:
30
+ - 0
31
+ - 8
32
+ - 2
23
33
  version: 0.8.2
24
34
  type: :runtime
25
35
  version_requirements: *id001
@@ -31,6 +41,11 @@ dependencies:
31
41
  requirements:
32
42
  - - ">="
33
43
  - !ruby/object:Gem::Version
44
+ hash: 15
45
+ segments:
46
+ - 1
47
+ - 4
48
+ - 4
34
49
  version: 1.4.4
35
50
  type: :runtime
36
51
  version_requirements: *id002
@@ -42,6 +57,11 @@ dependencies:
42
57
  requirements:
43
58
  - - ">="
44
59
  - !ruby/object:Gem::Version
60
+ hash: 13
61
+ segments:
62
+ - 1
63
+ - 0
64
+ - 13
45
65
  version: 1.0.13
46
66
  type: :development
47
67
  version_requirements: *id003
@@ -53,6 +73,11 @@ dependencies:
53
73
  requirements:
54
74
  - - ">="
55
75
  - !ruby/object:Gem::Version
76
+ hash: 23
77
+ segments:
78
+ - 2
79
+ - 6
80
+ - 0
56
81
  version: 2.6.0
57
82
  type: :development
58
83
  version_requirements: *id004
@@ -64,6 +89,11 @@ dependencies:
64
89
  requirements:
65
90
  - - ">="
66
91
  - !ruby/object:Gem::Version
92
+ hash: 23
93
+ segments:
94
+ - 0
95
+ - 0
96
+ - 4
67
97
  version: 0.0.4
68
98
  type: :development
69
99
  version_requirements: *id005
@@ -75,6 +105,11 @@ dependencies:
75
105
  requirements:
76
106
  - - ">="
77
107
  - !ruby/object:Gem::Version
108
+ hash: 11
109
+ segments:
110
+ - 1
111
+ - 6
112
+ - 2
78
113
  version: 1.6.2
79
114
  type: :development
80
115
  version_requirements: *id006
@@ -86,6 +121,11 @@ dependencies:
86
121
  requirements:
87
122
  - - ">="
88
123
  - !ruby/object:Gem::Version
124
+ hash: 59
125
+ segments:
126
+ - 0
127
+ - 8
128
+ - 2
89
129
  version: 0.8.2
90
130
  type: :development
91
131
  version_requirements: *id007
@@ -97,6 +137,9 @@ dependencies:
97
137
  requirements:
98
138
  - - ">="
99
139
  - !ruby/object:Gem::Version
140
+ hash: 3
141
+ segments:
142
+ - 0
100
143
  version: "0"
101
144
  type: :development
102
145
  version_requirements: *id008
@@ -108,6 +151,9 @@ dependencies:
108
151
  requirements:
109
152
  - - ">="
110
153
  - !ruby/object:Gem::Version
154
+ hash: 3
155
+ segments:
156
+ - 0
111
157
  version: "0"
112
158
  type: :development
113
159
  version_requirements: *id009
@@ -119,6 +165,9 @@ dependencies:
119
165
  requirements:
120
166
  - - ">="
121
167
  - !ruby/object:Gem::Version
168
+ hash: 3
169
+ segments:
170
+ - 0
122
171
  version: "0"
123
172
  type: :development
124
173
  version_requirements: *id010
@@ -130,6 +179,9 @@ dependencies:
130
179
  requirements:
131
180
  - - ">="
132
181
  - !ruby/object:Gem::Version
182
+ hash: 3
183
+ segments:
184
+ - 0
133
185
  version: "0"
134
186
  type: :development
135
187
  version_requirements: *id011
@@ -143,11 +195,9 @@ extensions: []
143
195
  extra_rdoc_files: []
144
196
 
145
197
  files:
146
- - lib/blankslate.rb
147
198
  - lib/resto/attributes.rb
199
+ - lib/resto/extra/assert_hash.rb
148
200
  - lib/resto/extra/copy.rb
149
- - lib/resto/extra/delegation.rb
150
- - lib/resto/extra/hash_args.rb
151
201
  - lib/resto/format/default.rb
152
202
  - lib/resto/format/json.rb
153
203
  - lib/resto/format/plain.rb
@@ -173,7 +223,6 @@ files:
173
223
  - lib/resto/version.rb
174
224
  - lib/resto.rb
175
225
  - spec/resto/extra/copy_spec.rb
176
- - spec/resto/extra/hash_args_spec.rb
177
226
  - spec/resto/format/default_spec.rb
178
227
  - spec/resto/format/json_spec.rb
179
228
  - spec/resto/format/plain_spec.rb
@@ -202,17 +251,27 @@ required_ruby_version: !ruby/object:Gem::Requirement
202
251
  requirements:
203
252
  - - ">="
204
253
  - !ruby/object:Gem::Version
254
+ hash: 57
255
+ segments:
256
+ - 1
257
+ - 8
258
+ - 7
205
259
  version: 1.8.7
206
260
  required_rubygems_version: !ruby/object:Gem::Requirement
207
261
  none: false
208
262
  requirements:
209
263
  - - ">="
210
264
  - !ruby/object:Gem::Version
265
+ hash: 23
266
+ segments:
267
+ - 1
268
+ - 3
269
+ - 6
211
270
  version: 1.3.6
212
271
  requirements: []
213
272
 
214
273
  rubyforge_project: resto
215
- rubygems_version: 1.7.2
274
+ rubygems_version: 1.8.5
216
275
  signing_key:
217
276
  specification_version: 3
218
277
  summary: Restful Web Service
data/lib/blankslate.rb DELETED
@@ -1,110 +0,0 @@
1
- # encoding: utf-8
2
- #!/usr/bin/env ruby
3
- #--
4
- # Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
5
- # All rights reserved.
6
-
7
- # Permission is granted for use, copying, modification, distribution,
8
- # and distribution of modified versions of this work as long as the
9
- # above copyright notice is included.
10
- #++
11
-
12
- ######################################################################
13
- # BlankSlate provides an abstract base class with no predefined
14
- # methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
15
- # BlankSlate is useful as a base class when writing classes that
16
- # depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
17
- #
18
- class BlankSlate
19
- class << self
20
-
21
- # Hide the method named +name+ in the BlankSlate class. Don't
22
- # hide +instance_eval+ or any method beginning with "__".
23
- def hide(name)
24
- if instance_methods.include?(name.to_s) and
25
- name !~ /^(__|instance_eval)/
26
- @hidden_methods ||= {}
27
- @hidden_methods[name.to_sym] = instance_method(name)
28
- undef_method name
29
- end
30
- end
31
-
32
- def find_hidden_method(name)
33
- @hidden_methods ||= {}
34
- @hidden_methods[name] || superclass.find_hidden_method(name)
35
- end
36
-
37
- # Redefine a previously hidden method so that it may be called on a blank
38
- # slate object.
39
- def reveal(name)
40
- hidden_method = find_hidden_method(name)
41
- fail "Don't know how to reveal method '#{name}'" unless hidden_method
42
- define_method(name, hidden_method)
43
- end
44
- end
45
-
46
- instance_methods.each { |m| hide(m) }
47
- end
48
-
49
- ######################################################################
50
- # Since Ruby is very dynamic, methods added to the ancestors of
51
- # BlankSlate <em>after BlankSlate is defined</em> will show up in the
52
- # list of available BlankSlate methods. We handle this by defining a
53
- # hook in the Object and Kernel classes that will hide any method
54
- # defined after BlankSlate has been loaded.
55
- #
56
- module Kernel
57
- class << self
58
- alias_method :blank_slate_method_added, :method_added
59
-
60
- # Detect method additions to Kernel and remove them in the
61
- # BlankSlate class.
62
- def method_added(name)
63
- result = blank_slate_method_added(name)
64
- return result if self != Kernel
65
- BlankSlate.hide(name)
66
- result
67
- end
68
- end
69
- end
70
-
71
- ######################################################################
72
- # Same as above, except in Object.
73
- #
74
- class Object
75
- class << self
76
- alias_method :blank_slate_method_added, :method_added
77
-
78
- # Detect method additions to Object and remove them in the
79
- # BlankSlate class.
80
- def method_added(name)
81
- result = blank_slate_method_added(name)
82
- return result if self != Object
83
- BlankSlate.hide(name)
84
- result
85
- end
86
-
87
- def find_hidden_method(name)
88
- nil
89
- end
90
- end
91
- end
92
-
93
- ######################################################################
94
- # Also, modules included into Object need to be scanned and have their
95
- # instance methods removed from blank slate. In theory, modules
96
- # included into Kernel would have to be removed as well, but a
97
- # "feature" of Ruby prevents late includes into modules from being
98
- # exposed in the first place.
99
- #
100
- class Module
101
- alias blankslate_original_append_features append_features
102
- def append_features(mod)
103
- result = blankslate_original_append_features(mod)
104
- return result if mod != Object
105
- instance_methods.each do |name|
106
- BlankSlate.hide(name)
107
- end
108
- result
109
- end
110
- end
@@ -1,26 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Resto
4
- module Extra
5
- module Delegation
6
-
7
- def delegate(*methods)
8
- options = methods.pop
9
- to = options[:to]
10
- unless options.is_a?(Hash) && to
11
- raise ArgumentError, "Delegation needs a target. Supply an options
12
- hash with a :to key as the last argument
13
- (e.g. delegate :hello, :to => :greeter)."
14
- end
15
-
16
- methods.each do |method|
17
- class_eval <<-EOS
18
- def #{method}(*args, &block)
19
- #{to}.__send__(#{method.inspect}, *args, &block)
20
- end
21
- EOS
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,56 +0,0 @@
1
- # encoding: utf-8
2
-
3
- class Resto::Extra::HashArgs; end
4
-
5
- class << Resto::Extra::HashArgs
6
-
7
- def key(key)
8
- @keys ||= []
9
-
10
- unless key.is_a?(Symbol)
11
- raise ArgumentError, "The key '#{key}' must be a symbol"
12
- end
13
-
14
- if @keys.include?(key)
15
- raise ArgumentError, "The key '#{key}' has already been defined."
16
- end
17
-
18
- @keys << key
19
- end
20
-
21
- private
22
- def assert_key(key)
23
- unless @keys.include?(key.to_sym)
24
- raise ArgumentError, "The key '#{key}' is not valid.
25
- Valid keys are: #{@keys.join(' ,')}"
26
- end
27
- end
28
- end
29
-
30
- class Resto::Extra::HashArgs
31
-
32
- def initialize(hash)
33
- hash ||= {}
34
- raise ArgumentError, "'#{hash}' must be a Hash" unless hash.is_a?(Hash)
35
- keys = hash.keys
36
- keys_as_symbols = keys.map(&:to_sym)
37
- if (keys_as_symbols.uniq.size != keys_as_symbols.size)
38
- raise ArgumentError, "duplicated keys: #{keys.join(', ')}"
39
- end
40
-
41
- @hash = {}
42
- keys.each do |key|
43
- self.class.send(:assert_key, key)
44
- @hash[key.to_sym] = hash.fetch(key)
45
- end
46
- end
47
-
48
- def fetch(key, &block)
49
- self.class.send(:assert_key, key)
50
- @hash.fetch(key, &block)
51
- end
52
-
53
- def keys
54
- @hash.keys
55
- end
56
- end
@@ -1,71 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
- require 'resto/extra/hash_args'
5
-
6
- describe Resto::Extra::HashArgs do
7
-
8
- class_context(%Q{
9
- class BasicAuthication < Resto::Extra::HashArgs
10
- key :username
11
- key :password
12
- end}) do
13
-
14
- it "returns the value from the block when no value is found by key" do
15
- BasicAuthication.new(nil).fetch(:username) { 'anders' }.should == 'anders'
16
- end
17
-
18
- it "returns the value found by the key" do
19
- BasicAuthication.new({'username' => 'anders', :password => 'secret'}).
20
- fetch(:password) { 'other' }.should == 'secret'
21
- end
22
-
23
- it "the key is translated to its symbol" do
24
- BasicAuthication.new({'username' => 'anders', :password => 'secret'}).
25
- fetch(:username) { 'other' }.should == 'anders'
26
- end
27
- end
28
-
29
- class_context(%Q{
30
- class FormatExt < Resto::Extra::HashArgs
31
- key :extension
32
- end}) do
33
-
34
- it "returns the value from the block" do
35
- FormatExt.new({}).fetch(:extension) { 'block' }.should == 'block'
36
- end
37
-
38
- if RUBY_VERSION < '1.9'
39
-
40
- it "raises IndexError when no value and no block" do
41
- expect { FormatExt.new({}).fetch(:extension) }.
42
- to raise_error(IndexError, 'key not found')
43
- end
44
-
45
- else
46
-
47
- it "raises KeyError when no value and no block" do
48
- lambda { FormatExt.new({}).fetch(:extension) }.
49
- should raise_error(KeyError, 'key not found: :extension')
50
- end
51
-
52
- end
53
-
54
- it "raises" do
55
- expect { FormatExt.new({:username => "anders"}) }.
56
- to raise_error(ArgumentError, /The key 'username'/)
57
-
58
- expect { FormatExt.new("string") }.
59
- to raise_error(ArgumentError, "'string' must be a Hash")
60
-
61
- expect { FormatExt.new(:extension => 'value', 'extension' => 'value') }.
62
- to raise_error(ArgumentError, "duplicated keys: extension, extension")
63
-
64
- expect { FormatExt.new({:invalid_key => 'invalid' }) }.
65
- to raise_error(ArgumentError, /The key 'invalid_key' is not valid/)
66
-
67
- expect { FormatExt.new({:extension => 'value' }).fetch(:invalid_key) }.
68
- to raise_error(ArgumentError, /The key 'invalid_key' is not valid/)
69
- end
70
- end
71
- end