standardapi 7.1.0 → 7.1.2
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/lib/standard_api/helpers.rb +11 -17
- data/lib/standard_api/version.rb +1 -1
- data/lib/standard_api/views/application/_record.json.jbuilder +13 -3
- data/lib/standard_api/views/application/_schema.json.jbuilder +11 -1
- data/lib/standard_api/views/application/_schema.streamer +10 -0
- data/lib/standard_api/visitors/validations.rb +60 -0
- data/lib/standard_api.rb +1 -0
- data/test/standard_api/standard_api_test.rb +59 -6
- data/test/standard_api/test_app/app/controllers/acl/account_acl.rb +5 -1
- data/test/standard_api/test_app/app/controllers/acl/property_acl.rb +13 -2
- data/test/standard_api/test_app/models.rb +17 -4
- data/test/standard_api/test_app.rb +1 -0
- data/test/standard_api/test_helper.rb +91 -7
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb9fe722c9847c5c2aa696b36fdbebbc4783f78d92036683b03cde0929f7b78a
|
4
|
+
data.tar.gz: 6723597440138d500614b84f34106279778addc18bd40419d854b0e7bbb628cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8aa742b8fda2329a7c17b314ac2b40c8ae46b139fa482516166e7c19a96e851c8438062a89b9de114a5425eaa429c69327faf1fb01b5b4e7e78f1415646a5669
|
7
|
+
data.tar.gz: 8f17ce9c37b5948c726e8cc13166acf8f5be4b7b63103d8c7eb55f308abfe65a6688000ac8b58a062bd30d8d385225a17ae9e8e80d34e8c757634084cab4776a
|
data/lib/standard_api/helpers.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module StandardAPI
|
2
2
|
module Helpers
|
3
|
-
|
3
|
+
|
4
4
|
def serialize_attribute(json, record, name, type)
|
5
5
|
value = record.send(name)
|
6
|
-
|
6
|
+
|
7
7
|
json.set! name, type == :binary ? value&.unpack1('H*') : value
|
8
8
|
end
|
9
9
|
|
@@ -17,31 +17,25 @@ module StandardAPI
|
|
17
17
|
preloads[key] = value
|
18
18
|
when Hash, ActiveSupport::HashWithIndifferentAccess
|
19
19
|
if !value.keys.any? { |x| ['when', 'where', 'limit', 'offset', 'order', 'distinct'].include?(x) }
|
20
|
-
|
21
|
-
preloads[key] = preloadables_hash(reflection.klass, value)
|
22
|
-
end
|
20
|
+
preloads[key.to_sym] = preloadables_hash(value)
|
23
21
|
end
|
24
22
|
end
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
28
|
-
preloads.
|
26
|
+
preloads.present? ? record.preload(preloads) : record
|
29
27
|
end
|
30
28
|
|
31
|
-
def preloadables_hash(
|
29
|
+
def preloadables_hash(iclds)
|
32
30
|
preloads = {}
|
33
31
|
|
34
32
|
iclds.each do |key, value|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
when
|
40
|
-
|
41
|
-
if !reflection.polymorphic?
|
42
|
-
preloads[key] = preloadables_hash(reflection.klass, value)
|
43
|
-
end
|
44
|
-
end
|
33
|
+
case value
|
34
|
+
when true
|
35
|
+
preloads[key] = value
|
36
|
+
when Hash, ActiveSupport::HashWithIndifferentAccess
|
37
|
+
if !value.keys.any? { |x| [ 'when', 'where', 'limit', 'offset', 'order', 'distinct' ].include?(x) }
|
38
|
+
preloads[key] = preloadables_hash(value)
|
45
39
|
end
|
46
40
|
end
|
47
41
|
end
|
data/lib/standard_api/version.rb
CHANGED
@@ -31,11 +31,21 @@ includes.each do |inc, subinc|
|
|
31
31
|
end
|
32
32
|
else
|
33
33
|
can_cache = can_cache_relation?(record, inc, subinc)
|
34
|
-
|
35
|
-
|
34
|
+
cache_key = nil
|
35
|
+
|
36
|
+
if can_cache
|
37
|
+
if association.is_a?(ActiveRecord::Reflection::BelongsToReflection)
|
38
|
+
can_cache = can_cache && !record.send(association.foreign_key).nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
if can_cache
|
42
|
+
cache_key = association_cache_key(record, inc, subinc)
|
43
|
+
can_cache = cache_key.present?
|
44
|
+
end
|
36
45
|
end
|
46
|
+
|
37
47
|
json.set! inc do
|
38
|
-
json.cache_if!(can_cache, can_cache ?
|
48
|
+
json.cache_if!(can_cache, can_cache ? cache_key : nil) do
|
39
49
|
value = record.send(inc)
|
40
50
|
if value.nil?
|
41
51
|
json.null!
|
@@ -69,7 +69,17 @@ else
|
|
69
69
|
# TODO: it would be nice if rails responded with a true or false here
|
70
70
|
# instead of the function itself
|
71
71
|
json.set! 'auto_populated', !!column.auto_populated? if column.respond_to?(:auto_populated?)
|
72
|
-
|
72
|
+
|
73
|
+
json.set! 'readonly', (if controller.respond_to?("#{ model.model_name.singular }_attributes")
|
74
|
+
!controller.send("#{ model.model_name.singular }_attributes").include?(column.name)
|
75
|
+
else
|
76
|
+
model.readonly_attribute?(column.name)
|
77
|
+
end)
|
78
|
+
|
79
|
+
visitor = StandardAPI::Visitors::Validator.new
|
80
|
+
validations = model.validators.select { |v| v.attributes.include?(column.name.to_sym) }.map { |v| visitor.accept(v, {}) }
|
81
|
+
json.set! 'validations', validations
|
82
|
+
end
|
73
83
|
end
|
74
84
|
end
|
75
85
|
|
@@ -79,6 +79,16 @@ else
|
|
79
79
|
# TODO: it would be nice if rails responded with a true or false here
|
80
80
|
# instead of the function itself
|
81
81
|
json.set! 'auto_populated', !!column.auto_populated? if column.respond_to?(:auto_populated?)
|
82
|
+
|
83
|
+
json.set! 'readonly', (if controller.respond_to?("#{ model.model_name.singular }_attributes")
|
84
|
+
!controller.send("#{ model.model_name.singular }_attributes").include?(column.name)
|
85
|
+
else
|
86
|
+
true
|
87
|
+
end)
|
88
|
+
|
89
|
+
visitor = StandardAPI::Visitors::Validator.new
|
90
|
+
validations = model.validators.select { |v| v.attributes.include?(column.name.to_sym) }.map { |v| visitor.accept(v, {}) }
|
91
|
+
json.set! 'validations', validations
|
82
92
|
end
|
83
93
|
end
|
84
94
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module StandardAPI
|
2
|
+
module Visitors
|
3
|
+
|
4
|
+
class Validator < Arel::Visitors::Visitor
|
5
|
+
|
6
|
+
def visit_ActiveRecord_Validations_AbsenceValidator(o, col)
|
7
|
+
visit_validator(:absence, o.options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def visit_ActiveRecord_Validations_AcceptanceValidator(o, col)
|
11
|
+
visit_validator(:acceptance, o.options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def visit_ActiveRecord_Validations_ComparisonValidator(o, col)
|
15
|
+
visit_validator(:comparison, o.options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def visit_ActiveRecord_Validations_ConfirmationValidator(o, col)
|
19
|
+
visit_validator(:confirmation, o.options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def visit_ActiveRecord_Validations_ExclusionValidator(o, col)
|
23
|
+
visit_validator(:exclusion, o.options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def visit_ActiveRecord_Validations_FormatValidator(o, col)
|
27
|
+
visit_validator(:format, o.options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def visit_ActiveRecord_Validations_InclusionValidator(o, col)
|
31
|
+
visit_validator(:inclusion, o.options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def visit_ActiveRecord_Validations_LengthValidator(o, col)
|
35
|
+
visit_validator(:length, o.options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def visit_ActiveRecord_Validations_NumericalityValidator(o, col)
|
39
|
+
visit_validator(:numericality, o.options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def visit_ActiveRecord_Validations_PresenceValidator(o, col)
|
43
|
+
visit_validator(:presence, o.options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def visit_ActiveRecord_Validations_WithValidator(o, col)
|
47
|
+
visit_validator(:with, o.options)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def visit_validator(name, options)
|
53
|
+
{ name => options.empty? ? true : options.as_json }
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
data/lib/standard_api.rb
CHANGED
@@ -16,6 +16,7 @@ require 'standard_api/helpers'
|
|
16
16
|
require 'standard_api/route_helpers'
|
17
17
|
require 'standard_api/active_record/connection_adapters/postgresql/schema_statements'
|
18
18
|
require 'standard_api/railtie'
|
19
|
+
require 'standard_api/visitors/validations'
|
19
20
|
|
20
21
|
module StandardAPI
|
21
22
|
autoload :AccessControlList, 'standard_api/access_control_list'
|
@@ -112,7 +112,7 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
|
|
112
112
|
@controller.params = {}
|
113
113
|
assert_equal 'SELECT "references".* FROM "references" WHERE "references"."subject_id" = 1', @controller.send(:resources).to_sql
|
114
114
|
end
|
115
|
-
|
115
|
+
|
116
116
|
test "Auto includes on a controller without a model" do
|
117
117
|
@controller = SessionsController.new
|
118
118
|
assert_nil @controller.send(:model)
|
@@ -154,13 +154,34 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
|
|
154
154
|
else
|
155
155
|
assert_nil schema.dig('models', model.name, 'attributes', column.name, 'comment')
|
156
156
|
end
|
157
|
-
|
157
|
+
|
158
158
|
if column.respond_to?(:auto_populated?)
|
159
159
|
assert_equal !!column.auto_populated?, schema.dig('models', model.name, 'attributes', column.name, 'auto_populated')
|
160
160
|
end
|
161
161
|
end
|
162
162
|
end
|
163
163
|
|
164
|
+
assert_equal true, schema['models']['Account']['attributes']['id']['readonly']
|
165
|
+
assert_equal false, schema['models']['Account']['attributes']['name']['readonly']
|
166
|
+
|
167
|
+
assert_equal [
|
168
|
+
{ "presence" => true }
|
169
|
+
], schema['models']['Property']['attributes']['name']['validations']
|
170
|
+
|
171
|
+
assert_equal [
|
172
|
+
{ "numericality" => {
|
173
|
+
"greater_than" => 1,
|
174
|
+
"greater_than_or_equal_to" => 2,
|
175
|
+
"equal_to" => 2,
|
176
|
+
"less_than_or_equal_to" => 2,
|
177
|
+
"less_than" => 3,
|
178
|
+
"other_than" => 0,
|
179
|
+
"even" => true,
|
180
|
+
"in" => "1..3"
|
181
|
+
}
|
182
|
+
}
|
183
|
+
], schema['models']['Property']['attributes']['numericality']['validations']
|
184
|
+
|
164
185
|
assert_equal 'test comment', schema['comment']
|
165
186
|
end
|
166
187
|
|
@@ -263,7 +284,7 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
|
|
263
284
|
assert JSON(response.body).has_key?('document')
|
264
285
|
assert_nil JSON(response.body)['document']
|
265
286
|
end
|
266
|
-
|
287
|
+
|
267
288
|
test 'rendering serialize_attribute' do
|
268
289
|
property = create(:property, description: 'This text will magically change')
|
269
290
|
get property_path(property, format: 'json'), params: { id: property.id, magic: true }
|
@@ -274,15 +295,15 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
|
|
274
295
|
|
275
296
|
test 'rendering an enum' do
|
276
297
|
public_document = create(:document, level: 'public')
|
277
|
-
|
298
|
+
|
278
299
|
get documents_path(format: 'json'), params: { limit: 1 }
|
279
300
|
assert_equal JSON(response.body)[0]['level'], 'public'
|
280
|
-
|
301
|
+
|
281
302
|
secret_document = create(:document, level: 'secret')
|
282
303
|
get document_path(secret_document, format: 'json')
|
283
304
|
assert_equal JSON(response.body)['level'], 'secret'
|
284
305
|
end
|
285
|
-
|
306
|
+
|
286
307
|
test '#index.json uses overridden partial' do
|
287
308
|
create(:property, photos: [create(:photo)])
|
288
309
|
get properties_path(format: 'json'), params: { limit: 100, include: [{:photos => { order: :id }}] }
|
@@ -726,4 +747,36 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
|
|
726
747
|
assert_equal [1], JSON(response.body)
|
727
748
|
end
|
728
749
|
|
750
|
+
test 'preloading polymorphic associations' do
|
751
|
+
p1 = create(:property)
|
752
|
+
p2 = create(:property)
|
753
|
+
c1 = create(:camera)
|
754
|
+
c2 = create(:camera)
|
755
|
+
a1 = create(:account, subject: p1, subject_cached_at: Time.now)
|
756
|
+
a2 = create(:account, subject: p2, subject_cached_at: Time.now)
|
757
|
+
a3 = create(:account, subject: c1, subject_cached_at: Time.now)
|
758
|
+
a4 = create(:account, subject: c2, subject_cached_at: Time.now)
|
759
|
+
a5 = create(:account, subject: c2, subject_cached_at: Time.now)
|
760
|
+
|
761
|
+
assert_sql(
|
762
|
+
'SELECT "properties".* FROM "properties" WHERE "properties"."id" IN ($1, $2)',
|
763
|
+
'SELECT "cameras".* FROM "cameras" WHERE "cameras"."id" IN ($1, $2)'
|
764
|
+
) do
|
765
|
+
assert_no_sql("SELECT \"properties\".* FROM \"properties\" WHERE \"properties\".\"id\" = $1 LIMIT $2") do
|
766
|
+
get accounts_path(limit: 10, include: { subject: { landlord: { when: { subject_type: 'Property' } } } }, format: 'json')
|
767
|
+
|
768
|
+
assert_equal p1.id, a1.subject_id
|
769
|
+
assert_equal p2.id, a2.subject_id
|
770
|
+
assert_equal c1.id, a3.subject_id
|
771
|
+
assert_equal p1.id, JSON(response.body).dig(0, 'subject', 'id')
|
772
|
+
assert_equal p2.id, JSON(response.body).dig(1, 'subject', 'id')
|
773
|
+
assert_equal c1.id, JSON(response.body).dig(2, 'subject', 'id')
|
774
|
+
assert_equal c2.id, JSON(response.body).dig(3, 'subject', 'id')
|
775
|
+
assert_equal c2.id, JSON(response.body).dig(4, 'subject', 'id')
|
776
|
+
end
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
|
781
|
+
|
729
782
|
end
|
@@ -7,7 +7,8 @@ module PropertyACL
|
|
7
7
|
:description,
|
8
8
|
:constructed,
|
9
9
|
:size,
|
10
|
-
:active
|
10
|
+
:active,
|
11
|
+
:numericality
|
11
12
|
# :photos_attributes,
|
12
13
|
# { photos_attributes: [ :id, :account_id, :property_id, :format] }
|
13
14
|
]
|
@@ -15,7 +16,17 @@ module PropertyACL
|
|
15
16
|
|
16
17
|
# Orderings allowed
|
17
18
|
def orders
|
18
|
-
[
|
19
|
+
[
|
20
|
+
"id",
|
21
|
+
"name",
|
22
|
+
"aliases",
|
23
|
+
"description",
|
24
|
+
"constructed",
|
25
|
+
"size",
|
26
|
+
"created_at",
|
27
|
+
"active",
|
28
|
+
"numericality"
|
29
|
+
]
|
19
30
|
end
|
20
31
|
|
21
32
|
# Sub resources allowed to be included in the response
|
@@ -14,7 +14,7 @@ end
|
|
14
14
|
|
15
15
|
class Document < ActiveRecord::Base
|
16
16
|
attr_accessor :file
|
17
|
-
|
17
|
+
|
18
18
|
enum level: { public: 0, secret: 1 }, _suffix: true
|
19
19
|
enum rating: { poor: 0, ok: 1, good: 2 }
|
20
20
|
end
|
@@ -29,13 +29,25 @@ class Property < ActiveRecord::Base
|
|
29
29
|
has_one :document_attachments, class_name: "Attachment", as: :record, inverse_of: :record
|
30
30
|
has_one :document, through: "document_attachments"
|
31
31
|
|
32
|
-
|
33
32
|
validates :name, presence: true
|
34
33
|
accepts_nested_attributes_for :photos
|
35
34
|
|
36
35
|
def english_name
|
37
36
|
'A Name'
|
38
37
|
end
|
38
|
+
|
39
|
+
# Numericality Validation
|
40
|
+
validates :numericality, numericality: {
|
41
|
+
greater_than: 1,
|
42
|
+
greater_than_or_equal_to: 2,
|
43
|
+
equal_to: 2,
|
44
|
+
less_than_or_equal_to: 2,
|
45
|
+
less_than: 3,
|
46
|
+
other_than: 0,
|
47
|
+
even: true,
|
48
|
+
in: 1..3
|
49
|
+
}
|
50
|
+
|
39
51
|
end
|
40
52
|
|
41
53
|
class LSNType < ActiveRecord::Type::Value
|
@@ -129,6 +141,7 @@ class CreateModelTables < ActiveRecord::Migration[6.0]
|
|
129
141
|
t.decimal "size"
|
130
142
|
t.datetime "created_at", null: false
|
131
143
|
t.boolean "active", default: false
|
144
|
+
t.integer "numericality", default: 2
|
132
145
|
end
|
133
146
|
|
134
147
|
create_table "references", force: :cascade do |t|
|
@@ -167,12 +180,12 @@ class CreateModelTables < ActiveRecord::Migration[6.0]
|
|
167
180
|
t.integer 'record_id'
|
168
181
|
t.integer 'document_id'
|
169
182
|
end
|
170
|
-
|
183
|
+
|
171
184
|
create_table "uuid_models", id: :uuid, force: :cascade do |t|
|
172
185
|
t.string 'title', default: 'recruit'
|
173
186
|
t.string 'name', default: -> { 'round(random() * 1000)' }
|
174
187
|
end
|
175
|
-
|
188
|
+
|
176
189
|
end
|
177
190
|
|
178
191
|
end
|
@@ -85,16 +85,58 @@ class ActiveSupport::TestCase
|
|
85
85
|
{ :controller => controller_path, :action => action }.merge(options)
|
86
86
|
end
|
87
87
|
|
88
|
-
def assert_sql(
|
89
|
-
|
90
|
-
|
91
|
-
|
88
|
+
def assert_sql(*expected)
|
89
|
+
return_value = nil
|
90
|
+
|
91
|
+
queries_ran = if block_given?
|
92
|
+
queries_ran = SQLLogger.log.size
|
93
|
+
return_value = yield if block_given?
|
94
|
+
SQLLogger.log[queries_ran...]
|
95
|
+
else
|
96
|
+
[expected.pop]
|
97
|
+
end
|
98
|
+
|
99
|
+
failed_patterns = []
|
100
|
+
expected.each do |pattern|
|
101
|
+
failed_patterns << pattern unless queries_ran.any?{ |sql| sql_equal(pattern, sql) }
|
92
102
|
end
|
93
103
|
|
94
|
-
|
104
|
+
assert failed_patterns.empty?, <<~MSG
|
105
|
+
Query pattern(s) not found:
|
106
|
+
- #{failed_patterns.map{|l| l.gsub(/\n\s*/, " ")}.join('\n - ')}
|
107
|
+
Queries Ran (queries_ran.size):
|
108
|
+
- #{queries_ran.map{|l| l.gsub(/\n\s*/, "\n ")}.join("\n - ")}
|
109
|
+
MSG
|
95
110
|
|
96
|
-
|
97
|
-
|
111
|
+
return_value
|
112
|
+
end
|
113
|
+
|
114
|
+
def assert_no_sql(*not_expected)
|
115
|
+
return_value = nil
|
116
|
+
queries_ran = block_given? ? SQLLogger.log.size : 0
|
117
|
+
return_value = yield if block_given?
|
118
|
+
ensure
|
119
|
+
failed_patterns = []
|
120
|
+
queries_ran = SQLLogger.log[queries_ran...]
|
121
|
+
not_expected.each do |pattern|
|
122
|
+
failed_patterns << pattern if queries_ran.any?{ |sql| sql_equal(pattern, sql) }
|
123
|
+
end
|
124
|
+
assert failed_patterns.empty?, <<~MSG
|
125
|
+
Unexpected Query pattern(s) found:
|
126
|
+
- #{failed_patterns.map(&:inspect).join('\n - ')}
|
127
|
+
Queries Ran (queries_ran.size):
|
128
|
+
- #{queries_ran.map{|l| l.gsub(/\n\s*/, "\n ")}.join("\n - ")}
|
129
|
+
MSG
|
130
|
+
|
131
|
+
return_value
|
132
|
+
end
|
133
|
+
def sql_equal(expected, sql)
|
134
|
+
sql = sql.strip.gsub(/"(\w+)"/, '\1').gsub(/\(\s+/, '(').gsub(/\s+\)/, ')').gsub(/\s+/, ' ')
|
135
|
+
if expected.is_a?(String)
|
136
|
+
expected = Regexp.new(Regexp.escape(expected.strip.gsub(/"(\w+)"/, '\1').gsub(/\(\s+/, '(').gsub(/\s+\)/, ')').gsub(/\s+/, ' ')), Regexp::IGNORECASE)
|
137
|
+
end
|
138
|
+
|
139
|
+
expected.match(sql)
|
98
140
|
end
|
99
141
|
|
100
142
|
def assert_rendered(options = {}, message = nil)
|
@@ -234,6 +276,48 @@ class ActiveSupport::TestCase
|
|
234
276
|
end
|
235
277
|
end
|
236
278
|
|
279
|
+
class SQLLogger
|
280
|
+
class << self
|
281
|
+
attr_accessor :ignored_sql, :log, :log_all
|
282
|
+
def clear_log; self.log = []; self.log_all = []; end
|
283
|
+
end
|
284
|
+
|
285
|
+
self.clear_log
|
286
|
+
|
287
|
+
self.ignored_sql = [/^PRAGMA/i, /^SELECT currval/i, /^SELECT CAST/i, /^SELECT @@IDENTITY/i, /^SELECT @@ROWCOUNT/i, /^SAVEPOINT/i, /^ROLLBACK TO SAVEPOINT/i, /^RELEASE SAVEPOINT/i, /^SHOW max_identifier_length/i, /^BEGIN/i, /^COMMIT/i]
|
288
|
+
|
289
|
+
# FIXME: this needs to be refactored so specific database can add their own
|
290
|
+
# ignored SQL, or better yet, use a different notification for the queries
|
291
|
+
# instead examining the SQL content.
|
292
|
+
oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im]
|
293
|
+
mysql_ignored = [/^SHOW FULL TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /, /^\s*SELECT (?:column_name|table_name)\b.*\bFROM information_schema\.(?:key_column_usage|tables)\b/im]
|
294
|
+
postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i]
|
295
|
+
sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im, /^\s*SELECT sql\b.*\bFROM sqlite_master/im]
|
296
|
+
|
297
|
+
[oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
|
298
|
+
ignored_sql.concat db_ignored_sql
|
299
|
+
end
|
300
|
+
|
301
|
+
attr_reader :ignore
|
302
|
+
|
303
|
+
def initialize(ignore = Regexp.union(self.class.ignored_sql))
|
304
|
+
@ignore = ignore
|
305
|
+
end
|
306
|
+
|
307
|
+
def call(name, start, finish, message_id, values)
|
308
|
+
sql = values[:sql]
|
309
|
+
|
310
|
+
# FIXME: this seems bad. we should probably have a better way to indicate
|
311
|
+
# the query was cached
|
312
|
+
return if 'CACHE' == values[:name]
|
313
|
+
|
314
|
+
self.class.log_all << sql
|
315
|
+
# puts sql
|
316
|
+
self.class.log << sql unless ignore =~ sql
|
317
|
+
end
|
318
|
+
end
|
319
|
+
ActiveSupport::Notifications.subscribe('sql.active_record', SQLLogger.new)
|
320
|
+
|
237
321
|
end
|
238
322
|
|
239
323
|
class ActionController::TestCase
|
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: 7.1.
|
4
|
+
version: 7.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Bracy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -306,6 +306,7 @@ files:
|
|
306
306
|
- lib/standard_api/views/application/schema.streamer
|
307
307
|
- lib/standard_api/views/application/show.json.jbuilder
|
308
308
|
- lib/standard_api/views/application/show.streamer
|
309
|
+
- lib/standard_api/visitors/validations.rb
|
309
310
|
- test/standard_api/caching_test.rb
|
310
311
|
- test/standard_api/controller/include_test.rb
|
311
312
|
- test/standard_api/controller/subresource_test.rb
|
@@ -361,7 +362,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
361
362
|
- !ruby/object:Gem::Version
|
362
363
|
version: '0'
|
363
364
|
requirements: []
|
364
|
-
rubygems_version: 3.5.
|
365
|
+
rubygems_version: 3.5.15
|
365
366
|
signing_key:
|
366
367
|
specification_version: 4
|
367
368
|
summary: StandardAPI makes it easy to expose a query interface for your Rails models
|