resto 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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