mobile_workflow 0.7.6 → 0.8.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +83 -0
  3. data/app/controllers/concerns/mobile_workflow/s3_storable.rb +7 -5
  4. data/app/controllers/mobile_workflow/sns_notifications_controller.rb +14 -37
  5. data/app/jobs/mobile_workflow/add_attachment_job.rb +30 -0
  6. data/app/models/concerns/mobile_workflow/attachable.rb +32 -0
  7. data/app/models/concerns/mobile_workflow/displayable/steps/form.rb +70 -0
  8. data/app/models/concerns/mobile_workflow/displayable/steps/list.rb +15 -0
  9. data/app/models/concerns/mobile_workflow/displayable/steps/map.rb +11 -0
  10. data/app/models/concerns/mobile_workflow/displayable/steps/pie_chart.rb +11 -0
  11. data/app/models/concerns/mobile_workflow/displayable/steps/question.rb +25 -0
  12. data/app/models/concerns/mobile_workflow/displayable/steps/stack.rb +83 -0
  13. data/app/models/concerns/mobile_workflow/displayable/steps/styled_content/grid.rb +30 -0
  14. data/app/models/concerns/mobile_workflow/displayable/steps/styled_content/stack.rb +41 -0
  15. data/app/models/concerns/mobile_workflow/displayable.rb +17 -146
  16. data/config/initializers/add_frozen_string_literal.rb +19 -0
  17. data/lib/generators/mobile_workflow/install/install_generator.rb +17 -1
  18. data/lib/generators/mobile_workflow/install/templates/Gemfile.erb +24 -7
  19. data/lib/generators/mobile_workflow/install/templates/api_controller.rb.erb +1 -1
  20. data/lib/generators/mobile_workflow/install/templates/app/helpers/application_helper.rb +1 -2
  21. data/lib/generators/mobile_workflow/install/templates/{ability.rb → app/models/ability.rb} +1 -1
  22. data/lib/generators/mobile_workflow/install/templates/app/models/application_record.rb +3 -2
  23. data/lib/generators/mobile_workflow/install/templates/config/initializers/mobile_workflow_rollbar.rb +7 -0
  24. data/lib/generators/mobile_workflow/install/templates/deserializer.rb.erb +13 -0
  25. data/lib/generators/mobile_workflow/install/templates/deserializer_spec.rb.erb +27 -0
  26. data/lib/generators/mobile_workflow/install/templates/seeds.rb.erb +1 -1
  27. data/lib/generators/mobile_workflow/install/templates/sessions_controller.rb.erb +3 -4
  28. data/lib/generators/mobile_workflow/install/templates/user.rb.erb +4 -0
  29. data/lib/generators/mobile_workflow/model_generator.rb +9 -2
  30. data/lib/generators/mobile_workflow/templates/controller.rb.erb +7 -5
  31. data/lib/generators/mobile_workflow/templates/controller_spec.rb.erb +17 -9
  32. data/lib/generators/mobile_workflow/templates/model.rb.erb +17 -2
  33. data/lib/mobile_workflow/cli/app_builder.rb +21 -4
  34. data/lib/mobile_workflow/cli/app_server_generator.rb +3 -1
  35. data/lib/mobile_workflow/displayable.rb +9 -0
  36. data/lib/mobile_workflow/engine.rb +7 -5
  37. data/lib/mobile_workflow/version.rb +1 -1
  38. data/lib/mobile_workflow.rb +1 -0
  39. metadata +57 -8
@@ -1,163 +1,34 @@
1
1
  module MobileWorkflow
2
2
  module Displayable
3
- extend ActiveSupport::Concern
4
- include Rails.application.routes.url_helpers
5
-
6
- ON_SUCCESS_OPTIONS = [:none, :reload, :backward, :forward]
7
- BUTTON_STYLES = [:primary, :outline, :danger]
8
- CONTENT_MODE_OPTIONS = [:scale_aspect_fill, :scale_aspect_fit]
9
- QUESTION_STYLES = [:single_choice, :multiple_choice]
10
-
11
- def mw_list_item(id: self.id, text:, detail_text: nil, sf_symbol_name: nil, material_icon_name: nil, image_attachment: nil)
12
- mw_list_item = {id: id, text: text, detailText: detail_text, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}
13
- mw_list_item[:imageURL] = preview_url(image_attachment, options: { resize_to_fill: [200, 200] }) if image_attachment
14
- mw_list_item.compact
15
- end
16
-
17
- def mw_list_search_suggestion(id: self.id, text:, section_name:, sf_symbol_name: nil)
18
- {id: id.to_s, text: text, sectionName: section_name, sfSymbolName: sf_symbol_name}.compact
19
- end
20
-
21
- def mw_map_item(id: self.id, text:, detail_text: nil, latitude:, longitude:)
22
- {id: id.to_s, text: text, detailText: detail_text, latitude: latitude, longitude: longitude}.compact
23
- end
24
-
25
- def mw_pie_chart_item(id: self.id, label:, value:)
26
- {id: id, label: label, value: value}.compact
27
- end
28
-
29
- def mw_display_text(text:, label: nil)
30
- {type: :text, label: label, text: text.to_s}.compact
31
- end
32
-
33
- def mw_display_image(attachment, content_mode: :scale_aspect_fill, options: { resize_to_fill: [1200, 600] })
34
- validate_content_mode!(content_mode)
35
-
36
- {type: :image, contentMode: content_mode.to_s.camelize(:lower), previewURL: preview_url(attachment, options: options), url: attachment_url(attachment)}
37
- end
38
-
39
- def mw_display_unsplash_image(image_url)
40
- if image_url.start_with? "https://unsplash.com/photos"
41
- unsplash_id = image_url.split('/').last
42
- image_url = "https://source.unsplash.com/#{unsplash_id}/800x600"
43
- end
44
-
45
- {type: :image, previewURL: image_url, url: image_url}.compact
46
- end
47
-
48
- def mw_display_video(attachment, preview_options: { resize_to_fill: [600, 1200] })
49
- {type: :video, previewURL: preview_url(attachment, options: preview_options), url: attachment_url(attachment)}
50
- end
51
-
52
- def mw_display_button(label:, style: :primary, on_success: :forward, sf_symbol_name: nil, material_icon_name: nil)
53
- validate_on_success!(on_success)
54
- validate_button_style!(style)
55
-
56
- {type: :button, label: label, style: style, onSuccess: on_success, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}.compact
57
- end
58
-
59
- def mw_display_delete_button(url:, label: "Delete", method: :delete, style: :danger, on_success: :backward)
60
- validate_on_success!(on_success)
61
- validate_button_style!(style)
62
-
63
- {type: :button, label: label, url: url, method: method, style: style, onSuccess: on_success, sfSymbolName: 'trash', materialIconName: 'delete'}.compact
64
- end
65
-
66
- def mw_display_url_button(label:, url:, method: :put, style: :primary, confirm_title: nil, confirm_text: nil, on_success: :reload, sf_symbol_name: nil, material_icon_name: nil)
67
- validate_on_success!(on_success)
68
- validate_button_style!(style)
69
-
70
- {type: :button, label: label, url: url, method: method, style: style, confirmTitle: confirm_title, confirmText: confirm_text, onSuccess: on_success, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}.compact
71
- end
72
- alias_method :mw_display_button_for_url, :mw_display_url_button
73
-
74
- def mw_display_system_url_button(label:, apple_system_url: nil, android_deep_link: nil, style: :primary, sf_symbol_name: nil, material_icon_name: nil)
75
- validate_button_style!(style)
76
- raise 'Invalid android_deep_link' if android_deep_link && !android_deep_link.start_with?('http')
77
-
78
- {type: :button, label: label, appleSystemURL: apple_system_url, androidDeepLink: android_deep_link, style: style, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}.compact
79
- end
80
- alias_method :mw_display_button_for_system_url, :mw_display_system_url_button
81
-
82
- def mw_display_modal_workflow_button(label:, modal_workflow_name:, style: :primary, on_success: :none, sf_symbol_name: nil, material_icon_name: nil)
83
- validate_on_success!(on_success)
84
- validate_button_style!(style)
85
-
86
- {type: :button, label: label, modalWorkflow: modal_workflow_name, style: style, onSuccess: on_success, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}.compact
87
- end
88
- alias_method :mw_display_button_for_modal_workflow, :mw_display_modal_workflow_button
89
-
90
- def mw_text_choice_question(question:, style:, text_choices:)
91
- raise 'Missing question' if question.blank?
92
- raise 'Text Choices should be a hash' unless text_choices.is_a?(Hash)
93
- validate_question_style!(style)
94
-
95
- text_choices_a = text_choices.map{|k, v| {_class: "ORKTextChoice", exclusive: false, text: k, value: v} }.to_a
96
- { question: question, answerFormat: { _class: "ORKTextChoiceAnswerFormat", style: style.to_s.camelize(:lower), textChoices: text_choices_a}}
97
- end
98
-
99
- def mw_grid_large_section(id:, text:)
100
- raise 'Missing id' if id.nil?
101
- raise 'Missing text' if text.nil?
102
-
103
- { id: id, text: text, type: :largeSection }
104
- end
105
-
106
- def mw_grid_small_section(id:, text:)
107
- raise 'Missing id' if id.nil?
108
- raise 'Missing text' if text.nil?
109
-
110
- { id: id, text: text, type: :smallSection }
3
+ def self.included(base)
4
+ base.extend(Steps::Form)
111
5
  end
6
+
7
+ include Steps::List
8
+ include Steps::Map
9
+ include Steps::PieChart
10
+ include Steps::Question
11
+ include Steps::Stack
12
+ include Steps::StyledContent::Grid
13
+ include Steps::StyledContent::Stack
14
+
15
+ BUTTON_STYLES = [:primary, :outline, :danger, :textOnly]
16
+ ON_SUCCESS_OPTIONS = [:none, :reload, :backward, :forward]
112
17
 
113
- def mw_grid_item(id: self.id, text:, image_attachment: nil, options: { resize_to_fill: [1560, 877.5] })
114
- raise 'Missing id' if id.nil?
115
- raise 'Missing text' if text.nil?
116
-
117
- item = { id: id, text: text, type: :item }
118
- item[:imageURL] = preview_url(image_attachment, options: options) if image_attachment
119
- item
120
- end
121
-
122
18
  private
123
19
  def validate_on_success!(on_success)
124
20
  raise 'Unknown on_success action' unless ON_SUCCESS_OPTIONS.include?(on_success)
125
21
  end
126
22
 
127
- def validate_content_mode!(on_success)
128
- raise 'Unknown content_mode' unless CONTENT_MODE_OPTIONS.include?(on_success)
129
- end
130
-
131
23
  def validate_button_style!(style)
132
24
  raise 'Unknown style' unless BUTTON_STYLES.include?(style)
133
25
  end
134
-
135
- def validate_question_style!(style)
136
- raise 'Unknown style' unless QUESTION_STYLES.include?(style)
137
- end
138
-
139
- def preview_url(attachment, options:)
140
- return nil unless attachment.attached?
141
-
142
- if attachment.image?
143
- Rails.application.routes.url_helpers.rails_representation_url(attachment.variant(options), host: heroku_attachment_host)
144
- elsif attachment.previewable?
145
- Rails.application.routes.url_helpers.rails_representation_url(attachment.preview(options), host: heroku_attachment_host)
146
- else
147
- return nil
148
- end
149
- end
150
26
 
151
- def attachment_url(attachment)
152
- return nil unless attachment.attached?
153
-
154
- Rails.application.routes.url_helpers.rails_blob_url(attachment, host: heroku_attachment_host)
155
- end
27
+ def camelcase_converter(string, first_letter: :upper)
28
+ string = string.split("_").map(&:capitalize).join
29
+ return string unless first_letter == :lower
156
30
 
157
- def heroku_attachment_host
158
- # TODO: MBS - move this to a configuration property
159
- app_name = Rails.env.test? ? 'test-app' : ENV.fetch('HEROKU_APP_NAME')
160
- "https://#{app_name}.herokuapp.com"
31
+ string[0].downcase + string[1..-1]
161
32
  end
162
33
  end
163
34
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Adds a `frozen_string_literal` comment to the top of files created by Rails generators.
4
+ # Taken from https://gist.github.com/thornomad/4e2f0905e2a4a6eefbc4be5772dfd4f7#gistcomment-3533276
5
+ #
6
+ # Warning! Doorkeeper auto generated files already include `frozen_string_literal`, so it will be duplicated.
7
+
8
+ return unless defined?(::Rails::Generators)
9
+
10
+ module RailsGeneratorFrozenStringLiteralPrepend
11
+ RUBY_EXTENSIONS = %w[.rb .rake]
12
+
13
+ def render
14
+ return super unless RUBY_EXTENSIONS.include? File.extname(self.destination)
15
+ "# frozen_string_literal: true\n\n" + super
16
+ end
17
+ end
18
+
19
+ Thor::Actions::CreateFile.prepend RailsGeneratorFrozenStringLiteralPrepend
@@ -67,6 +67,22 @@ module MobileWorkflow
67
67
  end
68
68
  end
69
69
 
70
+ def ability_generator
71
+ copy_file("app/models/ability.rb")
72
+ end
73
+
74
+ def generate_deserializers
75
+ say "Generating deserializers"
76
+
77
+ model_name_to_properties.each_pair do |model_name, properties|
78
+ @deserializer_class = model_name
79
+ @deserializer_properties = properties.split(' ').map { |attribute| attribute.split(':').first }
80
+
81
+ template("deserializer.rb.erb", "app/services/#{model_name}_deserializer.rb")
82
+ template("deserializer_spec.rb.erb", "spec/services/#{model_name}_deserializer_spec.rb")
83
+ end
84
+ end
85
+
70
86
  def generate_controllers_and_routes
71
87
  say "Generating controllers"
72
88
  controller_name_to_actions = open_api_spec.controller_name_to_actions
@@ -87,7 +103,7 @@ module MobileWorkflow
87
103
  route "resources :#{plural_controller_name}, only: [#{actions.map{|a| ":#{a}"}.join(", ")}]"
88
104
  end
89
105
  end
90
-
106
+
91
107
  def generate_seeds
92
108
  template("seeds.rb.erb", "db/seeds.rb", force: true)
93
109
  end
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
  git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
5
 
4
6
  ruby '<%= MobileWorkflow::RUBY_VERSION %>'
5
7
 
6
8
  # Core Gems
7
- gem 'rails', '<%= MobileWorkflow::RAILS_VERSION %>'
8
9
  gem 'puma', '~> 5.0'
10
+ gem 'rails', '<%= MobileWorkflow::RAILS_VERSION %>'
9
11
  gem 'sass-rails', '>= 6'
10
12
  gem 'turbolinks', '~> 5'
11
13
 
@@ -14,10 +16,12 @@ gem 'mobile_workflow', '<%= MobileWorkflow::VERSION %>'
14
16
 
15
17
  # Authorisation / Authentication
16
18
  <%- if options[:doorkeeper_oauth] %>
17
- gem 'doorkeeper'
18
19
  gem 'bcrypt'
19
20
  <%- end %>
20
21
  gem 'cancancan', '~> 3.1'
22
+ <%- if options[:doorkeeper_oauth] %>
23
+ gem 'doorkeeper'
24
+ <%- end %>
21
25
 
22
26
  # Admin console
23
27
  gem 'administrate', '~> 0.13.0'
@@ -26,26 +30,39 @@ gem 'administrate-field-enum'
26
30
 
27
31
  <%- if options[:s3_storage] %>
28
32
  # Backend storage for S3
29
- gem "image_processing"
30
33
  gem 'aws-sdk-s3', '~> 1.60', '>= 1.60.1'
31
34
  gem 'aws-sdk-sns', '~> 1.23'
35
+ gem "image_processing"
32
36
  <%- end %>
33
37
 
34
38
  # FFI for Mac M1
35
39
  gem 'ffi', '~> 1.15.1'
36
40
 
41
+ # Error tracking
42
+ gem 'rollbar'
43
+
44
+ # Data migrations
45
+ gem 'data_migrate', '~> 7.0.0'
46
+
37
47
  group :development do
38
- gem 'web-console', '>= 3.3.0'
39
48
  gem 'listen', '>= 3.0.5', '< 3.2'
49
+ gem 'web-console', '>= 3.3.0'
40
50
  end
41
51
 
42
52
  group :development, :test do
43
- gem 'sqlite3'
44
- gem 'rspec-rails', '~> 4.0.0'
45
- gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
53
+ gem 'byebug', platforms: %i[mri mingw x64_mingw]
46
54
  gem 'dotenv-rails'
47
55
  gem 'factory_bot_rails'
56
+ gem 'rspec-rails', '~> 4.0.0'
57
+ gem 'rubocop', '~> 1.16', require: false
58
+ gem 'rubocop-rails', '~> 2.10.0', require: false
59
+ gem 'rubocop-rspec', '~> 2.3.0', require: false
48
60
  gem "rufo"
61
+ gem 'sqlite3'
62
+ end
63
+
64
+ group :test do
65
+ gem 'simplecov', '~> 0.21.2', require: false
49
66
  end
50
67
 
51
68
  group :production do
@@ -10,7 +10,7 @@ class ApiController < ActionController::API
10
10
  def current_resource_owner
11
11
  @current_resource_owner ||= User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token&.accessible?
12
12
  end
13
- alias_method :current_user, :current_resource_owner
13
+ alias current_user current_resource_owner
14
14
 
15
15
  protected
16
16
  def anonymous_action?
@@ -1,9 +1,8 @@
1
1
  module ApplicationHelper
2
-
3
2
  def bootstrap_class_for(flash_type)
4
3
  { success: "alert-success", error: "alert-danger", alert: "alert-warning", notice: "alert-info" }.stringify_keys[flash_type.to_s] || flash_type.to_s
5
4
  end
6
-
5
+
7
6
  def flash_messages
8
7
  flash.each do |flash_type, message|
9
8
  concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(flash_type)}", role: 'alert'))
@@ -1,7 +1,7 @@
1
1
  class Ability
2
2
  include CanCan::Ability
3
3
 
4
- def initialize(user)
4
+ def initialize(_user)
5
5
  can :manage, :all
6
6
  end
7
7
  end
@@ -1,5 +1,6 @@
1
1
  class ApplicationRecord < ActiveRecord::Base
2
+ include MobileWorkflow::Attachable
2
3
  include MobileWorkflow::Displayable
3
-
4
+
4
5
  self.abstract_class = true
5
- end
6
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MobileWorkflow
4
+ class ApplicationJob < ActiveJob::Base
5
+ include Rollbar::ActiveJob
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ class <%= @deserializer_class.capitalize %>Deserializer
2
+ def self.parse(params)
3
+ <% @deserializer_properties.each do |attribute| -%>
4
+ <%= attribute %> = params.dig(:payload, :<%= attribute %>)
5
+ <% end -%>
6
+
7
+ { <%= @deserializer_class %>: {
8
+ <% @deserializer_properties.each do |attribute| -%>
9
+ <%= attribute %>: <%= attribute %>,
10
+ <% end -%>
11
+ }.compact }
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require 'rails_helper'
2
+ require 'json'
3
+
4
+ RSpec.describe <%= @deserializer_class.capitalize %>Deserializer do
5
+ let(:params) { {} }
6
+
7
+ describe 'ok' do
8
+ let(:params) { { payload: payload } }
9
+ let(:parsed_params) { { <%= @deserializer_class %>: <%= @deserializer_class %>_params } }
10
+ let(:payload) {
11
+ {
12
+ <% @deserializer_properties.each do |attribute| -%>
13
+ <%= attribute %>: 'string',
14
+ <% end -%>
15
+ }
16
+ }
17
+ let(:<%= @deserializer_class %>_params) {
18
+ {
19
+ <% @deserializer_properties.each do |attribute| -%>
20
+ <%= attribute %>: 'string',
21
+ <% end -%>
22
+ }
23
+ }
24
+
25
+ it { expect(described_class.parse(params)).to eq parsed_params }
26
+ end
27
+ end
@@ -1,4 +1,4 @@
1
1
  <%- if options[:doorkeeper_oauth] %>
2
2
  # You can set the OAuth client ID and client secret in your ENV in order to avoid them being reset each time you reset the database.
3
3
  Doorkeeper::Application.create! name: 'Main App', redirect_uri: 'mww://callback', scopes: 'public', uid: ENV['OAUTH_CLIENT_ID'], secret: ENV['OAUTH_CLIENT_SECRET']
4
- <%- end %>
4
+ <%- end %>
@@ -1,10 +1,9 @@
1
1
  class SessionsController < ApplicationController
2
- def new
3
- end
2
+ def new; end
4
3
 
5
4
  def create
6
5
  @user = User.find_by("LOWER(email)= ?", params[:email].downcase)
7
- if @user && @user.authenticate(params[:password])
6
+ if @user&.authenticate(params[:password])
8
7
  session[:user_id] = @user.id
9
8
  redirect_to params[:return_to] || root_url
10
9
  else
@@ -12,4 +11,4 @@ class SessionsController < ApplicationController
12
11
  render :new
13
12
  end
14
13
  end
15
- end
14
+ end
@@ -1,4 +1,8 @@
1
1
  class User < ApplicationRecord
2
2
  has_secure_password
3
+
4
+ has_many :access_grants, class_name: 'Doorkeeper::AccessGrant', foreign_key: :resource_owner_id, inverse_of: :resource_owner, dependent: :destroy
5
+ has_many :access_tokens, class_name: 'Doorkeeper::AccessToken', foreign_key: :resource_owner_id, inverse_of: :resource_owner, dependent: :destroy
6
+
3
7
  validates :email, presence: true, uniqueness: true
4
8
  end
@@ -5,11 +5,18 @@ module MobileWorkflow
5
5
 
6
6
  class ModelGenerator < ActiveRecord::Generators::ModelGenerator
7
7
  source_root File.join(File.dirname(ActiveRecord::Generators::ModelGenerator.instance_method(:create_migration_file).source_location.first), "templates")
8
-
8
+
9
+ class_option :doorkeeper_oauth, type: :boolean, default: false
10
+
9
11
  def create_model_file
10
12
  template File.join(File.dirname(__FILE__), "templates", "model.rb.erb"), File.join('app/models', class_path, "#{file_name}.rb")
11
13
  end
12
-
14
+
15
+ private
16
+
17
+ def doorkeeper_oauth?
18
+ options[:doorkeeper_oauth]
19
+ end
13
20
  end
14
21
  end
15
22
  end
@@ -4,19 +4,19 @@ class <%= controller_class_name %>Controller < ApiController
4
4
  before_action :rewrite_payload, only: :create
5
5
 
6
6
  load_and_authorize_resource
7
-
7
+
8
8
  <% if index_action? -%>
9
9
  def index
10
10
  render json: @<%= plural_table_name %>.collect(&:list_item_as_json)
11
11
  end
12
-
13
12
  <% end -%>
13
+
14
14
  <% if show_action? -%>
15
15
  def show
16
16
  render json: @<%= singular_table_name %>.display_as_json
17
17
  end
18
-
19
18
  <% end -%>
19
+
20
20
  <% if create_action? -%>
21
21
  def create
22
22
  <% if doorkeeper_oauth? -%>
@@ -41,11 +41,13 @@ class <%= controller_class_name %>Controller < ApiController
41
41
  # passport_id = params.dig(:payload, :choose_passport, :selected, :id)
42
42
 
43
43
  Rails.logger.debug "Pre-rewrite params: #{params}"
44
- # Do your rewriting here
44
+
45
+ parsed_params = <%= controller_class_name.singularize %>Deserializer.parse(params)
46
+ params.merge!(parsed_params)
45
47
  end
46
48
 
47
49
  def <%= singular_table_name.underscore %>_params
48
- params.require(:payload).permit(<%= permitted_params %>)
50
+ params.require(:<%= singular_table_name %>).permit(<%= permitted_params %>)
49
51
  end
50
52
  <% end -%>
51
53
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'rails_helper'
4
2
  require 'json'
5
3
 
@@ -11,50 +9,60 @@ RSpec.describe <%= controller_class_name %>Controller do
11
9
  let(:token) { instance_double(Doorkeeper::AccessToken, accessible?: true, acceptable?: true, resource_owner_id: user.id) }
12
10
  <% end -%>
13
11
 
14
- <% if index_action? -%>
12
+ <% if index_action? -%>
15
13
  describe 'GET #index' do
16
14
  let!(:<%= controller_class_name.singularize.underscore %>) { create(:<%= controller_class_name.singularize.underscore %>) }
15
+
17
16
  before(:each) do
18
17
  <% if doorkeeper_oauth? -%>
19
18
  allow(subject).to receive(:doorkeeper_token) { token }
20
19
  <% end -%>
21
20
  get :index, params: params
22
21
  end
23
-
22
+
24
23
  context 'ok' do
25
24
  it { expect(json_response[0][:id]).to eq <%= controller_class_name.singularize.underscore %>.id }
26
25
  it { expect(response.status).to eq 200 }
27
26
  end
28
27
  end
29
28
  <% end -%>
29
+
30
30
  <% if show_action? -%>
31
31
  describe 'GET #show' do
32
32
  let(:<%= controller_class_name.singularize.underscore %>) { create(:<%= controller_class_name.singularize.underscore %>) }
33
33
  let(:params) { { id: <%= controller_class_name.singularize.underscore %>.id } }
34
+
34
35
  before(:each) do
35
36
  <% if doorkeeper_oauth? -%>
36
37
  allow(subject).to receive(:doorkeeper_token) { token }
37
38
  <% end -%>
38
39
  get :show, params: params
39
40
  end
40
-
41
- context 'ok' do
41
+
42
+ context 'ok' do
42
43
  it { expect(response.status).to eq 200 }
43
44
  end
44
45
  end
45
-
46
46
  <% end -%>
47
+
47
48
  <% if create_action? -%>
48
49
  describe 'POST #create' do
49
- let(:payload_params) { {text: 'OK'} }
50
+ let(:payload_params) {
51
+ {
52
+ <% attributes_names.each do |attribute| -%>
53
+ <%= attribute %>: 'string',
54
+ <% end -%>
55
+ }
56
+ }
50
57
  let(:params) { { payload: payload_params, binaries: [{identifier: 'record', mimetype: 'video/mp4'}] } }
58
+
51
59
  before(:each) do
52
60
  <% if doorkeeper_oauth? -%>
53
61
  allow(subject).to receive(:doorkeeper_token) { token }
54
62
  <% end -%>
55
63
  post :create, params: params
56
64
  end
57
-
65
+
58
66
  context 'ok' do
59
67
  it { expect(<%= controller_class_name.singularize %>.count).to eq 1 }
60
68
  it { expect(response.status).to eq 201 }
@@ -8,18 +8,33 @@ class <%= class_name %> < <%= parent_class_name.classify %>
8
8
  <% end -%>
9
9
  <% attributes.select(&:attachments?).each do |attribute| -%>
10
10
  has_many_attached :<%= attribute.name %>
11
+ <% end -%>
12
+ <% if class_name == 'User' -%>
13
+ <% if doorkeeper_oauth? -%>
14
+ has_many :access_grants, class_name: 'Doorkeeper::AccessGrant', foreign_key: :resource_owner_id, inverse_of: :resource_owner, dependent: :destroy
15
+ has_many :access_tokens, class_name: 'Doorkeeper::AccessToken', foreign_key: :resource_owner_id, inverse_of: :resource_owner, dependent: :destroy
16
+ <% end -%>
17
+ <% attributes.each do |attribute| -%>
18
+ <% if attribute.name == 'email' -%>
19
+ validates :email, presence: true, uniqueness: { case_sensitive: false }
20
+ before_validation :downcase_email, if: :email_changed?
21
+
22
+ def downcase_email
23
+ self.email = email.downcase
24
+ end
25
+ <% end -%>
26
+ <% end -%>
11
27
  <% end -%>
12
28
 
13
29
  def list_item_as_json
14
30
  mw_list_item(text: <%= attributes.first.name %>)
15
31
  end
16
-
32
+
17
33
  def display_as_json
18
34
  [
19
35
  mw_display_text(label: 'ID', text: id.to_s),
20
36
  mw_display_text(label: 'Text', text: <%= attributes.first.name %>)
21
37
  ]
22
38
  end
23
-
24
39
  end
25
40
  <% end -%>
@@ -48,10 +48,6 @@ CODE
48
48
  generate 'administrate:routes'
49
49
  end
50
50
 
51
- def ability_generator
52
- copy_file 'ability.rb', 'app/models/ability.rb'
53
- end
54
-
55
51
  def active_storage
56
52
  rails_command 'active_storage:install'
57
53
  copy_file 'storage.s3.yml', 'config/storage.yml'
@@ -78,6 +74,27 @@ ADMIN_USER=#{admin_user}
78
74
  ADMIN_PASSWORD=#{admin_password}
79
75
  CODE
80
76
  end
77
+
78
+ def rubocop
79
+ copy_file '.rubocop.yml'
80
+ command = 'rubocop --auto-gen-config'
81
+
82
+ puts "Running: #{command}"
83
+ output = `#{command}`
84
+ puts "Output: #{output}"
85
+ end
86
+
87
+ def simplecov
88
+ append_to_file 'spec/rails_helper.rb', "\n# Config for Test Coverage\nrequire 'simplecov'\nSimpleCov.start\nSimpleCov.minimum_coverage 80\n"
89
+ append_to_file '.gitignore', "\n# Ignore test coverage reports\n/coverage\n"
90
+ end
91
+
92
+ def rollbar
93
+ generate 'rollbar'
94
+ gsub_file 'config/initializers/rollbar.rb', 'if Rails.env.test?', 'if Rails.env.test? || Rails.env.development?'
95
+ copy_file 'config/initializers/mobile_workflow_rollbar.rb'
96
+ gsub_file 'app/jobs/application_job.rb', 'class ApplicationJob < ActiveJob::Base', "class ApplicationJob < ActiveJob::Base\n include Rollbar::ActiveJob\n"
97
+ end
81
98
 
82
99
  def git_commit(message = 'Initial commit')
83
100
  git add: "."
@@ -34,13 +34,15 @@ module MobileWorkflow::Cli
34
34
  build :procfiles
35
35
  build :rspec_generator
36
36
  build :factory_bot
37
- build :ability_generator
37
+ build :simplecov
38
+ build :rollbar
38
39
  build :active_storage if options[:s3_storage]
39
40
  build :mobile_workflow_generator, ARGV[1]
40
41
  build :migrate_db
41
42
  build :administrate_generator
42
43
  build :format_source
43
44
  build :generate_dot_env
45
+ build :rubocop
44
46
  build :git_commit
45
47
  build :heroku if options[:heroku]
46
48
  build :dokku, options[:dokku_host] if options[:dokku]