roar-rails 0.1.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.markdown +5 -0
  3. data/Gemfile +1 -1
  4. data/README.markdown +12 -8
  5. data/gemfiles/Gemfile.rails3-0 +2 -0
  6. data/gemfiles/Gemfile.rails3-2 +1 -0
  7. data/lib/roar-rails.rb +4 -6
  8. data/lib/roar/rails/controller_additions.rb +26 -10
  9. data/lib/roar/rails/hal.rb +3 -3
  10. data/lib/roar/rails/railtie.rb +5 -0
  11. data/lib/roar/rails/responder.rb +1 -1
  12. data/lib/roar/rails/validations_representer.rb +3 -3
  13. data/lib/roar/rails/version.rb +1 -1
  14. data/roar-rails.gemspec +3 -3
  15. data/test/consume_test.rb +64 -2
  16. data/test/dummy/app/representers/band_representer.rb +2 -2
  17. data/test/dummy/app/representers/singer_alias_representer.rb +1 -1
  18. data/test/dummy/app/representers/singer_representer.rb +2 -2
  19. data/test/json_hal_renderer_test.rb +5 -13
  20. data/test/render_test.rb +1 -1
  21. data/test/responder_test.rb +44 -5
  22. data/test/test_helper.rb +9 -0
  23. metadata +66 -52
  24. data/test/dummy/Rakefile +0 -7
  25. data/test/dummy/app/helpers/application_helper.rb +0 -2
  26. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  27. data/test/dummy/app/views/musician/featured.html.erb +0 -1
  28. data/test/dummy/app/views/musician/featured_with_block.html.erb +0 -4
  29. data/test/dummy/app/views/musician/hamlet.html.haml +0 -1
  30. data/test/dummy/config/locales/en.yml +0 -5
  31. data/test/dummy/public/404.html +0 -26
  32. data/test/dummy/public/422.html +0 -26
  33. data/test/dummy/public/500.html +0 -26
  34. data/test/dummy/public/favicon.ico +0 -0
  35. data/test/dummy/public/javascripts/application.js +0 -2
  36. data/test/dummy/public/javascripts/controls.js +0 -965
  37. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  38. data/test/dummy/public/javascripts/effects.js +0 -1123
  39. data/test/dummy/public/javascripts/prototype.js +0 -4874
  40. data/test/dummy/public/javascripts/rails.js +0 -118
  41. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  42. data/test/dummy/script/rails +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7fbf7c943c30c7284ff2cf46861318701910e138
4
- data.tar.gz: e6e6cb5e5816418c71c75d023bbfc61dc4551def
3
+ metadata.gz: 1dbecd0bbc7e521087763fd2ff0313d7d3cda940
4
+ data.tar.gz: 3b14e95c24d51ee0003f97c4c079dafc62a40699
5
5
  SHA512:
6
- metadata.gz: 45631ddc9bfc4f96279e7b1f7538cf33473cf5f6cc5f5cb1abafb4b5b7a149a65e5ec67f95c25c63510821898c9a47d6c7b86aec31270399dbd821bb343339c0
7
- data.tar.gz: 5d20060c50ce90161bbd93ee41c81cb71e5544ad04c32e428dbf556e69b1d32153f63d7f5c1e5631d7db9bf6f60216d9286ea511cd6a955f35e6ceef4b57dca8
6
+ metadata.gz: 0bcfc77fe5ff9d9e0fdb48d962bad6ffd2f44fcbd7446d31b4fbf3f1c14bbadad93ead2356103024f2923d0d1104f0317c93398d5ede28b5e5beaa499d4b30d8
7
+ data.tar.gz: 87b69c8d04bfbf819c32b0bbe2dca74ac929d374f315837a87d449a76656eaf2b0ba090ef93b605bfeaa0579c34f91070c768f3f886be9e1902059dffebeb73c
data/CHANGES.markdown CHANGED
@@ -1,3 +1,8 @@
1
+ ## 1.0.0
2
+
3
+ * Requires Roar >= 1.0.0.
4
+ * In `#consume!` roar-rails now finds the correct representer for the `Content-type:` header. In former version the representer name was infered using the `Accept:` header, which was totally wrong. Thanks to @pgaertig for fixing that.
5
+
1
6
  ## 0.1.6
2
7
 
3
8
  * Added `ControllerAdditions::Render` to support `#render` in controller actions with representers.
data/Gemfile CHANGED
@@ -4,7 +4,7 @@ source "http://rubygems.org"
4
4
  gemspec
5
5
 
6
6
  group :test do
7
- #gem 'roar', path: "../roar" #">= 0.11.17"
7
+ # gem 'roar', path: "../roar" #">= 0.11.17"
8
8
  #gem 'representable', path: "../representable"
9
9
  gem 'rake', '10.1.0'
10
10
  end
data/README.markdown CHANGED
@@ -32,7 +32,7 @@ This will create the file `app/representers/band_representer.rb` with the follow
32
32
 
33
33
  ```ruby
34
34
  module BandRepresenter
35
- include Roar::Representer::JSON
35
+ include Roar::JSON
36
36
 
37
37
  property :id
38
38
  property :name
@@ -129,6 +129,7 @@ end
129
129
  ## Parsing incoming documents
130
130
 
131
131
  In `#create` and `#update` actions it is often necessary to parse the incoming representation and map it to a model instance. Use the `#consume!` method for this.
132
+ The client must provide a `Content-Type` request header with proper MIME type to let `#consume!` know which representer to use.
132
133
 
133
134
  ```ruby
134
135
  class SingersController < ApplicationController
@@ -143,17 +144,20 @@ class SingersController < ApplicationController
143
144
  end
144
145
  ```
145
146
 
146
- The `consume!` call will roughly do the following.
147
+ For instance, if content type is set to `application/xml` the `consume!` call will roughly do the following.
147
148
 
148
149
  ```ruby
149
150
  singer.
150
151
  extend(SingerRepresenter)
151
- from_json(request.body)
152
+ from_xml(request.body)
152
153
  ```
153
154
 
154
- So, `#consume!` helps you figuring out the representer module and reading the incoming document.
155
+ So, `#consume!` helps you figuring out the representer module and reading the incoming document. Just like Rails, depending on the registered MIME type for `Content-type` it picks the deserialize method (e.g. `from_json` vs. `from_xml`)
155
156
 
156
- Note that it respects settings from `#represents`. It uses the same mechanics known from `#respond_with` to choose a representer.
157
+ It is important to provide a known content type in the request. If it is missing or not supported by the responder
158
+ `#consume!` will raise an exception `Roar::Rails::ControllerAdditions::UnsupportedMediaType`. Unless you rescue the exception the action will stop and respond with HTTP status `406 Unsupported Media Type`.
159
+
160
+ Note that `#consume!` respects settings from `#represents`. It uses the same mechanics known from `#respond_with` to choose a representer.
157
161
 
158
162
  ```ruby
159
163
  consume!(singer, :represent_with => MusicianRepresenter)
@@ -165,7 +169,7 @@ If you prefer roar's decorator approach over extend, just go for it. roar-rails
165
169
 
166
170
  ```ruby
167
171
  class SingerRepresenter < Roar::Decorator
168
- include Roar::Representer::JSON
172
+ include Roar::JSON
169
173
 
170
174
  property :name
171
175
 
@@ -211,8 +215,8 @@ Any URL helpers from the Rails app are automatically available in representers.
211
215
 
212
216
  ```ruby
213
217
  module FruitRepresenter
214
- include Roar::Representer::JSON
215
- include Roar::Representer::Feature::Hypermedia
218
+ include Roar::JSON
219
+ include Roar::Hypermedia
216
220
 
217
221
  link :self do
218
222
  fruit_url self
@@ -5,3 +5,5 @@ gemspec :path => '../'
5
5
 
6
6
  gem 'railties', '~> 3.0.11'
7
7
  gem 'activerecord', '~> 3.0.11'
8
+ gem 'minitest', '4.7.5'
9
+
@@ -6,3 +6,4 @@ gemspec :path => '../'
6
6
 
7
7
  gem 'railties', '~> 3.2.13'
8
8
  gem 'activerecord', '~> 3.2.13'
9
+ gem 'minitest', '4.7.5'
data/lib/roar-rails.rb CHANGED
@@ -3,17 +3,15 @@ require "roar/representer"
3
3
  require "roar/decorator"
4
4
  require "roar/rails/railtie"
5
5
 
6
- module Roar::Representer
7
- autoload("XML", "roar/representer/xml")
8
- autoload("JSON", "roar/representer/json")
6
+ module Roar
7
+ autoload("XML", "roar/xml")
8
+ autoload("JSON", "roar/json")
9
9
 
10
10
  module JSON
11
11
  autoload("HAL", "roar/rails/hal")
12
12
  end
13
13
 
14
- module Feature
15
- autoload("Hypermedia", "roar/representer/feature/hypermedia")
16
- end
14
+ autoload("Hypermedia", "roar/hypermedia")
17
15
  end
18
16
 
19
17
 
@@ -17,17 +17,22 @@ module Roar::Rails
17
17
 
18
18
  module ClassMethods
19
19
  def represents(format, options)
20
- represents_options.add(format,options)
20
+ represents_options.add(format, options)
21
21
  respond_to format
22
22
  end
23
23
  end
24
24
 
25
25
 
26
+ # TODO: move into separate class so we don't pollute controller.
26
27
  def consume!(model, options={})
27
- format = formats.first # FIXME: i expected request.content_mime_type to do the job. copied from responder.rb. this will return the wrong format when the controller responds to :json and :xml and the Content-type is :xml (?)
28
+ content_type = request.content_type
29
+
30
+ format = Mime::Type.lookup(content_type).try(:symbol) or raise UnsupportedMediaType.new("Cannot consume unregistered media type '#{content_type.inspect}'")
31
+
32
+ parsing_method = compute_parsing_method(format)
28
33
  representer = prepare_model_for(format, model, options)
29
34
 
30
- representer.send(compute_parsing_method(format), incoming_string, options) # e.g. from_json("...")
35
+ representer.send(parsing_method, incoming_string, options) # e.g. from_json("...")
31
36
  model
32
37
  end
33
38
 
@@ -52,15 +57,23 @@ module Roar::Rails
52
57
  body.read
53
58
  end
54
59
 
55
- def render_to_body(options)
56
- if res = options[formats.first] and res.is_a?(Roar::Rails::Responder::Response)
57
- response.content_type = res.content_type
58
- return res.body
59
- end
60
+ # These methods deal with interfacing between the Roar Response object and
61
+ # ActionController, they simply pass the body of the Roar response up or do nothing
62
+ def _render_option_json(resource, options)
63
+ super(_resource_or_body(resource), options)
64
+ end
60
65
 
61
- super
66
+ def _render_option_xml(resource, options)
67
+ super(_resource_or_body(resource), options)
62
68
  end
63
69
 
70
+ def _render_option_hal(resource, options)
71
+ super(_resource_or_body(resource), options)
72
+ end
73
+
74
+ def _resource_or_body(resource)
75
+ resource.is_a?(Roar::Rails::Responder::Response) ? resource.body : resource
76
+ end
64
77
 
65
78
  # Include if you intend to use roar-rails with <tt>render json: model</tt>.
66
79
  module Render
@@ -70,4 +83,7 @@ module Roar::Rails
70
83
  end
71
84
  end
72
85
  end
73
- end
86
+
87
+ class UnsupportedMediaType < StandardError #:nodoc:
88
+ end
89
+ end
@@ -1,6 +1,6 @@
1
- require 'roar/representer/json/hal'
1
+ require 'roar/json/hal'
2
2
 
3
- Roar::Representer::JSON::HAL.class_eval do
3
+ Roar::JSON::HAL.class_eval do
4
4
  def to_hal(*args); to_json(*args); end
5
5
  def from_hal(*args); from_json(*args); end
6
- end
6
+ end
@@ -6,6 +6,11 @@ module Roar
6
6
  class Railtie < ::Rails::Railtie
7
7
  config.representer = ActiveSupport::OrderedOptions.new
8
8
 
9
+ rescue_responses = config.action_dispatch.rescue_responses || ActionDispatch::ShowExceptions.rescue_responses #newer or fallback to 3.0
10
+ rescue_responses.merge!(
11
+ 'Roar::Rails::ControllerAdditions::UnsupportedMediaType' => :unsupported_media_type
12
+ )
13
+
9
14
  initializer "roar.set_configs" do |app|
10
15
  ::Roar::Representer.module_eval do
11
16
  include app.routes.url_helpers
@@ -69,4 +69,4 @@ module Roar
69
69
  end
70
70
  end
71
71
  end
72
- end
72
+ end
@@ -6,14 +6,14 @@ module ValidatorsRepresenter
6
6
  class ValidatorClient
7
7
  attr_accessor :kind, :options
8
8
  end
9
-
9
+
10
10
  # Represents a single Validator instance.
11
11
  module ValidatorRepresenter
12
- include Roar::Representer::JSON
12
+ include Roar::JSON
13
13
  property :kind
14
14
  hash :options
15
15
  end
16
-
16
+
17
17
  # Represents an array of validators for an attribute.
18
18
  module AttributeValidators
19
19
  include Representable::JSON::Collection
@@ -1,5 +1,5 @@
1
1
  module Roar
2
2
  module Rails
3
- VERSION = "0.1.6"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
data/roar-rails.gemspec CHANGED
@@ -18,13 +18,13 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- s.add_runtime_dependency "roar", ">= 0.11.13"
21
+ s.add_runtime_dependency "roar", ">= 1.0.0", "<= 1.1.0"
22
22
  s.add_runtime_dependency "test_xml", ">= 0.1.6" # TODO: remove dependency as most people don't use XML.
23
23
  s.add_runtime_dependency "actionpack"
24
24
  s.add_runtime_dependency "railties", ">= 3.0.0"
25
- s.add_runtime_dependency "uber"
25
+ s.add_runtime_dependency "uber", ">= 0.0.5"
26
26
 
27
- s.add_development_dependency "minitest", "~> 4.0"
27
+ s.add_development_dependency "minitest"
28
28
  s.add_development_dependency "activemodel"
29
29
  s.add_development_dependency "activerecord"
30
30
  s.add_development_dependency "sqlite3"
data/test/consume_test.rb CHANGED
@@ -16,16 +16,33 @@ class ConsumeTest < ActionController::TestCase
16
16
  tests UnnamespaceSingersController
17
17
 
18
18
  test "#consume parses incoming document and updates the model" do
19
- post :consume_json, "{\"name\": \"Bumi\"}", :format => 'json'
19
+ @request.env['CONTENT_TYPE'] = 'application/json'
20
+ post :consume_json, "{\"name\": \"Bumi\"}"
20
21
  assert_equal %{#<struct Singer name="Bumi">}, @response.body
21
22
  end
22
23
  end
23
24
 
25
+ class ConsumeHalWithNoHalRespondTest < ActionController::TestCase
26
+ include Roar::Rails::TestCase
27
+
28
+ tests UnnamespaceSingersController
29
+
30
+ # Content-type is set properly, it's a registered mime but responder doesn't do #from_hal.
31
+ # FIXME: why does that still find a representer?
32
+ test "#consume parses hal document and updates the model" do
33
+ @request.env['CONTENT_TYPE'] = 'application/hal+json'
34
+ # assert_raises Roar::Rails::UnsupportedMediaType do
35
+ assert_raises NoMethodError do # currently, we don't know if a format is supported in general, or not.
36
+ post :consume_json, "{\"name\": \"Bumi\"}"
37
+ end
38
+ end
39
+ end
40
+
24
41
  class ConsumeWithConfigurationTest < ActionController::TestCase
25
42
  include Roar::Rails::TestCase
26
43
 
27
44
  module MusicianRepresenter
28
- include Roar::Representer::JSON
45
+ include Roar::JSON
29
46
  property :name, :as => :called
30
47
  end
31
48
 
@@ -44,9 +61,52 @@ class ConsumeWithConfigurationTest < ActionController::TestCase
44
61
  tests SingersController
45
62
 
46
63
  test "#consume uses ConsumeWithConfigurationTest::MusicianRepresenter to parse incoming document" do
64
+ @request.env['CONTENT_TYPE'] = 'application/json'
47
65
  post :consume_json, %{{"called":"Bumi"}}, :format => :json
48
66
  assert_equal %{#<struct Singer name="Bumi">}, @response.body
49
67
  end
68
+
69
+ test "#do not consume missing content type" do
70
+ assert_raises Roar::Rails::UnsupportedMediaType do
71
+ post :consume_json, "{\"name\": \"Bumi\"}"
72
+ end
73
+ end
74
+
75
+
76
+ test "#do not consume parses unknown content type" do
77
+ @request.env['CONTENT_TYPE'] = 'application/custom+json'
78
+ assert_raises Roar::Rails::UnsupportedMediaType do
79
+ post :consume_json, "{\"name\": \"Bumi\"}"
80
+ end
81
+ end
82
+ end
83
+
84
+ class ConsumeHalTest < ActionController::TestCase
85
+ include Roar::Rails::TestCase
86
+
87
+ module MusicianRepresenter
88
+ include Roar::JSON::HAL
89
+ property :name
90
+ end
91
+
92
+
93
+ class SingersController < ActionController::Base
94
+ include Roar::Rails::ControllerAdditions
95
+ represents :hal, :entity => MusicianRepresenter
96
+
97
+ def consume_hal
98
+ singer = consume!(Singer.new)
99
+ render :text => singer.inspect
100
+ end
101
+ end
102
+
103
+ tests SingersController
104
+
105
+ test "#consume parses HAL document and updates the model" do
106
+ @request.env['CONTENT_TYPE'] = 'application/hal+json'
107
+ post :consume_hal, "{\"name\": \"Bumi\"}"
108
+ assert_equal %{#<struct Singer name="Bumi">}, @response.body
109
+ end
50
110
  end
51
111
 
52
112
  class ConsumeWithOptionsOverridingConfigurationTest < ActionController::TestCase
@@ -66,6 +126,7 @@ class ConsumeWithOptionsOverridingConfigurationTest < ActionController::TestCase
66
126
  tests SingersController
67
127
 
68
128
  test "#consume uses #represents config to parse incoming document" do
129
+ @request.env['CONTENT_TYPE'] = 'application/json'
69
130
  post :consume_json, %{{"called":"Bumi"}}, :format => :json
70
131
  assert_equal %{#<struct Singer name="Bumi">}, @response.body
71
132
  end
@@ -73,6 +134,7 @@ end
73
134
 
74
135
  class RequestBodyStringTest < ConsumeTest
75
136
  test "#read rewinds before reading" do
137
+ @request.env['CONTENT_TYPE'] = 'application/json'
76
138
  @request.instance_eval do
77
139
  def body
78
140
  incoming = super
@@ -1,6 +1,6 @@
1
1
  class BandRepresenter < Roar::Decorator
2
- include Roar::Representer::JSON
3
- include Roar::Representer::Feature::Hypermedia
2
+ include Roar::JSON
3
+ include Roar::Hypermedia
4
4
 
5
5
  property :name
6
6
 
@@ -1,5 +1,5 @@
1
1
  module SingerAliasRepresenter
2
- include Roar::Representer::JSON
2
+ include Roar::JSON
3
3
 
4
4
  property :name, :as => :alias
5
5
 
@@ -1,6 +1,6 @@
1
1
  module SingerRepresenter
2
- include Roar::Representer::JSON
3
- include Roar::Representer::Feature::Hypermedia
2
+ include Roar::JSON
3
+ include Roar::Hypermedia
4
4
 
5
5
  property :name
6
6
 
@@ -1,19 +1,11 @@
1
1
  require 'test_helper'
2
2
 
3
- Mime::Type.register 'application/json+hal', :hal
4
- if Roar::Rails.rails_version >= 4.1
5
- ActionController.add_renderer :hal do |js, options|
6
- self.content_type ||= Mime::HAL
7
- js.to_json
8
- end
9
- end
10
-
11
3
  class HalRendererTest < ActionController::TestCase
12
4
  include Roar::Rails::TestCase
13
5
 
14
6
  class SingersController < ActionController::Base
15
7
  module HalSingerRepresenter
16
- include Roar::Representer::JSON::HAL
8
+ include Roar::JSON::HAL
17
9
 
18
10
  property :name
19
11
  link(:self) { "http://#{name}" }
@@ -30,13 +22,13 @@ class HalRendererTest < ActionController::TestCase
30
22
 
31
23
  tests SingersController
32
24
 
33
- test "should render correctly in response to a application/json+hal request" do
25
+ test "should render correctly in response to a application/hal+json request" do
34
26
  get :show, :id => "bumi", :format => :hal
35
27
  assert_body '{"name":"Bumi","_links":{"self":{"href":"http://Bumi"}}}'
36
28
  end
37
29
 
38
- test "should have a content_type of application/json+hal" do
30
+ test "should have a content_type of application/hal+json" do
39
31
  get :show, :id => "bumi", :format => :hal
40
- assert_equal response.content_type, 'application/json+hal'
32
+ assert_equal response.content_type, 'application/hal+json'
41
33
  end
42
- end
34
+ end
data/test/render_test.rb CHANGED
@@ -5,7 +5,7 @@ class RenderTest < ActionController::TestCase
5
5
 
6
6
  class SingersController < ActionController::Base
7
7
  module SingerRepresenter
8
- include Roar::Representer::JSON
8
+ include Roar::JSON
9
9
 
10
10
  property :name, :as => :title
11
11
  end