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
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