rails_on_rails 0.0.9

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.
Files changed (28) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +149 -0
  3. data/bin/rails_on_rails +5 -0
  4. data/lib/autogenerators/bash_commands/generate_controller.sh +1 -0
  5. data/lib/autogenerators/bash_commands/generate_model.sh +1 -0
  6. data/lib/autogenerators/bash_commands/install_rspec.sh +1 -0
  7. data/lib/autogenerators/generator_templates/application_record.txt +8 -0
  8. data/lib/autogenerators/generator_templates/controller.txt +14 -0
  9. data/lib/autogenerators/generator_templates/test_rspec_controller.txt +169 -0
  10. data/lib/autogenerators/generator_templates/test_seeder.txt +45 -0
  11. data/lib/autogenerators/handlers/controller.rb +28 -0
  12. data/lib/autogenerators/handlers/initialize_api_docs.rb +24 -0
  13. data/lib/autogenerators/handlers/initialize_global_controller.rb +7 -0
  14. data/lib/autogenerators/handlers/initialize_rspec.rb +23 -0
  15. data/lib/autogenerators/handlers/test_rspec_controller.rb +16 -0
  16. data/lib/autogenerators/handlers/test_seeder.rb +16 -0
  17. data/lib/autogenerators/rails_templates/api_documentation.rake +181 -0
  18. data/lib/autogenerators/rails_templates/auth_helper.rb +73 -0
  19. data/lib/autogenerators/rails_templates/documentation/index.html.erb +121 -0
  20. data/lib/autogenerators/rails_templates/documentation/layout.html.erb +14 -0
  21. data/lib/autogenerators/rails_templates/documentation_controller.rb +139 -0
  22. data/lib/autogenerators/rails_templates/global_controller.rb +411 -0
  23. data/lib/autogenerators/rails_templates/seeds.rb +91 -0
  24. data/lib/constants/cli_constants.rb +56 -0
  25. data/lib/constants/options.rb +61 -0
  26. data/lib/rails_on_rails.rb +124 -0
  27. data/lib/utils/rails_methods.rb +9 -0
  28. metadata +111 -0
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Automatic API Docs</title>
5
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
6
+ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
7
+ <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
8
+ <style>.flex-respond-row {display: flex !important;flex-direction: column !important;}@media (min-width: 992px) {.flex-respond-row {display: flex !important;flex-direction: row !important;}}.card-shadow {box-shadow: 0 7px 14px 0 rgba(50,50,93,.1), 0 3px 6px 0 rgba(0,0,0,.07);}.cursor-pointer{ cursor: pointer !important; }</style>
9
+ </head>
10
+
11
+ <body style="width: 100%; overflow-x: hidden;">
12
+ <%= yield %>
13
+ </body>
14
+ </html>
@@ -0,0 +1,139 @@
1
+ class DocumentationController < ApplicationController
2
+
3
+ def index
4
+ @allRoutes = allRoutes
5
+ render template: 'documentation/index', layout: 'documentation'
6
+ end
7
+
8
+ private
9
+
10
+ def allRoutes
11
+ rails_routes = Rails.application.routes.routes.map do |e|
12
+ route = e.path.spec.to_s.split('(')[0]
13
+ verb = e.defaults[:action]
14
+ method = e.verb
15
+ { method: method, route: route, verb: verb }
16
+ end.reject do |e|
17
+ first_path = e[:route].split('/')[1].to_s
18
+ first_path == 'rails' || first_path == 'assets' || first_path == 'documentation' || first_path == 'cable'
19
+ end
20
+
21
+ model_lookup = {}
22
+ description_lookup = {}
23
+ Dir.foreach("#{Rails.root}/app/models") do |model_path|
24
+ model, file_suffix = model_path.split('.')
25
+ if model && file_suffix
26
+ model_lookup[model.to_sym] = model.to_s
27
+ end
28
+ end
29
+
30
+ rails_routes.map do |r|
31
+ camelCased = ''
32
+ camelCased += r[:method].downcase
33
+ method = r[:method]
34
+ route = r[:route]
35
+ verb = r[:verb]
36
+
37
+ tempRoute = r[:route]
38
+ params = []
39
+
40
+ tempRoute.split('/').each do |line|
41
+ cap = line[0]
42
+ if cap == ':'
43
+ line[0] = ''
44
+ params.push(line)
45
+ ln = ''
46
+ line.split('-').each do |l|
47
+ ln += l.capitalize
48
+ end
49
+ camelCased += ln.capitalize + 'Param'
50
+ else
51
+ ln = ''
52
+ line.split('-').each do |l|
53
+ ln += l.capitalize
54
+ end
55
+ camelCased += ln.capitalize
56
+ end
57
+ end
58
+
59
+ routeHeader = ''
60
+ submitButtonColor = ''
61
+ if method == 'GET'
62
+ routeHeader = 'bg-success text-white'
63
+ submitButtonColor = 'btn btn-outline-success'
64
+ elsif method == 'POST'
65
+ routeHeader = 'bg-info text-white'
66
+ submitButtonColor = 'btn btn-outline-info'
67
+ elsif method == 'PATCH'
68
+ routeHeader = 'bg-warning text-white'
69
+ submitButtonColor = 'btn btn-outline-warning'
70
+ elsif method == 'PUT'
71
+ routeHeader = 'bg-warning text-white'
72
+ submitButtonColor = 'btn btn-outline-warning'
73
+ elsif method == 'DELETE'
74
+ routeHeader = 'bg-danger text-white'
75
+ submitButtonColor = 'btn btn-outline-danger'
76
+ end
77
+
78
+ route_path = route.split('/')
79
+
80
+ route_models = route_path.each_with_object([]) do |e, acc|
81
+ modal_name = e.underscore.singularize
82
+ if model_lookup[modal_name.to_sym]
83
+ acc.push(modal_name.classify.constantize)
84
+ end
85
+ acc
86
+ end
87
+
88
+ description = []
89
+
90
+ if route_models.length > 0
91
+ model = route_models.last
92
+ attrbute_keys = model.columns
93
+ if !description_lookup[model.name.to_sym]
94
+ include_assocations = model.reflect_on_all_associations.map(&:name).map { |e| "'#{e}'" }.join(",\n")
95
+
96
+ include_description = "Include Querystring options are: [#{include_assocations}]. \n"
97
+ where_description = "Where Querystring options are: [#{attrbute_keys.map { |e| "'#{e.name}:#{e.type}-value-type>'" }.join(", ")}]. \n"
98
+ like_description = "Like Querystring options are: [#{attrbute_keys.map { |e| "'#{e.name}:<#{e.type}-value-type>'" }.join(", ")}]. \n"
99
+ order_description = "Order Querystring options are: [#{attrbute_keys.map { |e| "'#{e.name} ASC'" }.concat(attrbute_keys.map { |e| "'#{e.name} DESC'" }).join(", \n")}]. \n"
100
+ body_params_description = "Available body params are: #{attrbute_keys.map { |e| e.name }.reject { |e| e == 'id' || e == 'created_at' || e == 'updated_at' }.map { |e| "'#{e}'" }.join(', ')}"
101
+ description_lookup[model.name.to_sym] = {
102
+ include_description: include_description,
103
+ where_description: where_description,
104
+ like_description: like_description,
105
+ order_description: order_description,
106
+ body_params_description: body_params_description,
107
+ }
108
+ end
109
+ include_description ||= description_lookup[model.name.to_sym][:include_description]
110
+ where_description ||= description_lookup[model.name.to_sym][:where_description]
111
+ like_description ||= description_lookup[model.name.to_sym][:like_description]
112
+ order_description ||= description_lookup[model.name.to_sym][:order_description]
113
+ body_params_description = description_lookup[model.name.to_sym][:body_params_description] || "Available body params are: #{attrbute_keys.map { |e| e.name }.reject { |e| e == 'id' || e == 'created_at' || e == 'updated_at' }.map { |e| "'#{e}'" }.join(', ')}"
114
+
115
+ description.push(include_description) if (verb == 'index' || verb == 'show') && include_description
116
+ description.push(where_description) if (verb == 'index' || verb == 'show') && where_description
117
+ description.push(like_description) if verb == 'index' && like_description
118
+ description.push(order_description) if verb == 'index' && order_description
119
+ description.push(body_params_description) if verb == 'create'
120
+ description.push(body_params_description) if verb == 'update'
121
+ end
122
+
123
+ description.push("Not available") if description.length == 0
124
+
125
+ {
126
+ method: method,
127
+ route: route,
128
+ params: params,
129
+ middleware: ['Not Available'],
130
+ routeHeader: routeHeader,
131
+ submitButtonColor: submitButtonColor,
132
+ camelCased: camelCased,
133
+ allowParams: tempRoute.split(':').length > 1,
134
+ allowBody: method == 'GET' ? false : method != 'DELETE',
135
+ description: description,
136
+ }
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,411 @@
1
+ class GlobalController < ApplicationController
2
+ def index
3
+ options = default_options || {}
4
+ default_error_handling(handle_pagination(model, options), 200, options)
5
+ end
6
+
7
+ def show
8
+ options = default_options || {}
9
+ default_error_handling(handle_show(model, options), 200, options)
10
+ end
11
+
12
+ def create
13
+ default_error_handling(handle_create(model, render_params), 201)
14
+ end
15
+
16
+ def bulk_create
17
+ default_error_handling(handle_bulk_create(model, params[:bulk]), 201)
18
+ end
19
+
20
+ def bulk_csv_create
21
+ default_error_handling(handle_bulk_create(model, csv_to_json), 201)
22
+ end
23
+
24
+ def update
25
+ default_error_handling(handle_update(model, render_params), 200)
26
+ end
27
+
28
+ def bulk_update
29
+ default_error_handling(handle_bulk_update(model, params[:bulk]), 200)
30
+ end
31
+
32
+ def destroy
33
+ default_error_handling(handle_destroy(model), 204)
34
+ end
35
+
36
+ def bulk_destroy
37
+ default_error_handling(handle_bulk_destroy(model, params[:ids]), 204)
38
+ end
39
+
40
+ def duplicate
41
+ default_error_handling(handle_duplicate(model), 201)
42
+ end
43
+
44
+ def handle_duplicate(model_hash, id = nil)
45
+ associations = model_hash[:model].reflect_on_all_associations.reject { |e| e.is_a?(ActiveRecord::Reflection::BelongsToReflection) }
46
+ existing = model_hash[:model].find_by(id: id || params[:id])
47
+ attributes = existing.attributes.symbolize_keys.without(:id, :created_at, :updated_at)
48
+ as_keyword_lookup = associations.each_with_object({}) do |e, acc|
49
+ keyword = e && e.options && e.options[:as] ?
50
+ e.options[:as] : e && e.options && e.options[:source] ?
51
+ e.options[:source] : false
52
+ if keyword
53
+ acc[e.klass.name.to_sym] = (keyword.to_s + '_id').to_sym
54
+ end
55
+ acc
56
+ end
57
+ parent_model_key = (model_hash[:model].name.to_s.underscore + '_id').to_sym
58
+ created = model_hash[:model].create(unique_attributes(attributes))
59
+ all_assocations = associations.select do |e|
60
+ lookup = e.klass.new.attributes.symbolize_keys
61
+ as_keyword_lookup[e.klass.name.to_sym] || lookup[parent_model_key]
62
+ end.map do |e|
63
+ existing.send(e.name)
64
+ end
65
+ all_assocations_with_klass = all_assocations.flat_map { |e| { model: e.klass, data: e } }.reject { |e| e[:data].length == 0 }
66
+ all_assocations_with_klass.each do |e|
67
+ model_name = e[:model].name.to_sym
68
+ polymorphic = as_keyword_lookup[model_name]
69
+ model_key = polymorphic || (model_hash[:model].name.to_s.underscore + '_id').to_sym
70
+ model_type = polymorphic || (model_hash[:model].name.to_s.underscore + '_type').to_sym
71
+ e[:data].each do |element|
72
+ attrs = element.attributes.symbolize_keys.without(:id, :created_at, :updated_at)
73
+ if (attrs[model_key] && !polymorphic) || (polymorphic && attrs[model_type] && attrs[model_key] && attrs[model_type] == e[:model].name)
74
+ attrs[model_key] = created.id
75
+ e[:model].create(unique_attributes(attrs))
76
+ end
77
+ end
78
+ end
79
+ created
80
+ end
81
+
82
+ def handle_pagination(model_hash, options_hash)
83
+ data = pagination_data_types(model_hash, options_hash)
84
+ count = pagination_count_type(model_hash, options_hash)
85
+ { data: data, count: count }
86
+ end
87
+
88
+ def handle_params(*args)
89
+ args.each_with_object({}) do |item, acc|
90
+ if item.is_a?(Array) && item.length > 1 && params[(item[0].to_s + '_id').to_sym]
91
+ acc[item[0].to_sym] = item[1].find_by(id: params[(item[0].to_s + '_id').to_sym])
92
+ elsif item.is_a?(String) && params[item.to_sym]
93
+ acc[item.to_sym] = params[item.to_sym]
94
+ elsif item.is_a?(Symbol) && params[item]
95
+ acc[item] = params[item]
96
+ end
97
+ end
98
+ end
99
+
100
+ def handle_show(model_hash, options_hash)
101
+ show_data_types(model_hash, options_hash)
102
+ end
103
+
104
+ def handle_create(model_hash, body_params)
105
+ model_hash[:model].create(body_params)
106
+ end
107
+
108
+ def handle_bulk_create(model_hash, body_array)
109
+ model_hash[:model].create(body_array)
110
+ end
111
+
112
+ def handle_update(model_hash, body_params)
113
+ model_hash[:model].where(id: params[:id]).update(body_params)
114
+ end
115
+
116
+ def handle_bulk_update(model_hash, body_hash)
117
+ model_hash[:model].update(body_hash.keys, body_hash.values)
118
+ end
119
+
120
+ def handle_destroy(model_hash)
121
+ model_hash[:model].where(id: params[:id]).delete_all
122
+ end
123
+
124
+ def handle_bulk_destroy(model_hash, id_array)
125
+ id_array.split(',').each do |id|
126
+ model_hash[:model].where(id: id).delete_all
127
+ end
128
+ id_array.length
129
+ end
130
+
131
+ def default_error_handling(data, status_code, opts = {})
132
+ if status_code == 204 && data
133
+ render json: data, status: status_code
134
+ elsif data.respond_to?(:errors) && data.errors.count > 0
135
+ render json: data.errors, status: 422
136
+ elsif data
137
+ if params[:include]
138
+ sorted_includes = sorted_include_array(params[:include])
139
+ includes = include_hash(nil, sorted_includes)
140
+
141
+ if data[:data] && data[:count]
142
+ json = data
143
+ json[:data] = data[:data].to_json(includes)
144
+ json[:data] = JSON.parse(json[:data])
145
+ else
146
+ json = data.to_json(includes)
147
+ json = JSON.parse(json)
148
+ end
149
+ elsif opts && opts[:include]
150
+ includes = include_hash(nil, opts[:include])
151
+
152
+ if data[:data] && data[:count]
153
+ json = data
154
+ json[:data] = data[:data].to_json(includes)
155
+ json[:data] = JSON.parse(json[:data])
156
+ else
157
+ json = data.to_json(includes)
158
+ json = JSON.parse(json)
159
+ end
160
+ else
161
+ json = data
162
+ end
163
+
164
+ if json.is_a?(Array)
165
+ response = json.map do |obj|
166
+ black_list_include_keys({}, obj)
167
+ end
168
+ elsif json[:data] && json[:count]
169
+ response = json
170
+ response[:data] = json[:data].map do |ele|
171
+ black_list_include_keys({}, ele)
172
+ end
173
+ else
174
+ response = black_list_include_keys({}, json)
175
+ end
176
+
177
+ render json: response, status: status_code
178
+ else
179
+ render json: { error: 'Bad Request' }, status: 400
180
+ end
181
+ end
182
+
183
+ private
184
+
185
+ def csv_to_json
186
+ csv_file = params[:file]
187
+ text = File.read(csv_file.tempfile)
188
+ text = text.gsub! "\r", ''
189
+ lines = text.split("\n")
190
+ keys = lines[0].split(',')
191
+ lines.shift
192
+ rows = lines
193
+
194
+ array = []
195
+ rows.each do |ln|
196
+ vals = ln.split(',')
197
+ hash = {}
198
+ keys.each_with_index do |key, index|
199
+ hash[key.to_sym] = vals[index]
200
+ end
201
+ array.push(hash)
202
+ end
203
+
204
+ array
205
+ end
206
+
207
+ def unique_attributes hash
208
+ if hash[:key]
209
+ hash[:key] = append_copy_stamp(hash[:key].to_s)
210
+ end
211
+ hash
212
+ end
213
+
214
+ def append_copy_stamp str
215
+ str + '_copy_' + DateTime.now.to_s.split('-').join('').split(':').join('').split('T').join('')
216
+ end
217
+
218
+ def pagination_utils
219
+ limit = params[:limit] || 25
220
+ offset = limit * (params[:offset] ? params[:offset].to_i : 0)
221
+ { limit: limit, offset: offset }
222
+ end
223
+
224
+ def pagination_data_types(model_hash, options_hash)
225
+ data = []
226
+
227
+ options_hash ||= {}
228
+
229
+ order = params[:order] || options_hash[:order] || nil
230
+ where = convert_where_hash(params[:where]) || options_hash[:where] || nil
231
+ like = convert_like_string(params[:like]) || options_hash[:like] || nil
232
+ includes = convert_include_array(params[:include]) || options_hash[:include] || nil
233
+
234
+ data = model_hash[:model].limit(pagination_utils[:limit]).offset(pagination_utils[:offset])
235
+ data = data.where(where) if where
236
+ data = data.where(*like) if like
237
+ data = data.includes(*includes) if includes
238
+ data = data.order(order) if order
239
+
240
+ data.all
241
+ end
242
+
243
+ def pagination_count_type(model_hash, options_hash)
244
+ options_hash ||= {}
245
+ where = options_hash[:where] || convert_where_hash(params[:where]) || nil
246
+ count = 0
247
+ count = if where
248
+ model_hash[:model].where(where).count
249
+ else
250
+ model_hash[:model].count
251
+ end
252
+ count
253
+ end
254
+
255
+ def show_data_types(model_hash, options_hash)
256
+ data = {}
257
+ id = params[:id]
258
+
259
+ options_hash ||= {}
260
+
261
+ where = convert_where_hash(params[:where]) || options_hash[:where] || nil
262
+ includes = convert_include_array(params[:include]) || options_hash[:include] || nil
263
+
264
+ includes = includes.is_a?(Array) ? includes : [includes]
265
+
266
+ data = if id.nil?
267
+ {}
268
+ elsif options_hash.nil?
269
+ model_hash[:model].find(id)
270
+ elsif includes && where
271
+ model_hash[:model].where(where).includes(*includes).first
272
+ elsif includes
273
+ model_hash[:model].includes(*includes).find(id)
274
+ elsif where
275
+ model_hash[:model].where(where).first
276
+ else
277
+ model_hash[:model].find(id)
278
+ end
279
+ data
280
+ end
281
+
282
+ def convert_like_string(str)
283
+ if str.nil?
284
+ nil
285
+ else
286
+ where_str = ''
287
+ types = str.split(',')
288
+ leng = types.length
289
+ index = 0
290
+
291
+ type_args = types.map do |e|
292
+ key, val = e.split(':')
293
+ index += 1
294
+ where_str += "#{key} LIKE ?"
295
+ where_str += ' and ' if index != leng
296
+
297
+ "%#{val}%"
298
+ end
299
+
300
+ [where_str].concat(type_args)
301
+ end
302
+ end
303
+
304
+ def convert_where_hash(str)
305
+ if str.nil?
306
+ nil
307
+ else
308
+ str.split(',').each_with_object ({}) do |item, acc|
309
+ key, val = item.split(':')
310
+ acc[key] = dynamic_types(val)
311
+ end
312
+ end
313
+ end
314
+
315
+ def dynamic_types(str)
316
+ if str == 'true'
317
+ true
318
+ elsif str == 'false'
319
+ false
320
+ elsif is_number?(str)
321
+ str.to_i
322
+ else
323
+ str
324
+ end
325
+ end
326
+
327
+ def is_number?(string)
328
+ true if Float(string)
329
+ rescue StandardError
330
+ false
331
+ end
332
+
333
+ def convert_include_array(str)
334
+ if str.nil?
335
+ nil
336
+ else
337
+ str.split(',').each_with_object ([]) do |item, acc|
338
+ array = item.split(':')
339
+ if array.length > 1
340
+ hash = hash_depth({}, array)
341
+ acc.push(hash)
342
+ else
343
+ key = array[0]
344
+ acc.push(key.to_sym)
345
+ end
346
+ end
347
+ end
348
+ end
349
+
350
+ def hash_depth(acc, array)
351
+ if array.length > 2
352
+ key = array.shift
353
+ acc[key.to_sym] = hash_depth({}, array)
354
+ else
355
+ key, val = array
356
+ acc[key.to_sym] = val
357
+ end
358
+ acc
359
+ end
360
+
361
+ def sorted_include_array(str)
362
+ array = convert_include_array(str)
363
+ array.sort_by { |a| [(a.is_a?(Hash) ? 1 : 0), a] }
364
+ end
365
+
366
+ def include_hash(data, hashOrArray)
367
+ data ||= { include: [] }
368
+
369
+ if hashOrArray.is_a?(Hash)
370
+ hashOrArray.each do |key, val|
371
+ hash = {}
372
+ hash[key.to_sym] = include_hash(nil, val)
373
+ data[:include].push(hash)
374
+ end
375
+ elsif hashOrArray.is_a?(Array)
376
+ hashOrArray.each { |element| include_hash(data, element) }
377
+ else
378
+ data[:include].push(hashOrArray)
379
+ end
380
+
381
+ data
382
+ end
383
+
384
+ # Black List all keys for any response
385
+ # that you do not want in the response
386
+ def black_list
387
+ {
388
+ password: true,
389
+ password_digest: true,
390
+ reset_password_token: true
391
+ }
392
+ end
393
+
394
+ def black_list_include_keys(acc, collection)
395
+ collection = collection.as_json
396
+
397
+ collection.each do |k, e|
398
+ if e.is_a?(Hash)
399
+ acc[k] = black_list_include_keys({}, e)
400
+ elsif e.is_a?(Array)
401
+ acc[k] = e.map do |element|
402
+ black_list_include_keys({}, element)
403
+ end
404
+ elsif !black_list[k.to_sym]
405
+ acc[k] = e
406
+ end
407
+ end
408
+
409
+ acc
410
+ end
411
+ end