kriangle 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'kriangle'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/kriangle.gemspec ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'kriangle/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'kriangle'
9
+ spec.version = Kriangle::VERSION
10
+ spec.authors = ['Lalit Kumar Maurya']
11
+ spec.email = ['lalit.logical@gmail.com']
12
+
13
+ spec.summary = 'Scaffold module (including REST API with documentation) with help of Grape and Swagger.'
14
+ spec.description = 'Scaffold module (including REST API with documentation) with help of Grape and Swagger.'
15
+ spec.homepage = 'https://github.com/lalitlogical/kriangle'
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|features)/})
20
+ end
21
+ spec.bindir = 'exe'
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.16'
26
+ spec.add_development_dependency 'rake', '~> 13.0'
27
+
28
+ spec.add_dependency('sqlite3', '~> 1.4', '>= 1.4.2')
29
+ spec.add_dependency('bcrypt', '~> 3.1.7')
30
+ spec.add_dependency('devise', '~> 4.4', '>= 4.4.3')
31
+ spec.add_dependency('dotenv-rails', '~> 2.7', '>= 2.7.6')
32
+ spec.add_dependency('grape', '~> 1.2', '>= 1.2.4')
33
+ spec.add_dependency('grape-active_model_serializers', '~> 1.5', '>= 1.5.2')
34
+ spec.add_dependency('grape-rails-cache', '~> 0.1.2')
35
+ spec.add_dependency('grape-swagger', '~> 1.4')
36
+ spec.add_dependency('grape-swagger-rails', '~> 0.3.1')
37
+ spec.add_dependency('api-pagination', '~> 4.7')
38
+ spec.add_dependency('kaminari', '~> 1.0', '>= 1.0.1')
39
+ spec.add_dependency('rack-cors', '~> 1.1', '>= 1.1.1')
40
+ spec.add_dependency('ransack', '~> 2.3')
41
+ spec.add_dependency('carrierwave', '~> 2.0', '>= 2.0.2')
42
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kriangle
4
+ module Generators
5
+ # Some helpers for generating scaffolding
6
+ module GeneratorHelpers
7
+ attr_accessor :options, :attributes
8
+
9
+ Attribute = Struct.new(:name, :type, :validate_presence, :search_by, :default)
10
+ Association = Struct.new(:association_type, :association_name, :dependent_type, :validate_presence, :counter_cache, :touch_record, :accepts_nested_attributes, :foreign_key, :class_name, :reference) do
11
+ def association_type_with_name
12
+ "#{association_type} :#{association_name}"
13
+ end
14
+
15
+ def association
16
+ txt = "#{association_type} :#{association_name}"
17
+ txt += ', counter_cache: true' if counter_cache == 'true'
18
+ txt += ', optional: true' if association_type == 'belongs_to' && validate_presence == 'false'
19
+ txt += ', touch: true' if touch_record == 'true'
20
+ txt += ", foreign_key: '#{foreign_key}'" if foreign_key.present?
21
+ txt += ", class_name: '#{class_name}'" if class_name.present?
22
+ txt += ", dependent: :#{dependent_type}" if association_type.match('has_') && dependent_type.present?
23
+ txt += "\n\t#{accepts_nested_attributes_text}" if association_type != 'belongs_to' && accepts_nested_attributes == 'true'
24
+ txt
25
+ end
26
+
27
+ def accepts_nested_attributes_text
28
+ "accepts_nested_attributes_for :#{association_name}, allow_destroy: true"
29
+ end
30
+ end
31
+
32
+ @@column_types = {
33
+ 'references': 'Integer',
34
+ 'text': 'String',
35
+ 'datetime': 'DateTime',
36
+ 'attachment': 'File',
37
+ 'jsonb': 'JSON',
38
+ 'json': 'JSON'
39
+ }
40
+
41
+ private
42
+
43
+ def model_columns_for_attributes
44
+ class_name.constantize.columns.reject do |column|
45
+ column.name.to_s =~ /^(id|user_id|created_at|updated_at)$/
46
+ end
47
+ end
48
+
49
+ def editable_attributes
50
+ attributes ||= model_columns_for_attributes.map do |column|
51
+ Rails::Generators::GeneratedAttribute.new(column.name.to_s, column.type.to_s)
52
+ end
53
+ end
54
+
55
+ def create_template(template_fname, fname, **options)
56
+ @options = options
57
+ if @options[:skip_if_exist] && File.exist?(File.join(destination_root, fname))
58
+ say_status 'skipped', fname
59
+ else
60
+ template template_fname, fname
61
+ end
62
+ end
63
+
64
+ def create_migration_file(migration_fname, fname, **options)
65
+ @options = options
66
+ if @options[:skip_if_exist] && self.class.migration_exists?('db/migrate', fname.split('/').last.gsub('.rb', ''))
67
+ say_status('skipped', "Migration '#{fname}' already exists")
68
+ else
69
+ migration_template migration_fname, fname, options
70
+ end
71
+ end
72
+
73
+ def get_record_not_found_exception
74
+ custom_orm == 'Mongoid' ? 'Mongoid::Errors::DocumentNotFound' : 'ActiveRecord::RecordNotFound'
75
+ end
76
+
77
+ def get_record_invalid_exception
78
+ custom_orm == 'Mongoid' ? 'Mongoid::Errors::InvalidFind' : 'ActiveRecord::RecordInvalid'
79
+ end
80
+
81
+ def get_attribute_name(name, attribute_type)
82
+ attribute_type == 'references' ? "#{name}_id" : name
83
+ end
84
+
85
+ def require_or_optional(attribute)
86
+ attribute.validate_presence == 'true' ? 'requires' : 'optional'
87
+ end
88
+
89
+ def get_attribute_type(attribute_type)
90
+ column_type = @@column_types[attribute_type.to_sym]
91
+ column_type.present? ? column_type : attribute_type.to_s.camelcase
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/migration'
5
+ require 'rails/generators/active_record'
6
+ require 'generators/kriangle/generator_helpers'
7
+
8
+ module Kriangle
9
+ module Generators
10
+ # Custom scaffolding generator
11
+ class InstallGenerator < Rails::Generators::Base
12
+ include Rails::Generators::Migration
13
+ include Kriangle::Generators::GeneratorHelpers
14
+
15
+ no_tasks do
16
+ attr_accessor :underscored_user_class,
17
+ :column_types,
18
+ :model_attributes,
19
+ :model_associations,
20
+ :controller_actions,
21
+ :wrapper,
22
+ :controller_path,
23
+ :custom_orm,
24
+ :self_reference,
25
+ :skip_tips,
26
+ :skip_swagger,
27
+ :skip_avatar,
28
+ :skip_migration,
29
+ :skip_authentication,
30
+ :database
31
+ end
32
+
33
+ # arguments
34
+ argument :user_class, type: :string, default: 'User'
35
+ # argument :mount_path, type: :string, default: 'User'
36
+ argument :args_for_c_m, type: :array, default: [], banner: 'model:attributes'
37
+
38
+ class_option :database, type: :string, default: 'sqlite3', desc: "database i.e. postgresql, mysql, sqlite3"
39
+
40
+ class_option :wrapper, type: :string, default: 'V1', desc: 'Skip "Swagger UI"'
41
+ class_option :controller_path, type: :string, desc: "controller's path"
42
+ class_option :skip_tips, type: :boolean, default: false, desc: 'Skip "Tips from different files i.e. model, serializer, etc."'
43
+ class_option :skip_swagger, type: :boolean, default: false, desc: 'Skip "Swagger UI"'
44
+ class_option :skip_avatar, type: :boolean, default: true, desc: 'Skip "Avatar Feature"'
45
+ class_option :skip_migration, desc: 'Don\'t generate migration file for model.', type: :boolean, default: false
46
+ class_option :custom_orm, type: :string, default: 'ActiveRecord', desc: 'ORM i.e. ActiveRecord, Mongoid'
47
+
48
+ source_root File.expand_path('templates', __dir__)
49
+
50
+ def initialize(*args, &block)
51
+ super
52
+ @model_attributes = []
53
+ @model_associations = []
54
+ @underscored_user_class = user_class.underscore
55
+ @wrapper = options.wrapper
56
+ @database = options.database
57
+ @custom_orm = 'ActiveRecord' # Kriangle.custom_orm
58
+ @skip_tips = options.skip_tips?
59
+ @skip_swagger = options.skip_swagger?
60
+ @skip_avatar = options.skip_avatar?
61
+ @skip_migration = options.skip_migration?
62
+ @skip_authentication = false
63
+ @controller_path = options.controller_path&.classify&.pluralize || user_class.classify&.pluralize
64
+
65
+ args_for_c_m.each do |arg|
66
+ next unless arg.include?(':')
67
+
68
+ options = arg.split(':')
69
+ if arg.match(/^ma:/).present?
70
+ options.shift
71
+ @model_associations << Association.new(*options)
72
+ else
73
+ @model_attributes << Attribute.new(*options)
74
+ end
75
+ end
76
+
77
+ if @model_attributes.blank?
78
+ default_attributes = ['first_name:string', 'last_name:string', 'about:text', 'age:integer', 'dob:datetime', 'gender:string']
79
+ @model_attributes = default_attributes.map { |arg| Attribute.new(*arg.split(':')) }
80
+ end
81
+ @model_attributes.uniq!(&:name)
82
+
83
+ @attributes = %i[id email]
84
+ @attributes += @model_attributes.map { |a| a.name.to_sym }
85
+ @attributes.uniq!
86
+ end
87
+
88
+ def self.next_migration_number(_path)
89
+ if @prev_migration_nr
90
+ @prev_migration_nr += 1
91
+ else
92
+ @prev_migration_nr = Time.now.utc.strftime('%Y%m%d%H%M%S').to_i
93
+ end
94
+ @prev_migration_nr.to_s
95
+ end
96
+
97
+ def copy_initializer
98
+ create_template 'kriangle.rb', 'config/initializers/kriangle.rb', skip_if_exist: true
99
+ create_template 'application_record.rb', 'app/models/application_record.rb', skip_if_exist: true
100
+ create_template 'swagger.rb', 'config/initializers/swagger.rb', skip_if_exist: true unless skip_swagger
101
+ end
102
+
103
+ def copy_migrations
104
+ if custom_orm == 'ActiveRecord' && !skip_migration
105
+ create_migration_file 'create_users.rb.erb', "db/migrate/create_#{user_class.pluralize.underscore}.rb"
106
+ create_migration_file 'create_authentications.rb', 'db/migrate/create_authentications.rb'
107
+ create_migration_file 'create_avatars.rb', 'db/migrate/create_avatars.rb' unless skip_avatar
108
+ end
109
+ end
110
+
111
+ def create_model_file
112
+ create_template 'user.rb', "app/models/#{user_class.underscore}.rb"
113
+ create_template 'authentication.rb', 'app/models/authentication.rb'
114
+ create_template 'avatar.rb', 'app/models/avatar.rb' unless skip_avatar
115
+
116
+ create_template 'active_serializer.rb', 'app/serializers/active_serializer.rb', skip_if_exist: true
117
+ create_template 'serializer.rb', "app/serializers/#{underscored_user_class}_serializer.rb", class_name: user_class, attributes: @attributes
118
+ create_template 'serializer.rb', 'app/serializers/avatar_serializer.rb', class_name: 'Avatar', attributes: %i[id image_url] unless skip_avatar
119
+
120
+ # Uploader File
121
+ create_template 'avatar_uploader.rb', 'app/uploaders/avatar_uploader.rb' unless skip_avatar
122
+ end
123
+
124
+ desc 'Generates required files.'
125
+ def copy_controller_and_spec_files
126
+ # Main base files
127
+ create_template 'base.rb', 'app/controllers/api/base.rb', skip_if_exist: true
128
+ inject_into_file 'app/controllers/api/base.rb', "\n\t\t\tmount Api::#{wrapper.capitalize}::Controllers", after: /Grape::API.*/
129
+
130
+ # All new controllers will go here
131
+ create_template 'controllers.rb', "app/controllers/api/#{@wrapper.underscore}/controllers.rb", skip_if_exist: true
132
+ inject_into_file "app/controllers/api/#{@wrapper.underscore}/controllers.rb", "\n\t\t\tmount Api::#{@wrapper.capitalize}::#{controller_path}", after: /Grape::API.*/
133
+
134
+ # Authentications related things will go there
135
+ create_template 'defaults.rb', "app/controllers/api/#{@wrapper.underscore}/defaults.rb", skip_if_exist: true
136
+ create_template 'custom_description.rb', 'app/controllers/api/custom_description.rb'
137
+ create_template 'authenticator.rb', 'app/controllers/api/authenticator.rb'
138
+ create_template 'responder.rb', 'app/controllers/api/responder.rb'
139
+
140
+ # Authentication i.e. login, register, logout
141
+ template 'auth.rb', "app/controllers/api/#{@wrapper.underscore}/#{controller_path.underscore}.rb"
142
+
143
+ # setup routes
144
+ inject_into_file 'config/routes.rb', "\n\tmount GrapeSwaggerRails::Engine => '/swagger'", after: /routes.draw.*/ unless skip_swagger
145
+ inject_into_file 'config/routes.rb', "\n\tmount Api::Base, at: '/'", after: /routes.draw.*/
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,273 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/migration'
5
+ require 'rails/generators/active_record'
6
+ require 'generators/kriangle/generator_helpers'
7
+
8
+ module Kriangle
9
+ module Generators
10
+ # Custom scaffolding generator
11
+ class ModuleGenerator < Rails::Generators::NamedBase
12
+ include Rails::Generators::ResourceHelpers
13
+ include Rails::Generators::Migration
14
+ include Kriangle::Generators::GeneratorHelpers
15
+
16
+ CONTROLLER_ACTIONS = %w[index show new create edit update destroy create_or_destroy].freeze
17
+
18
+ no_tasks do
19
+ attr_accessor :user_class,
20
+ :wrapper,
21
+ :controller_path,
22
+ :column_types,
23
+ :attributes,
24
+ :model_attributes,
25
+ :model_associations,
26
+ :controller_actions,
27
+ :custom_orm,
28
+ :initial_setup,
29
+ :skip_tips,
30
+ :skip_authentication,
31
+ :skip_model,
32
+ :skip_migration,
33
+ :skip_serializer,
34
+ :skip_timestamps,
35
+ :skip_controller,
36
+ :skip_pagination,
37
+ :skip_swagger,
38
+ :reference,
39
+ :reference_name,
40
+ :reference_name_create_update,
41
+ :association_type,
42
+ :touch_record,
43
+ :accepts_nested_attributes,
44
+ :counter_cache,
45
+ :self_reference,
46
+ :parent_association_name,
47
+ :child_association_name,
48
+ :additional_where_clause,
49
+ :reference_id_param,
50
+ :creation_method,
51
+ :like_dislike,
52
+ :resources,
53
+ :description_method_name,
54
+ :search_by,
55
+ :database,
56
+ :force
57
+ end
58
+
59
+ argument :args_for_c_m, type: :array, default: [], banner: 'model:attributes'
60
+
61
+ class_option :user_class, type: :string, desc: "User's model name"
62
+ class_option :wrapper, type: :string, default: 'V1', desc: 'Skip "Swagger UI"'
63
+ class_option :controller_path, type: :string, desc: "controller's path"
64
+
65
+ class_option :reference, desc: 'Reference to user', type: :boolean, default: false
66
+ class_option :reference_name, type: :string, default: 'current_user', desc: 'Reference Name'
67
+ class_option :association_type, desc: 'Association with any model', type: :string
68
+ class_option :touch_record, desc: 'Touch the updated_at column', type: :boolean, default: false
69
+ class_option :accepts_nested_attributes, desc: 'accepts nested attributes', type: :boolean, default: false
70
+ class_option :counter_cache, desc: 'Counter cache support', type: :boolean, default: false
71
+
72
+ class_option :self_reference, desc: 'Counter cache support', type: :boolean, default: false
73
+ class_option :parent_association_name, type: :string, default: 'parent', desc: 'Parent Association Name'
74
+ class_option :child_association_name, type: :string, default: 'children', desc: 'Child Association Name'
75
+
76
+ class_option :creation_method, type: :string, default: 'new', desc: 'Creation Method'
77
+ class_option :like_dislike, desc: 'Like Dislike', type: :boolean, default: false
78
+
79
+ class_option :resources, desc: 'Resources routes', type: :boolean, default: true
80
+ class_option :initial_setup, type: :boolean, default: false, desc: 'Skip "Initial Setup i.e. Routes, Base models, etc."'
81
+
82
+ class_option :skip_swagger, type: :boolean, default: false, desc: 'Skip "Swagger UI"'
83
+ class_option :skip_tips, type: :boolean, default: false, desc: 'Skip "Tips from different files i.e. model, serializer, etc."'
84
+ class_option :skip_model, desc: 'Don\'t generate a model or migration file.', type: :boolean, default: false
85
+ class_option :skip_controller, desc: 'Don\'t generate a controller.', type: :boolean, default: false
86
+ class_option :skip_migration, desc: 'Don\'t generate migration file for model.', type: :boolean, default: false
87
+ class_option :skip_serializer, desc: 'Don\'t generate serializer file for model.', type: :boolean, default: false
88
+ class_option :skip_timestamps, desc: 'Don\'t add timestamps to migration file.', type: :boolean, default: false
89
+ class_option :skip_pagination, desc: 'Don\'t add pagination to index method.', type: :boolean, default: false
90
+ class_option :skip_authentication, desc: 'Don\'t require authentication for this controller.', type: :boolean, default: false
91
+ class_option :description_method_name, type: :string, default: 'desc', desc: 'desc or description'
92
+ class_option :force, desc: 'Force', type: :boolean, default: false
93
+
94
+ source_root File.expand_path('templates', __dir__)
95
+
96
+ def initialize(*args, &block)
97
+ super
98
+ @controller_actions = []
99
+ @model_attributes = []
100
+ @model_associations = []
101
+ @attributes = []
102
+ @references = []
103
+ @polymorphics = []
104
+
105
+ @user_class = options.user_class&.underscore
106
+ @wrapper = options.wrapper
107
+ @database = Kriangle.database
108
+ @controller_path = options.controller_path&.classify&.pluralize || controller_class_name
109
+ @force = options.force
110
+ @resources = options.resources?
111
+
112
+ @reference = options.reference?
113
+ if @reference
114
+ @association_type = options.association_type
115
+ @touch_record = options.touch_record?
116
+ @accepts_nested_attributes = options.accepts_nested_attributes?
117
+ @counter_cache = options.counter_cache?
118
+
119
+ @reference_name = options.reference_name
120
+ if @reference_name.match(/current_/)
121
+ @reference_name_create_update = @reference_name
122
+ @user_class ||= @reference_name.gsub(/current_/, '').underscore
123
+ else
124
+ @user_class ||= @reference_name.underscore
125
+ @reference_id_param = get_attribute_name(@reference_name.underscore, 'references')
126
+ @reference_name_create_update = "#{@reference_name}.find(params[:#{reference_id_param}])"
127
+ @reference_name = "#{@reference_name}.find(params[:#{reference_id_param}])"
128
+ end
129
+ @model_associations << Association.new('belongs_to', user_class, nil, 'true', counter_cache.to_s, touch_record.to_s, accepts_nested_attributes.to_s, '', '', true)
130
+ end
131
+
132
+ @self_reference = options.self_reference?
133
+ if @self_reference
134
+ @parent_association_name = options.parent_association_name
135
+ @child_association_name = options.child_association_name
136
+ @additional_where_clause = @self_reference ? '.only_parent' : ''
137
+ end
138
+ @creation_method = options.creation_method
139
+ @like_dislike = options.like_dislike
140
+
141
+ @custom_orm = 'ActiveRecord' # Kriangle.custom_orm
142
+ @initial_setup = options.initial_setup?
143
+ @skip_tips = options.skip_tips?
144
+ @skip_swagger = options.skip_swagger?
145
+ @skip_model = options.skip_model?
146
+ @skip_controller = options.skip_controller?
147
+ @skip_migration = options.skip_migration?
148
+ @skip_serializer = options.skip_serializer?
149
+ @skip_timestamps = options.skip_timestamps?
150
+ @skip_pagination = options.skip_pagination?
151
+
152
+ # skip authentication if authenticator not found
153
+ @skip_authentication = options.skip_authentication?
154
+ @skip_authentication = true unless File.exist?(File.join(destination_root, 'app/controllers/api/authenticator.rb'))
155
+ @description_method_name = @skip_authentication ? 'desc' : 'description'
156
+
157
+ args_for_c_m.each do |arg|
158
+ if arg.include?(':') || !CONTROLLER_ACTIONS.include?(arg)
159
+ options = arg.split(':')
160
+ if arg.match(/^ma:/).present?
161
+ options.shift
162
+ @model_associations << Association.new(*options)
163
+ else
164
+ @model_attributes << Attribute.new(*options)
165
+ end
166
+ else
167
+ @controller_actions << arg
168
+ @controller_actions << 'create' if arg == 'new'
169
+ @controller_actions << 'update' if arg == 'edit'
170
+ end
171
+ end
172
+
173
+ # Default controller actions
174
+ if @controller_actions.blank?
175
+ @controller_actions = %w[show create update destroy]
176
+ @controller_actions << 'index' if @resources
177
+ end
178
+
179
+ # Get attribute's name
180
+ @search_by = model_attributes.any? { |ma| ma.search_by.present? }
181
+
182
+ # get different types of attributes
183
+ @model_attributes.uniq!(&:name)
184
+ @model_attributes.each do |attribute|
185
+ if attribute.type.match('polymorphic').present?
186
+ @polymorphics << attribute
187
+ elsif attribute.type.match('references').present?
188
+ @references << attribute
189
+ else
190
+ @attributes << attribute
191
+ end
192
+ end
193
+ end
194
+
195
+ def self.next_migration_number(_path)
196
+ if @prev_migration_nr
197
+ @prev_migration_nr += 1
198
+ else
199
+ @prev_migration_nr = Time.now.utc.strftime('%Y%m%d%H%M%S').to_i
200
+ end
201
+ @prev_migration_nr.to_s
202
+ end
203
+
204
+ def copy_initializer
205
+ create_template 'application_record.rb', 'app/models/application_record.rb', skip_if_exist: true
206
+ create_template 'swagger.rb', 'config/initializers/swagger.rb', skip_if_exist: true unless skip_swagger
207
+
208
+ create_template 'base.rb', 'app/controllers/api/base.rb', skip_if_exist: true
209
+ create_template 'custom_description.rb', 'app/controllers/api/custom_description.rb', skip_if_exist: true
210
+ create_template 'responder.rb', 'app/controllers/api/responder.rb'
211
+
212
+ create_template 'controllers.rb', "app/controllers/api/#{@wrapper.underscore}/controllers.rb", skip_if_exist: true unless skip_controller
213
+ create_template 'defaults.rb', "app/controllers/api/#{@wrapper.underscore}/defaults.rb", skip_if_exist: true
214
+
215
+ inject_into_file 'app/controllers/api/base.rb', "\n\t\t\tmount Api::#{wrapper.capitalize}::Controllers", after: /Grape::API.*/
216
+
217
+ if initial_setup
218
+ inject_into_file 'config/routes.rb', "\n\tmount GrapeSwaggerRails::Engine => '/swagger'", after: /routes.draw.*/ unless skip_swagger
219
+ inject_into_file 'config/routes.rb', "\n\tmount Api::Base, at: '/'", after: /routes.draw.*/
220
+ end
221
+ end
222
+
223
+ desc 'Generates model with the given NAME.'
224
+ def create_model_file
225
+ # default options
226
+ options = { references: @references.map(&:name), polymorphics: @polymorphics.map(&:name) }
227
+
228
+ # create module model & migration
229
+ unless skip_model
230
+ options[:attributes] = @attributes.select { |a| a.validate_presence == 'true' }.map(&:name)
231
+ create_template 'model.rb', "app/models/#{singular_name}.rb", options
232
+ end
233
+
234
+ unless skip_model
235
+ model_associations.select { |ma| ma.association_type == 'belongs_to' }.each do |ma|
236
+ regex = "has_many :#{plural_name}"
237
+ file_path = ma.class_name.present? ? "app/models/#{ma.class_name.underscore}.rb" : "app/models/#{ma.association_name}.rb"
238
+ contents = File.foreach(file_path).grep /#{regex}/
239
+ next if contents.count != 0
240
+
241
+ association = "\n\thas_many :#{plural_name}, dependent: :destroy"
242
+ association += "\n\taccepts_nested_attributes_for :#{plural_name}, allow_destroy: true" if ma.accepts_nested_attributes == 'true'
243
+ belongs_to = ma.class_name.present? ? ma.class_name : ma.association_name.classify
244
+ inject_into_file file_path, association, after: /class #{belongs_to} < ApplicationRecord.*/
245
+ end
246
+ end
247
+
248
+ create_migration_file 'module_migration.rb', "db/migrate/create_#{plural_name}.rb", force: force if !skip_migration && custom_orm == 'ActiveRecord'
249
+ if custom_orm == 'ActiveRecord'
250
+ model_associations.select { |ma| ma.association_type == 'belongs_to' && ma.counter_cache == 'true' }.uniq { |ma| ma.association_name.classify && ma.class_name }.each do |ma|
251
+ belongs_to = ma.class_name.present? ? ma.class_name.underscore : ma.association_name
252
+ create_migration_file 'counter_cache_migration.rb', "db/migrate/add_#{class_name.pluralize.underscore}_count_to_#{belongs_to.pluralize}.rb", force: force, belongs_to: belongs_to
253
+ end
254
+ end
255
+
256
+ # create active serializer & module serializer
257
+ unless skip_serializer
258
+ create_template 'active_serializer.rb', 'app/serializers/active_serializer.rb', skip_if_exist: true
259
+ options[:attributes] = [:id] + @attributes.map(&:name)
260
+ create_template 'serializer.rb', "app/serializers/#{singular_name}_serializer.rb", options
261
+ inject_into_file "app/serializers/#{user_class}_serializer.rb", "\n\tattributes :#{class_name.pluralize.underscore}_count", after: /class #{user_class.classify}Serializer < ActiveSerializer*/ if counter_cache && custom_orm == 'ActiveRecord'
262
+ end
263
+ end
264
+
265
+ desc 'Generates controller with the given NAME.'
266
+ def copy_controller_and_spec_files
267
+ template 'controller.rb', "app/controllers/api/#{@wrapper.underscore}/#{controller_path.underscore}.rb" unless skip_controller
268
+
269
+ inject_into_file "app/controllers/api/#{@wrapper.underscore}/controllers.rb", "\n\t\t\tmount Api::#{@wrapper.capitalize}::#{controller_path}", after: /Grape::API.*/ unless skip_controller
270
+ end
271
+ end
272
+ end
273
+ end