standardapi 6.0.0.30 → 6.0.0.32

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf7ed75a06eda9f024ebd87caccab44573802435d0494944137d92da6403fca2
4
- data.tar.gz: f44ad9cdd0d8bb87bc971f0b11b8f50b5f5f1289911d7bf4764f6f867b59b407
3
+ metadata.gz: 6f8e9f6eee237614e9c303c8fe9e2e624b081272627f9a7aa3f680276872c1a9
4
+ data.tar.gz: 73ede0c9b6229117b4781071441ac0581a06b16592f95f04959932c010ecb8f0
5
5
  SHA512:
6
- metadata.gz: 2f0f4ba53a851f87467a584b0a37708a1d2c6a52fc3360c31fbd50462af17918b7c43b435001410984291db24a451940e97487fc549f993d35ecc63d832d3b20
7
- data.tar.gz: 26d70be42fd115039c04a4a0ba614225c05a549af0847d3473659c9d593002cd11addcfd330000163eb9d1597843c274593ba99ddd6f9a57ee39b83e8a05fcbb
6
+ metadata.gz: 904192ff81007b628b672e094dfaad19f4b2c49a0e4119631dd46c320a85d0e2c06200b5570d29d3b70ded73046b843dfd28ce3e40358d3b2294bbf9bb7b50f6
7
+ data.tar.gz: f9ed735aabd0830d6b2c7a390d4f2bca3bf8bcc1d8b806b615a76462404f58c9b65241c75e7b7e62abe84539705249ed10c82f9f5dcc4cc1ee4911e51513200f
@@ -1,3 +1,3 @@
1
1
  module StandardAPI
2
- VERSION = '6.0.0.30'
2
+ VERSION = '6.0.0.32'
3
3
  end
@@ -6,39 +6,42 @@ end
6
6
 
7
7
  includes.each do |inc, subinc|
8
8
  next if ["limit", "offset", "order", "when", "where", "distinct", "distinct_on"].include?(inc)
9
-
9
+
10
+
10
11
  case association = record.class.reflect_on_association(inc)
11
- when ActiveRecord::Reflection::HasManyReflection, ActiveRecord::Reflection::HasAndBelongsToManyReflection, ActiveRecord::Reflection::ThroughReflection
12
- can_cache = can_cache_relation?(record.class, inc, subinc)
13
- json.cache_if!(can_cache, can_cache ? association_cache_key(record, inc, subinc) : nil) do
14
- partial = model_partial(association.klass)
15
- json.set! inc do
16
- # TODO limit causes preloaded assocations to reload
17
- sub_records = record.send(inc)
18
-
19
- sub_records = sub_records.limit(subinc['limit']) if subinc['limit']
20
- sub_records = sub_records.offset(subinc['offset']) if subinc['offset']
21
- sub_records = sub_records.order(subinc['order']) if subinc['order']
22
- sub_records = sub_records.filter(subinc['where']) if subinc['where']
23
- sub_records = sub_records.distinct if subinc['distinct']
24
- sub_records = sub_records.distinct_on(subinc['distinct_on']) if subinc['distinct_on']
12
+ when ActiveRecord::Reflection::AbstractReflection
13
+ if association.collection?
14
+ can_cache = can_cache_relation?(record.class, inc, subinc)
15
+ json.cache_if!(can_cache, can_cache ? association_cache_key(record, inc, subinc) : nil) do
16
+ partial = model_partial(association.klass)
17
+ json.set! inc do
18
+ # TODO limit causes preloaded assocations to reload
19
+ sub_records = record.send(inc)
20
+
21
+ sub_records = sub_records.limit(subinc['limit']) if subinc['limit']
22
+ sub_records = sub_records.offset(subinc['offset']) if subinc['offset']
23
+ sub_records = sub_records.order(subinc['order']) if subinc['order']
24
+ sub_records = sub_records.filter(subinc['where']) if subinc['where']
25
+ sub_records = sub_records.distinct if subinc['distinct']
26
+ sub_records = sub_records.distinct_on(subinc['distinct_on']) if subinc['distinct_on']
25
27
 
26
- json.array! sub_records, partial: partial, as: partial.split('/').last, locals: { includes: subinc }
28
+ json.array! sub_records, partial: partial, as: partial.split('/').last, locals: { includes: subinc }
29
+ end
27
30
  end
28
- end
29
- when ActiveRecord::Reflection::BelongsToReflection, ActiveRecord::Reflection::HasOneReflection
30
- can_cache = can_cache_relation?(record.class, inc, subinc)
31
- if association.is_a?(ActiveRecord::Reflection::BelongsToReflection)
32
- can_cache = can_cache && !record.send(association.foreign_key).nil?
33
- end
34
- json.cache_if!(can_cache, can_cache ? association_cache_key(record, inc, subinc) : nil) do
35
- value = record.send(inc)
36
- if value.nil?
37
- json.set! inc, nil
38
- else
39
- partial = model_partial(value)
40
- json.set! inc do
41
- json.partial! partial, partial.split('/').last.to_sym => value, includes: subinc
31
+ else
32
+ can_cache = can_cache_relation?(record.class, inc, subinc)
33
+ if association.is_a?(ActiveRecord::Reflection::BelongsToReflection)
34
+ can_cache = can_cache && !record.send(association.foreign_key).nil?
35
+ end
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)
42
+ json.set! inc do
43
+ json.partial! partial, partial.split('/').last.to_sym => value, includes: subinc
44
+ end
42
45
  end
43
46
  end
44
47
  end
@@ -57,7 +60,7 @@ includes.each do |inc, subinc|
57
60
  end
58
61
  end
59
62
  end
60
-
63
+
61
64
  end
62
65
 
63
66
  if !record.errors.blank?
@@ -0,0 +1,33 @@
1
+ require 'standard_api/test_helper'
2
+
3
+ class AccountsControllerTest < ActionController::TestCase
4
+
5
+ test 'include with cache' do
6
+ account = create(:account, photos: [])
7
+ photo = create(:photo, account_id: account.id)
8
+
9
+ t1 = 1.day.from_now
10
+ t2 = 2.days.from_now
11
+
12
+ columns = Account.column_names + ['photos_account_cached_at', 'photos_cached_at']
13
+ Account.stubs(:column_names).returns(columns)
14
+
15
+ # Cache Miss
16
+ Account.any_instance.stubs(:photos_cached_at).returns(t1)
17
+ get :show, params: {id: account.id, include: :photos}, format: :json
18
+ assert_equal [photo.id], JSON(response.body)['photos'].map{|x| x['id']}
19
+
20
+ # Cache Hit
21
+ Account.any_instance.stubs(:photos).returns([])
22
+ Account.any_instance.stubs(:photos_cached_at).returns(t1)
23
+ get :show, params: {id: account.id, include: :photos}, format: :json
24
+ assert_equal [photo.id], JSON(response.body)['photos'].map{|x| x['id']}
25
+
26
+ # Cache Miss, photos_cached_at updated
27
+ Account.any_instance.stubs(:photos).returns(Photo.where('false = true'))
28
+ Account.any_instance.stubs(:photos_cached_at).returns(t2)
29
+ get :show, params: {id: account.id, include: :photos}, format: :json
30
+ assert_equal [], JSON(response.body)['photos'].map{|x| x['id']}
31
+ end
32
+
33
+ end
@@ -0,0 +1,156 @@
1
+ require 'standard_api/test_helper'
2
+
3
+ class HelpersTest < ActionView::TestCase
4
+ include StandardAPI::Helpers
5
+
6
+ test "::cached_at_columns_for_includes(includes)" do
7
+ assert_equal(
8
+ ['photos_cached_at'],
9
+ cached_at_columns_for_includes({photos: {}})
10
+ )
11
+
12
+ assert_equal(
13
+ ['photos_cached_at', 'photos_account_cached_at'],
14
+ cached_at_columns_for_includes({photos: {account: {}}})
15
+ )
16
+
17
+ assert_equal(
18
+ ['photos_cached_at', 'photos_account_cached_at', 'photos_properties_cached_at'],
19
+ cached_at_columns_for_includes({photos: {account: {}, properties: {}}})
20
+ )
21
+
22
+ assert_equal(
23
+ ['photos_cached_at', 'photos_account_cached_at', 'photos_properties_cached_at', 'photos_properties_landlord_cached_at'],
24
+ cached_at_columns_for_includes({photos: {account: {}, properties: {landlord: {}}}})
25
+ )
26
+ end
27
+
28
+ test "::can_cache?" do
29
+ Account.expects(:column_names).returns(['id'])
30
+ assert !can_cache?(Account, {})
31
+
32
+ Account.expects(:column_names).returns(['id', 'cached_at'])
33
+ assert can_cache?(Account, {})
34
+
35
+ Account.expects(:column_names).returns(['id', 'cached_at'])
36
+ assert !can_cache?(Account, {photos: {}})
37
+
38
+ Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at'])
39
+ assert can_cache?(Account, {photos: {}})
40
+
41
+ Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at'])
42
+ assert !can_cache?(Account, {photos: {account: {}}})
43
+
44
+ Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at', 'photos_account_cached_at'])
45
+ assert can_cache?(Account, {photos: {account: {}}})
46
+ end
47
+
48
+ test '::can_cache_relation?' do
49
+ Account.expects(:column_names).returns(['id', 'cached_at'])
50
+ assert !can_cache_relation?(Account, :photos, {})
51
+
52
+ Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at'])
53
+ assert can_cache_relation?(Account, :photos, {})
54
+
55
+ Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at'])
56
+ assert !can_cache_relation?(Account, :photos, {account: {}})
57
+
58
+ Account.expects(:column_names).returns(['id', 'cached_at', 'photos_cached_at', 'photos_account_cached_at'])
59
+ assert can_cache_relation?(Account, :photos, {account: {}})
60
+ end
61
+
62
+ test '::association_cache_key(record, relation, subincludes)' do
63
+ account = create(:account)
64
+ t1 = Time.now
65
+ t2 = 1.day.from_now
66
+ t3 = 2.days.from_now
67
+
68
+ account.expects(:photos_cached_at).returns(t1)
69
+
70
+ assert_equal(
71
+ "accounts/#{account.id}/photos-#{t1.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
72
+ association_cache_key(account, :photos, {})
73
+ )
74
+
75
+
76
+ account.expects(:photos_cached_at).returns(t1)
77
+ account.expects(:photos_property_cached_at).returns(t2)
78
+ assert_equal(
79
+ "accounts/#{account.id}/photos-2ea683a694a33359514c41435f8f0646-#{t2.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
80
+ association_cache_key(account, :photos, {property: {}})
81
+ )
82
+
83
+ account.expects(:photos_cached_at).returns(t1)
84
+ account.expects(:photos_property_cached_at).returns(t2)
85
+ assert_equal(
86
+ "accounts/#{account.id}/photos-779c17ef027655fd8c06c3083d2df64b-#{t2.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
87
+ association_cache_key(account, :photos, { "property" => { "order" => { "x" => "desc" }}})
88
+ )
89
+
90
+ account.expects(:photos_cached_at).returns(t1)
91
+ account.expects(:photos_property_cached_at).returns(t2)
92
+ account.expects(:photos_agents_cached_at).returns(t3)
93
+ assert_equal(
94
+ "accounts/#{account.id}/photos-abbee2d4535400c162c8dbf14bbef6d5-#{t3.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
95
+ association_cache_key(account, :photos, {property: {}, agents: {}})
96
+ )
97
+
98
+ account.expects(:photos_cached_at).returns(t1)
99
+ account.expects(:photos_property_cached_at).returns(t2)
100
+ account.expects(:photos_property_agents_cached_at).returns(t3)
101
+ assert_equal(
102
+ "accounts/#{account.id}/photos-0962ae73347c5c605d329eaa25e2be49-#{t3.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
103
+ association_cache_key(account, :photos, {property: {agents: {}}})
104
+ )
105
+
106
+ account.expects(:photos_cached_at).returns(t1)
107
+ account.expects(:photos_property_cached_at).returns(t2)
108
+ account.expects(:photos_agents_cached_at).returns(t2)
109
+ account.expects(:photos_property_addresses_cached_at).returns(t3)
110
+ assert_equal(
111
+ "accounts/#{account.id}/photos-00ea6afe3ff68037f8b4dcdb275e2a24-#{t3.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
112
+ association_cache_key(account, :photos, {property: {addresses: {}}, agents: {}})
113
+ )
114
+
115
+ # Belongs to
116
+ photo = create(:photo, account: account)
117
+ photo.expects(:account_cached_at).returns(t1)
118
+ assert_equal(
119
+ "accounts/#{account.id}-#{t1.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
120
+ association_cache_key(photo, :account, {})
121
+ )
122
+
123
+ photo = create(:photo, account: account)
124
+ photo.expects(:account_cached_at).returns(t1)
125
+ photo.expects(:account_photos_cached_at).returns(t2)
126
+ assert_equal(
127
+ "accounts/#{account.id}/07437ce3863467f4cd715ae1ef930f08-#{t2.utc.to_s(ActiveRecord::Base.cache_timestamp_format)}",
128
+ association_cache_key(photo, :account, {photos: {}})
129
+ )
130
+ end
131
+
132
+ test '::json_column_type(sql_type)' do
133
+ assert_equal 'string', json_column_type('character varying')
134
+ assert_equal 'string', json_column_type('character varying(2)')
135
+ assert_equal 'string', json_column_type('character varying(255)')
136
+ assert_equal 'datetime', json_column_type('timestamp without time zone')
137
+ assert_equal 'datetime', json_column_type('time without time zone')
138
+ assert_equal 'string', json_column_type('text')
139
+ assert_equal 'hash', json_column_type('json')
140
+ assert_equal 'hash', json_column_type('jsonb')
141
+ assert_equal 'integer', json_column_type('bigint')
142
+ assert_equal 'integer', json_column_type('integer')
143
+ assert_equal 'string', json_column_type('inet')
144
+ assert_equal 'hash', json_column_type('hstore')
145
+ assert_equal 'datetime', json_column_type('date')
146
+ assert_equal 'decimal', json_column_type('numeric')
147
+ assert_equal 'decimal', json_column_type('numeric(12)')
148
+ assert_equal 'decimal', json_column_type('numeric(12,2)')
149
+ assert_equal 'decimal', json_column_type('double precision')
150
+ assert_equal 'string', json_column_type('ltree')
151
+ assert_equal 'boolean', json_column_type('boolean')
152
+ assert_equal 'ewkb', json_column_type('geometry')
153
+ assert_equal 'string', json_column_type('uuid')
154
+ end
155
+
156
+ end
@@ -0,0 +1,39 @@
1
+ require 'standard_api/test_helper'
2
+ require "benchmark/ips"
3
+
4
+ TIME = (ENV["BENCHMARK_TIME"] || 20).to_i
5
+ RECORDS = (ENV["BENCHMARK_RECORDS"] || TIME * 1000).to_i
6
+
7
+ app = ::ActionDispatch::IntegrationTest.new(:app)
8
+ properties = Array.new(1000) do
9
+ photos = Array.new(10) { FactoryBot.create(:photo) }
10
+ FactoryBot.create(:property, photos: photos)
11
+ end
12
+
13
+ Benchmark.ips(TIME) do |x|
14
+
15
+ # ActionView::Template.unregister_template_handler :streamer
16
+ # ActionView::Template.register_template_handler :jbuilder, JbuilderHandler
17
+
18
+ x.report("#index.json.jbuilder") do
19
+ app.get('/properties.json', params: { limit: 100 })
20
+ end
21
+
22
+ x.report("#show.json.jbuilder") do
23
+ app.get("/properties/#{properties.sample.id}.json")
24
+ end
25
+
26
+ x.report("#show.json.jbuilder w/ include=photos") do
27
+ app.get("/properties/#{properties.sample.id}.json", params: { include: :photos })
28
+ end
29
+
30
+ # ActionView::Template.unregister_template_handler :jbuilder
31
+ # ActionView::Template.register_template_handler :streamer, TurboStreamer::Handler
32
+
33
+ # x.report("#index.json.streamer") do
34
+ # app.get('/properties.json', params: { limit: 100 })
35
+ # end
36
+
37
+ # x.compare!
38
+
39
+ end
@@ -0,0 +1,33 @@
1
+ require 'standard_api/test_helper'
2
+
3
+ class RouteHelpersTest < ActionDispatch::IntegrationTest
4
+
5
+ test 'standard_resources' do
6
+ assert_routing({ path: '/properties', method: :get }, { controller: 'properties', action: 'index' })
7
+ assert_routing({ path: '/properties/1', method: :get }, { controller: 'properties', action: 'show', id: '1' })
8
+ assert_routing({ path: '/properties/new', method: :get }, { controller: 'properties', action: 'new' })
9
+ assert_routing({ path: '/properties', method: :post }, { controller: 'properties', action: 'create' })
10
+ assert_routing({ path: '/properties/1', method: :put }, { controller: 'properties', action: 'update', id: '1' })
11
+ assert_routing({ path: '/properties/1', method: :patch }, { controller: 'properties', action: 'update', id: '1' })
12
+ assert_routing({ path: '/properties/1', method: :delete }, { controller: 'properties', action: 'destroy', id: '1' })
13
+ assert_routing({ path: '/properties/schema', method: :get }, { controller: 'properties', action: 'schema' })
14
+ assert_routing({ path: '/properties/calculate', method: :get }, { controller: 'properties', action: 'calculate' })
15
+ end
16
+
17
+ test 'standard_resource' do
18
+ assert_routing({ path: '/account', method: :get }, { controller: 'accounts', action: 'show' })
19
+ assert_routing({ path: '/account/new', method: :get }, { controller: 'accounts', action: 'new' })
20
+ assert_routing({ path: '/account', method: :post }, { controller: 'accounts', action: 'create' })
21
+ assert_routing({ path: '/account', method: :put }, { controller: 'accounts', action: 'update' })
22
+ assert_routing({ path: '/account', method: :patch }, { controller: 'accounts', action: 'update' })
23
+ assert_routing({ path: '/account', method: :delete }, { controller: 'accounts', action: 'destroy' })
24
+ assert_routing({ path: '/account/schema', method: :get }, { controller: 'accounts', action: 'schema' })
25
+ assert_routing({ path: '/account/calculate', method: :get }, { controller: 'accounts', action: 'calculate' })
26
+ end
27
+
28
+ test 'standard_resources subresource routes' do
29
+ assert_routing({ path: '/photos/1/properties/1', method: :post }, { controller: 'photos', action: 'add_resource', id: '1', relationship: 'properties', resource_id: '1' })
30
+ assert_routing({ path: '/photos/1/properties/1', method: :delete }, { controller: 'photos', action: 'remove_resource', id: '1', relationship: 'properties', resource_id: '1' })
31
+ end
32
+
33
+ end
@@ -0,0 +1,591 @@
1
+ require 'standard_api/test_helper'
2
+
3
+ class PropertiesControllerTest < ActionDispatch::IntegrationTest
4
+ include StandardAPI::TestCase
5
+ include StandardAPI::Helpers
6
+
7
+ self.includes = [ :photos, :landlord, :english_name ]
8
+
9
+ # def normalizers
10
+ # {
11
+ # Property => {
12
+ # "size" => lambda { |value| value.to_i }
13
+ # }
14
+ # }
15
+ # end
16
+
17
+ # = Routing Tests
18
+ #
19
+ # These also can't be included in StandardAPI::TestCase because we don't know
20
+ # how the other's routes are setup
21
+
22
+ # test 'route to #metadata' do
23
+ # assert_routing '/metadata', path_with_action('metadata')
24
+ # assert_recognizes path_with_action('metadata'), "/metadata"
25
+ # end
26
+
27
+ test 'route to #tables.json' do
28
+ assert_recognizes({"controller"=>"application", "action"=>"tables"}, { method: :get, path: "/tables" })
29
+ end
30
+
31
+ test 'route to #create.json' do
32
+ assert_routing({ method: :post, path: "/#{plural_name}" }, path_with_action('create'))
33
+ assert_recognizes(path_with_action('create'), { method: :post, path: "/#{plural_name}" })
34
+ end
35
+
36
+ test 'route to #calculate.json' do
37
+ assert_routing "/#{plural_name}/calculate", path_with_action('calculate')
38
+ assert_recognizes(path_with_action('calculate'), "/#{plural_name}/calculate")
39
+ end
40
+
41
+ test 'route to #destroy.json' do
42
+ assert_routing({ method: :delete, path: "/#{plural_name}/1" }, path_with_action('destroy', id: '1'))
43
+ assert_recognizes(path_with_action('destroy', id: '1'), { method: :delete, path: "/#{plural_name}/1" })
44
+ end
45
+
46
+ test 'route to #index.json' do
47
+ assert_routing "/#{plural_name}", path_with_action('index')
48
+ assert_recognizes path_with_action('index'), "/#{plural_name}"
49
+ end
50
+
51
+ test 'route to #show.json' do
52
+ assert_routing "/#{plural_name}/1", path_with_action('show', id: '1')
53
+ assert_recognizes(path_with_action('show', id: '1'), "/#{plural_name}/1")
54
+ end
55
+
56
+ test 'route to #update.json' do
57
+ assert_routing({ method: :put, path: "#{plural_name}/1" }, path_with_action('update', id: '1'))
58
+ assert_recognizes(path_with_action('update', id: '1'), { method: :put, path: "/#{plural_name}/1" })
59
+ assert_routing({ method: :patch, path: "/#{plural_name}/1" }, path_with_action('update', id: '1'))
60
+ assert_recognizes(path_with_action('update', id: '1'), { method: :patch, path: "/#{plural_name}/1" })
61
+ end
62
+
63
+ test 'route to #schema.json' do
64
+ assert_routing({ method: :get, path: "/#{plural_name}/schema" }, path_with_action('schema'))
65
+ assert_recognizes(path_with_action('schema'), { method: :get, path: "/#{plural_name}/schema" })
66
+ end
67
+
68
+ # = Controller Tests
69
+
70
+ test 'StandardAPI-Version' do
71
+ get schema_references_path(format: 'json')
72
+ assert_equal StandardAPI::VERSION, response.headers['StandardAPI-Version']
73
+ end
74
+
75
+ test 'Controller#new' do
76
+ @controller = ReferencesController.new
77
+ assert_equal @controller.send(:model), Reference
78
+
79
+ @controller = SessionsController.new
80
+ assert_nil @controller.send(:model)
81
+ get new_session_path
82
+ assert_response :ok
83
+ end
84
+
85
+ test 'Controller#model_orders defaults to []' do
86
+ @controller = ReferencesController.new
87
+ assert_equal @controller.send(:model_orders), []
88
+ end
89
+
90
+ test 'Controller#model_includes defaults to []' do
91
+ @controller = DocumentsController.new
92
+ assert_equal @controller.send(:model_includes), []
93
+ end
94
+
95
+ test 'Controller#model_params defaults to []' do
96
+ @controller = ReferencesController.new
97
+ assert_equal @controller.send(:model_params), []
98
+ end
99
+
100
+ test 'Controller#current_mask' do
101
+ @controller = ReferencesController.new
102
+ @controller.instance_variable_set('@current_mask', { 'references' => { 'subject_id' => 1 }})
103
+ @controller.params = {}
104
+ assert_equal 'SELECT "references".* FROM "references" WHERE "references"."subject_id" = 1', @controller.send(:resources).to_sql
105
+ end
106
+
107
+ test 'ApplicationController#schema.json' do
108
+ get schema_path(format: 'json')
109
+
110
+ schema = JSON(response.body)
111
+ controllers = ApplicationController.descendants
112
+ controllers.select! { |c| c.ancestors.include?(StandardAPI::Controller) && c != StandardAPI::Controller }
113
+
114
+ @controller.send(:models).reject { |x| x.name == 'Photo' }.each do |model|
115
+ assert_equal true, schema['models'].has_key?(model.name)
116
+
117
+ model_comment = model.connection.table_comment(model.table_name)
118
+ if model_comment.nil? then
119
+ assert_nil schema.dig('models', model.name, 'comment')
120
+ else
121
+ assert_equal model_comment, schema.dig('models', model.name, 'comment')
122
+ end
123
+
124
+ model.columns.each do |column|
125
+ assert_equal json_column_type(column.sql_type), schema.dig('models', model.name, 'attributes', column.name, 'type')
126
+ default = column.default || column.default_function
127
+ if default then
128
+ assert_equal default, schema.dig('models', model.name, 'attributes', column.name, 'default')
129
+ else
130
+ assert_nil schema.dig('models', model.name, 'attributes', column.name, 'default')
131
+ end
132
+ assert_equal column.name == model.primary_key, schema.dig('models', model.name, 'attributes', column.name, 'primary_key')
133
+ assert_equal column.null, schema.dig('models', model.name, 'attributes', column.name, 'null')
134
+ assert_equal column.array, schema.dig('models', model.name, 'attributes', column.name, 'array')
135
+ if column.comment then
136
+ assert_equal column.comment, schema.dig('models', model.name, 'attributes', column.name, 'comment')
137
+ else
138
+ assert_nil schema.dig('models', model.name, 'attributes', column.name, 'comment')
139
+ end
140
+ end
141
+ end
142
+
143
+ assert_equal 'test comment', schema['comment']
144
+ end
145
+
146
+ test 'Controller#schema.json' do
147
+ get schema_references_path(format: 'json')
148
+
149
+ schema = JSON(response.body)
150
+ assert_equal true, schema.has_key?('attributes')
151
+ assert_equal true, schema['attributes']['id']['primary_key']
152
+ assert_equal 1000, schema['limit']
153
+ end
154
+
155
+ test 'Controller#schema.json w/o limit' do
156
+ get schema_unlimited_index_path(format: 'json')
157
+
158
+ schema = JSON(response.body)
159
+ assert_equal true, schema.has_key?('attributes')
160
+ assert_equal true, schema['attributes']['id']['primary_key']
161
+ assert_nil schema['limit']
162
+ end
163
+
164
+ test 'Controller#index w/o limit' do
165
+ account = create(:account)
166
+ get unlimited_index_path(format: 'json')
167
+
168
+ assert_equal [account.id], JSON(response.body).map { |x| x['id'] }
169
+ end
170
+
171
+ test 'Controller#index with default limit' do
172
+ create(:account)
173
+ get default_limit_index_path(format: 'json')
174
+ assert_response :ok
175
+ end
176
+
177
+ test 'Controller#create redirects to correct route with STI models' do
178
+ attrs = attributes_for(:pdf)
179
+ post documents_path, params: { document: attrs }
180
+ assert_response :redirect
181
+ end
182
+
183
+ test 'Controller#update redirects to correct route with STI models' do
184
+ pdf = create(:pdf)
185
+ patch document_path(pdf), params: { document: pdf.attributes }
186
+ assert_redirected_to document_path(pdf)
187
+ end
188
+
189
+ test 'Controller#add_resource' do
190
+ property = create(:property, photos: [])
191
+ photo = create(:photo)
192
+
193
+ post "/properties/#{property.id}/photos/#{photo.id}.json"
194
+ assert_equal property.photos.reload.map(&:id), [photo.id]
195
+ assert_response :created
196
+
197
+ post "/properties/#{property.id}/photos/9999999"
198
+ assert_response :not_found
199
+ end
200
+
201
+ test 'Controller#remove_resource' do
202
+ photo = create(:photo)
203
+ property = create(:property, photos: [photo])
204
+ delete "/properties/#{property.id}/photos/#{photo.id}"
205
+ assert_equal property.photos.reload, []
206
+ assert_response :no_content
207
+
208
+ delete "/properties/#{property.id}/photos/9999999"
209
+ assert_response :not_found
210
+ end
211
+
212
+ # = View Tests
213
+
214
+ test 'rendering tables' do
215
+ get tables_path(format: 'json')
216
+ assert_response :ok
217
+ # assert_equal ['properties', 'accounts', 'photos', 'references', 'sessions', 'unlimited'], response.parsed_body
218
+ # Multiple 'accounts' because multiple controllers with that model for testing.
219
+ assert_equal ["properties", "accounts", "documents", "photos", "references", "accounts", 'accounts'].sort, response.parsed_body.sort
220
+ end
221
+
222
+ test 'rendering null attribute' do
223
+ property = create(:property)
224
+ get property_path(property, format: 'json'), params: { id: property.id, include: [:landlord] }
225
+ assert_equal true, JSON(response.body).has_key?('landlord')
226
+ assert_nil JSON(response.body)['landlord']
227
+ end
228
+
229
+ test 'rendering null attribute for has_one through' do
230
+ property = create(:property)
231
+ get property_path(property, format: 'json'), params: { id: property.id, include: [:document] }
232
+ assert JSON(response.body).has_key?('document')
233
+ assert_nil JSON(response.body)['document']
234
+ end
235
+
236
+ test '#index.json uses overridden partial' do
237
+ create(:property, photos: [create(:photo)])
238
+ get properties_path(format: 'json'), params: { limit: 100, include: [{:photos => { order: :id }}] }
239
+
240
+ photo = JSON(response.body)[0]['photos'][0]
241
+ assert_equal true, photo.has_key?('template')
242
+ assert_equal 'photos/_photo', photo['template']
243
+ end
244
+
245
+ test '#show.json uses overridden partial' do
246
+ property = create(:property, photos: [create(:photo)])
247
+ get property_path(property, format: 'json'), params: { id: property.id, include: [:photos] }
248
+
249
+ photo = JSON(response.body)['photos'][0]
250
+ assert_equal true, photo.has_key?('template')
251
+ assert_equal 'photos/_photo', photo['template']
252
+ end
253
+
254
+ test '#schema.json uses overridden partial' do
255
+ get schema_photos_path(format: 'json')
256
+
257
+ schema = JSON(response.body)
258
+ assert_rendered 'photos/schema', format: 'json', handler: 'jbuilder'
259
+ assert_equal true, schema.has_key?('template')
260
+ assert_equal 'photos/schema', schema['template']
261
+ end
262
+
263
+ test 'application#schema.json renders overridden #schema.json partials' do
264
+ get schema_path(format: 'json')
265
+
266
+ schema = JSON(response.body)
267
+ assert_rendered 'application/schema', format: 'json', handler: 'jbuilder'
268
+ assert_equal 'photos/schema', schema.dig('models', 'Photo', 'template')
269
+ end
270
+
271
+ test 'belongs_to polymorphic association' do
272
+ photo = create(:photo)
273
+ reference = create(:reference, subject: photo)
274
+ get reference_path(reference, include: :subject, format: 'json')
275
+
276
+ json = JSON(response.body)
277
+ assert_equal 'photos/_photo', json['subject']['template']
278
+ end
279
+
280
+ test '#index.json includes polymorphic association' do
281
+ property1 = create(:property)
282
+ property2 = create(:property)
283
+ photo = create(:photo)
284
+ create(:reference, subject: property1)
285
+ create(:reference, subject: property2)
286
+ create(:reference, subject: photo)
287
+
288
+ get references_path(format: 'json'), params: { include: [:subject], limit: 10 }
289
+
290
+ json = JSON(response.body)
291
+ assert_equal 'photos/_photo', json.find { |x| x['subject_type'] == "Photo"}['subject']['template']
292
+ end
293
+
294
+ test 'has_many association' do
295
+ p = create(:property, photos: [create(:photo)])
296
+ get properties_path(format: 'json'), params: { limit: 100, include: [:photos] }
297
+ assert_equal p.photos.first.id, JSON(response.body)[0]['photos'][0]['id']
298
+ end
299
+
300
+ test 'belongs_to association' do
301
+ account = create(:account)
302
+ photo = create(:photo, account: account)
303
+ get photo_path(photo, include: 'account', format: 'json')
304
+ assert_equal account.id, JSON(response.body)['account']['id']
305
+ end
306
+
307
+ test 'has_one association' do
308
+ account = create(:account)
309
+ property = create(:property, landlord: account)
310
+ get property_path(property, include: 'landlord', format: 'json')
311
+ assert_equal account.id, JSON(response.body)['landlord']['id']
312
+ end
313
+
314
+ test 'include method' do
315
+ property = create(:property)
316
+ get property_path(property, include: 'english_name', format: 'json')
317
+ assert_equal 'A Name', JSON(response.body)['english_name']
318
+ end
319
+
320
+ test 'include with where key' do
321
+ photo_a = create(:photo)
322
+ photo_b = create(:photo)
323
+
324
+ property = create(:property, photos: [photo_b])
325
+ get property_path(property, include: { photos: { where: { id: photo_a.id } } }, format: :json)
326
+ assert_equal [], JSON(response.body)['photos']
327
+
328
+ property.photos << photo_a
329
+ get property_path(property, include: { photos: { where: { id: photo_a.id } } }, format: :json)
330
+ assert_equal [photo_a.id], JSON(response.body)['photos'].map { |x| x['id'] }
331
+ end
332
+
333
+ test 'include with order key' do
334
+ photos = Array.new(5) { create(:photo) }
335
+ property = create(:property, photos: photos)
336
+
337
+ get property_path(property, include: { photos: { order: { id: :asc } } }, format: 'json')
338
+ assert_equal photos.map(&:id).sort, JSON(response.body)['photos'].map { |x| x['id'] }
339
+ end
340
+
341
+ test 'include with limit key' do
342
+ 5.times { create(:property, photos: Array.new(5) { create(:photo) }) }
343
+ get properties_path(include: { photos: { limit: 1 } }, limit: 5, format: 'json')
344
+
345
+ properties = JSON(response.body)
346
+ assert_equal 5, properties.length
347
+
348
+ properties.each do |property|
349
+ assert_equal 1, property['photos'].length
350
+ end
351
+ end
352
+
353
+ test 'include with when key' do
354
+ photo = create(:photo)
355
+ account = create(:account, photos: [ photo ])
356
+ account_reference = create(:reference, subject: account)
357
+
358
+ property = create(:property, landlord: account)
359
+ property_reference = create(:reference, subject: property)
360
+
361
+
362
+ get references_path(
363
+ include: {
364
+ "subject" => {
365
+ "landlord" => {
366
+ "when" => {
367
+ "subject_type" => 'Property'
368
+ }
369
+ },
370
+ "photos" => {
371
+ "when" => {
372
+ "subject_type" => 'Account'
373
+ }
374
+ }
375
+ }
376
+ },
377
+ limit: 20,
378
+ format: 'json'
379
+ )
380
+
381
+ json = JSON(response.body)
382
+
383
+ assert_equal json.find { |x| x['id'] == account_reference.id }.dig('subject', 'photos', 0, 'id'), photo.id
384
+ assert_equal json.find { |x| x['id'] == property_reference.id }.dig('subject', 'landlord', 'id'), account.id
385
+ end
386
+
387
+ test 'include with distinct key' do
388
+ account = create(:account)
389
+ photos = Array.new(5) { create(:photo, account: account) }
390
+ property = create(:property, photos: photos)
391
+
392
+ get property_path(property, include: { photos: { distinct: true } }, format: 'json')
393
+ assert_equal 5, JSON(response.body)['photos'].size
394
+ end
395
+
396
+ test 'include with distinct_on key' do
397
+ account = create(:account)
398
+ photos = Array.new(5) { create(:photo, account: account) }
399
+ property = create(:property, photos: photos)
400
+
401
+ get property_path(property,
402
+ include: {
403
+ photos: {
404
+ distinct_on: :account_id,
405
+ # order: [:account_id, { id: :asc }]
406
+ order: { account_id: :asc, id: :asc }
407
+ }
408
+ },
409
+ format: 'json')
410
+
411
+ assert_equal [photos.first.id], JSON(response.body)['photos'].map { |x| x['id'] }
412
+
413
+ get property_path(property,
414
+ include: {
415
+ photos: {
416
+ distinct_on: :account_id,
417
+ order: { account_id: :asc, id: :desc }
418
+ }
419
+ }, format: 'json')
420
+
421
+ assert_equal [photos.last.id], JSON(response.body)['photos'].map { |x| x['id'] }
422
+ end
423
+
424
+ test 'unknown inlcude' do
425
+ property = create(:property, accounts: [ create(:account) ])
426
+ get property_path(property, include: [:accounts], format: 'json')
427
+ assert_response :bad_request
428
+ assert_equal 'found unpermitted parameter: "accounts"', response.body
429
+ end
430
+
431
+ test 'unknown order' do
432
+ create(:property)
433
+ get properties_path(order: 'updated_at', limit: 1, format: 'json')
434
+ assert_response :bad_request
435
+ assert_equal 'found unpermitted parameter: "updated_at"', response.body
436
+ end
437
+
438
+ # Includes Test
439
+
440
+ test 'Includes::normailze' do
441
+ method = StandardAPI::Includes.method(:normalize)
442
+ assert_equal method.call(:x), { 'x' => {} }
443
+ assert_equal method.call([:x, :y]), { 'x' => {}, 'y' => {} }
444
+ assert_equal method.call([ { x: true }, { y: true } ]), { 'x' => {}, 'y' => {} }
445
+ assert_equal method.call({ x: true, y: true }), { 'x' => {}, 'y' => {} }
446
+ assert_equal method.call({ x: { y: true } }), { 'x' => { 'y' => {} } }
447
+ assert_equal method.call({ x: { y: {} } }), { 'x' => { 'y' => {} } }
448
+ assert_equal method.call({ x: [:y] }), { 'x' => { 'y' => {} } }
449
+
450
+
451
+ assert_equal method.call({ x: { where: { y: false } } }), { 'x' => { 'where' => { 'y' => false } } }
452
+ assert_equal method.call({ x: { order: { y: :asc } } }), { 'x' => { 'order' => { 'y' => :asc } } }
453
+ end
454
+
455
+ # sanitize({:key => {}}, [:key]) # => {:key => {}}
456
+ # sanitize({:key => {}}, {:key => true}) # => {:key => {}}
457
+ # sanitize({:key => {}}, :value => {}}, [:key]) => # Raises ParseError
458
+ # sanitize({:key => {}}, :value => {}}, {:key => true}) => # Raises ParseError
459
+ # sanitize({:key => {:value => {}}}, {:key => [:value]}) # => {:key => {:value => {}}}
460
+ # sanitize({:key => {:value => {}}}, {:key => {:value => true}}) # => {:key => {:value => {}}}
461
+ # sanitize({:key => {:value => {}}}, [:key]) => # Raises ParseError
462
+ test 'Includes::sanitize' do
463
+ method = StandardAPI::Includes.method(:sanitize)
464
+ assert_equal method.call(:x, [:x]), { 'x' => {} }
465
+ assert_equal method.call(:x, {:x => true}), { 'x' => {} }
466
+
467
+ assert_raises(StandardAPI::UnpermittedParameters) do
468
+ method.call([:x, :y], [:x])
469
+ end
470
+
471
+ assert_raises(StandardAPI::UnpermittedParameters) do
472
+ method.call([:x, :y], {:x => true})
473
+ end
474
+
475
+ assert_raises(StandardAPI::UnpermittedParameters) do
476
+ method.call({:x => true, :y => true}, [:x])
477
+ end
478
+ assert_raises(StandardAPI::UnpermittedParameters) do
479
+ method.call({:x => true, :y => true}, {:x => true})
480
+ end
481
+ assert_raises(StandardAPI::UnpermittedParameters) do
482
+ method.call({ x: { y: true }}, { x: true })
483
+ end
484
+
485
+ assert_equal method.call({ x: { y: true }}, { x: { y: true } }), { 'x' => { 'y' => {} } }
486
+ end
487
+
488
+ # Order Test
489
+
490
+ test 'Orders::sanitize(:column, [:column])' do
491
+ method = StandardAPI::Orders.method(:sanitize)
492
+
493
+ assert_equal :x, method.call(:x, :x)
494
+ assert_equal :x, method.call(:x, [:x])
495
+ assert_equal :x, method.call([:x], [:x])
496
+ assert_raises(StandardAPI::UnpermittedParameters) do
497
+ method.call(:x, :y)
498
+ end
499
+
500
+ assert_equal({ x: :asc }, method.call({ x: :asc }, :x))
501
+ assert_equal({ x: :desc }, method.call({ x: :desc }, :x))
502
+ assert_equal([{ x: :desc}, {y: :desc }], method.call({ x: :desc, y: :desc }, [:x, :y]))
503
+ assert_equal({ x: :asc }, method.call([{ x: :asc }], :x))
504
+ assert_equal({ x: :desc }, method.call([{ x: :desc }], :x))
505
+ assert_equal({ x: { asc: :nulls_last } }, method.call([{ x: { asc: :nulls_last } }], :x))
506
+ assert_equal({ x: { asc: :nulls_first } }, method.call([{ x: { asc: :nulls_first } }], :x))
507
+ assert_equal({ x: { desc: :nulls_last } }, method.call([{ x: { desc: :nulls_last } }], :x))
508
+ assert_equal({ x: { desc: :nulls_first }}, method.call([{ x: { desc: :nulls_first } }], :x))
509
+ assert_equal({ relation: :id }, method.call(['relation.id'], { relation: :id }))
510
+ assert_equal({ relation: :id }, method.call([{ relation: :id }], { relation: :id }))
511
+ assert_equal({ relation: :id }, method.call([{ relation: :id }], [{ relation: :id }]))
512
+ assert_equal({ relation: :id }, method.call([{ relation: [:id] }], { relation: [:id] }))
513
+ assert_equal({ relation: :id }, method.call([{ relation: [:id] }], [{ relation: [:id] }]))
514
+ assert_equal({ relation: { id: :desc } }, method.call([{'relation.id' => :desc}], { relation: :id }))
515
+ assert_equal({ relation: { id: :desc } }, method.call([{ relation: { id: :desc } }], { relation: [:id] }))
516
+ assert_equal({ relation: { id: :desc } }, method.call([{ relation: { id: :desc } }], [{ relation: [:id] }]))
517
+ assert_equal({ relation: { id: :desc } }, method.call([{ relation: [{ id: :desc }] }], [{ relation: [:id] }]))
518
+ assert_equal({ relation: { id: :desc } }, method.call([{ relation: [{ id: :desc }] }], [{ relation: [:id] }]))
519
+ assert_equal({ relation: {:id => {:asc => :nulls_last}} }, method.call([{ relation: {:id => {:asc => :nulls_last}} }], [{ relation: [:id] }]))
520
+ assert_equal({ relation: {:id => {:asc => :nulls_last}} }, method.call([{ relation: {:id => {:asc => :nulls_last}} }], [{ relation: [:id] }]))
521
+ assert_equal({ relation: {:id => [{:asc => :nulls_last}]} }, method.call([{ relation: {:id => [{:asc => :nulls_last}]} }], [{ relation: [:id] }]))
522
+ assert_equal({ relation: {:id => [{:asc => :nulls_last}]} }, method.call([{ relation: {:id => [{:asc => :nulls_last}]} }], [{ relation: [:id] }]))
523
+ end
524
+
525
+ test 'order: :attribute' do
526
+ properties = Array.new(2) { create(:property) }
527
+
528
+ get properties_path(order: :id, limit: 100, format: 'json')
529
+ assert_equal properties.map(&:id).sort, JSON(response.body).map { |x| x['id'] }
530
+ end
531
+
532
+ test 'order: { attribute: :direction }' do
533
+ properties = Array.new(2) { create(:property) }
534
+
535
+ get properties_path(order: { id: :asc }, limit: 100, format: 'json')
536
+ assert_equal properties.map(&:id).sort, JSON(response.body).map { |x| x['id'] }
537
+
538
+ get properties_path(order: { id: :desc }, limit: 100, format: 'json')
539
+ assert_equal properties.map(&:id).sort.reverse, JSON(response.body).map { |x| x['id'] }
540
+ end
541
+
542
+ test 'order: { attribute: { direction: :nulls } }' do
543
+ properties = [ create(:property), create(:property, description: nil) ]
544
+
545
+ get properties_path(order: { description: { asc: :nulls_last } }, limit: 100, format: 'json')
546
+ assert_equal properties.map(&:id).sort, JSON(response.body).map { |x| x['id'] }
547
+
548
+ get properties_path(order: { description: { asc: :nulls_first } }, limit: 100, format: 'json')
549
+ assert_equal properties.map(&:id).sort.reverse, JSON(response.body).map { |x| x['id'] }
550
+ end
551
+
552
+ # Calculate Test
553
+ test 'calculate' do
554
+ create(:photo)
555
+ get '/photos/calculate', params: {select: {count: "*"}}
556
+ assert_equal [1], JSON(response.body)
557
+ end
558
+
559
+ test 'calculate group_by' do
560
+ create(:photo, format: 'jpg')
561
+ create(:photo, format: 'jpg')
562
+ create(:photo, format: 'png')
563
+ get '/photos/calculate', params: {select: {count: "*"}, group_by: 'format'}
564
+ assert_equal ({'png' => 1, 'jpg' => 2}), JSON(response.body)
565
+ end
566
+
567
+ test 'calculate join' do
568
+ p1 = create(:property)
569
+ p2 = create(:property)
570
+ create(:account, photos_count: 1, property: p1)
571
+ create(:account, photos_count: 2, property: p2)
572
+
573
+ get '/properties/calculate', params: {select: {sum: "accounts.photos_count"}, join: 'accounts'}
574
+ assert_equal [3], JSON(response.body)
575
+ end
576
+
577
+ test 'calculate count distinct' do
578
+ photo = create(:photo)
579
+ landlord = create(:account)
580
+ create(:property, landlord: landlord, photos: [photo])
581
+ create(:property, landlord: landlord, photos: [photo])
582
+
583
+ get '/photos/calculate', params: {select: {count: "*"},
584
+ where: {properties: {landlord: {id: landlord.id}}},
585
+ distinct: true
586
+ }
587
+
588
+ assert_equal [1], JSON(response.body)
589
+ end
590
+
591
+ end
@@ -33,7 +33,7 @@ class ApplicationController < ActionController::Base
33
33
  end
34
34
 
35
35
  def property_includes
36
- [:photos, :landlord, :english_name]
36
+ [:photos, :landlord, :english_name, :document]
37
37
  end
38
38
 
39
39
  def reference_includes
@@ -21,6 +21,8 @@ class Property < ActiveRecord::Base
21
21
  has_and_belongs_to_many :photos
22
22
  has_many :accounts
23
23
  has_one :landlord, class_name: 'Account'
24
+ has_one :document_attachments, class_name: "Attachment", as: :record, inverse_of: :record
25
+ has_one :document, through: "document_attachments"
24
26
 
25
27
  validates :name, presence: true
26
28
  accepts_nested_attributes_for :photos
@@ -34,6 +36,15 @@ class Reference < ActiveRecord::Base
34
36
  belongs_to :subject, polymorphic: true
35
37
  end
36
38
 
39
+ class Document < ActiveRecord::Base
40
+ attr_accessor :file
41
+ end
42
+
43
+ class Attachment < ActiveRecord::Base
44
+ belongs_to :record, polymorphic: true
45
+ belongs_to :document
46
+ end
47
+
37
48
  # = Migration
38
49
 
39
50
  class CreateModelTables < ActiveRecord::Migration[6.0]
@@ -87,6 +98,12 @@ class CreateModelTables < ActiveRecord::Migration[6.0]
87
98
  create_table "documents", force: :cascade do |t|
88
99
  t.string 'type'
89
100
  end
101
+
102
+ create_table "attachments", force: :cascade do |t|
103
+ t.string 'record_type'
104
+ t.integer 'record_id'
105
+ t.integer 'document_id'
106
+ end
90
107
  end
91
108
 
92
109
  end
@@ -0,0 +1,226 @@
1
+ require File.expand_path('../test_app', __FILE__)
2
+
3
+ require "minitest/autorun"
4
+ require 'minitest/unit'
5
+ require 'factory_bot'
6
+ require 'faker'
7
+ require 'standard_api/test_case'
8
+ require 'byebug'
9
+ require 'mocha/minitest'
10
+
11
+ # Setup the test db
12
+ ActiveSupport.test_order = :random
13
+
14
+ include ActionDispatch::TestProcess
15
+
16
+ class ActiveSupport::TestCase
17
+ include ActiveRecord::TestFixtures
18
+ include FactoryBot::Syntax::Methods
19
+
20
+ def setup
21
+ @routes ||= TestApplication.routes
22
+ @subscribers, @layouts, @partials = [], {}, {}
23
+
24
+ Rails.cache.clear
25
+
26
+ @subscribers << ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload|
27
+ path = payload[:identifier]
28
+ virtual_path = payload[:virtual_path]
29
+ format, handler = *path.split("/").last.split('.').last(2)
30
+
31
+ partial = virtual_path =~ /^.*\/_[^\/]*$/
32
+
33
+ if partial
34
+ if @partials[virtual_path]
35
+ @partials[virtual_path][:count] += 1
36
+ else
37
+ @partials[virtual_path] = {
38
+ count: 1,
39
+ path: virtual_path,
40
+ format: format,
41
+ handler: handler
42
+ }
43
+ end
44
+ else
45
+ if @layouts[virtual_path]
46
+ @layouts[virtual_path][:count] += 1
47
+ else
48
+ @layouts[virtual_path] = {
49
+ count: 1,
50
+ path: virtual_path,
51
+ format: format,
52
+ handler: handler
53
+ }
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def teardown
60
+ @subscribers.each do |subscriber|
61
+ ActiveSupport::Notifications.unsubscribe(subscriber)
62
+ end
63
+ end
64
+
65
+ # = Helper Methods
66
+
67
+ def controller_path
68
+ if defined?(@controller)
69
+ @controller.controller_path
70
+ else
71
+ controller_class.new.controller_path
72
+ end
73
+ end
74
+
75
+ def path_with_action(action, options={})
76
+ { :controller => controller_path, :action => action }.merge(options)
77
+ end
78
+
79
+ def assert_rendered(options = {}, message = nil)
80
+ options = case options
81
+ when NilClass, Regexp, String, Symbol
82
+ { layout: options }
83
+ when Hash
84
+ options
85
+ else
86
+ raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil"
87
+ end
88
+
89
+ options.assert_valid_keys(:layout, :partial, :count, :format, :handler)
90
+
91
+ if expected_layout = options[:layout]
92
+ case expected_layout
93
+ when String, Symbol
94
+ msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
95
+ expected_layout, @layouts.keys)
96
+ assert_includes @layouts.keys, expected_layout.to_s, msg
97
+
98
+ key = expected_layout.to_s
99
+ value = @layouts[key]
100
+
101
+ if expected_count = options[:count]
102
+ actual_count = value[:count]
103
+ msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
104
+ expected_partial, expected_count, actual_count)
105
+ assert_equal expected_count, actual_count, msg
106
+ end
107
+
108
+ if expected_format = options[:format]
109
+ actual_format = value[:format]
110
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
111
+ expected_partial, expected_format, actual_format)
112
+ assert_equal expected_format, actual_format, msg
113
+ end
114
+
115
+ if expected_handler = options[:handler]
116
+ actual_handler = value[:handler]
117
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
118
+ expected_partial, expected_handler, actual_handler)
119
+ assert_equal expected_handler, actual_handler, msg
120
+ end
121
+ when Regexp
122
+ msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
123
+ expected_layout, @layouts.keys)
124
+ assert(@layouts.keys.any? {|l| l =~ expected_layout }, msg)
125
+
126
+ key = @layouts.keys.find {|l| l =~ expected_layout }
127
+ value = @layouts[key]
128
+
129
+ if expected_count = options[:count]
130
+ actual_count = value[:count]
131
+ msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
132
+ expected_partial, expected_count, actual_count)
133
+ assert_equal expected_count, actual_count, msg
134
+ end
135
+
136
+ if expected_format = options[:format]
137
+ actual_format = value[:format]
138
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
139
+ expected_partial, expected_format, actual_format)
140
+ assert_equal expected_format, actual_format, msg
141
+ end
142
+
143
+ if expected_handler = options[:handler]
144
+ actual_handler = value[:handler]
145
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
146
+ expected_partial, expected_handler, actual_handler)
147
+ assert_equal expected_handler, actual_handler, msg
148
+ end
149
+ when nil, false
150
+ assert(@layouts.empty?, msg)
151
+ end
152
+ elsif expected_partial = options[:partial]
153
+ case expected_partial
154
+ when String, Symbol
155
+ msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
156
+ expected_partial, @partials.keys)
157
+ assert_includes @partials.keys, expected_partial.to_s, msg
158
+
159
+ key = expected_partial.to_s
160
+ value = @partials[key]
161
+
162
+ if expected_count = options[:count]
163
+ actual_count = value[:count]
164
+ msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
165
+ expected_partial, expected_count, actual_count)
166
+ assert_equal expected_count, actual_count, msg
167
+ end
168
+
169
+ if expected_format = options[:format]
170
+ actual_format = value[:format]
171
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
172
+ expected_partial, expected_format, actual_format)
173
+ assert_equal expected_format, actual_format, msg
174
+ end
175
+
176
+ if expected_handler = options[:handler]
177
+ actual_handler = value[:handler]
178
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
179
+ expected_partial, expected_handler, actual_handler)
180
+ assert_equal expected_handler, actual_handler, msg
181
+ end
182
+ when Regexp
183
+ msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
184
+ expected_partial, @partials.keys)
185
+ assert(@partials.keys.any? {|l| l =~ expected_partial }, msg)
186
+
187
+ key = @partials.keys.find {|l| l =~ expected_partial }
188
+ value = @partials[key]
189
+
190
+ if expected_count = options[:count]
191
+ actual_count = value[:count]
192
+ msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
193
+ expected_partial, expected_count, actual_count)
194
+ assert_equal expected_count, actual_count, msg
195
+ end
196
+
197
+ if expected_format = options[:format]
198
+ actual_format = value[:format]
199
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
200
+ expected_partial, expected_format, actual_format)
201
+ assert_equal expected_format, actual_format, msg
202
+ end
203
+
204
+ if expected_handler = options[:handler]
205
+ actual_handler = value[:handler]
206
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
207
+ expected_partial, expected_handler, actual_handler)
208
+ assert_equal expected_handler, actual_handler, msg
209
+ end
210
+ when nil, false
211
+ assert(@partials.empty?, msg)
212
+ end
213
+ end
214
+ end
215
+
216
+ end
217
+
218
+ class ActionController::TestCase
219
+
220
+ def assigns(key = nil)
221
+ assigns = {}.with_indifferent_access
222
+ @controller.view_assigns.each { |k, v| assigns.regular_writer(k, v) }
223
+ key.nil? ? assigns : assigns[key]
224
+ end
225
+
226
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: standardapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0.30
4
+ version: 6.0.0.32
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Bracy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-10 00:00:00.000000000 Z
11
+ date: 2020-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -276,6 +276,11 @@ files:
276
276
  - lib/standard_api/views/application/schema.streamer
277
277
  - lib/standard_api/views/application/show.json.jbuilder
278
278
  - lib/standard_api/views/application/show.streamer
279
+ - test/standard_api/caching_test.rb
280
+ - test/standard_api/helpers_test.rb
281
+ - test/standard_api/performance.rb
282
+ - test/standard_api/route_helpers_test.rb
283
+ - test/standard_api/standard_api_test.rb
279
284
  - test/standard_api/test_app.rb
280
285
  - test/standard_api/test_app/config/database.yml
281
286
  - test/standard_api/test_app/controllers.rb
@@ -290,6 +295,7 @@ files:
290
295
  - test/standard_api/test_app/views/photos/schema.streamer
291
296
  - test/standard_api/test_app/views/properties/edit.html.erb
292
297
  - test/standard_api/test_app/views/sessions/new.html.erb
298
+ - test/standard_api/test_helper.rb
293
299
  homepage: https://github.com/waratuman/standardapi
294
300
  licenses:
295
301
  - MIT
@@ -312,7 +318,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
312
318
  - !ruby/object:Gem::Version
313
319
  version: '0'
314
320
  requirements: []
315
- rubygems_version: 3.0.3
321
+ rubygems_version: 3.1.2
316
322
  signing_key:
317
323
  specification_version: 4
318
324
  summary: StandardAPI makes it easy to expose a query interface for your Rails models