mobile_workflow 0.7.5 → 0.7.9

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +83 -0
  3. data/app/controllers/concerns/mobile_workflow/s3_storable.rb +14 -11
  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 +75 -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 +40 -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 +53 -4
@@ -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]