standardapi 6.0.0.24 → 6.0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +15 -6
- data/lib/standard_api.rb +1 -0
- data/lib/standard_api/active_record/connection_adapters/postgresql/schema_statements.rb +21 -0
- data/lib/standard_api/controller.rb +45 -69
- data/lib/standard_api/errors.rb +9 -0
- data/lib/standard_api/helpers.rb +62 -10
- data/lib/standard_api/test_case.rb +11 -11
- data/lib/standard_api/test_case/calculate_tests.rb +2 -2
- data/lib/standard_api/test_case/create_tests.rb +6 -6
- data/lib/standard_api/test_case/index_tests.rb +4 -4
- data/lib/standard_api/test_case/schema_tests.rb +19 -3
- data/lib/standard_api/version.rb +1 -1
- data/lib/standard_api/views/application/_schema.json.jbuilder +68 -0
- data/lib/standard_api/views/application/_schema.streamer +78 -0
- data/lib/standard_api/views/application/schema.json.jbuilder +1 -12
- data/lib/standard_api/views/application/schema.streamer +1 -16
- data/test/standard_api/test_app.rb +55 -0
- data/test/standard_api/test_app/config/database.yml +4 -0
- data/test/standard_api/test_app/controllers.rb +107 -0
- data/test/standard_api/test_app/models.rb +94 -0
- data/test/standard_api/test_app/test/factories.rb +50 -0
- data/test/standard_api/test_app/test/fixtures/photo.png +0 -0
- data/test/standard_api/test_app/views/photos/_photo.json.jbuilder +15 -0
- data/test/standard_api/test_app/views/photos/_photo.streamer +17 -0
- data/test/standard_api/test_app/views/photos/_schema.json.jbuilder +1 -0
- data/test/standard_api/test_app/views/photos/_schema.streamer +3 -0
- data/test/standard_api/test_app/views/photos/schema.json.jbuilder +1 -0
- data/test/standard_api/test_app/views/photos/schema.streamer +1 -0
- data/test/standard_api/test_app/views/properties/edit.html.erb +1 -0
- data/test/standard_api/test_app/views/sessions/new.html.erb +0 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf7ed75a06eda9f024ebd87caccab44573802435d0494944137d92da6403fca2
|
4
|
+
data.tar.gz: f44ad9cdd0d8bb87bc971f0b11b8f50b5f5f1289911d7bf4764f6f867b59b407
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f0f4ba53a851f87467a584b0a37708a1d2c6a52fc3360c31fbd50462af17918b7c43b435001410984291db24a451940e97487fc549f993d35ecc63d832d3b20
|
7
|
+
data.tar.gz: 26d70be42fd115039c04a4a0ba614225c05a549af0847d3473659c9d593002cd11addcfd330000163eb9d1597843c274593ba99ddd6f9a57ee39b83e8a05fcbb
|
data/README.md
CHANGED
@@ -105,11 +105,12 @@ including the author, the photos that the author took can also be included.
|
|
105
105
|
# API Usage
|
106
106
|
Resources can be queried via REST style end points
|
107
107
|
```
|
108
|
-
GET /records/:id
|
109
|
-
PATCH /records/:id
|
110
|
-
GET /records
|
111
|
-
|
112
|
-
|
108
|
+
GET /records/:id fetch record
|
109
|
+
PATCH /records/:id update record
|
110
|
+
GET /records/ fetch records
|
111
|
+
GET /records/calculate apply count and other functions on record(s)
|
112
|
+
POST /records create record
|
113
|
+
DELETE /records destroy record
|
113
114
|
```
|
114
115
|
|
115
116
|
All resource end points can be filtered, ordered, limited, offset, and have includes. All options are passed via query string in a nested URI encoded format.
|
@@ -163,8 +164,16 @@ location: {within: 0106000020e6...} WHERE ST_Within("listings"."location
|
|
163
164
|
|
164
165
|
// On Relationships
|
165
166
|
property: {size: 10000} JOIN properties WHERE properties.size = 10000"
|
167
|
+
```
|
168
|
+
## Calculations
|
169
|
+
|
170
|
+
The only change on calculate routes is the `selects` paramater contains the functions to apply. Currently just `minimum`, `maximum`, `average`, `sum`, and `count`.
|
166
171
|
|
167
|
-
|
172
|
+
```
|
173
|
+
{ count: '*' } SELECT COUNT(*)
|
174
|
+
[{ count: '*' }] SELECT COUNT(*)
|
175
|
+
[{ count: '*', maximum: :id, minimum: :id }] SELECT COUNT(*), MAXIMUM(id), MINIMUM(id)
|
176
|
+
[{ maximum: :id }, { maximum: :count }] SELECT MAXIMUM(id), MAXIMUM(count)
|
168
177
|
```
|
169
178
|
|
170
179
|
# Testing
|
data/lib/standard_api.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class PostgreSQLAdapter < AbstractAdapter
|
4
|
+
|
5
|
+
# Returns a comment stored in database for given table
|
6
|
+
def database_comment(database_name=nil) # :nodoc:
|
7
|
+
database_name ||= current_database
|
8
|
+
|
9
|
+
scope = quoted_scope(database_name, type: "BASE TABLE")
|
10
|
+
if scope[:name]
|
11
|
+
query_value(<<~SQL, "SCHEMA")
|
12
|
+
SELECT pg_catalog.shobj_description(d.oid, 'pg_database')
|
13
|
+
FROM pg_catalog.pg_database d
|
14
|
+
WHERE datname = #{scope[:name]};
|
15
|
+
SQL
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,23 +1,32 @@
|
|
1
1
|
module StandardAPI
|
2
2
|
module Controller
|
3
3
|
|
4
|
+
delegate :preloadables, to: :helpers
|
5
|
+
|
4
6
|
def self.included(klass)
|
5
|
-
klass.helper_method :includes, :orders, :model, :resource_limit,
|
6
|
-
:default_limit
|
7
|
+
klass.helper_method :includes, :orders, :model, :models, :resource_limit,
|
8
|
+
:default_limit
|
7
9
|
klass.before_action :set_standardapi_headers
|
10
|
+
klass.rescue_from StandardAPI::ParameterMissing, with: :bad_request
|
8
11
|
klass.rescue_from StandardAPI::UnpermittedParameters, with: :bad_request
|
9
12
|
klass.append_view_path(File.join(File.dirname(__FILE__), 'views'))
|
10
13
|
klass.extend(ClassMethods)
|
11
14
|
end
|
12
15
|
|
13
16
|
def tables
|
14
|
-
Rails.application.eager_load! if Rails.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
render json:
|
17
|
+
Rails.application.eager_load! if !Rails.application.config.eager_load
|
18
|
+
|
19
|
+
tables = ApplicationController.descendants
|
20
|
+
tables.select! { |c| c.ancestors.include?(self.class) && c != self.class }
|
21
|
+
tables.map!(&:model).compact!
|
22
|
+
tables.map!(&:table_name)
|
23
|
+
render json: tables
|
24
|
+
end
|
25
|
+
|
26
|
+
if Rails.env == 'development'
|
27
|
+
def schema
|
28
|
+
Rails.application.eager_load! if !Rails.application.config.eager_load
|
29
|
+
end
|
21
30
|
end
|
22
31
|
|
23
32
|
def index
|
@@ -34,7 +43,7 @@ module StandardAPI
|
|
34
43
|
end
|
35
44
|
end
|
36
45
|
@calculations = Hash[@calculations] if @calculations[0].is_a?(Array) && params[:group_by]
|
37
|
-
|
46
|
+
|
38
47
|
render json: @calculations
|
39
48
|
end
|
40
49
|
|
@@ -95,12 +104,12 @@ module StandardAPI
|
|
95
104
|
resources.find(params[:id]).destroy!
|
96
105
|
head :no_content
|
97
106
|
end
|
98
|
-
|
107
|
+
|
99
108
|
def remove_resource
|
100
109
|
resource = resources.find(params[:id])
|
101
110
|
subresource_class = resource.association(params[:relationship]).klass
|
102
111
|
subresource = subresource_class.find_by_id(params[:resource_id])
|
103
|
-
|
112
|
+
|
104
113
|
if(subresource)
|
105
114
|
result = resource.send(params[:relationship]).delete(subresource)
|
106
115
|
head result ? :no_content : :bad_request
|
@@ -108,10 +117,10 @@ module StandardAPI
|
|
108
117
|
head :not_found
|
109
118
|
end
|
110
119
|
end
|
111
|
-
|
120
|
+
|
112
121
|
def add_resource
|
113
122
|
resource = resources.find(params[:id])
|
114
|
-
|
123
|
+
|
115
124
|
subresource_class = resource.association(params[:relationship]).klass
|
116
125
|
subresource = subresource_class.find_by_id(params[:resource_id])
|
117
126
|
if(subresource)
|
@@ -120,7 +129,7 @@ module StandardAPI
|
|
120
129
|
else
|
121
130
|
head :not_found
|
122
131
|
end
|
123
|
-
|
132
|
+
|
124
133
|
end
|
125
134
|
|
126
135
|
# Override if you want to support masking
|
@@ -129,7 +138,7 @@ module StandardAPI
|
|
129
138
|
end
|
130
139
|
|
131
140
|
module ClassMethods
|
132
|
-
|
141
|
+
|
133
142
|
def model
|
134
143
|
return @model if defined?(@model)
|
135
144
|
@model = name.sub(/Controller\z/, '').singularize.camelize.safe_constantize
|
@@ -151,6 +160,15 @@ module StandardAPI
|
|
151
160
|
self.class.model
|
152
161
|
end
|
153
162
|
|
163
|
+
def models
|
164
|
+
return @models if defined?(@models)
|
165
|
+
Rails.application.eager_load! if !Rails.application.config.eager_load
|
166
|
+
|
167
|
+
@models = ApplicationController.descendants
|
168
|
+
@models.select! { |c| c.ancestors.include?(self.class) && c != self.class }
|
169
|
+
@models.map!(&:model).compact!
|
170
|
+
end
|
171
|
+
|
154
172
|
def model_includes
|
155
173
|
if self.respond_to?("#{model.model_name.singular}_includes", true)
|
156
174
|
self.send("#{model.model_name.singular}_includes")
|
@@ -190,81 +208,39 @@ module StandardAPI
|
|
190
208
|
|
191
209
|
def resources
|
192
210
|
query = model.filter(params['where']).filter(current_mask[model.table_name])
|
193
|
-
|
211
|
+
|
194
212
|
if params[:distinct_on]
|
195
213
|
query = query.distinct_on(params[:distinct_on])
|
196
214
|
elsif params[:distinct]
|
197
215
|
query = query.distinct
|
198
216
|
end
|
199
|
-
|
217
|
+
|
200
218
|
if params[:join]
|
201
219
|
query = query.joins(params[:join].to_sym)
|
202
220
|
end
|
203
|
-
|
221
|
+
|
204
222
|
if params[:group_by]
|
205
223
|
query = query.group(params[:group_by])
|
206
224
|
end
|
207
|
-
|
225
|
+
|
208
226
|
query
|
209
227
|
end
|
210
228
|
|
211
229
|
def includes
|
212
230
|
@includes ||= StandardAPI::Includes.sanitize(params[:include], model_includes)
|
213
231
|
end
|
214
|
-
|
215
|
-
def preloadables(record, includes)
|
216
|
-
preloads = {}
|
217
|
-
|
218
|
-
includes.each do |key, value|
|
219
|
-
if reflection = record.klass.reflections[key]
|
220
|
-
case value
|
221
|
-
when true
|
222
|
-
preloads[key] = value
|
223
|
-
when Hash, ActiveSupport::HashWithIndifferentAccess
|
224
|
-
if !value.keys.any? { |x| ['when', 'where', 'limit', 'offset', 'order', 'distinct'].include?(x) }
|
225
|
-
if !reflection.polymorphic?
|
226
|
-
preloads[key] = preloadables_hash(reflection.klass, value)
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
preloads.empty? ? record : record.preload(preloads)
|
234
|
-
end
|
235
|
-
|
236
|
-
def preloadables_hash(klass, iclds)
|
237
|
-
preloads = {}
|
238
|
-
|
239
|
-
iclds.each do |key, value|
|
240
|
-
if reflection = klass.reflections[key]
|
241
|
-
case value
|
242
|
-
when true
|
243
|
-
preloads[key] = value
|
244
|
-
when Hash, ActiveSupport::HashWithIndifferentAccess
|
245
|
-
if !value.keys.any? { |x| ['when', 'where', 'limit', 'offset', 'order', 'distinct'].include?(x) }
|
246
|
-
if !reflection.polymorphic?
|
247
|
-
preloads[key] = preloadables_hash(reflection.klass, value)
|
248
|
-
end
|
249
|
-
end
|
250
|
-
end
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
preloads
|
255
|
-
end
|
256
|
-
|
232
|
+
|
257
233
|
def required_orders
|
258
234
|
[]
|
259
235
|
end
|
260
|
-
|
236
|
+
|
261
237
|
def default_orders
|
262
238
|
nil
|
263
239
|
end
|
264
240
|
|
265
241
|
def orders
|
266
242
|
exluded_required_orders = required_orders.map(&:to_s)
|
267
|
-
|
243
|
+
|
268
244
|
case params[:order]
|
269
245
|
when Hash, ActionController::Parameters
|
270
246
|
exluded_required_orders -= params[:order].keys.map(&:to_s)
|
@@ -280,7 +256,7 @@ module StandardAPI
|
|
280
256
|
when String
|
281
257
|
exluded_required_orders.delete(params[:order])
|
282
258
|
end
|
283
|
-
|
259
|
+
|
284
260
|
if !exluded_required_orders.empty?
|
285
261
|
params[:order] = exluded_required_orders.unshift(params[:order])
|
286
262
|
end
|
@@ -309,9 +285,9 @@ module StandardAPI
|
|
309
285
|
limit = params.permit(:limit)[:limit]&.to_i || default_limit
|
310
286
|
|
311
287
|
if !limit
|
312
|
-
raise
|
288
|
+
raise StandardAPI::ParameterMissing.new(:limit)
|
313
289
|
elsif limit > resource_limit
|
314
|
-
raise
|
290
|
+
raise StandardAPI::UnpermittedParameters.new([:limit, limit])
|
315
291
|
end
|
316
292
|
|
317
293
|
limit
|
@@ -338,7 +314,7 @@ module StandardAPI
|
|
338
314
|
@model = parts[0].singularize.camelize.constantize
|
339
315
|
column = parts[1]
|
340
316
|
end
|
341
|
-
|
317
|
+
|
342
318
|
column = column == '*' ? Arel.star : column.to_sym
|
343
319
|
if functions.include?(func.to_s.downcase)
|
344
320
|
node = (defined?(@model) ? @model : model).arel_table[column].send(func)
|
data/lib/standard_api/errors.rb
CHANGED
@@ -2,6 +2,15 @@ module StandardAPI
|
|
2
2
|
class StandardAPIError < StandardError
|
3
3
|
end
|
4
4
|
|
5
|
+
class ParameterMissing < StandardAPIError
|
6
|
+
attr_reader :param
|
7
|
+
|
8
|
+
def initialize(param)
|
9
|
+
@param = param
|
10
|
+
super("param is missing or the value is empty: #{param}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
5
14
|
class UnpermittedParameters < StandardAPIError
|
6
15
|
attr_reader :params
|
7
16
|
|
data/lib/standard_api/helpers.rb
CHANGED
@@ -1,6 +1,58 @@
|
|
1
1
|
module StandardAPI
|
2
2
|
module Helpers
|
3
|
-
|
3
|
+
|
4
|
+
def preloadables(record, includes)
|
5
|
+
preloads = {}
|
6
|
+
|
7
|
+
includes.each do |key, value|
|
8
|
+
if reflection = record.klass.reflections[key]
|
9
|
+
case value
|
10
|
+
when true
|
11
|
+
preloads[key] = value
|
12
|
+
when Hash, ActiveSupport::HashWithIndifferentAccess
|
13
|
+
if !value.keys.any? { |x| ['when', 'where', 'limit', 'offset', 'order', 'distinct'].include?(x) }
|
14
|
+
if !reflection.polymorphic?
|
15
|
+
preloads[key] = preloadables_hash(reflection.klass, value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
preloads.empty? ? record : record.preload(preloads)
|
23
|
+
end
|
24
|
+
|
25
|
+
def preloadables_hash(klass, iclds)
|
26
|
+
preloads = {}
|
27
|
+
|
28
|
+
iclds.each do |key, value|
|
29
|
+
if reflection = klass.reflections[key]
|
30
|
+
case value
|
31
|
+
when true
|
32
|
+
preloads[key] = value
|
33
|
+
when Hash, ActiveSupport::HashWithIndifferentAccess
|
34
|
+
if !value.keys.any? { |x| ['when', 'where', 'limit', 'offset', 'order', 'distinct'].include?(x) }
|
35
|
+
if !reflection.polymorphic?
|
36
|
+
preloads[key] = preloadables_hash(reflection.klass, value)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
preloads
|
44
|
+
end
|
45
|
+
|
46
|
+
def schema_partial(model)
|
47
|
+
path = model.model_name.plural
|
48
|
+
|
49
|
+
if lookup_context.exists?("schema", path, true)
|
50
|
+
[path, "schema"].join('/')
|
51
|
+
else
|
52
|
+
'application/schema'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
4
56
|
def model_partial(record)
|
5
57
|
if lookup_context.exists?(record.model_name.element, record.model_name.plural, true)
|
6
58
|
[record.model_name.plural, record.model_name.element].join('/')
|
@@ -17,7 +69,7 @@ module StandardAPI
|
|
17
69
|
false
|
18
70
|
end
|
19
71
|
end
|
20
|
-
|
72
|
+
|
21
73
|
def cache_key(record, includes)
|
22
74
|
timestamp_keys = ['cached_at'] + record.class.column_names.select{|x| x.ends_with? "_cached_at"}
|
23
75
|
if includes.empty?
|
@@ -27,7 +79,7 @@ module StandardAPI
|
|
27
79
|
"#{record.model_name.cache_key}/#{record.id}-#{digest_hash(sort_hash(includes))}-#{timestamp.utc.to_s(record.cache_timestamp_format)}"
|
28
80
|
end
|
29
81
|
end
|
30
|
-
|
82
|
+
|
31
83
|
def can_cache_relation?(klass, relation, subincludes)
|
32
84
|
cache_columns = ["#{relation}_cached_at"] + cached_at_columns_for_includes(subincludes).map {|c| "#{relation}_#{c}"}
|
33
85
|
if (cache_columns - klass.column_names).empty?
|
@@ -36,12 +88,12 @@ module StandardAPI
|
|
36
88
|
false
|
37
89
|
end
|
38
90
|
end
|
39
|
-
|
91
|
+
|
40
92
|
def association_cache_key(record, relation, subincludes)
|
41
93
|
timestamp = ["#{relation}_cached_at"] + cached_at_columns_for_includes(subincludes).map {|c| "#{relation}_#{c}"}
|
42
94
|
timestamp.map! { |col| record.send(col) }
|
43
95
|
timestamp = timestamp.max
|
44
|
-
|
96
|
+
|
45
97
|
case association = record.class.reflect_on_association(relation)
|
46
98
|
when ActiveRecord::Reflection::HasManyReflection, ActiveRecord::Reflection::HasAndBelongsToManyReflection, ActiveRecord::Reflection::HasOneReflection, ActiveRecord::Reflection::ThroughReflection
|
47
99
|
"#{record.model_name.cache_key}/#{record.id}/#{includes_to_cache_key(relation, subincludes)}-#{timestamp.utc.to_s(record.cache_timestamp_format)}"
|
@@ -56,13 +108,13 @@ module StandardAPI
|
|
56
108
|
raise ArgumentError, 'Unkown association type'
|
57
109
|
end
|
58
110
|
end
|
59
|
-
|
111
|
+
|
60
112
|
def cached_at_columns_for_includes(includes)
|
61
113
|
includes.select { |k,v| !['when', 'where', 'limit', 'order', 'distinct', 'distinct_on'].include?(k) }.map do |k, v|
|
62
114
|
["#{k}_cached_at"] + cached_at_columns_for_includes(v).map { |v2| "#{k}_#{v2}" }
|
63
115
|
end.flatten
|
64
116
|
end
|
65
|
-
|
117
|
+
|
66
118
|
def includes_to_cache_key(relation, subincludes)
|
67
119
|
if subincludes.empty?
|
68
120
|
relation.to_s
|
@@ -70,7 +122,7 @@ module StandardAPI
|
|
70
122
|
"#{relation}-#{digest_hash(sort_hash(subincludes))}"
|
71
123
|
end
|
72
124
|
end
|
73
|
-
|
125
|
+
|
74
126
|
def sort_hash(hash)
|
75
127
|
hash.keys.sort.reduce({}) do |seed, key|
|
76
128
|
if seed[key].is_a?(Hash)
|
@@ -81,7 +133,7 @@ module StandardAPI
|
|
81
133
|
seed
|
82
134
|
end
|
83
135
|
end
|
84
|
-
|
136
|
+
|
85
137
|
def digest_hash(*hashes)
|
86
138
|
hashes.compact!
|
87
139
|
hashes.map! { |h| sort_hash(h) }
|
@@ -141,4 +193,4 @@ module StandardAPI
|
|
141
193
|
end
|
142
194
|
|
143
195
|
end
|
144
|
-
end
|
196
|
+
end
|
@@ -18,7 +18,7 @@ module StandardAPI::TestCase
|
|
18
18
|
assert_equal(expected, *args)
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def self.included(klass)
|
23
23
|
[:filters, :orders, :includes].each do |attribute|
|
24
24
|
klass.send(:class_attribute, attribute)
|
@@ -53,14 +53,14 @@ module StandardAPI::TestCase
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
def supports_format(format)
|
56
|
+
def supports_format(format, action=nil)
|
57
57
|
count = controller_class.view_paths.count do |path|
|
58
|
-
!Dir.glob("#{path.instance_variable_get(:@path)}/{#{model.name.underscore},application}
|
58
|
+
!Dir.glob("#{path.instance_variable_get(:@path)}/{#{model.name.underscore},application}/**/#{action || '*'}.#{format}*").empty?
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
count > 0
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
def default_orders
|
65
65
|
controller_class.new.send(:default_orders)
|
66
66
|
end
|
@@ -76,7 +76,7 @@ module StandardAPI::TestCase
|
|
76
76
|
def model
|
77
77
|
self.class.model
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
def mask
|
81
81
|
{}
|
82
82
|
end
|
@@ -98,11 +98,11 @@ module StandardAPI::TestCase
|
|
98
98
|
def singular_name
|
99
99
|
model.model_name.singular
|
100
100
|
end
|
101
|
-
|
101
|
+
|
102
102
|
def plural_name
|
103
103
|
model.model_name.plural
|
104
104
|
end
|
105
|
-
|
105
|
+
|
106
106
|
def create_webmocks(attributes)
|
107
107
|
attributes.each do |attribute, value|
|
108
108
|
self.class.model.validators_on(attribute)
|
@@ -122,7 +122,7 @@ module StandardAPI::TestCase
|
|
122
122
|
value
|
123
123
|
end
|
124
124
|
end
|
125
|
-
|
125
|
+
|
126
126
|
def normalize_to_json(record, attribute, value)
|
127
127
|
value = normalize_attribute(record, attribute, value)
|
128
128
|
return nil if value.nil?
|
@@ -149,7 +149,7 @@ module StandardAPI::TestCase
|
|
149
149
|
|
150
150
|
def controller_class
|
151
151
|
controller_class_name = self.name.gsub(/Test$/, '')
|
152
|
-
controller_class_name.constantize
|
152
|
+
controller_class_name.constantize
|
153
153
|
rescue NameError => e
|
154
154
|
raise e if e.message != "uninitialized constant #{controller_class_name}"
|
155
155
|
end
|
@@ -166,7 +166,7 @@ module StandardAPI::TestCase
|
|
166
166
|
return @model if defined?(@model) && @model
|
167
167
|
|
168
168
|
klass_name = controller_class.name.gsub(/Controller$/, '').singularize
|
169
|
-
|
169
|
+
|
170
170
|
begin
|
171
171
|
@model = klass_name.constantize
|
172
172
|
rescue NameError
|
@@ -12,7 +12,7 @@ module StandardAPI
|
|
12
12
|
create_model
|
13
13
|
|
14
14
|
math_column = model.columns.find { |x| CALCULATE_COLUMN_TYPES.include?(x.sql_type) }
|
15
|
-
|
15
|
+
|
16
16
|
if math_column
|
17
17
|
column = math_column
|
18
18
|
selects = [{ count: column.name }, { maximum: column.name }, { minimum: column.name }, { average: column.name }]
|
@@ -35,7 +35,7 @@ module StandardAPI
|
|
35
35
|
create_model
|
36
36
|
|
37
37
|
math_column = model.columns.find { |x| CALCULATE_COLUMN_TYPES.include?(x.sql_type) }
|
38
|
-
|
38
|
+
|
39
39
|
if math_column
|
40
40
|
column = math_column
|
41
41
|
selects = [{ count: column.name}, { maximum: column.name }, { minimum: column.name }, { average: column.name }]
|
@@ -85,15 +85,15 @@ module StandardAPI
|
|
85
85
|
end
|
86
86
|
|
87
87
|
test '#create.html' do
|
88
|
-
return unless supports_format(:html)
|
88
|
+
return unless supports_format(:html, :create)
|
89
|
+
|
90
|
+
attrs = attributes_for(singular_name, :nested).select do |k,v|
|
91
|
+
!model.readonly_attributes.include?(k.to_s)
|
92
|
+
end
|
89
93
|
|
90
|
-
attrs = attributes_for(singular_name, :nested).select{ |k,v| !model.readonly_attributes.include?(k.to_s) }
|
91
94
|
mask.each { |k, v| attrs[k] = v }
|
92
95
|
create_webmocks(attrs)
|
93
96
|
|
94
|
-
file_upload = attrs.any? { |k, v| v.is_a?(Rack::Test::UploadedFile) }
|
95
|
-
as = file_upload ? nil : :json
|
96
|
-
|
97
97
|
assert_difference("#{model.name}.count") do
|
98
98
|
post resource_path(:create), params: { singular_name => attrs }, as: :html
|
99
99
|
assert_response :redirect
|
@@ -101,7 +101,7 @@ module StandardAPI
|
|
101
101
|
end
|
102
102
|
|
103
103
|
test '#create.html with invalid attributes renders edit action' do
|
104
|
-
return unless supports_format(:html)
|
104
|
+
return unless supports_format(:html, :create)
|
105
105
|
|
106
106
|
trait = FactoryBot.factories[singular_name].definition.defined_traits.any? { |x| x.name.to_s == 'invalid' }
|
107
107
|
|
@@ -37,10 +37,10 @@ module StandardAPI
|
|
37
37
|
|
38
38
|
test '#index.json params[:limit] does not exceed maximum limit' do
|
39
39
|
return if !resource_limit || resource_limit == Float::INFINITY
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
|
41
|
+
get resource_path(:index, format: :json), params: { limit: resource_limit + 1 }
|
42
|
+
assert_response :bad_request
|
43
|
+
assert_equal 'found unpermitted parameters: :limit, 1001', response.body
|
44
44
|
end
|
45
45
|
|
46
46
|
test '#index.json params[:where]' do
|
@@ -9,13 +9,29 @@ module StandardAPI
|
|
9
9
|
get resource_path(:schema, format: :json)
|
10
10
|
assert_response :ok
|
11
11
|
json = JSON(@response.body)
|
12
|
-
assert json['
|
12
|
+
assert json['attributes']
|
13
|
+
|
13
14
|
model.columns.map do |column|
|
14
|
-
|
15
|
+
actual_column = json['attributes'][column.name]
|
16
|
+
assert_not_nil actual_column['type'], "Missing `type` for \"#{model}\" attribute \"#{column.name}\""
|
17
|
+
assert_equal_or_nil model.primary_key == column.name, actual_column['primary_key']
|
18
|
+
assert_equal_or_nil column.null, actual_column['null']
|
19
|
+
assert_equal_or_nil column.array, actual_column['array']
|
20
|
+
assert_equal_or_nil column.comment, actual_column['comment']
|
21
|
+
assert_equal_or_nil (column.default || column.default_function), actual_column['default']
|
15
22
|
end
|
23
|
+
|
16
24
|
assert json['limit']
|
25
|
+
assert_equal_or_nil model.connection.table_comment(model.table_name), json['comment']
|
17
26
|
end
|
18
27
|
|
28
|
+
def assert_equal_or_nil(expected, actual, msg=nil)
|
29
|
+
if expected.nil?
|
30
|
+
assert_nil actual, msg
|
31
|
+
else
|
32
|
+
assert_equal expected, actual, msg
|
33
|
+
end
|
34
|
+
end
|
19
35
|
end
|
20
36
|
end
|
21
|
-
end
|
37
|
+
end
|
data/lib/standard_api/version.rb
CHANGED
@@ -0,0 +1,68 @@
|
|
1
|
+
if model.nil? && controller_name == "application"
|
2
|
+
routes = Rails.application.routes.routes.reject(&:internal).collect do |route|
|
3
|
+
{ name: route.name,
|
4
|
+
verb: route.verb,
|
5
|
+
path: route.path.spec.to_s.gsub(/\(\.format\)\Z/, ''),
|
6
|
+
controller: route.requirements[:controller],
|
7
|
+
action: route.requirements[:action],
|
8
|
+
array: ['index'].include?(route.requirements[:action]) }
|
9
|
+
end
|
10
|
+
|
11
|
+
json.set! 'comment', ActiveRecord::Base.connection.database_comment
|
12
|
+
|
13
|
+
json.set! 'routes' do
|
14
|
+
json.array!(routes) do |route|
|
15
|
+
controller = if controller_name = route[:controller]
|
16
|
+
begin
|
17
|
+
controller_param = controller_name.underscore
|
18
|
+
const_name = "#{controller_param.camelize}Controller"
|
19
|
+
const = ActiveSupport::Dependencies.constantize(const_name)
|
20
|
+
if const.ancestors.include?(StandardAPI::Controller)
|
21
|
+
const
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
rescue NameError
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
next if controller.nil?
|
30
|
+
|
31
|
+
resource_limit = controller.resource_limit if controller.respond_to?(:resource_limit)
|
32
|
+
|
33
|
+
json.set! 'path', route[:path]
|
34
|
+
json.set! 'method', route[:verb]
|
35
|
+
json.set! 'model', controller.model&.name
|
36
|
+
json.set! 'array', route[:array]
|
37
|
+
json.set! 'limit', resource_limit
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
json.set! 'models' do
|
42
|
+
models.each do |model|
|
43
|
+
json.set! model.name do
|
44
|
+
json.partial! partial: schema_partial(model), model: model
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
else
|
50
|
+
|
51
|
+
json.set! 'attributes' do
|
52
|
+
model.columns.each do |column|
|
53
|
+
json.set! column.name, {
|
54
|
+
type: json_column_type(column.sql_type),
|
55
|
+
default: column.default || column.default_function,
|
56
|
+
primary_key: column.name == model.primary_key,
|
57
|
+
null: column.null,
|
58
|
+
array: column.array,
|
59
|
+
comment: column.comment
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
json.set! 'limit', resource_limit # This should be removed?
|
65
|
+
json.set! 'comment', model.connection.table_comment(model.table_name)
|
66
|
+
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
if model.nil? && controller_name == "application"
|
2
|
+
routes = Rails.application.routes.routes.reject(&:internal).collect do |route|
|
3
|
+
{ name: route.name,
|
4
|
+
verb: route.verb,
|
5
|
+
path: route.path.spec.to_s.gsub(/\(\.:format\)\Z/, ''),
|
6
|
+
controller: route.requirements[:controller],
|
7
|
+
action: route.requirements[:action],
|
8
|
+
array: ['index'].include?(route.requirements[:action]) }
|
9
|
+
end
|
10
|
+
|
11
|
+
json.object! do
|
12
|
+
json.set! 'comment', ActiveRecord::Base.connection.database_comment
|
13
|
+
|
14
|
+
json.set! 'routes' do
|
15
|
+
json.array!(routes) do |route|
|
16
|
+
controller = if controller_name = route[:controller]
|
17
|
+
begin
|
18
|
+
controller_param = controller_name.underscore
|
19
|
+
const_name = "#{controller_param.camelize}Controller"
|
20
|
+
const = ActiveSupport::Dependencies.constantize(const_name)
|
21
|
+
if const.ancestors.include?(StandardAPI::Controller)
|
22
|
+
const
|
23
|
+
else
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
rescue NameError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
next if controller.nil?
|
31
|
+
|
32
|
+
resource_limit = controller.resource_limit if controller.respond_to?(:resource_limit)
|
33
|
+
|
34
|
+
json.object! do
|
35
|
+
json.set! 'path', route[:path]
|
36
|
+
json.set! 'method', route[:verb]
|
37
|
+
json.set! 'model', controller.model&.name
|
38
|
+
json.set! 'array', route[:array]
|
39
|
+
json.set! 'limit', resource_limit
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
json.set! 'models' do
|
46
|
+
json.object! do
|
47
|
+
models.each do |model|
|
48
|
+
json.set! model.name do
|
49
|
+
json.partial!(schema_partial(model), model: model)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
else
|
57
|
+
|
58
|
+
json.object! do
|
59
|
+
json.set! 'attributes' do
|
60
|
+
json.object! do
|
61
|
+
model.columns.each do |column|
|
62
|
+
json.set! column.name, {
|
63
|
+
type: json_column_type(column.sql_type),
|
64
|
+
default: column.default || column.default_function,
|
65
|
+
primary_key: column.name == model.primary_key,
|
66
|
+
null: column.null,
|
67
|
+
array: column.array,
|
68
|
+
comment: column.comment
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
json.set! 'limit', resource_limit
|
75
|
+
json.set! 'comment', model.connection.table_comment(model.table_name)
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -1,12 +1 @@
|
|
1
|
-
json.
|
2
|
-
model.columns.each do |column|
|
3
|
-
json.set! column.name, {
|
4
|
-
type: json_column_type(column.sql_type),
|
5
|
-
primary_key: column.name == model.primary_key,
|
6
|
-
null: column.null,
|
7
|
-
array: column.array
|
8
|
-
}
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
json.set! 'limit', resource_limit
|
1
|
+
json.partial!('schema', model: model)
|
@@ -1,16 +1 @@
|
|
1
|
-
json.
|
2
|
-
json.set! 'columns' do
|
3
|
-
json.object! do
|
4
|
-
model.columns.each do |column|
|
5
|
-
json.set! column.name, {
|
6
|
-
type: json_column_type(column.sql_type),
|
7
|
-
primary_key: column.name == model.primary_key,
|
8
|
-
null: column.null,
|
9
|
-
array: column.array
|
10
|
-
}
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
json.set! 'limit', resource_limit
|
16
|
-
end
|
1
|
+
json.partial!('schema', model: model)
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "rails"
|
2
|
+
require "active_model/railtie"
|
3
|
+
require "active_record/railtie"
|
4
|
+
require "action_controller/railtie"
|
5
|
+
require "action_view/railtie"
|
6
|
+
require "rails/test_unit/railtie"
|
7
|
+
Bundler.require(*Rails.groups)
|
8
|
+
|
9
|
+
require 'standard_api'
|
10
|
+
|
11
|
+
# Test Application Config
|
12
|
+
Rails.env = 'test'
|
13
|
+
|
14
|
+
class TestApplication < Rails::Application
|
15
|
+
config.root = File.join(File.dirname(__FILE__), 'test_app')
|
16
|
+
config.secret_key_base = 'test key base'
|
17
|
+
config.eager_load = true
|
18
|
+
config.cache_classes = true
|
19
|
+
config.action_controller.perform_caching = true
|
20
|
+
config.cache_store = :memory_store, { size: 8.megabytes }
|
21
|
+
config.action_dispatch.show_exceptions = false
|
22
|
+
|
23
|
+
# if defined?(FactoryBotRails)
|
24
|
+
# config.factory_bot.definition_file_paths += [ '../factories' ]
|
25
|
+
# end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Test Application initialization
|
29
|
+
TestApplication.initialize!
|
30
|
+
|
31
|
+
# Test Application Models
|
32
|
+
require 'standard_api/test_app/models'
|
33
|
+
|
34
|
+
# Test Application Controllers
|
35
|
+
require 'standard_api/test_app/controllers'
|
36
|
+
|
37
|
+
# Test Application Routes
|
38
|
+
Rails.application.routes.draw do
|
39
|
+
get :tables, to: 'application#tables', as: :tables
|
40
|
+
get :schema, to: 'application#schema', as: :schema
|
41
|
+
|
42
|
+
[:properties, :photos, :documents, :references, :sessions, :unlimited, :default_limit].each do |r|
|
43
|
+
standard_resources r
|
44
|
+
end
|
45
|
+
|
46
|
+
standard_resource :account
|
47
|
+
end
|
48
|
+
|
49
|
+
# Test Application Helpers
|
50
|
+
Object.const_set(:ApplicationHelper, Module.new)
|
51
|
+
|
52
|
+
# require 'turbostreamer'
|
53
|
+
# require 'wankel'
|
54
|
+
# ActionView::Template.unregister_template_handler :jbuilder
|
55
|
+
# ActionView::Template.register_template_handler :streamer, TurboStreamer::Handler
|
@@ -0,0 +1,107 @@
|
|
1
|
+
class ApplicationController < ActionController::Base
|
2
|
+
include StandardAPI::Controller
|
3
|
+
prepend_view_path File.join(File.dirname(__FILE__), 'views')
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def account_params
|
8
|
+
[ "property_id", "name" ]
|
9
|
+
end
|
10
|
+
|
11
|
+
def account_orders
|
12
|
+
[ "id" ]
|
13
|
+
end
|
14
|
+
|
15
|
+
def account_includes
|
16
|
+
[ "photos" ]
|
17
|
+
end
|
18
|
+
|
19
|
+
def property_params
|
20
|
+
[ :name,
|
21
|
+
:aliases,
|
22
|
+
:description,
|
23
|
+
:constructed,
|
24
|
+
:size,
|
25
|
+
:active,
|
26
|
+
:photos_attributes,
|
27
|
+
{ photos_attributes: [ :id, :account_id, :property_id, :format] }
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
def property_orders
|
32
|
+
["id", "name", "aliases", "description", "constructed", "size", "created_at", "active"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def property_includes
|
36
|
+
[:photos, :landlord, :english_name]
|
37
|
+
end
|
38
|
+
|
39
|
+
def reference_includes
|
40
|
+
{ subject: [ :landlord, :photos ] }
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
class PropertiesController < ApplicationController
|
46
|
+
end
|
47
|
+
|
48
|
+
class AccountsController < ApplicationController
|
49
|
+
end
|
50
|
+
|
51
|
+
class DocumentsController < ApplicationController
|
52
|
+
|
53
|
+
def document_params
|
54
|
+
[ :file, :type ]
|
55
|
+
end
|
56
|
+
|
57
|
+
def document_orders
|
58
|
+
[:id]
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
class PhotosController < ApplicationController
|
64
|
+
|
65
|
+
def photo_params
|
66
|
+
[ :id, :account_id, :property_id, :format ]
|
67
|
+
end
|
68
|
+
|
69
|
+
def photo_orders
|
70
|
+
[:id]
|
71
|
+
end
|
72
|
+
|
73
|
+
def photo_includes
|
74
|
+
[:account]
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
class ReferencesController < ApplicationController
|
80
|
+
end
|
81
|
+
|
82
|
+
class SessionsController < ApplicationController
|
83
|
+
end
|
84
|
+
|
85
|
+
class UnlimitedController < ApplicationController
|
86
|
+
|
87
|
+
def self.model
|
88
|
+
Account
|
89
|
+
end
|
90
|
+
|
91
|
+
def resource_limit
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
class DefaultLimitController < ApplicationController
|
98
|
+
|
99
|
+
def self.model
|
100
|
+
Account
|
101
|
+
end
|
102
|
+
|
103
|
+
def default_limit
|
104
|
+
100
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# = Models
|
2
|
+
|
3
|
+
class Account < ActiveRecord::Base
|
4
|
+
has_many :photos
|
5
|
+
belongs_to :property
|
6
|
+
end
|
7
|
+
|
8
|
+
class Photo < ActiveRecord::Base
|
9
|
+
belongs_to :account, :counter_cache => true
|
10
|
+
has_and_belongs_to_many :properties
|
11
|
+
end
|
12
|
+
|
13
|
+
class Document < ActiveRecord::Base
|
14
|
+
attr_accessor :file
|
15
|
+
end
|
16
|
+
|
17
|
+
class Pdf < Document
|
18
|
+
end
|
19
|
+
|
20
|
+
class Property < ActiveRecord::Base
|
21
|
+
has_and_belongs_to_many :photos
|
22
|
+
has_many :accounts
|
23
|
+
has_one :landlord, class_name: 'Account'
|
24
|
+
|
25
|
+
validates :name, presence: true
|
26
|
+
accepts_nested_attributes_for :photos
|
27
|
+
|
28
|
+
def english_name
|
29
|
+
'A Name'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Reference < ActiveRecord::Base
|
34
|
+
belongs_to :subject, polymorphic: true
|
35
|
+
end
|
36
|
+
|
37
|
+
# = Migration
|
38
|
+
|
39
|
+
class CreateModelTables < ActiveRecord::Migration[6.0]
|
40
|
+
|
41
|
+
def self.up
|
42
|
+
|
43
|
+
comment = "test comment"
|
44
|
+
exec_query(<<-SQL, "SQL")
|
45
|
+
COMMENT ON DATABASE #{quote_column_name(current_database)} IS #{quote(comment)};
|
46
|
+
SQL
|
47
|
+
|
48
|
+
create_table "accounts", force: :cascade do |t|
|
49
|
+
t.string 'name', limit: 255
|
50
|
+
t.integer 'property_id'
|
51
|
+
t.integer 'photos_count', null: false, default: 0
|
52
|
+
end
|
53
|
+
|
54
|
+
create_table "photos", force: :cascade do |t|
|
55
|
+
t.integer "account_id"
|
56
|
+
t.integer "property_id"
|
57
|
+
t.string "format", limit: 255
|
58
|
+
end
|
59
|
+
|
60
|
+
create_table "properties", force: :cascade do |t|
|
61
|
+
t.string "name", limit: 255
|
62
|
+
t.string "aliases", default: [], array: true
|
63
|
+
t.text "description"
|
64
|
+
t.integer "constructed"
|
65
|
+
t.decimal "size"
|
66
|
+
t.datetime "created_at", null: false
|
67
|
+
t.boolean "active", default: false
|
68
|
+
end
|
69
|
+
|
70
|
+
create_table "references", force: :cascade do |t|
|
71
|
+
t.integer "subject_id"
|
72
|
+
t.string "subject_type", limit: 255
|
73
|
+
t.string "key"
|
74
|
+
t.string "value"
|
75
|
+
end
|
76
|
+
|
77
|
+
create_table "photos_properties", force: :cascade do |t|
|
78
|
+
t.integer "photo_id"
|
79
|
+
t.integer "property_id"
|
80
|
+
end
|
81
|
+
|
82
|
+
create_table "landlords_properties", force: :cascade do |t|
|
83
|
+
t.integer "landlord_id"
|
84
|
+
t.integer "property_id"
|
85
|
+
end
|
86
|
+
|
87
|
+
create_table "documents", force: :cascade do |t|
|
88
|
+
t.string 'type'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
ActiveRecord::Migration.verbose = false
|
94
|
+
CreateModelTables.up
|
@@ -0,0 +1,50 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :account do
|
3
|
+
name { Faker::Name.name }
|
4
|
+
|
5
|
+
trait(:nested) { }
|
6
|
+
trait(:invalid) do
|
7
|
+
name { nil }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
factory :landlord do
|
12
|
+
name { Faker::Name.name }
|
13
|
+
end
|
14
|
+
|
15
|
+
factory :photo do
|
16
|
+
format { ['jpg', 'png', 'tiff'].sample }
|
17
|
+
end
|
18
|
+
|
19
|
+
factory :document do
|
20
|
+
file { fixture_file_upload(Rails.root + 'test/fixtures/photo.png', 'image/png') }
|
21
|
+
end
|
22
|
+
|
23
|
+
factory :pdf do
|
24
|
+
type { 'Pdf' }
|
25
|
+
file { fixture_file_upload(Rails.root + 'test/fixtures/photo.png', 'image/png') }
|
26
|
+
end
|
27
|
+
|
28
|
+
factory :reference do
|
29
|
+
subject_type { 'Photo' }
|
30
|
+
subject_id { create(:photo).id }
|
31
|
+
end
|
32
|
+
|
33
|
+
factory :property do
|
34
|
+
name { Faker::Lorem.words(number: Kernel.rand(1..4)).join(' ') }
|
35
|
+
description { Faker::Lorem.paragraphs.join("\n\n") }
|
36
|
+
constructed { Kernel.rand(1800..(Time.now.year - 2)) }
|
37
|
+
size { Kernel.rand(1000..10000000).to_f / 100 }
|
38
|
+
active { [true, false].sample }
|
39
|
+
photos { [create(:photo)] }
|
40
|
+
|
41
|
+
trait(:nested) do
|
42
|
+
photos_attributes { [attributes_for(:photo)] }
|
43
|
+
end
|
44
|
+
|
45
|
+
trait(:invalid) do
|
46
|
+
name { nil }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
Binary file
|
@@ -0,0 +1,15 @@
|
|
1
|
+
json.set! :id, photo.id
|
2
|
+
json.set! :account_id, photo.account_id
|
3
|
+
json.set! :property_id, photo.property_id
|
4
|
+
json.set! :format, photo.format
|
5
|
+
json.set! :template, 'photos/_photo'
|
6
|
+
|
7
|
+
if includes[:account]
|
8
|
+
json.set! :account do
|
9
|
+
if photo.account
|
10
|
+
json.partial! 'application/record', record: photo.account, includes: includes[:account]
|
11
|
+
else
|
12
|
+
json.null!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
json.object! do
|
2
|
+
json.set! :id, photo.id
|
3
|
+
json.set! :account_id, photo.account_id
|
4
|
+
json.set! :property_id, photo.property_id
|
5
|
+
json.set! :format, photo.format
|
6
|
+
json.set! :template, 'photos/_photo'
|
7
|
+
|
8
|
+
if includes[:account]
|
9
|
+
json.set! :account do
|
10
|
+
if photo.account
|
11
|
+
json.partial! 'application/record', record: photo.account, includes: includes[:account]
|
12
|
+
else
|
13
|
+
json.null!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
json.set! 'template', 'photos/schema'
|
@@ -0,0 +1 @@
|
|
1
|
+
json.partial! 'schema'
|
@@ -0,0 +1 @@
|
|
1
|
+
json.partial! 'schema'
|
@@ -0,0 +1 @@
|
|
1
|
+
properties#edit.html
|
File without changes
|
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.
|
4
|
+
version: 6.0.0.30
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Bracy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -245,6 +245,7 @@ extra_rdoc_files:
|
|
245
245
|
files:
|
246
246
|
- README.md
|
247
247
|
- lib/standard_api.rb
|
248
|
+
- lib/standard_api/active_record/connection_adapters/postgresql/schema_statements.rb
|
248
249
|
- lib/standard_api/controller.rb
|
249
250
|
- lib/standard_api/errors.rb
|
250
251
|
- lib/standard_api/helpers.rb
|
@@ -265,6 +266,8 @@ files:
|
|
265
266
|
- lib/standard_api/version.rb
|
266
267
|
- lib/standard_api/views/application/_record.json.jbuilder
|
267
268
|
- lib/standard_api/views/application/_record.streamer
|
269
|
+
- lib/standard_api/views/application/_schema.json.jbuilder
|
270
|
+
- lib/standard_api/views/application/_schema.streamer
|
268
271
|
- lib/standard_api/views/application/index.json.jbuilder
|
269
272
|
- lib/standard_api/views/application/index.streamer
|
270
273
|
- lib/standard_api/views/application/new.json.jbuilder
|
@@ -273,6 +276,20 @@ files:
|
|
273
276
|
- lib/standard_api/views/application/schema.streamer
|
274
277
|
- lib/standard_api/views/application/show.json.jbuilder
|
275
278
|
- lib/standard_api/views/application/show.streamer
|
279
|
+
- test/standard_api/test_app.rb
|
280
|
+
- test/standard_api/test_app/config/database.yml
|
281
|
+
- test/standard_api/test_app/controllers.rb
|
282
|
+
- test/standard_api/test_app/models.rb
|
283
|
+
- test/standard_api/test_app/test/factories.rb
|
284
|
+
- test/standard_api/test_app/test/fixtures/photo.png
|
285
|
+
- test/standard_api/test_app/views/photos/_photo.json.jbuilder
|
286
|
+
- test/standard_api/test_app/views/photos/_photo.streamer
|
287
|
+
- test/standard_api/test_app/views/photos/_schema.json.jbuilder
|
288
|
+
- test/standard_api/test_app/views/photos/_schema.streamer
|
289
|
+
- test/standard_api/test_app/views/photos/schema.json.jbuilder
|
290
|
+
- test/standard_api/test_app/views/photos/schema.streamer
|
291
|
+
- test/standard_api/test_app/views/properties/edit.html.erb
|
292
|
+
- test/standard_api/test_app/views/sessions/new.html.erb
|
276
293
|
homepage: https://github.com/waratuman/standardapi
|
277
294
|
licenses:
|
278
295
|
- MIT
|
@@ -283,6 +300,7 @@ rdoc_options:
|
|
283
300
|
- README.md
|
284
301
|
require_paths:
|
285
302
|
- lib
|
303
|
+
- test
|
286
304
|
required_ruby_version: !ruby/object:Gem::Requirement
|
287
305
|
requirements:
|
288
306
|
- - ">="
|