kriangle 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|