wrest 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/README.rdoc +31 -17
  2. data/Rakefile +147 -134
  3. data/VERSION.yml +1 -1
  4. data/bin/jwrest +3 -0
  5. data/bin/wrest +1 -20
  6. data/bin/wrest_shell.rb +21 -0
  7. data/lib/wrest/components.rb +4 -1
  8. data/lib/wrest/components/attributes_container.rb +65 -10
  9. data/lib/wrest/{translators/xml.rb → components/mutators.rb} +11 -13
  10. data/lib/wrest/components/mutators/base.rb +43 -0
  11. data/lib/wrest/components/mutators/camel_to_snake_case.rb +20 -0
  12. data/lib/wrest/components/mutators/xml_simple_type_caster.rb +35 -0
  13. data/lib/wrest/{translators.rb → components/translators.rb} +6 -7
  14. data/lib/wrest/{translators → components/translators}/content_types.rb +5 -5
  15. data/lib/wrest/components/translators/json.rb +22 -0
  16. data/lib/wrest/components/translators/xml.rb +26 -0
  17. data/lib/wrest/components/typecast_helpers.rb +41 -0
  18. data/lib/wrest/core_ext/hash.rb +5 -0
  19. data/lib/wrest/core_ext/hash/conversions.rb +44 -0
  20. data/lib/wrest/exceptions.rb +6 -1
  21. data/lib/wrest/exceptions/method_not_overridden_exception.rb +17 -0
  22. data/lib/wrest/exceptions/unsupported_content_type_exception.rb +11 -9
  23. data/lib/wrest/resource/base.rb +5 -1
  24. data/lib/wrest/response.rb +2 -2
  25. data/lib/wrest/uri.rb +3 -3
  26. data/lib/wrest/version.rb +1 -1
  27. data/spec/spec_helper.rb +2 -1
  28. data/spec/wrest/components/attributes_container_spec.rb +66 -11
  29. data/spec/wrest/components/mutators/base_spec.rb +38 -0
  30. data/spec/wrest/components/mutators/camel_to_snake_spec.rb +22 -0
  31. data/spec/wrest/components/mutators/xml_simple_type_caster_spec.rb +47 -0
  32. data/spec/wrest/{translators → components/translators}/xml_spec.rb +3 -3
  33. data/spec/wrest/components/translators_spec.rb +9 -0
  34. data/spec/wrest/core_ext/hash/conversions_spec.rb +22 -0
  35. data/{lib/wrest/translators/json.rb → spec/wrest/core_ext/string/conversions_spec.rb} +4 -9
  36. data/spec/wrest/resource/base_spec.rb +1 -1
  37. data/spec/wrest/response_spec.rb +3 -3
  38. data/spec/wrest/uri_spec.rb +2 -2
  39. metadata +34 -19
  40. data/lib/wrest/translators/typed_hash.rb +0 -4
  41. data/spec/wrest/core_ext/string_spec.rb +0 -7
  42. data/spec/wrest/translators/typed_hash_spec.rb +0 -9
  43. data/spec/wrest/translators_spec.rb +0 -9
@@ -1 +1,6 @@
1
- require "#{WREST_ROOT}/wrest/exceptions/unsupported_content_type_exception"
1
+ module Wrest
2
+ module Exceptions #:nodoc:
3
+ end
4
+ end
5
+ require "#{WREST_ROOT}/wrest/exceptions/unsupported_content_type_exception"
6
+ require "#{WREST_ROOT}/wrest/exceptions/method_not_overridden_exception"
@@ -0,0 +1,17 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ module Wrest
11
+ module Exceptions
12
+ # Raised when a base method that should be overriden
13
+ # is invoked on a subclass that has not implemented it.
14
+ class MethodNotOverriddenException < StandardError
15
+ end
16
+ end
17
+ end
@@ -1,15 +1,17 @@
1
1
  # Copyright 2009 Sidu Ponnappa
2
2
 
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
- # Unless required by applicable law or agreed to in writing, software distributed under the License
7
- # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
- # See the License for the specific language governing permissions and limitations under the License.
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
9
 
10
10
  module Wrest
11
- # Raised when a translator for an unregisterd response content type
12
- # is requested. See Translators.
13
- class UnsupportedContentTypeException < StandardError
11
+ module Exceptions
12
+ # Raised when a translator for an unregisterd response content type
13
+ # is requested. See Translators.
14
+ class UnsupportedContentTypeException < StandardError
15
+ end
14
16
  end
15
17
  end
@@ -14,6 +14,7 @@ module Wrest::Resource #:nodoc:
14
14
  include Wrest::Components::AttributesContainer
15
15
 
16
16
  has_attributes :id
17
+ typecast :id => as_integer
17
18
  attr_reader :attributes
18
19
 
19
20
  class << self
@@ -40,7 +41,10 @@ module Wrest::Resource #:nodoc:
40
41
  def set_host(host)
41
42
  self.class_eval "def self.host; '#{host}';end"
42
43
  end
43
-
44
+
45
+ def set_redirect_handler(method_object)
46
+ end
47
+
44
48
  def resource_path
45
49
  @resource_path ||= "/#{resource_name.underscore.pluralize}"
46
50
  end
@@ -28,11 +28,11 @@ module Wrest #:nodoc:
28
28
  end
29
29
 
30
30
  def deserialise
31
- deserialise_using(Wrest::Translators.load(@http_response.content_type))
31
+ deserialise_using(Wrest::Components::Translators.lookup(@http_response.content_type))
32
32
  end
33
33
 
34
34
  def deserialise_using(translator)
35
- translator.call(@http_response)
35
+ translator.deserialise(@http_response)
36
36
  end
37
37
  end
38
38
  end
data/lib/wrest/uri.rb CHANGED
@@ -27,7 +27,7 @@ module Wrest #:nodoc:
27
27
  end
28
28
 
29
29
  def hash
30
- self.uri.hash + self.class.object_id
30
+ self.uri.hash + 20090423
31
31
  end
32
32
 
33
33
  # Make a HTTP get request to this URI.
@@ -44,8 +44,8 @@ module Wrest #:nodoc:
44
44
  do_request 'post', @uri.request_uri, body.to_s, headers.stringify_keys
45
45
  end
46
46
 
47
- def delete(headers = {})
48
- do_request 'delete', @uri.request_uri, headers.stringify_keys
47
+ def delete(parameters = {}, headers = {})
48
+ do_request 'delete', parameters.empty? ? @uri.request_uri : "#{@uri.request_uri}?#{parameters.to_query}", headers.stringify_keys
49
49
  end
50
50
 
51
51
  def do_request(method, url, *args)
data/lib/wrest/version.rb CHANGED
@@ -12,7 +12,7 @@ module Wrest
12
12
  unless defined? MAJOR
13
13
  MAJOR = 0
14
14
  MINOR = 0
15
- TINY = 5
15
+ TINY = 6
16
16
 
17
17
  STRING = [MAJOR, MINOR, TINY].join('.')
18
18
 
data/spec/spec_helper.rb CHANGED
@@ -10,7 +10,8 @@ require 'spec'
10
10
  Wrest.logger = Logger.new(File.open("#{WREST_ROOT}/../log/test.log", 'a'))
11
11
 
12
12
  def p(*args)
13
- super *(args << caller[0])
13
+ # super *(args << caller[0])
14
+ super *(args << '<br>')
14
15
  end
15
16
 
16
17
  Spec::Runner.configure do |config|
@@ -20,6 +20,61 @@ module Wrest::Components
20
20
  lambda{ HumanBeing.new }.should_not raise_error
21
21
  end
22
22
 
23
+ describe 'typecast' do
24
+ before :each do
25
+ @Demon = Class.new
26
+ @Demon.class_eval{include AttributesContainer}
27
+ end
28
+
29
+ it "should know how to apply a lambda to the string value of a given key casting it to a new type" do
30
+ @Demon.class_eval{ typecast :age => lambda{|id_string| id_string.to_i} }
31
+ kai_wren = @Demon.new('age' => '1')
32
+ kai_wren.age.should == 1
33
+ end
34
+
35
+ it "should not apply a lambda to the value of a given key if it is not a string" do
36
+ @Demon.class_eval{ typecast :age => lambda{|id_string| id_string.to_i} }
37
+ kai_wren = @Demon.new('age' => :ooga)
38
+ kai_wren.age.should == :ooga
39
+ end
40
+
41
+ it "should leave nils unchanged" do
42
+ @Demon.class_eval{ typecast :age => lambda{|id_string| id_string.to_i} }
43
+ kai_wren = @Demon.new('age' => nil)
44
+ kai_wren.age.should be_nil
45
+ end
46
+
47
+ it "should provide helpers for typcasting common types" do
48
+ @Demon.class_eval{ typecast :age => as_integer }
49
+ kai_wren = @Demon.new('age' => '1500')
50
+ kai_wren.age.should == 1500
51
+ end
52
+
53
+ describe 'in subclasses' do
54
+ before :each do
55
+ @Sidhe = Class.new
56
+ @Sidhe.class_eval{
57
+ include AttributesContainer
58
+ typecast :age => as_integer
59
+ }
60
+ end
61
+
62
+ it "should inherit all defined typecasts" do
63
+ @ChineseSidhe = Class.new(@Sidhe)
64
+ kai_wren = @ChineseSidhe.new('age' => '1500')
65
+ kai_wren.age.should == 1500
66
+ end
67
+
68
+ it "should discard all typecasts from parent if defined in child" do
69
+ @ChineseSidhe = Class.new(@Sidhe)
70
+ @ChineseSidhe.class_eval{ typecast :born_in => as_integer }
71
+ kai_wren = @ChineseSidhe.new('age' => '1500', 'born_in' => '509')
72
+ kai_wren.age.should == '1500'
73
+ kai_wren.born_in.should == 509
74
+ end
75
+ end
76
+ end
77
+
23
78
  describe 'has_attributes' do
24
79
  describe 'method creation' do
25
80
  before :each do
@@ -68,7 +123,7 @@ module Wrest::Components
68
123
  @Demon.class_eval{
69
124
  include AttributesContainer
70
125
  has_attributes :trainer
71
-
126
+
72
127
  def method_missing(method_name, *args)
73
128
  # Ensuring that the instance level
74
129
  # attribute methods don't kick in
@@ -107,16 +162,6 @@ module Wrest::Components
107
162
  @li_piao['profession'].should == "Natural Magician"
108
163
  end
109
164
 
110
- it "should provide a generic key based getter that understands symbols" do
111
- @li_piao[:enhanced_by] = "Viss"
112
- @li_piao.instance_variable_get('@attributes')[:enhanced_by].should == "Viss"
113
- end
114
-
115
- it "should provide a generic key based getter that translates strings to symbols" do
116
- @li_piao['enhanced_by'] = "Viss"
117
- @li_piao.instance_variable_get('@attributes')[:enhanced_by].should == "Viss"
118
- end
119
-
120
165
  it "should fail when getter methods for attributes that don't exist are invoked" do
121
166
  lambda{ @li_piao.ooga }.should raise_error(NoMethodError)
122
167
  end
@@ -135,6 +180,16 @@ module Wrest::Components
135
180
  @li_piao.should_not respond_to(:gods)
136
181
  end
137
182
 
183
+ it "should provide a generic key based setter that understands symbols" do
184
+ @li_piao[:enhanced_by] = "Viss"
185
+ @li_piao.instance_variable_get('@attributes')[:enhanced_by].should == "Viss"
186
+ end
187
+
188
+ it "should provide a generic key based setter that translates strings to symbols" do
189
+ @li_piao['enhanced_by'] = "Viss"
190
+ @li_piao.instance_variable_get('@attributes')[:enhanced_by].should == "Viss"
191
+ end
192
+
138
193
  it "should create a setter method when one is invoked for attributes that don't exist" do
139
194
  @li_piao.niece = 'Li Plum'
140
195
  @li_piao.instance_variable_get('@attributes')[:niece].should == 'Li Plum'
@@ -0,0 +1,38 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ require File.dirname(__FILE__) + '/../../../spec_helper'
11
+
12
+ module Wrest::Components
13
+ describe Mutators::Base do
14
+ it "should raise an exception if mutate is invoked without do_mutate being implemented in a subclass" do
15
+ lambda{ Class.new(Mutators::Base).new.mutate([]) }.should raise_error(Wrest::Exceptions::MethodNotOverriddenException)
16
+ end
17
+
18
+ it "should ensure that the next mutator is invoked for a subclass" do
19
+ next_mutator = mock('Mutator')
20
+ mutator = Mutators::CamelToSnakeCase.new(next_mutator)
21
+
22
+ next_mutator.should_receive(:mutate).with(['a', 1]).and_return([:a, '1'])
23
+
24
+ mutator.mutate(['a', 1]).should == [:a, '1']
25
+ end
26
+
27
+ it "should know how to chain mutators recursively" do
28
+ mutator = Mutators::XmlSimpleTypeCaster.new(Mutators::CamelToSnakeCase.new)
29
+ mutator.mutate(
30
+ ["Result", [{
31
+ "Publish-Date"=>["1240326000"],
32
+ "News-Source"=>[{"Online" => ["PC via News"], "Unique-Id" => [{"type"=>"integer", "content"=>"1"}]}]
33
+ }]]
34
+ ).should == ["result", {"publish_date" => "1240326000", "news_source" => {"online"=>"PC via News", "unique_id"=>1}}]
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ require File.dirname(__FILE__) + '/../../../spec_helper'
11
+
12
+ module Wrest::Components
13
+ describe Mutators::CamelToSnakeCase do
14
+ before(:each) do
15
+ @mutator = Mutators::CamelToSnakeCase.new
16
+ end
17
+
18
+ it "should underscore the key in a tuple" do
19
+ @mutator.mutate(["universe-id", "1"]).should == ["universe_id", "1"]
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,47 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ require File.dirname(__FILE__) + '/../../../spec_helper'
11
+
12
+ module Wrest::Components
13
+ describe Mutators::XmlSimpleTypeCaster do
14
+ before(:each) do
15
+ @mutator = Mutators::XmlSimpleTypeCaster.new
16
+ end
17
+
18
+ # {"lead-bottle"=>[{"name"=>["Wooz"], "universe-id"=>[{"type"=>"integer", "nil"=>"true"}], "id"=>[{"type"=>"integer", "content"=>"1"}]}]}
19
+
20
+ it "should typecast a nil value in a tuple" do
21
+ @mutator.mutate(
22
+ ["universe-id", [{"type"=>"integer", "nil"=>"true"}]]
23
+ ).should == ["universe-id", nil]
24
+ end
25
+
26
+ it "should leave a string value in a tuple unchanged" do
27
+ @mutator.mutate(
28
+ ["name", ["Wooz"]]
29
+ ).should == ["name", "Wooz"]
30
+ end
31
+
32
+ it "should cast an integer value in a tuple" do
33
+ @mutator.mutate(
34
+ ["id", [{"type"=>"integer", "content"=>"1"}]]
35
+ ).should == ["id", 1]
36
+ end
37
+
38
+ it "should step into a value if it is a hash" do
39
+ @mutator.mutate(
40
+ ["Result", [{
41
+ "PublishDate"=>["1240326000"],
42
+ "NewsSource"=>[{"Online" => ["PC via News"], "UniqueId" => [{"type"=>"integer", "content"=>"1"}]}]
43
+ }]]
44
+ ).should == ["Result", {"PublishDate" => "1240326000", "NewsSource" => {"Online"=>"PC via News", "UniqueId"=>1}}]
45
+ end
46
+ end
47
+ end
@@ -1,12 +1,12 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
2
 
3
- module Wrest::Translators
3
+ module Wrest::Components::Translators
4
4
  describe Xml do
5
5
  it "should know how to convert xml to a hashmap" do
6
6
  http_response = mock('Http Reponse')
7
7
  http_response.should_receive(:body).and_return("<ooga><age>12</age></ooga>")
8
8
 
9
- Xml.call(http_response).should == {"ooga"=>[{"age"=>["12"]}]}
9
+ Xml.deserialise(http_response).should == {"ooga"=>[{"age"=>["12"]}]}
10
10
  end
11
11
  end
12
12
  end
@@ -0,0 +1,9 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ module Wrest::Components
4
+ describe Translators do
5
+ it "should know how to raise an exception if the mime type doesn't exist" do
6
+ lambda{ Translators.lookup('weird/unknown')}.should raise_error(Wrest::Exceptions::UnsupportedContentTypeException)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ require File.dirname(__FILE__) + '/../../../spec_helper'
11
+
12
+ describe Hash, 'conversions' do
13
+ it "should know how to build a mutated hash given a hash mutator" do
14
+ class StringToSymbolMutator < Wrest::Components::Mutators::Base
15
+ def mutate(pair)
16
+ [pair.first.to_sym, pair.last]
17
+ end
18
+ end
19
+
20
+ {'ooga' => 'booga'}.mutate_using(StringToSymbolMutator.new).should == {:ooga => 'booga'}
21
+ end
22
+ end
@@ -7,15 +7,10 @@
7
7
  # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
8
  # See the License for the specific language governing permissions and limitations under the License.
9
9
 
10
- require 'json'
10
+ require File.dirname(__FILE__) + '/../../../spec_helper'
11
11
 
12
- module Wrest
13
- module Translators
14
- # Knows how to deserialise json.
15
- # Depends on the json gem.
16
- Json = lambda{|response|
17
- JSON.parse(response.body)
18
- }
12
+ describe String, 'conversions' do
13
+ it "should know how to convert a string to a Wrest::Uri" do
14
+ 'http://localhost:3000'.to_uri.should == Wrest::Uri.new('http://localhost:3000')
19
15
  end
20
16
  end
21
-
@@ -38,7 +38,7 @@ module Wrest
38
38
  it "should know how to create an instance using deserialised attributes" do
39
39
  # Json => {"lead_bottle"=>{"name"=>"Wooz", "id"=>1, "universe_id"=>nil}}
40
40
  # Xml => {"lead-bottle"=>[{"name"=>["Wooz"], "universe-id"=>[{"type"=>"integer", "nil"=>"true"}], "id"=>[{"type"=>"integer", "content"=>"1"}]}]}
41
- universe = @BottledUniverse.new "name"=>"Wooz", "id"=>1, "universe_id"=>nil, 'owner_id'=>nil
41
+ universe = @BottledUniverse.new "name"=>"Wooz", "id"=>'1', "universe_id"=>nil, 'owner_id'=>nil
42
42
  universe.name.should == "Wooz"
43
43
  universe.owner_id.should be_nil
44
44
  universe.id.should == 1
@@ -4,8 +4,8 @@ module Wrest
4
4
  describe Response do
5
5
  it "should know how to delegate to a translator" do
6
6
  http_response = mock('response')
7
- Translators::Xml.should_receive(:call).with(http_response)
8
- Response.new(http_response).deserialise_using(Translators::Xml)
7
+ Components::Translators::Xml.should_receive(:deserialise).with(http_response)
8
+ Response.new(http_response).deserialise_using(Components::Translators::Xml)
9
9
  end
10
10
 
11
11
  it "should know how to load a translator based on content type" do
@@ -13,7 +13,7 @@ module Wrest
13
13
  http_response.should_receive(:content_type).and_return('application/xml')
14
14
 
15
15
  response = Response.new(http_response)
16
- response.should_receive(:deserialise_using).with(Translators::Xml)
16
+ response.should_receive(:deserialise_using).with(Components::Translators::Xml)
17
17
 
18
18
  response.deserialise
19
19
  end