kaiwren-wrest 0.0.6 → 0.0.8

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 (51) hide show
  1. data/README.rdoc +83 -31
  2. data/Rakefile +29 -13
  3. data/VERSION.yml +1 -1
  4. data/examples/delicious.rb +58 -0
  5. data/examples/wow_realm_status.rb +57 -0
  6. data/lib/wrest.rb +11 -2
  7. data/lib/wrest/components.rb +1 -2
  8. data/lib/wrest/components/attributes_container.rb +34 -74
  9. data/lib/wrest/components/attributes_container/typecaster.rb +121 -0
  10. data/lib/wrest/components/mutators.rb +18 -1
  11. data/lib/wrest/components/mutators/base.rb +52 -39
  12. data/lib/wrest/components/mutators/camel_to_snake_case.rb +7 -5
  13. data/lib/wrest/components/mutators/xml_mini_type_caster.rb +43 -0
  14. data/lib/wrest/components/mutators/xml_simple_type_caster.rb +22 -20
  15. data/lib/wrest/components/translators.rb +20 -17
  16. data/lib/wrest/components/translators/content_types.rb +2 -2
  17. data/lib/wrest/components/translators/json.rb +11 -8
  18. data/lib/wrest/components/translators/xml.rb +9 -12
  19. data/lib/wrest/core_ext/hash/conversions.rb +1 -1
  20. data/lib/wrest/core_ext/string/conversions.rb +2 -2
  21. data/lib/wrest/http.rb +24 -0
  22. data/lib/wrest/http/delete.rb +23 -0
  23. data/lib/wrest/http/get.rb +23 -0
  24. data/lib/wrest/http/options.rb +23 -0
  25. data/lib/wrest/http/post.rb +23 -0
  26. data/lib/wrest/http/put.rb +23 -0
  27. data/lib/wrest/http/request.rb +48 -0
  28. data/lib/wrest/http/response.rb +44 -0
  29. data/lib/wrest/resource/base.rb +52 -25
  30. data/lib/wrest/resource/state.rb +6 -0
  31. data/lib/wrest/uri.rb +85 -29
  32. data/lib/wrest/uri_template.rb +18 -1
  33. data/lib/wrest/version.rb +1 -1
  34. data/spec/spec.opts +1 -1
  35. data/spec/spec_helper.rb +8 -1
  36. data/spec/wrest/components/attributes_container/typecaster_spec.rb +63 -0
  37. data/spec/wrest/components/attributes_container_spec.rb +17 -61
  38. data/spec/wrest/components/mutators/base_spec.rb +5 -1
  39. data/spec/wrest/components/mutators/xml_mini_type_caster_spec.rb +75 -0
  40. data/spec/wrest/components/mutators_spec.rb +21 -0
  41. data/spec/wrest/components/translators/xml_spec.rb +1 -1
  42. data/spec/wrest/components/translators_spec.rb +9 -0
  43. data/spec/wrest/core_ext/string/conversions_spec.rb +9 -0
  44. data/spec/wrest/http/request_spec.rb +22 -0
  45. data/spec/wrest/{response_spec.rb → http/response_spec.rb} +4 -4
  46. data/spec/wrest/resource/base_spec.rb +126 -11
  47. data/spec/wrest/uri_spec.rb +124 -20
  48. data/spec/wrest/uri_template_spec.rb +11 -1
  49. metadata +33 -20
  50. data/lib/wrest/components/typecast_helpers.rb +0 -41
  51. data/lib/wrest/response.rb +0 -38
@@ -19,9 +19,26 @@ module Wrest
19
19
  # the corressponding values.
20
20
  #
21
21
  # Example:
22
- # template = UriTemplate.new("http://localhost:3000/:resource/:id.:format")
22
+ # template = UriTemplate.new("http://coathangers.com/:resource/:id.:format")
23
23
  # template.to_uri(:resource => 'shen_coins', :id => 5, :format => :json)
24
24
  # => #<Wrest::Uri:0x1225514 @uri=#<URI::HTTP:0x9127d8 URL:http://localhost:3000/shen_coins/5.json>>
25
+ #
26
+ # This feature can also be used to handle HTTP authentication where the username
27
+ # and password needs changing at runtime. However, this approach _will_ fail if
28
+ # the password contains characters like ^ and @.
29
+ #
30
+ # Note that beacuse because both HTTP Auth and UriTemplate
31
+ # use ':' as a delimiter, the pattern does look slightly weird, but it still works.
32
+ #
33
+ # Example:
34
+ # template = UriTemplate.new("http://:username::password@coathangers.com/:resource/:id.:format")
35
+ # template.to_uri(
36
+ # :user => 'kaiwren',
37
+ # :password => 'fupuppies',
38
+ # :resource => 'portal',
39
+ # :id => '1'
40
+ # )
41
+ # => #<Wrest::Uri:0x18e0bec @uri=#<URI::HTTP:0x18e09a8 URL:http://kaiwren:fupuppies@coathangers.com/portal/1>>
25
42
  def to_uri(options = {})
26
43
  options.inject(uri_pattern.clone) do |uri_string, tuple|
27
44
  key, value = tuple
@@ -12,7 +12,7 @@ module Wrest
12
12
  unless defined? MAJOR
13
13
  MAJOR = 0
14
14
  MINOR = 0
15
- TINY = 6
15
+ TINY = 8
16
16
 
17
17
  STRING = [MAJOR, MINOR, TINY].join('.')
18
18
 
@@ -1,6 +1,6 @@
1
1
  --colour
2
2
  --format
3
- progress
3
+ specdoc
4
4
  --loadby
5
5
  mtime
6
6
  --reverse
@@ -11,7 +11,14 @@ Wrest.logger = Logger.new(File.open("#{WREST_ROOT}/../log/test.log", 'a'))
11
11
 
12
12
  def p(*args)
13
13
  # super *(args << caller[0])
14
- super *(args << '<br>')
14
+ super *(args << '<br/>')
15
+ # super *args
16
+ end
17
+
18
+ def puts(*args)
19
+ # super *(args << caller[0])
20
+ super *(args << '<br/>')
21
+ # super *args
15
22
  end
16
23
 
17
24
  Spec::Runner.configure do |config|
@@ -0,0 +1,63 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ module Wrest::Components
4
+ describe AttributesContainer::Typecaster do
5
+ before :each do
6
+ @Demon = Class.new
7
+ @Demon.class_eval do
8
+ include AttributesContainer
9
+ include AttributesContainer::Typecaster
10
+ end
11
+ end
12
+
13
+ it "should know how to apply a lambda to the string value of a given key casting it to a new type" do
14
+ @Demon.class_eval{ typecast :age => lambda{|id_string| id_string.to_i} }
15
+ kai_wren = @Demon.new('age' => '1')
16
+ kai_wren.age.should == 1
17
+ end
18
+
19
+ it "should not apply a lambda to the value of a given key if it is not a string" do
20
+ @Demon.class_eval{ typecast :age => lambda{|id_string| id_string.to_i} }
21
+ kai_wren = @Demon.new('age' => :ooga)
22
+ kai_wren.age.should == :ooga
23
+ end
24
+
25
+ it "should leave nils unchanged" do
26
+ @Demon.class_eval{ typecast :age => lambda{|id_string| id_string.to_i} }
27
+ kai_wren = @Demon.new('age' => nil)
28
+ kai_wren.age.should be_nil
29
+ end
30
+
31
+ it "should provide helpers for typcasting common types" do
32
+ @Demon.class_eval{ typecast :age => as_integer }
33
+ kai_wren = @Demon.new('age' => '1500')
34
+ kai_wren.age.should == 1500
35
+ end
36
+
37
+ describe 'in subclasses' do
38
+ before :each do
39
+ @Sidhe = Class.new
40
+ @Sidhe.class_eval do
41
+ include AttributesContainer
42
+ include AttributesContainer::Typecaster
43
+
44
+ typecast :age => as_integer
45
+ end
46
+ end
47
+
48
+ it "should inherit all defined typecasts" do
49
+ @ChineseSidhe = Class.new(@Sidhe)
50
+ kai_wren = @ChineseSidhe.new('age' => '1500')
51
+ kai_wren.age.should == 1500
52
+ end
53
+
54
+ it "should discard all typecasts from parent if defined in child" do
55
+ @ChineseSidhe = Class.new(@Sidhe)
56
+ @ChineseSidhe.class_eval{ typecast :born_in => as_integer }
57
+ kai_wren = @ChineseSidhe.new('age' => '1500', 'born_in' => '509')
58
+ kai_wren.age.should == '1500'
59
+ kai_wren.born_in.should == 509
60
+ end
61
+ end
62
+ end
63
+ end
@@ -13,69 +13,14 @@ module Wrest::Components
13
13
  describe AttributesContainer do
14
14
  class HumanBeing
15
15
  include AttributesContainer
16
- has_attributes :id
16
+ always_has :id
17
17
  end
18
18
 
19
19
  it "should allow instantiation with no attributes" do
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
-
78
- describe 'has_attributes' do
23
+ describe 'always_has' do
79
24
  describe 'method creation' do
80
25
  before :each do
81
26
  @Demon = Class.new
@@ -87,7 +32,7 @@ module Wrest::Components
87
32
 
88
33
  @Demon.class_eval{
89
34
  include AttributesContainer
90
- has_attributes :trainer
35
+ always_has :trainer
91
36
  }
92
37
 
93
38
  kai_wren.methods.should include('trainer')
@@ -99,7 +44,7 @@ module Wrest::Components
99
44
 
100
45
  @Demon.class_eval{
101
46
  include AttributesContainer
102
- has_attributes :trainer
47
+ always_has :trainer
103
48
  }
104
49
 
105
50
  kai_wren.methods.should include('trainer=')
@@ -111,7 +56,7 @@ module Wrest::Components
111
56
 
112
57
  @Demon.class_eval{
113
58
  include AttributesContainer
114
- has_attributes :trainer
59
+ always_has :trainer
115
60
  }
116
61
  kai_wren.methods.should include('trainer?')
117
62
  end
@@ -122,7 +67,7 @@ module Wrest::Components
122
67
  @Demon = Class.new
123
68
  @Demon.class_eval{
124
69
  include AttributesContainer
125
- has_attributes :trainer
70
+ always_has :trainer
126
71
 
127
72
  def method_missing(method_name, *args)
128
73
  # Ensuring that the instance level
@@ -234,6 +179,17 @@ module Wrest::Components
234
179
  @li_piao.id = 6
235
180
  @li_piao.id.should == 6
236
181
  end
182
+
183
+ it "should provide getter and query methods to instance which has corresponding attribute" do
184
+ zotoh_zhaan = HumanBeing.new(:species => "Delvian")
185
+ zotoh_zhaan.species.should == "Delvian"
186
+ zotoh_zhaan.species?.should be_true
187
+ zotoh_zhaan.species = "Human"
188
+ lambda{@li_piao.species}.should raise_error(NoMethodError)
189
+ lambda{@li_piao.species?}.should raise_error(NoMethodError)
190
+ @li_piao.should_not respond_to(:species=)
191
+ @li_piao.methods.grep(/:species=/).should be_empty
192
+ end
237
193
  end
238
194
  end
239
195
  end
@@ -33,6 +33,10 @@ module Wrest::Components
33
33
  }]]
34
34
  ).should == ["result", {"publish_date" => "1240326000", "news_source" => {"online"=>"PC via News", "unique_id"=>1}}]
35
35
  end
36
-
36
+
37
+ it "should register all subclasses in the registry" do
38
+ class SomeMutator < Mutators::Base; end
39
+ Mutators::REGISTRY[:some_mutator].should == SomeMutator
40
+ end
37
41
  end
38
42
  end
@@ -0,0 +1,75 @@
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::XmlMiniTypeCaster do
14
+ before(:each) do
15
+ @mutator = Mutators::XmlMiniTypeCaster.new
16
+ end
17
+
18
+ # {"lead-bottle"=>{"name"=>{"__content__"=>"Wooz"}, "universe-id"=>{"type"=>"integer", "nil"=>"true"}, "id"=>{"__content__"=>"1", "type"=>"integer"}}}
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", {"__content__" => "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
+ ["ResultSet", {
41
+ "firstResultPosition"=>"1", "totalResultsReturned"=>"1",
42
+ "xsi:schemaLocation"=>"urn:ooga:on http://api.search.ooga.com/NewsSearchService/V1/NewsSearchResponse.xsd",
43
+ "totalResultsAvailable"=>"23287",
44
+ "Result"=>{
45
+ "UniqueId"=>{"__content__"=>"1", "type" => "integer"},
46
+ "PublishDate"=>{"__content__"=>"20090424", "type" => "date"},
47
+ "Language"=>{"__content__"=>"en"},
48
+ "Title"=>{"__content__"=>"Wootler: Wook focus should be Klingon, not India"},
49
+ "ClickUrls"=>[
50
+ {"One" => {"__content__"=>"http://news.ooga.com/s/ap/20090424/ap_on_go_ca_st_pe/us_us_wookieland_5"}},
51
+ {"Two" => {"__content__"=>"http://news.ooga.com/s/ap/20090424/ap_on_go_ca_st_pe/us_us_wookieland_6"}},
52
+ ]
53
+
54
+ }
55
+ }
56
+ ]
57
+ ).should == ["ResultSet", {
58
+ "firstResultPosition"=>"1", "totalResultsReturned"=>"1",
59
+ "xsi:schemaLocation"=>"urn:ooga:on http://api.search.ooga.com/NewsSearchService/V1/NewsSearchResponse.xsd",
60
+ "totalResultsAvailable"=>"23287",
61
+ "Result"=>{
62
+ "UniqueId"=> 1,
63
+ "PublishDate"=>Date.parse("20090424"),
64
+ "Language"=>"en",
65
+ "Title"=>"Wootler: Wook focus should be Klingon, not India",
66
+ "ClickUrls"=>[
67
+ {"One" => "http://news.ooga.com/s/ap/20090424/ap_on_go_ca_st_pe/us_us_wookieland_5"},
68
+ {"Two" => "http://news.ooga.com/s/ap/20090424/ap_on_go_ca_st_pe/us_us_wookieland_6"},
69
+ ]
70
+ }
71
+ }
72
+ ]
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,21 @@
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 do
14
+ it "should know how to chain mutators without having to namespace them all" do
15
+ mutator = Mutators.chain(:xml_mini_type_caster, :xml_simple_type_caster, :camel_to_snake_case)
16
+ mutator.class.should == Mutators::XmlMiniTypeCaster
17
+ mutator.next_mutator.class.should == Mutators::XmlSimpleTypeCaster
18
+ mutator.next_mutator.next_mutator.class.should == Mutators::CamelToSnakeCase
19
+ end
20
+ end
21
+ end
@@ -6,7 +6,7 @@ module Wrest::Components::Translators
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.deserialise(http_response).should == {"ooga"=>[{"age"=>["12"]}]}
9
+ Xml.deserialise(http_response).should == {"ooga"=>{"age"=>{"__content__" => "12"}}}
10
10
  end
11
11
  end
12
12
  end
@@ -1,3 +1,12 @@
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
+
1
10
  require File.dirname(__FILE__) + '/../../spec_helper'
2
11
 
3
12
  module Wrest::Components
@@ -13,4 +13,13 @@ describe String, 'conversions' do
13
13
  it "should know how to convert a string to a Wrest::Uri" do
14
14
  'http://localhost:3000'.to_uri.should == Wrest::Uri.new('http://localhost:3000')
15
15
  end
16
+
17
+ it "should accept username and password as options" do
18
+ uri = 'http://localhost:3000'.to_uri(
19
+ :username => 'ooga',
20
+ :password => 'booga'
21
+ )
22
+ uri.username.should == 'ooga'
23
+ uri.password.should == 'booga'
24
+ end
16
25
  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 Wrest::Http::Request do
13
+ it "should convert all symbols in header keys to strings" do
14
+ Wrest::Http::Request.new(
15
+ 'http://localhost/foo'.to_uri, Net::HTTP::Get, {},
16
+ nil, 'Content-Type' => 'application/xml', :per_page => '10'
17
+ ).headers.should == {
18
+ 'Content-Type' => 'application/xml',
19
+ 'per_page' => '10'
20
+ }
21
+ end
22
+ end
@@ -1,18 +1,18 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
2
 
3
3
  module Wrest
4
- describe Response do
4
+ describe Http::Response do
5
5
  it "should know how to delegate to a translator" do
6
6
  http_response = mock('response')
7
7
  Components::Translators::Xml.should_receive(:deserialise).with(http_response)
8
- Response.new(http_response).deserialise_using(Components::Translators::Xml)
8
+ Http::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
12
12
  http_response = mock('response')
13
13
  http_response.should_receive(:content_type).and_return('application/xml')
14
14
 
15
- response = Response.new(http_response)
15
+ response = Http::Response.new(http_response)
16
16
  response.should_receive(:deserialise_using).with(Components::Translators::Xml)
17
17
 
18
18
  response.deserialise