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.
- checksums.yaml +4 -4
- data/README.md +80 -58
- data/lib/standard_api/access_control_list.rb +148 -0
- data/lib/standard_api/controller.rb +116 -27
- data/lib/standard_api/helpers.rb +17 -10
- data/lib/standard_api/includes.rb +9 -0
- data/lib/standard_api/middleware/query_encoding.rb +3 -3
- data/lib/standard_api/middleware.rb +5 -0
- data/lib/standard_api/railtie.rb +30 -2
- data/lib/standard_api/route_helpers.rb +64 -14
- data/lib/standard_api/test_case/calculate_tests.rb +15 -8
- data/lib/standard_api/test_case/create_tests.rb +7 -9
- data/lib/standard_api/test_case/destroy_tests.rb +19 -7
- data/lib/standard_api/test_case/index_tests.rb +10 -6
- data/lib/standard_api/test_case/schema_tests.rb +7 -1
- data/lib/standard_api/test_case/show_tests.rb +8 -7
- data/lib/standard_api/test_case/update_tests.rb +15 -15
- data/lib/standard_api/test_case.rb +13 -3
- data/lib/standard_api/version.rb +1 -1
- data/lib/standard_api/views/application/_record.json.jbuilder +18 -17
- data/lib/standard_api/views/application/_record.streamer +40 -37
- data/lib/standard_api/views/application/_schema.json.jbuilder +20 -8
- data/lib/standard_api/views/application/_schema.streamer +22 -8
- data/lib/standard_api/views/application/new.streamer +1 -1
- data/lib/standard_api.rb +5 -0
- data/test/standard_api/caching_test.rb +14 -4
- data/test/standard_api/controller/include_test.rb +107 -0
- data/test/standard_api/controller/subresource_test.rb +157 -0
- data/test/standard_api/helpers_test.rb +34 -17
- data/test/standard_api/nested_attributes/belongs_to_test.rb +71 -0
- data/test/standard_api/nested_attributes/has_and_belongs_to_many_test.rb +70 -0
- data/test/standard_api/nested_attributes/has_many_test.rb +85 -0
- data/test/standard_api/nested_attributes/has_one_test.rb +71 -0
- data/test/standard_api/route_helpers_test.rb +56 -0
- data/test/standard_api/standard_api_test.rb +182 -44
- data/test/standard_api/test_app/app/controllers/acl/account_acl.rb +15 -0
- data/test/standard_api/test_app/app/controllers/acl/camera_acl.rb +7 -0
- data/test/standard_api/test_app/app/controllers/acl/photo_acl.rb +13 -0
- data/test/standard_api/test_app/app/controllers/acl/property_acl.rb +33 -0
- data/test/standard_api/test_app/app/controllers/acl/reference_acl.rb +7 -0
- data/test/standard_api/test_app/controllers.rb +28 -43
- data/test/standard_api/test_app/models.rb +76 -7
- data/test/standard_api/test_app/test/factories.rb +7 -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_app/views/sessions/create.json.jbuilder +1 -0
- data/test/standard_api/test_app/views/sessions/create.streamer +3 -0
- data/test/standard_api/test_app.rb +12 -1
- data/test/standard_api/test_helper.rb +21 -0
- metadata +59 -16
@@ -1,46 +1,49 @@
|
|
1
1
|
json.object! do
|
2
|
-
|
3
|
-
record.
|
2
|
+
|
3
|
+
record.attribute_names.each do |name|
|
4
4
|
# Skip if attribute is included in excludes
|
5
|
-
next if defined?(excludes) && excludes[record.model_name.singular.to_sym].try(:find) { |x| x.to_s == name
|
6
|
-
|
5
|
+
next if defined?(excludes) && excludes[record.model_name.singular.to_sym].try(:find) { |x| x.to_s == name }
|
6
|
+
|
7
|
+
serialize_attribute(json, record, name, record.type_for_attribute(name).type)
|
7
8
|
end
|
8
9
|
|
9
10
|
includes.each do |inc, subinc|
|
10
11
|
next if ["limit", "offset", "order", "when", "where", "distinct", "distinct_on"].include?(inc)
|
11
|
-
|
12
|
+
|
12
13
|
case association = record.class.reflect_on_association(inc)
|
13
|
-
when ActiveRecord::Reflection::
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
when ActiveRecord::Reflection::AbstractReflection
|
15
|
+
if association.collection?
|
16
|
+
can_cache = can_cache_relation?(record, inc, subinc)
|
17
|
+
json.cache_if!(can_cache, can_cache ? [inc, association_cache_key(record, inc, subinc)] : nil) do
|
18
|
+
json.set! inc do
|
19
|
+
partial = model_partial(association.klass)
|
20
|
+
# TODO limit causes preloaded assocations to reload
|
21
|
+
sub_records = record.send(inc)
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
sub_records = sub_records.limit(subinc["limit"]) if subinc["limit"]
|
24
|
+
sub_records = sub_records.offset(subinc["offset"]) if subinc["offset"]
|
25
|
+
sub_records = sub_records.reorder(subinc["order"]) if subinc["order"]
|
26
|
+
sub_records = sub_records.filter(subinc["where"]) if subinc["where"]
|
27
|
+
sub_records = sub_records.distinct if subinc["distinct"]
|
28
|
+
sub_records = sub_records.distinct_on(subinc["distinct_on"]) if subinc["distinct_on"]
|
27
29
|
|
28
|
-
|
30
|
+
json.array! sub_records, partial: partial, as: partial.split("/").last, locals: { includes: subinc }
|
31
|
+
end
|
29
32
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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)
|
33
|
+
else
|
34
|
+
can_cache = can_cache_relation?(record, inc, subinc)
|
35
|
+
if association.is_a?(ActiveRecord::Reflection::BelongsToReflection)
|
36
|
+
can_cache = can_cache && !record.send(association.foreign_key).nil?
|
37
|
+
end
|
38
|
+
json.cache_if!(can_cache, can_cache ? [inc, association_cache_key(record, inc, subinc)] : nil) do
|
42
39
|
json.set! inc do
|
43
|
-
|
40
|
+
value = record.send(inc)
|
41
|
+
if value.nil?
|
42
|
+
json.value! nil
|
43
|
+
else
|
44
|
+
partial = model_partial(value)
|
45
|
+
json.partial! partial, partial.split("/").last.to_sym => value, includes: subinc
|
46
|
+
end
|
44
47
|
end
|
45
48
|
end
|
46
49
|
end
|
@@ -52,20 +55,20 @@ json.object! do
|
|
52
55
|
elsif value.is_a?(ActiveModel::Model)
|
53
56
|
json.set! inc do
|
54
57
|
partial = model_partial(value)
|
55
|
-
json.partial! partial, partial.split(
|
58
|
+
json.partial! partial, partial.split("/").last.to_sym => value, includes: subinc
|
56
59
|
end
|
57
60
|
else
|
58
61
|
json.set! inc, value.as_json
|
59
62
|
end
|
60
63
|
end
|
61
64
|
end
|
62
|
-
|
65
|
+
|
63
66
|
end
|
64
|
-
|
67
|
+
|
65
68
|
if !record.errors.blank?
|
66
69
|
errs = record.errors.to_hash
|
67
70
|
errs.default_proc = nil
|
68
|
-
json.set!
|
71
|
+
json.set! "errors", errs
|
69
72
|
end
|
70
|
-
|
73
|
+
|
71
74
|
end
|
@@ -50,14 +50,26 @@ else
|
|
50
50
|
|
51
51
|
json.set! 'attributes' do
|
52
52
|
model.columns.each do |column|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
53
|
+
default = column.default ? model.connection.lookup_cast_type_from_column(column).deserialize(column.default) : nil
|
54
|
+
type = case model.type_for_attribute(column.name)
|
55
|
+
when ActiveRecord::Enum::EnumType
|
56
|
+
default = model.defined_enums[column.name].key(default)
|
57
|
+
"string"
|
58
|
+
else
|
59
|
+
json_column_type(column.sql_type)
|
60
|
+
end
|
61
|
+
|
62
|
+
json.set! column.name do
|
63
|
+
json.set! 'type', type
|
64
|
+
json.set! 'default', default
|
65
|
+
json.set! 'primary_key', column.name == model.primary_key
|
66
|
+
json.set! 'null', column.null
|
67
|
+
json.set! 'array', column.array
|
68
|
+
json.set! 'comment', column.comment
|
69
|
+
# TODO: it would be nice if rails responded with a true or false here
|
70
|
+
# instead of the function itself
|
71
|
+
json.set! 'auto_populated', !!column.auto_populated? if column.respond_to?(:auto_populated?)
|
72
|
+
end
|
61
73
|
end
|
62
74
|
end
|
63
75
|
|
@@ -59,14 +59,28 @@ else
|
|
59
59
|
json.set! 'attributes' do
|
60
60
|
json.object! do
|
61
61
|
model.columns.each do |column|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
62
|
+
default = column.default ? model.connection.lookup_cast_type_from_column(column).deserialize(column.default) : nil
|
63
|
+
type = case model.type_for_attribute(column.name)
|
64
|
+
when ActiveRecord::Enum::EnumType
|
65
|
+
default = model.defined_enums[column.name].key(default)
|
66
|
+
"string"
|
67
|
+
else
|
68
|
+
json_column_type(column.sql_type)
|
69
|
+
end
|
70
|
+
|
71
|
+
json.set! column.name do
|
72
|
+
json.object! do
|
73
|
+
json.set! 'type', type
|
74
|
+
json.set! 'default', default
|
75
|
+
json.set! 'primary_key', column.name == model.primary_key
|
76
|
+
json.set! 'null', column.null
|
77
|
+
json.set! 'array', column.array
|
78
|
+
json.set! 'comment', column.comment
|
79
|
+
# TODO: it would be nice if rails responded with a true or false here
|
80
|
+
# instead of the function itself
|
81
|
+
json.set! 'auto_populated', !!column.auto_populated? if column.respond_to?(:auto_populated?)
|
82
|
+
end
|
83
|
+
end
|
70
84
|
end
|
71
85
|
end
|
72
86
|
end
|
@@ -1 +1 @@
|
|
1
|
-
json.partial! model_partial(model), model_partial(model).split('/').last.to_sym => instance_variable_get("@#{model.model_name.singular}"), includes: includes
|
1
|
+
json.partial! model_partial(model), model_partial(model).split('/').last.to_sym => instance_variable_get("@#{model.model_name.singular}"), includes: includes
|
data/lib/standard_api.rb
CHANGED
@@ -16,3 +16,8 @@ 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
|
+
autoload :Middleware, 'standard_api/middleware'
|
23
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'standard_api/test_helper'
|
2
2
|
|
3
|
-
class AccountsControllerTest <
|
3
|
+
class AccountsControllerTest < ActionDispatch::IntegrationTest
|
4
4
|
|
5
5
|
test 'include with cache' do
|
6
6
|
account = create(:account, photos: [])
|
@@ -14,20 +14,30 @@ class AccountsControllerTest < ActionController::TestCase
|
|
14
14
|
|
15
15
|
# Cache Miss
|
16
16
|
Account.any_instance.stubs(:photos_cached_at).returns(t1)
|
17
|
-
get
|
17
|
+
get account_path(account, include: :photos, format: :json)
|
18
18
|
assert_equal [photo.id], JSON(response.body)['photos'].map{|x| x['id']}
|
19
19
|
|
20
20
|
# Cache Hit
|
21
21
|
Account.any_instance.stubs(:photos).returns([])
|
22
22
|
Account.any_instance.stubs(:photos_cached_at).returns(t1)
|
23
|
-
get
|
23
|
+
get account_path(account, include: :photos, format: :json)
|
24
24
|
assert_equal [photo.id], JSON(response.body)['photos'].map{|x| x['id']}
|
25
25
|
|
26
26
|
# Cache Miss, photos_cached_at updated
|
27
27
|
Account.any_instance.stubs(:photos).returns(Photo.where('false = true'))
|
28
28
|
Account.any_instance.stubs(:photos_cached_at).returns(t2)
|
29
|
-
get
|
29
|
+
get account_path(account, include: :photos, format: :json)
|
30
30
|
assert_equal [], JSON(response.body)['photos'].map{|x| x['id']}
|
31
|
+
|
32
|
+
# Two associations that reference the same model
|
33
|
+
property = create(:property)
|
34
|
+
account = create(:account, property: property, subject: property)
|
35
|
+
Account.any_instance.expects(:property_cached_at).twice.returns(t1)
|
36
|
+
Account.any_instance.expects(:subject_cached_at).twice.returns(t1)
|
37
|
+
get account_path(account, include: { property: true, subject: true }, format: 'json')
|
38
|
+
json = JSON(response.body)
|
39
|
+
assert json.has_key?('property')
|
40
|
+
assert json.has_key?('subject')
|
31
41
|
end
|
32
42
|
|
33
43
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'standard_api/test_helper'
|
2
|
+
|
3
|
+
class ControllerIncludesTest < ActionDispatch::IntegrationTest
|
4
|
+
|
5
|
+
# = Including an invalid include
|
6
|
+
|
7
|
+
test "Controller#create with a valid include" do
|
8
|
+
property = build(:property)
|
9
|
+
|
10
|
+
json = assert_difference 'Property.count', 1 do
|
11
|
+
post "/properties", params: { property: property.attributes, include: [:photos] }, as: :json
|
12
|
+
JSON.parse(response.body)
|
13
|
+
end
|
14
|
+
|
15
|
+
assert_response :created
|
16
|
+
assert_equal [], json['photos']
|
17
|
+
end
|
18
|
+
|
19
|
+
test "Controller#update with a valid include" do
|
20
|
+
photo = create(:photo)
|
21
|
+
property = create(:property, {name: "A", photos: [photo]})
|
22
|
+
patch "/properties/#{property.id}", params: { property: {name: "B"}, include: [:photos] }, as: :json
|
23
|
+
|
24
|
+
json = JSON.parse(response.body)
|
25
|
+
assert_response :ok
|
26
|
+
assert_equal [photo.id], json['photos'].map { |j| j['id'] }
|
27
|
+
end
|
28
|
+
|
29
|
+
# test "Controller#destroy with a valid include" do
|
30
|
+
# No body is returned on a destroy so includes are used
|
31
|
+
# end
|
32
|
+
|
33
|
+
test "Controller#create_resource with a valid include" do
|
34
|
+
property = create(:property, accounts: [])
|
35
|
+
account = build(:account)
|
36
|
+
|
37
|
+
post "/properties/#{property.id}/accounts", params: { account: account.attributes, include: [:photos] }, as: :json
|
38
|
+
|
39
|
+
json = JSON.parse(response.body)
|
40
|
+
assert_response :created
|
41
|
+
assert_equal [], json['photos']
|
42
|
+
end
|
43
|
+
|
44
|
+
# test "Controller#add_resource with a valid include" do
|
45
|
+
# No body is returned on a destroy so includes are used
|
46
|
+
# end
|
47
|
+
|
48
|
+
# test "Controller#remove_resource with an invalid include" do
|
49
|
+
# No body is returned on a destroy so includes are used
|
50
|
+
# end
|
51
|
+
|
52
|
+
# = Including an invalid include
|
53
|
+
|
54
|
+
test "Controller#create with an invalid include" do
|
55
|
+
property = build(:property)
|
56
|
+
|
57
|
+
assert_no_difference 'Property.count' do
|
58
|
+
post "/properties", params: { property: property.attributes, include: [:accounts] }, as: :json
|
59
|
+
end
|
60
|
+
|
61
|
+
assert_response :bad_request
|
62
|
+
end
|
63
|
+
|
64
|
+
test "Controller#update with an invalid include" do
|
65
|
+
property = create(:property, {name: "A"})
|
66
|
+
patch "/properties/#{property.id}", params: { property: {name: "B"}, include: [:accounts] }, as: :json
|
67
|
+
|
68
|
+
assert_response :bad_request
|
69
|
+
assert_equal 'A', property.reload.name
|
70
|
+
end
|
71
|
+
|
72
|
+
# test "Controller#destroy with an invalid include" do
|
73
|
+
# No body is returned on a destroy so includes are used
|
74
|
+
# end
|
75
|
+
|
76
|
+
test "Controller#create_resource with an invalid include" do
|
77
|
+
property = create(:property, photos: [])
|
78
|
+
photo = build(:photo)
|
79
|
+
|
80
|
+
post "/properties/#{property.id}/photos", params: { photo: photo.attributes, include: [:camera] }, as: :json
|
81
|
+
assert_equal 0, property.reload.photos.count
|
82
|
+
assert_response :bad_request
|
83
|
+
end
|
84
|
+
|
85
|
+
# This test passes because includes are not used, response is a HEAD response
|
86
|
+
# and no includes are used.
|
87
|
+
test "Controller#add_resource with an invalid include" do
|
88
|
+
property = create(:property, photos: [])
|
89
|
+
photo = create(:photo)
|
90
|
+
|
91
|
+
post "/properties/#{property.id}/photos/#{photo.id}", params: { include: [:camera] }, as: :json
|
92
|
+
assert_equal 1, property.reload.photos.count
|
93
|
+
assert_response :created
|
94
|
+
end
|
95
|
+
|
96
|
+
# This test passes because includes are not used, response is a HEAD response
|
97
|
+
# and no includes are used.
|
98
|
+
test "Controller#remove_resource with an invalid include" do
|
99
|
+
photo = create(:photo)
|
100
|
+
property = create(:property, photos: [photo])
|
101
|
+
|
102
|
+
delete "/properties/#{property.id}/photos/#{photo.id}", params: { include: [:camera] }, as: :json
|
103
|
+
assert_equal 0, property.reload.photos.count
|
104
|
+
assert_response :no_content
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'standard_api/test_helper'
|
2
|
+
|
3
|
+
class ControllerSubresourceTest < ActionDispatch::IntegrationTest
|
4
|
+
|
5
|
+
# add_resource
|
6
|
+
test 'Controller#add_resource with has_many' do
|
7
|
+
property = create(:property, photos: [])
|
8
|
+
photo = create(:photo)
|
9
|
+
|
10
|
+
post "/properties/#{property.id}/photos/#{photo.id}"
|
11
|
+
assert_equal property.photos.reload.map(&:id), [photo.id]
|
12
|
+
assert_response :created
|
13
|
+
|
14
|
+
assert_raises ActiveRecord::RecordNotFound do
|
15
|
+
post "/properties/#{property.id}/photos/9999999"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
test 'Controller#add_resource with has_and_belongs_to_many' do
|
20
|
+
photo1 = create(:photo)
|
21
|
+
photo2 = create(:photo)
|
22
|
+
property = create(:property, photos: [photo1])
|
23
|
+
|
24
|
+
post "/properties/#{property.id}/photos/#{photo2.id}"
|
25
|
+
assert_equal property.photos.reload.map(&:id), [photo1.id, photo2.id]
|
26
|
+
assert_response :created
|
27
|
+
|
28
|
+
assert_raises ActiveRecord::RecordNotFound do
|
29
|
+
post "/properties/#{property.id}/photos/9999999"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
test 'Controller#add_resource with belongs_to' do
|
34
|
+
photo = create(:photo)
|
35
|
+
account = create(:account)
|
36
|
+
|
37
|
+
post "/photos/#{photo.id}/account/#{account.id}"
|
38
|
+
assert_equal photo.reload.account_id, account.id
|
39
|
+
assert_response :created
|
40
|
+
end
|
41
|
+
|
42
|
+
test 'Controller#add_resource with has_one' do
|
43
|
+
photo = create(:document)
|
44
|
+
property = create(:property)
|
45
|
+
post "/properties/#{property.id}/document/#{photo.id}"
|
46
|
+
assert_equal property.reload.document, photo
|
47
|
+
assert_response :created
|
48
|
+
end
|
49
|
+
|
50
|
+
test 'Controller#add_resource that is already there' do
|
51
|
+
photo = create(:photo)
|
52
|
+
property = create(:property, photos: [photo])
|
53
|
+
|
54
|
+
post "/properties/#{property.id}/photos/#{photo.id}"
|
55
|
+
assert_response :bad_request
|
56
|
+
assert_equal JSON(response.body), {
|
57
|
+
"errors" => [
|
58
|
+
"Relationship between Property and Photo violates unique constraints"
|
59
|
+
]
|
60
|
+
}
|
61
|
+
|
62
|
+
assert_raises ActiveRecord::RecordNotFound do
|
63
|
+
post "/properties/#{property.id}/photos/9999999"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# create_resource
|
68
|
+
test 'Controller#create_resource with has_many' do
|
69
|
+
property = create(:property, photos: [])
|
70
|
+
photo = build(:photo)
|
71
|
+
|
72
|
+
post "/properties/#{property.id}/photos", params: { photo: photo.attributes }, as: :json
|
73
|
+
assert_equal property.photos.reload.map(&:id), [JSON.parse(response.body)['id']]
|
74
|
+
assert_equal property.photos.count, 1
|
75
|
+
assert_response :created
|
76
|
+
end
|
77
|
+
|
78
|
+
test 'Controller#create_resource with has_and_belongs_to_many' do
|
79
|
+
photo1 = create(:photo)
|
80
|
+
photo2 = build(:photo)
|
81
|
+
property = create(:property, photos: [photo1])
|
82
|
+
|
83
|
+
post "/properties/#{property.id}/photos", params: { photo: photo2.attributes }, as: :json
|
84
|
+
assert_equal property.photos.reload.map(&:id).sort, [photo1.id, JSON.parse(response.body)['id']].sort
|
85
|
+
assert_equal property.photos.count, 2
|
86
|
+
assert_response :created
|
87
|
+
end
|
88
|
+
|
89
|
+
test 'Controller#create_resource with belongs_to' do
|
90
|
+
photo = create(:photo)
|
91
|
+
account = build(:account)
|
92
|
+
|
93
|
+
post "/photos/#{photo.id}/account", params: { account: account.attributes }, as: :json
|
94
|
+
assert_equal photo.reload.account_id, JSON.parse(response.body)['id']
|
95
|
+
assert_response :created
|
96
|
+
end
|
97
|
+
|
98
|
+
test 'Controller#create_resource with has_one' do
|
99
|
+
account = build(:account)
|
100
|
+
property = create(:property)
|
101
|
+
post "/properties/#{property.id}/landlord", params: { landlord: account.attributes }, as: :json
|
102
|
+
assert_equal property.reload.landlord.id, JSON.parse(response.body)['id']
|
103
|
+
assert_response :created
|
104
|
+
end
|
105
|
+
|
106
|
+
# remove_resource
|
107
|
+
test 'Controller#remove_resource' do
|
108
|
+
photo = create(:photo)
|
109
|
+
property = create(:property, photos: [photo])
|
110
|
+
assert_equal property.photos.reload, [photo]
|
111
|
+
delete "/properties/#{property.id}/photos/#{photo.id}"
|
112
|
+
assert_equal property.photos.reload, []
|
113
|
+
assert_response :no_content
|
114
|
+
|
115
|
+
assert_raises ActiveRecord::RecordNotFound do
|
116
|
+
delete "/properties/#{property.id}/photos/9999999"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
test 'Controller#remove_resource with has_one' do
|
121
|
+
photo = create(:document)
|
122
|
+
property = create(:property, document: photo)
|
123
|
+
assert_equal property.document, photo
|
124
|
+
delete "/properties/#{property.id}/document/#{photo.id}"
|
125
|
+
assert_nil property.reload.document
|
126
|
+
assert_response :no_content
|
127
|
+
end
|
128
|
+
|
129
|
+
test 'Controller#remove_resource with belongs_to' do
|
130
|
+
account = create(:account)
|
131
|
+
photo = create(:photo, account: account)
|
132
|
+
|
133
|
+
delete "/photos/#{photo.id}/account/#{account.id}"
|
134
|
+
assert_nil photo.reload.account_id
|
135
|
+
assert_response :no_content
|
136
|
+
end
|
137
|
+
|
138
|
+
test 'Controller#remove_resource with belongs_to unless not match' do
|
139
|
+
account1 = create(:account)
|
140
|
+
account2 = create(:account)
|
141
|
+
photo = create(:photo, account: account1)
|
142
|
+
|
143
|
+
delete "/photos/#{photo.id}/account/#{account2.id}"
|
144
|
+
assert_equal photo.reload.account_id, account1.id
|
145
|
+
assert_response :not_found
|
146
|
+
end
|
147
|
+
|
148
|
+
test 'Controller#remove_resource with belongs_to unless not match and is nil' do
|
149
|
+
account = create(:account)
|
150
|
+
photo = create(:photo)
|
151
|
+
|
152
|
+
delete "/photos/#{photo.id}/account/#{account.id}"
|
153
|
+
assert_nil photo.reload.account_id
|
154
|
+
assert_response :not_found
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
@@ -45,86 +45,102 @@ class HelpersTest < ActionView::TestCase
|
|
45
45
|
assert can_cache?(Account, {photos: {account: {}}})
|
46
46
|
end
|
47
47
|
|
48
|
-
test '::can_cache_relation?' do
|
48
|
+
test '::can_cache_relation? with non-persisted record' do
|
49
|
+
account = build(:account)
|
50
|
+
assert !can_cache_relation?(account, :photos, {})
|
51
|
+
assert !can_cache_relation?(account, :photos, {})
|
52
|
+
assert !can_cache_relation?(account, :photos, {account: {}})
|
53
|
+
assert !can_cache_relation?(account, :photos, {account: {}})
|
54
|
+
end
|
55
|
+
|
56
|
+
test '::can_cache_relation? with persisted record' do
|
57
|
+
account = create(:account)
|
58
|
+
|
49
59
|
Account.expects(:column_names).returns(['id', 'cached_at'])
|
50
|
-
assert !can_cache_relation?(
|
60
|
+
assert !can_cache_relation?(account, :photos, {})
|
51
61
|
|
52
62
|
Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at'])
|
53
|
-
assert can_cache_relation?(
|
63
|
+
assert can_cache_relation?(account, :photos, {})
|
54
64
|
|
55
65
|
Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at'])
|
56
|
-
assert !can_cache_relation?(
|
66
|
+
assert !can_cache_relation?(account, :photos, {account: {}})
|
57
67
|
|
58
68
|
Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at', 'photos_account_cached_at'])
|
59
|
-
assert can_cache_relation?(
|
69
|
+
assert can_cache_relation?(account, :photos, {account: {}})
|
60
70
|
end
|
61
71
|
|
62
72
|
test '::association_cache_key(record, relation, subincludes)' do
|
63
73
|
account = create(:account)
|
74
|
+
photo = create(:photo, account: account)
|
64
75
|
t1 = Time.now
|
65
76
|
t2 = 1.day.from_now
|
66
77
|
t3 = 2.days.from_now
|
67
78
|
|
79
|
+
Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at', 'photos_property_cached_at'])
|
68
80
|
account.expects(:photos_cached_at).returns(t1)
|
69
81
|
|
70
82
|
assert_equal(
|
71
|
-
"accounts/#{account.id}/photos-#{t1.utc.
|
83
|
+
"accounts/#{account.id}/photos-#{t1.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
|
72
84
|
association_cache_key(account, :photos, {})
|
73
85
|
)
|
74
86
|
|
75
|
-
|
87
|
+
Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at', 'photos_property_cached_at'])
|
76
88
|
account.expects(:photos_cached_at).returns(t1)
|
77
89
|
account.expects(:photos_property_cached_at).returns(t2)
|
78
90
|
assert_equal(
|
79
|
-
"accounts/#{account.id}/photos-2ea683a694a33359514c41435f8f0646-#{t2.utc.
|
91
|
+
"accounts/#{account.id}/photos-2ea683a694a33359514c41435f8f0646-#{t2.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
|
80
92
|
association_cache_key(account, :photos, {property: {}})
|
81
93
|
)
|
82
94
|
|
95
|
+
Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at', 'photos_property_cached_at'])
|
83
96
|
account.expects(:photos_cached_at).returns(t1)
|
84
97
|
account.expects(:photos_property_cached_at).returns(t2)
|
85
98
|
assert_equal(
|
86
|
-
"accounts/#{account.id}/photos-779c17ef027655fd8c06c3083d2df64b-#{t2.utc.
|
99
|
+
"accounts/#{account.id}/photos-779c17ef027655fd8c06c3083d2df64b-#{t2.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
|
87
100
|
association_cache_key(account, :photos, { "property" => { "order" => { "x" => "desc" }}})
|
88
101
|
)
|
89
102
|
|
103
|
+
Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at', 'photos_property_cached_at', 'photos_agents_cached_at'])
|
90
104
|
account.expects(:photos_cached_at).returns(t1)
|
91
105
|
account.expects(:photos_property_cached_at).returns(t2)
|
92
106
|
account.expects(:photos_agents_cached_at).returns(t3)
|
93
107
|
assert_equal(
|
94
|
-
"accounts/#{account.id}/photos-abbee2d4535400c162c8dbf14bbef6d5-#{t3.utc.
|
108
|
+
"accounts/#{account.id}/photos-abbee2d4535400c162c8dbf14bbef6d5-#{t3.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
|
95
109
|
association_cache_key(account, :photos, {property: {}, agents: {}})
|
96
110
|
)
|
97
111
|
|
112
|
+
Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at', 'photos_property_cached_at', 'photos_property_agents_cached_at'])
|
98
113
|
account.expects(:photos_cached_at).returns(t1)
|
99
114
|
account.expects(:photos_property_cached_at).returns(t2)
|
100
115
|
account.expects(:photos_property_agents_cached_at).returns(t3)
|
101
116
|
assert_equal(
|
102
|
-
"accounts/#{account.id}/photos-0962ae73347c5c605d329eaa25e2be49-#{t3.utc.
|
117
|
+
"accounts/#{account.id}/photos-0962ae73347c5c605d329eaa25e2be49-#{t3.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
|
103
118
|
association_cache_key(account, :photos, {property: {agents: {}}})
|
104
119
|
)
|
105
120
|
|
121
|
+
Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at', 'photos_property_cached_at', 'photos_agents_cached_at', 'photos_property_addresses_cached_at'])
|
106
122
|
account.expects(:photos_cached_at).returns(t1)
|
107
123
|
account.expects(:photos_property_cached_at).returns(t2)
|
108
124
|
account.expects(:photos_agents_cached_at).returns(t2)
|
109
125
|
account.expects(:photos_property_addresses_cached_at).returns(t3)
|
110
126
|
assert_equal(
|
111
|
-
"accounts/#{account.id}/photos-00ea6afe3ff68037f8b4dcdb275e2a24-#{t3.utc.
|
127
|
+
"accounts/#{account.id}/photos-00ea6afe3ff68037f8b4dcdb275e2a24-#{t3.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
|
112
128
|
association_cache_key(account, :photos, {property: {addresses: {}}, agents: {}})
|
113
129
|
)
|
114
130
|
|
115
131
|
# Belongs to
|
116
|
-
|
132
|
+
Photo.expects(:column_names).returns(['id', 'cached_at', 'account_cached_at'])
|
117
133
|
photo.expects(:account_cached_at).returns(t1)
|
118
134
|
assert_equal(
|
119
|
-
"accounts/#{account.id}-#{t1.utc.
|
135
|
+
"accounts/#{account.id}-#{t1.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
|
120
136
|
association_cache_key(photo, :account, {})
|
121
137
|
)
|
122
138
|
|
123
|
-
|
139
|
+
Photo.expects(:column_names).returns(['id', 'cached_at', 'account_cached_at', 'account_photos_cached_at'])
|
124
140
|
photo.expects(:account_cached_at).returns(t1)
|
125
141
|
photo.expects(:account_photos_cached_at).returns(t2)
|
126
142
|
assert_equal(
|
127
|
-
"accounts/#{account.id}/07437ce3863467f4cd715ae1ef930f08-#{t2.utc.
|
143
|
+
"accounts/#{account.id}/07437ce3863467f4cd715ae1ef930f08-#{t2.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
|
128
144
|
association_cache_key(photo, :account, {photos: {}})
|
129
145
|
)
|
130
146
|
end
|
@@ -134,6 +150,7 @@ class HelpersTest < ActionView::TestCase
|
|
134
150
|
assert_equal 'string', json_column_type('character varying(2)')
|
135
151
|
assert_equal 'string', json_column_type('character varying(255)')
|
136
152
|
assert_equal 'datetime', json_column_type('timestamp without time zone')
|
153
|
+
assert_equal 'datetime', json_column_type('timestamp(6) without time zone')
|
137
154
|
assert_equal 'datetime', json_column_type('time without time zone')
|
138
155
|
assert_equal 'string', json_column_type('text')
|
139
156
|
assert_equal 'hash', json_column_type('json')
|
@@ -151,6 +168,6 @@ class HelpersTest < ActionView::TestCase
|
|
151
168
|
assert_equal 'boolean', json_column_type('boolean')
|
152
169
|
assert_equal 'ewkb', json_column_type('geometry')
|
153
170
|
assert_equal 'string', json_column_type('uuid')
|
154
|
-
|
171
|
+
end
|
155
172
|
|
156
173
|
end
|