rabl 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (211) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +3 -1
  3. data/CHANGELOG.md +8 -0
  4. data/Gemfile +2 -1
  5. data/README.md +16 -10
  6. data/Rakefile +13 -4
  7. data/fixtures/ashared/NOTES +46 -0
  8. data/fixtures/ashared/controllers_rails_5/posts_controller.rb +17 -0
  9. data/fixtures/ashared/controllers_rails_5/users_controller.rb +11 -0
  10. data/fixtures/ashared/helpers/application_helper.rb +5 -0
  11. data/fixtures/ashared/views_rails_3/users/phone_number.xml.rabl +1 -0
  12. data/fixtures/padrino_test/app/views +1 -0
  13. data/fixtures/padrino_test/db/migrate +1 -0
  14. data/fixtures/padrino_test/models +1 -0
  15. data/fixtures/padrino_test/test/app/controllers/posts_controller_test.rb +1 -0
  16. data/fixtures/padrino_test/test/app/controllers/users_controller_test.rb +1 -0
  17. data/fixtures/rails2/app/models +1 -0
  18. data/fixtures/rails2/app/views +1 -0
  19. data/fixtures/rails2/db/migrate +1 -0
  20. data/fixtures/rails2/test/functionals/posts_controller_test.rb +1 -0
  21. data/fixtures/rails2/test/functionals/users_controller_test.rb +1 -0
  22. data/fixtures/rails3/app/models +1 -0
  23. data/fixtures/rails3/app/views +1 -0
  24. data/fixtures/rails3/db/migrate +1 -0
  25. data/fixtures/rails3/db/schema.rb +42 -0
  26. data/fixtures/rails3/test/functional/posts_controller_test.rb +1 -0
  27. data/fixtures/rails3/test/functional/users_controller_test.rb +1 -0
  28. data/fixtures/rails3_2/app/models +1 -0
  29. data/fixtures/rails3_2/app/views +1 -0
  30. data/fixtures/rails3_2/db/migrate +1 -0
  31. data/fixtures/rails3_2/test/functional/posts_controller_test.rb +1 -0
  32. data/fixtures/rails3_2/test/functional/users_controller_test.rb +1 -0
  33. data/fixtures/rails4/Gemfile +5 -1
  34. data/fixtures/rails4/app/models +1 -0
  35. data/fixtures/rails4/app/views +1 -0
  36. data/fixtures/rails4/config/environments/test.rb +3 -0
  37. data/fixtures/rails4/db/migrate +1 -0
  38. data/fixtures/rails4/db/schema.rb +42 -0
  39. data/fixtures/rails4/test/functional/posts_controller_test.rb +1 -0
  40. data/fixtures/rails4/test/functional/users_controller_test.rb +1 -0
  41. data/fixtures/rails5/.gitignore +21 -0
  42. data/fixtures/rails5/Gemfile +53 -0
  43. data/fixtures/rails5/README.md +24 -0
  44. data/fixtures/rails5/Rakefile +13 -0
  45. data/fixtures/rails5/app/assets/config/manifest.js +3 -0
  46. data/fixtures/rails5/app/assets/images/.keep +0 -0
  47. data/fixtures/rails5/app/assets/javascripts/application.js +16 -0
  48. data/fixtures/rails5/app/assets/javascripts/cable.js +13 -0
  49. data/fixtures/rails5/app/assets/javascripts/channels/.keep +0 -0
  50. data/fixtures/rails5/app/assets/stylesheets/application.css +15 -0
  51. data/fixtures/rails5/app/channels/application_cable/channel.rb +4 -0
  52. data/fixtures/rails5/app/channels/application_cable/connection.rb +4 -0
  53. data/fixtures/rails5/app/controllers/application_controller.rb +3 -0
  54. data/fixtures/rails5/app/controllers/concerns/.keep +0 -0
  55. data/fixtures/rails5/app/controllers/posts_controller.rb +1 -0
  56. data/fixtures/rails5/app/controllers/users_controller.rb +1 -0
  57. data/fixtures/rails5/app/helpers +1 -0
  58. data/fixtures/rails5/app/jobs/application_job.rb +2 -0
  59. data/fixtures/rails5/app/mailers/application_mailer.rb +4 -0
  60. data/fixtures/rails5/app/models +1 -0
  61. data/fixtures/rails5/app/views +1 -0
  62. data/fixtures/rails5/bin/bundle +3 -0
  63. data/fixtures/rails5/bin/rails +9 -0
  64. data/fixtures/rails5/bin/rake +9 -0
  65. data/fixtures/rails5/bin/setup +34 -0
  66. data/fixtures/rails5/bin/spring +15 -0
  67. data/fixtures/rails5/bin/update +29 -0
  68. data/fixtures/rails5/config/application.rb +15 -0
  69. data/fixtures/rails5/config/boot.rb +3 -0
  70. data/fixtures/rails5/config/cable.yml +9 -0
  71. data/fixtures/rails5/config/database.yml +25 -0
  72. data/fixtures/rails5/config/environment.rb +5 -0
  73. data/fixtures/rails5/config/environments/development.rb +54 -0
  74. data/fixtures/rails5/config/environments/production.rb +86 -0
  75. data/fixtures/rails5/config/environments/test.rb +42 -0
  76. data/fixtures/rails5/config/initializers/application_controller_renderer.rb +6 -0
  77. data/fixtures/rails5/config/initializers/assets.rb +11 -0
  78. data/fixtures/rails5/config/initializers/backtrace_silencers.rb +7 -0
  79. data/fixtures/rails5/config/initializers/cookies_serializer.rb +5 -0
  80. data/fixtures/rails5/config/initializers/filter_parameter_logging.rb +4 -0
  81. data/fixtures/rails5/config/initializers/inflections.rb +16 -0
  82. data/fixtures/rails5/config/initializers/mime_types.rb +6 -0
  83. data/fixtures/rails5/config/initializers/new_framework_defaults.rb +24 -0
  84. data/fixtures/rails5/config/initializers/session_store.rb +3 -0
  85. data/fixtures/rails5/config/initializers/wrap_parameters.rb +14 -0
  86. data/fixtures/rails5/config/locales/en.yml +23 -0
  87. data/fixtures/rails5/config/puma.rb +47 -0
  88. data/fixtures/rails5/config/routes.rb +8 -0
  89. data/fixtures/rails5/config/secrets.yml +22 -0
  90. data/fixtures/rails5/config/spring.rb +6 -0
  91. data/fixtures/rails5/config.ru +5 -0
  92. data/fixtures/rails5/db/migrate +1 -0
  93. data/fixtures/rails5/db/schema.rb +41 -0
  94. data/fixtures/rails5/db/seeds.rb +7 -0
  95. data/fixtures/rails5/lib/assets/.keep +0 -0
  96. data/fixtures/rails5/lib/tasks/.keep +0 -0
  97. data/fixtures/rails5/log/.keep +0 -0
  98. data/fixtures/rails5/public/404.html +67 -0
  99. data/fixtures/rails5/public/422.html +67 -0
  100. data/fixtures/rails5/public/500.html +66 -0
  101. data/fixtures/rails5/public/apple-touch-icon-precomposed.png +0 -0
  102. data/fixtures/rails5/public/apple-touch-icon.png +0 -0
  103. data/fixtures/rails5/public/favicon.ico +0 -0
  104. data/fixtures/rails5/public/robots.txt +5 -0
  105. data/fixtures/rails5/test/controllers/.keep +0 -0
  106. data/fixtures/rails5/test/fixtures/.keep +0 -0
  107. data/fixtures/rails5/test/fixtures/files/.keep +0 -0
  108. data/fixtures/rails5/test/functional/posts_controller_test.rb +1 -0
  109. data/fixtures/rails5/test/functional/users_controller_test.rb +1 -0
  110. data/fixtures/rails5/test/helpers/.keep +0 -0
  111. data/fixtures/rails5/test/integration/.keep +0 -0
  112. data/fixtures/rails5/test/mailers/.keep +0 -0
  113. data/fixtures/rails5/test/models/.keep +0 -0
  114. data/fixtures/rails5/test/test_helper.rb +26 -0
  115. data/fixtures/rails5/tmp/.keep +0 -0
  116. data/fixtures/rails5/vendor/assets/javascripts/.keep +0 -0
  117. data/fixtures/rails5/vendor/assets/stylesheets/.keep +0 -0
  118. data/fixtures/rails5_api/.gitignore +21 -0
  119. data/fixtures/rails5_api/Gemfile +46 -0
  120. data/fixtures/rails5_api/README.md +24 -0
  121. data/fixtures/rails5_api/Rakefile +13 -0
  122. data/fixtures/rails5_api/app/channels/application_cable/channel.rb +4 -0
  123. data/fixtures/rails5_api/app/channels/application_cable/connection.rb +4 -0
  124. data/fixtures/rails5_api/app/controllers/application_controller.rb +2 -0
  125. data/fixtures/rails5_api/app/controllers/concerns/.keep +0 -0
  126. data/fixtures/rails5_api/app/controllers/posts_controller.rb +1 -0
  127. data/fixtures/rails5_api/app/controllers/users_controller.rb +1 -0
  128. data/fixtures/rails5_api/app/helpers +1 -0
  129. data/fixtures/rails5_api/app/jobs/application_job.rb +2 -0
  130. data/fixtures/rails5_api/app/mailers/application_mailer.rb +4 -0
  131. data/fixtures/rails5_api/app/models +1 -0
  132. data/fixtures/rails5_api/app/views +1 -0
  133. data/fixtures/rails5_api/bin/bundle +3 -0
  134. data/fixtures/rails5_api/bin/rails +9 -0
  135. data/fixtures/rails5_api/bin/rake +9 -0
  136. data/fixtures/rails5_api/bin/setup +34 -0
  137. data/fixtures/rails5_api/bin/spring +17 -0
  138. data/fixtures/rails5_api/bin/update +29 -0
  139. data/fixtures/rails5_api/config/application.rb +30 -0
  140. data/fixtures/rails5_api/config/boot.rb +3 -0
  141. data/fixtures/rails5_api/config/cable.yml +9 -0
  142. data/fixtures/rails5_api/config/database.yml +25 -0
  143. data/fixtures/rails5_api/config/environment.rb +5 -0
  144. data/fixtures/rails5_api/config/environments/development.rb +47 -0
  145. data/fixtures/rails5_api/config/environments/production.rb +78 -0
  146. data/fixtures/rails5_api/config/environments/test.rb +42 -0
  147. data/fixtures/rails5_api/config/initializers/application_controller_renderer.rb +6 -0
  148. data/fixtures/rails5_api/config/initializers/backtrace_silencers.rb +7 -0
  149. data/fixtures/rails5_api/config/initializers/cors.rb +16 -0
  150. data/fixtures/rails5_api/config/initializers/filter_parameter_logging.rb +4 -0
  151. data/fixtures/rails5_api/config/initializers/inflections.rb +16 -0
  152. data/fixtures/rails5_api/config/initializers/mime_types.rb +5 -0
  153. data/fixtures/rails5_api/config/initializers/new_framework_defaults.rb +18 -0
  154. data/fixtures/rails5_api/config/initializers/wrap_parameters.rb +14 -0
  155. data/fixtures/rails5_api/config/locales/en.yml +23 -0
  156. data/fixtures/rails5_api/config/puma.rb +47 -0
  157. data/fixtures/rails5_api/config/routes.rb +8 -0
  158. data/fixtures/rails5_api/config/secrets.yml +22 -0
  159. data/fixtures/rails5_api/config/spring.rb +6 -0
  160. data/fixtures/rails5_api/config.ru +5 -0
  161. data/fixtures/rails5_api/db/migrate +1 -0
  162. data/fixtures/rails5_api/db/schema.rb +41 -0
  163. data/fixtures/rails5_api/db/seeds.rb +7 -0
  164. data/fixtures/rails5_api/lib/tasks/.keep +0 -0
  165. data/fixtures/rails5_api/log/.keep +0 -0
  166. data/fixtures/rails5_api/public/robots.txt +5 -0
  167. data/fixtures/rails5_api/test/controllers/.keep +0 -0
  168. data/fixtures/rails5_api/test/fixtures/.keep +0 -0
  169. data/fixtures/rails5_api/test/fixtures/files/.keep +0 -0
  170. data/fixtures/rails5_api/test/functional/posts_controller_test.rb +1 -0
  171. data/fixtures/rails5_api/test/functional/users_controller_test.rb +1 -0
  172. data/fixtures/rails5_api/test/integration/.keep +0 -0
  173. data/fixtures/rails5_api/test/mailers/.keep +0 -0
  174. data/fixtures/rails5_api/test/models/.keep +0 -0
  175. data/fixtures/rails5_api/test/test_helper.rb +39 -0
  176. data/fixtures/rails5_api/tmp/.keep +0 -0
  177. data/fixtures/sinatra_test/db/migrate +1 -0
  178. data/fixtures/sinatra_test/models +1 -0
  179. data/fixtures/sinatra_test/test/functional/posts_controller_test.rb +1 -0
  180. data/fixtures/sinatra_test/test/functional/users_controller_test.rb +1 -0
  181. data/fixtures/sinatra_test/views +1 -0
  182. data/lib/rabl/builder.rb +13 -3
  183. data/lib/rabl/configuration.rb +3 -1
  184. data/lib/rabl/digestor/rails3.rb +10 -0
  185. data/lib/rabl/digestor/rails41.rb +10 -0
  186. data/lib/rabl/digestor/rails5.rb +4 -0
  187. data/lib/rabl/digestor.rb +3 -37
  188. data/lib/rabl/engine.rb +3 -1
  189. data/lib/rabl/railtie.rb +17 -0
  190. data/lib/rabl/version.rb +1 -1
  191. data/lib/rabl.rb +2 -2
  192. data/rabl.gemspec +1 -1
  193. data/test/builder_test.rb +26 -0
  194. data/test/engine_test.rb +6 -0
  195. data/test/integration/rails5/posts_controller_test.rb +251 -0
  196. data/test/integration/rails5/users_controller_test.rb +87 -0
  197. data/test/models/user.rb +10 -9
  198. metadata +169 -5
  199. data/fixtures/ashared/views_rails_3/users/phone_number.xml.rabl +0 -8
  200. data/fixtures/padrino_test/test/app/controllers/posts_controller_test.rb +0 -108
  201. data/fixtures/padrino_test/test/app/controllers/users_controller_test.rb +0 -87
  202. data/fixtures/rails2/test/functionals/posts_controller_test.rb +0 -108
  203. data/fixtures/rails2/test/functionals/users_controller_test.rb +0 -87
  204. data/fixtures/rails3/test/functional/posts_controller_test.rb +0 -108
  205. data/fixtures/rails3/test/functional/users_controller_test.rb +0 -87
  206. data/fixtures/rails3_2/test/functional/posts_controller_test.rb +0 -263
  207. data/fixtures/rails3_2/test/functional/users_controller_test.rb +0 -87
  208. data/fixtures/rails4/test/functional/posts_controller_test.rb +0 -248
  209. data/fixtures/rails4/test/functional/users_controller_test.rb +0 -87
  210. data/fixtures/sinatra_test/test/functional/posts_controller_test.rb +0 -108
  211. data/fixtures/sinatra_test/test/functional/users_controller_test.rb +0 -87
@@ -0,0 +1,10 @@
1
+ module Rabl
2
+ class Digestor < ActionView::Digestor
3
+ def self.digest(options = {})
4
+ cache_key = [options[:name]] + Array.wrap(options[:dependencies])
5
+ @@cache[cache_key.join('.')] ||= begin
6
+ Digestor.new({ :name => options[:name], :finder => options[:finder] }.merge!(options)).digest
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ module Rabl
2
+ class Digestor < ActionView::Digestor
3
+ end
4
+ end
data/lib/rabl/digestor.rb CHANGED
@@ -3,45 +3,11 @@ module Rabl
3
3
  # Override the original digest function to ignore partial which
4
4
  # rabl doesn't use the Rails conventional _ symbol.
5
5
  if Gem::Version.new(Rails.version) >= Gem::Version.new('5.0.0.beta1')
6
- def self.digest(name:, finder:, **options)
7
-
8
- options.assert_valid_keys(:dependencies, :partial)
9
-
10
- cache_key = ([ name ].compact + Array.wrap(options[:dependencies])).join('.')
11
-
12
- # this is a correctly done double-checked locking idiom
13
- # (Concurrent::Map's lookups have volatile semantics)
14
- finder.digest_cache[cache_key] || @@digest_monitor.synchronize do
15
- finder.digest_cache.fetch(cache_key) do # re-check under lock
16
- begin
17
- # Prevent re-entry or else recursive templates will blow the stack.
18
- # There is no need to worry about other threads seeing the +false+ value,
19
- # as they will then have to wait for this thread to let go of the @@digest_monitor lock.
20
-
21
- pre_stored = finder.digest_cache.put_if_absent(cache_key, false).nil? # put_if_absent returns nil on insertion
22
-
23
- finder.digest_cache[cache_key] = stored_digest = Digestor.new(name, finder, options).digest
24
- ensure
25
- # something went wrong or ActionView::Resolver.caching? is false, make sure not to corrupt the @@cache
26
- finder.digest_cache.delete_pair(cache_key, false) if pre_stored && !stored_digest
27
- end
28
- end
29
- end
30
- end
6
+ require 'rabl/digestor/rails5'
31
7
  elsif Gem::Version.new(Rails.version) >= Gem::Version.new('4.1')
32
- def self.digest(options = {})
33
- cache_key = [options[:name]] + Array.wrap(options[:dependencies])
34
- @@cache[cache_key.join('.')] ||= begin
35
- Digestor.new({ :name => options[:name], :finder => options[:finder] }.merge!(options)).digest
36
- end
37
- end
8
+ require 'rabl/digestor/rails41'
38
9
  else
39
- def self.digest(name, format, finder, options = {})
40
- cache_key = [name, format] + Array.wrap(options[:dependencies])
41
- @@cache[cache_key.join('.')] ||= begin
42
- Digestor.new(name, format, finder, options).digest
43
- end
44
- end
10
+ require 'rabl/digestor/rails3'
45
11
  end
46
12
 
47
13
  private
data/lib/rabl/engine.rb CHANGED
@@ -27,6 +27,8 @@ module Rabl
27
27
  # Renders the representation based on source, object, context_scope and locals
28
28
  # Rabl::Engine.new("...source...", { :format => "xml" }).apply(context_scope, { :foo => "bar", :object => @user })
29
29
  def apply(context_scope, locals, &block)
30
+ locals = locals.dup unless locals.nil?
31
+
30
32
  set_instance_variables!(context_scope, locals)
31
33
 
32
34
  reset_settings!
@@ -417,5 +419,5 @@ module Rabl
417
419
  instance_eval(@_source)
418
420
  end
419
421
  end
420
- end
422
+ end
421
423
  end
data/lib/rabl/railtie.rb CHANGED
@@ -1,6 +1,23 @@
1
1
  module Rabl
2
2
  class Railtie < Rails::Railtie
3
3
  initializer "rabl.initialize" do |app|
4
+ # Force Rails to load view templates even in API mode
5
+ # Stolen shamelessly from jbuilder: https://github.com/rails/jbuilder/blob/master/lib/jbuilder/railtie.rb
6
+ if Rails::VERSION::MAJOR >= 5
7
+ module ::ActionController
8
+ module ApiRendering
9
+ include ActionView::Rendering
10
+ end
11
+ end
12
+
13
+ ActiveSupport.on_load :action_controller do
14
+ if self == ActionController::API
15
+ include ActionController::Helpers
16
+ include ActionController::ImplicitRender
17
+ end
18
+ end
19
+ end
20
+
4
21
  ActiveSupport.on_load(:action_view) do
5
22
  Rabl.register!
6
23
 
data/lib/rabl/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rabl
2
- VERSION = "0.13.0"
2
+ VERSION = "0.14.0"
3
3
  end
data/lib/rabl.rb CHANGED
@@ -16,7 +16,7 @@ require 'rabl/configuration'
16
16
  require 'rabl/renderer'
17
17
  require 'rabl/cache_engine'
18
18
 
19
- if defined?(Rails)
19
+ if defined?(Rails) && Rails.respond_to?(:version)
20
20
  require 'rabl/tracker' if Rails.version =~ /^[45]/
21
21
  require 'rabl/digestor' if Rails.version =~ /^[45]/
22
22
  require 'rabl/railtie' if Rails.version =~ /^[345]/
@@ -83,6 +83,6 @@ end
83
83
  if defined?(Padrino)
84
84
  require 'padrino-core'
85
85
  Padrino.after_load { Rabl.register! }
86
- elsif defined?(Rails) && Rails.version =~ /^2/
86
+ elsif defined?(Rails.version) && Rails.version =~ /^2/
87
87
  Rabl.register!
88
88
  end
data/rabl.gemspec CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |s|
30
30
  s.add_development_dependency 'rake'
31
31
  s.add_development_dependency 'tilt'
32
32
  s.add_development_dependency 'oj'
33
- s.add_development_dependency 'msgpack', '~> 0.4.5'
33
+ s.add_development_dependency 'msgpack', '~> 1.0.0'
34
34
  s.add_development_dependency 'bson', '~> 1.7.0'
35
35
  s.add_development_dependency 'plist'
36
36
  end
data/test/builder_test.rb CHANGED
@@ -91,6 +91,32 @@ context "Rabl::Builder" do
91
91
  Rabl.configuration.exclude_nil_values = false
92
92
  end
93
93
  end
94
+
95
+ context "when keys are camelized" do
96
+ setup do
97
+ Rabl.configuration.camelize_keys = true
98
+ builder(nil, { :attributes => [ { :name => :first_pets_name } ] })
99
+ end
100
+ asserts "that the key is camelized" do
101
+ topic.to_hash(User.new)
102
+ end.equivalent_to({ :firstPetsName => 'jack' })
103
+ teardown do
104
+ Rabl.configuration.camelize_keys = false
105
+ end
106
+ end
107
+
108
+ context "when keys are camelized with upper case first letter" do
109
+ setup do
110
+ Rabl.configuration.camelize_keys = :upper
111
+ builder(nil, { :attributes => [ { :name => :first_pets_name } ] })
112
+ end
113
+ asserts "that the key is camelized with upper case first letter" do
114
+ topic.to_hash(User.new)
115
+ end.equivalent_to({ :FirstPetsName => 'jack' })
116
+ teardown do
117
+ Rabl.configuration.camelize_keys = false
118
+ end
119
+ end
94
120
  end
95
121
 
96
122
  context "#attribute" do
data/test/engine_test.rb CHANGED
@@ -23,6 +23,12 @@ context "Rabl::Engine" do
23
23
  asserts_topic.assigns :_view_path
24
24
  end
25
25
 
26
+ context "#apply" do
27
+ denies("that it raises exception when given frozen locals").raises(RuntimeError) do
28
+ Rabl::Engine.new("").apply(Object.new, {}.freeze)
29
+ end
30
+ end
31
+
26
32
  context "#request_format" do
27
33
  context "is json by default" do
28
34
  setup do
@@ -0,0 +1,251 @@
1
+ # Lives in <rabl>/test/integration/posts_controller_test.rb
2
+ # Symlinked to fixture applications
3
+
4
+ begin # Padrino
5
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_config.rb')
6
+ rescue LoadError # Rails
7
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper.rb')
8
+ end
9
+
10
+ require 'rexml/document'
11
+
12
+ context "PostsController" do
13
+ helper(:json_output) { JSON.parse(last_response.body) }
14
+
15
+ setup do
16
+ create_users!
17
+ Post.delete_all
18
+ @post1 = Post.create(:title => "Foo", :body => "Bar", :user_id => @user1.id)
19
+ @post2 = Post.create(:title => "Baz", :body => "Bah", :user_id => @user2.id)
20
+ @post3 = Post.create(:title => "Kaz", :body => "<script>alert('xss & test');</script>", :user_id => @user3.id)
21
+ @posts = [@post1, @post2, @post3]
22
+ end
23
+
24
+ context "for index action" do
25
+ setup do
26
+ get "/posts", format: :json
27
+ end
28
+
29
+ # Attributes (regular)
30
+ asserts("contains post titles") do
31
+ json_output['articles'].map { |o| o["article"]["title"] }
32
+ end.equals { @posts.map(&:title) }
33
+
34
+ asserts("contains post bodies") do
35
+ json_output['articles'].map { |o| o["article"]["body"] }
36
+ end.equals { @posts.map(&:body) }
37
+
38
+ # Attributes (custom name)
39
+ asserts("contains post posted_at") do
40
+ json_output['articles'].map { |o| o["article"]["posted_at"] }
41
+ end.equals { @posts.map(&:created_at).map{ |t| t.strftime("%Y-%m-%d %H:%M:%S %Z") } }
42
+
43
+ # Child
44
+ asserts("contains post user child username") do
45
+ json_output['articles'].map { |o| o["article"]["user"]["username"] }
46
+ end.equals { @posts.map(&:user).map(&:username) }
47
+
48
+ asserts("contains post user child role") do
49
+ json_output['articles'].map { |o| o["article"]["user"]["role"] }
50
+ end.equals { ["normal", "normal", "admin"] }
51
+
52
+ # Child Numbers of the Child User
53
+ asserts("contains post user child numbers") do
54
+ json_output['articles'].map { |o| o["article"]["user"]["pnumbers"][0]["pnumber"]["formatted"] }
55
+ end.equals { @posts.map(&:user).map(&:phone_numbers).map(&:first).map(&:formatted) }
56
+
57
+ # Glue (username to article)
58
+ asserts("contains glued usernames") do
59
+ json_output['articles'].map { |o| o["article"]["author_name"] }
60
+ end.equals { @posts.map(&:user).map(&:username) }
61
+
62
+ # Conditional Child (admin)
63
+ asserts("contains admin child only for admins") do
64
+ json_output['articles'].map { |o| o["article"]["admin"]["username"] if o["article"].has_key?("admin") }.compact
65
+ end.equals { [@user3.username] }
66
+
67
+ # Conditional Node (created_by_admin)
68
+ asserts("contains created_by_admin node for admins") do
69
+ json_output['articles'].last['article']['created_by_admin']
70
+ end.equals { true }
71
+
72
+ denies("contains no created_by_admin node for non-admins") do
73
+ json_output['articles'].first['article']
74
+ end.includes(:created_by_admin)
75
+ end # index action, json
76
+
77
+ context "escaping output in index action" do
78
+ context "for first post" do
79
+ setup do
80
+ Rabl.configuration.escape_all_output = true
81
+ get "/posts/#{@post1.id}", format: :json
82
+ json_output['post']
83
+ end
84
+
85
+ # Attributes (regular)
86
+ asserts("contains post title") { topic['title'] }.equals { @post1.title }
87
+ asserts("contains post body") { topic['body'] }.equals { @post1.body }
88
+ end
89
+
90
+ context "for third post with script tags" do
91
+ setup do
92
+ Rabl.configuration.escape_all_output = true
93
+ get "/posts/#{@post3.id}", format: :json
94
+ json_output['post']
95
+ end
96
+
97
+ # Attributes (regular)
98
+ asserts("contains post title") { topic['title'] }.equals { @post3.title }
99
+ asserts("contains escaped post body") { topic['body'] }.equals { ERB::Util.h(@post3.body) }
100
+ end
101
+ end # escaping output
102
+
103
+ context "for show action" do
104
+ setup do
105
+ get "/posts/#{@post1.id}", format: :json
106
+ json_output['post']
107
+ end
108
+
109
+ # Attributes (regular)
110
+ asserts("contains post title") { topic['title'] }.equals { @post1.title }
111
+ asserts("contains post body") { topic['body'] }.equals { @post1.body }
112
+
113
+ # Attributes (custom name)
114
+ asserts("contains post posted_at") { topic['posted_at'] }.equals { @post1.created_at.strftime("%Y-%m-%d %H:%M:%S %Z") }
115
+
116
+ # Child
117
+ asserts("contains post user child username") { topic["user"]["username"] }.equals { @post1.user.username }
118
+ asserts("contains post user child role") { topic["user"]["role"] }.equals { "normal" }
119
+
120
+ # Child Numbers of the Child User
121
+ asserts("contains post user child numbers") do
122
+ topic["user"]["pnumbers"][0]["pnumber"]["formatted"]
123
+ end.equals { @post1.user.phone_numbers[0].formatted }
124
+
125
+ # Glue (username to article)
126
+ asserts("contains glued username") { topic["author_name"] }.equals { @post1.user.username }
127
+
128
+ # Non-ORM Date Node Partial
129
+ context "for date node" do
130
+ setup { json_output['post']['created_date'] }
131
+ asserts("contains date partial with day") { topic['day'] }.equals { @post1.created_at.day }
132
+ asserts("contains date partial with hour") { topic['hour'] }.equals { @post1.created_at.hour }
133
+ asserts("contains date partial with full") { topic['full'] }.equals { @post1.created_at.iso8601 }
134
+ end # date node
135
+
136
+ asserts("contains helper action") { topic["foo"] }.equals { "BAR!" }
137
+ denies("contains helper action") { topic["created_at_in_words"] }.nil
138
+
139
+ asserts("contains post attributes via node") { topic["post"] }.equals { [@post1.title, @post1.body] }
140
+ end # show action, json
141
+
142
+ context "renderer" do
143
+ setup do
144
+ mock(ActionController::Base).perform_caching.any_number_of_times { true }
145
+ get "/posts/#{@post1.id}/renderer"
146
+ json_output['post']
147
+ end
148
+
149
+ # Attributes (regular)
150
+ asserts("contains post title") { topic['title'] }.equals { @post1.title }
151
+ asserts("contains post body") { topic['body'] }.equals { @post1.body }
152
+
153
+ # Attributes (partial)
154
+ asserts("contains post partial title") { topic['partial']['title'] }.equals { @post1.title }
155
+ asserts("contains post partial body") { topic['partial']['body'] }.equals { @post1.body }
156
+ end # renderer action, json
157
+
158
+ # HTML isn't supported in API mode
159
+ unless Rails.application.config.api_only
160
+ context "for index action rendering JSON within HTML" do
161
+ setup do
162
+ get "/posts", format: :html
163
+ end
164
+
165
+ asserts(:body).includes { "<html>" }
166
+ end # index action, html
167
+
168
+ context "for show action rendering JSON within HTML" do
169
+ setup do
170
+ get "/posts/#{@post1.id}", format: :html
171
+ end
172
+
173
+ asserts(:body).includes { "<html>" }
174
+ end # show action, html
175
+ end
176
+
177
+ context "mime_type" do
178
+ setup do
179
+ get "/posts/#{@post1.id}", format: :rabl_test_v1
180
+ end
181
+
182
+ asserts("contains post title") { json_output['post']['title_v1'] }.equals { @post1.title }
183
+ asserts("contains username") { json_output['post']['user']['username_v1'] }.equals { @post1.user.username }
184
+ end
185
+
186
+ # context "caching" do
187
+ # helper(:cache_hit) do |key|
188
+ # Rails.cache.read(ActiveSupport::Cache.expand_cache_key(key, :rabl))
189
+ # end
190
+ #
191
+ # setup do
192
+ # mock(ActionController::Base).perform_caching.any_number_of_times { true }
193
+ # Rails.cache.clear
194
+ # end
195
+ #
196
+ # context "for index action with caching in json" do
197
+ # setup do
198
+ # get "/posts", format: :json
199
+ # end
200
+ #
201
+ # asserts("contains post titles") do
202
+ # json_output['articles'].map { |o| o['article']['title'] }
203
+ # end.equals { @posts.map(&:title) }
204
+ #
205
+ # asserts(:body).equals { cache_hit ['kittens!', @posts, nil, 'json', 'e83f65eee5ffb454c418a59105f222c4'] }
206
+ #
207
+ # asserts("contains cache hits per object (posts by title)") do
208
+ # json_output['articles'].map { |o| o['article']['title'] }
209
+ # end.equals { @posts.map { |p| cache_hit([p, nil, 'hash', 'e373525f49a3b3b044af05255e84839d'])[:title] } }
210
+ # end # index action, caching, json
211
+ #
212
+ # context "for index action with caching in xml" do
213
+ # setup do
214
+ # get "/posts", format: :xml
215
+ # end
216
+ #
217
+ # asserts("contains post titles") do
218
+ # doc = REXML::Document.new topic.body
219
+ # doc.elements.inject('articles/article/title', []) {|arr, ele| arr << ele.text}
220
+ # end.equals { @posts.map(&:title) }
221
+ #
222
+ # asserts(:body).equals { cache_hit ['kittens!', @posts, nil, 'xml', 'e83f65eee5ffb454c418a59105f222c4'] }
223
+ # end # index action, caching, xml
224
+ #
225
+ # context "for show action with caching" do
226
+ # setup do
227
+ # get "/posts/#{@post1.id}", format: :json
228
+ # end
229
+ #
230
+ # asserts("contains post title") { json_output['post']['title'] }.equals { @post1.title }
231
+ #
232
+ # asserts(:body).equals { cache_hit [@post1, nil, 'json', 'e373525f49a3b3b044af05255e84839d'] }
233
+ # end # show action, caching, json
234
+ #
235
+ # context "cache_all_output" do
236
+ # helper(:cache_hit) do |key|
237
+ # Rails.cache.read(ActiveSupport::Cache.expand_cache_key([key, 'article', 'json'], :rabl))
238
+ # end
239
+ #
240
+ # setup do
241
+ # Rabl.configuration.cache_all_output = true
242
+ # get "/posts", format: :json
243
+ # end
244
+ #
245
+ # asserts("contains cache hits per object (posts by title)") do
246
+ # json_output['articles'].map { |o| o['article']['title'] }
247
+ # end.equals { @posts.map{ |p| cache_hit(p)['article'][:title] } }
248
+ # end # index action, cache_all_output
249
+ # end
250
+
251
+ end
@@ -0,0 +1,87 @@
1
+ # Lives in <rabl>/test/integration/users_controller_test.rb
2
+ # Symlinked to fixture applications
3
+
4
+ begin # Sinatra
5
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_config.rb')
6
+ rescue LoadError # Rails
7
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper.rb')
8
+ end
9
+
10
+ context "UsersController" do
11
+ helper(:json_output) { JSON.parse(last_response.body) }
12
+
13
+ setup do
14
+ create_users!
15
+ end
16
+
17
+ context "for index action" do
18
+ # Tests `collection @users` extending from 'show' template
19
+
20
+ setup do
21
+ get "/users", format: :json
22
+ end
23
+
24
+ # Attributes (regular)
25
+ asserts("contains user usernames") do
26
+ json_output.map { |u| u["user"]["username"] }
27
+ end.equals { @users.map(&:username) }
28
+ asserts("contains email") do
29
+ json_output.map { |u| u["user"]["email"] }
30
+ end.equals { @users.map(&:email) }
31
+ asserts("contains location") do
32
+ json_output.map { |u| u["user"]["location"] }
33
+ end.equals { @users.map(&:location) }
34
+
35
+ # Attributes (custom name)
36
+ asserts("contains registered_at") do
37
+ json_output.map { |u| u["user"]["registered_at"] }
38
+ end.equals { @users.map(&:created_at).map { |t| t.strftime("%Y-%m-%d %H:%M:%S %Z") } }
39
+
40
+ # Node (renders based on attribute)
41
+ asserts("contains role") do
42
+ json_output.map { |u| u["user"]["role"] }
43
+ end.equals ['normal', 'normal', 'admin']
44
+
45
+ # Child (custom collection name)
46
+ asserts("contains formatted phone numbers") do
47
+ json_output.map { |u| u["user"]["pnumbers"].map { |n| n["pnumber"]["formatted"] } }
48
+ end.equals { @users.map { |u| u.phone_numbers.map(&:formatted) } }
49
+
50
+ # Node (renders collection partial)
51
+ asserts("contains reversed node numbers") do
52
+ json_output.map { |u| u["user"]["node_numbers"].map { |n| n["reversed"] } }
53
+ end.equals { @users.map { |u| u.phone_numbers.map(&:formatted).map(&:reverse) } }
54
+ end # index
55
+
56
+ context "for show action" do
57
+ # Tests `object :user => :person` custom parent node name
58
+ setup do
59
+ get "/users/#{@user1.id}", format: :json
60
+ end
61
+
62
+ # Attributes (regular)
63
+ asserts("contains username") { json_output["person"]["username"] }.equals { @user1.username }
64
+ asserts("contains email") { json_output["person"]["email"] }.equals { @user1.email }
65
+ asserts("contains location") { json_output["person"]["location"] }.equals { @user1.location }
66
+ # Attributes (custom name)
67
+ asserts("contains registered_at") { json_output["person"]["registered_at"] }.equals { @user1.created_at.strftime("%Y-%m-%d %H:%M:%S %Z") }
68
+ # Node (renders based on attribute)
69
+ asserts("contains role node") { json_output["person"]["role"] }.equals "normal"
70
+
71
+ # Child (custom collection name)
72
+ asserts("contains first phone number") {
73
+ json_output["person"]["pnumbers"][0]["pnumber"]["formatted"]
74
+ }.equals { @user1.phone_numbers[0].formatted }
75
+ asserts("contains second phone number") {
76
+ json_output["person"]["pnumbers"][1]["pnumber"]["formatted"]
77
+ }.equals { @user1.phone_numbers[1].formatted }
78
+
79
+ # Node (renders collection partial)
80
+ asserts("contains first node number") {
81
+ json_output["person"]["node_numbers"][0]["formatted"]
82
+ }.equals { @user1.phone_numbers[0].formatted }
83
+ asserts("contains second node number") {
84
+ json_output["person"]["node_numbers"][1]["formatted"]
85
+ }.equals { @user1.phone_numbers[1].formatted }
86
+ end # show
87
+ end
data/test/models/user.rb CHANGED
@@ -1,17 +1,18 @@
1
1
  unless defined?(User)
2
2
  class User
3
- attr_accessor :age, :city, :name, :first, :float, :hobbies, :twitter
3
+ attr_accessor :age, :city, :name, :first, :float, :hobbies, :twitter, :first_pets_name
4
4
 
5
- DEFAULT_AGE = 24
6
- DEFAULT_CITY = 'irvine'
7
- DEFAULT_NAME = 'rabl'
8
- DEFAULT_FIRST = 'bob'
9
- DEFAULT_FLOAT = 1234.56
10
- DEFAULT_HOBBIES = ['Photography']
11
- DEFAULT_TWITTER = 'rablgem'
5
+ DEFAULT_AGE = 24
6
+ DEFAULT_CITY = 'irvine'
7
+ DEFAULT_NAME = 'rabl'
8
+ DEFAULT_FIRST = 'bob'
9
+ DEFAULT_FLOAT = 1234.56
10
+ DEFAULT_HOBBIES = ['Photography']
11
+ DEFAULT_TWITTER = 'rablgem'
12
+ DEFAULT_FIRST_PETS_NAME = 'jack'
12
13
 
13
14
  def initialize(attributes = {})
14
- %w(age city name first float hobbies twitter).each do |attr|
15
+ %w(age city name first float hobbies twitter first_pets_name).each do |attr|
15
16
  self.send "#{attr}=", (attributes.has_key?(attr.to_sym) ? attributes[attr.to_sym] : self.class.const_get("DEFAULT_#{attr.upcase}"))
16
17
  end
17
18
  self.hobbies = self.hobbies.map { |h| Hobby.new(h) }