standardapi 6.0.0.24 → 6.0.0.30
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
- - ">="
|