standardapi 6.1.0 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +80 -58
  3. data/lib/standard_api/access_control_list.rb +40 -6
  4. data/lib/standard_api/controller.rb +96 -28
  5. data/lib/standard_api/helpers.rb +13 -7
  6. data/lib/standard_api/middleware.rb +5 -0
  7. data/lib/standard_api/railtie.rb +17 -0
  8. data/lib/standard_api/route_helpers.rb +59 -9
  9. data/lib/standard_api/test_case/calculate_tests.rb +7 -6
  10. data/lib/standard_api/test_case/destroy_tests.rb +19 -7
  11. data/lib/standard_api/test_case/index_tests.rb +7 -13
  12. data/lib/standard_api/test_case/show_tests.rb +7 -7
  13. data/lib/standard_api/test_case/update_tests.rb +7 -6
  14. data/lib/standard_api/version.rb +1 -1
  15. data/lib/standard_api/views/application/_record.json.jbuilder +4 -3
  16. data/lib/standard_api/views/application/_record.streamer +4 -3
  17. data/lib/standard_api/views/application/_schema.json.jbuilder +20 -8
  18. data/lib/standard_api/views/application/_schema.streamer +22 -8
  19. data/lib/standard_api.rb +1 -0
  20. data/test/standard_api/caching_test.rb +2 -2
  21. data/test/standard_api/controller/include_test.rb +107 -0
  22. data/test/standard_api/controller/subresource_test.rb +157 -0
  23. data/test/standard_api/helpers_test.rb +9 -8
  24. data/test/standard_api/nested_attributes/belongs_to_test.rb +71 -0
  25. data/test/standard_api/nested_attributes/has_and_belongs_to_many_test.rb +70 -0
  26. data/test/standard_api/nested_attributes/has_many_test.rb +85 -0
  27. data/test/standard_api/nested_attributes/has_one_test.rb +71 -0
  28. data/test/standard_api/route_helpers_test.rb +56 -0
  29. data/test/standard_api/standard_api_test.rb +80 -50
  30. data/test/standard_api/test_app/app/controllers/acl/camera_acl.rb +7 -0
  31. data/test/standard_api/test_app/app/controllers/acl/photo_acl.rb +13 -0
  32. data/test/standard_api/test_app/app/controllers/acl/property_acl.rb +7 -1
  33. data/test/standard_api/test_app/controllers.rb +17 -0
  34. data/test/standard_api/test_app/models.rb +59 -2
  35. data/test/standard_api/test_app/test/factories.rb +3 -0
  36. data/test/standard_api/test_app/views/sessions/create.json.jbuilder +1 -0
  37. data/test/standard_api/test_app/views/sessions/create.streamer +3 -0
  38. data/test/standard_api/test_app.rb +12 -1
  39. data/test/standard_api/test_helper.rb +9 -0
  40. metadata +52 -13
@@ -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
@@ -117,27 +117,21 @@ module StandardAPI
117
117
  end
118
118
  end
119
119
 
120
- test '#index.json mask' do
120
+ test '#index.json mask_for' do
121
121
  # This is just to instance @controller
122
122
  get resource_path(:index, format: :json), params: { limit: 1 }
123
123
 
124
- # If #current_mask isn't defined by StandardAPI we don't know how to
125
- # test other's implementation of #current_mask. Return and don't test.
126
- 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
127
127
 
128
128
  m = create_model
129
- @controller.current_mask[plural_name] = { id: m.id }
130
- get :index, format: :json
131
- models = @controller.instance_variable_get("@#{plural_name}")
132
- assert_equal model.where(id: m.id).sort(required_orders).to_sql, models.to_sql
133
- @controller.current_mask.delete(plural_name)
134
-
135
- @controller.current_mask[plural_name.to_sym] = { id: m.id }
129
+ @controller.define_singleton_method(:mask_for) do |table_name|
130
+ { id: m.id }
131
+ end
136
132
  get :index, format: :json
137
133
  models = @controller.instance_variable_get("@#{plural_name}")
138
134
  assert_equal model.where(id: m.id).sort(required_orders).to_sql, models.to_sql
139
- @controller.current_mask.delete(plural_name.to_sym)
140
-
141
135
  end
142
136
  end
143
137
  end
@@ -52,22 +52,22 @@ module StandardAPI
52
52
  end
53
53
  end
54
54
 
55
- test '#show.json mask' do
55
+ test '#show.json mask_for' do
56
56
  m = create_model
57
57
 
58
58
  # This is just to instance @controller
59
59
  get resource_path(:show, id: m.id, format: :json)
60
60
 
61
- # If #current_mask isn't defined by StandardAPI we don't know how to
62
- # test other's implementation of #current_mask. Return and don't test.
63
- return if @controller.method(:current_mask).owner != StandardAPI
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
-
66
- @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
67
68
  assert_raises(ActiveRecord::RecordNotFound) do
68
69
  get resource_path(:show, id: m.id, format: :json)
69
70
  end
70
- @controller.current_mask.delete(plural_name)
71
71
  end
72
72
 
73
73
  end
@@ -131,21 +131,22 @@ module StandardAPI
131
131
  end
132
132
  end
133
133
 
134
- test '#update.json mask' do
134
+ test '#update.json mask_for' do
135
135
  m = create_model
136
136
 
137
137
  # This is just to instance @controller
138
138
  get resource_path(:index, format: :json), params: { limit: 1 }
139
139
 
140
- # If #current_mask isn't defined by StandardAPI we don't know how to
141
- # test other's implementation of #current_mask. Return and don't test.
142
- 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
143
143
 
144
- @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
145
147
  assert_raises(ActiveRecord::RecordNotFound) do
146
148
  put resource_path(:update, :id => m.id), as: :json
147
149
  end
148
- @controller.current_mask.delete(plural_name)
149
150
  end
150
151
 
151
152
  end
@@ -1,3 +1,3 @@
1
1
  module StandardAPI
2
- VERSION = '6.1.0'
2
+ VERSION = '7.1.0'
3
3
  end
@@ -1,7 +1,8 @@
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|
@@ -1,9 +1,10 @@
1
1
  json.object! do
2
2
 
3
- record.attributes.each do |name, value|
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.to_s }
6
- json.set! name, value
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|
@@ -50,14 +50,26 @@ else
50
50
 
51
51
  json.set! 'attributes' do
52
52
  model.columns.each do |column|
53
- json.set! column.name, {
54
- type: json_column_type(column.sql_type),
55
- default: column.default ? model.connection.lookup_cast_type_from_column(column).deserialize(column.default) : nil,
56
- primary_key: column.name == model.primary_key,
57
- null: column.null,
58
- array: column.array,
59
- comment: column.comment
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
- json.set! column.name, {
63
- type: json_column_type(column.sql_type),
64
- default: column.default ? model.connection.lookup_cast_type_from_column(column).deserialize(column.default) : nil,
65
- primary_key: column.name == model.primary_key,
66
- null: column.null,
67
- array: column.array,
68
- comment: column.comment
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
data/lib/standard_api.rb CHANGED
@@ -19,4 +19,5 @@ require 'standard_api/railtie'
19
19
 
20
20
  module StandardAPI
21
21
  autoload :AccessControlList, 'standard_api/access_control_list'
22
+ autoload :Middleware, 'standard_api/middleware'
22
23
  end
@@ -32,8 +32,8 @@ class AccountsControllerTest < ActionDispatch::IntegrationTest
32
32
  # Two associations that reference the same model
33
33
  property = create(:property)
34
34
  account = create(:account, property: property, subject: property)
35
- Account.any_instance.expects(:property_cached_at).returns(t1)
36
- Account.any_instance.expects(:subject_cached_at).returns(t1)
35
+ Account.any_instance.expects(:property_cached_at).twice.returns(t1)
36
+ Account.any_instance.expects(:subject_cached_at).twice.returns(t1)
37
37
  get account_path(account, include: { property: true, subject: true }, format: 'json')
38
38
  json = JSON(response.body)
39
39
  assert json.has_key?('property')
@@ -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
@@ -80,7 +80,7 @@ class HelpersTest < ActionView::TestCase
80
80
  account.expects(:photos_cached_at).returns(t1)
81
81
 
82
82
  assert_equal(
83
- "accounts/#{account.id}/photos-#{t1.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
83
+ "accounts/#{account.id}/photos-#{t1.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
84
84
  association_cache_key(account, :photos, {})
85
85
  )
86
86
 
@@ -88,7 +88,7 @@ class HelpersTest < ActionView::TestCase
88
88
  account.expects(:photos_cached_at).returns(t1)
89
89
  account.expects(:photos_property_cached_at).returns(t2)
90
90
  assert_equal(
91
- "accounts/#{account.id}/photos-2ea683a694a33359514c41435f8f0646-#{t2.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
91
+ "accounts/#{account.id}/photos-2ea683a694a33359514c41435f8f0646-#{t2.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
92
92
  association_cache_key(account, :photos, {property: {}})
93
93
  )
94
94
 
@@ -96,7 +96,7 @@ class HelpersTest < ActionView::TestCase
96
96
  account.expects(:photos_cached_at).returns(t1)
97
97
  account.expects(:photos_property_cached_at).returns(t2)
98
98
  assert_equal(
99
- "accounts/#{account.id}/photos-779c17ef027655fd8c06c3083d2df64b-#{t2.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
99
+ "accounts/#{account.id}/photos-779c17ef027655fd8c06c3083d2df64b-#{t2.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
100
100
  association_cache_key(account, :photos, { "property" => { "order" => { "x" => "desc" }}})
101
101
  )
102
102
 
@@ -105,7 +105,7 @@ class HelpersTest < ActionView::TestCase
105
105
  account.expects(:photos_property_cached_at).returns(t2)
106
106
  account.expects(:photos_agents_cached_at).returns(t3)
107
107
  assert_equal(
108
- "accounts/#{account.id}/photos-abbee2d4535400c162c8dbf14bbef6d5-#{t3.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
108
+ "accounts/#{account.id}/photos-abbee2d4535400c162c8dbf14bbef6d5-#{t3.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
109
109
  association_cache_key(account, :photos, {property: {}, agents: {}})
110
110
  )
111
111
 
@@ -114,7 +114,7 @@ class HelpersTest < ActionView::TestCase
114
114
  account.expects(:photos_property_cached_at).returns(t2)
115
115
  account.expects(:photos_property_agents_cached_at).returns(t3)
116
116
  assert_equal(
117
- "accounts/#{account.id}/photos-0962ae73347c5c605d329eaa25e2be49-#{t3.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
117
+ "accounts/#{account.id}/photos-0962ae73347c5c605d329eaa25e2be49-#{t3.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
118
118
  association_cache_key(account, :photos, {property: {agents: {}}})
119
119
  )
120
120
 
@@ -124,7 +124,7 @@ class HelpersTest < ActionView::TestCase
124
124
  account.expects(:photos_agents_cached_at).returns(t2)
125
125
  account.expects(:photos_property_addresses_cached_at).returns(t3)
126
126
  assert_equal(
127
- "accounts/#{account.id}/photos-00ea6afe3ff68037f8b4dcdb275e2a24-#{t3.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
127
+ "accounts/#{account.id}/photos-00ea6afe3ff68037f8b4dcdb275e2a24-#{t3.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
128
128
  association_cache_key(account, :photos, {property: {addresses: {}}, agents: {}})
129
129
  )
130
130
 
@@ -132,7 +132,7 @@ class HelpersTest < ActionView::TestCase
132
132
  Photo.expects(:column_names).returns(['id', 'cached_at', 'account_cached_at'])
133
133
  photo.expects(:account_cached_at).returns(t1)
134
134
  assert_equal(
135
- "accounts/#{account.id}-#{t1.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
135
+ "accounts/#{account.id}-#{t1.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
136
136
  association_cache_key(photo, :account, {})
137
137
  )
138
138
 
@@ -140,7 +140,7 @@ class HelpersTest < ActionView::TestCase
140
140
  photo.expects(:account_cached_at).returns(t1)
141
141
  photo.expects(:account_photos_cached_at).returns(t2)
142
142
  assert_equal(
143
- "accounts/#{account.id}/07437ce3863467f4cd715ae1ef930f08-#{t2.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
143
+ "accounts/#{account.id}/07437ce3863467f4cd715ae1ef930f08-#{t2.utc.to_fs(ActiveRecord::Base.cache_timestamp_format)}",
144
144
  association_cache_key(photo, :account, {photos: {}})
145
145
  )
146
146
  end
@@ -150,6 +150,7 @@ class HelpersTest < ActionView::TestCase
150
150
  assert_equal 'string', json_column_type('character varying(2)')
151
151
  assert_equal 'string', json_column_type('character varying(255)')
152
152
  assert_equal 'datetime', json_column_type('timestamp without time zone')
153
+ assert_equal 'datetime', json_column_type('timestamp(6) without time zone')
153
154
  assert_equal 'datetime', json_column_type('time without time zone')
154
155
  assert_equal 'string', json_column_type('text')
155
156
  assert_equal 'hash', json_column_type('json')