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.
- checksums.yaml +4 -4
- data/README.md +83 -0
- data/app/controllers/concerns/mobile_workflow/s3_storable.rb +14 -11
- data/app/controllers/mobile_workflow/sns_notifications_controller.rb +14 -37
- data/app/jobs/mobile_workflow/add_attachment_job.rb +30 -0
- data/app/models/concerns/mobile_workflow/attachable.rb +32 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/form.rb +70 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/list.rb +15 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/map.rb +11 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/pie_chart.rb +11 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/question.rb +25 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/stack.rb +75 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/styled_content/grid.rb +30 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/styled_content/stack.rb +40 -0
- data/app/models/concerns/mobile_workflow/displayable.rb +17 -146
- data/config/initializers/add_frozen_string_literal.rb +19 -0
- data/lib/generators/mobile_workflow/install/install_generator.rb +17 -1
- data/lib/generators/mobile_workflow/install/templates/Gemfile.erb +24 -7
- data/lib/generators/mobile_workflow/install/templates/api_controller.rb.erb +1 -1
- data/lib/generators/mobile_workflow/install/templates/app/helpers/application_helper.rb +1 -2
- data/lib/generators/mobile_workflow/install/templates/{ability.rb → app/models/ability.rb} +1 -1
- data/lib/generators/mobile_workflow/install/templates/app/models/application_record.rb +3 -2
- data/lib/generators/mobile_workflow/install/templates/config/initializers/mobile_workflow_rollbar.rb +7 -0
- data/lib/generators/mobile_workflow/install/templates/deserializer.rb.erb +13 -0
- data/lib/generators/mobile_workflow/install/templates/deserializer_spec.rb.erb +27 -0
- data/lib/generators/mobile_workflow/install/templates/seeds.rb.erb +1 -1
- data/lib/generators/mobile_workflow/install/templates/sessions_controller.rb.erb +3 -4
- data/lib/generators/mobile_workflow/install/templates/user.rb.erb +4 -0
- data/lib/generators/mobile_workflow/model_generator.rb +9 -2
- data/lib/generators/mobile_workflow/templates/controller.rb.erb +7 -5
- data/lib/generators/mobile_workflow/templates/controller_spec.rb.erb +17 -9
- data/lib/generators/mobile_workflow/templates/model.rb.erb +17 -2
- data/lib/mobile_workflow/cli/app_builder.rb +21 -4
- data/lib/mobile_workflow/cli/app_server_generator.rb +3 -1
- data/lib/mobile_workflow/displayable.rb +9 -0
- data/lib/mobile_workflow/engine.rb +7 -5
- data/lib/mobile_workflow/version.rb +1 -1
- data/lib/mobile_workflow.rb +1 -0
- metadata +53 -4
@@ -1,163 +1,34 @@
|
|
1
1
|
module MobileWorkflow
|
2
2
|
module Displayable
|
3
|
-
|
4
|
-
|
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
|
152
|
-
|
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
|
-
|
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 '
|
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
|
-
|
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'))
|
@@ -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
|
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
|
-
|
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(
|
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) {
|
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 :
|
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]
|