restfulie 0.1.0.beta1 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. data/README.textile +87 -99
  2. data/Rakefile +14 -109
  3. data/lib/restfulie.rb +183 -23
  4. metadata +12 -197
  5. data/Gemfile +0 -28
  6. data/Gemfile.lock +0 -128
  7. data/LICENSE +0 -17
  8. data/lib/restfulie/client.rb +0 -26
  9. data/lib/restfulie/client/base.rb +0 -36
  10. data/lib/restfulie/client/cache.rb +0 -11
  11. data/lib/restfulie/client/cache/basic.rb +0 -76
  12. data/lib/restfulie/client/cache/fake.rb +0 -15
  13. data/lib/restfulie/client/cache/http_ext.rb +0 -123
  14. data/lib/restfulie/client/cache/restrictions.rb +0 -13
  15. data/lib/restfulie/client/configuration.rb +0 -67
  16. data/lib/restfulie/client/dsl.rb +0 -66
  17. data/lib/restfulie/client/entry_point.rb +0 -61
  18. data/lib/restfulie/client/ext/atom_ext.rb +0 -14
  19. data/lib/restfulie/client/ext/http_ext.rb +0 -22
  20. data/lib/restfulie/client/ext/json_ext.rb +0 -16
  21. data/lib/restfulie/client/feature.rb +0 -5
  22. data/lib/restfulie/client/feature/base.rb +0 -75
  23. data/lib/restfulie/client/feature/base_request.rb +0 -35
  24. data/lib/restfulie/client/feature/cache.rb +0 -16
  25. data/lib/restfulie/client/feature/enhance_response.rb +0 -12
  26. data/lib/restfulie/client/feature/follow_request.rb +0 -41
  27. data/lib/restfulie/client/feature/history.rb +0 -26
  28. data/lib/restfulie/client/feature/history_request.rb +0 -19
  29. data/lib/restfulie/client/feature/open_search.rb +0 -21
  30. data/lib/restfulie/client/feature/open_search/pattern_matcher.rb +0 -25
  31. data/lib/restfulie/client/feature/serialize_body.rb +0 -32
  32. data/lib/restfulie/client/feature/setup_header.rb +0 -22
  33. data/lib/restfulie/client/feature/throw_error.rb +0 -41
  34. data/lib/restfulie/client/feature/verb.rb +0 -119
  35. data/lib/restfulie/client/http.rb +0 -7
  36. data/lib/restfulie/client/http/cache.rb +0 -28
  37. data/lib/restfulie/client/http/error.rb +0 -77
  38. data/lib/restfulie/client/http/response_holder.rb +0 -29
  39. data/lib/restfulie/client/master_delegator.rb +0 -31
  40. data/lib/restfulie/client/mikyung.rb +0 -15
  41. data/lib/restfulie/client/mikyung/concatenator.rb +0 -18
  42. data/lib/restfulie/client/mikyung/core.rb +0 -70
  43. data/lib/restfulie/client/mikyung/languages.rb +0 -11
  44. data/lib/restfulie/client/mikyung/languages/german.rb +0 -24
  45. data/lib/restfulie/client/mikyung/languages/portuguese.rb +0 -23
  46. data/lib/restfulie/client/mikyung/rest_process_model.rb +0 -191
  47. data/lib/restfulie/client/mikyung/steady_state_walker.rb +0 -38
  48. data/lib/restfulie/client/mikyung/then_condition.rb +0 -39
  49. data/lib/restfulie/client/mikyung/when_condition.rb +0 -57
  50. data/lib/restfulie/common.rb +0 -18
  51. data/lib/restfulie/common/converter.rb +0 -43
  52. data/lib/restfulie/common/converter/atom.rb +0 -12
  53. data/lib/restfulie/common/converter/atom/base.rb +0 -91
  54. data/lib/restfulie/common/converter/atom/builder.rb +0 -111
  55. data/lib/restfulie/common/converter/atom/helpers.rb +0 -17
  56. data/lib/restfulie/common/converter/json.rb +0 -12
  57. data/lib/restfulie/common/converter/json/base.rb +0 -87
  58. data/lib/restfulie/common/converter/json/builder.rb +0 -102
  59. data/lib/restfulie/common/converter/json/helpers.rb +0 -17
  60. data/lib/restfulie/common/converter/open_search.rb +0 -16
  61. data/lib/restfulie/common/converter/open_search/descriptor.rb +0 -32
  62. data/lib/restfulie/common/converter/values.rb +0 -33
  63. data/lib/restfulie/common/converter/xml.rb +0 -14
  64. data/lib/restfulie/common/converter/xml/base.rb +0 -63
  65. data/lib/restfulie/common/converter/xml/builder.rb +0 -113
  66. data/lib/restfulie/common/converter/xml/helpers.rb +0 -17
  67. data/lib/restfulie/common/converter/xml/link.rb +0 -30
  68. data/lib/restfulie/common/converter/xml/links.rb +0 -21
  69. data/lib/restfulie/common/core_ext.rb +0 -1
  70. data/lib/restfulie/common/core_ext/hash.rb +0 -18
  71. data/lib/restfulie/common/error.rb +0 -19
  72. data/lib/restfulie/common/links.rb +0 -9
  73. data/lib/restfulie/common/logger.rb +0 -19
  74. data/lib/restfulie/common/representation.rb +0 -3
  75. data/lib/restfulie/common/representation/atom.rb +0 -20
  76. data/lib/restfulie/common/representation/atom/atom.rng +0 -597
  77. data/lib/restfulie/common/representation/atom/base.rb +0 -142
  78. data/lib/restfulie/common/representation/atom/category.rb +0 -41
  79. data/lib/restfulie/common/representation/atom/entry.rb +0 -59
  80. data/lib/restfulie/common/representation/atom/factory.rb +0 -43
  81. data/lib/restfulie/common/representation/atom/feed.rb +0 -110
  82. data/lib/restfulie/common/representation/atom/link.rb +0 -68
  83. data/lib/restfulie/common/representation/atom/person.rb +0 -48
  84. data/lib/restfulie/common/representation/atom/source.rb +0 -59
  85. data/lib/restfulie/common/representation/atom/tag_collection.rb +0 -38
  86. data/lib/restfulie/common/representation/atom/xml.rb +0 -90
  87. data/lib/restfulie/common/representation/generic.rb +0 -22
  88. data/lib/restfulie/common/representation/json.rb +0 -13
  89. data/lib/restfulie/common/representation/json/base.rb +0 -27
  90. data/lib/restfulie/common/representation/json/keys_as_methods.rb +0 -74
  91. data/lib/restfulie/common/representation/json/link.rb +0 -29
  92. data/lib/restfulie/common/representation/json/link_collection.rb +0 -23
  93. data/lib/restfulie/common/representation/links.rb +0 -11
  94. data/lib/restfulie/server.rb +0 -25
  95. data/lib/restfulie/server/action_controller.rb +0 -11
  96. data/lib/restfulie/server/action_controller/base.rb +0 -48
  97. data/lib/restfulie/server/action_controller/params_parser.rb +0 -100
  98. data/lib/restfulie/server/action_controller/patch.rb +0 -6
  99. data/lib/restfulie/server/action_controller/restful_responder.rb +0 -12
  100. data/lib/restfulie/server/action_controller/trait.rb +0 -9
  101. data/lib/restfulie/server/action_controller/trait/cacheable.rb +0 -81
  102. data/lib/restfulie/server/action_controller/trait/created.rb +0 -17
  103. data/lib/restfulie/server/action_view.rb +0 -10
  104. data/lib/restfulie/server/action_view/helpers.rb +0 -50
  105. data/lib/restfulie/server/action_view/template_handlers.rb +0 -30
  106. data/lib/restfulie/server/action_view/template_handlers/tokamak.rb +0 -21
  107. data/lib/restfulie/server/configuration.rb +0 -24
  108. data/lib/restfulie/server/controller.rb +0 -74
  109. data/lib/restfulie/server/core_ext.rb +0 -1
  110. data/lib/restfulie/server/core_ext/array.rb +0 -61
  111. data/lib/restfulie/version.rb +0 -14
data/README.textile CHANGED
@@ -1,138 +1,126 @@
1
- h1. Web site
2
-
3
- Restfulie's website can be found at "http://restfulie.caelumobjects.com":http://restfulie.caelumobjects.com
4
- From there you can see some videos and read the "Restfulie from Scratch guide":http://restfulie.caelumobjects.com/caelumobjects-restful-rails.pdf.
5
-
6
1
  h1. Quit pretending
7
2
 
8
- CRUD through HTTP is a good step forward to using resources and becoming RESTful, another step further into it is to make use of hypermedia and semantic meaningful media types: this gem allows you to do it really fast.
9
-
10
- You can read the "article on using the web for real":http://guilhermesilveira.wordpress.com/2009/11/03/quit-pretending-use-the-web-for-real-restfulie/ which gives an introduction to hypermedia/resources/services.
3
+ CRUD through HTTP is a good step forward to using resources and becoming RESTFul, another step further into is to make use of hypermedia based services and this gem allows you to do it really fast.
11
4
 
12
- h2. Why would I use restfulie?
5
+ h1. Restfulie: client-side
13
6
 
14
- # Easy --> writing hypermedia and semantic meaningful media type aware clients
15
- # Small -> it's not a bloated solution with a huge list of APIs
16
- # HATEOAS --> clients you are unaware of will not bother if you change
17
- # HATEOAS --> consumed resources will not affect your software whenever they change their flow
18
- # Adaptability --> clients are able to adapt to your changes
19
-
20
- h2. Simple server example
21
-
22
- In the server side, all you need to do is notify inherited_resources which media types you are able to represent your resource:
7
+ Here you can see how to access a resource and its services through the restfulie API:
23
8
 
24
9
  <pre>
25
- class OrdersController < ApplicationController
26
- include Restfulie::Server::ActionController::Base
10
+ order = Order.from_web resource_uri # retrieves order resource (xml/atom/json support)
27
11
 
28
- respond_to :atom, :html, :xml, :commerce, :opensearch
12
+ puts "Order price is #{order.price}"
29
13
 
30
- def index
31
- respond_with @orders = Order.all
32
- end
14
+ order.pay payment # sends a post request to pay this order
33
15
 
34
- def show
35
- respond_with @order = Order.find(params[:id])
36
- end
37
-
38
- end
16
+ order.cancel # sends a delete request
39
17
  </pre>
40
18
 
41
- That's it. Restfulie will take care of rendering a valid representation according to content negotiation. You can configure the rendering process through a custom tokamak view:
19
+ h1. Restfulie: server-side
20
+
21
+ This is a simple example how to make your state changes available to your resource consumers:
42
22
 
43
23
  <pre>
44
- collection(@orders) do |collection|
45
- collection.values do |value|
46
- value.id = orders_url
24
+ class Order < ActiveRecord::Base
25
+ def following_states
26
+ states = [ {:controller => :orders, :action => :show } ]
27
+ states << {:controller => :orders, :action => :destroy} if can_cancel?
28
+ states << {:controller => :orders, :action => :pay, :id => id} if can_pay?
29
+ states << {:controller => :payments, :action => :show, :payment_id => payment.id } if paied?
30
+ states
47
31
  end
48
-
49
- collection.link("create", orders_url)
50
- collection.members
51
- end
52
32
  </pre>
53
33
 
54
- You can go through an entire application by "watching this video":http://guilhermesilveira.wordpress.com or "downloading an example application contained within the restfulie ":http://github.com/caelum/restfulie/tree/master/full-examples/rest_from_scratch/
55
-
56
- h2. Simple client example
34
+ h2. Installing
57
35
 
58
- The following example is a partial REST client implementation that navigates through some relations:
36
+ Just add in your environment.rb the following line:
59
37
 
60
38
  <pre>
61
- order = Restfulie.at('http://localhost:3000/orders/1').get!
62
- order.items.each { |item| puts items }
63
- receipt = order.payment.post! { :amount => 500 }
64
- puts receipt.id
39
+ config.gem "restfulie", :source => "http://gemcutter.org"
65
40
  </pre>
66
41
 
67
- In order to create a full REST client, "watch this video":http://guilhermesilveira.wordpress.com.
42
+ Execute:
43
+ <pre>rake gems:install</pre>
68
44
 
69
- h2. Full examples
45
+ or, if you prefer to install as a plugin:
70
46
 
71
- You can view an entire application running Restfulie under *spec/integration/order/server* and *spec/integration/order/client* in this git repository.
47
+ script/plugin install git://github.com/caelum/restfulie.git
72
48
 
73
- "You can also download a full example of a REST based agent and server":http://github.com/caelum/restfulie/tree/master/full-examples/rest_from_scratch/ using Restfulie, according to the Rest Architecture Maturity Model.
49
+ h2. Typical Restful Example
74
50
 
75
- h2. Documentation
51
+ Trying to follow the definition of a restful application supporting resources with hypermedia content, a typical restful resource would be:
76
52
 
77
- Appart from the simple server and client examples provided here, you can find the following links useful:
53
+ <pre>
54
+ <order>
55
+ <product>basic rails course</product>
56
+ <product>restful training</product>
57
+ <atom:link rel="refresh" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/>
58
+ <atom:link rel="update" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/>
59
+ <atom:link rel="pay" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/>
60
+ <atom:link rel="destroy" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/>
61
+ </order>
62
+ </pre>
78
63
 
79
- * "RDocs":http://rdoc.info/projects/caelum/restfulie
80
- * "Official website":http://restfulie.caelumobjects.com
81
- * "How-tos":http://restfulie.caelumobjects.com/caelumobjects-restful-rails.pdf
82
- * "Buying through Rest: Rest to the enterprise (video)":http://guilhermesilveira.wordpress.com/2010/04/13/buying-through-rest-applying-rest-to-the-enterprise/
64
+ h2. Client Usage
83
65
 
84
- h2. Installing
66
+ One should first acquire the representation from the server through your common GET process and process it through the usual from_* methods:
67
+ <pre>xml = Net::HTTP.get(URI.parse('http://www.caelum.com.br/orders/1'))
68
+ order = Order.from_xml(xml)</pre>
85
69
 
86
- Execute:
70
+ And now you can invoke all those actions in order to change your resource's state:
87
71
 
88
72
  <pre>
89
- gem install restfulie
73
+ order.refresh
74
+ order.update
75
+ order.destroy
76
+ order.pay(payment)
90
77
  </pre>
91
78
 
92
- For use in Rails 2.3, make sure to require the responders_backport gem in addition to the restfulie gem, either in environment.rb or in the Gemfile.
79
+ Note that:
80
+ * refresh is get
81
+ * update is put (and you have to put everything back)
82
+ * destroy is delete
83
+ * pay (unknown methods) is post
84
+
85
+ h2. Resource format support
86
+
87
+ Restfulie currently supports xml+atom and will soon expand its support to xml+rel links and json+links supports. Those new formats will also support automatic http verb detection - if possible and reasonable.
88
+
89
+
90
+ h2. Help
91
+
92
+ If you are looking for or want to help, let us know at the mailing list: http://groups.google.com/group/restfulie
93
+
94
+ h2. How to customize your request
95
+
96
+ By default, restfulie uses the following table:
93
97
 
94
- h2. Building the project
98
+ * destroy, cancel and delete send a DELETE request
99
+ * update sends a POST request
100
+ * refresh, reload, show sends a GET request
101
+ * other methods sends a POST request
95
102
 
96
- If you want to build the project and run its tests, remember to install all (client and server) required gem.
97
- "Bundler":http://gembundler.com/ is required to easily manage dependencies
103
+ If you want to use a custom http verb in order to send your request, you can do it by setting the optional string 'method':
98
104
 
99
105
  <pre>
100
- bundle install
101
- rake test:spec test:integration
106
+ order.update(:method=>"post")
102
107
  </pre>
103
108
 
104
- <script type="text/javascript">
105
- var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
106
- document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
107
- </script>
108
- <script type="text/javascript">
109
- try {
110
- var pageTracker = _gat._getTracker("UA-11770776-1");
111
- pageTracker._trackPageview();
112
- } catch(err) {}</script>
113
-
114
- h2. Contributions
115
-
116
- Restfulie was created and is maintained by "Caelum":http://caelumobjects.com, and has received enormous contributions from all those developers:
117
-
118
- Project Leader
119
- Guilherme Silveira, "Caelum":http://www.caelum.com.br
120
-
121
- Caue Guerra, caelum
122
- George Guimaraes, abril and plataforma
123
- Fabio Akita
124
- Diego Carrion
125
- Leandro Silva
126
- Gavin-John Noonan
127
- Antonio Marques
128
- Luis Cipriani, abril
129
- Everton Ribeiro, abril
130
- Paulo Ahagon, abril
131
- Elomar França
132
- Thomas Stefano
133
- "David Paniz":"http://www.caelum.com.br"
134
- "Caike Souza":"http://www.caikesouza.com/blog"
135
-
136
- h2. Rails 2
137
-
138
- If you want to use Restfulie with Rails2, please use any release up to 0.9.2 and its minor releases.
109
+ h2. License
110
+
111
+ /***
112
+ * Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
113
+ * All rights reserved.
114
+ *
115
+ * Licensed under the Apache License, Version 2.0 (the "License");
116
+ * you may not use this file except in compliance with the License.
117
+ * You may obtain a copy of the License at
118
+ *
119
+ * http://www.apache.org/licenses/LICENSE-2.0
120
+ *
121
+ * Unless required by applicable law or agreed to in writing, software
122
+ * distributed under the License is distributed on an "AS IS" BASIS,
123
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124
+ * See the License for the specific language governing permissions and
125
+ * limitations under the License.
126
+ */
data/Rakefile CHANGED
@@ -2,18 +2,14 @@ require 'rubygems'
2
2
  require 'rubygems/specification'
3
3
  require 'rake'
4
4
  require 'rake/gempackagetask'
5
- require 'rake/rdoctask'
6
- require 'rspec'
7
- require 'rspec/core'
8
- require 'rspec/core/rake_task'
9
- require File.expand_path('lib/restfulie')
5
+ require 'spec/rake/spectask'
10
6
 
11
7
  GEM = "restfulie"
12
- GEM_VERSION = Restfulie::VERSION
13
- SUMMARY = "Hypermedia aware resource based library in ruby (client side) and ruby on rails (server side)."
14
- AUTHOR = "Guilherme Silveira, Caue Guerra, Luis Cipriani, Everton Ribeiro, George Guimaraes, Paulo Ahagon, Several contributors"
15
- EMAIL = "guilherme.silveira@caelum.com.br"
16
- HOMEPAGE = "http://restfulie.caelumobjects.com"
8
+ GEM_VERSION = "0.1"
9
+ SUMMARY = "This is a small cute plugin to show how to implement hypermedia based services in a easy way using rails."
10
+ AUTHOR = "Guilherme Silveira, Caue Guerra"
11
+ EMAIL = "guilherme.silveira@caelum.com.br"
12
+ HOMEPAGE = "http://github.com/caelum/restfulie"
17
13
 
18
14
  spec = Gem::Specification.new do |s|
19
15
  s.name = GEM
@@ -21,114 +17,27 @@ spec = Gem::Specification.new do |s|
21
17
  s.platform = Gem::Platform::RUBY
22
18
  s.summary = SUMMARY
23
19
  s.require_paths = ['lib']
24
- s.files = FileList['lib/**/*.rb', '[A-Z]*', 'lib/**/*.rng'].to_a
25
- s.add_dependency("nokogiri", [">= 1.4.2"])
26
- s.add_dependency("actionpack", [">= 2.3.2"])
27
- s.add_dependency("activesupport", [">= 2.3.2"])
28
- s.add_dependency("json_pure", [">= 1.2.4"])
20
+ s.files = FileList['lib/**/*.rb', '[A-Z]*'].to_a
21
+
22
+ # s.add_dependency(%q<rubigen>, [">= 1.3.4"])
29
23
 
30
24
  s.author = AUTHOR
31
25
  s.email = EMAIL
32
26
  s.homepage = HOMEPAGE
33
27
  end
34
28
 
35
- module FakeServer
36
- def self.wait_server(port=3000)
37
- (1..15).each do
38
- begin
39
- Net::HTTP.get(URI.parse("http://localhost:#{port}/"))
40
- return
41
- rescue
42
- sleep 1
43
- end
44
- end
45
- raise "Waited for the server but it did not finish"
46
- end
47
-
48
- def self.start_sinatra
49
- IO.popen("cd tests && ruby ./spec/requests/fake_server.rb") do |pipe|
50
- wait_server 4567
51
- yield
52
- Process.kill 'INT', pipe.pid
53
- end
54
- end
55
-
56
- def self.run(setup, process)
57
- success = IO.popen(setup) do |pipe|
58
- wait_server
59
- success = system "rake spec"
60
- Process.kill 'INT', pipe.pid
61
- success
62
- end
63
- if !success
64
- raise "Some of the specs failed"
65
- end
66
- end
67
-
68
- def self.start_server_and_run_spec(target_dir)
69
- success = Dir.chdir(File.join(File.dirname(__FILE__), target_dir)) do
70
- system('rake db:drop db:create db:migrate')
71
- self.run "rails server", "rake spec"
72
- end
73
- end
74
-
75
- end
76
-
77
- # optionally loads a task if the required gems exist
78
- def optionally
79
- begin
80
- yield
81
- rescue LoadError; end
82
- end
83
-
84
- namespace :test do
85
-
86
- task :spec do
87
- FakeServer.start_sinatra do
88
- FakeServer.start_server_and_run_spec "tests"
89
- end
90
- end
91
-
92
- task :integration do
93
- FakeServer.start_server_and_run_spec "full-examples/rest_from_scratch/part_1"
94
- FakeServer.start_server_and_run_spec "full-examples/rest_from_scratch/part_2"
95
- FakeServer.start_server_and_run_spec "full-examples/rest_from_scratch/part_3"
96
- end
97
-
98
- task :sinatra do
99
- FakeServer.start_sinatra do
100
- puts "Press something to quit"
101
- gets
102
- end
103
- end
104
-
105
- task :all => ["spec","integration"]
106
-
107
- end
108
-
109
- RSpec::Core::RakeTask.new(:spec) do |t|
110
- # t.spec_files = FileList['spec_*.rb']
111
- t.spec_opts = ['--colour', '--format progress']
29
+ Spec::Rake::SpecTask.new do |t|
30
+ t.spec_files = FileList['spec/**/*_spec.rb']
31
+ t.spec_opts = %w(-fs -fh:doc/specs.html --color)
112
32
  end
113
33
 
114
34
  Rake::GemPackageTask.new(spec) do |pkg|
115
35
  pkg.gem_spec = spec
116
36
  end
117
37
 
118
- Rake::RDocTask.new("rdoc") do |rdoc|
119
- rdoc.options << '--line-numbers' << '--inline-source'
120
- end
121
-
122
- optionally do
123
- require 'yard'
124
- YARD::Rake::YardocTask.new do |t|
125
- t.files = ['lib/restfulie/**/*.rb', 'README.textile']
126
- end
127
- end
128
-
129
38
  desc "Install the gem locally"
130
39
  task :install => [:package] do
131
- sh %{gem install pkg/#{GEM}-#{GEM_VERSION} -l}
40
+ sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
132
41
  end
133
42
 
134
43
  desc "Create a gemspec file"
@@ -139,8 +48,4 @@ task :make_spec do
139
48
  end
140
49
 
141
50
  desc "Builds the project"
142
- task :build => ["install", "test:spec"]
143
-
144
- desc "Default build will run specs"
145
- task :default => :build
146
-
51
+ task :build => :spec
data/lib/restfulie.rb CHANGED
@@ -1,34 +1,194 @@
1
- $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__))
1
+ require 'net/http'
2
+ require 'uri'
2
3
 
3
- require 'restfulie/version'
4
- require 'restfulie/common'
5
- require 'restfulie/client'
6
- require 'restfulie/server'
7
-
8
- # Shortcut to RestfulieDsl
9
4
  module Restfulie
5
+ def to_json
6
+ super :methods => :following_states
7
+ end
10
8
 
11
- # creates a new entry point for executing requests
12
- def self.at(uri)
13
- Restfulie.use.at(uri)
9
+ def to_xml(options = {})
10
+ controller = options[:controller]
11
+ return super if controller.nil?
12
+
13
+ options[:skip_types] = true
14
+ super options do |xml|
15
+ if respond_to?(:following_states)
16
+ states = following_states
17
+ states = [states] if states.class.to_s != 'Array'
18
+ states.each do |action|
19
+ rel = action[:action]
20
+ if action[:rel]
21
+ rel = action[:rel]
22
+ action[:rel] = nil
23
+ end
24
+ translate_href = controller.url_for(action)
25
+ if options[:use_name_based_link]
26
+ xml.tag!(rel, translate_href)
27
+ else
28
+ xml.tag!('atom:link', 'xmlns:atom' => 'http://www.w3.org/2005/Atom', :rel => rel, :href => translate_href)
29
+ end
30
+ end
31
+ end
32
+ end
14
33
  end
15
34
 
16
- def self.using(&block)
17
- RestfulieUsing.new.instance_eval(&block)
35
+ def create_method(name, &block)
36
+ self.class.send(:define_method, name, &block)
18
37
  end
19
-
20
- def self.use(&block)
21
- if block_given?
22
- Restfulie::Client::Dsl.new.instance_eval(&block)
23
- else
24
- Restfulie::Client::Dsl.new
38
+
39
+ end
40
+
41
+ module ActiveRecord
42
+ class Base
43
+
44
+ include Restfulie
45
+ attr_accessor :_possible_states
46
+ attr_accessor :_came_from
47
+
48
+ def self.add_states(result, states)
49
+ result._possible_states = {}
50
+ states.each do |state|
51
+ result._possible_states[state["rel"]] = state
52
+ end
53
+ def result.respond_to?(sym)
54
+ has_state(sym.to_s) || super(sym)
55
+ end
56
+
57
+ def result.has_state(name)
58
+ !@_possible_states[name].nil?
59
+ end
60
+
61
+ states.each do |state|
62
+ name = state["rel"]
63
+ self.module_eval do
64
+ def current_method
65
+ caller[0]=~/`(.*?)'/
66
+ $1
67
+ end
68
+ def temp_method(options = {}, &block)
69
+ name = current_method
70
+ state = _possible_states[name]
71
+ data = options[:data] || {}
72
+ url = URI.parse(state["href"])
73
+ get = false
74
+
75
+ # gs: i dont know how to meta play here! i suck
76
+ if options[:method]=="delete"
77
+ req = Net::HTTP::Delete.new(url.path)
78
+ elsif options[:method]=="put"
79
+ req = Net::HTTP::Put.new(url.path)
80
+ elsif options[:method]=="get"
81
+ req = Net::HTTP::Get.new(url.path)
82
+ get = true
83
+ elsif options[:method]=="post"
84
+ req = Net::HTTP::Post.new(url.path)
85
+ elsif ['destroy','delete','cancel'].include? name
86
+ req = Net::HTTP::Delete.new(url.path)
87
+ elsif ['refresh', 'reload', 'show', 'latest'].include? name
88
+ req = Net::HTTP::Get.new(url.path)
89
+ get = true
90
+ else
91
+ req = Net::HTTP::Post.new(url.path)
92
+ end
93
+
94
+ req.set_form_data(data)
95
+ req.add_field("Accept", "text/xml") if _came_from == :xml
96
+
97
+ http = Net::HTTP.new(url.host, url.port)
98
+ response = http.request(req)
99
+ return yield(response) if !block.nil?
100
+ if get
101
+ case response.content_type
102
+ when "application/xml"
103
+ content = response.body
104
+ hash = Hash.from_xml content
105
+ return hash if hash.keys.length == 0
106
+ raise "unable to parse an xml with more than one root element" if hash.keys.length>1
107
+ key = hash.keys[0]
108
+ type = key.camelize.constantize
109
+ return type.from_xml content
110
+ else
111
+ raise :unknown_content_type
112
+ end
113
+ end
114
+ response
115
+
116
+ end
117
+ alias_method name, :temp_method
118
+ undef :temp_method
119
+ end
120
+ end
121
+
122
+ result
123
+ end
124
+
125
+ def self.from_web(uri)
126
+ url = URI.parse(uri)
127
+ req = Net::HTTP::Get.new(url.path)
128
+ http = Net::HTTP.new(url.host, url.port)
129
+ res = http.request(req)
130
+ raise :invalid_request, res if res.code != "200"
131
+ case res.content_type
132
+ when "application/xml"
133
+ self.from_xml res.body
134
+ when "application/json"
135
+ self.from_json res.body
136
+ else
137
+ raise :unknown_content_type
138
+ end
139
+ end
140
+
141
+ # basic code from Matt Pulver
142
+ # found at http://www.xcombinator.com/2008/08/11/activerecord-from_xml-and-from_json-part-2/
143
+ # addapted to support links
144
+ def self.from_hash( hash )
145
+ h = {}
146
+ h = hash.dup if hash
147
+ links = nil
148
+ h.each do |key,value|
149
+ case value.class.to_s
150
+ when 'Array'
151
+ if key=="link"
152
+ links = h[key]
153
+ h.delete("link")
154
+ else
155
+ h[key].map! { |e| reflect_on_association(key.to_sym ).klass.from_hash e }
156
+ end
157
+ when /\AHash(WithIndifferentAccess)?\Z/
158
+ if key=="link"
159
+ links = [h[key]]
160
+ h.delete("link")
161
+ else
162
+ h[key] = reflect_on_association(key.to_sym ).klass.from_hash value
163
+ end
164
+ end
165
+ end
166
+ result = self.new h
167
+ add_states(result, links) unless links.nil?
168
+ result
169
+ end
170
+
171
+ def self.from_json( json )
172
+ from_hash safe_json_decode( json )
173
+ end
174
+
175
+ # The xml has a surrounding class tag (e.g. ship-to),
176
+ # but the hash has no counterpart (e.g. 'ship_to' => {} )
177
+ def self.from_xml( xml )
178
+ hash = Hash.from_xml xml
179
+ head = hash[self.to_s.underscore]
180
+ result = self.from_hash head
181
+ return nil if result.nil?
182
+ result._came_from = :xml
183
+ result
25
184
  end
26
185
  end
27
-
28
186
  end
29
187
 
30
- class RestfulieUsing
31
- def method_missing(sym, *args)
32
- @current = "Restfulie::Client::HTTP::#{sym.to_s.classify}".constantize.new(@current || Restfulie::Client::HTTP::RequestAdapter.new, *args)
33
- end
188
+ def safe_json_decode( json )
189
+ return {} if !json
190
+ begin
191
+ ActiveSupport::JSON.decode json
192
+ rescue ; {} end
34
193
  end
194
+ # end of code based on Matt Pulver's