resto 0.0.3 → 0.0.4

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.
Files changed (47) hide show
  1. data/lib/blankslate.rb +110 -0
  2. data/lib/resto.rb +264 -0
  3. data/lib/resto/attributes.rb +56 -0
  4. data/lib/resto/extra/copy.rb +34 -0
  5. data/lib/resto/extra/delegation.rb +26 -0
  6. data/lib/resto/extra/hash_args.rb +56 -0
  7. data/lib/resto/format.rb +20 -0
  8. data/lib/resto/format/default.rb +11 -0
  9. data/lib/resto/format/json.rb +30 -0
  10. data/lib/resto/format/plain.rb +14 -0
  11. data/lib/resto/format/xml.rb +66 -0
  12. data/lib/resto/property.rb +50 -0
  13. data/lib/resto/property/handler.rb +57 -0
  14. data/lib/resto/property/integer.rb +29 -0
  15. data/lib/resto/property/string.rb +19 -0
  16. data/lib/resto/property/time.rb +43 -0
  17. data/lib/resto/request/base.rb +88 -0
  18. data/lib/resto/request/factory.rb +66 -0
  19. data/lib/resto/request/header.rb +58 -0
  20. data/lib/resto/request/option.rb +126 -0
  21. data/lib/resto/request/uri.rb +50 -0
  22. data/lib/resto/response/base.rb +85 -0
  23. data/lib/resto/translator/request_factory.rb +44 -0
  24. data/lib/resto/translator/response_factory.rb +28 -0
  25. data/lib/resto/validate.rb +37 -0
  26. data/lib/resto/validate/inclusion.rb +39 -0
  27. data/lib/resto/validate/length.rb +36 -0
  28. data/lib/resto/validate/presence.rb +24 -0
  29. data/lib/resto/version.rb +5 -0
  30. data/resto.gemspec +43 -0
  31. data/spec/resto/extra/copy_spec.rb +58 -0
  32. data/spec/resto/extra/hash_args_spec.rb +71 -0
  33. data/spec/resto/format/default_spec.rb +24 -0
  34. data/spec/resto/format/json_spec.rb +29 -0
  35. data/spec/resto/format/plain_spec.rb +21 -0
  36. data/spec/resto/format/xml_spec.rb +105 -0
  37. data/spec/resto/property/handler_spec.rb +57 -0
  38. data/spec/resto/property/integer_spec.rb +67 -0
  39. data/spec/resto/property/time_spec.rb +124 -0
  40. data/spec/resto/property_spec.rb +60 -0
  41. data/spec/resto/request/base_spec.rb +253 -0
  42. data/spec/resto/request/factory_spec.rb +114 -0
  43. data/spec/resto/translator/response_factory_spec.rb +93 -0
  44. data/spec/resto/validate/presence_spec.rb +102 -0
  45. data/spec/resto_spec.rb +531 -0
  46. data/spec/spec_helper.rb +119 -0
  47. metadata +48 -3
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ class Article
5
+ def initialize; @errors = {}; end
6
+ def title_without_cast; " 200 "; end
7
+ def empty_title_without_cast; " "; end
8
+ def errors; @errors; end
9
+ def add_error(key, error); @errors.store(key, error); end
10
+ end
11
+
12
+ class AProperty;
13
+ include Resto::Property;
14
+ def cast(value, errors); value.to_i; end
15
+ end
16
+
17
+ describe Resto::Property::Handler do
18
+ let(:article) { Article.new }
19
+ let(:property) { AProperty.new(:title) }
20
+ let(:property2) { AProperty.new(:empty_title) }
21
+
22
+ context "#add(property)" do
23
+ before { subject.add(property) }
24
+
25
+ describe "#attribute_key(property_key)" do
26
+ it { subject.attribute_key('title').should == :title }
27
+ end
28
+
29
+ describe "cast(:property_key, '200', errors)" do
30
+ it { subject.cast(:title, '200', nil).should == 200 }
31
+ end
32
+
33
+ describe "#attribute_key('non_existing_title')" do
34
+ it { subject.attribute_key('non_existing_title').should == false }
35
+ end
36
+
37
+ context "#add(other_property)" do
38
+ describe "#validate(article)" do
39
+ before do
40
+ property.validate_presence
41
+ property2.validate_presence
42
+ subject.add(property2)
43
+ subject.validate(article)
44
+ end
45
+
46
+ context "then article has the following error" do
47
+ it { article.errors.fetch(:title_presence, false).should == nil }
48
+
49
+ it do
50
+ article.errors.fetch(:empty_title_presence, false)
51
+ .should == ":empty_title can’t be blank"
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,67 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'resto/property'
5
+
6
+ describe Resto::Property::Integer do
7
+
8
+ context "Resto::Property::Integer.new(:age)" do
9
+ let(:errors) { {} }
10
+ subject { Resto::Property::Integer.new(:age) }
11
+
12
+ context ".cast(20, errors)" do
13
+ it("returns 20 with no errors") do
14
+ subject.cast(20, errors).should == 20
15
+ errors.fetch(:age_integer, false).should == nil
16
+ end
17
+ end
18
+
19
+ context ".cast('22', errors)" do
20
+ it("returns 22 with no errors") do
21
+ subject.cast('22', errors).should == 22
22
+ errors.fetch(:age_integer, false).should == nil
23
+ end
24
+ end
25
+
26
+ context ".cast('', errors)" do
27
+ it("returns nil with no errors") do
28
+ subject.cast('', errors).should == nil
29
+ errors.fetch(:age_integer, false).should == nil
30
+ end
31
+ end
32
+ context ".cast(' ', errors)" do
33
+ it("returns nil with no errors") do
34
+ subject.cast(' ', errors).should == nil
35
+ errors.fetch(:age_integer, false).should == nil
36
+ end
37
+ end
38
+
39
+ context ".cast(nil, errors)" do
40
+ it("returns nil with no errors") do
41
+ subject.cast(nil, errors).should == nil
42
+ errors.fetch(:age_integer, false).should == nil
43
+ end
44
+ end
45
+
46
+ context ".cast('22q', errors)" do
47
+ it("returns nil and sets errors[:age_integer]") do
48
+ subject.cast('22q', errors).should == nil
49
+ errors.fetch(:age_integer, false).should == ':age is not an integer.'
50
+ end
51
+ end
52
+
53
+ context ".cast('q22', errors)" do
54
+ it("returns nil and sets errors[:age_integer]") do
55
+ subject.cast('q22', errors).should == nil
56
+ errors.fetch(:age_integer, false).should == ':age is not an integer.'
57
+ end
58
+ end
59
+ context ".cast('twenty', errors)" do
60
+ it("returns nil and sets errors[:age_integer]") do
61
+ subject.cast('twenty', errors).should == nil
62
+ errors.fetch(:age_integer, false).should == ':age is not an integer.'
63
+ end
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,124 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'resto/property'
5
+
6
+ describe Resto::Property::Time do
7
+ let(:iso_8601_plus) { "2010-10-25T11:48:00+00:00" } # UTC
8
+ let(:iso_8601_minus) { "2010-10-25T23:48:00-00:00" } # UTC
9
+ let(:iso_8601_z) { "2010-10-26T12:48:00Z" } # UTC
10
+ let(:iso_8601_z_summer) { "2010-05-26T12:48:00Z" } # UTC
11
+ let(:iso_8601_cet_summer) { "2011-05-01T14:46:00+02:00" } #GMT +2, UTC +2
12
+ let(:iso_8601_cet_winter) { "2011-01-01T14:46:00+01:00" } #GMT +1, UTC +1
13
+
14
+ let(:errors) { {} }
15
+ subject { Resto::Property::Time.new(:date_of_birth) }
16
+
17
+ describe ".cast(time_string, errors)" do
18
+ context "local time is 2010-10-25T12:00:00 PST" do
19
+ it "returns a time object parsed from the string" do
20
+ at_time("2010-10-25T12:00:00 PST", '-08:00') do #UTC -8
21
+ time = subject.cast(iso_8601_plus, errors)
22
+ time.iso8601.should == '2010-10-25T03:48:00-08:00'
23
+ time.utc.iso8601.should == '2010-10-25T11:48:00Z'
24
+ errors.fetch(:date_of_birth_time, false).should == nil
25
+ end
26
+ end
27
+
28
+ it "returns a time object parsed from the string" do
29
+ at_time("2010-10-25T12:00:00 PST", '-08:00') do #UTC -8
30
+ time = subject.cast(iso_8601_cet_winter, errors)
31
+ time.iso8601.should == '2011-01-01T05:46:00-08:00'
32
+ time.utc.iso8601.should == '2011-01-01T13:46:00Z'
33
+ errors.fetch(:date_of_birth_time, false).should == nil
34
+ end
35
+ end
36
+
37
+ it "returns a time object parsed from the string" do
38
+ at_time("2010-10-25T12:00:00 PST", '-08:00') do #UTC -8
39
+ time = subject.cast(iso_8601_cet_summer, errors)
40
+ time.iso8601.should == '2011-05-01T04:46:00-08:00'
41
+ time.utc.iso8601.should == '2011-05-01T12:46:00Z'
42
+ errors.fetch(:date_of_birth_time, false).should == nil
43
+ end
44
+ end
45
+
46
+ it "returns a time object parsed from the string" do
47
+ at_time("2010-10-25T12:00:00 PST", '-08:00') do #UTC -8
48
+ time = subject.cast(iso_8601_z_summer, errors)
49
+ time.localtime.iso8601.should == '2010-05-26T04:48:00-08:00'
50
+ time.utc.iso8601.should == '2010-05-26T12:48:00Z'
51
+ errors.fetch(:date_of_birth_time, false).should == nil
52
+ end
53
+ end
54
+ end
55
+
56
+ context ".cast('', errors)" do
57
+ it("returns nil with no errors") do
58
+ subject.cast('', errors).should == nil
59
+ errors.fetch(:date_of_birth_time, false).should == nil
60
+ end
61
+ end
62
+ context ".cast(' ', errors)" do
63
+ it("returns nil with no errors") do
64
+ subject.cast(' ', errors).should == nil
65
+ errors.fetch(:date_of_birth_time, false).should == nil
66
+ end
67
+ end
68
+
69
+ context ".cast(nil, errors)" do
70
+ it("returns nil with no errors") do
71
+ subject.cast(nil, errors).should == nil
72
+ errors.fetch(:date_of_birth_time, false).should == nil
73
+ end
74
+ end
75
+
76
+ context ".cast('q', errors)" do
77
+ it("returns nil and sets errors[:date_of_birth_time]") do
78
+ subject.cast('q', errors).should == nil
79
+ errors.fetch(:date_of_birth_time, false).should ==
80
+ ':date_of_birth is not a valid time format.'
81
+ end
82
+ end
83
+
84
+ context ".cast('22q', errors)" do
85
+ it("returns nil and sets errors[:date_of_birth_time]") do
86
+ subject.cast('22q', errors).should == nil
87
+ errors.fetch(:date_of_birth_time, false).should ==
88
+ ':date_of_birth is not a valid time format.'
89
+ end
90
+ end
91
+
92
+ context ".cast('q22', errors)" do
93
+ it("returns nil and sets errors[:date_of_birth_time]") do
94
+ subject.cast('q22', errors).should == nil
95
+ errors.fetch(:date_of_birth_time, false).should ==
96
+ ':date_of_birth is not a valid time format.'
97
+ end
98
+ end
99
+
100
+ context ".cast('twenty', errors)" do
101
+ it("returns nil and sets errors[:date_of_birth_time]") do
102
+ subject.cast('twenty', errors).should == nil
103
+ errors.fetch(:date_of_birth_time, false).should ==
104
+ ':date_of_birth is not a valid time format.'
105
+ end
106
+ end
107
+
108
+ context ".cast('2010-11-02T:1X', errors)" do
109
+ it("returns nil and sets errors[:date_of_birth_time]") do
110
+ subject.cast('2010-11-02T:1X', errors).should == nil
111
+ errors.fetch(:date_of_birth_time, false).should ==
112
+ ':date_of_birth is not a valid time format.'
113
+ end
114
+ end
115
+
116
+ context ".cast('22/02/2010 00:00:00 UTC', errors)" do
117
+ it("returns a time object parsed from the string") do
118
+ time = subject.cast('22/02/2010 00:00:00 UTC', errors)
119
+ time.utc.iso8601.should == '2010-02-22T00:00:00Z'
120
+ errors.fetch(:date_of_birth_time, false).should == nil
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ class Article
5
+ def initialize; @errors = {}; end
6
+ def title_without_cast; " 200 "; end
7
+ def empty_title_without_cast; " "; end
8
+ def errors; @errors; end
9
+ def add_error(key, error); @errors.store(key, error); end
10
+ end
11
+
12
+ class AProperty; include Resto::Property; end
13
+
14
+ describe Resto::Property do
15
+ let(:article) { Article.new }
16
+
17
+ context "AProperty.new(:title, :remote_name => :a_very_bad__title_name)" do
18
+ subject { AProperty.new(:title, :remote_name => :a_very_bad__title_name) }
19
+
20
+ its(:remote_key) { should == 'a_very_bad__title_name' }
21
+ its(:attribute_key) { should == :title }
22
+ its(:attribute_key_as_string) { should == 'title' }
23
+
24
+ context "when #validate_presence is called" do
25
+ before { subject.validate_presence }
26
+
27
+ describe "#validate(article, :title)" do
28
+ context "then article.errors" do
29
+
30
+ before { subject.validate(article, :title) }
31
+
32
+ it { article.errors[:title_presence].should == nil }
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ context "AProperty.new(:empty_title)" do
39
+ subject { AProperty.new(:empty_title) }
40
+
41
+ its(:remote_key) { should == 'empty_title' }
42
+ its(:attribute_key) { should == :empty_title }
43
+ its(:attribute_key_as_string) { should == 'empty_title' }
44
+
45
+ context "when #validate_presence is called" do
46
+ before { subject.validate_presence }
47
+
48
+ describe "#validate(article, :empty_title)" do
49
+ context "then article.errors" do
50
+ before { subject.validate(article, :empty_title) }
51
+
52
+ it {
53
+ article.errors[:empty_title_presence]
54
+ .should == ":empty_title can’t be blank"
55
+ }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,253 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+ require 'yajl'
5
+ require 'resto/request/base'
6
+
7
+ describe Resto::Request::Base do
8
+
9
+ context "Resto::Request::Base.new" do
10
+
11
+ its(:composed_headers) do
12
+ should == { "accept"=> "*/*", "user-agent"=> "Ruby" }
13
+ end
14
+
15
+ its(:current_formatter) { should == Resto::Format::Default }
16
+
17
+ its(:read_host) { should == nil }
18
+ its(:read_port) { should == 80 }
19
+ its(:composed_path) { should == '/' }
20
+
21
+ describe "@request" do
22
+ it do
23
+ subject.instance_eval { @request }
24
+ .should be_instance_of(Resto::Request::Factory)
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "#content_type('application/x-www-form-urlencoded')" do
30
+
31
+ before { subject.content_type('application/x-www-form-urlencoded') }
32
+
33
+ its(:composed_headers) do
34
+ should == { 'content-type' => 'application/x-www-form-urlencoded',
35
+ "accept"=> "*/*", "user-agent"=> "Ruby" }
36
+ end
37
+ end
38
+
39
+ context "#content_type('text/html').headers({'content-type' => 'text/plain',
40
+ 'User-agent' => 'Ruby' })" do
41
+ before do
42
+ subject.content_type('text/html')
43
+ .headers({'content-type' => 'text/plain', 'user-agent' => 'Ruby' })
44
+ end
45
+
46
+ its(:composed_headers) do
47
+ should == { 'content-type' => 'text/plain',
48
+ "accept"=> "*/*",
49
+ "user-agent"=> "Ruby" }
50
+ end
51
+ end
52
+
53
+ context "#basic_auth('username' => 'developer', 'password' => 'secret')
54
+ .content_type('text/html')" do
55
+
56
+ before do
57
+ subject.basic_auth('username' => "developer", "password" => "secret")
58
+ .content_type('text/html')
59
+ end
60
+
61
+ its(:composed_headers) do
62
+
63
+ should == { 'authorization' => basic_encode('developer', 'secret'),
64
+ 'content-type' => 'text/html',
65
+ "accept"=> "*/*",
66
+ "user-agent"=> "Ruby" }
67
+ end
68
+ end
69
+
70
+ context "#basic_auth('username' => 'developer', 'password' => 'secret')
71
+ .headers('content-type' => 'text/plain')" do
72
+
73
+ before { subject.headers('content-type' => 'text/plain') }
74
+
75
+ its(:composed_headers) do
76
+ should == { 'content-type' => 'text/plain',
77
+ "accept"=> "*/*",
78
+ "user-agent"=> "Ruby" }
79
+ end
80
+ end
81
+
82
+ context "#url('www.aftonbladet.se/')" do
83
+
84
+ before { subject.url('www.aftonbladet.se/') }
85
+
86
+ its(:read_host) { should == 'www.aftonbladet.se' }
87
+ its(:read_port) { should == 80 }
88
+ its(:composed_path) { should == '/' }
89
+ end
90
+
91
+ context "#url('www.aftonbladet.se')" do
92
+
93
+ before { subject.url('www.aftonbladet.se') }
94
+
95
+ its(:read_host) { should == 'www.aftonbladet.se' }
96
+ its(:read_port) { should == 80 }
97
+ its(:composed_path) { should == '/' }
98
+ end
99
+
100
+ context "#url('http://www.aftonbladet.se:92/customers')" do
101
+
102
+ before { subject.url('http://www.aftonbladet.se:92/customers') }
103
+
104
+ its(:read_host) { should == 'www.aftonbladet.se' }
105
+ its(:read_port) { should == 92 }
106
+ its(:composed_path) { should == '/customers' }
107
+ end
108
+
109
+ context "Resto::Request::Base.new()
110
+ .url('http://www.aftonbladet.se:92/customers)
111
+ .query('q=adam')
112
+ .params('longUrl' => 'http://betaworks.com', 'short' => 'htt')" do
113
+
114
+ before do
115
+ subject.url('http://www.aftonbladet.se:92/customers')
116
+ .query('q=adam')
117
+ .params('longUrl' => 'http://betaworks.com', 'short' => 'htt')
118
+ end
119
+
120
+ its(:read_host) { should == 'www.aftonbladet.se' }
121
+ its(:read_port) { should == 92 }
122
+ its(:composed_path) do
123
+ should == '/customers?q=adam&longUrl=http%3A%2F%2Fbetaworks.com&short=htt'
124
+ end
125
+ end
126
+
127
+ context "#port(40)
128
+ .url('http://www.aftonbladet.se:92/customers)
129
+ .query('q=adam')
130
+ .path('contacts')" do
131
+
132
+ before do
133
+ subject.port(40)
134
+ .url('http://www.aftonbladet.se:92/customers')
135
+ .query('q=adam')
136
+ .path('contacts')
137
+ end
138
+
139
+ its(:read_host) { should == 'www.aftonbladet.se' }
140
+ its(:read_port) { should == 40 }
141
+ its(:composed_path) { should == '/contacts?q=adam' }
142
+ end
143
+
144
+ context "#url('http://www.aftonbladet.se:92/customers/?q=adam')" do
145
+
146
+ before { subject.url('http://www.aftonbladet.se:92/customers/?q=adam') }
147
+
148
+ its(:read_host) { should == 'www.aftonbladet.se' }
149
+ its(:read_port) { should == 92 }
150
+ its(:composed_path) { should == '/customers/?q=adam' }
151
+ end
152
+
153
+ context "#url('http://www.aftonbladet.se:92/customers/?q=adam')
154
+ .append_path(1)" do
155
+
156
+ before do
157
+ subject.url('http://www.aftonbladet.se:92/customers/?q=adam')
158
+ .append_path(1)
159
+ end
160
+
161
+ its(:read_host) { should == 'www.aftonbladet.se' }
162
+ its(:read_port) { should == 92 }
163
+ its(:composed_path) { should == '/customers/1?q=adam' }
164
+ end
165
+
166
+ context "#url('http://www.aftonbladet.se:92/customers/?q=adam')
167
+ .query('q=take presendent')" do
168
+
169
+ before do
170
+ subject.url('http://www.aftonbladet.se:92/customers/?q=adam')
171
+ .query("q=take presendent")
172
+ end
173
+
174
+ its(:read_host) { should == 'www.aftonbladet.se' }
175
+ its(:read_port) { should == 92 }
176
+ its(:composed_path) { should == '/customers/?q=take presendent' }
177
+ end
178
+
179
+ context "#host('www.take_presedent.se')
180
+ .url('http://www.aftonbladet.se:92/customers/?q=adam')
181
+ .query('q=take presendent')" do
182
+
183
+ before do
184
+ subject.host('www.take-presedent.se')
185
+ .url('http://www.aftonbladet.se:92/customers/?q=adam')
186
+ .query('q=take presendent')
187
+ end
188
+
189
+ its(:read_host) { should == 'www.take-presedent.se' }
190
+ its(:read_port) { should == 92 }
191
+ its(:composed_path) { should == '/customers/?q=take presendent' }
192
+ end
193
+
194
+ context "Resto::Request::Base.new.body('body text')" do
195
+ before { subject.body('body text') }
196
+
197
+ its(:read_body) { should == 'body text' }
198
+ end
199
+
200
+ describe "#body" do
201
+
202
+ context "when formatter is Resto::Format::Default" do
203
+ before { subject.body('body text') }
204
+
205
+ its(:read_body) { should == 'body text' }
206
+ end
207
+
208
+ context "when formatter is Resto::Format::Json" do
209
+ before do
210
+ subject.format(:json)
211
+ .body( { :foo => 12425125, :bar => "some string" } )
212
+ end
213
+
214
+ its(:read_body) do
215
+ should == Yajl::Encoder.encode({ :foo => 12425125,
216
+ :bar => "some string" })
217
+ end
218
+ end
219
+ end
220
+
221
+ context "Resto::Request::Base.new(RequestKlass)" do
222
+ subject do
223
+ request_object = mock('request',
224
+ :head => "head",
225
+ :get => "get",
226
+ :post => "post",
227
+ :put => "put",
228
+ :delete => "delete")
229
+ Resto::Request::Base.new(mock('request_klass', :new => request_object))
230
+ end
231
+
232
+ it "delegates method :head to Request instance" do
233
+ subject.head.should == "head"
234
+ end
235
+
236
+ it "delegates method :get to Request instance" do
237
+ subject.get.should == "get"
238
+ end
239
+
240
+ it "delegates method :post to Request instance" do
241
+ subject.post.should == "post"
242
+ end
243
+
244
+ it "delegates method :put to Request instance" do
245
+ subject.put.should == "put"
246
+ end
247
+
248
+ it "delegates method :delete to Request instance" do
249
+ subject.delete.should == "delete"
250
+ end
251
+ end
252
+
253
+ end