kriangle 0.1.0

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 (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/CODE_OF_CONDUCT.md +74 -0
  4. data/Gemfile +30 -0
  5. data/Gemfile.lock +216 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +307 -0
  8. data/Rakefile +4 -0
  9. data/bin/console +15 -0
  10. data/bin/setup +8 -0
  11. data/kriangle.gemspec +42 -0
  12. data/lib/generators/kriangle/generator_helpers.rb +95 -0
  13. data/lib/generators/kriangle/install_generator.rb +149 -0
  14. data/lib/generators/kriangle/module_generator.rb +273 -0
  15. data/lib/generators/kriangle/templates/active_serializer.rb +9 -0
  16. data/lib/generators/kriangle/templates/application_record.rb +9 -0
  17. data/lib/generators/kriangle/templates/auth.rb +138 -0
  18. data/lib/generators/kriangle/templates/authentication.rb +7 -0
  19. data/lib/generators/kriangle/templates/authenticator.rb +70 -0
  20. data/lib/generators/kriangle/templates/avatar.rb +5 -0
  21. data/lib/generators/kriangle/templates/avatar_uploader.rb +49 -0
  22. data/lib/generators/kriangle/templates/base.rb +6 -0
  23. data/lib/generators/kriangle/templates/controller.rb +241 -0
  24. data/lib/generators/kriangle/templates/controllers.rb +21 -0
  25. data/lib/generators/kriangle/templates/counter_cache_migration.rb +5 -0
  26. data/lib/generators/kriangle/templates/create_authentications.rb +10 -0
  27. data/lib/generators/kriangle/templates/create_avatars.rb +10 -0
  28. data/lib/generators/kriangle/templates/create_users.rb.erb +48 -0
  29. data/lib/generators/kriangle/templates/custom_description.rb +30 -0
  30. data/lib/generators/kriangle/templates/defaults.rb +29 -0
  31. data/lib/generators/kriangle/templates/kriangle.rb +5 -0
  32. data/lib/generators/kriangle/templates/model.rb +33 -0
  33. data/lib/generators/kriangle/templates/module_migration.rb +33 -0
  34. data/lib/generators/kriangle/templates/responder.rb +170 -0
  35. data/lib/generators/kriangle/templates/serializer.rb +35 -0
  36. data/lib/generators/kriangle/templates/swagger.rb +8 -0
  37. data/lib/generators/kriangle/templates/user.rb +35 -0
  38. data/lib/kriangle.rb +36 -0
  39. data/lib/kriangle/version.rb +5 -0
  40. metadata +355 -0
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ActiveSerializer < ActiveModel::Serializer
4
+ <%- if custom_orm == 'Mongoid' -%>
5
+ def id
6
+ object._id.to_s
7
+ end
8
+ <%- end -%>
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% if custom_orm == 'Mongoid' %>
4
+ class ApplicationRecord
5
+ <% else %>
6
+ class ApplicationRecord < ActiveRecord::Base
7
+ self.abstract_class = true
8
+ <% end %>
9
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Api
4
+ module <%= wrapper.capitalize %>
5
+ class <%= controller_path %> < Grape::API
6
+ include Api::<%= wrapper.capitalize %>::Defaults
7
+
8
+ resource :<%= controller_path.underscore %> do
9
+ include Api::CustomDescription
10
+
11
+ desc "Register new <%= underscored_user_class %>"
12
+ params do
13
+ requires :<%= underscored_user_class %>, type: Hash do
14
+ requires :email, type: String, desc: "Email address"
15
+ requires :password, type: String, desc: "Password"
16
+ requires :password_confirmation, type: String, desc: "Password Confirmation"
17
+ # Additional(optional) parameters
18
+ <%- for attribute in model_attributes -%>
19
+ <% next if attribute.name == 'email' %>
20
+ <%- if attribute.name == 'gender' -%>
21
+ <%= require_or_optional(attribute) %> :<%= get_attribute_name(attribute.name, attribute.type) %>, type: <%= get_attribute_type(attribute.type) %>, desc: "<%= attribute.name.titleize %>", default: 'Male', values: ['Male', 'Female', 'Other']
22
+ <%- else -%>
23
+ <%= require_or_optional(attribute) %> :<%= get_attribute_name(attribute.name, attribute.type) %>, type: <%= get_attribute_type(attribute.type) %>, desc: "<%= attribute.name.titleize %>"
24
+ <%- end -%>
25
+ <%- end -%>
26
+ end
27
+ end
28
+ post :register do
29
+ <%= underscored_user_class %> = <%= user_class %>.new(params[:<%= underscored_user_class %>])
30
+ if <%= underscored_user_class %>.save
31
+ create_authentication(<%= underscored_user_class %>)
32
+ render_object(<%= underscored_user_class %>, additional_response: { message: "You have registered successfully." })
33
+ else
34
+ json_error_response({ errors: <%= underscored_user_class %>.errors.full_messages })
35
+ end
36
+ end
37
+
38
+ desc "Creates and returns <%= underscored_user_class %> with access token if valid login"
39
+ params do
40
+ requires :<%= underscored_user_class %>, type: Hash do
41
+ requires :email, type: String, desc: "Email address"
42
+ requires :password, type: String, desc: "Password"
43
+ end
44
+ end
45
+ post :login do
46
+ <%= underscored_user_class %> = <%= user_class %>.find_by(email: params[:<%= underscored_user_class %>][:email].downcase)
47
+ if <%= underscored_user_class %> && <%= underscored_user_class %>.valid_password?(params[:<%= underscored_user_class %>][:password])
48
+ create_authentication(<%= underscored_user_class %>)
49
+ render_object(<%= underscored_user_class %>, additional_response: { message: "You have successfully logged in." })
50
+ else
51
+ json_error_response({ errors: ['Invalid email or password.'] }, 401)
52
+ end
53
+ end
54
+
55
+ description "Logout <%= underscored_user_class %>"
56
+ post :logout do
57
+ destroy_authentication_token
58
+ json_success_response(message: "You have successfully logout.")
59
+ end
60
+
61
+ description "Return pong if logged in correctly"
62
+ get :ping do
63
+ authenticate!
64
+ json_success_response(message: "pong")
65
+ end
66
+
67
+ desc "Forgot Password"
68
+ params do
69
+ requires :<%= underscored_user_class %>, type: Hash do
70
+ requires :email, type: String, desc: "Email address"
71
+ end
72
+ end
73
+ post :forgot_password do
74
+ <%= underscored_user_class %> = <%= user_class %>.find_by(email: params[:<%= underscored_user_class %>][:email].downcase)
75
+ if <%= underscored_user_class %>.present?
76
+ <%= underscored_user_class %>.update(reset_token: token)
77
+ # send Forgot Password email
78
+ json_success_response(message: "You will receive email with instructions to reset password shortly.")
79
+ else
80
+ json_error_response({ errors: ['Invalid email address.'] })
81
+ end
82
+ end
83
+
84
+ desc "Reset Password"
85
+ params do
86
+ requires :reset_token, type: String, desc: "Reset Password"
87
+ requires :<%= underscored_user_class %>, type: Hash do
88
+ requires :password, type: String, desc: "Password"
89
+ requires :password_confirmation, type: String, desc: "Password Confirmation"
90
+ end
91
+ end
92
+ post :reset_password do
93
+ <%= underscored_user_class %> = <%= user_class %>.find_by(reset_token: params[:reset_token])
94
+ if <%= underscored_user_class %>.update(params[:<%= underscored_user_class %>])
95
+ # send Reset Password email
96
+ json_success_response(message: "Your password have successfully changed.")
97
+ else
98
+ json_error_response({ errors: ['Invalid reset token.'] })
99
+ end
100
+ end
101
+
102
+ description "Return <%= underscored_user_class %>"
103
+ get '' do
104
+ authenticate!
105
+ render_object(current_<%= underscored_user_class %>)
106
+ end
107
+
108
+ description "Update <%= underscored_user_class %>"
109
+ params do
110
+ requires :<%= underscored_user_class %>, type: Hash do
111
+ # Additional(optional) parameters
112
+ <%- for attribute in model_attributes -%>
113
+ <% next if attribute.name == 'email' %>
114
+ <%- if attribute.name == 'gender' -%>
115
+ <%= require_or_optional(attribute) %> :<%= attribute.name %>, type: <%= get_attribute_type(attribute.type) %>, desc: "<%= attribute.name.titleize %>", default: 'Male', values: ['Male', 'Female', 'Other']
116
+ <%- else -%>
117
+ <%= require_or_optional(attribute) %> :<%= get_attribute_name(attribute.name, attribute.type) %>, type: <%= get_attribute_type(attribute.type) %>, desc: "<%= attribute.name.titleize %>"
118
+ <%- end -%>
119
+ <%- end -%>
120
+ # group :avatars_attributes, type: Hash, desc: "An array of avatars" do
121
+ # optional :id, type: Integer
122
+ # optional :image, type: String
123
+ # optional :_destroy, type: Boolean
124
+ # end
125
+ end
126
+ end
127
+ put "" do
128
+ authenticate!
129
+ if current_<%= underscored_user_class %>.update(params[:<%= underscored_user_class %>])
130
+ render_object(current_<%= underscored_user_class %>)
131
+ else
132
+ json_error_response({ errors: current_<%= underscored_user_class %>.errors.full_messages })
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Authentication < ApplicationRecord
4
+ belongs_to :<%= underscored_user_class %>
5
+
6
+ validates :user_id, :client_id, :token, presence: true
7
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bcrypt'
4
+
5
+ module Api
6
+ module Authenticator
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ helpers do
11
+ def generate_random_string
12
+ "#{SecureRandom.urlsafe_base64}#{DateTime.now.to_i}#{SecureRandom.urlsafe_base64}"
13
+ end
14
+
15
+ def create_authentication(<%= underscored_user_class %>, client_id = ENV['CLIENT_ID'])
16
+ # delete all old tokens if any present
17
+ <%= underscored_user_class %>.authentications.delete_all
18
+
19
+ # create new auth token
20
+ client_id ||= SecureRandom.urlsafe_base64(nil, false)
21
+ token = generate_random_string
22
+ authentication = <%= underscored_user_class %>.authentications.create(client_id: client_id, token: BCrypt::Password.create(token))
23
+
24
+ # build auth header
25
+ header 'X-Uid', authentication.<%= underscored_user_class %>_id
26
+ header 'X-Client-Id', authentication.client_id
27
+ header 'X-Authentication-Token', token
28
+ end
29
+
30
+ def authentication
31
+ # user has already been found and authenticated
32
+ return @authentication if @authentication
33
+
34
+ # get details from header or params
35
+ uid = headers['X-Uid'] || params['uid']
36
+ @token ||= headers['X-Authentication-Token'] || params['access-token']
37
+ @client_id ||= request.headers['X-Client-Id'] || params['client-id']
38
+
39
+ # client_id isn't required, set to 'default' if absent
40
+ @client_id ||= 'default'
41
+
42
+ # ensure we clear the client_id
43
+ unless @token
44
+ @client_id = nil
45
+ return
46
+ end
47
+
48
+ return unless @token
49
+
50
+ auth = Authentication.where(<%= underscored_user_class %>_id: uid, client_id: @client_id).last || return
51
+ return @authentication = auth if ::BCrypt::Password.new(auth.token) == @token
52
+
53
+ @authentication = nil
54
+ end
55
+
56
+ def destroy_authentication_token
57
+ authentication&.destroy
58
+ end
59
+
60
+ def current_<%= underscored_user_class %>
61
+ @current_<%= underscored_user_class %> ||= authentication&.<%= underscored_user_class %>
62
+ end
63
+
64
+ def authenticate!
65
+ render_unauthorized_access && return unless current_<%= underscored_user_class %>
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,5 @@
1
+ class Avatar < ApplicationRecord
2
+ belongs_to :<%= underscored_user_class %>
3
+
4
+ mount_uploader :image, AvatarUploader
5
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AvatarUploader < CarrierWave::Uploader::Base
4
+ # Include RMagick or MiniMagick support:
5
+ # include CarrierWave::RMagick
6
+ # include CarrierWave::MiniMagick
7
+
8
+ # Choose what kind of storage to use for this uploader:
9
+ storage :file
10
+ # storage :fog
11
+
12
+ # Override the directory where uploaded files will be stored.
13
+ # This is a sensible default for uploaders that are meant to be mounted:
14
+ def store_dir
15
+ "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
16
+ end
17
+
18
+ # Provide a default URL as a default if there hasn't been a file uploaded:
19
+ # def default_url(*args)
20
+ # # For Rails 3.1+ asset pipeline compatibility:
21
+ # # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
22
+ #
23
+ # "/images/fallback/" + [version_name, "default.png"].compact.join('_')
24
+ # end
25
+
26
+ # Process files as they are uploaded:
27
+ # process scale: [200, 300]
28
+ #
29
+ # def scale(width, height)
30
+ # # do something
31
+ # end
32
+
33
+ # Create different versions of your uploaded files:
34
+ # version :thumb do
35
+ # process resize_to_fit: [50, 50]
36
+ # end
37
+
38
+ # Add a white list of extensions which are allowed to be uploaded.
39
+ # For images you might use something like this:
40
+ # def extension_whitelist
41
+ # %w(jpg jpeg gif png)
42
+ # end
43
+
44
+ # Override the filename of the uploaded files:
45
+ # Avoid using model.id or version_name here, see uploader/store.rb for details.
46
+ # def filename
47
+ # "something.jpg" if original_filename
48
+ # end
49
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Api
4
+ class Base < Grape::API
5
+ end
6
+ end
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Api
4
+ module <%= wrapper.capitalize %>
5
+ class <%= controller_path %> < Grape::API
6
+ include Api::<%= wrapper.capitalize %>::Defaults
7
+
8
+ resource :<%= controller_path.underscore %> do
9
+ <%- unless skip_authentication -%>
10
+ include Api::CustomDescription
11
+
12
+ before do
13
+ authenticate!
14
+ end
15
+ <%- end -%>
16
+ <%- if resources -%>
17
+ <%- if controller_actions.include?('index') -%>
18
+
19
+ <%= description_method_name %> "Return all <%= plural_name %>"
20
+ <%- if !skip_pagination || search_by -%>
21
+ <%- if !reference || (reference && association_type == 'has_many') || reference_id_param -%>
22
+ params do
23
+ <%- if search_by -%>
24
+ optional :q, type: Hash do
25
+ optional :m, type: String, desc: 'Matching case', default: 'or', values: ['or', 'and']
26
+ <%- for attribute in model_attributes.select { |ma| ma.search_by.present? } -%>
27
+ optional :<%= attribute.name %><%= attribute.search_by %>, type: <%= get_attribute_type(attribute.type) %>, desc: "Search by <%= attribute.name.titleize %>"
28
+ <%- end -%>
29
+ end
30
+ <%- end -%>
31
+ <%- if reference_id_param -%>
32
+ requires :<%= reference_id_param %>, type: Integer, desc: "<%= @user_class.classify %>'s id"
33
+ <%- end -%>
34
+ <%- if !skip_pagination && (!reference || (reference && association_type == 'has_many')) -%>
35
+ optional :page, type: Integer, desc: "Page number", default: 0
36
+ optional :per_page, type: Integer, desc: "Per Page", default: 15
37
+ <%- end -%>
38
+ end
39
+ <%- end -%>
40
+ <%- end -%>
41
+ get "", root: :<%= plural_name %> do
42
+ <%- if reference -%>
43
+ <%- if association_type == 'has_many' -%>
44
+ <%- if search_by -%>
45
+ @q = <%= reference_name %>.<%= plural_name %><%= additional_where_clause %>.ransack(params[:q])
46
+ results = @q.result(distinct: true)
47
+ <%- else -%>
48
+ results = <%= reference_name %>.<%= plural_name %><%= additional_where_clause %>
49
+ <%- end -%>
50
+ <%- else -%>
51
+ <%= singular_name %> = <%= reference_name %>.<%= singular_name %> || raise(<%= get_record_not_found_exception %>)
52
+ render_object(<%= singular_name %>)
53
+ <%- end -%>
54
+ <%- else -%>
55
+ <%- if search_by -%>
56
+ @q = <%= class_name %><%= additional_where_clause %>.ransack(params[:q])
57
+ results = @q.result(distinct: true)
58
+ <%- else -%>
59
+ results = <%= class_name %><%= additional_where_clause %>.all
60
+ <%- end -%>
61
+ <%- end -%>
62
+ <%- if !reference || association_type == 'has_many' -%>
63
+ <%- if skip_pagination -%>
64
+ render_objects(results)
65
+ <%- else -%>
66
+ render_objects(paginate results)
67
+ <%- end -%>
68
+ <%- end -%>
69
+ end
70
+ <%- end -%>
71
+ <%- end -%>
72
+ <%- if controller_actions.include?('show') -%>
73
+
74
+ <%= description_method_name %> "Return a <%= singular_name %>"
75
+ <%- if reference_id_param -%>
76
+ params do
77
+ requires :<%= reference_id_param %>, type: Integer, desc: "<%= @user_class.classify %>'s id"
78
+ end
79
+ <%- end -%>
80
+ get ":id", root: "<%= singular_name %>" do
81
+ <%- if reference -%>
82
+ <%- if association_type == 'has_many' -%>
83
+ <%= singular_name %> = <%= reference_name %>.<%= plural_name %>.find(params[:id])
84
+ <%- else -%>
85
+ <%= singular_name %> = <%= reference_name %>.<%= singular_name %> || raise(<%= get_record_not_found_exception %>)
86
+ <%- end -%>
87
+ <%- else -%>
88
+ <%= singular_name %> = <%= class_name %>.find(params[:id])
89
+ <%- end -%>
90
+ render_object(<%= singular_name %>)
91
+ end
92
+ <%- end -%>
93
+ <%- if controller_actions.include?('create') -%>
94
+
95
+ <%= description_method_name %> "Create a <%= singular_name %>"
96
+ params do
97
+ <%- if reference_id_param -%>
98
+ requires :<%= reference_id_param %>, type: Integer, desc: "<%= @user_class.classify %>'s id"
99
+ <%- end -%>
100
+ requires :<%= singular_name %>, type: Hash do
101
+ <%- if self_reference -%>
102
+ optional :<%= parent_association_name %>_id, type: Integer, desc: "<%= class_name.classify %>'s id as parent"
103
+ <%- end -%>
104
+ <%- for attribute in model_associations.select { |ma| ma.association_type == 'belongs_to' && !ma.class_name && !ma.reference } -%>
105
+ <%= require_or_optional(attribute) %> :<%= attribute.association_name + '_id' %>, type: Integer, desc: "<%= attribute.association_name.titleize %>"
106
+ <%- end -%>
107
+ <%- for attribute in model_attributes -%>
108
+ <%= require_or_optional(attribute) %> :<%= get_attribute_name(attribute.name, attribute.type) %>, type: <%= get_attribute_type(attribute.type) %>, desc: "<%= attribute.name.titleize %>"
109
+ <%- end -%>
110
+ <%- unless skip_tips -%>
111
+ # requires :title, type: String, desc: "Title of the <%= singular_name %>"
112
+ # requires :content, type: String, desc: "Content of the <%= singular_name %>"
113
+ <%- end -%>
114
+ end
115
+ end
116
+ post "", root: "<%= singular_name %>" do
117
+ <%- if reference -%>
118
+ <%- if association_type == 'has_many' -%>
119
+ <%= singular_name %> = <%= reference_name_create_update %>.<%= plural_name %>.<%= creation_method %>(params[:<%= singular_name %>])
120
+ <%- else -%>
121
+ <%= singular_name %> = <%= reference_name_create_update %>.<%= singular_name %> || <%= reference_name_create_update %>.build_<%= singular_name %>(params[:<%= singular_name %>])
122
+ <%= singular_name %>.attributes = params[:<%= singular_name %>] if <%= singular_name %>.persisted?
123
+ <%- end -%>
124
+ <%- else -%>
125
+ <%= singular_name %> = <%= class_name %>.<%= creation_method %>(params[:<%= singular_name %>])
126
+ <%- end -%>
127
+ if <%= singular_name %>.save
128
+ render_object(<%= singular_name %>, additional_response: { message: "<%= class_name %> created successfully." })
129
+ else
130
+ json_error_response(errors: <%= singular_name %>.errors.full_messages)
131
+ end
132
+ end
133
+ <%- end -%>
134
+ <%- if controller_actions.include?('update') -%>
135
+
136
+ <%= description_method_name %> "Update a <%= singular_name %>"
137
+ params do
138
+ <%- if reference_id_param -%>
139
+ requires :<%= reference_id_param %>, type: Integer, desc: "<%= @user_class.classify %>'s id"
140
+ <%- end -%>
141
+ requires :<%= singular_name %>, type: Hash do
142
+ <%- for attribute in model_attributes -%>
143
+ optional :<%= get_attribute_name(attribute.name, attribute.type) %>, type: <%= get_attribute_type(attribute.type) %>, desc: "<%= attribute.name.titleize %>"
144
+ <%- end -%>
145
+ <%- unless skip_tips -%>
146
+ # requires :title, type: String, desc: "Title of the <%= singular_name %>"
147
+ # requires :content, type: String, desc: "Content of the <%= singular_name %>"
148
+ # optional :views, type: String, desc: "Content of the <%= singular_name %>"
149
+ <%- end -%>
150
+ end
151
+ end
152
+ put ":id", root: "<%= singular_name %>" do
153
+ <%- if reference -%>
154
+ <%- if association_type == 'has_many' -%>
155
+ <%= singular_name %> = <%= reference_name_create_update %>.<%= plural_name %>.find(params[:id])
156
+ <%- else -%>
157
+ <%= singular_name %> = <%= reference_name_create_update %>.<%= singular_name %> || raise(<%= get_record_not_found_exception %>)
158
+ <%- end -%>
159
+ <%- else -%>
160
+ <%= singular_name %> = <%= class_name %>.find(params[:id])
161
+ <%- end -%>
162
+ if <%= singular_name %>.update(params[:<%= singular_name %>])
163
+ render_object(<%= singular_name %>, additional_response: { message: "<%= class_name %> updated successfully." })
164
+ else
165
+ json_error_response(errors: <%= singular_name %>.errors.full_messages)
166
+ end
167
+ end
168
+ <%- end -%>
169
+ <%- if controller_actions.include?('destroy') -%>
170
+
171
+ <%= description_method_name %> "Destroy a <%= singular_name %>"
172
+ <%- if reference_id_param -%>
173
+ params do
174
+ requires :<%= reference_id_param %>, type: Integer, desc: "<%= @user_class.classify %>'s id"
175
+ end
176
+ <%- end -%>
177
+ delete ":id", root: "<%= singular_name %>" do
178
+ <%- if reference -%>
179
+ <%- if association_type == 'has_many' -%>
180
+ <%= singular_name %> = <%= reference_name %>.<%= plural_name %>.find(params[:id])
181
+ <%- else -%>
182
+ <%= singular_name %> = <%= reference_name %>.<%= singular_name %> || raise(<%= get_record_not_found_exception %>)
183
+ <%- end -%>
184
+ <%- else -%>
185
+ <%= singular_name %> = <%= class_name %>.find(params[:id])
186
+ <%- end -%>
187
+ if <%= singular_name %>.destroy
188
+ json_success_response(message: "<%= class_name %> destroyed successfully.")
189
+ else
190
+ json_error_response(errors: <%= singular_name %>.errors.full_messages)
191
+ end
192
+ end
193
+ <%- end -%>
194
+ <%- if controller_actions.include?('create_or_destroy') -%>
195
+
196
+ <%= description_method_name %> "Create or Destroy a <%= singular_name %>"
197
+ params do
198
+ <%- if reference_id_param -%>
199
+ requires :<%= reference_id_param %>, type: Integer, desc: "<%= @user_class.classify %>'s id"
200
+ <%- end -%>
201
+ requires :<%= singular_name %>, type: Hash do
202
+ <%- if self_reference -%>
203
+ optional :<%= parent_association_name %>_id, type: Integer, desc: "<%= class_name.classify %>'s id as parent"
204
+ <%- end -%>
205
+ <%- for attribute in model_associations.select { |ma| ma.association_type == 'belongs_to' && !ma.class_name && !ma.reference } -%>
206
+ <%= require_or_optional(attribute) %> :<%= attribute.association_name + '_id' %>, type: Integer, desc: "<%= attribute.association_name.titleize %>"
207
+ <%- end -%>
208
+ <%- for attribute in model_attributes -%>
209
+ <%= require_or_optional(attribute) %> :<%= get_attribute_name(attribute.name, attribute.type) %>, type: <%= get_attribute_type(attribute.type) %>, desc: "<%= attribute.name.titleize %>"
210
+ <%- end -%>
211
+ end
212
+ end
213
+ post "", root: "<%= singular_name %>" do
214
+ <%- if reference -%>
215
+ <%- if association_type == 'has_many' -%>
216
+ <%= singular_name %> = <%= reference_name_create_update %>.<%= plural_name %>.find_or_initialize_by(params[:<%= singular_name %>])
217
+ <%- else -%>
218
+ <%= singular_name %> = <%= reference_name_create_update %>.<%= singular_name %> || <%= reference_name_create_update %>.build_<%= singular_name %>(params[:<%= singular_name %>])
219
+ <%- end -%>
220
+ <%- else -%>
221
+ <%= singular_name %> = <%= class_name %>.find_or_initialize_by(params[:<%= singular_name %>])
222
+ <%- end -%>
223
+ if <%= singular_name %>.persisted?
224
+ if <%= singular_name %>.destroy_all
225
+ json_success_response(message: "<%= class_name %> destroyed successfully.")
226
+ else
227
+ json_error_response(errors: <%= singular_name %>.errors.full_messages)
228
+ end
229
+ else
230
+ if <%= singular_name %>.save
231
+ render_object(<%= singular_name %>, additional_response: { message: "<%= class_name %> created successfully." })
232
+ else
233
+ json_error_response(errors: <%= singular_name %>.errors.full_messages)
234
+ end
235
+ end
236
+ end
237
+ <%- end -%>
238
+ end
239
+ end
240
+ end
241
+ end