kaiwren-wrest 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/README.rdoc +104 -0
  2. data/Rakefile +203 -0
  3. data/VERSION.yml +4 -0
  4. data/bin/wrest +22 -0
  5. data/lib/wrest.rb +41 -0
  6. data/lib/wrest/core_ext/string.rb +5 -0
  7. data/lib/wrest/core_ext/string/conversions.rb +23 -0
  8. data/lib/wrest/exceptions.rb +1 -0
  9. data/lib/wrest/exceptions/unsupported_content_type_exception.rb +15 -0
  10. data/lib/wrest/mappers.rb +21 -0
  11. data/lib/wrest/mappers/attributes_container.rb +123 -0
  12. data/lib/wrest/mappers/resource.rb +17 -0
  13. data/lib/wrest/mappers/resource/base.rb +69 -0
  14. data/lib/wrest/mappers/resource/collection.rb +12 -0
  15. data/lib/wrest/mappers/simple_resource.rb +17 -0
  16. data/lib/wrest/response.rb +38 -0
  17. data/lib/wrest/translators.rb +26 -0
  18. data/lib/wrest/translators/content_types.rb +20 -0
  19. data/lib/wrest/translators/json.rb +21 -0
  20. data/lib/wrest/translators/typed_hash.rb +4 -0
  21. data/lib/wrest/translators/xml.rb +24 -0
  22. data/lib/wrest/uri.rb +74 -0
  23. data/lib/wrest/uri_template.rb +32 -0
  24. data/lib/wrest/version.rb +22 -0
  25. data/spec/custom_matchers/custom_matchers.rb +2 -0
  26. data/spec/rcov.opts +4 -0
  27. data/spec/spec.opts +6 -0
  28. data/spec/spec_helper.rb +18 -0
  29. data/spec/wrest/mappers/attributes_container_spec.rb +184 -0
  30. data/spec/wrest/mappers/resource/base_spec.rb +158 -0
  31. data/spec/wrest/mappers/simple_resource_spec.rb +7 -0
  32. data/spec/wrest/response_spec.rb +21 -0
  33. data/spec/wrest/translators/typed_hash_spec.rb +9 -0
  34. data/spec/wrest/translators/xml_spec.rb +12 -0
  35. data/spec/wrest/translators_spec.rb +9 -0
  36. data/spec/wrest/uri_spec.rb +131 -0
  37. data/spec/wrest/uri_template_spec.rb +28 -0
  38. metadata +128 -0
@@ -0,0 +1,32 @@
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
+ class UriTemplate
12
+ attr_reader :uri_pattern
13
+ def initialize(uri_pattern)
14
+ @uri_pattern = uri_pattern.clone
15
+ end
16
+
17
+ # Builds a new Wrest::Uri from this uri template
18
+ # by replacing the keys in the options that match with
19
+ # the corressponding values.
20
+ #
21
+ # Example:
22
+ # template = UriTemplate.new("http://localhost:3000/:resource/:id.:format")
23
+ # template.to_uri(:resource => 'shen_coins', :id => 5, :format => :json)
24
+ # => #<Wrest::Uri:0x1225514 @uri=#<URI::HTTP:0x9127d8 URL:http://localhost:3000/shen_coins/5.json>>
25
+ def to_uri(options = {})
26
+ options.inject(uri_pattern.clone) do |uri_string, tuple|
27
+ key, value = tuple
28
+ uri_string.gsub(":#{key.to_s}", value.to_s)
29
+ end.to_uri
30
+ end
31
+ end
32
+ 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
+ module Wrest
11
+ module VERSION
12
+ unless defined? MAJOR
13
+ MAJOR = 0
14
+ MINOR = 0
15
+ TINY = 4
16
+
17
+ STRING = [MAJOR, MINOR, TINY].join('.')
18
+
19
+ SUMMARY = "wrest version #{STRING}"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,2 @@
1
+ module CustomMatchers
2
+ end
@@ -0,0 +1,4 @@
1
+ --text-report
2
+ --html
3
+ --exclude-only ^\/usr,^\/Library,^\/System,spec\/
4
+ --output coverage
@@ -0,0 +1,6 @@
1
+ --colour
2
+ --format
3
+ progress
4
+ --loadby
5
+ mtime
6
+ --reverse
@@ -0,0 +1,18 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/wrest")
2
+ require 'spec'
3
+
4
+ ["/custom_matchers/**/*.rb"].each{|directory|
5
+ Dir["#{File.expand_path(File.dirname(__FILE__) + directory)}"].each { |file|
6
+ require file
7
+ }
8
+ }
9
+
10
+ Wrest.logger = Logger.new(File.open("#{WREST_ROOT}/../log/test.log", 'a'))
11
+
12
+ def p(*args)
13
+ super *(args << caller[0])
14
+ end
15
+
16
+ Spec::Runner.configure do |config|
17
+ config.include(CustomMatchers)
18
+ end
@@ -0,0 +1,184 @@
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::Mappers
13
+ describe AttributesContainer do
14
+ class HumanBeing
15
+ include AttributesContainer
16
+ has_attributes :id
17
+ end
18
+
19
+ it "should allow instantiation with no attributes" do
20
+ lambda{ HumanBeing.new }.should_not raise_error
21
+ end
22
+
23
+ describe 'has_attributes' do
24
+ describe 'method creation' do
25
+ before :each do
26
+ @Demon = Class.new
27
+ end
28
+
29
+ it "should define attribute getters at the class level" do
30
+ kai_wren = @Demon.new
31
+ kai_wren.methods.should_not include('trainer')
32
+
33
+ @Demon.class_eval{
34
+ include AttributesContainer
35
+ has_attributes :trainer
36
+ }
37
+
38
+ kai_wren.methods.should include('trainer')
39
+ end
40
+
41
+ it "should define attribute setters at the class level" do
42
+ kai_wren = @Demon.new
43
+ kai_wren.methods.should_not include('trainer=')
44
+
45
+ @Demon.class_eval{
46
+ include AttributesContainer
47
+ has_attributes :trainer
48
+ }
49
+
50
+ kai_wren.methods.should include('trainer=')
51
+ end
52
+
53
+ it "should define attribute query methods at the class level" do
54
+ kai_wren = @Demon.new
55
+ kai_wren.methods.should_not include('trainer?')
56
+
57
+ @Demon.class_eval{
58
+ include AttributesContainer
59
+ has_attributes :trainer
60
+ }
61
+ kai_wren.methods.should include('trainer?')
62
+ end
63
+ end
64
+
65
+ describe 'method functionality' do
66
+ before :each do
67
+ @Demon = Class.new
68
+ @Demon.class_eval{
69
+ include AttributesContainer
70
+ has_attributes :trainer
71
+
72
+ def method_missing(method_name, *args)
73
+ # Ensuring that the instance level
74
+ # attribute methods don't kick in
75
+ # by overriding method_missing
76
+ raise NoMethodError.new("Method #{method_name} was invoked, but doesn't exist", method_name)
77
+ end
78
+ }
79
+ @kai_wren = @Demon.new
80
+ end
81
+
82
+ it "should define attribute getters at the class level" do
83
+ @kai_wren.instance_variable_get("@attributes")[:trainer] = 'Viss'
84
+ @kai_wren.trainer.should == 'Viss'
85
+ end
86
+
87
+ it "should define attribute setters at the class level" do
88
+ @kai_wren.trainer = 'Viss'
89
+ @kai_wren.instance_variable_get("@attributes")[:trainer].should == 'Viss'
90
+ end
91
+
92
+ it "should define attribute query methods at the class level" do
93
+ @kai_wren.trainer?.should be_false
94
+ @kai_wren.instance_variable_get("@attributes")[:trainer] = 'Viss'
95
+ @kai_wren.trainer?.should be_true
96
+ end
97
+ end
98
+ end
99
+
100
+ describe 'provides an attributes interface which' do
101
+ before :each do
102
+ @li_piao = HumanBeing.new(:id => 5, :profession => 'Natural Magician', 'enhanced_by' => 'Kai Wren')
103
+ end
104
+
105
+ it "should provide a generic key based getter that requires symbols" do
106
+ @li_piao[:profession].should == "Natural Magician"
107
+ @li_piao['profession'].should == "Natural Magician"
108
+ end
109
+
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
+ it "should fail when getter methods for attributes that don't exist are invoked" do
121
+ lambda{ @li_piao.ooga }.should raise_error(NoMethodError)
122
+ end
123
+
124
+ it "should provide getter methods for attributes" do
125
+ @li_piao.profession.should == 'Natural Magician'
126
+ @li_piao.enhanced_by.should == 'Kai Wren'
127
+ end
128
+
129
+ it "should respond to getter methods for attributes" do
130
+ @li_piao.should respond_to(:profession)
131
+ @li_piao.should respond_to(:enhanced_by)
132
+ end
133
+
134
+ it "should not respond to getter methods for attributes that don't exist" do
135
+ @li_piao.should_not respond_to(:gods)
136
+ end
137
+
138
+ it "should create a setter method when one is invoked for attributes that don't exist" do
139
+ @li_piao.niece = 'Li Plum'
140
+ @li_piao.instance_variable_get('@attributes')[:niece].should == 'Li Plum'
141
+ @li_piao.niece.should == 'Li Plum'
142
+ end
143
+
144
+ it "should provide setter methods for attributes" do
145
+ @li_piao.enhanced_by = 'He of the Towers of Light'
146
+ @li_piao.instance_variable_get('@attributes')[:enhanced_by].should == 'He of the Towers of Light'
147
+ end
148
+
149
+ it "should respond to setter methods for attributes" do
150
+ @li_piao.should respond_to(:profession=)
151
+ @li_piao.should respond_to(:enhanced_by=)
152
+ end
153
+
154
+ it "should not respond to setter methods for attributes that don't exist" do
155
+ @li_piao.should_not respond_to(:god=)
156
+ end
157
+
158
+ it "should fail when query methods for attributes that don't exist are invoked" do
159
+ lambda{ @li_piao.ooga? }.should raise_error(NoMethodError)
160
+ end
161
+
162
+ it "should provide query methods for attributes" do
163
+ li_piao = HumanBeing.new( :profession => 'Natural Magician', :enhanced_by => nil)
164
+ li_piao.profession?.should be_true
165
+ li_piao.enhanced_by?.should be_false
166
+ end
167
+
168
+ it "should respond to query methods for attributes" do
169
+ @li_piao.should respond_to(:profession?)
170
+ @li_piao.should respond_to(:enhanced_by?)
171
+ end
172
+
173
+ it "should not respond to query methods for attributes that don't exist" do
174
+ @li_piao.should_not respond_to(:theronic?)
175
+ end
176
+
177
+ it "should override methods which already exist on the container" do
178
+ @li_piao.id.should == 5
179
+ @li_piao.id = 6
180
+ @li_piao.id.should == 6
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,158 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ class Glassware < Wrest::Mappers::Resource::Base
4
+ set_host "http://localhost:3000"
5
+ end
6
+
7
+ class BottledUniverse < Glassware
8
+ set_host "http://localhost:3001"
9
+ end
10
+
11
+ module Wrest::Mappers
12
+ describe Resource::Base do
13
+ it "should not affect other classes when setting up its macros" do
14
+ Class.should_not respond_to(:host=)
15
+ Object.should_not respond_to(:host=)
16
+ end
17
+
18
+ it "should not affect itself when subclasses use its macros" do
19
+ Resource::Base.should_not respond_to(:host)
20
+ end
21
+
22
+ describe 'subclasses' do
23
+ before(:each) do
24
+ @BottledUniverse = Class.new(Resource::Base)
25
+ @BottledUniverse.class_eval do
26
+ set_resource_name 'BottledUniverse'
27
+ end
28
+ end
29
+
30
+ it "should know its name as a resource by default" do
31
+ BottledUniverse.resource_name.should == 'BottledUniverse'
32
+ end
33
+
34
+ it "should allow its name as a resource to be configured for anonymous classes" do
35
+ @BottledUniverse.resource_name.should == 'BottledUniverse'
36
+ end
37
+
38
+ it "should know how to create an instance using deserialised attributes" do
39
+ # Json => {"lead_bottle"=>{"name"=>"Wooz", "id"=>1, "universe_id"=>nil}}
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
42
+ universe.name.should == "Wooz"
43
+ universe.owner_id.should be_nil
44
+ universe.id.should == 1
45
+ end
46
+
47
+ it "should allow instantiation with no attributes" do
48
+ lambda{ @BottledUniverse.new }.should_not raise_error
49
+ end
50
+
51
+ it "should have a method to set the host url" do
52
+ @BottledUniverse.should respond_to(:set_host)
53
+ end
54
+
55
+ it "should have a method to retrive the host url after it is set" do
56
+ @BottledUniverse.class_eval{ set_host "http://localhost:3000" }
57
+ @BottledUniverse.should respond_to(:host)
58
+ end
59
+
60
+ it "should know what its site is" do
61
+ @BottledUniverse.class_eval{ set_host "http://localhost:3000" }
62
+ @BottledUniverse.host.should == "http://localhost:3000"
63
+ end
64
+
65
+ it "should not use the same string" do
66
+ url = "http://localhost:3000"
67
+ @BottledUniverse.class_eval{ set_host url }
68
+ url.upcase!
69
+ @BottledUniverse.host.should == "http://localhost:3000"
70
+ end
71
+
72
+ it "should know its resource path" do
73
+ Glassware.resource_path.should == '/glasswares'
74
+ end
75
+ end
76
+
77
+ describe 'subclasses of sublasses' do
78
+ it "should configure its host without affecting its superclass" do
79
+ Glassware.host.should == "http://localhost:3000"
80
+ BottledUniverse.host.should == "http://localhost:3001"
81
+ end
82
+
83
+ it "should know its resource path when it is a subclass of a subclass" do
84
+ BottledUniverse.resource_path.should == '/bottled_universes'
85
+ end
86
+ end
87
+
88
+ describe 'attribute interface' do
89
+ it "should fail when getter methods for attributes that don't exist are invoked" do
90
+ universe = Glassware.new(:owner => 'Kai Wren', :guardian => 'Lung Shan')
91
+ lambda{ universe.ooga }.should raise_error(NoMethodError)
92
+ end
93
+
94
+ it "should provide getter methods for attributes" do
95
+ universe = Glassware.new(:owner => 'Kai Wren', :guardian => 'Lung Shan')
96
+ universe.owner.should == 'Kai Wren'
97
+ universe.guardian.should == 'Lung Shan'
98
+ end
99
+
100
+ it "should respond to getter methods for attributes" do
101
+ universe = Glassware.new(:owner => 'Kai Wren', :guardian => 'Lung Shan')
102
+ universe.should respond_to(:owner)
103
+ universe.should respond_to(:guardian)
104
+ end
105
+
106
+ it "should not respond to getter methods for attributes that don't exist" do
107
+ universe = Glassware.new(:owner => 'Kai Wren', :guardian => 'Lung Shan')
108
+ universe.should_not respond_to(:theronic)
109
+ end
110
+
111
+ it "should create a setter method when one is invoked for attributes that don't exist" do
112
+ universe = Glassware.new(:owner => 'Kai Wren', :guardian => 'Lung Shan')
113
+ universe.fu_dog = 'Shiriki'
114
+ universe.attributes[:fu_dog].should == 'Shiriki'
115
+ universe.fu_dog.should == 'Shiriki'
116
+ end
117
+
118
+ it "should provide setter methods for attributes" do
119
+ universe = Glassware.new(:owner => 'Kai Wren', :guardian => 'Lung Shan')
120
+ universe.guardian = 'Effervescent Tiger'
121
+ universe.attributes[:guardian].should == 'Effervescent Tiger'
122
+ end
123
+
124
+ it "should respond to setter methods for attributes" do
125
+ universe = Glassware.new(:owner => 'Kai Wren', :guardian => 'Lung Shan')
126
+ universe.should respond_to(:owner=)
127
+ universe.should respond_to(:guardian=)
128
+ end
129
+
130
+ it "should not respond to setter methods for attributes that don't exist" do
131
+ universe = Glassware.new(:owner => 'Kai Wren', :guardian => 'Lung Shan')
132
+ universe.should_not respond_to(:theronic=)
133
+ end
134
+
135
+ it "should fail when query methods for attributes that don't exist are invoked" do
136
+ universe = Glassware.new(:owner => 'Kai Wren', :guardian => 'Lung Shan')
137
+ lambda{ universe.ooga? }.should raise_error(NoMethodError)
138
+ end
139
+
140
+ it "should provide query methods for attributes" do
141
+ universe = Glassware.new(:owner => 'Kai Wren', :guardian => nil)
142
+ universe.owner?.should be_true
143
+ universe.guardian?.should be_false
144
+ end
145
+
146
+ it "should respond to query methods for attributes" do
147
+ universe = Glassware.new(:owner => 'Kai Wren', :guardian => 'Lung Shan')
148
+ universe.should respond_to(:owner?)
149
+ universe.should respond_to(:guardian?)
150
+ end
151
+
152
+ it "should not respond to query methods for attributes that don't exist" do
153
+ universe = Glassware.new(:owner => 'Kai Wren', :guardian => 'Lung Shan')
154
+ universe.should_not respond_to(:theronic?)
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,7 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ module Wrest::Mappers
4
+ describe SimpleResource do
5
+ it "should do something"
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module Wrest
4
+ describe Response do
5
+ it "should know how to delegate to a translator" do
6
+ http_response = mock('response')
7
+ Translators::Xml.should_receive(:call).with(http_response)
8
+ Response.new(http_response).deserialise_using(Translators::Xml)
9
+ end
10
+
11
+ it "should know how to load a translator based on content type" do
12
+ http_response = mock('response')
13
+ http_response.should_receive(:content_type).and_return('application/xml')
14
+
15
+ response = Response.new(http_response)
16
+ response.should_receive(:deserialise_using).with(Translators::Xml)
17
+
18
+ response.deserialise
19
+ end
20
+ end
21
+ end