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.
- data/README.rdoc +104 -0
- data/Rakefile +230 -0
- data/VERSION.yml +4 -0
- data/bin/wrest +22 -0
- data/lib/wrest/core_ext/string/conversions.rb +23 -0
- data/lib/wrest/core_ext/string.rb +5 -0
- data/lib/wrest/exceptions/unsupported_content_type_exception.rb +15 -0
- data/lib/wrest/exceptions.rb +1 -0
- data/lib/wrest/mappers/attributes_container.rb +123 -0
- data/lib/wrest/mappers/resource/base.rb +69 -0
- data/lib/wrest/mappers/resource/collection.rb +12 -0
- data/lib/wrest/mappers/resource.rb +17 -0
- data/lib/wrest/mappers/simple_resource.rb +17 -0
- data/lib/wrest/mappers.rb +21 -0
- data/lib/wrest/response.rb +38 -0
- data/lib/wrest/translators/content_types.rb +20 -0
- data/lib/wrest/translators/json.rb +21 -0
- data/lib/wrest/translators/typed_hash.rb +4 -0
- data/lib/wrest/translators/xml.rb +24 -0
- data/lib/wrest/translators.rb +26 -0
- data/lib/wrest/uri.rb +74 -0
- data/lib/wrest/uri_template.rb +32 -0
- data/lib/wrest/version.rb +22 -0
- data/lib/wrest.rb +41 -0
- data/spec/custom_matchers/custom_matchers.rb +2 -0
- data/spec/rcov.opts +4 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/wrest/mappers/attributes_container_spec.rb +184 -0
- data/spec/wrest/mappers/resource/base_spec.rb +158 -0
- data/spec/wrest/mappers/simple_resource_spec.rb +7 -0
- data/spec/wrest/response_spec.rb +21 -0
- data/spec/wrest/translators/typed_hash_spec.rb +9 -0
- data/spec/wrest/translators/xml_spec.rb +12 -0
- data/spec/wrest/translators_spec.rb +9 -0
- data/spec/wrest/uri_spec.rb +131 -0
- data/spec/wrest/uri_template_spec.rb +28 -0
- metadata +130 -0
data/README.rdoc
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
= Wrest
|
2
|
+
|
3
|
+
(c) Copyright 2009 {Sidu Ponnappa}[http://blog.sidu.in]. All Rights Reserved.
|
4
|
+
|
5
|
+
Wrest is a ruby REST client library which allows you to quickly build object oriented wrappers around any web service. It has two components - Wrest Core and Wrest::Resource.
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
The source is available at git://github.com/kaiwren/wrest.git
|
10
|
+
|
11
|
+
To install as a Rails plugin, do <tt>script/plugin install git://github.com/kaiwren/wrest.git</tt>
|
12
|
+
|
13
|
+
To install the Wrest gem, you'll need to add GitHub to you gem sources if you haven't already done so:
|
14
|
+
|
15
|
+
gem sources -a http://gems.github.com
|
16
|
+
|
17
|
+
Then to install Wrest and its dependencies:
|
18
|
+
|
19
|
+
sudo gem install kaiwren-wrest
|
20
|
+
|
21
|
+
|
22
|
+
== Wrest Core
|
23
|
+
|
24
|
+
* Designed to be used as a library, not just a command line REST client
|
25
|
+
* Provides basic infrastructure (including an interactive shell) and convenient HTTP wrappers
|
26
|
+
* Makes it easy to extend and modify both serialisation, deserialisation and object creation
|
27
|
+
* Isn't coupled to Rails (usable in a pure Ruby application to consume any REST api)
|
28
|
+
* Can be used both stand alone as well as to build object oriented abstractions around web services (Wrest::Resource is an example of the latter)
|
29
|
+
|
30
|
+
=== Usage: Shell
|
31
|
+
|
32
|
+
You can launch the interactive Wrest shell by running bin/wrest if you have the source or invoking <tt>wrest</tt> from your prompt if you've installed the gem.
|
33
|
+
$ wrest
|
34
|
+
>> "http://search.yahooapis.com/NewsSearchService/V1/newsSearch?appid=YahooDemo&output=json&query=India&results=3&start=1".to_uri.get
|
35
|
+
|
36
|
+
=== Usage: Library
|
37
|
+
|
38
|
+
require 'rubygems'
|
39
|
+
require 'wrest'
|
40
|
+
y "http://search.yahooapis.com/NewsSearchService/V1/newsSearch".to_uri.get(
|
41
|
+
:appid => 'YahooDemo',
|
42
|
+
:output => 'xml',
|
43
|
+
:query => 'India',
|
44
|
+
:results=> '3',
|
45
|
+
:start => '1'
|
46
|
+
)
|
47
|
+
=== Basic Http Calls
|
48
|
+
|
49
|
+
==== Get
|
50
|
+
|
51
|
+
A couple of ways to get the Yahoo news as hash map (needs the JSON gem - gem install json).
|
52
|
+
|
53
|
+
* This example simply does a get on a uri and figures out the appropriate deserialiser using the content-type (in this case 'text/javascript', which uses Wrest::Translators::Json). See content_types.rb under lib/wrest/mappers/translators.
|
54
|
+
"http://search.yahooapis.com/NewsSearchService/V1/newsSearch?appid=YahooDemo&output=json&query=India&results=3&start=1".to_uri.get.deserialise
|
55
|
+
|
56
|
+
* This example does a get on a base uri with several parameters passed to it, resulting in a uri essentially the same as the one above. It also shows how you can use a custom deserialiser to produce a hash-map from the response.
|
57
|
+
"http://search.yahooapis.com/NewsSearchService/V1/newsSearch".to_uri.get(
|
58
|
+
:appid => 'YahooDemo',
|
59
|
+
:output => 'xml',
|
60
|
+
:query => 'India',
|
61
|
+
:results=> '3',
|
62
|
+
:start => '1'
|
63
|
+
).deserialise_using(Wrest::Translators::Xml)
|
64
|
+
|
65
|
+
|
66
|
+
=== Logging
|
67
|
+
|
68
|
+
The Wrest logger can be set and accessed through Wrest.logger and is configured by default to log to STDOUT. If you're using Wrest in a Rails application, you can configure logging by placing the following line in your environment file:
|
69
|
+
Wrest.logger = ActiveRecord::Base.logger
|
70
|
+
|
71
|
+
=== Build
|
72
|
+
|
73
|
+
Standard options are available and can be listed using <tt>rake -T</tt>. Use rake:rcov for coverage and rake:rdoc to generate documentation.
|
74
|
+
|
75
|
+
== Wrest::Resource
|
76
|
+
|
77
|
+
Wrest::Resource is an alternative to ActiveResource which targets Rails REST services; it is currently under development.
|
78
|
+
|
79
|
+
* Out of the box support for collections
|
80
|
+
* Out of the box support for collection pagination (including support for WillPaginate), both header based and xml attribute based
|
81
|
+
* Out of the box support for operations on all the records on the collection
|
82
|
+
* Out of the box support for nested resources
|
83
|
+
* Out of the box support for cached resources
|
84
|
+
* Out of the box support for type-casting data that comes in as parameter strings
|
85
|
+
* More natural mapping of deserialised entities to existing classes
|
86
|
+
* No communication via exceptions for http error status codes
|
87
|
+
* Better extensibility - allows access to request/response objects, avoids class variables, favours symbols over strings etc.
|
88
|
+
|
89
|
+
== Dependencies
|
90
|
+
|
91
|
+
* gems
|
92
|
+
* xmlsimple
|
93
|
+
* json
|
94
|
+
* rspec
|
95
|
+
* rcov
|
96
|
+
* active_support
|
97
|
+
|
98
|
+
== Support
|
99
|
+
|
100
|
+
This project uses Assembla for ticketing: http://www.assembla.com/spaces/wrest/tickets
|
101
|
+
|
102
|
+
== Licence
|
103
|
+
|
104
|
+
Wrest is released under the Apache 2.0 licence
|
data/Rakefile
ADDED
@@ -0,0 +1,230 @@
|
|
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
|
+
gem 'rspec'
|
11
|
+
require 'rake'
|
12
|
+
require 'rake/rdoctask'
|
13
|
+
require 'spec'
|
14
|
+
require 'spec/rake/spectask'
|
15
|
+
require 'rcov'
|
16
|
+
require 'rcov/rcovtask'
|
17
|
+
|
18
|
+
desc 'Default: run spec tests.'
|
19
|
+
task :default => :spec
|
20
|
+
|
21
|
+
desc "Run all specs"
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |task|
|
23
|
+
task.spec_files = FileList['spec/wrest/**/*_spec.rb']
|
24
|
+
task.spec_opts = ['--options', 'spec/spec.opts']
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Generate documentation for Wrest'
|
28
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
29
|
+
rdoc.rdoc_dir = 'doc'
|
30
|
+
rdoc.title = 'WRest'
|
31
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
32
|
+
rdoc.rdoc_files.include('README.rdoc')
|
33
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "Run all specs in spec directory with RCov"
|
37
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
38
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
39
|
+
t.spec_files = FileList["spec/wrest/**/*_spec.rb"]
|
40
|
+
t.rcov = true
|
41
|
+
t.rcov_opts = lambda do
|
42
|
+
IO.readlines("spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
|
43
|
+
end
|
44
|
+
# t.verbose = true
|
45
|
+
end
|
46
|
+
|
47
|
+
begin
|
48
|
+
require 'jeweler'
|
49
|
+
Jeweler::Tasks.new do |gemspec|
|
50
|
+
gemspec.name = "wrest"
|
51
|
+
gemspec.summary = "REST client library for Ruby."
|
52
|
+
gemspec.description = "Wrest is a REST client library which allows you to quickly build object oriented wrappers around any web service. It has two main components - Wrest Core and Wrest::Resource."
|
53
|
+
gemspec.authors = ["Sidu Ponnappa"]
|
54
|
+
gemspec.email = "ckponnappa@gmail.com"
|
55
|
+
gemspec.homepage = "http://github.com/kaiwren/wrest"
|
56
|
+
gemspec.has_rdoc = true
|
57
|
+
gemspec.rubyforge_project = 'wrest'
|
58
|
+
gemspec.platform = Gem::Platform::RUBY
|
59
|
+
gemspec.executables = ['wrest']
|
60
|
+
gemspec.require_path = "lib"
|
61
|
+
gemspec.files.exclude 'spec/wrest/meh_spec.rb'
|
62
|
+
gemspec.test_files.exclude 'spec/wrest/meh_spec.rb'
|
63
|
+
gemspec.add_dependency('activesupport', '>= 2.1.0')
|
64
|
+
gemspec.add_dependency('json', '>= 1.1.3')
|
65
|
+
gemspec.add_dependency('xml-simple', '>= 1.0.11')
|
66
|
+
end
|
67
|
+
rescue LoadError
|
68
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
69
|
+
end
|
70
|
+
|
71
|
+
begin
|
72
|
+
require 'rake/contrib/sshpublisher'
|
73
|
+
namespace :rubyforge do
|
74
|
+
|
75
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
76
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
77
|
+
|
78
|
+
namespace :release do
|
79
|
+
desc "Publish RDoc to RubyForge."
|
80
|
+
task :docs => [:rdoc] do
|
81
|
+
config = YAML.load(
|
82
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
83
|
+
)
|
84
|
+
|
85
|
+
host = "#{config['username']}@rubyforge.org"
|
86
|
+
remote_dir = "/var/www/gforge-projects/the-perfect-gem/"
|
87
|
+
local_dir = 'rdoc'
|
88
|
+
|
89
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
rescue LoadError
|
94
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
namespace (:benchmark) do
|
99
|
+
desc "Create classes to be used in Wrest::Resource vs. ActiveResource"
|
100
|
+
task :setup_test_classes do
|
101
|
+
require 'active_resource'
|
102
|
+
require 'wrest'
|
103
|
+
|
104
|
+
class Ooga < Wrest::Mappers::Resource::Base;end
|
105
|
+
class Booga < ActiveResource::Base; self.site='';end
|
106
|
+
end
|
107
|
+
|
108
|
+
desc "Benchmark when objects are created each time before getting data; i.e there are few queries per instantiation"
|
109
|
+
task :create_and_get => :setup_test_classes do |t|
|
110
|
+
|
111
|
+
n = 10000
|
112
|
+
puts "Running #{n} times per report"
|
113
|
+
Benchmark.bmbm(10) do |rpt|
|
114
|
+
rpt.report("Wrest::Resource") do
|
115
|
+
n.times {
|
116
|
+
ooga = Ooga.new(:id => 5, :profession => 'Natural Magician', :enhanced_by => 'Kai Wren')
|
117
|
+
ooga.profession
|
118
|
+
ooga.profession?
|
119
|
+
ooga.enhanced_by
|
120
|
+
ooga.enhanced_by?
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
rpt.report("ActiveResource") do
|
125
|
+
n.times {
|
126
|
+
booga = Booga.new(:id => 5, :profession => 'Natural Magician', :enhanced_by => 'Kai Wren')
|
127
|
+
booga.profession
|
128
|
+
booga.profession?
|
129
|
+
booga.enhanced_by
|
130
|
+
booga.enhanced_by?
|
131
|
+
}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
desc "Benchmark when objects are created beforehand; i.e there are many queries per instantiation"
|
137
|
+
task :create_once_and_get => :setup_test_classes do |t|
|
138
|
+
|
139
|
+
n = 10000
|
140
|
+
puts "Running #{n} times per report"
|
141
|
+
|
142
|
+
Benchmark.bmbm(10) do |rpt|
|
143
|
+
rpt.report("Wrest::Resource") do
|
144
|
+
ooga = Ooga.new(:id => 5, :profession => 'Natural Magician', :enhanced_by => 'Kai Wren')
|
145
|
+
|
146
|
+
n.times {
|
147
|
+
ooga.profession
|
148
|
+
ooga.profession?
|
149
|
+
ooga.enhanced_by
|
150
|
+
ooga.enhanced_by?
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
rpt.report("ActiveResource") do
|
155
|
+
booga = Booga.new(:id => 5, :profession => 'Natural Magician', :enhanced_by => 'Kai Wren')
|
156
|
+
|
157
|
+
n.times {
|
158
|
+
booga.profession
|
159
|
+
booga.profession?
|
160
|
+
booga.enhanced_by
|
161
|
+
booga.enhanced_by?
|
162
|
+
}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
desc "Benchmark objects respond_to? performance without invocation"
|
168
|
+
task :responds_to_before => :setup_test_classes do |t|
|
169
|
+
|
170
|
+
n = 10000
|
171
|
+
puts "Running #{n} times per report"
|
172
|
+
|
173
|
+
Benchmark.bmbm(10) do |rpt|
|
174
|
+
rpt.report("Wrest::Resource") do
|
175
|
+
ooga = Ooga.new(:id => 5, :profession => 'Natural Magician', :enhanced_by => 'Kai Wren')
|
176
|
+
|
177
|
+
n.times {
|
178
|
+
ooga.respond_to?(:profession)
|
179
|
+
ooga.respond_to?(:profession?)
|
180
|
+
ooga.respond_to?(:profession=)
|
181
|
+
}
|
182
|
+
end
|
183
|
+
|
184
|
+
rpt.report("ActiveResource") do
|
185
|
+
booga = Booga.new(:id => 5, :profession => 'Natural Magician', :enhanced_by => 'Kai Wren')
|
186
|
+
|
187
|
+
n.times {
|
188
|
+
booga.respond_to?(:profession)
|
189
|
+
booga.respond_to?(:profession?)
|
190
|
+
booga.respond_to?(:profession=)
|
191
|
+
}
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
desc "Benchmark objects respond_to? performance after invocation"
|
197
|
+
task :responds_to_after => :setup_test_classes do |t|
|
198
|
+
|
199
|
+
n = 10000
|
200
|
+
puts "Running #{n} times per report"
|
201
|
+
|
202
|
+
Benchmark.bmbm(10) do |rpt|
|
203
|
+
rpt.report("Wrest::Resource") do
|
204
|
+
ooga = Ooga.new(:id => 5, :profession => 'Natural Magician', :enhanced_by => 'Kai Wren')
|
205
|
+
ooga.profession
|
206
|
+
ooga.profession?
|
207
|
+
ooga.profession = ''
|
208
|
+
|
209
|
+
n.times {
|
210
|
+
ooga.respond_to?(:profession)
|
211
|
+
ooga.respond_to?(:profession?)
|
212
|
+
ooga.respond_to?(:profession=)
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
rpt.report("ActiveResource") do
|
217
|
+
booga = Booga.new(:id => 5, :profession => 'Natural Magician', :enhanced_by => 'Kai Wren')
|
218
|
+
booga.profession
|
219
|
+
booga.profession?
|
220
|
+
booga.profession = ''
|
221
|
+
|
222
|
+
n.times {
|
223
|
+
booga.respond_to?(:profession)
|
224
|
+
booga.respond_to?(:profession?)
|
225
|
+
booga.respond_to?(:profession=)
|
226
|
+
}
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
data/VERSION.yml
ADDED
data/bin/wrest
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#!/usr/bin/env ruby
|
4
|
+
entry_point = "#{File.dirname(__FILE__)}/../lib/wrest.rb"
|
5
|
+
version = "#{File.dirname(__FILE__)}/../lib/wrest/version"
|
6
|
+
|
7
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
8
|
+
|
9
|
+
require 'optparse'
|
10
|
+
options = { :irb => irb }
|
11
|
+
OptionParser.new do |opt|
|
12
|
+
opt.banner = "Usage: console [options]"
|
13
|
+
opt.on("--irb=[#{irb}]", 'Invoke a different irb.') { |v| options[:irb] = v }
|
14
|
+
opt.parse!(ARGV)
|
15
|
+
end
|
16
|
+
|
17
|
+
libs = " -r irb/completion"
|
18
|
+
libs << " -r #{entry_point}"
|
19
|
+
|
20
|
+
require version
|
21
|
+
puts "Loading (Wrest #{Wrest::VERSION::STRING})"
|
22
|
+
exec "#{options[:irb]} #{libs} --simple-prompt"
|
@@ -0,0 +1,23 @@
|
|
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 CoreExt #:nodoc:
|
12
|
+
module String #:nodoc:
|
13
|
+
# Makes it easier to build other objects from a String
|
14
|
+
module Conversions
|
15
|
+
|
16
|
+
# A convenience method equivalent to Wrest::Uri.new(string)
|
17
|
+
def to_uri
|
18
|
+
Wrest::Uri.new(self)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
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
|
+
# Raised when a translator for an unregisterd response content type
|
12
|
+
# is requested. See Translators.
|
13
|
+
class UnsupportedContentTypeException < StandardError
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "#{WREST_ROOT}/wrest/exceptions/unsupported_content_type_exception"
|
@@ -0,0 +1,123 @@
|
|
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::Mappers #:nodoc:
|
11
|
+
|
12
|
+
# Adds behaviour allowing a class to
|
13
|
+
# contain attributes and providing support
|
14
|
+
# for dynamic getters, setters and query methods.
|
15
|
+
# These methods are added at runtime, on the first
|
16
|
+
# invocation and on a per instance basis.
|
17
|
+
# <tt>respond_to?</tt> however will respond as though
|
18
|
+
# they are all already present.
|
19
|
+
# This means that two different instances of the same
|
20
|
+
# <tt>AttributesContainer</tt> could well have
|
21
|
+
# different attribute getters/setters/query methods.
|
22
|
+
#
|
23
|
+
# Note that this means the first call to a particular
|
24
|
+
# method will be slower because the method is defined
|
25
|
+
# at that point; subsequent calls will be much faster.
|
26
|
+
#
|
27
|
+
# If you're implementing your own initialize method
|
28
|
+
# remember to delegate to the default initialize
|
29
|
+
# of AttributesContainer by invoking <tt>super(attributes)</tt>
|
30
|
+
# Also keep in mind that attribute getter/setter/query methods
|
31
|
+
# will _not_ override any existing methods on the class.
|
32
|
+
#
|
33
|
+
# In situations where this is a problem, such as a client consuming Rails
|
34
|
+
# REST services where <tt>id</tt> is a common attribute and clashes with
|
35
|
+
# Object#id it is recommended to create getter/setter/query methods
|
36
|
+
# on the class (which affects all instances) using the <tt>has_attributes</tt> macro.
|
37
|
+
module AttributesContainer
|
38
|
+
def self.included(klass) #:nodoc:
|
39
|
+
klass.extend AttributesContainer::ClassMethods
|
40
|
+
klass.class_eval{ include AttributesContainer::InstanceMethods }
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.build_attribute_getter(attribute_name) #:nodoc:
|
44
|
+
"def #{attribute_name};@attributes[:#{attribute_name}];end;"
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.build_attribute_setter(attribute_name) #:nodoc:
|
48
|
+
"def #{attribute_name}=(value);@attributes[:#{attribute_name}] = value;end;"
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.build_attribute_queryer(attribute_name) #:nodoc:
|
52
|
+
"def #{attribute_name}?;not @attributes[:#{attribute_name}].nil?;end;"
|
53
|
+
end
|
54
|
+
|
55
|
+
module ClassMethods
|
56
|
+
# This macro explicitly creates getter, setter and query methods on
|
57
|
+
# a class, overriding any exisiting methods with the same names.
|
58
|
+
# This can be used when attribute names clash with method names;
|
59
|
+
# an example would be Rails REST services which frequently make use
|
60
|
+
# an attribute named <tt>id</tt> which clashes with Object#id. Also,
|
61
|
+
# this can be used as a performance optimisation if the incoming
|
62
|
+
# attributes are known beforehand, as defining methods on the first
|
63
|
+
# invocation is no longer necessary.
|
64
|
+
def has_attributes(*attribute_names)
|
65
|
+
attribute_names.each do |attribute_name|
|
66
|
+
self.class_eval(
|
67
|
+
AttributesContainer.build_attribute_getter(attribute_name) +
|
68
|
+
AttributesContainer.build_attribute_setter(attribute_name) +
|
69
|
+
AttributesContainer.build_attribute_queryer(attribute_name)
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module InstanceMethods
|
76
|
+
# Sets up any class to act like
|
77
|
+
# an attributes container by creating
|
78
|
+
# two variables, @attributes and @interface.
|
79
|
+
# Remember not to use these two variable names
|
80
|
+
# when using AttributesContainer in your
|
81
|
+
# own class.
|
82
|
+
def initialize(attributes = {})
|
83
|
+
@attributes = attributes.symbolize_keys
|
84
|
+
@interface = Module.new
|
85
|
+
self.extend @interface
|
86
|
+
end
|
87
|
+
|
88
|
+
def [](key)
|
89
|
+
@attributes[key.to_sym]
|
90
|
+
end
|
91
|
+
|
92
|
+
def []=(key, value)
|
93
|
+
@attributes[key.to_sym] = value
|
94
|
+
end
|
95
|
+
|
96
|
+
def respond_to?(method_name, include_private = false)
|
97
|
+
super(method_name, include_private) ? true : @attributes.include?(method_name.to_s.gsub(/(\?$)|(=$)/, '').to_sym)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Creates getter, setter and query methods for
|
101
|
+
# attributes on the first call.
|
102
|
+
def method_missing(method_sym, *arguments)
|
103
|
+
method_name = method_sym.to_s
|
104
|
+
attribute_name = method_name.gsub(/(\?$)|(=$)/, '')
|
105
|
+
|
106
|
+
if @attributes.include?(attribute_name.to_sym) || method_name.last == '='
|
107
|
+
case method_name.last
|
108
|
+
when '='
|
109
|
+
@interface.module_eval AttributesContainer.build_attribute_setter(attribute_name)
|
110
|
+
when '?'
|
111
|
+
@interface.module_eval AttributesContainer.build_attribute_queryer(attribute_name)
|
112
|
+
else
|
113
|
+
@interface.module_eval AttributesContainer.build_attribute_getter(attribute_name)
|
114
|
+
end
|
115
|
+
send(method_sym, *arguments)
|
116
|
+
else
|
117
|
+
super(method_sym, *arguments)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,69 @@
|
|
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::Mappers::Resource #:nodoc:
|
11
|
+
# Resource::Base is the equivalent of ActiveResource::Base.
|
12
|
+
# It is a REST client targetted at Rails REST apps.
|
13
|
+
class Base
|
14
|
+
include Wrest::Mappers::AttributesContainer
|
15
|
+
|
16
|
+
has_attributes :id
|
17
|
+
attr_reader :attributes
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def inherited(klass)
|
21
|
+
klass.set_resource_name klass.name
|
22
|
+
end
|
23
|
+
|
24
|
+
# Allows the resource name to be configured and creates
|
25
|
+
# a getter method for it.
|
26
|
+
# This is a useful feature when using anonymous classes like
|
27
|
+
# we often do while writing tests.
|
28
|
+
# By default, the resource name is set to the name of the class.
|
29
|
+
def set_resource_name(resource_name)
|
30
|
+
self.class_eval "def self.resource_name; '#{resource_name}';end"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Allows the host url at which the resource is found to be configured
|
34
|
+
# and creates a getter method for it.
|
35
|
+
# For example in the url
|
36
|
+
# http://localhost:3000/users/1/settings
|
37
|
+
# you would set
|
38
|
+
# http://localhost:3000
|
39
|
+
# as the host url.
|
40
|
+
def set_host(host)
|
41
|
+
self.class_eval "def self.host; '#{host}';end"
|
42
|
+
end
|
43
|
+
|
44
|
+
def resource_path
|
45
|
+
@resource_path ||= "/#{resource_name.underscore.pluralize}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def resource_url
|
49
|
+
"#{host}#{resource_path}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_all
|
53
|
+
end
|
54
|
+
|
55
|
+
def find(id)
|
56
|
+
response_hash = "#{resource_url}/#{id}".to_uri.get.deserialise
|
57
|
+
resource_type = response_hash.keys.first
|
58
|
+
if(resource_type.underscore.camelize == self.name)
|
59
|
+
self.new(response_hash[resource_type].first)
|
60
|
+
else
|
61
|
+
response_hash
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def objectify(hash)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +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
|
+
|
10
|
+
# Understands how to contain a collection of Wrest::Resources
|
11
|
+
class Wrest::Mappers::Resource::Collection
|
12
|
+
end
|
@@ -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 Mappers
|
12
|
+
module Resource
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require "#{WREST_ROOT}/wrest/mappers/resource/base"
|
@@ -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 Mappers
|
12
|
+
class SimpleResource
|
13
|
+
include Wrest::Mappers::AttributesContainer
|
14
|
+
attr_reader :attributes
|
15
|
+
end
|
16
|
+
end
|
17
|
+
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
|
+
module Wrest #:nodoc:
|
11
|
+
# A Mapper is anything that constructs an object
|
12
|
+
# or object graph from a hash map. This hash map
|
13
|
+
# is typically obtained by using a Translator to
|
14
|
+
# deserialise a Response.
|
15
|
+
module Mappers
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
require "#{WREST_ROOT}/wrest/mappers/attributes_container"
|
20
|
+
require "#{WREST_ROOT}/wrest/mappers/simple_resource"
|
21
|
+
require "#{WREST_ROOT}/wrest/mappers/resource"
|