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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +30 -0
- data/Gemfile.lock +216 -0
- data/LICENSE.txt +21 -0
- data/README.md +307 -0
- data/Rakefile +4 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/kriangle.gemspec +42 -0
- data/lib/generators/kriangle/generator_helpers.rb +95 -0
- data/lib/generators/kriangle/install_generator.rb +149 -0
- data/lib/generators/kriangle/module_generator.rb +273 -0
- data/lib/generators/kriangle/templates/active_serializer.rb +9 -0
- data/lib/generators/kriangle/templates/application_record.rb +9 -0
- data/lib/generators/kriangle/templates/auth.rb +138 -0
- data/lib/generators/kriangle/templates/authentication.rb +7 -0
- data/lib/generators/kriangle/templates/authenticator.rb +70 -0
- data/lib/generators/kriangle/templates/avatar.rb +5 -0
- data/lib/generators/kriangle/templates/avatar_uploader.rb +49 -0
- data/lib/generators/kriangle/templates/base.rb +6 -0
- data/lib/generators/kriangle/templates/controller.rb +241 -0
- data/lib/generators/kriangle/templates/controllers.rb +21 -0
- data/lib/generators/kriangle/templates/counter_cache_migration.rb +5 -0
- data/lib/generators/kriangle/templates/create_authentications.rb +10 -0
- data/lib/generators/kriangle/templates/create_avatars.rb +10 -0
- data/lib/generators/kriangle/templates/create_users.rb.erb +48 -0
- data/lib/generators/kriangle/templates/custom_description.rb +30 -0
- data/lib/generators/kriangle/templates/defaults.rb +29 -0
- data/lib/generators/kriangle/templates/kriangle.rb +5 -0
- data/lib/generators/kriangle/templates/model.rb +33 -0
- data/lib/generators/kriangle/templates/module_migration.rb +33 -0
- data/lib/generators/kriangle/templates/responder.rb +170 -0
- data/lib/generators/kriangle/templates/serializer.rb +35 -0
- data/lib/generators/kriangle/templates/swagger.rb +8 -0
- data/lib/generators/kriangle/templates/user.rb +35 -0
- data/lib/kriangle.rb +36 -0
- data/lib/kriangle/version.rb +5 -0
- metadata +355 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
<%- unless skip_swagger -%>
|
4
|
+
require 'grape-swagger'
|
5
|
+
<%- end -%>
|
6
|
+
|
7
|
+
module Api
|
8
|
+
module <%= wrapper.capitalize %>
|
9
|
+
class Controllers < Grape::API
|
10
|
+
<%- unless skip_swagger -%>
|
11
|
+
|
12
|
+
add_swagger_documentation(
|
13
|
+
api_version: "<%= wrapper.underscore %>",
|
14
|
+
hide_documentation_path: true,
|
15
|
+
mount_path: "/kriangle/api/<%= wrapper.underscore %>/swagger_doc",
|
16
|
+
hide_format: true
|
17
|
+
)
|
18
|
+
<%- end -%>
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class CreateAuthentications < ActiveRecord::Migration<%= "[#{Rails::VERSION::STRING[0..2]}]" if Rails::VERSION::MAJOR > 4 %>
|
2
|
+
def change
|
3
|
+
create_table :authentications do |t|
|
4
|
+
t.references :<%= underscored_user_class %>, foreign_key: true
|
5
|
+
|
6
|
+
t.text :client_id
|
7
|
+
t.text :token
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class CreateAvatars < ActiveRecord::Migration<%= "[#{Rails::VERSION::STRING[0..2]}]" if Rails::VERSION::MAJOR > 4 %>
|
2
|
+
def change
|
3
|
+
create_table :avatars do |t|
|
4
|
+
t.references :<%= underscored_user_class %>, foreign_key: true
|
5
|
+
|
6
|
+
t.text :image
|
7
|
+
t.integer :sorting, default: 0
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Create<%= user_class.pluralize %> < ActiveRecord::Migration<%= "[#{Rails::VERSION::STRING[0..2]}]" if Rails::VERSION::MAJOR > 4 %>
|
2
|
+
def change
|
3
|
+
create_table :<%= user_class.pluralize.underscore %> do |t|
|
4
|
+
## User Info
|
5
|
+
<%- for attribute in model_attributes -%>
|
6
|
+
<%- next if attribute.name == 'email' -%>
|
7
|
+
t.<%= attribute.type %> :<%= attribute.name %>
|
8
|
+
<%- end -%>
|
9
|
+
|
10
|
+
## Database authenticatable
|
11
|
+
t.string :email, :null => false, :default => ""
|
12
|
+
t.string :encrypted_password, :null => false, :default => ""
|
13
|
+
|
14
|
+
## Recoverable
|
15
|
+
t.string :reset_password_token
|
16
|
+
t.datetime :reset_password_sent_at
|
17
|
+
t.boolean :allow_password_change, :default => false
|
18
|
+
|
19
|
+
## Rememberable
|
20
|
+
t.datetime :remember_created_at
|
21
|
+
|
22
|
+
## Trackable
|
23
|
+
t.integer :sign_in_count, :default => 0, :null => false
|
24
|
+
t.datetime :current_sign_in_at
|
25
|
+
t.datetime :last_sign_in_at
|
26
|
+
t.string :current_sign_in_ip
|
27
|
+
t.string :last_sign_in_ip
|
28
|
+
|
29
|
+
## Confirmable
|
30
|
+
t.string :confirmation_token
|
31
|
+
t.datetime :confirmed_at
|
32
|
+
t.datetime :confirmation_sent_at
|
33
|
+
t.string :unconfirmed_email # Only if using reconfirmable
|
34
|
+
|
35
|
+
## Lockable
|
36
|
+
t.integer :failed_attempts, :default => 0, :null => false # Only if lock strategy is :failed_attempts
|
37
|
+
t.string :unlock_token # Only if unlock strategy is :email or :both
|
38
|
+
t.datetime :locked_at
|
39
|
+
|
40
|
+
t.timestamps
|
41
|
+
end
|
42
|
+
|
43
|
+
add_index :<%= user_class.pluralize.underscore %>, :email, unique: true
|
44
|
+
add_index :<%= user_class.pluralize.underscore %>, :reset_password_token, unique: true
|
45
|
+
add_index :<%= user_class.pluralize.underscore %>, :confirmation_token, unique: true
|
46
|
+
# add_index :<%= user_class.pluralize.underscore %>, :unlock_token, unique: true
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Api
|
4
|
+
module CustomDescription
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
class_methods do
|
8
|
+
def description(title)
|
9
|
+
desc title,
|
10
|
+
headers: {
|
11
|
+
'X-Uid' => {
|
12
|
+
description: 'User Id',
|
13
|
+
required: true,
|
14
|
+
default: ENV['X_UID']
|
15
|
+
},
|
16
|
+
'X-Client-Id' => {
|
17
|
+
description: 'Client Id',
|
18
|
+
required: true,
|
19
|
+
default: ENV['X_CLIENT_ID']
|
20
|
+
},
|
21
|
+
'X-Authentication-Token' => {
|
22
|
+
description: 'Authentication Token',
|
23
|
+
required: true,
|
24
|
+
default: ENV['X_AUTH_TOKEN']
|
25
|
+
}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Api
|
4
|
+
module <%= wrapper.capitalize %>
|
5
|
+
module Defaults
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
prefix "api"
|
10
|
+
version "<%= wrapper.underscore %>", using: :path
|
11
|
+
default_format :json
|
12
|
+
format :json
|
13
|
+
formatter :json, Grape::Formatter::ActiveModelSerializers
|
14
|
+
|
15
|
+
# Authenticator and Responder
|
16
|
+
<%- unless skip_authentication -%>
|
17
|
+
include Api::Authenticator
|
18
|
+
<%- end -%>
|
19
|
+
include Api::Responder
|
20
|
+
|
21
|
+
helpers do
|
22
|
+
def logger
|
23
|
+
Rails.logger
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= class_name %> < ApplicationRecord
|
4
|
+
<%- for ma in model_associations.uniq { |ma| ma.association_name } -%>
|
5
|
+
<%= ma.association %>
|
6
|
+
<%- end -%>
|
7
|
+
<%- if self_reference -%>
|
8
|
+
belongs_to :<%= parent_association_name %>, :class_name => '<%= class_name %>', optional: true
|
9
|
+
has_many :<%= child_association_name %>, :class_name => '<%= class_name %>', :foreign_key => '<%= parent_association_name %>_id'
|
10
|
+
|
11
|
+
scope :only_parent, -> { includes(:<%= child_association_name %>).where(<%= parent_association_name %>_id: nil) }
|
12
|
+
<%- end -%>
|
13
|
+
<%- for polymorphic in @options[:polymorphics] -%>
|
14
|
+
belongs_to :<%= polymorphic %>, polymorphic: true
|
15
|
+
# use below into referenced model
|
16
|
+
# has_many :<%= polymorphic.gsub('able','').pluralize %>, as: :<%= polymorphic %>, dependent: :destroy
|
17
|
+
<%- end -%>
|
18
|
+
<%- for parent_model in @options[:references] -%>
|
19
|
+
<%- if !reference || parent_model != user_class -%>
|
20
|
+
belongs_to :<%= parent_model %>
|
21
|
+
<%- end -%>
|
22
|
+
<%- end -%>
|
23
|
+
<%- if database == 'sqlite3' -%>
|
24
|
+
<%- attributes.select { |a| a.type == 'array' }.each do |a| -%>
|
25
|
+
serialize :<%= a.name %>, Array
|
26
|
+
<%- end -%>
|
27
|
+
<%- end -%>
|
28
|
+
<%- if @options[:attributes].size != 0 -%>
|
29
|
+
|
30
|
+
# validation's on columns
|
31
|
+
validates :<%= @options[:attributes].join(', :') %>, presence: true
|
32
|
+
<%- end -%>
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Create<%= class_name.pluralize %> < ActiveRecord::Migration[5.2]
|
2
|
+
def change
|
3
|
+
create_table :<%= controller_file_name %> do |t|
|
4
|
+
<%- for ma in model_associations.select { |ma| ma.association_type == 'belongs_to' }.uniq { |ma| ma.association_name } -%>
|
5
|
+
<%- if ma.foreign_key.present? -%>
|
6
|
+
t.integer :<%= ma.foreign_key %>, index: true
|
7
|
+
<%- else -%>
|
8
|
+
t.references :<%= ma.association_name %><%= ", foreign_key: true" if ma.class_name.blank? %>
|
9
|
+
<%- end -%>
|
10
|
+
<%- end -%>
|
11
|
+
<%- if self_reference -%>
|
12
|
+
t.references :<%= parent_association_name %>, index: true
|
13
|
+
<%- end -%>
|
14
|
+
<%- for attribute in @polymorphics -%>
|
15
|
+
t.references :<%= attribute.name %>, polymorphic: true
|
16
|
+
<%- end -%>
|
17
|
+
<%- for attribute in @attributes -%>
|
18
|
+
<%- if attribute.type == 'array' -%>
|
19
|
+
<%- if database == 'sqlite3' -%>
|
20
|
+
t.text :<%= attribute.name %>
|
21
|
+
<%- else -%>
|
22
|
+
t.text :<%= attribute.name %>, array: true, default: []
|
23
|
+
<%- end -%>
|
24
|
+
<%- else -%>
|
25
|
+
t.<%= attribute.type || 'string' %> :<%= attribute.name %><%= ", default: #{attribute.default.gsub('~', "'")}" unless attribute.default.nil? %>
|
26
|
+
<%- end -%>
|
27
|
+
<%- end -%>
|
28
|
+
<%- unless skip_timestamps -%>
|
29
|
+
t.timestamps
|
30
|
+
<%- end -%>
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class JsonResponse
|
4
|
+
attr_reader :success, :message, :data, :meta, :errors
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@success = options[:success].to_s.empty? ? true : options[:success]
|
8
|
+
@message = options[:message] || options[:errors].try(:first) || ''
|
9
|
+
@data = options[:data] || []
|
10
|
+
@meta = options[:meta] || {}
|
11
|
+
@errors = options[:errors] || []
|
12
|
+
end
|
13
|
+
|
14
|
+
def as_json(*)
|
15
|
+
{
|
16
|
+
success: success,
|
17
|
+
message: message,
|
18
|
+
data: data,
|
19
|
+
meta: meta,
|
20
|
+
errors: errors
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Api
|
26
|
+
module Responder
|
27
|
+
extend ActiveSupport::Concern
|
28
|
+
|
29
|
+
included do
|
30
|
+
# catch exception and return JSON-formatted error
|
31
|
+
# def handle_exceptions
|
32
|
+
# begin
|
33
|
+
# yield
|
34
|
+
# rescue <%= get_record_not_found_exception %> => e
|
35
|
+
# status_code = 404
|
36
|
+
# rescue <%= get_record_invalid_exception %> => e
|
37
|
+
# json_error_response(e.record) && return
|
38
|
+
# rescue ArgumentError => e
|
39
|
+
# status_code = 400
|
40
|
+
# rescue StandardError => e
|
41
|
+
# status_code = 500
|
42
|
+
# end
|
43
|
+
# json_error_response({ message: e.class.to_s, errors: [{ detail: e.message, trace: e.backtrace }] }, status_code) unless e.class == NilClass
|
44
|
+
# end
|
45
|
+
|
46
|
+
helpers do
|
47
|
+
# extract options
|
48
|
+
# i.e. serializer = nil, options = {}, additional_response = {}
|
49
|
+
def extract_options resource, options, collection = false
|
50
|
+
# identify serializer
|
51
|
+
@serializer = options[:serializer]
|
52
|
+
unless @serializer
|
53
|
+
if collection
|
54
|
+
class_name = resource&.first&.class
|
55
|
+
if class_name.present? && class_name != NilClass
|
56
|
+
@serializer = "#{class_name}Serializer".constantize
|
57
|
+
end
|
58
|
+
else
|
59
|
+
@serializer = "#{resource.class}Serializer".constantize
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# additional params
|
64
|
+
@serializer_options = options[:serializer_options] || {}
|
65
|
+
@additional_response = options[:additional_response] || {}
|
66
|
+
end
|
67
|
+
|
68
|
+
# render single object with serializer
|
69
|
+
def render_object object, **options
|
70
|
+
extract_options(object, options) # extract required options
|
71
|
+
|
72
|
+
json_success_response({
|
73
|
+
data: @serializer.present? ? single_serializer.new(object, serializer: @serializer, serializer_options: @serializer_options) : {}
|
74
|
+
}.merge(@additional_response))
|
75
|
+
end
|
76
|
+
|
77
|
+
def format_aggregation(aggs)
|
78
|
+
return [] if aggs.blank?
|
79
|
+
|
80
|
+
aggregations = []
|
81
|
+
aggs.each do |k, value|
|
82
|
+
value['buckets'].each do |bucket|
|
83
|
+
bucket['count'] = bucket.delete('doc_count')
|
84
|
+
end
|
85
|
+
aggregations << { name: k, buckets: value['buckets'] }
|
86
|
+
end
|
87
|
+
aggregations
|
88
|
+
end
|
89
|
+
|
90
|
+
# render collection of objects with serializer
|
91
|
+
def render_objects objects, **options
|
92
|
+
extract_options(objects.to_a, options, true) # extract required options
|
93
|
+
|
94
|
+
# collect meta data if any present there
|
95
|
+
meta = options[:extra_params].present? ? options[:extra_params] : {}
|
96
|
+
meta[:suggestions] = objects.suggestions if objects.respond_to?(:suggestions) && objects.suggestions.present?
|
97
|
+
meta[:aggregations] = format_aggregation(objects.aggs) if objects.respond_to?(:aggs)
|
98
|
+
if objects.respond_to?(:total_count)
|
99
|
+
meta[:pagination] = {
|
100
|
+
total_count: objects.total_count,
|
101
|
+
current_page: objects.current_page,
|
102
|
+
next_page: objects.next_page,
|
103
|
+
per_page: objects.try(:per_page) || objects.try(:limit_value) || 10000
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
# send data & meta
|
108
|
+
json_success_response({
|
109
|
+
data: @serializer.present? ? array_serializer.new(objects, serializer: @serializer, serializer_options: @serializer_options) : [],
|
110
|
+
meta: meta
|
111
|
+
}.merge(@additional_response))
|
112
|
+
end
|
113
|
+
|
114
|
+
def render_not_found(resource)
|
115
|
+
render_error_response(["#{ resource } not found."], :not_found)
|
116
|
+
end
|
117
|
+
|
118
|
+
def render_errors(object)
|
119
|
+
render_error_response(object.errors, :unprocessable_entity)
|
120
|
+
end
|
121
|
+
|
122
|
+
def render_error
|
123
|
+
render_error_response(['Something went wrong. Please try after sometime.'], :unprocessable_entity)
|
124
|
+
end
|
125
|
+
|
126
|
+
def render_interval_server_error
|
127
|
+
render_error_response(['Internal server error.'], 500)
|
128
|
+
end
|
129
|
+
|
130
|
+
def render_unprocessable_entity(errors)
|
131
|
+
render_error_response(errors, 422) and return
|
132
|
+
end
|
133
|
+
|
134
|
+
def render_unauthorized_access
|
135
|
+
render_error_response(['Invalid or expired token.'], 401) and return
|
136
|
+
end
|
137
|
+
|
138
|
+
def render_error_response(errors = [], status = 422)
|
139
|
+
json_error_response({ errors: errors }, status)
|
140
|
+
end
|
141
|
+
|
142
|
+
def json_success_response response = {}
|
143
|
+
JsonResponse.new(response.merge(status: true)).as_json
|
144
|
+
end
|
145
|
+
|
146
|
+
def json_error_response response = {}, status = 422
|
147
|
+
error!(JsonResponse.new(response.merge(status: true)).as_json, status)
|
148
|
+
end
|
149
|
+
|
150
|
+
def array_serializer
|
151
|
+
ActiveModel::Serializer::CollectionSerializer
|
152
|
+
end
|
153
|
+
|
154
|
+
def single_serializer
|
155
|
+
ActiveModelSerializers::SerializableResource
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
rescue_from <%= get_record_not_found_exception %> do |e|
|
160
|
+
message = e.try(:problem) || e.try(:message)
|
161
|
+
model_name = message.match(/(?<=class|find)[^w]+/)&.to_s&.strip
|
162
|
+
json_error_response(errors: ["No #{model_name || 'Record'} Found."], status: 404)
|
163
|
+
end
|
164
|
+
|
165
|
+
rescue_from <%= get_record_invalid_exception %> do |e|
|
166
|
+
json_error_response(errors: [e.message], status: 422)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= @options[:class_name] || class_name %>Serializer < ActiveSerializer
|
4
|
+
attributes :<%= @options[:attributes].join(', :') %>
|
5
|
+
<%- unless skip_tips -%>
|
6
|
+
# attributes :custom_function
|
7
|
+
<%- end -%>
|
8
|
+
|
9
|
+
<%- @options[:references].try(:each) do |parent_model| -%>
|
10
|
+
belongs_to :<%= parent_model %>
|
11
|
+
<%- end -%>
|
12
|
+
<%- for ma in model_associations -%>
|
13
|
+
<%= ma.association_type_with_name %>
|
14
|
+
<%- end -%>
|
15
|
+
<%- if self_reference -%>
|
16
|
+
has_many :<%= child_association_name %>, serializer: <%= @options[:class_name] || class_name %>Serializer
|
17
|
+
<%- end -%>
|
18
|
+
<%- unless skip_tips -%>
|
19
|
+
# association's example
|
20
|
+
# belongs_to :user
|
21
|
+
# has_one :address
|
22
|
+
# has_many :avatars
|
23
|
+
|
24
|
+
# def custom_function
|
25
|
+
# object.association(:association_model_name).loaded? ? object.association_model_name : {}
|
26
|
+
# end
|
27
|
+
|
28
|
+
# add your custom attributes here if required
|
29
|
+
# def attributes(*args)
|
30
|
+
# hash = super
|
31
|
+
# # hash[:your_key] = object.some_method_call
|
32
|
+
# hash
|
33
|
+
# end
|
34
|
+
<%- end -%>
|
35
|
+
end
|