standardapi 6.0.0.32 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|