standardapi 6.0.0.26 → 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/README.md +15 -6
- data/lib/standard_api.rb +5 -0
- data/lib/standard_api/access_control_list.rb +114 -0
- data/lib/standard_api/active_record/connection_adapters/postgresql/schema_statements.rb +21 -0
- data/lib/standard_api/controller.rb +75 -78
- data/lib/standard_api/errors.rb +9 -0
- data/lib/standard_api/helpers.rb +66 -13
- 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 +24 -14
- data/lib/standard_api/test_case/calculate_tests.rb +10 -4
- data/lib/standard_api/test_case/create_tests.rb +13 -15
- data/lib/standard_api/test_case/index_tests.rb +14 -4
- data/lib/standard_api/test_case/schema_tests.rb +25 -3
- 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 +33 -30
- data/lib/standard_api/views/application/_record.streamer +36 -34
- data/lib/standard_api/views/application/_schema.json.jbuilder +68 -0
- data/lib/standard_api/views/application/_schema.streamer +78 -0
- data/lib/standard_api/views/application/new.streamer +1 -1
- data/lib/standard_api/views/application/schema.json.jbuilder +1 -12
- data/lib/standard_api/views/application/schema.streamer +1 -16
- data/test/standard_api/caching_test.rb +43 -0
- data/test/standard_api/helpers_test.rb +172 -0
- data/test/standard_api/performance.rb +39 -0
- data/test/standard_api/route_helpers_test.rb +33 -0
- data/test/standard_api/standard_api_test.rb +699 -0
- data/test/standard_api/test_app.rb +1 -0
- 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 +38 -4
- 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 +18 -0
- data/test/standard_api/test_app/views/photos/_schema.json.jbuilder +1 -0
- data/test/standard_api/test_app/views/photos/_schema.streamer +3 -0
- data/test/standard_api/test_app/views/photos/schema.json.jbuilder +1 -1
- data/test/standard_api/test_app/views/photos/schema.streamer +1 -0
- data/test/standard_api/test_helper.rb +238 -0
- metadata +33 -17
- data/test/standard_api/test_app/log/test.log +0 -129516
data/lib/standard_api/errors.rb
CHANGED
@@ -2,6 +2,15 @@ module StandardAPI
|
|
2
2
|
class StandardAPIError < StandardError
|
3
3
|
end
|
4
4
|
|
5
|
+
class ParameterMissing < StandardAPIError
|
6
|
+
attr_reader :param
|
7
|
+
|
8
|
+
def initialize(param)
|
9
|
+
@param = param
|
10
|
+
super("param is missing or the value is empty: #{param}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
5
14
|
class UnpermittedParameters < StandardAPIError
|
6
15
|
attr_reader :params
|
7
16
|
|
data/lib/standard_api/helpers.rb
CHANGED
@@ -1,6 +1,58 @@
|
|
1
1
|
module StandardAPI
|
2
2
|
module Helpers
|
3
|
-
|
3
|
+
|
4
|
+
def preloadables(record, includes)
|
5
|
+
preloads = {}
|
6
|
+
|
7
|
+
includes.each do |key, value|
|
8
|
+
if reflection = record.klass.reflections[key]
|
9
|
+
case value
|
10
|
+
when true
|
11
|
+
preloads[key] = value
|
12
|
+
when Hash, ActiveSupport::HashWithIndifferentAccess
|
13
|
+
if !value.keys.any? { |x| ['when', 'where', 'limit', 'offset', 'order', 'distinct'].include?(x) }
|
14
|
+
if !reflection.polymorphic?
|
15
|
+
preloads[key] = preloadables_hash(reflection.klass, value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
preloads.empty? ? record : record.preload(preloads)
|
23
|
+
end
|
24
|
+
|
25
|
+
def preloadables_hash(klass, iclds)
|
26
|
+
preloads = {}
|
27
|
+
|
28
|
+
iclds.each do |key, value|
|
29
|
+
if reflection = klass.reflections[key]
|
30
|
+
case value
|
31
|
+
when true
|
32
|
+
preloads[key] = value
|
33
|
+
when Hash, ActiveSupport::HashWithIndifferentAccess
|
34
|
+
if !value.keys.any? { |x| ['when', 'where', 'limit', 'offset', 'order', 'distinct'].include?(x) }
|
35
|
+
if !reflection.polymorphic?
|
36
|
+
preloads[key] = preloadables_hash(reflection.klass, value)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
preloads
|
44
|
+
end
|
45
|
+
|
46
|
+
def schema_partial(model)
|
47
|
+
path = model.model_name.plural
|
48
|
+
|
49
|
+
if lookup_context.exists?("schema", path, true)
|
50
|
+
[path, "schema"].join('/')
|
51
|
+
else
|
52
|
+
'application/schema'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
4
56
|
def model_partial(record)
|
5
57
|
if lookup_context.exists?(record.model_name.element, record.model_name.plural, true)
|
6
58
|
[record.model_name.plural, record.model_name.element].join('/')
|
@@ -17,7 +69,7 @@ module StandardAPI
|
|
17
69
|
false
|
18
70
|
end
|
19
71
|
end
|
20
|
-
|
72
|
+
|
21
73
|
def cache_key(record, includes)
|
22
74
|
timestamp_keys = ['cached_at'] + record.class.column_names.select{|x| x.ends_with? "_cached_at"}
|
23
75
|
if includes.empty?
|
@@ -27,21 +79,22 @@ module StandardAPI
|
|
27
79
|
"#{record.model_name.cache_key}/#{record.id}-#{digest_hash(sort_hash(includes))}-#{timestamp.utc.to_s(record.cache_timestamp_format)}"
|
28
80
|
end
|
29
81
|
end
|
30
|
-
|
31
|
-
def can_cache_relation?(
|
82
|
+
|
83
|
+
def can_cache_relation?(record, relation, subincludes)
|
84
|
+
return false if record.new_record?
|
32
85
|
cache_columns = ["#{relation}_cached_at"] + cached_at_columns_for_includes(subincludes).map {|c| "#{relation}_#{c}"}
|
33
|
-
if (cache_columns -
|
86
|
+
if (cache_columns - record.class.column_names).empty?
|
34
87
|
true
|
35
88
|
else
|
36
89
|
false
|
37
90
|
end
|
38
91
|
end
|
39
|
-
|
92
|
+
|
40
93
|
def association_cache_key(record, relation, subincludes)
|
41
94
|
timestamp = ["#{relation}_cached_at"] + cached_at_columns_for_includes(subincludes).map {|c| "#{relation}_#{c}"}
|
42
|
-
timestamp.map! { |col| record.send(col) }
|
95
|
+
timestamp = (timestamp & record.class.column_names).map! { |col| record.send(col) }
|
43
96
|
timestamp = timestamp.max
|
44
|
-
|
97
|
+
|
45
98
|
case association = record.class.reflect_on_association(relation)
|
46
99
|
when ActiveRecord::Reflection::HasManyReflection, ActiveRecord::Reflection::HasAndBelongsToManyReflection, ActiveRecord::Reflection::HasOneReflection, ActiveRecord::Reflection::ThroughReflection
|
47
100
|
"#{record.model_name.cache_key}/#{record.id}/#{includes_to_cache_key(relation, subincludes)}-#{timestamp.utc.to_s(record.cache_timestamp_format)}"
|
@@ -56,13 +109,13 @@ module StandardAPI
|
|
56
109
|
raise ArgumentError, 'Unkown association type'
|
57
110
|
end
|
58
111
|
end
|
59
|
-
|
112
|
+
|
60
113
|
def cached_at_columns_for_includes(includes)
|
61
114
|
includes.select { |k,v| !['when', 'where', 'limit', 'order', 'distinct', 'distinct_on'].include?(k) }.map do |k, v|
|
62
115
|
["#{k}_cached_at"] + cached_at_columns_for_includes(v).map { |v2| "#{k}_#{v2}" }
|
63
116
|
end.flatten
|
64
117
|
end
|
65
|
-
|
118
|
+
|
66
119
|
def includes_to_cache_key(relation, subincludes)
|
67
120
|
if subincludes.empty?
|
68
121
|
relation.to_s
|
@@ -70,7 +123,7 @@ module StandardAPI
|
|
70
123
|
"#{relation}-#{digest_hash(sort_hash(subincludes))}"
|
71
124
|
end
|
72
125
|
end
|
73
|
-
|
126
|
+
|
74
127
|
def sort_hash(hash)
|
75
128
|
hash.keys.sort.reduce({}) do |seed, key|
|
76
129
|
if seed[key].is_a?(Hash)
|
@@ -81,7 +134,7 @@ module StandardAPI
|
|
81
134
|
seed
|
82
135
|
end
|
83
136
|
end
|
84
|
-
|
137
|
+
|
85
138
|
def digest_hash(*hashes)
|
86
139
|
hashes.compact!
|
87
140
|
hashes.map! { |h| sort_hash(h) }
|
@@ -141,4 +194,4 @@ module StandardAPI
|
|
141
194
|
end
|
142
195
|
|
143
196
|
end
|
144
|
-
end
|
197
|
+
end
|
@@ -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
|
@@ -18,15 +18,14 @@ module StandardAPI::TestCase
|
|
18
18
|
assert_equal(expected, *args)
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def self.included(klass)
|
23
23
|
[:filters, :orders, :includes].each do |attribute|
|
24
24
|
klass.send(:class_attribute, attribute)
|
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)
|
@@ -53,14 +52,14 @@ module StandardAPI::TestCase
|
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
56
|
-
def supports_format(format)
|
55
|
+
def supports_format(format, action=nil)
|
57
56
|
count = controller_class.view_paths.count do |path|
|
58
|
-
!Dir.glob("#{path.instance_variable_get(:@path)}/{#{model.name.underscore},application}
|
57
|
+
!Dir.glob("#{path.instance_variable_get(:@path)}/{#{model.name.underscore},application}/**/#{action || '*'}.#{format}*").empty?
|
59
58
|
end
|
60
|
-
|
59
|
+
|
61
60
|
count > 0
|
62
61
|
end
|
63
|
-
|
62
|
+
|
64
63
|
def default_orders
|
65
64
|
controller_class.new.send(:default_orders)
|
66
65
|
end
|
@@ -76,7 +75,7 @@ module StandardAPI::TestCase
|
|
76
75
|
def model
|
77
76
|
self.class.model
|
78
77
|
end
|
79
|
-
|
78
|
+
|
80
79
|
def mask
|
81
80
|
{}
|
82
81
|
end
|
@@ -98,11 +97,11 @@ module StandardAPI::TestCase
|
|
98
97
|
def singular_name
|
99
98
|
model.model_name.singular
|
100
99
|
end
|
101
|
-
|
100
|
+
|
102
101
|
def plural_name
|
103
102
|
model.model_name.plural
|
104
103
|
end
|
105
|
-
|
104
|
+
|
106
105
|
def create_webmocks(attributes)
|
107
106
|
attributes.each do |attribute, value|
|
108
107
|
self.class.model.validators_on(attribute)
|
@@ -122,7 +121,7 @@ module StandardAPI::TestCase
|
|
122
121
|
value
|
123
122
|
end
|
124
123
|
end
|
125
|
-
|
124
|
+
|
126
125
|
def normalize_to_json(record, attribute, value)
|
127
126
|
value = normalize_attribute(record, attribute, value)
|
128
127
|
return nil if value.nil?
|
@@ -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
|
|
@@ -149,7 +159,7 @@ module StandardAPI::TestCase
|
|
149
159
|
|
150
160
|
def controller_class
|
151
161
|
controller_class_name = self.name.gsub(/Test$/, '')
|
152
|
-
controller_class_name.constantize
|
162
|
+
controller_class_name.constantize
|
153
163
|
rescue NameError => e
|
154
164
|
raise e if e.message != "uninitialized constant #{controller_class_name}"
|
155
165
|
end
|
@@ -166,7 +176,7 @@ module StandardAPI::TestCase
|
|
166
176
|
return @model if defined?(@model) && @model
|
167
177
|
|
168
178
|
klass_name = controller_class.name.gsub(/Controller$/, '').singularize
|
169
|
-
|
179
|
+
|
170
180
|
begin
|
171
181
|
@model = klass_name.constantize
|
172
182
|
rescue NameError
|
@@ -12,7 +12,7 @@ module StandardAPI
|
|
12
12
|
create_model
|
13
13
|
|
14
14
|
math_column = model.columns.find { |x| CALCULATE_COLUMN_TYPES.include?(x.sql_type) }
|
15
|
-
|
15
|
+
|
16
16
|
if math_column
|
17
17
|
column = math_column
|
18
18
|
selects = [{ count: column.name }, { maximum: column.name }, { minimum: column.name }, { average: column.name }]
|
@@ -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
|
@@ -35,7 +41,7 @@ module StandardAPI
|
|
35
41
|
create_model
|
36
42
|
|
37
43
|
math_column = model.columns.find { |x| CALCULATE_COLUMN_TYPES.include?(x.sql_type) }
|
38
|
-
|
44
|
+
|
39
45
|
if math_column
|
40
46
|
column = math_column
|
41
47
|
selects = [{ count: column.name}, { maximum: column.name }, { minimum: column.name }, { average: column.name }]
|
@@ -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
|
|
@@ -85,15 +84,15 @@ module StandardAPI
|
|
85
84
|
end
|
86
85
|
|
87
86
|
test '#create.html' do
|
88
|
-
return unless supports_format(:html)
|
87
|
+
return unless supports_format(:html, :create)
|
88
|
+
|
89
|
+
attrs = attributes_for(singular_name, :nested).select do |k,v|
|
90
|
+
!model.readonly_attributes.include?(k.to_s)
|
91
|
+
end
|
89
92
|
|
90
|
-
attrs = attributes_for(singular_name, :nested).select{ |k,v| !model.readonly_attributes.include?(k.to_s) }
|
91
93
|
mask.each { |k, v| attrs[k] = v }
|
92
94
|
create_webmocks(attrs)
|
93
95
|
|
94
|
-
file_upload = attrs.any? { |k, v| v.is_a?(Rack::Test::UploadedFile) }
|
95
|
-
as = file_upload ? nil : :json
|
96
|
-
|
97
96
|
assert_difference("#{model.name}.count") do
|
98
97
|
post resource_path(:create), params: { singular_name => attrs }, as: :html
|
99
98
|
assert_response :redirect
|
@@ -101,13 +100,12 @@ module StandardAPI
|
|
101
100
|
end
|
102
101
|
|
103
102
|
test '#create.html with invalid attributes renders edit action' do
|
104
|
-
return unless supports_format(:html)
|
103
|
+
return unless supports_format(:html, :create)
|
105
104
|
|
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
|