hal-client 2.0.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -47,7 +47,7 @@ In the example above `item` is the link rel. The `#related` method extracts embe
47
47
 
48
48
  #### Request timing
49
49
 
50
- If the `author` relationship was a link in the above example the HTTP GET to retrieve Bob's representation from the server does not happen until the `#property` method is called. This lazy dereferencing allows for working with efficiently with larger relationship sets.
50
+ If the `author` relationship was a regular link (that is, not embedded) in the above example the HTTP GET to retrieve Bob's representation from the server does not happen until the `#property` method is called. This lazy dereferencing allows for working with efficiently with larger relationship sets.
51
51
 
52
52
  #### CURIEs
53
53
 
@@ -66,7 +66,7 @@ Bob's home location can be retrieved with
66
66
 
67
67
  Links are always accessed using the full link relation, rather than the CURIE, because the document producer can use any arbitrary string as the prefix. This means that clients must not make any assumptions regarding what prefix will be used because it might change over time or even between documents.
68
68
 
69
- ### Templated links
69
+ #### Templated links
70
70
 
71
71
  The `#related` methods takes a `Hash` as its second argument which is used to expand any templated links that are involved in the navigation.
72
72
 
@@ -88,6 +88,26 @@ All `HalClient::Representation`s exposed an `#href` attribute which is its ident
88
88
  blog['title'] # => "Some Person's Blog"
89
89
  blog['item'] # => #<RepresentationSet:...>
90
90
 
91
+ ### POST requests
92
+
93
+ HalClient supports POST requests to remote resources via it's `#post` method.
94
+
95
+ blog.post(new_article_as_hal_json_str)
96
+ #=> #<Representation: http://blog.me>
97
+
98
+ The argument to post may be `String` or any object that responds to `#to_hal`. Additional options may be passed to change the content type of the post, etc.
99
+
100
+ ### Paged collections
101
+
102
+ HalClient provides a high level abstraction for paged collections encoded using [standard `item`, `next` and `prev` link relations](http://tools.ietf.org/html/rfc6573).
103
+
104
+ articles = HalClient::Collection.new(blog)
105
+ articles.each do |an_article|
106
+ # do something with each article representation
107
+ end
108
+
109
+ If the collection is paged this will navigate to the next page after yielding all the items on the current page. `HalClient::Collection` is `Enumerable` so all your favorite collection methods are available.
110
+
91
111
  ### Custom media types
92
112
 
93
113
  If the API uses one or more a custom mime types we can specify that they be included in the `Accept` header field of each request.
@@ -96,7 +116,12 @@ If the API uses one or more a custom mime types we can specify that they be incl
96
116
  my_client.get("http://blog.me/")
97
117
  # => #<Representation: http://blog.me/>
98
118
 
99
- ### Parsing presentations from clients
119
+ Similarly we can set the default `Content-Type` for post requests.
120
+
121
+ my_client = HalClient.new(accept: "application/vnd.myapp+hal+json",
122
+ content_type: "application/vnd.myapp+hal+json")
123
+
124
+ ### Parsing representations on the server side
100
125
 
101
126
  HalClient can be used by servers of HAL APIs to interpret the bodies of requests. For example,
102
127
 
data/hal-client.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency "rest-client", "~> 1.6", '>= 1.6.0'
22
22
  spec.add_dependency "addressable", "~> 2.3", '>= 2.3.0'
23
- spec.add_dependency "multi_json", "~> 1.8", '>= 1.8.0'
23
+ spec.add_dependency "multi_json", "~> 1.9", '>= 1.9.0'
24
24
 
25
25
  spec.add_development_dependency "bundler", "~> 1.5"
26
26
  spec.add_development_dependency "rake", "~> 10.1", '>= 10.1.0'
@@ -0,0 +1,73 @@
1
+ require_relative "../hal_client"
2
+
3
+ class HalClient
4
+
5
+ # Enumerable for items in a paged collection of HAL representations
6
+ # that are encoded using the IANA standard `item`, `next` and `prev`
7
+ # link rels.
8
+ #
9
+ # This will fetch subsequent pages on iteration
10
+ class Collection
11
+ include Enumerable
12
+
13
+ # Initializes a collection starting at `first_page`.
14
+ #
15
+ # first_page - The HalClient::Representation of the first page of
16
+ # the collection to be iterated over.
17
+ #
18
+ # Raises HalClient::NotACollectionError if `first_page` is not a
19
+ # page of a collection.
20
+ # Raises ArgumentError if `first_page` is some page other than
21
+ # the first of the collection.
22
+ def initialize(first_page)
23
+ (fail NotACollectionError) unless first_page.has_related? "item"
24
+ (fail ArgumentError, "Not the first page of the collection") if first_page.has_related? "prev"
25
+
26
+ @first_page = first_page
27
+ end
28
+
29
+ # Returns the number of items in the collection if it is fast to
30
+ # calculate.
31
+ #
32
+ # Raises NotImplementedError if any of the pages of the collection
33
+ # have not already been cached.
34
+ def count(&blk)
35
+ (fail NotImplementedError, "Cowardly refusing to make an arbitrary number of HTTP requests") unless all_pages_cached?
36
+
37
+ total = 0
38
+ each_page do |p|
39
+ total += p.related("item").count
40
+ end
41
+
42
+ total
43
+ end
44
+
45
+ # Iterates over the members of the collection fetching the next
46
+ # page as necessary.
47
+ #
48
+ # Yields the next item of the iteration.
49
+ def each(&blk)
50
+ each_page do |a_page|
51
+ a_page.related("item").each(&blk)
52
+ end
53
+ end
54
+
55
+ protected
56
+
57
+ attr_reader :first_page
58
+
59
+ def all_pages_cached?
60
+ ! first_page.has_related?("next")
61
+ end
62
+
63
+ def each_page(&blk)
64
+ yield first_page
65
+
66
+ cur_page = first_page
67
+ while cur_page.has_related? "next"
68
+ cur_page = cur_page.related("next").first
69
+ yield cur_page
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,3 +1,4 @@
1
1
  class HalClient
2
2
  InvalidRepresentationError = Class.new(StandardError)
3
+ NotACollectionError = Class.new(StandardError)
3
4
  end
@@ -27,7 +27,15 @@ class HalClient
27
27
  @raw.nil? && @href.nil?
28
28
 
29
29
  (fail InvalidRepresentationError, "Invalid HAL representation: #{raw.inspect}") if
30
- raw && ! hashish?(raw)
30
+ @raw && ! hashish?(@raw)
31
+ end
32
+
33
+ # Posts a `Representation` or `String` to this resource.
34
+ #
35
+ # data - a `String` or an object that responds to `#to_hal`
36
+ # options - set of options to pass to `HalClient#post`
37
+ def post(data, options={})
38
+ @hal_client.post(href, data, options)
31
39
  end
32
40
 
33
41
  # Returns The value of the specified property or the specified
@@ -146,13 +154,19 @@ class HalClient
146
154
  "#<" + self.class.name + ": " + href + ">"
147
155
  end
148
156
 
157
+ # Returns the raw json representation of this representation
158
+ def to_json
159
+ raw.to_json
160
+ end
161
+
149
162
  protected
150
163
  attr_reader :hal_client
151
164
 
152
165
  MISSING = Object.new
153
166
 
154
167
  def raw
155
- if @raw.nil? && @href && hal_client
168
+ if @raw.nil? && @href
169
+ (fail "unable to make requests due to missing hal client") unless hal_client
156
170
  @raw ||= hal_client.get(@href).raw
157
171
  end
158
172
 
@@ -27,6 +27,29 @@ class HalClient
27
27
  RepresentationSet.new flat_map{|it| it.related(link_rel, options){[]}.to_a }
28
28
  end
29
29
 
30
+ # Returns true if any member representation contains a link
31
+ # (including embedded links) whose rel is `link_rel`.
32
+ #
33
+ # link_rel - The link rel of interest
34
+ def has_related?(link_rel)
35
+ _ = related link_rel
36
+ true
37
+
38
+ rescue KeyError
39
+ false
40
+ end
41
+
42
+ # Post a `Representation` or `String` to the resource.
43
+ #
44
+ # NOTE: This only works for a single representation.
45
+ #
46
+ # data - a `String` or an object that responds to `#to_hal`
47
+ # options - set of options to pass to `HalClient#post`
48
+ def post(data, options={})
49
+ raise NotImplementedError, "We only posts to singular resources." if count > 1
50
+ first.post(data, options)
51
+ end
52
+
30
53
  protected
31
54
 
32
55
  attr_reader :reprs
@@ -1,3 +1,3 @@
1
1
  class HalClient
2
- VERSION = "2.0.2"
2
+ VERSION = "2.2.0"
3
3
  end
data/lib/hal_client.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "hal_client/version"
2
2
  require 'rest-client'
3
+ require 'multi_json'
3
4
 
4
5
  # Adapter used to access resources.
5
6
  class HalClient
@@ -7,14 +8,18 @@ class HalClient
7
8
  autoload :RepresentationSet, 'hal_client/representation_set'
8
9
  autoload :CurieResolver, 'hal_client/curie_resolver'
9
10
  autoload :InvalidRepresentationError, 'hal_client/errors'
11
+ autoload :NotACollectionError, 'hal_client/errors'
10
12
 
11
13
  # Initializes a new client instance
12
14
  #
13
15
  # options - hash of configuration options
14
- # :accept - one or more content types that should be
16
+ # :accept - one or more content types that should be
15
17
  # prepended to the `Accept` header field of each request.
18
+ # :content_type - a single content type that should be
19
+ # prepended to the `Content-Type` header field of each request.
16
20
  def initialize(options={})
17
21
  @default_accept = options.fetch(:accept, 'application/hal+json')
22
+ @default_content_type = options.fetch(:content_type, 'application/hal+json')
18
23
  end
19
24
 
20
25
  # Returns a `Representation` of the resource identified by `url`.
@@ -22,16 +27,38 @@ class HalClient
22
27
  # url - The URL of the resource of interest.
23
28
  # options - set of options to pass to `RestClient#get`
24
29
  def get(url, options={})
25
- resp = RestClient.get url, rest_client_options(options)
30
+ resp = RestClient.get url, get_options(options)
26
31
  Representation.new hal_client: self, parsed_json: MultiJson.load(resp)
27
32
  end
28
33
 
34
+ # Post a `Representation` or `String` to the resource identified at `url`.
35
+ #
36
+ # url - The URL of the resource of interest.
37
+ # data - a `String` or an object that responds to `#to_hal`
38
+ # options - set of options to pass to `RestClient#post`
39
+ def post(url, data, options={})
40
+ resp = RestClient.post url, data, post_options(options)
41
+
42
+ begin
43
+ Representation.new hal_client: self, parsed_json: MultiJson.load(resp)
44
+ rescue MultiJson::ParseError, InvalidRepresentationError => e
45
+ resp
46
+ end
47
+ end
48
+
29
49
  protected
30
50
 
31
- attr_reader :default_accept
51
+ attr_reader :default_accept, :default_content_type
32
52
 
33
- def rest_client_options(overrides)
34
- {accept: default_accept}.merge overrides
53
+ def get_options(overrides)
54
+ { accept: default_accept }.merge overrides
55
+ end
56
+
57
+ def post_options(overrides)
58
+ {
59
+ accept: default_accept,
60
+ content_type: default_content_type
61
+ }.merge overrides
35
62
  end
36
63
 
37
64
  module EntryPointCovenienceMethods
@@ -43,6 +70,15 @@ class HalClient
43
70
  default_client.get(url, options)
44
71
  end
45
72
 
73
+ # Post a `Representation` or `String` to the resource identified at `url`.
74
+ #
75
+ # url - The URL of the resource of interest.
76
+ # data - a `String` or an object that responds to `#to_hal`
77
+ # options - set of options to pass to `RestClient#get`
78
+ def post(url, data, options={})
79
+ default_client.post(url, data, options)
80
+ end
81
+
46
82
  protected
47
83
 
48
84
  def default_client
@@ -0,0 +1,87 @@
1
+ require_relative "../spec_helper"
2
+
3
+ require 'hal_client/collection'
4
+
5
+ describe HalClient::Collection do
6
+ describe "creation" do
7
+ subject { described_class }
8
+
9
+ specify do
10
+ expect { described_class.new(collection_page) }
11
+ .not_to raise_error
12
+ end
13
+
14
+ specify do
15
+ expect { described_class.new(non_collection_repr) }
16
+ .to raise_error HalClient::NotACollectionError
17
+ end
18
+
19
+ specify do
20
+ expect { described_class.new(non_first_page) }
21
+ .to raise_error ArgumentError, /first page/
22
+ end
23
+
24
+ let(:non_collection_repr) { repr({}) }
25
+ let(:non_first_page) { collection_page(prev_href: "http://example.com/p1") }
26
+ end
27
+
28
+ describe "multi-item, multi-page" do
29
+ subject(:collection) { described_class.new(first_page) }
30
+ let!(:second_page_req) { stub_request(:get, second_page.href)
31
+ .to_return body: second_page.to_json }
32
+
33
+ it "fetches all the pages when iterating" do
34
+ collection.each do |it| end
35
+
36
+ expect(a_request(:get, second_page.href)).to have_been_made
37
+ end
38
+
39
+ it "iteration yields all the items" do
40
+ yielded = collection.map { |it| it.href }
41
+ expect(yielded).to eq ["foo", "bar", "baz"]
42
+ end
43
+
44
+ specify { expect { collection.count }.to raise_exception }
45
+
46
+
47
+ let(:first_page_href) { "http://example.com/p1" }
48
+ let(:first_page) { collection_page(next_href: second_page_href,
49
+ self_href: first_page_href,
50
+ items: ["foo", "bar"]) }
51
+
52
+ let(:second_page_href) { "http://example.com/p2" }
53
+ let(:second_page) { collection_page(items: ["baz"],
54
+ self_href: second_page_href,
55
+ prev_href: first_page_href) }
56
+ end
57
+
58
+ describe "multi-item, single page" do
59
+ subject(:collection) { described_class.new(only_page) }
60
+
61
+ specify { expect(collection.count).to eq 2 }
62
+
63
+ let(:only_page) { collection_page(self_href: "http://example.com/p1",
64
+ items: ["foo", "bar"]) }
65
+ end
66
+
67
+ let(:hal_client) { HalClient.new }
68
+
69
+ def collection_page(opts={})
70
+ next_href = opts[:next_href]
71
+ prev_href = opts[:prev_href]
72
+ self_href = opts.fetch(:self_href, "a_page")
73
+ items = opts.fetch(:items, [])
74
+ .map{|it| {"_links"=>{"self"=>{"href"=>it}}} }
75
+
76
+ full = {"_embedded"=>{"item"=>items},
77
+ "_links"=>{"self"=>{"href"=>self_href}}}
78
+ full["_links"]["next"] = {"href" => next_href} if next_href
79
+ full["_links"]["prev"] = {"href" => prev_href} if prev_href
80
+
81
+ repr full
82
+ end
83
+
84
+ def repr(a_hash)
85
+ HalClient::Representation.new parsed_json: a_hash, hal_client: hal_client
86
+ end
87
+ end
@@ -28,6 +28,7 @@ describe HalClient::RepresentationSet do
28
28
  it "returns true if there are any" do
29
29
  expect(subject.any?{|it| it == foo_repr }).to be_true
30
30
  end
31
+
31
32
  it "returns false if there aren't any" do
32
33
  expect(subject.any?{|it| false }).to be_false
33
34
  end
@@ -40,6 +41,7 @@ describe HalClient::RepresentationSet do
40
41
  it { should include_representation_of "http://example.com/bar-spouse" }
41
42
  it { should have(2).items }
42
43
  end
44
+
43
45
  context "multiple targets" do
44
46
  subject(:returned_val) { repr_set.related("sibling") }
45
47
  it { should include_representation_of "http://example.com/foo-brother" }
@@ -47,6 +49,7 @@ describe HalClient::RepresentationSet do
47
49
  it { should include_representation_of "http://example.com/bar-brother" }
48
50
  it { should have(3).items }
49
51
  end
52
+
50
53
  context "templated" do
51
54
  subject(:returned_val) { repr_set.related("cousin", distance: "first") }
52
55
  it { should include_representation_of "http://example.com/foo-first-cousin" }
@@ -56,6 +59,24 @@ describe HalClient::RepresentationSet do
56
59
  end
57
60
  end
58
61
 
62
+ describe "#post" do
63
+ context "with a single representation" do
64
+ subject(:repr_single_set) { described_class.new([foo_repr]) }
65
+ let!(:post_request) { stub_request(:post, "example.com/foo") }
66
+
67
+ before(:each) do
68
+ repr_single_set.post("abc")
69
+ end
70
+
71
+ it "makes an HTTP POST with the data within the representation" do
72
+ expect(
73
+ post_request.
74
+ with(:body => "abc", :headers => {'Content-Type' => 'application/hal+json'})
75
+ ).to have_been_made
76
+ end
77
+ end
78
+ end
79
+
59
80
  let(:a_client) { HalClient.new }
60
81
 
61
82
  let(:foo_repr) { HalClient::Representation.new hal_client: a_client, parsed_json: MultiJson.load(foo_hal)}
@@ -101,7 +122,6 @@ describe HalClient::RepresentationSet do
101
122
  to_return body: %Q|{"_links":{"self":{"href":#{url.to_json}}}}|
102
123
  end
103
124
 
104
-
105
125
  RSpec::Matchers.define(:include_representation_of) do |url|
106
126
  match { |repr_set|
107
127
  repr_set.any?{|it| it.href == url}
@@ -23,6 +23,23 @@ HAL
23
23
  subject(:repr) { described_class.new(hal_client: a_client,
24
24
  parsed_json: MultiJson.load(raw_repr)) }
25
25
 
26
+ describe "#post" do
27
+ let!(:post_request) {
28
+ stub_request(:post, "example.com/bar")
29
+ }
30
+
31
+ before(:each) do
32
+ repr.related("link1").post("abc")
33
+ end
34
+
35
+ specify {
36
+ expect(
37
+ post_request.
38
+ with(:body => "abc", :headers => {'Content-Type' => 'application/hal+json'})
39
+ ).to have_been_made
40
+ }
41
+ end
42
+
26
43
  describe "#to_s" do
27
44
  subject(:return_val) { repr.to_s }
28
45
 
@@ -34,6 +51,7 @@ HAL
34
51
  subject { repr.property "prop1" }
35
52
  it { should eq 1 }
36
53
  end
54
+
37
55
  context "non-existent" do
38
56
  it "raises exception" do
39
57
  expect{repr.property 'wat'}.to raise_exception KeyError
@@ -48,6 +66,7 @@ HAL
48
66
  subject { repr.fetch "prop1" }
49
67
  it { should eq 1 }
50
68
  end
69
+
51
70
  context "for existent link" do
52
71
  subject { repr.fetch "link1" }
53
72
  it { should have(1).item }
@@ -55,6 +74,7 @@ HAL
55
74
  expect(subject.first.href).to eq "http://example.com/bar"
56
75
  end
57
76
  end
77
+
58
78
  context "for existent embedded" do
59
79
  subject { repr.fetch "embed1" }
60
80
  it { should have(1).item }
@@ -62,15 +82,18 @@ HAL
62
82
  expect(subject.first.href).to eq "http://example.com/baz"
63
83
  end
64
84
  end
85
+
65
86
  context "non-existent item w/o default" do
66
87
  it "raises exception" do
67
88
  expect{repr.fetch 'wat'}.to raise_exception KeyError
68
89
  end
69
90
  end
91
+
70
92
  context "non-existent item w/ default value" do
71
93
  subject { repr.fetch "wat", "whatevs" }
72
94
  it { should eq "whatevs" }
73
95
  end
96
+
74
97
  context "non-existent item w/ default value generator" do
75
98
  subject { repr.fetch("wat"){|key| key+"gen" } }
76
99
  it { should eq "watgen" }
@@ -82,16 +105,19 @@ HAL
82
105
  subject { repr["prop1"] }
83
106
  it { should eq 1 }
84
107
  end
108
+
85
109
  context "for existent link" do
86
110
  subject { repr["link1"] }
87
111
  it { should have(1).item }
88
112
  it { should include_representation_of "http://example.com/bar" }
89
113
  end
114
+
90
115
  context "for existent embedded" do
91
116
  subject { repr["embed1"] }
92
117
  it { should have(1).item }
93
118
  it { should include_representation_of "http://example.com/baz" }
94
119
  end
120
+
95
121
  context "non-existent item w/o default" do
96
122
  subject { repr["wat"] }
97
123
  it { should be_nil }
@@ -104,22 +130,26 @@ HAL
104
130
  it { should have(1).item }
105
131
  it { should include_representation_of "http://example.com/bar" }
106
132
  end
133
+
107
134
  context "for existent compound link" do
108
135
  subject { repr.related "link3" }
109
136
  it { should have(2).item }
110
137
  it { should include_representation_of "http://example.com/link3-a" }
111
138
  it { should include_representation_of "http://example.com/link3-b" }
112
139
  end
140
+
113
141
  context "for existent templated link" do
114
142
  subject { repr.related "link2", name: "bob" }
115
143
  it { should have(1).item }
116
144
  it { should include_representation_of "http://example.com/people?name=bob" }
117
145
  end
146
+
118
147
  context "for existent embedded" do
119
148
  subject { repr.related "embed1" }
120
149
  it { should have(1).item }
121
150
  it { should include_representation_of "http://example.com/baz" }
122
151
  end
152
+
123
153
  context "non-existent item w/o default" do
124
154
  it "raises exception" do
125
155
  expect{repr.related 'wat'}.to raise_exception KeyError
@@ -133,11 +163,13 @@ HAL
133
163
  it { should have(1).item }
134
164
  it { should include "http://example.com/bar" }
135
165
  end
166
+
136
167
  context "for existent embedded" do
137
168
  subject { repr.related_hrefs "embed1" }
138
169
  it { should have(1).item }
139
170
  it { should include "http://example.com/baz" }
140
171
  end
172
+
141
173
  context "non-existent item w/o default" do
142
174
  it "raises exception" do
143
175
  expect{repr.related_hrefs 'wat'}.to raise_exception KeyError
@@ -146,6 +178,7 @@ HAL
146
178
  end
147
179
 
148
180
  specify { expect(subject.has_related? "link1").to be true }
181
+ specify { expect(subject.has_related? "link3").to be true }
149
182
  specify { expect(subject.has_related? "embed1").to be true }
150
183
 
151
184
  specify { expect(subject.has_related? "no-such-link").to be false }
@@ -39,7 +39,7 @@ describe HalClient do
39
39
  end
40
40
  end
41
41
 
42
- describe ".get(<url>)" do
42
+ describe ".get(<url>)" do
43
43
  let!(:return_val) { HalClient.get "http://example.com/foo" }
44
44
 
45
45
  it "returns a HalClient::Representation" do
@@ -57,6 +57,70 @@ describe HalClient do
57
57
  end
58
58
  end
59
59
 
60
+ describe "#post(<url>)" do
61
+ subject(:client) { HalClient.new }
62
+ let!(:return_val) { client.post "http://example.com/foo", post_data }
63
+
64
+ it "returns a HalClient::Representation" do
65
+ expect(return_val).to be_kind_of HalClient::Representation
66
+ end
67
+
68
+ describe "request" do
69
+ subject { post_request }
70
+ it("should have been made") { should have_been_made }
71
+
72
+ it "sends content type header" do
73
+ expect(post_request.with(headers: {'Content-Type' => 'application/hal+json'})).
74
+ to have_been_made
75
+ end
76
+ end
77
+
78
+ context "explicit content type" do
79
+ subject(:client) { HalClient.new content_type: 'app/test' }
80
+ it "sends specified content-type header" do
81
+ expect(post_request.with(headers: {'Content-Type' => 'app/test'})).
82
+ to have_been_made
83
+ end
84
+ end
85
+
86
+ context "with no response body" do
87
+ subject { empty_post_request }
88
+ let!(:return_val) { client.post "http://example.com/foo", nil }
89
+
90
+ it "returns a 2xx status code in the response" do
91
+ expect(return_val.code.to_s).to match(/^2../)
92
+ end
93
+ end
94
+ end
95
+
96
+ describe ".post(<url>)" do
97
+ let!(:return_val) { HalClient.post "http://example.com/foo", post_data }
98
+
99
+ it "returns a HalClient::Representation" do
100
+ expect(return_val).to be_kind_of HalClient::Representation
101
+ end
102
+
103
+ describe "request" do
104
+ subject { post_request }
105
+ it("should have been made") { should have_been_made }
106
+
107
+ it "sends accept header" do
108
+ expect(post_request.with(headers: {'Content-Type' => 'application/hal+json'})).
109
+ to have_been_made
110
+ end
111
+ end
112
+ end
113
+
114
+ let(:post_data) { "ABC" }
115
+
116
+ let!(:empty_post_request) { stub_request(:post, "http://example.com/foo").
117
+ with(:body => nil).
118
+ to_return body: nil }
119
+
120
+ let!(:post_request) { stub_request(:post, "http://example.com/foo").
121
+ with(:body => post_data).
122
+ to_return body: "{}" }
123
+
60
124
  let!(:request) { stub_request(:get, "http://example.com/foo").
61
125
  to_return body: "{}" }
62
126
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hal-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-03-15 00:00:00.000000000 Z
12
+ date: 2014-03-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
@@ -62,10 +62,10 @@ dependencies:
62
62
  requirements:
63
63
  - - ~>
64
64
  - !ruby/object:Gem::Version
65
- version: '1.8'
65
+ version: '1.9'
66
66
  - - ! '>='
67
67
  - !ruby/object:Gem::Version
68
- version: 1.8.0
68
+ version: 1.9.0
69
69
  type: :runtime
70
70
  prerelease: false
71
71
  version_requirements: !ruby/object:Gem::Requirement
@@ -73,10 +73,10 @@ dependencies:
73
73
  requirements:
74
74
  - - ~>
75
75
  - !ruby/object:Gem::Version
76
- version: '1.8'
76
+ version: '1.9'
77
77
  - - ! '>='
78
78
  - !ruby/object:Gem::Version
79
- version: 1.8.0
79
+ version: 1.9.0
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: bundler
82
82
  requirement: !ruby/object:Gem::Requirement
@@ -175,11 +175,13 @@ files:
175
175
  - hal-client.gemspec
176
176
  - lib/hal-client.rb
177
177
  - lib/hal_client.rb
178
+ - lib/hal_client/collection.rb
178
179
  - lib/hal_client/curie_resolver.rb
179
180
  - lib/hal_client/errors.rb
180
181
  - lib/hal_client/representation.rb
181
182
  - lib/hal_client/representation_set.rb
182
183
  - lib/hal_client/version.rb
184
+ - spec/hal_client/collection_spec.rb
183
185
  - spec/hal_client/curie_resolver_spec.rb
184
186
  - spec/hal_client/representation_set_spec.rb
185
187
  - spec/hal_client/representation_spec.rb
@@ -200,7 +202,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
200
202
  version: '0'
201
203
  segments:
202
204
  - 0
203
- hash: -2118316168271544208
205
+ hash: 2964904468148201420
204
206
  required_rubygems_version: !ruby/object:Gem::Requirement
205
207
  none: false
206
208
  requirements:
@@ -209,7 +211,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
211
  version: '0'
210
212
  segments:
211
213
  - 0
212
- hash: -2118316168271544208
214
+ hash: 2964904468148201420
213
215
  requirements: []
214
216
  rubyforge_project:
215
217
  rubygems_version: 1.8.23
@@ -217,6 +219,7 @@ signing_key:
217
219
  specification_version: 3
218
220
  summary: Use HAL APIs easily
219
221
  test_files:
222
+ - spec/hal_client/collection_spec.rb
220
223
  - spec/hal_client/curie_resolver_spec.rb
221
224
  - spec/hal_client/representation_set_spec.rb
222
225
  - spec/hal_client/representation_spec.rb