wrest 0.0.5 → 0.0.6

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 (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