rails_on_rails 0.0.9 → 0.0.17

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36958c631c1d24258e9702004ed3508e01cbb96fc73e0a8fc438f144e9e61c3c
4
- data.tar.gz: d06ab2bf6e5dcc635cd1b5a9c877456a327cac3d559a2a4e15b7a6c5a9105982
3
+ metadata.gz: eb3dbb175a9306c55ce2219b84038c0dfbdf4b79b7a3a97f985ec221622ae6da
4
+ data.tar.gz: 7f2f855bfa4447c615c6320e37cd8139e9fb14b69a1ca14f3bd6f44c2f6ab6a7
5
5
  SHA512:
6
- metadata.gz: 297087d5e99077cdb55c44498b2e71dded0ceab7fc6d4e50a0de308d08ba9407fc300c85885dd9c8002f08abaa1e58df3f3bb4c04ac9e1d749624a8f713e9659
7
- data.tar.gz: a0de593f1c62b04e9f91a7d076f5dd22ca74188ba6407bcc34ef056ef979e376cca6554202845e0b85a8e6540e28de2161e5a86ea5cc97294e5d2d57907ce918
6
+ metadata.gz: 5835c6bfa33b804955685fdc3793c493c97cf81d636069229af555d44cfe0f5831e9b3b0f7903db80a5441ca20b0d29d4966509f6f64673ab10b73ac80d494e4
7
+ data.tar.gz: a23a9adcc55b146c9660b35a8034a0c08c0b5fe80539c12427df83b99151c27720f4ab5ca8419dd1901ab226bbc686368a4684cafc3a782842f402fcfc7e6196
data/README.md CHANGED
@@ -40,7 +40,27 @@ In your `config/routes.rb` add this route for your documentation page
40
40
  resources :documentation, only: :index
41
41
  ```
42
42
 
43
+ You are now free to run the `rails_on_rails model` command which has the same paramters as `rails g model`
44
+ But instead generates a controller for your model with unit tests to test the fully operationality controller after you declare your routes
45
+ and update a model assocations.
46
+
47
+ ## CLI Commands
48
+ Rails on Rails: Command Line Interface to make your life easier.
49
+ => The Rails on Rails command is 'rails_on_rails'. To blast this project into the fifth dimension.
50
+ => Use '--help' or '-h on any of the commands listed below for more details.
51
+
52
+ List of commands:
53
+ => i - Setup your initial Rails on Rails project
54
+ => init - Setup your initial Rails on Rails project
55
+ => model - Generate a new model migration, fully operational controller, and unit tests for that controller
56
+ => test:seeder - Generate a test seeder for your model
57
+ => test:spec - Generate a test spec for your controller
58
+
43
59
  ## Generate your CRUD Operations for Index, Show, Create, Update, & Destroy
60
+ The `rails_on_rails model` command which has the same paramters as `rails g model`
61
+ But instead generates a controller for your model with unit tests to test the fully operationality controller after you declare your routes
62
+ and update a model assocations.
63
+
44
64
  ```
45
65
  rails_on_rails model <ModelName> <*rails-model-generate-parameters>
46
66
  ```
@@ -86,7 +106,7 @@ params[:bulk] = { 1 => { name: 'Luke Skywalker' }, 2 => { name: 'Darth Vader' }
86
106
 
87
107
  #### Declaring bonus methods in `config/routes.rb`
88
108
  ```
89
- rsources :user, except: [:edit, :new] do
109
+ resources :user, except: [:edit, :new] do
90
110
  collection do
91
111
  post 'duplicate'
92
112
  post 'bulk_create'
@@ -104,7 +124,7 @@ Also setup your file uploads (multipart) to be independent of erb templating
104
124
  For custom headers that you may add in the future update the `lib/api_documentation.rake`
105
125
  Line 48: is where you can update the needed headers for your axios ajax requests.
106
126
  ```
107
- var csrfHeader = { headers: { 'X-CSRF-Token': null, 'Accept': 'application/json', 'Content-Type': 'application/json' } };
127
+ var csrfHeader = { headers: { 'X-CSRF-Token': null } };
108
128
  ```
109
129
 
110
130
  By default only json formats are unaccept for RESTful API JSON formatting
@@ -147,3 +167,29 @@ Under the 'Param' title
147
167
  This dropdown is only used for file uploads by default they are set to JSON
148
168
  Use 'Form Data' for file uploads
149
169
 
170
+ ## RSpec Unit Tests
171
+
172
+ ### Authenicated Routes
173
+ In your support file there is a called auth_helper. auth_helper.rb contains a login method.
174
+ You can enter your own login endpoint here and pass in any needed params with email and password as an example
175
+ so you can test authenicated routes with a JSON Web Token in your headers
176
+
177
+ ### Test Database Seeders
178
+ The database seeders that are generated are a template that work for most types of schemas.
179
+ For custom schemas that may have unique constraints or unique behaviors. You can set a conditional to
180
+ search for the unique column name and set a value for it to satsify your unique needs.
181
+
182
+ These seeders are only for your test database with randomized data from faker
183
+
184
+ ### Controller Specs
185
+ The controller spec has tests for the generated controller. After you declare your routes, you can restrict the
186
+ tests for the routes you declare. The config_routes hash at the top will toggle those tests on or off because on what you set.
187
+ By default all tests are true.
188
+ Tests only cover basic CRUD resources: index, show, create, update, and destroy
189
+
190
+ ## GlobalController
191
+ There are many helpful util functions in the global controller at your deposal.
192
+ You can inherit the GlobalController instead of ApplicationController for your new controllers.
193
+ This gives you both the default Rails controller functionality and all these utils
194
+
195
+ In the autogeneration we utilize inheritance as much as posssible for cleaner controllers
@@ -1,11 +1,7 @@
1
1
  class {{ MODEL }}Controller < GlobalController
2
2
 
3
3
  def model
4
- { model: {{ MODEL }} }
5
- end
6
-
7
- def default_options
8
- {}
4
+ {{ MODEL }}
9
5
  end
10
6
 
11
7
  def render_params
@@ -16,9 +16,17 @@ RSpec.describe {{ MODEL }}Controller, type: :request do
16
16
  created = nil
17
17
  created_2 = nil
18
18
 
19
+ before :all do
20
+ @user = User.first || create(:user_create)
21
+ @headers = {}.merge(login_user)
22
+ end
23
+
19
24
  blacklist = {}
20
25
  associations = get_associations({{ MODEL }}).reject { |e| blacklist[e.to_sym] }
21
26
 
27
+ count = {{ MODEL }}.count
28
+
29
+ pagination = pagination_querystring_checks(count)
22
30
  includes = include_querystrings(associations)
23
31
  wheres = where_querystrings([
24
32
  { id: 1 },
@@ -33,6 +41,7 @@ RSpec.describe {{ MODEL }}Controller, type: :request do
33
41
  ])
34
42
 
35
43
  pagination_querystrings = concat_all_with_default([
44
+ pagination,
36
45
  includes,
37
46
  wheres,
38
47
  likes,
@@ -45,6 +54,16 @@ RSpec.describe {{ MODEL }}Controller, type: :request do
45
54
 
46
55
  if config_routes[:index]
47
56
  pagination_querystrings.each do |val|
57
+ querys = parse_qs_value(val)
58
+
59
+ if querys[:offset] && querys[:limit]
60
+ it "Pagination check count" do
61
+ get "#{route_name}?#{val}", headers: @headers
62
+ @resp = JSON.parse(response.body)
63
+ expect(@resp["data"].length).to be <= querys[:limit].to_i
64
+ end
65
+ end
66
+
48
67
  it "No Auth: Index: No 500 errors" do
49
68
  get "#{route_name}?#{val}"
50
69
  expect(response.status).to be < 500
@@ -87,18 +106,18 @@ RSpec.describe {{ MODEL }}Controller, type: :request do
87
106
 
88
107
  if config_routes[:create]
89
108
  it "No Auth: Create: No 500 errors" do
90
- post "#{route_name}", params: {}
109
+ post "#{route_name}", params: { }
91
110
  expect(response.status).to be < 500
92
111
  end
93
112
 
94
113
  it "Create: No 500 errors" do
95
- post "#{route_name}", params: {}, headers: @headers
114
+ post "#{route_name}", params: { }, headers: @headers
96
115
  created = JSON.parse(response.body)
97
116
  expect(response.status).to be < 500
98
117
  end
99
118
 
100
119
  it "Create: Body Format" do
101
- post "#{route_name}", params: {}, headers: @headers
120
+ post "#{route_name}", params: { }, headers: @headers
102
121
  @resp = JSON.parse(response.body)
103
122
  created_2 = @resp
104
123
  @check = @resp.is_a?(Hash)
@@ -1,12 +1,13 @@
1
1
  require_relative '../../utils/rails_methods.rb'
2
2
 
3
- MODEL_NAME = ARGV[0]
3
+ SKIP_MODEL = true?(ARGV[0])
4
+ MODEL_NAME = ARGV[1]
4
5
  MODEL_PARAMS = ARGV
5
- MODEL_PARAMS.shift
6
+ 2.times { |i| MODEL_PARAMS.shift }
6
7
 
7
8
  MODEL_SNAKE_CASE = MODEL_NAME.underscore
8
9
 
9
- system("bash #{__dir__}/../bash_commands/generate_model.sh #{MODEL_NAME} #{MODEL_PARAMS.join(' ')}")
10
+ system("bash #{__dir__}/../bash_commands/generate_model.sh #{MODEL_NAME} #{MODEL_PARAMS.join(' ')}") if !SKIP_MODEL
10
11
  system("bash #{__dir__}/../bash_commands/generate_controller.sh #{MODEL_SNAKE_CASE} --no-assets --no-view-specs --no-helper")
11
12
 
12
13
  controller_path = "#{Dir.pwd}/app/controllers/#{MODEL_SNAKE_CASE}_controller.rb"
@@ -18,7 +19,12 @@ length = MODEL_PARAMS.length
18
19
  PARAMS_STRING = MODEL_PARAMS.map do |item|
19
20
  str = ''
20
21
  key, type = item.split(':')
21
- str += type == 'references' ? "[:#{key}, " + "#{key.split('_').map(&:capitalize!).join('')}]" : ":#{key}"
22
+ polymorphoric = false
23
+ if /references/.match?(type)
24
+ polymorphoric = /poly/.match?(type)
25
+ type = 'references'
26
+ end
27
+ str += type == 'references' ? "{ key: :#{key}, model: #{key.split('_').map(&:capitalize!).join('')} }" : ":#{key}"
22
28
  str
23
29
  end.join(', ')
24
30
 
@@ -2,13 +2,24 @@ require 'date'
2
2
  require_relative '../../utils/rails_methods.rb'
3
3
 
4
4
  MODEL_NAME = ARGV[0]
5
+ MODEL_PARAMS = ARGV
5
6
  MODEL_SNAKE_CASE = MODEL_NAME.underscore
7
+ MODEL_PARAMS.shift
6
8
 
7
9
  TIMESTAMP = DateTime.now.to_s.split('-').join('').split(':').join('').split('T').join('').slice(0, 14)
8
10
 
9
11
  controller_path = Dir.pwd + "/db/seedfiles/test/#{TIMESTAMP}_#{MODEL_SNAKE_CASE}.rb"
10
12
  template_path = __dir__ + "/../generator_templates/test_seeder.txt"
11
13
 
14
+ MODEL_PARAMS.each do |item|
15
+ key, type = item.split(':')
16
+ if /references/.match?(type)
17
+ polymorphoric = /poly/.match?(type)
18
+ type = 'references'
19
+ puts "\nPolymorphic associations will require extra safety checks in seeder file.\nCheck readme for more details."
20
+ end
21
+ end
22
+
12
23
  read_file = File.open(template_path, 'r:UTF-8', &:read)
13
24
 
14
25
  read_file = read_file.gsub! '{{ MODEL }}', MODEL_NAME
@@ -9,7 +9,7 @@ namespace :api_documentation do
9
9
  { route: route, method: method }
10
10
  end.reject do |e|
11
11
  first_path = e[:route].split('/')[1].to_s
12
- first_path == 'rails' || first_path == 'assets' || first_path == 'docs' || first_path == 'cable'
12
+ first_path == 'rails' || first_path == 'assets' || first_path == 'documentation' || first_path == 'cable'
13
13
  end
14
14
 
15
15
  javascriptString = ''
@@ -1,73 +1,89 @@
1
1
  def login_user
2
- # Update your own login authenication endpoint
3
- post '/login', params: { email: @user[:email], password: @user[:password] }
4
- auth_header = response.headers['Authorization']
5
- { Authorization: auth_header }
2
+ # Update your own login authenication endpoint
3
+ post '/login', params: { email: @user[:email], password: @user[:password] }
4
+ auth_header = response.headers['Authorization']
5
+ { Authorization: auth_header }
6
+ end
7
+
8
+ def objects_list_to_querystring qs, object
9
+ querystring = "#{qs}="
10
+ v = object.map { |key, val| "#{key.to_s}:#{val.to_s}" }.join(',')
11
+ querystring + v
12
+ end
13
+
14
+ def parse_qs_value string
15
+ string.split('&').each_with_object({}) do |e, acc|
16
+ key, val = e.split('=')
17
+ acc[key.to_sym] = val
18
+ acc
6
19
  end
7
-
8
- def objects_list_to_querystring qs, object
9
- querystring = "#{qs}="
10
- v = object.map { |key, val| "#{key.to_s}:#{val.to_s}" }.join(',')
11
- querystring + v
12
- end
13
-
14
- def string_list_to_querystring qs, array
15
- array = array.is_a?(Array) ? array : [array]
16
- querystring = "#{qs}="
17
- v = array.join(',')
18
- querystring + v
19
- end
20
-
21
- def string_permuations array
22
- index = 0
23
- perms = array.each_with_object([]) do |e, acc|
24
- index += 1
25
- acc.push(array.slice(0, index))
26
- end
27
- order = perms.each_with_object([]) do |e, acc|
28
- acc.push(e.permutation.to_a.map { |d| d.join(',') })
29
- end
30
- order.each_with_object([]) do |e, acc|
31
- acc = acc.concat(e)
32
- acc
33
- end
34
- end
35
-
36
- def order_options array
37
- array.each_with_object([]) do |e, acc|
38
- acc.push("#{e} ASC")
39
- acc.push("#{e} DESC")
40
- end
20
+ end
21
+
22
+ def pagination_querystring_checks count, init_limit = 3
23
+ if count < init_limit
24
+ ["offset=0&limit=1"]
25
+ else
26
+ limit = (count/init_limit.to_f).ceil
27
+ init_limit.times.map { |i| "offset=#{i}&limit=#{limit}" }
28
+ end
29
+ end
30
+
31
+ def string_list_to_querystring qs, array
32
+ array = array.is_a?(Array) ? array : [array]
33
+ querystring = "#{qs}="
34
+ v = array.join(',')
35
+ querystring + v
36
+ end
37
+
38
+ def string_permuations array
39
+ index = 0
40
+ perms = array.each_with_object([]) do |e, acc|
41
+ index += 1
42
+ acc.push(array.slice(0, index))
41
43
  end
42
-
43
- def concat_all_with_default array
44
- array.each_with_object(['']) do |e, acc|
45
- acc = acc.concat(e)
46
- acc
47
- end
44
+ order = perms.each_with_object([]) do |e, acc|
45
+ acc.push(e.permutation.to_a.map { |d| d.join(',') })
48
46
  end
49
-
50
- def include_querystrings array
51
- [''].concat(string_permuations(array).map { |e| string_list_to_querystring('include', e) })
47
+ order.each_with_object([]) do |e, acc|
48
+ acc = acc.concat(e)
49
+ acc
52
50
  end
53
-
54
- def where_querystrings array
55
- [''].concat(array.map { |e| objects_list_to_querystring('where', e) })
51
+ end
52
+
53
+ def order_options array
54
+ array.each_with_object([]) do |e, acc|
55
+ acc.push("#{e} ASC")
56
+ acc.push("#{e} DESC")
56
57
  end
57
-
58
- def like_querystrings array
59
- [''].concat(array.map { |e| objects_list_to_querystring('like', e) })
58
+ end
59
+
60
+ def concat_all_with_default array
61
+ array.each_with_object(['']) do |e, acc|
62
+ acc = acc.concat(e)
63
+ acc
60
64
  end
65
+ end
66
+
67
+ def include_querystrings array
68
+ [''].concat(string_permuations(array).map { |e| string_list_to_querystring('include', e) })
69
+ end
70
+
71
+ def where_querystrings array
72
+ [''].concat(array.map { |e| objects_list_to_querystring('where', e) })
73
+ end
74
+
75
+ def like_querystrings array
76
+ [''].concat(array.map { |e| objects_list_to_querystring('like', e) })
77
+ end
78
+
79
+ def order_querystrings array
80
+ [''].concat(array.map { |e| string_list_to_querystring('like', e) })
81
+ end
82
+
83
+ def get_associations model
84
+ model.reflect_on_all_associations.map(&:name)
85
+ .reject do |e|
86
+ model.reflect_on_association(e).is_a?(ActiveRecord::Reflection::ThroughReflection)
87
+ end.map { |e| e.to_s }
88
+ end
61
89
 
62
- def order_querystrings array
63
- [''].concat(array.map { |e| string_list_to_querystring('like', e) })
64
- end
65
-
66
- def get_associations model
67
- model.reflect_on_all_associations.map(&:name)
68
- .reject do |e|
69
- model.reflect_on_association(e).is_a?(ActiveRecord::Reflection::ThroughReflection)
70
- end.map { |e| e.to_s }
71
- end
72
-
73
-
@@ -15,73 +15,41 @@ class DocumentationController < ApplicationController
15
15
  { method: method, route: route, verb: verb }
16
16
  end.reject do |e|
17
17
  first_path = e[:route].split('/')[1].to_s
18
- first_path == 'rails' || first_path == 'assets' || first_path == 'documentation' || first_path == 'cable'
18
+ first_path == 'rails' || first_path == 'assets' || first_path == 'docs' || first_path == 'cable'
19
19
  end
20
20
 
21
- model_lookup = {}
22
21
  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
22
+ model_lookup = models_lookup
29
23
 
30
24
  rails_routes.map do |r|
31
- camelCased = ''
32
- camelCased += r[:method].downcase
25
+ camel_cased = r[:method].downcase
33
26
  method = r[:method]
34
27
  route = r[:route]
35
28
  verb = r[:verb]
36
29
 
37
- tempRoute = r[:route]
30
+ temp_route = r[:route]
38
31
  params = []
39
32
 
40
- tempRoute.split('/').each do |line|
33
+ temp_route.split('/').each do |line|
41
34
  cap = line[0]
42
35
  if cap == ':'
43
36
  line[0] = ''
44
37
  params.push(line)
45
- ln = ''
46
- line.split('-').each do |l|
47
- ln += l.capitalize
48
- end
49
- camelCased += ln.capitalize + 'Param'
38
+ camel_cased += route_name_convention(line).capitalize + 'Param'
50
39
  else
51
- ln = ''
52
- line.split('-').each do |l|
53
- ln += l.capitalize
54
- end
55
- camelCased += ln.capitalize
40
+ camel_cased += route_name_convention(line).capitalize
56
41
  end
57
42
  end
58
43
 
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
44
+ color = color_lookup[method.upcase.to_sym] || 'light'
45
+ route_header = "bg-#{color} text-white"
46
+ submit_button_color = "btn btn-outline-#{color}"
77
47
 
78
48
  route_path = route.split('/')
79
49
 
80
50
  route_models = route_path.each_with_object([]) do |e, acc|
81
51
  modal_name = e.underscore.singularize
82
- if model_lookup[modal_name.to_sym]
83
- acc.push(modal_name.classify.constantize)
84
- end
52
+ acc.push(modal_name.classify.constantize) if model_lookup[modal_name.to_sym]
85
53
  acc
86
54
  end
87
55
 
@@ -106,18 +74,18 @@ class DocumentationController < ApplicationController
106
74
  body_params_description: body_params_description,
107
75
  }
108
76
  end
77
+
78
+ offset_description = "Offset Querystring option is the page you are going to retrieve for pagination \n"
79
+ limit_description = "Limit Querystring option is the page count for pagination \n"
109
80
  include_description ||= description_lookup[model.name.to_sym][:include_description]
110
81
  where_description ||= description_lookup[model.name.to_sym][:where_description]
111
82
  like_description ||= description_lookup[model.name.to_sym][:like_description]
112
83
  order_description ||= description_lookup[model.name.to_sym][:order_description]
113
84
  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
85
 
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'
86
+ description = description.concat([where_description, include_description]) if (verb == 'index' || verb == 'show')
87
+ description = description.concat([order_description, like_description, limit_description, offset_description]) if verb == 'index'
88
+ description.push(body_params_description) if verb == 'create' || verb == 'update'
121
89
  end
122
90
 
123
91
  description.push("Not available") if description.length == 0
@@ -127,13 +95,38 @@ class DocumentationController < ApplicationController
127
95
  route: route,
128
96
  params: params,
129
97
  middleware: ['Not Available'],
130
- routeHeader: routeHeader,
131
- submitButtonColor: submitButtonColor,
132
- camelCased: camelCased,
133
- allowParams: tempRoute.split(':').length > 1,
98
+ routeHeader: route_header,
99
+ submitButtonColor: submit_button_color,
100
+ camelCased: camel_cased,
101
+ allowParams: temp_route.split(':').length > 1,
134
102
  allowBody: method == 'GET' ? false : method != 'DELETE',
135
103
  description: description,
136
104
  }
137
105
  end
138
106
  end
107
+
108
+ def route_name_convention line
109
+ line.split('-').inject('') { |acc, l| acc += l.capitalize }
110
+ end
111
+
112
+ def color_lookup
113
+ {
114
+ GET: 'success',
115
+ POST: 'info',
116
+ PUT: 'warning',
117
+ PATCH: 'warning',
118
+ DELETE: 'danger',
119
+ }
120
+ end
121
+
122
+ def models_lookup
123
+ model_lookup = {}
124
+ Dir.foreach("#{Rails.root}/app/models") do |model_path|
125
+ model, file_suffix = model_path.split('.')
126
+ if model && file_suffix
127
+ model_lookup[model.to_sym] = model.to_s
128
+ end
129
+ end
130
+ model_lookup
131
+ end
139
132
  end
@@ -1,73 +1,144 @@
1
1
  class GlobalController < ApplicationController
2
+ # Black List any of these keys from your response
3
+ def black_list
4
+ {
5
+ # response_key: true,
6
+ password: true,
7
+ password_digest: true,
8
+ reset_password_token: true
9
+ }
10
+ end
11
+
12
+ # Default declarations
13
+
14
+ def model
15
+ raise StandardError, 'Define a model'
16
+ end
17
+
18
+ def render_params
19
+ raise StandardError, 'Define your render_params'
20
+ end
21
+
22
+ def default_options
23
+ {}
24
+ end
25
+
26
+ # Resource Action Methods
27
+
2
28
  def index
3
- options = default_options || {}
4
- default_error_handling(handle_pagination(model, options), 200, options)
29
+ default_error_handling(handle_pagination, 200)
5
30
  end
6
31
 
7
32
  def show
8
- options = default_options || {}
9
- default_error_handling(handle_show(model, options), 200, options)
33
+ default_error_handling(handle_show, 200)
10
34
  end
11
35
 
12
36
  def create
13
- default_error_handling(handle_create(model, render_params), 201)
37
+ default_error_handling(handle_create(create_params), 201)
14
38
  end
15
39
 
16
40
  def bulk_create
17
- default_error_handling(handle_bulk_create(model, params[:bulk]), 201)
41
+ default_error_handling(handle_bulk_create(bulk_create_params), 201)
18
42
  end
19
43
 
20
44
  def bulk_csv_create
21
- default_error_handling(handle_bulk_create(model, csv_to_json), 201)
45
+ default_error_handling(handle_bulk_create(bulk_csv_create_params), 201)
22
46
  end
23
47
 
24
48
  def update
25
- default_error_handling(handle_update(model, render_params), 200)
49
+ data = handle_update(update_params)
50
+ default_error_handling(data[:data], data[:status])
26
51
  end
27
52
 
28
53
  def bulk_update
29
- default_error_handling(handle_bulk_update(model, params[:bulk]), 200)
54
+ default_error_handling(handle_bulk_update(bulk_update_params), 200)
30
55
  end
31
56
 
32
57
  def destroy
33
- default_error_handling(handle_destroy(model), 204)
58
+ default_error_handling(handle_destroy, 204)
34
59
  end
35
60
 
36
61
  def bulk_destroy
37
- default_error_handling(handle_bulk_destroy(model, params[:ids]), 204)
62
+ default_error_handling(handle_bulk_destroy(bulk_delete_params), 204)
38
63
  end
39
64
 
40
65
  def duplicate
41
- default_error_handling(handle_duplicate(model), 201)
66
+ default_error_handling(handle_duplicate, 201)
42
67
  end
43
68
 
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
69
+ # Custom params.permit methods for Global Controller
70
+
71
+ def handle_params(*args)
72
+ args.each_with_object({}) do |item, acc|
73
+ if item.is_a?(Hash) && !item.empty? && params[(item[:key].to_s + '_id').to_sym]
74
+ acc[item[:key].to_sym] = item[:model].find_by(id: params[(item[:key].to_s + '_id').to_sym])
75
+ elsif item.is_a?(String) && params[item.to_sym]
76
+ acc[item.to_sym] = params[item.to_sym]
77
+ elsif item.is_a?(Symbol) && params[item]
78
+ acc[item] = params[item]
79
+ end
80
+ end
81
+ end
82
+
83
+ def handle_bulk_parcode .
84
+ ams(*args)
85
+ params[:bulk].map do |element|
86
+ args.each_with_object({}) do |item, acc|
87
+ if item.is_a?(Hash) && !item.empty? && element[(item[:key].to_s + '_id').to_sym]
88
+ acc[item[:key].to_sym] = item[:model].find_by(id: element[(item[:key].to_s + '_id').to_sym])
89
+ elsif item.is_a?(String) && element[item.to_sym]
90
+ acc[item.to_sym] = element[item.to_sym]
91
+ elsif item.is_a?(Symbol) && element[item]
92
+ acc[item] = element[item]
93
+ end
54
94
  end
55
- acc
56
95
  end
57
- parent_model_key = (model_hash[:model].name.to_s.underscore + '_id').to_sym
58
- created = model_hash[:model].create(unique_attributes(attributes))
96
+ end
97
+
98
+ # Default param declarations
99
+
100
+ def create_params
101
+ render_params
102
+ end
103
+
104
+ def update_params
105
+ render_params
106
+ end
107
+
108
+ def bulk_create_params
109
+ params[:bulk]
110
+ end
111
+
112
+ def bulk_delete_params
113
+ params[:ids]
114
+ end
115
+
116
+ def bulk_csv_create_params
117
+ params[:csv_json]
118
+ end
119
+
120
+ # Handlers for Global Controller methods
121
+
122
+ def handle_duplicate(id = nil)
123
+ associations = model.reflect_on_all_associations.reject { |e| e.is_a?(ActiveRecord::Reflection::BelongsToReflection) }
124
+ existing = model.find_by(id: id || params[:id])
125
+ attributes = existing.attributes.symbolize_keys.without(:id, :created_at, :updated_at)
126
+
127
+ as_keyword_lookup = association_lookup(associations)
128
+ parent_model_key = (model.name.to_s.underscore + '_id').to_sym
129
+ created = model.create(unique_attributes(attributes))
130
+
59
131
  all_assocations = associations.select do |e|
60
132
  lookup = e.klass.new.attributes.symbolize_keys
61
133
  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 }
134
+ end.map { |e| existing.send(e.name) }
135
+
136
+ all_assocations_with_klass = all_assocations.flat_map { |e| { model: e.klass, data: e } }.reject { |e| e[:data].empty? }
66
137
  all_assocations_with_klass.each do |e|
67
138
  model_name = e[:model].name.to_sym
68
139
  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
140
+ model_key = polymorphic || (model.name.to_s.underscore + '_id').to_sym
141
+ model_type = polymorphic || (model.name.to_s.underscore + '_type').to_sym
71
142
  e[:data].each do |element|
72
143
  attrs = element.attributes.symbolize_keys.without(:id, :created_at, :updated_at)
73
144
  if (attrs[model_key] && !polymorphic) || (polymorphic && attrs[model_type] && attrs[model_key] && attrs[model_type] == e[:model].name)
@@ -79,102 +150,63 @@ class GlobalController < ApplicationController
79
150
  created
80
151
  end
81
152
 
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 }
153
+ def handle_pagination
154
+ { data: pagination_data_types, count: pagination_count_type }
86
155
  end
87
156
 
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
157
+ def handle_show
158
+ show_data_types
98
159
  end
99
160
 
100
- def handle_show(model_hash, options_hash)
101
- show_data_types(model_hash, options_hash)
161
+ def handle_create(body_params)
162
+ model.create(body_params)
102
163
  end
103
164
 
104
- def handle_create(model_hash, body_params)
105
- model_hash[:model].create(body_params)
165
+ def handle_bulk_create(body_array)
166
+ model.create(body_array)
106
167
  end
107
168
 
108
- def handle_bulk_create(model_hash, body_array)
109
- model_hash[:model].create(body_array)
169
+ def handle_update(body_params)
170
+ data = model.where(id: params[:id]).update(body_params)
171
+ if data.is_a?(Array) && !data.empty?
172
+ { status: 200, data: data.first }
173
+ else
174
+ { status: 400, data: { error: 'Bad Request' } }
175
+ end
110
176
  end
111
177
 
112
- def handle_update(model_hash, body_params)
113
- model_hash[:model].where(id: params[:id]).update(body_params)
178
+ def handle_bulk_update(body_hash)
179
+ model.update(body_hash.keys, body_hash.values)
114
180
  end
115
181
 
116
- def handle_bulk_update(model_hash, body_hash)
117
- model_hash[:model].update(body_hash.keys, body_hash.values)
182
+ def bulk_update_params
183
+ params[:bulk]
118
184
  end
119
185
 
120
- def handle_destroy(model_hash)
121
- model_hash[:model].where(id: params[:id]).delete_all
186
+ def handle_destroy
187
+ model.where(id: params[:id]).delete_all
122
188
  end
123
189
 
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
190
+ def handle_bulk_destroy(id_array)
191
+ id_array.split(',').map { |id| model.where(id: id).delete_all }.length
129
192
  end
130
193
 
131
- def default_error_handling(data, status_code, opts = {})
194
+ # Querystring Parsing & Error Handling
195
+
196
+ def default_error_handling(data, status_code)
132
197
  if status_code == 204 && data
133
198
  render json: data, status: status_code
134
199
  elsif data.respond_to?(:errors) && data.errors.count > 0
135
200
  render json: data.errors, status: 422
136
201
  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
202
+ if params[:include] || (default_options && default_options[:include])
203
+ includes = include_hash(nil, params[:include] ? sorted_include_array(params[:include]) : default_options[:include])
204
+ json = inject_includes(data, includes)
160
205
  else
161
206
  json = data
162
207
  end
163
208
 
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
209
+ render json: filter_json_response(json), status: status_code
178
210
  else
179
211
  render json: { error: 'Bad Request' }, status: 400
180
212
  end
@@ -182,56 +214,64 @@ class GlobalController < ApplicationController
182
214
 
183
215
  private
184
216
 
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)
217
+ def filter_json_response(json)
218
+ if json.is_a?(Array)
219
+ json.map { |obj| black_list_include_keys({}, obj) }
220
+ elsif json[:data] && json[:count]
221
+ response = json
222
+ response[:data] = json[:data].map { |obj| black_list_include_keys({}, obj) }
223
+ response
224
+ else
225
+ black_list_include_keys({}, json)
202
226
  end
227
+ end
203
228
 
204
- array
229
+ def association_lookup(associations)
230
+ associations.each_with_object({}) do |e, acc|
231
+ keyword = e.options && e.options[:as] ?
232
+ e.options[:as] : e.options && e.options[:source] ?
233
+ e.options[:source] : false
234
+ acc[e.klass.name.to_sym] = (keyword.to_s + '_id').to_sym if keyword
235
+ acc
236
+ end
205
237
  end
206
238
 
207
- def unique_attributes hash
208
- if hash[:key]
209
- hash[:key] = append_copy_stamp(hash[:key].to_s)
239
+ def inject_includes(data, includes)
240
+ if data[:data] && data[:count]
241
+ json = data
242
+ json[:data] = data[:data].to_json(includes)
243
+ json[:data] = JSON.parse(json[:data])
244
+ else
245
+ json = data.to_json(includes)
246
+ json = JSON.parse(json)
210
247
  end
248
+ json
249
+ end
250
+
251
+ def unique_attributes(hash)
252
+ hash[:key] = append_copy_stamp(hash[:key].to_s) if hash[:key]
211
253
  hash
212
254
  end
213
255
 
214
- def append_copy_stamp str
256
+ def append_copy_stamp(str)
215
257
  str + '_copy_' + DateTime.now.to_s.split('-').join('').split(':').join('').split('T').join('')
216
258
  end
217
259
 
218
260
  def pagination_utils
219
261
  limit = params[:limit] || 25
220
- offset = limit * (params[:offset] ? params[:offset].to_i : 0)
262
+ offset = limit.to_i * (params[:offset] ? params[:offset].to_i : 0)
221
263
  { limit: limit, offset: offset }
222
264
  end
223
265
 
224
- def pagination_data_types(model_hash, options_hash)
266
+ def pagination_data_types
225
267
  data = []
226
268
 
227
- options_hash ||= {}
269
+ order = params[:order] || default_options[:order] || nil
270
+ where = convert_where_hash(params[:where]) || default_options[:where] || nil
271
+ like = convert_like_string(params[:like]) || default_options[:like] || nil
272
+ includes = convert_include_array(params[:include]) || default_options[:include] || nil
228
273
 
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])
274
+ data = model.limit(pagination_utils[:limit]).offset(pagination_utils[:offset])
235
275
  data = data.where(where) if where
236
276
  data = data.where(*like) if like
237
277
  data = data.includes(*includes) if includes
@@ -240,43 +280,37 @@ class GlobalController < ApplicationController
240
280
  data.all
241
281
  end
242
282
 
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
283
+ def pagination_count_type
284
+ where = default_options[:where] || convert_where_hash(params[:where]) || nil
285
+ like = convert_like_string(params[:like]) || default_options[:like] || nil
286
+
287
+ data = model
288
+ data = model.where(where) if where
289
+ data = model.where(*like) if like
290
+
291
+ data.count
253
292
  end
254
293
 
255
- def show_data_types(model_hash, options_hash)
294
+ def show_data_types
256
295
  data = {}
257
296
  id = params[:id]
258
297
 
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
298
+ where = convert_where_hash(params[:where]) || default_options[:where] || nil
299
+ includes = convert_include_array(params[:include]) || default_options[:include] || nil
263
300
 
264
301
  includes = includes.is_a?(Array) ? includes : [includes]
265
302
 
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
303
+ if id.nil?
304
+ {}
305
+ elsif includes && where
306
+ model.where(where).includes(*includes).first
307
+ elsif includes
308
+ model.includes(*includes).find(id)
309
+ elsif where
310
+ model.where(where).first
311
+ else
312
+ model.find(id)
313
+ end
280
314
  end
281
315
 
282
316
  def convert_like_string(str)
@@ -305,7 +339,7 @@ class GlobalController < ApplicationController
305
339
  if str.nil?
306
340
  nil
307
341
  else
308
- str.split(',').each_with_object ({}) do |item, acc|
342
+ str.split(',').each_with_object({}) do |item, acc|
309
343
  key, val = item.split(':')
310
344
  acc[key] = dynamic_types(val)
311
345
  end
@@ -381,23 +415,13 @@ class GlobalController < ApplicationController
381
415
  data
382
416
  end
383
417
 
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
418
  def black_list_include_keys(acc, collection)
395
419
  collection = collection.as_json
396
420
 
397
421
  collection.each do |k, e|
398
422
  if e.is_a?(Hash)
399
423
  acc[k] = black_list_include_keys({}, e)
400
- elsif e.is_a?(Array)
424
+ elsif e.is_a?(Array) && e.all? { |item| item.is_a? Hash }
401
425
  acc[k] = e.map do |element|
402
426
  black_list_include_keys({}, element)
403
427
  end
@@ -27,7 +27,7 @@ List of commands:
27
27
  "This command has no parameters but has optional commands. Optional flag options are:
28
28
  --skip=<options-separated-by-a-comma-for-multiple-options>
29
29
  Skip Options:
30
- #{opts.join("\\n")}
30
+ #{opts.join("\n")}
31
31
  "
32
32
  end
33
33
 
@@ -37,6 +37,7 @@ List of commands:
37
37
 
38
38
  Use the parameters in 'rails generate model' to generate your CRUD operators.
39
39
  Optional flag options are:
40
+ --no-model => Do not generate a model in this command
40
41
  --skip=<options-separated-by-a-comma-for-multiple-options>
41
42
  Skip Options:
42
43
  #{opts.join("\n")}
@@ -2,7 +2,7 @@ class CLIOptions
2
2
  class <<self
3
3
 
4
4
  def gem_version
5
- "0.0.9"
5
+ "0.0.17"
6
6
  end
7
7
 
8
8
  def ascii_art
@@ -76,6 +76,7 @@ class Commander < CLIOptions
76
76
  puts "Generating model and controller for #{args[2]}..."
77
77
  all_action_methods = [:generate_test_seeder, :generate_test_rspec]
78
78
  skip_options = options[:skip]
79
+ no_model = !options[:"no-model"].nil?
79
80
  skipping = {}
80
81
  if skip_options
81
82
  skipping = skip_options.split(',').flat_map { |e| skip_model_controller_options[e.to_sym] }.select { |e| e.nil? == false }.each_with_object({}) do |e, acc|
@@ -85,7 +86,7 @@ class Commander < CLIOptions
85
86
  end
86
87
  all_action_methods = all_action_methods.reject { |e| skipping[e].nil? == false }
87
88
  path = __dir__ + '/autogenerators/handlers/controller.rb'
88
- system("ruby #{path} #{arguments.join(' ')}")
89
+ system("ruby #{path} #{no_model} #{arguments.join(' ')}")
89
90
  all_action_methods.each { |e| method(e).call(*args) }
90
91
  puts "Finished generating model and controller for #{args[2]}"
91
92
  end
@@ -6,4 +6,8 @@ class String
6
6
  tr("-", "_").
7
7
  downcase
8
8
  end
9
+ end
10
+
11
+ def true?(obj)
12
+ obj.to_s == "true"
9
13
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_on_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aidan Miles