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