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,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,5 @@
1
+ class Add<%= class_name.pluralize %>CountTo<%= @options[:belongs_to].classify.pluralize %> < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :<%= @options[:belongs_to].pluralize %>, :<%= class_name.pluralize.underscore %>_count, :integer, default: 0
4
+ end
5
+ 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Kriangle.setup do |config|
4
+ config.database = '<%= database %>'
5
+ 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