standardapi 6.0.0.32 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +80 -58
  3. data/lib/standard_api/access_control_list.rb +148 -0
  4. data/lib/standard_api/controller.rb +116 -27
  5. data/lib/standard_api/helpers.rb +17 -10
  6. data/lib/standard_api/includes.rb +9 -0
  7. data/lib/standard_api/middleware/query_encoding.rb +3 -3
  8. data/lib/standard_api/middleware.rb +5 -0
  9. data/lib/standard_api/railtie.rb +30 -2
  10. data/lib/standard_api/route_helpers.rb +64 -14
  11. data/lib/standard_api/test_case/calculate_tests.rb +15 -8
  12. data/lib/standard_api/test_case/create_tests.rb +7 -9
  13. data/lib/standard_api/test_case/destroy_tests.rb +19 -7
  14. data/lib/standard_api/test_case/index_tests.rb +10 -6
  15. data/lib/standard_api/test_case/schema_tests.rb +7 -1
  16. data/lib/standard_api/test_case/show_tests.rb +8 -7
  17. data/lib/standard_api/test_case/update_tests.rb +15 -15
  18. data/lib/standard_api/test_case.rb +13 -3
  19. data/lib/standard_api/version.rb +1 -1
  20. data/lib/standard_api/views/application/_record.json.jbuilder +18 -17
  21. data/lib/standard_api/views/application/_record.streamer +40 -37
  22. data/lib/standard_api/views/application/_schema.json.jbuilder +20 -8
  23. data/lib/standard_api/views/application/_schema.streamer +22 -8
  24. data/lib/standard_api/views/application/new.streamer +1 -1
  25. data/lib/standard_api.rb +5 -0
  26. data/test/standard_api/caching_test.rb +14 -4
  27. data/test/standard_api/controller/include_test.rb +107 -0
  28. data/test/standard_api/controller/subresource_test.rb +157 -0
  29. data/test/standard_api/helpers_test.rb +34 -17
  30. data/test/standard_api/nested_attributes/belongs_to_test.rb +71 -0
  31. data/test/standard_api/nested_attributes/has_and_belongs_to_many_test.rb +70 -0
  32. data/test/standard_api/nested_attributes/has_many_test.rb +85 -0
  33. data/test/standard_api/nested_attributes/has_one_test.rb +71 -0
  34. data/test/standard_api/route_helpers_test.rb +56 -0
  35. data/test/standard_api/standard_api_test.rb +182 -44
  36. data/test/standard_api/test_app/app/controllers/acl/account_acl.rb +15 -0
  37. data/test/standard_api/test_app/app/controllers/acl/camera_acl.rb +7 -0
  38. data/test/standard_api/test_app/app/controllers/acl/photo_acl.rb +13 -0
  39. data/test/standard_api/test_app/app/controllers/acl/property_acl.rb +33 -0
  40. data/test/standard_api/test_app/app/controllers/acl/reference_acl.rb +7 -0
  41. data/test/standard_api/test_app/controllers.rb +28 -43
  42. data/test/standard_api/test_app/models.rb +76 -7
  43. data/test/standard_api/test_app/test/factories.rb +7 -3
  44. data/test/standard_api/test_app/views/photos/_photo.json.jbuilder +1 -0
  45. data/test/standard_api/test_app/views/photos/_photo.streamer +2 -1
  46. data/test/standard_api/test_app/views/sessions/create.json.jbuilder +1 -0
  47. data/test/standard_api/test_app/views/sessions/create.streamer +3 -0
  48. data/test/standard_api/test_app.rb +12 -1
  49. data/test/standard_api/test_helper.rb +21 -0
  50. metadata +59 -16
@@ -13,8 +13,8 @@ require 'msgpack'
13
13
  module StandardAPI
14
14
  module Middleware
15
15
  class QueryEncoding
16
- MSGPACK_MIME_TYPE = "application/msgpack".freeze
17
- HTTP_METHOD_OVERRIDE_HEADER = "HTTP_QUERY_ENCODING".freeze
16
+ MSGPACK_MIME_TYPE = "application/msgpack"
17
+ HTTP_METHOD_OVERRIDE_HEADER = "HTTP_QUERY_ENCODING"
18
18
 
19
19
  def initialize(app)
20
20
  @app = app
@@ -31,4 +31,4 @@ module StandardAPI
31
31
 
32
32
  end
33
33
  end
34
- end
34
+ end
@@ -0,0 +1,5 @@
1
+ module StandardAPI
2
+ module Middleware
3
+ autoload :QueryEncoding, 'standard_api/middleware/query_encoding'
4
+ end
5
+ end
@@ -1,12 +1,40 @@
1
1
  module StandardAPI
2
2
  class Railtie < ::Rails::Railtie
3
3
 
4
- initializer 'standardapi' do
4
+ initializer 'standardapi', :before => :set_autoload_paths do |app|
5
+ if app.root.join('app', 'controllers', 'acl').exist?
6
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
7
+ inflect.acronym 'ACL'
8
+ end
9
+
10
+ app.config.autoload_paths << app.root.join('app', 'controllers', 'acl').to_s
11
+ end
12
+
13
+ ActiveSupport.on_load(:before_configuration) do
14
+ ::ActionDispatch::Routing::Mapper.send :include, StandardAPI::RouteHelpers
15
+ end
16
+
5
17
  ActiveSupport.on_load(:action_view) do
6
18
  ::ActionView::Base.send :include, StandardAPI::Helpers
7
- ::ActionDispatch::Routing::Mapper.send :include, StandardAPI::RouteHelpers
8
19
  end
9
20
  end
10
21
 
11
22
  end
23
+
24
+ module AutosaveByDefault
25
+ def self.included base
26
+ base.class_eval do
27
+ class <<self
28
+ alias_method :standard_build, :build
29
+ end
30
+
31
+ def self.build(model, name, scope, options, &block)
32
+ options[:autosave] = true
33
+ standard_build(model, name, scope, options, &block)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ ::ActiveRecord::Associations::Builder::Association.include(AutosaveByDefault)
12
40
  end
@@ -1,10 +1,10 @@
1
1
  module StandardAPI
2
2
  module RouteHelpers
3
-
3
+
4
4
  # StandardAPI wrapper for ActionDispatch::Routing::Mapper::Resources#resources
5
5
  #
6
6
  # Includes the following routes
7
- #
7
+ #
8
8
  # GET /schema
9
9
  # GET /calculate
10
10
  #
@@ -20,20 +20,47 @@ module StandardAPI
20
20
  # end
21
21
  def standard_resources(*resources, &block)
22
22
  options = resources.extract_options!.dup
23
-
23
+
24
24
  resources(*resources, options) do
25
- get :schema, on: :collection
26
- get :calculate, on: :collection
27
- delete ':relationship/:resource_id' => :remove_resource, on: :member
28
- post ':relationship/:resource_id' => :add_resource, on: :member
29
- block.call if block
25
+ block.call if block # custom routes take precedence over standardapi routes
26
+
27
+ available_actions = if only = parent_resource.instance_variable_get(:@only)
28
+ Array(only).map(&:to_sym)
29
+ else
30
+ if parent_resource.instance_variable_get(:@api_only)
31
+ [:index, :create, :show, :update, :destroy]
32
+ else
33
+ [:index, :create, :new, :show, :update, :destroy, :edit]
34
+ end + [ :schema, :calculate, :add_resource, :remove_resource, :create_resource ]
35
+ end
36
+
37
+ actions = if except = parent_resource.instance_variable_get(:@except)
38
+ available_actions - Array(except).map(&:to_sym)
39
+ else
40
+ available_actions
41
+ end
42
+
43
+ get :schema, on: :collection if actions.include?(:schema)
44
+ get :calculate, on: :collection if actions.include?(:calculate)
45
+
46
+ if actions.include?(:add_resource)
47
+ post ':relationship/:resource_id' => :add_resource, on: :member
48
+ end
49
+
50
+ if actions.include?(:create_resource)
51
+ post ':relationship' => :create_resource, on: :member
52
+ end
53
+
54
+ if actions.include?(:remove_resource)
55
+ delete ':relationship/:resource_id' => :remove_resource, on: :member
56
+ end
30
57
  end
31
58
  end
32
59
 
33
60
  # StandardAPI wrapper for ActionDispatch::Routing::Mapper::Resources#resource
34
61
  #
35
62
  # Includes the following routes
36
- #
63
+ #
37
64
  # GET /schema
38
65
  # GET /calculate
39
66
  #
@@ -49,12 +76,35 @@ module StandardAPI
49
76
  # end
50
77
  def standard_resource(*resource, &block)
51
78
  options = resource.extract_options!.dup
52
-
79
+
53
80
  resource(*resource, options) do
54
- get :schema, on: :collection
55
- get :calculate, on: :collection
56
- delete ':relationship/:resource_id' => :remove_resource, on: :member
57
- post ':relationship/:resource_id' => :add_resource, on: :member
81
+ available_actions = if only = parent_resource.instance_variable_get(:@only)
82
+ Array(only).map(&:to_sym)
83
+ else
84
+ if parent_resource.instance_variable_get(:@api_only)
85
+ [:index, :create, :show, :update, :destroy]
86
+ else
87
+ [:index, :create, :new, :show, :update, :destroy, :edit]
88
+ end + [ :schema, :calculate, :add_resource, :remove_resource ]
89
+ end
90
+
91
+ actions = if except = parent_resource.instance_variable_get(:@except)
92
+ available_actions - Array(except).map(&:to_sym)
93
+ else
94
+ available_actions
95
+ end
96
+
97
+ get :schema, on: :collection if actions.include?(:schema)
98
+ get :calculate, on: :collection if actions.include?(:calculate)
99
+
100
+ if actions.include?(:add_resource)
101
+ post ':relationship/:resource_id' => :add_resource, on: :member
102
+ end
103
+
104
+ if actions.include?(:remove_resource)
105
+ delete ':relationship/:resource_id' => :remove_resource, on: :member
106
+ end
107
+
58
108
  block.call if block
59
109
  end
60
110
  end
@@ -26,8 +26,14 @@ module StandardAPI
26
26
  calculations = @controller.instance_variable_get('@calculations')
27
27
  expectations = selects.map { |s| model.send(s.keys.first, column.name) }
28
28
  expectations = [expectations] if expectations.length > 1
29
- assert_equal expectations,
30
- calculations
29
+
30
+ if math_column
31
+ assert_equal expectations.map { |a| a.map { |b| b.round(9) } },
32
+ calculations.map { |a| a.map { |b| b.round(9) } }
33
+ else
34
+ assert_equal expectations.map { |b| b.round(9) },
35
+ calculations.map { |b| b.round(9) }
36
+ end
31
37
  end
32
38
 
33
39
  test '#calculate.json params[:where]' do
@@ -53,22 +59,23 @@ module StandardAPI
53
59
  # calculations
54
60
  end
55
61
 
56
- test '#calculate.json mask' do
62
+ test '#calculate.json mask_for' do
57
63
  # This is just to instance @controller
58
64
  get resource_path(:calculate)
59
65
 
60
- # If #current_mask isn't defined by StandardAPI we don't know how to
61
- # test other's implementation of #current_mask. Return and don't test.
62
- return if @controller.method(:current_mask).owner != StandardAPI
66
+ # If #mask isn't defined by StandardAPI we don't know how to
67
+ # test other's implementation of #mask_for. Return and don't test.
68
+ return if @controller.method(:mask_for).owner != StandardAPI
63
69
 
64
70
  m = create_model
65
71
 
66
- @controller.current_mask[plural_name] = { id: m.id + 100 }
72
+ @controller.define_singleton_method(:mask_for) do |table_name|
73
+ { id: m.id + 100 }
74
+ end
67
75
  selects = [{ count: :id}, { maximum: :id }, { minimum: :id }, { average: :id }]
68
76
  get :calculate, select: selects, format: 'json'
69
77
  assert_response :ok
70
78
  assert_equal [[0, nil, nil, nil]], @controller.instance_variable_get('@calculations')
71
- @controller.current_mask.delete(plural_name)
72
79
  end
73
80
 
74
81
  end
@@ -22,7 +22,7 @@ module StandardAPI
22
22
  json = JSON.parse(response.body)
23
23
  assert json.is_a?(Hash)
24
24
 
25
- view_attributes(m.reload).select { |x| attrs.keys.map(&:to_s).include?(x) }.each do |key, value|
25
+ create_attributes(m.reload).select { |x| attrs.keys.map(&:to_s).include?(x) }.each do |key, value|
26
26
  message = "Model / Attribute: #{m.class.name}##{key}"
27
27
  if value.is_a?(BigDecimal)
28
28
  assert_equal_or_nil normalize_to_json(m, key, attrs[key.to_sym]).to_s.to_f, json[key.to_s].to_s.to_f, message
@@ -53,9 +53,9 @@ module StandardAPI
53
53
  json = JSON.parse(response.body)
54
54
  assert json.is_a?(Hash)
55
55
  m.reload
56
- view_attributes(m).select { |x| attrs.keys.map(&:to_s).include?(x) }.each do |key, value|
56
+ create_attributes(m).select { |x| attrs.keys.map(&:to_s).include?(x) }.each do |key, value|
57
57
  message = "Model / Attribute: #{m.class.name}##{key}"
58
- assert_equal_or_nil normalize_attribute(m, key, attrs[key.to_sym]), value, message
58
+ assert_equal_or_nil normalize_attribute(m, key, attrs[key.to_sym]), normalize_attribute(m, key, value), message
59
59
  end
60
60
  end
61
61
  end
@@ -64,8 +64,7 @@ module StandardAPI
64
64
  trait = FactoryBot.factories[singular_name].definition.defined_traits.any? { |x| x.name.to_s == 'invalid' }
65
65
 
66
66
  if !trait
67
- Rails.logger.try(:warn, "No invalid trait for #{model.name}. Skipping invalid tests")
68
- warn("No invalid trait for #{model.name}. Skipping invalid tests")
67
+ skip("No invalid trait for #{model.name}. Skipping invalid tests")
69
68
  return
70
69
  end
71
70
 
@@ -106,8 +105,7 @@ module StandardAPI
106
105
  trait = FactoryBot.factories[singular_name].definition.defined_traits.any? { |x| x.name.to_s == 'invalid' }
107
106
 
108
107
  if !trait
109
- Rails.logger.try(:warn, "No invalid trait for #{model.name}. Skipping invalid tests")
110
- warn("No invalid trait for #{model.name}. Skipping invalid tests")
108
+ skip("No invalid trait for #{model.name}. Skipping invalid tests")
111
109
  return
112
110
  end
113
111
 
@@ -146,7 +144,7 @@ module StandardAPI
146
144
  next if !association
147
145
 
148
146
  if ['belongs_to', 'has_one'].include?(association.macro.to_s)
149
- view_attributes(m.send(included)) do |key, value|
147
+ create_attributes(m.send(included)) do |key, value|
150
148
  assert_equal json[included.to_s][key.to_s], normalize_to_json(m, key, value)
151
149
  end
152
150
  else
@@ -160,7 +158,7 @@ module StandardAPI
160
158
  nil
161
159
  end
162
160
 
163
- view_attributes(m2).each do |key, value|
161
+ create_attributes(m2).each do |key, value|
164
162
  message = "Model / Attribute: #{m2.class.name}##{key}"
165
163
  if m_json[key.to_s].nil?
166
164
  assert_nil normalize_to_json(m2, key, value), message
@@ -13,23 +13,35 @@ module StandardAPI
13
13
  end
14
14
  end
15
15
 
16
- test '#destroy.json mask' do
16
+ test '#destroy.json mask_for' do
17
17
  m = create_model
18
18
 
19
19
  # This is just to instance @controller
20
20
  get resource_path(:show, id: m.id, format: 'json')
21
21
 
22
- # If #current_mask isn't defined by StandardAPI we don't know how to
23
- # test other's implementation of #current_mask. Return and don't test.
24
- return if @controller.method(:current_mask).owner != StandardAPI
22
+ # If #mask_for isn't defined by StandardAPI we don't know how to
23
+ # test other's implementation of #mask_for. Return and don't test.
24
+ return if @controller.method(:mask_for).owner != StandardAPI
25
25
 
26
- @controller.current_mask[plural_name] = { id: m.id + 1 }
26
+ @controller.define_singleton_method(:mask_for) do |table_name|
27
+ { id: m.id + 1 }
28
+ end
27
29
  assert_raises(ActiveRecord::RecordNotFound) do
28
30
  delete resource_path(:destroy, id: m.id, format: :json)
29
31
  end
30
- @controller.current_mask.delete(plural_name)
31
32
  end
32
33
 
34
+ test '#destroy.json with comma separated ids' do
35
+ m1 = create_model
36
+ m2 = create_model
37
+ m3 = create_model
38
+
39
+ assert_difference("#{model.name}.count", -3) do
40
+ delete resource_path(:destroy, id: "#{m1.id},#{m2.id},#{m3.id}", format: :json)
41
+ assert_response :no_content
42
+ assert_equal '', response.body
43
+ end
44
+ end
33
45
  end
34
46
  end
35
- end
47
+ end
@@ -69,9 +69,12 @@ module StandardAPI
69
69
  end
70
70
 
71
71
  test '#index.json params[:include]' do
72
+ next if includes.empty?
73
+
72
74
  travel_to Time.now do
73
75
  create_model
74
76
  get resource_path(:index, format: :json), params: { limit: 100, include: includes }
77
+ assert_response :ok
75
78
 
76
79
  json = JSON.parse(response.body)[0]
77
80
  assert json.is_a?(Hash)
@@ -114,20 +117,21 @@ module StandardAPI
114
117
  end
115
118
  end
116
119
 
117
- test '#index.json mask' do
120
+ test '#index.json mask_for' do
118
121
  # This is just to instance @controller
119
122
  get resource_path(:index, format: :json), params: { limit: 1 }
120
123
 
121
- # If #current_mask isn't defined by StandardAPI we don't know how to
122
- # test other's implementation of #current_mask. Return and don't test.
123
- return if @controller.method(:current_mask).owner != StandardAPI
124
+ # If #mask_for isn't defined by StandardAPI we don't know how to
125
+ # test other's implementation of #mask_for. Return and don't test.
126
+ return if @controller.method(:mask_for).owner != StandardAPI
124
127
 
125
128
  m = create_model
126
- @controller.current_mask[plural_name] = { id: m.id }
129
+ @controller.define_singleton_method(:mask_for) do |table_name|
130
+ { id: m.id }
131
+ end
127
132
  get :index, format: :json
128
133
  models = @controller.instance_variable_get("@#{plural_name}")
129
134
  assert_equal model.where(id: m.id).sort(required_orders).to_sql, models.to_sql
130
- @controller.current_mask.delete(plural_name)
131
135
  end
132
136
  end
133
137
  end
@@ -18,7 +18,13 @@ module StandardAPI
18
18
  assert_equal_or_nil column.null, actual_column['null']
19
19
  assert_equal_or_nil column.array, actual_column['array']
20
20
  assert_equal_or_nil column.comment, actual_column['comment']
21
- assert_equal_or_nil (column.default || column.default_function), actual_column['default']
21
+
22
+ if column.default
23
+ default = model.connection.lookup_cast_type_from_column(column).deserialize(column.default)
24
+ assert_equal default, actual_column['default']
25
+ else
26
+ assert_nil column.default
27
+ end
22
28
  end
23
29
 
24
30
  assert json['limit']
@@ -15,6 +15,7 @@ module StandardAPI
15
15
  test '#show.json params[:include]' do
16
16
  m = create_model
17
17
  get resource_path(:show, id: m.id, include: includes, format: :json)
18
+ assert_response :ok
18
19
 
19
20
  json = JSON.parse(response.body)
20
21
  includes.each do |included|
@@ -51,22 +52,22 @@ module StandardAPI
51
52
  end
52
53
  end
53
54
 
54
- test '#show.json mask' do
55
+ test '#show.json mask_for' do
55
56
  m = create_model
56
57
 
57
58
  # This is just to instance @controller
58
59
  get resource_path(:show, id: m.id, format: :json)
59
60
 
60
- # If #current_mask isn't defined by StandardAPI we don't know how to
61
- # test other's implementation of #current_mask. Return and don't test.
62
- return if @controller.method(:current_mask).owner != StandardAPI
63
-
61
+ # If #mask_for isn't defined by StandardAPI we don't know how to
62
+ # test other's implementation of #mask_for. Return and don't test.
63
+ return if @controller.method(:mask_for).owner != StandardAPI
64
64
 
65
- @controller.current_mask[plural_name] = { id: m.id + 1 }
65
+ @controller.define_singleton_method(:mask_for) do |table_name|
66
+ { id: m.id + 1 }
67
+ end
66
68
  assert_raises(ActiveRecord::RecordNotFound) do
67
69
  get resource_path(:show, id: m.id, format: :json)
68
70
  end
69
- @controller.current_mask.delete(plural_name)
70
71
  end
71
72
 
72
73
  end
@@ -16,7 +16,7 @@ module StandardAPI
16
16
  put resource_path(:update, :id => m.id, format: format), params: { singular_name => attrs }, as: as
17
17
  assert_response :ok, "Updating #{m.class.name} with #{attrs.inspect}"
18
18
 
19
- view_attributes(m.reload).select { |x| attrs.keys.map(&:to_s).include?(x) }.each do |key, value|
19
+ update_attributes(m.reload).select { |x| attrs.keys.map(&:to_s).include?(x) }.each do |key, value|
20
20
  message = "Model / Attribute: #{m.class.name}##{key}"
21
21
  if value.is_a?(BigDecimal)
22
22
  assert_equal_or_nil normalize_attribute(m, key, attrs[key.to_sym]).to_s.to_f, value.to_s.to_f, message
@@ -34,7 +34,7 @@ module StandardAPI
34
34
  get resource_path(:show, id: m.id), as: :json
35
35
 
36
36
  return if @controller.method(:update).owner != StandardAPI
37
-
37
+
38
38
  attrs = attributes_for(singular_name).select{|k,v| !model.readonly_attributes.include?(k.to_s) }
39
39
  create_webmocks(attrs)
40
40
 
@@ -57,9 +57,9 @@ module StandardAPI
57
57
  assert_response :ok, "Updating #{m.class.name} with #{attrs.inspect}"
58
58
 
59
59
  # (m.attribute_names & attrs.keys.map(&:to_s)).each do |test_key|
60
- view_attributes(m.reload).select { |x| attrs.keys.map(&:to_s).include?(x) }.each do |key, value|
60
+ update_attributes(m.reload).select { |x| attrs.keys.map(&:to_s).include?(x) }.each do |key, value|
61
61
  message = "Model / Attribute: #{m.class.name}##{key}"
62
- assert_equal_or_nil normalize_attribute(m, key, attrs[key.to_sym]), value, message
62
+ assert_equal_or_nil normalize_attribute(m, key, attrs[key.to_sym]), normalize_attribute(m, key, value), message
63
63
  end
64
64
  assert JSON.parse(@response.body).is_a?(Hash)
65
65
  end
@@ -68,8 +68,7 @@ module StandardAPI
68
68
  trait = FactoryBot.factories[singular_name].definition.defined_traits.any? { |x| x.name.to_s == 'invalid' }
69
69
 
70
70
  if !trait
71
- Rails.logger.try(:warn, "No invalid trait for #{model.name}. Skipping invalid tests")
72
- warn("No invalid trait for #{model.name}. Skipping invalid tests")
71
+ skip("No invalid trait for #{model.name}. Skipping invalid tests")
73
72
  return
74
73
  end
75
74
 
@@ -94,7 +93,7 @@ module StandardAPI
94
93
 
95
94
  put resource_path(:update, :id => m.id, format: format), params: { include: includes, singular_name => attrs }, as: as
96
95
  assert_response :ok, "Updating #{m.class.name} with #{attrs.inspect}"
97
-
96
+
98
97
  controller_model = @controller.instance_variable_get("@#{singular_name}")
99
98
  json = JSON.parse(response.body)
100
99
  includes.each do |included|
@@ -104,7 +103,7 @@ module StandardAPI
104
103
  next if !association
105
104
 
106
105
  if ['belongs_to', 'has_one'].include?(association.macro.to_s)
107
- view_attributes(controller_model.send(included)) do |key, value|
106
+ update_attributes(controller_model.send(included)) do |key, value|
108
107
  message = "Model / Attribute: #{controller_model.send(included).class.name}##{key}"
109
108
  assert_equal json[included.to_s][key.to_s], value, message
110
109
  end
@@ -118,7 +117,7 @@ module StandardAPI
118
117
  nil
119
118
  end
120
119
 
121
- view_attributes(m).each do |key, value|
120
+ update_attributes(m).each do |key, value|
122
121
  message = "Model / Attribute: #{m.class.name}##{key}"
123
122
  if m_json[key.to_s].nil?
124
123
  assert_nil normalize_to_json(m, key, value), message
@@ -132,21 +131,22 @@ module StandardAPI
132
131
  end
133
132
  end
134
133
 
135
- test '#update.json mask' do
134
+ test '#update.json mask_for' do
136
135
  m = create_model
137
136
 
138
137
  # This is just to instance @controller
139
138
  get resource_path(:index, format: :json), params: { limit: 1 }
140
139
 
141
- # If #current_mask isn't defined by StandardAPI we don't know how to
142
- # test other's implementation of #current_mask. Return and don't test.
143
- return if @controller.method(:current_mask).owner != StandardAPI
140
+ # If #mask_for isn't defined by StandardAPI we don't know how to
141
+ # test other's implementation of #mask_for. Return and don't test.
142
+ return if @controller.method(:mask_for).owner != StandardAPI
144
143
 
145
- @controller.current_mask[plural_name] = { id: m.id + 1 }
144
+ @controller.define_singleton_method(:mask_for) do |table_name|
145
+ { id: m.id + 1 }
146
+ end
146
147
  assert_raises(ActiveRecord::RecordNotFound) do
147
148
  put resource_path(:update, :id => m.id), as: :json
148
149
  end
149
- @controller.current_mask.delete(plural_name)
150
150
  end
151
151
 
152
152
  end
@@ -25,8 +25,7 @@ module StandardAPI::TestCase
25
25
  end
26
26
 
27
27
  begin
28
- model_class_name = klass.name.gsub(/ControllerTest$/, '').singularize
29
- model_class = model_class_name.constantize
28
+ model_class = klass.name.gsub(/Test$/, '').constantize.model
30
29
 
31
30
  klass.send(:filters=, model_class.attribute_names)
32
31
  klass.send(:orders=, model_class.attribute_names)
@@ -138,8 +137,19 @@ module StandardAPI::TestCase
138
137
 
139
138
  def view_attributes(record)
140
139
  return [] if record.nil?
141
- record.attributes.select { |x| !@controller.send(:excludes_for, record.class).include?(x.to_sym) }
140
+ record.attributes.select do |x|
141
+ !@controller.send(:excludes_for, record.class).include?(x.to_sym)
142
+ end
143
+ end
144
+
145
+ def update_attributes(record)
146
+ return [] if record.nil?
147
+ record.attributes.select do |x|
148
+ !record.class.readonly_attributes.include?(x.to_s) &&
149
+ !@controller.send(:excludes_for, record.class).include?(x.to_sym)
150
+ end
142
151
  end
152
+ alias_method :create_attributes, :update_attributes
143
153
 
144
154
  module ClassMethods
145
155
 
@@ -1,3 +1,3 @@
1
1
  module StandardAPI
2
- VERSION = '6.0.0.32'
2
+ VERSION = '7.1.0'
3
3
  end
@@ -1,26 +1,27 @@
1
- record.attributes.each do |name, value|
1
+ record.attribute_names.each do |name|
2
2
  # Skip if attribute is included in excludes
3
- next if defined?(excludes) && excludes[record.model_name.singular.to_sym].try(:find) { |x| x.to_s == name.to_s }
4
- json.set! name, value
3
+ next if defined?(excludes) && excludes[record.model_name.singular.to_sym].try(:find) { |x| x.to_s == name }
4
+
5
+ serialize_attribute(json, record, name, record.type_for_attribute(name).type)
5
6
  end
6
7
 
7
8
  includes.each do |inc, subinc|
8
9
  next if ["limit", "offset", "order", "when", "where", "distinct", "distinct_on"].include?(inc)
9
10
 
10
-
11
11
  case association = record.class.reflect_on_association(inc)
12
12
  when ActiveRecord::Reflection::AbstractReflection
13
13
  if association.collection?
14
- can_cache = can_cache_relation?(record.class, inc, subinc)
15
- json.cache_if!(can_cache, can_cache ? association_cache_key(record, inc, subinc) : nil) do
16
- partial = model_partial(association.klass)
17
- json.set! inc do
14
+ can_cache = can_cache_relation?(record, inc, subinc)
15
+ json.set! inc do
16
+ json.cache_if!(can_cache, can_cache ? association_cache_key(record, inc, subinc) : nil) do
17
+ partial = model_partial(association.klass)
18
+
18
19
  # TODO limit causes preloaded assocations to reload
19
20
  sub_records = record.send(inc)
20
21
 
21
22
  sub_records = sub_records.limit(subinc['limit']) if subinc['limit']
22
23
  sub_records = sub_records.offset(subinc['offset']) if subinc['offset']
23
- sub_records = sub_records.order(subinc['order']) if subinc['order']
24
+ sub_records = sub_records.reorder(subinc['order']) if subinc['order']
24
25
  sub_records = sub_records.filter(subinc['where']) if subinc['where']
25
26
  sub_records = sub_records.distinct if subinc['distinct']
26
27
  sub_records = sub_records.distinct_on(subinc['distinct_on']) if subinc['distinct_on']
@@ -29,17 +30,17 @@ includes.each do |inc, subinc|
29
30
  end
30
31
  end
31
32
  else
32
- can_cache = can_cache_relation?(record.class, inc, subinc)
33
+ can_cache = can_cache_relation?(record, inc, subinc)
33
34
  if association.is_a?(ActiveRecord::Reflection::BelongsToReflection)
34
35
  can_cache = can_cache && !record.send(association.foreign_key).nil?
35
36
  end
36
- json.cache_if!(can_cache, can_cache ? association_cache_key(record, inc, subinc) : nil) do
37
- value = record.send(inc)
38
- if value.nil?
39
- json.set! inc, nil
40
- else
41
- partial = model_partial(value)
42
- json.set! inc do
37
+ json.set! inc do
38
+ json.cache_if!(can_cache, can_cache ? association_cache_key(record, inc, subinc) : nil) do
39
+ value = record.send(inc)
40
+ if value.nil?
41
+ json.null!
42
+ else
43
+ partial = model_partial(value)
43
44
  json.partial! partial, partial.split('/').last.to_sym => value, includes: subinc
44
45
  end
45
46
  end