standardapi 6.0.0.32 → 6.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.
- checksums.yaml +4 -4
- data/lib/standard_api.rb +4 -0
- data/lib/standard_api/access_control_list.rb +114 -0
- data/lib/standard_api/controller.rb +32 -11
- data/lib/standard_api/helpers.rb +4 -3
- data/lib/standard_api/includes.rb +9 -0
- data/lib/standard_api/middleware/query_encoding.rb +3 -3
- data/lib/standard_api/railtie.rb +13 -2
- data/lib/standard_api/route_helpers.rb +5 -5
- data/lib/standard_api/test_case.rb +13 -3
- data/lib/standard_api/test_case/calculate_tests.rb +8 -2
- data/lib/standard_api/test_case/create_tests.rb +7 -9
- data/lib/standard_api/test_case/index_tests.rb +10 -0
- data/lib/standard_api/test_case/schema_tests.rb +7 -1
- data/lib/standard_api/test_case/show_tests.rb +1 -0
- data/lib/standard_api/test_case/update_tests.rb +8 -9
- data/lib/standard_api/version.rb +1 -1
- data/lib/standard_api/views/application/_record.json.jbuilder +14 -14
- data/lib/standard_api/views/application/_record.streamer +36 -34
- data/lib/standard_api/views/application/_schema.json.jbuilder +1 -1
- data/lib/standard_api/views/application/_schema.streamer +1 -1
- data/lib/standard_api/views/application/new.streamer +1 -1
- data/test/standard_api/caching_test.rb +14 -4
- data/test/standard_api/helpers_test.rb +25 -9
- data/test/standard_api/standard_api_test.rb +122 -14
- data/test/standard_api/test_app/app/controllers/acl/account_acl.rb +15 -0
- data/test/standard_api/test_app/app/controllers/acl/property_acl.rb +27 -0
- data/test/standard_api/test_app/app/controllers/acl/reference_acl.rb +7 -0
- data/test/standard_api/test_app/controllers.rb +13 -45
- data/test/standard_api/test_app/models.rb +17 -5
- data/test/standard_api/test_app/test/factories.rb +4 -3
- data/test/standard_api/test_app/views/photos/_photo.json.jbuilder +1 -0
- data/test/standard_api/test_app/views/photos/_photo.streamer +2 -1
- data/test/standard_api/test_helper.rb +12 -0
- metadata +19 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7134ec77418da381ff4cb9aa8717f797784ab1f4d45faefde89261e6e7a82ad9
|
4
|
+
data.tar.gz: 83e924e7fd2ded8c5d4239f9d775ef232fa985a4daa838aec374105eed65df2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e477baa763d068d112a29a9539145b75edf47be726c21e679fc4842ae63157914c8b2753f0f7e338c43fe2e328cbe85c4cb3681958113b5533af478bcb43fc3
|
7
|
+
data.tar.gz: 551a57b70b62f0f6c27f97aba97930b220a40b80799058a7c1a35d0d4a0afa787dd760ac01d42110d050a4d0d0f1387d6b00d63629c523c96e0b49a6910b77c1
|
data/lib/standard_api.rb
CHANGED
@@ -16,3 +16,7 @@ require 'standard_api/helpers'
|
|
16
16
|
require 'standard_api/route_helpers'
|
17
17
|
require 'standard_api/active_record/connection_adapters/postgresql/schema_statements'
|
18
18
|
require 'standard_api/railtie'
|
19
|
+
|
20
|
+
module StandardAPI
|
21
|
+
autoload :AccessControlList, 'standard_api/access_control_list'
|
22
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module StandardAPI
|
2
|
+
module AccessControlList
|
3
|
+
|
4
|
+
def self.traverse(path, prefix: nil, &block)
|
5
|
+
path.children.each do |child|
|
6
|
+
if child.file? && child.basename('.rb').to_s.ends_with?('_acl')
|
7
|
+
block.call([prefix, child.basename('.rb').to_s].compact.join('/'))
|
8
|
+
elsif child.directory?
|
9
|
+
traverse(child, prefix: [prefix, child.basename.to_s].compact.join('/'), &block)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.included(application_controller)
|
15
|
+
acl_dir = Rails.application.root.join('app', 'controllers', 'acl')
|
16
|
+
return if !acl_dir.exist?
|
17
|
+
|
18
|
+
traverse(acl_dir) do |child|
|
19
|
+
mod = child.classify.constantize
|
20
|
+
prefix = child.delete_suffix('_acl').gsub('/', '_')
|
21
|
+
|
22
|
+
[:orders, :includes, :attributes].each do |m|
|
23
|
+
next if !mod.instance_methods.include?(m)
|
24
|
+
mod.send :alias_method, "#{prefix}_#{m}".to_sym, m
|
25
|
+
mod.send :remove_method, m
|
26
|
+
end
|
27
|
+
|
28
|
+
if mod.instance_methods.include?(:nested)
|
29
|
+
mod.send :alias_method, "nested_#{prefix}_attributes".to_sym, :nested
|
30
|
+
mod.send :remove_method, :nested
|
31
|
+
end
|
32
|
+
|
33
|
+
if mod.instance_methods.include?(:filter)
|
34
|
+
mod.send :alias_method, "filter_#{prefix}_params".to_sym, :filter
|
35
|
+
mod.send :remove_method, :filter
|
36
|
+
end
|
37
|
+
|
38
|
+
application_controller.include mod
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def model_orders
|
43
|
+
if self.respond_to?("#{model.model_name.singular}_orders", true)
|
44
|
+
self.send("#{model.model_name.singular}_orders")
|
45
|
+
else
|
46
|
+
[]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def model_params
|
51
|
+
if self.respond_to?("filter_#{model_name(model)}_params", true)
|
52
|
+
self.send("filter_#{model_name(model)}_params", params[model_name(model)], id: params[:id])
|
53
|
+
else
|
54
|
+
filter_model_params(params[model_name(model)], model.base_class)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def filter_model_params(model_params, model, id: nil, allow_id: nil)
|
59
|
+
permitted_params = if self.respond_to?("#{model_name(model)}_attributes", true)
|
60
|
+
permits = self.send("#{model_name(model)}_attributes")
|
61
|
+
|
62
|
+
allow_id ? model_params.permit(permits, :id) : model_params.permit(permits)
|
63
|
+
else
|
64
|
+
ActionController::Parameters.new
|
65
|
+
end
|
66
|
+
|
67
|
+
if self.respond_to?("nested_#{model_name(model)}_attributes", true)
|
68
|
+
self.send("nested_#{model_name(model)}_attributes").each do |relation|
|
69
|
+
relation = model.reflect_on_association(relation)
|
70
|
+
attributes_key = "#{relation.name}_attributes"
|
71
|
+
|
72
|
+
if model_params.has_key?(attributes_key)
|
73
|
+
filter_method = "filter_#{relation.klass.base_class.model_name.singular}_params"
|
74
|
+
if model_params[attributes_key].nil?
|
75
|
+
permitted_params[attributes_key] = nil
|
76
|
+
elsif model_params[attributes_key].is_a?(Array) && model_params[attributes_key].all? { |a| a.keys.map(&:to_sym) == [:id] }
|
77
|
+
permitted_params["#{relation.name.to_s.singularize}_ids"] = model_params[attributes_key].map{|a| a['id']}
|
78
|
+
elsif self.respond_to?(filter_method, true)
|
79
|
+
permitted_params[attributes_key] = if model_params[attributes_key].is_a?(Array)
|
80
|
+
model_params[attributes_key].map { |i| self.send(filter_method, i, allow_id: true) }
|
81
|
+
else
|
82
|
+
self.send(filter_method, model_params[attributes_key], allow_id: true)
|
83
|
+
end
|
84
|
+
else
|
85
|
+
permitted_params[attributes_key] = if model_params[attributes_key].is_a?(Array)
|
86
|
+
model_params[attributes_key].map { |i| filter_model_params(i, relation.klass.base_class, allow_id: true) }
|
87
|
+
else
|
88
|
+
filter_model_params(model_params[attributes_key], relation.klass.base_class, allow_id: true)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
elsif relation.collection? && model_params.has_key?("#{relation.name.to_s.singularize}_ids")
|
92
|
+
permitted_params["#{relation.name.to_s.singularize}_ids"] = model_params["#{relation.name.to_s.singularize}_ids"]
|
93
|
+
elsif model_params.has_key?(relation.foreign_key)
|
94
|
+
permitted_params[relation.foreign_key] = model_params[relation.foreign_key]
|
95
|
+
permitted_params[relation.foreign_type] = model_params[relation.foreign_type] if relation.polymorphic?
|
96
|
+
end
|
97
|
+
|
98
|
+
permitted_params.permit!
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
permitted_params
|
103
|
+
end
|
104
|
+
|
105
|
+
def model_name(model)
|
106
|
+
if model.model_name.singular.starts_with?('habtm_')
|
107
|
+
model.reflect_on_all_associations.map { |a| a.klass.base_class.model_name.singular }.sort.join('_')
|
108
|
+
else
|
109
|
+
model.model_name.singular
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
@@ -107,12 +107,16 @@ module StandardAPI
|
|
107
107
|
|
108
108
|
def remove_resource
|
109
109
|
resource = resources.find(params[:id])
|
110
|
-
|
111
|
-
subresource =
|
110
|
+
association = resource.association(params[:relationship])
|
111
|
+
subresource = association.klass.find_by_id(params[:resource_id])
|
112
112
|
|
113
113
|
if(subresource)
|
114
|
-
|
115
|
-
|
114
|
+
if association.is_a? ActiveRecord::Associations::HasManyAssociation
|
115
|
+
resource.send(params[:relationship]).delete(subresource)
|
116
|
+
else
|
117
|
+
resource.send("#{params[:relationship]}=", nil)
|
118
|
+
end
|
119
|
+
head :no_content
|
116
120
|
else
|
117
121
|
head :not_found
|
118
122
|
end
|
@@ -120,11 +124,15 @@ module StandardAPI
|
|
120
124
|
|
121
125
|
def add_resource
|
122
126
|
resource = resources.find(params[:id])
|
127
|
+
association = resource.association(params[:relationship])
|
128
|
+
subresource = association.klass.find_by_id(params[:resource_id])
|
123
129
|
|
124
|
-
subresource_class = resource.association(params[:relationship]).klass
|
125
|
-
subresource = subresource_class.find_by_id(params[:resource_id])
|
126
130
|
if(subresource)
|
127
|
-
|
131
|
+
if association.is_a? ActiveRecord::Associations::HasManyAssociation
|
132
|
+
result = resource.send(params[:relationship]) << subresource
|
133
|
+
else
|
134
|
+
result = resource.send("#{params[:relationship]}=", subresource)
|
135
|
+
end
|
128
136
|
head result ? :created : :bad_request
|
129
137
|
else
|
130
138
|
head :not_found
|
@@ -189,7 +197,7 @@ module StandardAPI
|
|
189
197
|
if self.respond_to?("#{model.model_name.singular}_params", true)
|
190
198
|
params.require(model.model_name.singular).permit(self.send("#{model.model_name.singular}_params"))
|
191
199
|
else
|
192
|
-
|
200
|
+
ActionController::Parameters.new
|
193
201
|
end
|
194
202
|
end
|
195
203
|
|
@@ -207,7 +215,8 @@ module StandardAPI
|
|
207
215
|
end
|
208
216
|
|
209
217
|
def resources
|
210
|
-
|
218
|
+
mask = current_mask[model.table_name] || current_mask[model.table_name.to_sym]
|
219
|
+
query = model.filter(params['where']).filter(mask)
|
211
220
|
|
212
221
|
if params[:distinct_on]
|
213
222
|
query = query.distinct_on(params[:distinct_on])
|
@@ -309,7 +318,19 @@ module StandardAPI
|
|
309
318
|
@selects = []
|
310
319
|
@selects << params[:group_by] if params[:group_by]
|
311
320
|
Array(params[:select]).each do |select|
|
312
|
-
select.each do |func,
|
321
|
+
select.each do |func, value|
|
322
|
+
distinct = false
|
323
|
+
|
324
|
+
column = case value
|
325
|
+
when ActionController::Parameters
|
326
|
+
# TODO: Add support for other aggregate expressions
|
327
|
+
# https://www.postgresql.org/docs/current/sql-expressions.html#SYNTAX-AGGREGATES
|
328
|
+
distinct = !value[:distinct].nil?
|
329
|
+
value[:distinct]
|
330
|
+
else
|
331
|
+
value
|
332
|
+
end
|
333
|
+
|
313
334
|
if (parts = column.split(".")).length > 1
|
314
335
|
@model = parts[0].singularize.camelize.constantize
|
315
336
|
column = parts[1]
|
@@ -318,7 +339,7 @@ module StandardAPI
|
|
318
339
|
column = column == '*' ? Arel.star : column.to_sym
|
319
340
|
if functions.include?(func.to_s.downcase)
|
320
341
|
node = (defined?(@model) ? @model : model).arel_table[column].send(func)
|
321
|
-
node.distinct =
|
342
|
+
node.distinct = distinct
|
322
343
|
@selects << node
|
323
344
|
end
|
324
345
|
end
|
data/lib/standard_api/helpers.rb
CHANGED
@@ -80,9 +80,10 @@ module StandardAPI
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
-
def can_cache_relation?(
|
83
|
+
def can_cache_relation?(record, relation, subincludes)
|
84
|
+
return false if record.new_record?
|
84
85
|
cache_columns = ["#{relation}_cached_at"] + cached_at_columns_for_includes(subincludes).map {|c| "#{relation}_#{c}"}
|
85
|
-
if (cache_columns -
|
86
|
+
if (cache_columns - record.class.column_names).empty?
|
86
87
|
true
|
87
88
|
else
|
88
89
|
false
|
@@ -91,7 +92,7 @@ module StandardAPI
|
|
91
92
|
|
92
93
|
def association_cache_key(record, relation, subincludes)
|
93
94
|
timestamp = ["#{relation}_cached_at"] + cached_at_columns_for_includes(subincludes).map {|c| "#{relation}_#{c}"}
|
94
|
-
timestamp.map! { |col| record.send(col) }
|
95
|
+
timestamp = (timestamp & record.class.column_names).map! { |col| record.send(col) }
|
95
96
|
timestamp = timestamp.max
|
96
97
|
|
97
98
|
case association = record.class.reflect_on_association(relation)
|
@@ -20,6 +20,15 @@ module StandardAPI
|
|
20
20
|
normalized[k] = case k.to_s
|
21
21
|
when 'when', 'where', 'order'
|
22
22
|
case v
|
23
|
+
when Array
|
24
|
+
v.map do |x|
|
25
|
+
case x
|
26
|
+
when Hash then x.to_h
|
27
|
+
when ActionController::Parameters then x.to_unsafe_h
|
28
|
+
else
|
29
|
+
x
|
30
|
+
end
|
31
|
+
end
|
23
32
|
when Hash then v.to_h
|
24
33
|
when ActionController::Parameters then v.to_unsafe_h
|
25
34
|
end
|
@@ -13,8 +13,8 @@ require 'msgpack'
|
|
13
13
|
module StandardAPI
|
14
14
|
module Middleware
|
15
15
|
class QueryEncoding
|
16
|
-
MSGPACK_MIME_TYPE = "application/msgpack"
|
17
|
-
HTTP_METHOD_OVERRIDE_HEADER = "HTTP_QUERY_ENCODING"
|
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
|
data/lib/standard_api/railtie.rb
CHANGED
@@ -1,10 +1,21 @@
|
|
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
|
|
@@ -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,7 +20,7 @@ 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
25
|
get :schema, on: :collection
|
26
26
|
get :calculate, on: :collection
|
@@ -33,7 +33,7 @@ module StandardAPI
|
|
33
33
|
# StandardAPI wrapper for ActionDispatch::Routing::Mapper::Resources#resource
|
34
34
|
#
|
35
35
|
# Includes the following routes
|
36
|
-
#
|
36
|
+
#
|
37
37
|
# GET /schema
|
38
38
|
# GET /calculate
|
39
39
|
#
|
@@ -49,7 +49,7 @@ module StandardAPI
|
|
49
49
|
# end
|
50
50
|
def standard_resource(*resource, &block)
|
51
51
|
options = resource.extract_options!.dup
|
52
|
-
|
52
|
+
|
53
53
|
resource(*resource, options) do
|
54
54
|
get :schema, on: :collection
|
55
55
|
get :calculate, on: :collection
|
@@ -25,8 +25,7 @@ module StandardAPI::TestCase
|
|
25
25
|
end
|
26
26
|
|
27
27
|
begin
|
28
|
-
|
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
|
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
|
|
@@ -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
|
-
|
30
|
-
|
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
|
@@ -22,7 +22,7 @@ module StandardAPI
|
|
22
22
|
json = JSON.parse(response.body)
|
23
23
|
assert json.is_a?(Hash)
|
24
24
|
|
25
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|