standardapi 6.0.0.32 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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