wrest 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. data/README.rdoc +24 -17
  2. data/Rakefile +5 -4
  3. data/VERSION.yml +1 -1
  4. data/examples/delicious.rb +7 -3
  5. data/examples/redirection.rb +27 -0
  6. data/examples/twitter.rb +78 -0
  7. data/{spec/wrest/http/request_spec.rb → examples/twitter_public_timeline.rb} +15 -12
  8. data/examples/wow_realm_status.rb +7 -3
  9. data/lib/wrest/components/attributes_container/alias_accessors.rb +66 -0
  10. data/lib/wrest/components/attributes_container/typecaster.rb +44 -38
  11. data/lib/wrest/components/attributes_container.rb +23 -5
  12. data/lib/wrest/components/mutators/base.rb +1 -1
  13. data/lib/wrest/components/translators.rb +1 -1
  14. data/lib/wrest/core_ext/string/conversions.rb +1 -1
  15. data/lib/wrest/exceptions.rb +14 -2
  16. data/lib/wrest/http/get.rb +2 -0
  17. data/lib/wrest/http/redirection.rb +35 -0
  18. data/lib/wrest/http/request.rb +37 -13
  19. data/lib/wrest/http/response.rb +17 -0
  20. data/lib/wrest/http.rb +1 -0
  21. data/lib/wrest/resource/base.rb +1 -2
  22. data/lib/wrest/uri.rb +2 -0
  23. data/lib/wrest/version.rb +1 -1
  24. data/spec/functional/sample_rails_app/README +3 -0
  25. data/spec/functional/sample_rails_app/Rakefile +10 -0
  26. data/spec/functional/sample_rails_app/app/controllers/application_controller.rb +10 -0
  27. data/spec/functional/sample_rails_app/app/controllers/lead_bottles_controller.rb +7 -0
  28. data/spec/functional/sample_rails_app/app/helpers/application_helper.rb +3 -0
  29. data/spec/functional/sample_rails_app/app/models/bottle.rb +3 -0
  30. data/spec/functional/sample_rails_app/app/models/glass_bottle.rb +3 -0
  31. data/spec/functional/sample_rails_app/app/models/lead_bottle.rb +3 -0
  32. data/spec/functional/sample_rails_app/config/boot.rb +110 -0
  33. data/spec/functional/sample_rails_app/config/database.yml +16 -0
  34. data/spec/functional/sample_rails_app/config/environment.rb +42 -0
  35. data/spec/functional/sample_rails_app/config/environments/development.rb +17 -0
  36. data/spec/functional/sample_rails_app/config/environments/production.rb +28 -0
  37. data/spec/functional/sample_rails_app/config/environments/test.rb +28 -0
  38. data/spec/functional/sample_rails_app/config/initializers/backtrace_silencers.rb +7 -0
  39. data/spec/functional/sample_rails_app/config/initializers/inflections.rb +10 -0
  40. data/spec/functional/sample_rails_app/config/initializers/mime_types.rb +5 -0
  41. data/spec/functional/sample_rails_app/config/initializers/new_rails_defaults.rb +19 -0
  42. data/spec/functional/sample_rails_app/config/initializers/session_store.rb +15 -0
  43. data/spec/functional/sample_rails_app/config/locales/en.yml +5 -0
  44. data/spec/functional/sample_rails_app/config/routes.rb +3 -0
  45. data/spec/functional/sample_rails_app/db/development.sqlite3 +0 -0
  46. data/spec/functional/sample_rails_app/db/migrate/20090319115628_create_bottle.rb +13 -0
  47. data/spec/functional/sample_rails_app/db/schema.rb +20 -0
  48. data/spec/functional/sample_rails_app/db/test.sqlite3 +0 -0
  49. data/spec/functional/sample_rails_app/log/development.log +1 -0
  50. data/spec/functional/sample_rails_app/public/404.html +30 -0
  51. data/spec/functional/sample_rails_app/public/422.html +30 -0
  52. data/spec/functional/sample_rails_app/public/500.html +30 -0
  53. data/spec/functional/sample_rails_app/public/favicon.ico +0 -0
  54. data/spec/functional/sample_rails_app/public/images/rails.png +0 -0
  55. data/spec/functional/sample_rails_app/public/index.html +275 -0
  56. data/spec/functional/sample_rails_app/public/robots.txt +5 -0
  57. data/spec/functional/sample_rails_app/script/about +4 -0
  58. data/spec/functional/sample_rails_app/script/autospec +6 -0
  59. data/spec/functional/sample_rails_app/script/console +3 -0
  60. data/spec/functional/sample_rails_app/script/dbconsole +3 -0
  61. data/spec/functional/sample_rails_app/script/destroy +3 -0
  62. data/spec/functional/sample_rails_app/script/generate +3 -0
  63. data/spec/functional/sample_rails_app/script/performance/benchmarker +3 -0
  64. data/spec/functional/sample_rails_app/script/performance/profiler +3 -0
  65. data/spec/functional/sample_rails_app/script/plugin +3 -0
  66. data/spec/functional/sample_rails_app/script/runner +3 -0
  67. data/spec/functional/sample_rails_app/script/server +3 -0
  68. data/spec/functional/sample_rails_app/script/spec +10 -0
  69. data/spec/functional/sample_rails_app/script/spec_server +9 -0
  70. data/spec/functional/sample_rails_app/test/performance/browsing_test.rb +9 -0
  71. data/spec/functional/sample_rails_app/test/test_helper.rb +38 -0
  72. data/spec/functional/sample_rails_app/tmtags +2559 -0
  73. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/MIT-LICENSE +20 -0
  74. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/README.rdoc +100 -0
  75. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/Rakefile +18 -0
  76. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/init.rb +5 -0
  77. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/install.rb +1 -0
  78. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/base.rb +140 -0
  79. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/controllers/resources.rb +16 -0
  80. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/controllers/resources_controller.rb +26 -0
  81. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/controllers/routes_controller.rb +16 -0
  82. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/core_extensions/api.rb +26 -0
  83. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/core_extensions/exception.rb +23 -0
  84. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/core_extensions/from_json.rb +15 -0
  85. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/dispatch.rb +235 -0
  86. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/models/resourced_route.rb +84 -0
  87. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/query.rb +337 -0
  88. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/render/html.rb +50 -0
  89. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/render/json.rb +75 -0
  90. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/render/xml.rb +65 -0
  91. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/render.rb +63 -0
  92. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/retrieve.rb +74 -0
  93. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full/version.rb +9 -0
  94. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/lib/resource_full.rb +14 -0
  95. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/spec/resource_full/base_spec.rb +88 -0
  96. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/spec/resource_full/controllers/resources_spec.rb +29 -0
  97. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/spec/resource_full/dispatch_spec.rb +262 -0
  98. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/spec/resource_full/models/resourced_route_spec.rb +62 -0
  99. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/spec/resource_full/query/parameter_spec.rb +57 -0
  100. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/spec/resource_full/query_spec.rb +462 -0
  101. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/spec/resource_full/render/html_spec.rb +4 -0
  102. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/spec/resource_full/render/json_spec.rb +107 -0
  103. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/spec/resource_full/render/xml_spec.rb +98 -0
  104. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/spec/resource_full/render_spec.rb +5 -0
  105. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/spec/resource_full/retrieve_spec.rb +173 -0
  106. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/spec/spec_helper.rb +98 -0
  107. data/spec/functional/sample_rails_app/vendor/plugins/resource_full/uninstall.rb +1 -0
  108. data/spec/functional/spec_helper.rb +0 -0
  109. data/spec/{spec_helper.rb → unit/spec_helper.rb} +2 -2
  110. data/spec/unit/wrest/components/attributes_container/alias_accessors_spec.rb +49 -0
  111. data/spec/{wrest → unit/wrest}/components/attributes_container/typecaster_spec.rb +28 -8
  112. data/spec/{wrest → unit/wrest}/components/attributes_container_spec.rb +42 -15
  113. data/spec/{wrest → unit/wrest}/components/mutators/base_spec.rb +1 -1
  114. data/spec/{wrest → unit/wrest}/components/mutators/camel_to_snake_spec.rb +0 -0
  115. data/spec/{wrest → unit/wrest}/components/mutators/xml_mini_type_caster_spec.rb +0 -0
  116. data/spec/{wrest → unit/wrest}/components/mutators/xml_simple_type_caster_spec.rb +0 -0
  117. data/spec/{wrest → unit/wrest}/components/mutators_spec.rb +0 -0
  118. data/spec/{wrest → unit/wrest}/components/translators/xml_spec.rb +0 -0
  119. data/spec/{wrest → unit/wrest}/components/translators_spec.rb +1 -1
  120. data/spec/{wrest → unit/wrest}/core_ext/hash/conversions_spec.rb +0 -0
  121. data/spec/{wrest → unit/wrest}/core_ext/string/conversions_spec.rb +14 -0
  122. data/spec/unit/wrest/http/redirection_spec.rb +42 -0
  123. data/spec/unit/wrest/http/request_spec.rb +70 -0
  124. data/spec/unit/wrest/http/response_spec.rb +45 -0
  125. data/spec/{wrest → unit/wrest}/resource/base_spec.rb +5 -4
  126. data/spec/{wrest → unit/wrest}/uri_spec.rb +68 -67
  127. data/spec/{wrest → unit/wrest}/uri_template_spec.rb +0 -0
  128. metadata +187 -38
  129. data/lib/wrest/exceptions/method_not_overridden_exception.rb +0 -17
  130. data/lib/wrest/exceptions/unsupported_content_type_exception.rb +0 -17
  131. data/spec/wrest/http/response_spec.rb +0 -21
data/README.rdoc CHANGED
@@ -1,15 +1,15 @@
1
- = Wrest
1
+ = Wrest 0.0.9
2
2
 
3
3
  (c) Copyright 2009 {Sidu Ponnappa}[http://blog.sidu.in]. All Rights Reserved.
4
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.
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. If you want to be notified whenever new features are added to Wrest, you can subscribe to my {twitter feed}[http://twitter.com/ponnappa].
6
6
 
7
- If you were wondering why the words 'demon', 'chi' and 'puppies' (among others) show up in nearly every example and spec, it's because they all refer to characters and ideas from {Roger Zelazny's}[http://en.wikipedia.org/wiki/Roger_Zelazny] last book, 'Lord Demon.'
7
+ If you were wondering why the words 'demon', 'chi' and 'fu-puppies' (among others) show up in nearly every example and spec, it's because they all refer to characters and ideas from {Roger Zelazny's}[http://en.wikipedia.org/wiki/Roger_Zelazny] last book, 'Lord Demon.'
8
8
 
9
9
  == Wrest Core: Features
10
10
 
11
- * Provides convenient HTTP wrappers, serialisation and deserialisation with caching and redirect handling in the works.
12
- * Designed to be used as a library, not just a command line REST client (fewer class methods, more object oriented)
11
+ * Provides convenient HTTP wrappers, redirect handling, serialisation and deserialisation with response caching in the works.
12
+ * Designed to be used as a library, not just a command line REST client (fewer class/static methods, more object oriented)
13
13
  * Isn't coupled to Rails (usable in a pure Ruby application to consume any HTTP/REST api)
14
14
  * Can be used both stand alone as well as to build object oriented abstractions around resources and web services (Wrest::Resource is an example of the latter)
15
15
 
@@ -23,8 +23,9 @@ The source is available at git://github.com/kaiwren/wrest.git
23
23
 
24
24
  To install as a Rails plugin, do <tt>script/plugin install git://github.com/kaiwren/wrest.git</tt>
25
25
 
26
- To install the Wrest gem, do <tt>(sudo) gem install wrest</tt>
27
- Wrest is also available as a gem for JRuby; you can instally it by running <tt>(sudo) jruby -S gem install wrest</tt>.
26
+ To install the Wrest gem, do <tt>(sudo) gem install wrest</tt>. Wrest supports both Ruby 1.8 and 1.9.
27
+
28
+ Wrest is also available as a gem for JRuby; you can install it by running <tt>(sudo) jruby -S gem install wrest</tt>.
28
29
 
29
30
  === Usage: Shell
30
31
 
@@ -89,24 +90,26 @@ To find out what actions are permitted on a URI:
89
90
 
90
91
  === Usage: Attributes Container
91
92
 
92
- Allows any class to hold an attributes hash, somewhat like ActiveResource. It also supports several extensions to this base fuctionality such as support for typecasting attribute values.
93
+ Allows any class to hold an attributes hash, somewhat like ActiveResource. It also supports several extensions to this base fuctionality such as support for typecasting attribute values. See examples/twitter.rb and examples/wow_realm_status.rb for more samples.
93
94
 
94
95
  Example:
95
96
 
96
97
  class Demon
97
98
  include Wrest::Components::AttributesContainer
98
- include Wrest::Components::AttributesContainer::Typecaster
99
99
 
100
- always_has :id
100
+ always_has :id
101
101
  typecast :age => as_integer,
102
102
  :chi => lambda{|chi| Chi.new(chi)}
103
+
104
+ alias_accessors :chi => :energy
103
105
  end
104
106
 
105
- kai_wren = Demon.new('id => '1', 'age' => '1500', 'chi' => '1024', 'teacher' => 'Viss')
106
- kai_wren.id # => '1'
107
- kai_wren.age # => 1500
108
- kai_wren.chi # => #<Chi:0x113af8c @count="1024">
109
- kai_wren.teacher # => 'Viss'
107
+ kai_wren = Demon.new('id' => '1', 'age' => '1500', 'chi' => '1024', 'teacher' => 'Viss')
108
+ kai_wren.id # => '1'
109
+ kai_wren.age # => 1500
110
+ kai_wren.chi # => #<Chi:0x113af8c @count="1024">
111
+ kai_wren.energy # => #<Chi:0x113af8c @count="1024">
112
+ kai_wren.teacher # => 'Viss'
110
113
 
111
114
  === Usage: Logging
112
115
 
@@ -125,6 +128,8 @@ Wrest RDocs can be found at http://wrest.rubyforge.org
125
128
 
126
129
  Wrest::Resource is an alternative to Rails' ActiveResource. It targets Rails REST (well, POX, since Rails isn't really RESTful) services and is currently under development. Since no single REST library can provide an object oriented wrapper suitable for _all_ available web services, it follows that Wrest should focus on providing you with the tools to help you roll your own. Wrest::Resource is an example of this - an object oriented wrapper for the kind of REST APIs exposed by Rails applications, that you would otherwise use ActiveResource to consume.
127
130
 
131
+ If you're looking for help doing this on Rails on the server side, take a look at {resource_full}[http://github.com/bguthrie/resource_full], a Rails plugin that makes RESTful Rails a whole order of magnitude easier. resource_full is a stable project and is currently in use in production.
132
+
128
133
 
129
134
  * No more pretending that REST resources are the same as records in a database (yeah, no more freaking ActiveResource::Connection)
130
135
  * Treat put as 'create or update,' not just 'update'
@@ -147,19 +152,21 @@ Wrest::Resource is an alternative to Rails' ActiveResource. It targets Rails RES
147
152
  == Dependencies
148
153
 
149
154
  === Source
155
+
150
156
  * gems
151
157
  * json (json-jruby on JRuby)
152
158
  * active_support
153
159
  * ruby-libxml (Recommended, will fall back to REXML if absent; be aware that Wrest uses ActiveSupport::XmlMini, so if you're using Wrest as a plugin in a Rails application, all xml parsing across the application will switch to using libxml if it's available. You're free to change this by hand by using the ActiveSupport::XmlMini.backend= method.)
154
160
 
155
161
  === Build
162
+
156
163
  * rspec
157
164
  * rcov (unsupported on JRuby)
158
165
  * jeweler
159
166
 
160
- == Support
167
+ == Roadmap
161
168
 
162
- This project uses Assembla for ticketing: http://www.assembla.com/spaces/wrest/tickets
169
+ Features that are planned, in progress or already implemented are documented in the {CHANGELOG}[http://github.com/kaiwren/wrest/tree/master/CHANGELOG] starting from version 0.0.8.
163
170
 
164
171
  == Licence
165
172
 
data/Rakefile CHANGED
@@ -31,7 +31,7 @@ task :default => :spec
31
31
 
32
32
  desc "Run all specs"
33
33
  Spec::Rake::SpecTask.new(:spec) do |task|
34
- task.spec_files = FileList['spec/wrest/**/*_spec.rb']
34
+ task.spec_files = FileList['spec/unit/wrest/**/*_spec.rb']
35
35
  task.spec_opts = ['--options', 'spec/spec.opts']
36
36
  end
37
37
 
@@ -56,7 +56,7 @@ begin
56
56
  desc "Run all specs in spec directory with RCov"
57
57
  Spec::Rake::SpecTask.new(:rcov) do |t|
58
58
  t.spec_opts = ['--options', "spec/spec.opts"]
59
- t.spec_files = FileList["spec/wrest/**/*_spec.rb"]
59
+ t.spec_files = FileList["spec/unit/wrest/**/*_spec.rb"]
60
60
  t.rcov = true
61
61
  t.rcov_opts = lambda do
62
62
  IO.readlines("spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
@@ -80,8 +80,8 @@ begin
80
80
  gemspec.rubyforge_project = 'wrest'
81
81
  gemspec.executables = ['wrest', 'jwrest']
82
82
  gemspec.require_path = "lib"
83
- gemspec.files.exclude 'spec/wrest/meh_spec.rb'
84
- gemspec.test_files.exclude 'spec/wrest/meh_spec.rb'
83
+ gemspec.files.exclude 'spec/unit/wrest/meh_spec.rb'
84
+ gemspec.test_files.exclude 'spec/unit/wrest/meh_spec.rb'
85
85
  gemspec.add_dependency('activesupport', '>= 2.3.2')
86
86
  case RUBY_PLATFORM
87
87
  when /java/
@@ -94,6 +94,7 @@ begin
94
94
  end
95
95
  rescue LoadError
96
96
  puts "Jeweler not available. Install it with: gem install technicalpickles-jeweler -s http://gems.github.com"
97
+ puts "If you're using JRuby and are having trouble installing jeweler, try installing the git (gem install git) and rubyforge (gem install rubyforge) gems by hand. Also remember to update gems itself (jruby -S gem update --system)."
97
98
  end
98
99
 
99
100
  begin
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 8
2
+ :patch: 9
3
3
  :minor: 0
4
4
  :major: 0
@@ -13,6 +13,10 @@ require 'pp'
13
13
  Wrest.logger = Logger.new(STDOUT)
14
14
  Wrest.logger.level = Logger::DEBUG # Set this to Logger::INFO or higher to disable request logging
15
15
 
16
+ # This example demonstrates the usage of GET, POST, PUT and
17
+ # DELETE over HTTPS. Its also shows how Wrest::Uris can have
18
+ # paths extended making accessing an API easy as pie.
19
+ #
16
20
  # API reference: http://delicious.com/help/api
17
21
  class Delicious
18
22
  def initialize(options)
@@ -45,14 +49,14 @@ pp account.bookmark(
45
49
  :tags => 'ruby hacking'
46
50
  ).deserialise
47
51
 
48
- puts '----------'
52
+ puts '', '*'*70, ''
49
53
 
50
54
  pp account.bookmarks(:tag => 'rails', :dt => '20090712').deserialise
51
55
 
52
- puts '----------'
56
+ puts '', '*'*70, ''
53
57
 
54
58
  pp recently_saved_uris = account.recent(:tag => 'ruby').deserialise["posts"]["post"].collect{|bookmark| bookmark['href']}
55
59
 
56
- puts '----------'
60
+ puts '', '*'*70, ''
57
61
 
58
62
  pp account.delete(:url => 'http://blog.sidu.in/search/label/ruby').deserialise
@@ -0,0 +1,27 @@
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.expand_path(File.dirname(__FILE__) + "/../lib/wrest")
11
+ require 'pp'
12
+
13
+ Wrest.logger = Logger.new(STDOUT)
14
+ Wrest.logger.level = Logger::DEBUG # Set this to Logger::INFO or higher to disable request logging
15
+
16
+ # google.com redirects to www.google.com so this is live test for redirection
17
+ pp 'http://google.com'.to_uri.get.body
18
+
19
+ puts '', '*'*70, ''
20
+
21
+ # Do a get with auto follow redirects turned off
22
+ pp 'http://google.com'.to_uri(:follow_redirects => false).get.body
23
+
24
+ puts '', '*'*70, ''
25
+
26
+ # Do a get with auto follow redirects limited, causing an exception.
27
+ 'http://google.com'.to_uri(:follow_redirects_limit => 1).get.body
@@ -0,0 +1,78 @@
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.expand_path(File.dirname(__FILE__) + "/../lib/wrest")
11
+ require 'pp'
12
+
13
+ Wrest.logger = Logger.new(STDOUT)
14
+ Wrest.logger.level = Logger::DEBUG # Set this to Logger::INFO or higher to disable request logging
15
+
16
+ # This example shows a more object oriented approach
17
+ # to accessing Twitter using Wrest.
18
+ #
19
+ # Twitter is your twitter account. Every tweet is wrapped in
20
+ # an instance of Tweet. Every Tweet has one TwitterUser.
21
+
22
+ class Twitter
23
+ def initialize(options)
24
+ @uri = "https://twitter.com".to_uri(options)
25
+ end
26
+
27
+ # which can be :friends, :user or :public
28
+ # options[:query] can be things like since, since_id, count, etc.
29
+ def timeline(which = :friends, options={})
30
+ @uri["/statuses/#{which}_timeline.json"].get(options).deserialise.collect{|tweet| Tweet.new(tweet)}
31
+ end
32
+
33
+ def post(text)
34
+ Tweet.new @uri['/statuses/update.json'].post('', {'User-Agent' => "Wrest/#{Wrest::VERSION::STRING}"}, {:status => text}).deserialise
35
+ end
36
+ end
37
+
38
+ class TwitterUser
39
+ # This will turn this class into a wrapper
40
+ # for the deserialised data from a response.
41
+ #
42
+ # All the keys in the hash are exposed as methods.
43
+ include Wrest::Components::AttributesContainer
44
+
45
+ # We'd prefer the user's profile url to be
46
+ # a Wrest::Uri rather than a String, wouldn't we?
47
+ #
48
+ # Remember, using typecasting _will_
49
+ # slow down instance construction marginally, so turn it on
50
+ # only if you need it.
51
+ typecast :url => lambda{|url| url.to_uri}
52
+ end
53
+
54
+ class Tweet
55
+ include Wrest::Components::AttributesContainer
56
+
57
+ typecast :user => lambda{|user| TwitterUser.new(user) }
58
+ end
59
+
60
+
61
+ twitter = Twitter.new(:username => 'ponnappa', :password => 'ha!likeImchecking*that*in')
62
+
63
+ pp twitter.post("This tweet via the Twitter example in #wrest #{Wrest::VERSION::STRING}, http://github.com/kaiwren/wrest")
64
+
65
+ puts '', '*'*70, ''
66
+
67
+ tweets = twitter.timeline(:friends, :since_id => 20751449)
68
+
69
+ # Print the name of the first user in my timeline.
70
+ puts tweets.first.user.name
71
+
72
+ puts '', '*'*70, ''
73
+
74
+ # Just remember that not everyone on Twitter has a
75
+ # url. On the other hand, some have more than one.
76
+ # This is just a cute little example that deals with
77
+ # the simple case of a single url.
78
+ puts tweets.first.user.url.get.body
@@ -7,16 +7,19 @@
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 File.dirname(__FILE__) + '/../../spec_helper'
10
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/wrest")
11
+ require 'pp'
11
12
 
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
13
+ Wrest.logger = Logger.new(STDOUT)
14
+ Wrest.logger.level = Logger::DEBUG # Set this to Logger::INFO or higher to disable request logging
15
+
16
+
17
+ # This is a basic example demonstrating GET and json deserialisation.
18
+
19
+ response = 'http://twitter.com/statuses/public_timeline.json'.to_uri.get
20
+
21
+ puts "Code: #{response.code}"
22
+ puts "Message: #{response.message}"
23
+ puts "Headers: #{response.headers.inspect}"
24
+ puts "Body: #{response.body.inspect}"
25
+ puts "Deserialised: #{response.deserialise.inspect}"
@@ -16,7 +16,6 @@ include Wrest
16
16
 
17
17
  class Realm
18
18
  include Components::AttributesContainer
19
- include Components::AttributesContainer::Typecaster
20
19
 
21
20
  typecast :t => lambda{|type|
22
21
  case type
@@ -40,6 +39,11 @@ class Realm
40
39
  when '4' then 'Max'
41
40
  end
42
41
  }
42
+
43
+ alias_accessors :t => :type,
44
+ :s => :status,
45
+ :l => :load,
46
+ :n => :name
43
47
 
44
48
  def available?
45
49
  self.s == 'Available'
@@ -48,10 +52,10 @@ end
48
52
 
49
53
  realms = "http://www.worldofwarcraft.com/realmstatus/status.xml".to_uri.get.deserialise['page']['rs']['r'].collect{|data| Realm.new(data)}
50
54
 
51
- puts "Status of Nagrand: #{realms.find{|realm| realm.n == 'Nagrand'}.s}"
55
+ puts "Status of Nagrand: #{realms.find{|realm| realm.name == 'Nagrand'}.status}"
52
56
  puts
53
57
  puts "All Available Realms:"
54
58
  puts
55
59
  puts "Realm\tLoad\tType"
56
60
  puts "-----------"
57
- realms.select(&:available?).each{|realm| puts "#{realm.n}\t#{realm.l}\t#{realm.t}" }
61
+ realms.select(&:available?).each{|realm| puts "#{realm.name}\t#{realm.load}\t#{realm.type}" }
@@ -0,0 +1,66 @@
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 Components::AttributesContainer
12
+ module AliasAccessors
13
+ def self.included(klass) #:nodoc:
14
+ klass.extend AliasAccessors::ClassMethods
15
+ end
16
+
17
+ def self.build_aliased_attribute_getter(attribute_name, alias_name) #:nodoc:
18
+ "def #{alias_name};#{attribute_name};end;"
19
+ end
20
+
21
+ def self.build_aliased_attribute_setter(attribute_name, alias_name) #:nodoc:
22
+ "def #{alias_name}=(value);self.#{attribute_name}=value;end;"
23
+ end
24
+
25
+ def self.build_aliased_attribute_queryer(attribute_name, alias_name) #:nodoc:
26
+ "def #{alias_name}?;self.#{attribute_name}?;end;"
27
+ end
28
+
29
+ module ClassMethods
30
+ # Creates an alias set of getter, setter and query methods for
31
+ # attributes that aren't quite the way you'd like them to be; this
32
+ # is especially useful when you have no control over the source web
33
+ # sevice/resource.
34
+ #
35
+ # For example, lets say that a particular resource exposes a
36
+ # User's age as 'a' and sex as s. Typically, you'd have to access it as
37
+ # user.a and user.s whereas you's like to access it as user.age and user.sex.
38
+ # This is where alias_accessors comes into the picture. Your User class would
39
+ # look somethig like this:
40
+ #
41
+ # class User
42
+ # include Wrest::Components::AttributesContainer
43
+ #
44
+ # alias_accessors :a => :age,
45
+ # :s => :sex
46
+ # end
47
+ # This would create the methods user.age, user.age= and user.age? which delegate
48
+ # to user.a, user.a= and user.a? respectively. Ditto for s to sex.
49
+ #
50
+ # See examples/wow_realm_status.rb for a working example.
51
+ #
52
+ # WARNING: If you try to create an alias with the same name as the attribute,
53
+ # and then use it, you _will_ cause an infinite loop.
54
+ def alias_accessors(alias_map)
55
+ alias_map.each do |attribute_name, alias_name|
56
+ self.class_eval(
57
+ AliasAccessors.build_aliased_attribute_getter(attribute_name, alias_name) +
58
+ AliasAccessors.build_aliased_attribute_setter(attribute_name, alias_name) +
59
+ AliasAccessors.build_aliased_attribute_queryer(attribute_name, alias_name)
60
+ )
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -26,19 +26,61 @@ module Wrest
26
26
  klass.class_eval{ include Typecaster::InstanceMethods }
27
27
  klass.alias_method_chain :initialize, :typecasting
28
28
  end
29
+
30
+ module Helpers
31
+ def as_base64Binary
32
+ ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['base64Binary']
33
+ end
34
+
35
+ def as_boolean
36
+ ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['boolean']
37
+ end
38
+
39
+ def as_decimal
40
+ ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['decimal']
41
+ end
42
+
43
+ def as_date
44
+ ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['date']
45
+ end
46
+
47
+ def as_datetime
48
+ ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['datetime']
49
+ end
50
+
51
+ def as_float
52
+ ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['float']
53
+ end
54
+
55
+ def as_integer
56
+ ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['integer']
57
+ end
29
58
 
59
+ def as_symbol
60
+ ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['symbol']
61
+ end
62
+
63
+ def as_yaml
64
+ ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['yaml']
65
+ end
66
+ end
67
+
30
68
  module ClassMethods
31
69
  # Accepts a set of attribute-name/lambda pairs which are used
32
70
  # to typecast string values injected through the constructor.
33
71
  # Typically needed when populating an +AttributesContainer+
34
72
  # directly from request params. Typecasting kicks in for
35
- # a given value _only_ if it is a string.
73
+ # a given value _only_ if it is a String, Hash or Array, the
74
+ # three classes that deserilisation can produce.
36
75
  #
37
76
  # Typecast information is inherited by subclasses; however be
38
77
  # aware that explicitly invoking +typecast+ in a subclass will
39
78
  # discard inherited typecast information leaving only the casts
40
79
  # defined in the subclass.
41
80
  #
81
+ # Note that this _will_ increase the time needed to initialize
82
+ # instances.
83
+ #
42
84
  # Common typecasts such as integer, float, datetime etc. are
43
85
  # available through predefined helpers. See TypecastHelpers
44
86
  # for a full list.
@@ -69,42 +111,6 @@ module Wrest
69
111
  {}
70
112
  end
71
113
  end
72
-
73
- def as_base64Binary
74
- ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['base64Binary']
75
- end
76
-
77
- def as_boolean
78
- ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['boolean']
79
- end
80
-
81
- def as_decimal
82
- ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['decimal']
83
- end
84
-
85
- def as_date
86
- ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['date']
87
- end
88
-
89
- def as_datetime
90
- ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['datetime']
91
- end
92
-
93
- def as_float
94
- ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['float']
95
- end
96
-
97
- def as_integer
98
- ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['integer']
99
- end
100
-
101
- def as_symbol
102
- ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['symbol']
103
- end
104
-
105
- def as_yaml
106
- ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['yaml']
107
- end
108
114
  end
109
115
 
110
116
  module InstanceMethods # :nodoc:
@@ -112,7 +118,7 @@ module Wrest
112
118
  initialize_without_typecasting(attributes)
113
119
  self.class.typecast_map.each do |key, typecaster|
114
120
  value = @attributes[key]
115
- @attributes[key] = typecaster.call(value) if value.is_a?(String)
121
+ @attributes[key] = typecaster.call(value) if (value.is_a?(String) || value.is_a?(Hash) || value.is_a?(Array))
116
122
  end
117
123
  end
118
124
  end
@@ -13,6 +13,7 @@ module Wrest
13
13
  end
14
14
 
15
15
  require "#{WREST_ROOT}/wrest/components/attributes_container/typecaster"
16
+ require "#{WREST_ROOT}/wrest/components/attributes_container/alias_accessors"
16
17
 
17
18
  module Wrest::Components
18
19
 
@@ -57,7 +58,11 @@ module Wrest::Components
57
58
  module AttributesContainer
58
59
  def self.included(klass) #:nodoc:
59
60
  klass.extend AttributesContainer::ClassMethods
60
- klass.class_eval{ include AttributesContainer::InstanceMethods }
61
+ klass.extend AttributesContainer::Typecaster::Helpers
62
+ klass.class_eval do
63
+ include AttributesContainer::InstanceMethods
64
+ include AttributesContainer::AliasAccessors
65
+ end
61
66
  end
62
67
 
63
68
  def self.build_attribute_getter(attribute_name) #:nodoc:
@@ -74,9 +79,9 @@ module Wrest::Components
74
79
 
75
80
  module ClassMethods
76
81
  # This macro explicitly creates getter, setter and query methods on
77
- # a class, overriding any exisiting methods with the same names.
78
- # This can be used when attribute names clash with method names;
79
- # an example would be Rails REST services which frequently make use
82
+ # an AttributesContainer, overriding any exisiting methods with the same names.
83
+ # This can be used when attribute names clash with existing method names;
84
+ # an example would be Rails REST resources which frequently make use
80
85
  # an attribute named <tt>id</tt> which clashes with Object#id. Also,
81
86
  # this can be used as a performance optimisation if the incoming
82
87
  # attributes are known beforehand.
@@ -89,6 +94,19 @@ module Wrest::Components
89
94
  )
90
95
  end
91
96
  end
97
+
98
+ # This is a convenience macro which includes
99
+ # Wrest::Components::AttributesContainer::Typecaster into
100
+ # the class (effectively overwriting this method) before delegating to
101
+ # the actual typecast method that is a part of that module.
102
+ # This saves us the effort of explicitly doing the include. Easy to use API is king.
103
+ #
104
+ # Remember that using typecast carries a performance penalty.
105
+ # See Wrest::Components::AttributesContainer::Typecaster for the actual docs.
106
+ def typecast(cast_map)
107
+ self.class_eval{ include Wrest::Components::AttributesContainer::Typecaster }
108
+ self.typecast cast_map
109
+ end
92
110
  end
93
111
 
94
112
  module InstanceMethods
@@ -119,7 +137,7 @@ module Wrest::Components
119
137
  def method_missing(method_sym, *arguments)
120
138
  method_name = method_sym.to_s
121
139
  attribute_name = method_name.gsub(/(\?$)|(=$)/, '')
122
- if @attributes.include?(attribute_name.to_sym) || method_name.last == '='
140
+ if @attributes.include?(attribute_name.to_sym) || method_name.last == '=' || method_name.last == '?'
123
141
  case method_name.last
124
142
  when '='
125
143
  self.instance_eval AttributesContainer.build_attribute_setter(attribute_name)
@@ -49,7 +49,7 @@ module Wrest
49
49
 
50
50
  protected
51
51
  def do_mutate(tuple)
52
- raise Wrest::Exceptions::MethodNotOverriddenException
52
+ raise Wrest::Exceptions::MethodNotOverridden
53
53
  end
54
54
  end
55
55
  end
@@ -17,7 +17,7 @@ module Wrest
17
17
  # the content type
18
18
  def self.lookup(content_type)
19
19
  translator = CONTENT_TYPES[content_type]
20
- translator || (raise Wrest::Exceptions::UnsupportedContentTypeException.new("Unsupported content type #{content_type}"))
20
+ translator || (raise Wrest::Exceptions::UnsupportedContentType.new("Unsupported content type #{content_type}"))
21
21
  end
22
22
  end
23
23
  end
@@ -15,7 +15,7 @@ module Wrest
15
15
 
16
16
  # A convenience method equivalent to Wrest::Uri.new(string)
17
17
  def to_uri(options = {})
18
- Wrest::Uri.new(self, options)
18
+ Wrest::Uri.new(self.strip, options)
19
19
  end
20
20
  end
21
21
  end
@@ -1,6 +1,18 @@
1
1
  module Wrest
2
2
  module Exceptions #:nodoc:
3
+ # Raised when a base method that should be overriden
4
+ # is invoked on a subclass that has not implemented it.
5
+ class MethodNotOverridden < StandardError
6
+ end
7
+
8
+ # Raised when a translator for an unregisterd response content type
9
+ # is requested. See Translators.
10
+ class UnsupportedContentType < StandardError
11
+ end
12
+
13
+ # Raised when a request auto redirects more times than are allowed
14
+ # by its follow_redirects_limit. See Wrest::Http::Redirection.
15
+ class AutoRedirectLimitExceeded < StandardError
16
+ end
3
17
  end
4
18
  end
5
- require "#{WREST_ROOT}/wrest/exceptions/unsupported_content_type_exception"
6
- require "#{WREST_ROOT}/wrest/exceptions/method_not_overridden_exception"
@@ -10,6 +10,8 @@
10
10
  module Wrest::Http
11
11
  class Get < Request
12
12
  def initialize(wrest_uri, parameters = {}, headers = {}, options = {})
13
+ follow_redirects = options[:follow_redirects]
14
+ options[:follow_redirects] = (follow_redirects == nil ? true : follow_redirects)
13
15
  super(
14
16
  wrest_uri,
15
17
  Net::HTTP::Get,